Re: [PATCH v2 2/6] btrfs-progs: check/common: Introduce a function to find imode using INODE_REF

Qu Wenruo
2019-09-09T14:24:40Z

On 2019/9/9 下午9:25, Nikolay Borisov wrote:
>
>
> On 5.09.19 г. 10:57 ч., Qu Wenruo wrote:
>> Introduce a function, find_file_type(), to find filetype using
>> INODE_REF.
>>
>> This function will:
>> - Search DIR_INDEX first
>> DIR_INDEX is easier since there is only one item in it.
>>
>> - Valid the DIR_INDEX item
>> If the DIR_INDEX is valid, use the filetype and call it a day.
>>
>> - Search DIR_ITEM then
>>
>> - Valide the DIR_ITEM
>> If valid, call it a day. Or return -ENOENT;
>>
>> This would be used as the primary method to determine the imode in later
>> imode repair code.
>>
>> Signed-off-by: Qu Wenruo <wqu@suse.com>
>> ---
>> check/mode-common.c | 99 +++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 99 insertions(+)
>>
>> diff --git a/check/mode-common.c b/check/mode-common.c
>> index 195b6efaa7aa..c0ddc50a1dd0 100644
>> --- a/check/mode-common.c
>> +++ b/check/mode-common.c
>> @@ -16,6 +16,7 @@
>>
>> #include <time.h>
>> #include "ctree.h"
>> +#include "hash.h"
>> #include "common/internal.h"
>> #include "common/messages.h"
>> #include "transaction.h"
>> @@ -836,6 +837,104 @@ int reset_imode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
>> return ret;
>> }
>>
>> +static int find_file_type(struct btrfs_root *root, u64 ino, u64 dirid,
>> + u64 index, const char *name, u32 name_len,
>> + u32 *imode_ret)
>> +{
>> + struct btrfs_path path;
>> + struct btrfs_key location;
>> + struct btrfs_key key;
>> + struct btrfs_dir_item *di;
>> + char namebuf[BTRFS_NAME_LEN] = {0};
>> + unsigned long cur;
>> + unsigned long end;
>> + bool found = false;
>> + u8 filetype;
>> + u32 len;
>> + int ret;
>> +
>> + btrfs_init_path(&path);
>> +
>> + /* Search DIR_INDEX first */
>> + key.objectid = dirid;
>> + key.offset = index;
>> + key.type = BTRFS_DIR_INDEX_KEY;
>> +
>> + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
>> + if (ret > 0)
>> + ret = -ENOENT;
>
> Even if it returns 1 meaning there is no DIR_INDEX item perhaps it still
> makes sense to go to dir_item: label to search for DIR_ITEM, what if the
> corruption has affected just the DIR_INDEX item?

I didn't get the point.

The next line is just going to do dir_item search, just as you mentioned.
>
>> + if (ret < 0)
>> + goto dir_item;
> nit: Use elseif to make it more explicit it is a single construct.

Not sure such usage is recommened, but I see a lot of usage like:
ret = btrfs_search_slot();
if (ret > 0)
ret = -ENOENT;
if (ret < 0)
goto error;

So I just followed this practice.

>
>> + di = btrfs_item_ptr(path.nodes[0], path.slots[0],
>> + struct btrfs_dir_item);
>> + btrfs_dir_item_key_to_cpu(path.nodes[0], di, &location);
>> +
>> + /* Various basic check */
>> + if (location.objectid != ino || location.type != BTRFS_INODE_ITEM_KEY ||
>> + location.offset != 0)
>> + goto dir_item;
>> + filetype = btrfs_dir_type(path.nodes[0], di);
>> + if (filetype >= BTRFS_FT_MAX || filetype == BTRFS_FT_UNKNOWN)
>> + goto dir_item;
>> + len = min_t(u32, btrfs_item_size_nr(path.nodes[0], path.slots[0]) -
>> + sizeof(*di), BTRFS_NAME_LEN);
>> + len = min_t(u32, len, btrfs_dir_name_len(path.nodes[0], di));
>> + read_extent_buffer(path.nodes[0], namebuf, (unsigned long)(di + 1), len);
>> + if (name_len != len || memcmp(namebuf, name, len))
>> + goto dir_item;
>> +
>> + /* Got a correct filetype */
>> + found = true;
>> + *imode_ret = btrfs_type_to_imode(filetype);
>> + ret = 0;
>> + goto out;
>> +
>> +dir_item:
>
> This function is needlessly structured in an awkward way. E.g. there is
> no dependence between "searching by DIR INDEX item" and "searching by
> DIR_ITEM". I think the best way is to have 3 function:
>
> Top level find_file_type which will call find_file_type_by_dir_index if
> the reeturn value is negative -> call 2nd function
> find_file_type_by_dir_item. This will result in 3 fairly short and easy
> to read/parse functions.

I'm OK with that, which is also my original plan, but it's just too easy
to put them altogether into one function, thus I forgot that plan.

>
>> + btrfs_release_path(&path);
>> + key.objectid = dirid;
>> + key.offset = btrfs_name_hash(name, name_len);
>> +
>> + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
>> + if (ret > 0)
>> + ret = -ENOENT;
>> + if (ret < 0) {
>> + btrfs_release_path(&path);
>> + return ret;
>> + }
>
> ditto about the else if construct
>
>> + cur = btrfs_item_ptr_offset(path.nodes[0], path.slots[0]);
>> + end = cur + btrfs_item_size_nr(path.nodes[0], path.slots[0]);
>> + while (cur < end) {
>
> Just checking : this is needed in case we have items whose names create
> a crc32 collision in the DIR_ITEM offset?

Yep.
For DIR_ITEM we can have collision while for DIR_INDEX it won't cause
collision.

>
>> + di = (struct btrfs_dir_item *)cur;
>> + btrfs_dir_item_key_to_cpu(path.nodes[0], di, &location);
>> + /* Various basic check */
>> + if (location.objectid != ino ||
>> + location.type != BTRFS_INODE_ITEM_KEY ||
>> + location.offset != 0)
>> + goto next;> + filetype = btrfs_dir_type(path.nodes[0], di);
>> + if (filetype >= BTRFS_FT_MAX || filetype == BTRFS_FT_UNKNOWN)
>> + goto next;
>> + len = min_t(u32, BTRFS_NAME_LEN,
>> + btrfs_item_size_nr(path.nodes[0], path.slots[0]) -
>> + sizeof(*di));
>> + len = min_t(u32, len, btrfs_dir_name_len(path.nodes[0], di));
>> + read_extent_buffer(path.nodes[0], namebuf,
>> + (unsigned long)(di + 1), len);
>> + if (name_len != len || memcmp(namebuf, name, len))
>> + goto next;
>> + *imode_ret = btrfs_type_to_imode(filetype);
>> + found = true;
>> + goto out;
>> +next:
>> + cur += btrfs_dir_name_len(path.nodes[0], di) + sizeof(*di);
>
> If this line is moved right after assigning to 'di' instead of having a
> 'next' label you can simply use the 'continue' keyword

Right, saving a tag is never a bad idea.

Thanks,
Qu
>
>> + }
>> +out:
>> + btrfs_release_path(&path);
>> + if (!found && !ret)
>> + ret = -ENOENT;
>> + return ret;
>> +}
>> +
>> /*
>> * Reset the inode mode of the inode specified by @path.
>> *
>>