For further lowmem_repair, introduce 'find_dir_index' to get the index by other inode item information. Remove 'check_dir_item' error msg print.
Adjust 'find_dir_item' args and remove err msg print. Signed-off-by: Su Yue <suy.f...@cn.fujitsu.com> --- cmds-check.c | 231 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 155 insertions(+), 76 deletions(-) diff --git a/cmds-check.c b/cmds-check.c index 892a22ba..c45dfae4 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -130,6 +130,8 @@ struct data_backref { #define LAST_ITEM (1<<15) /* Complete this tree traversal */ #define ROOT_REF_MISSING (1<<16) /* ROOT_REF not found */ #define ROOT_REF_MISMATCH (1<<17) /* ROOT_REF found but not match */ +#define DIR_INDEX_MISSING (1<<18) /* INODE_INDEX not found */ +#define DIR_INDEX_MISMATCH (1<<19) /* INODE_INDEX found but not match */ static inline struct data_backref* to_data_backref(struct extent_backref *back) { @@ -4133,29 +4135,30 @@ out: return err; } +static int find_dir_index(struct btrfs_root *root, u64 dirid, u64 location_id, + u64 *index_ret, char *namebuf, u32 name_len, + u8 file_type); /* * Find DIR_ITEM/DIR_INDEX for the given key and check it with the specified * INODE_REF/INODE_EXTREF match. * * @root: the root of the fs/file tree - * @ref_key: the key of the INODE_REF/INODE_EXTREF - * @key: the key of the DIR_ITEM/DIR_INDEX - * @index: the index in the INODE_REF/INODE_EXTREF, be used to - * distinguish root_dir between normal dir/file - * @name: the name in the INODE_REF/INODE_EXTREF - * @namelen: the length of name in the INODE_REF/INODE_EXTREF - * @mode: the st_mode of INODE_ITEM + * @key: the key of the DIR_ITEM/DIR_INDEX, key->offset will be right + * value while find index + * @location_key: location key of the struct btrfs_dir_item to match + * @name: the name to match + * @namelen: the length of name + * @file_type: the type of file to math * * Return 0 if no error occurred. - * Return ROOT_DIR_ERROR if found DIR_ITEM/DIR_INDEX for root_dir. - * Return DIR_ITEM_MISSING if couldn't find DIR_ITEM/DIR_INDEX for normal - * dir/file. - * Return DIR_ITEM_MISMATCH if INODE_REF/INODE_EXTREF and DIR_ITEM/DIR_INDEX - * not match for normal dir/file. + * Return DIR_ITEM_MISSING/DIR_INDEX_MISSING if couldn't find + * DIR_ITEM/DIR_INDEX + * Return DIR_ITEM_MISMATCH/DIR_INDEX_MISMATCH if INODE_REF/INODE_EXTREF + * and DIR_ITEM/DIR_INDEX mismatch */ -static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key, - struct btrfs_key *key, u64 index, char *name, - u32 namelen, u32 mode) +static int find_dir_item(struct btrfs_root *root, struct btrfs_key *key, + struct btrfs_key *location_key, char *name, + u32 namelen, u8 file_type) { struct btrfs_path path; struct extent_buffer *node; @@ -4165,104 +4168,166 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key, u32 total; u32 cur = 0; u32 len; - u32 name_len; u32 data_len; u8 filetype; int slot; int ret; + /* get the index by traversing all index */ + if (key->type == BTRFS_DIR_INDEX_KEY && key->offset == (u64)-1) { + ret = find_dir_index(root, key->objectid, + location_key->objectid, &key->offset, + name, namelen, file_type); + if (ret) + ret = DIR_INDEX_MISSING; + return ret; + } + btrfs_init_path(&path); ret = btrfs_search_slot(NULL, root, key, &path, 0, 0); - if (ret < 0) { - ret = DIR_ITEM_MISSING; + if (ret) { + ret = key->type == BTRFS_DIR_ITEM_KEY ? DIR_ITEM_MISSING : + DIR_INDEX_MISSING; goto out; } - /* Process root dir and goto out*/ - if (index == 0) { - if (ret == 0) { - ret = ROOT_DIR_ERROR; - error( - "root %llu INODE %s[%llu %llu] ROOT_DIR shouldn't have %s", - root->objectid, - ref_key->type == BTRFS_INODE_REF_KEY ? - "REF" : "EXTREF", - ref_key->objectid, ref_key->offset, - key->type == BTRFS_DIR_ITEM_KEY ? - "DIR_ITEM" : "DIR_INDEX"); - } else { - ret = 0; - } + /* Check whether inode_id/filetype/name match */ + node = path.nodes[0]; + slot = path.slots[0]; + di = btrfs_item_ptr(node, slot, struct btrfs_dir_item); + total = btrfs_item_size_nr(node, slot); + while (cur < total) { + ret = key->type == BTRFS_DIR_ITEM_KEY ? + DIR_ITEM_MISMATCH : DIR_INDEX_MISMATCH; - goto out; - } + len = btrfs_dir_name_len(node, di); + data_len = btrfs_dir_data_len(node, di); - /* Process normal file/dir */ - if (ret > 0) { - ret = DIR_ITEM_MISSING; - error( - "root %llu INODE %s[%llu %llu] doesn't have related %s[%llu %llu] namelen %u filename %s filetype %d", + btrfs_dir_item_key_to_cpu(node, di, &location); + if (location.objectid != location_key->objectid || + location.type != location_key->type || + location.offset != location_key->offset) + goto next; + + filetype = btrfs_dir_type(node, di); + if (file_type != filetype) + goto next; + + if (len > BTRFS_NAME_LEN) { + len = BTRFS_NAME_LEN; + warning("root %llu %s[%llu %llu] name too long %u, trimmed", root->objectid, - ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF", - ref_key->objectid, ref_key->offset, key->type == BTRFS_DIR_ITEM_KEY ? - "DIR_ITEM" : "DIR_INDEX", - key->objectid, key->offset, namelen, name, - imode_to_type(mode)); + "DIR_ITEM" : "DIR_INDEX", + key->objectid, key->offset, len); + } + read_extent_buffer(node, namebuf, (unsigned long)(di + 1), + len); + if (len != namelen || strncmp(namebuf, name, len)) + goto next; + + ret = 0; goto out; +next: + len += sizeof(*di) + data_len; + di = (struct btrfs_dir_item *)((char *)di + len); + cur += len; } +out: + btrfs_release_path(&path); + return ret; +} + +/* + * Find the @index according @ino. + * Notice:the time efficiency is O(n) + * + * @root: the root of the fs/file tree + * @index_ret: the index as return value + * @namebuf: the name to match + * @name_len: the length of name to match + * @file_type: the file_type of INODE_ITEM to match + * + * Returns 0 if found and *index will be modified with right value + * Returns< 0 not found and *index will be (u64)-1 + */ +static int find_dir_index(struct btrfs_root *root, u64 dirid, u64 location_id, + u64 *index_ret, char *namebuf, u32 name_len, + u8 file_type) +{ + struct btrfs_path path; + struct extent_buffer *node; + struct btrfs_dir_item *di; + struct btrfs_key key; + struct btrfs_key location; + char name[BTRFS_NAME_LEN] = {0}; + + u32 total; + u32 cur = 0; + u32 len; + u32 data_len; + u8 filetype; + int slot; + int ret; + + ASSERT(index_ret); + + /* search from the last index */ + key.objectid = dirid; + key.offset = (u64)-1; + key.type = BTRFS_DIR_INDEX_KEY; + btrfs_init_path(&path); + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0 || !ret) + return -EIO; + +loop: + ret = btrfs_previous_item(root, &path, dirid, BTRFS_DIR_INDEX_KEY); + if (ret) { + ret = -ENOENT; + *index_ret = (64)-1; + goto out; + } /* Check whether inode_id/filetype/name match */ node = path.nodes[0]; slot = path.slots[0]; di = btrfs_item_ptr(node, slot, struct btrfs_dir_item); total = btrfs_item_size_nr(node, slot); while (cur < total) { - ret = DIR_ITEM_MISMATCH; - name_len = btrfs_dir_name_len(node, di); + ret = -ENOENT; + len = btrfs_dir_name_len(node, di); data_len = btrfs_dir_data_len(node, di); btrfs_dir_item_key_to_cpu(node, di, &location); - if (location.objectid != ref_key->objectid || - location.type != BTRFS_INODE_ITEM_KEY || + if (location.objectid != location_id || + location.type != BTRFS_INODE_ITEM_KEY || location.offset != 0) goto next; filetype = btrfs_dir_type(node, di); - if (imode_to_type(mode) != filetype) + if (file_type != filetype) goto next; - if (name_len <= BTRFS_NAME_LEN) { - len = name_len; - } else { + if (len > BTRFS_NAME_LEN) len = BTRFS_NAME_LEN; - warning("root %llu %s[%llu %llu] name too long %u, trimmed", - root->objectid, - key->type == BTRFS_DIR_ITEM_KEY ? - "DIR_ITEM" : "DIR_INDEX", - key->objectid, key->offset, name_len); - } - read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len); - if (len != namelen || strncmp(namebuf, name, len)) + + read_extent_buffer(node, name, (unsigned long)(di + 1), len); + if (len != name_len || strncmp(namebuf, name, len)) goto next; + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + *index_ret = key.offset; ret = 0; goto out; next: - len = sizeof(*di) + name_len + data_len; + len += sizeof(*di) + data_len; di = (struct btrfs_dir_item *)((char *)di + len); cur += len; } - if (ret == DIR_ITEM_MISMATCH) - error( - "root %llu INODE %s[%llu %llu] and %s[%llu %llu] mismatch namelen %u filename %s filetype %d", - root->objectid, - ref_key->type == BTRFS_INODE_REF_KEY ? "REF" : "EXTREF", - ref_key->objectid, ref_key->offset, - key->type == BTRFS_DIR_ITEM_KEY ? - "DIR_ITEM" : "DIR_INDEX", - key->objectid, key->offset, namelen, name, - imode_to_type(mode)); + goto loop; + out: btrfs_release_path(&path); return ret; @@ -4284,6 +4349,7 @@ static int check_inode_ref(struct btrfs_root *root, struct btrfs_key *ref_key, int mode) { struct btrfs_key key; + struct btrfs_key location; struct btrfs_inode_ref *ref; char namebuf[BTRFS_NAME_LEN] = {0}; u32 total; @@ -4293,6 +4359,10 @@ static int check_inode_ref(struct btrfs_root *root, struct btrfs_key *ref_key, u64 index; int ret, err = 0; + location.objectid = ref_key->objectid; + location.type = BTRFS_INODE_ITEM_KEY; + location.offset = 0; + ref = btrfs_item_ptr(node, slot, struct btrfs_inode_ref); total = btrfs_item_size_nr(node, slot); @@ -4324,14 +4394,16 @@ next: key.objectid = ref_key->offset; key.type = BTRFS_DIR_INDEX_KEY; key.offset = index; - ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + ret = find_dir_item(root, &key, &location, namebuf, len, + imode_to_type(mode)); err |= ret; /* Find related dir_item */ key.objectid = ref_key->offset; key.type = BTRFS_DIR_ITEM_KEY; key.offset = btrfs_name_hash(namebuf, len); - ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + ret = find_dir_item(root, &key, &location, namebuf, len, + imode_to_type(mode)); err |= ret; len = sizeof(*ref) + name_len; @@ -4360,6 +4432,7 @@ static int check_inode_extref(struct btrfs_root *root, int mode) { struct btrfs_key key; + struct btrfs_key location; struct btrfs_inode_extref *extref; char namebuf[BTRFS_NAME_LEN] = {0}; u32 total; @@ -4371,6 +4444,10 @@ static int check_inode_extref(struct btrfs_root *root, int ret; int err = 0; + location.objectid = ref_key->objectid; + location.type = BTRFS_INODE_ITEM_KEY; + location.offset = 0; + extref = btrfs_item_ptr(node, slot, struct btrfs_inode_extref); total = btrfs_item_size_nr(node, slot); @@ -4401,14 +4478,16 @@ next: key.objectid = parent; key.type = BTRFS_DIR_INDEX_KEY; key.offset = index; - ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + ret = find_dir_item(root, &key, &location, namebuf, len, + imode_to_type(mode)); err |= ret; /* find related dir_item */ key.objectid = parent; key.type = BTRFS_DIR_ITEM_KEY; key.offset = btrfs_name_hash(namebuf, len); - ret = find_dir_item(root, ref_key, &key, index, namebuf, len, mode); + ret = find_dir_item(root, &key, &location, namebuf, len, + imode_to_type(mode)); err |= ret; len = sizeof(*extref) + name_len; -- 2.11.1 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html