btrfs-progs: check: change find_dir_index/item

The changes in the patch is for further repair:
1.Introduce find_dir_index() to get the index by traversing items.

2.We should distinguish dir_index error and dir_item error.
However, there are only DIR_ITEM_MISSING and DIR_ITEM_MISMATCH.
Introduce marcos DIR_INDEX_MISSING and DIR_INDEX_MISMATCH
to represent index missing/mismatch.

3.Because find_dir_item() prints message right now if it detects any
error.
Remove message output now and next patches will introduce functions
to print error message.

Signed-off-by: Su Yue <suy.fnst@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
master
Su Yue 2017-08-28 14:18:27 +08:00 committed by David Sterba
parent f32ca72125
commit 2f4bc981d6
1 changed files with 156 additions and 89 deletions

View File

@ -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)
{
@ -4239,29 +4241,121 @@ out:
return err;
}
/*
* Find the @index according by @ino and name.
* Notice: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_ret will be modified with right value
* Returns< 0 not found and *@index_ret 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)
return ret;
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 = -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 != location_id ||
location.type != BTRFS_INODE_ITEM_KEY ||
location.offset != 0)
goto next;
filetype = btrfs_dir_type(node, di);
if (file_type != filetype)
goto next;
if (len > BTRFS_NAME_LEN)
len = BTRFS_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) + data_len;
di = (struct btrfs_dir_item *)((char *)di + len);
cur += len;
}
goto loop;
out:
btrfs_release_path(&path);
return ret;
}
/*
* 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;
@ -4271,50 +4365,26 @@ 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;
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;
}
goto out;
}
/* 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",
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));
if (ret) {
ret = key->type == BTRFS_DIR_ITEM_KEY ? DIR_ITEM_MISSING :
DIR_INDEX_MISSING;
goto out;
}
@ -4324,57 +4394,43 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key,
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 = key->type == BTRFS_DIR_ITEM_KEY ?
DIR_ITEM_MISMATCH : DIR_INDEX_MISMATCH;
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 ||
location.offset != 0)
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 (imode_to_type(mode) != filetype)
if (file_type != filetype)
goto next;
if (cur + sizeof(*di) + name_len > total ||
name_len > BTRFS_NAME_LEN) {
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);
if (cur + sizeof(*di) > total)
break;
len = min_t(u32, total - cur - sizeof(*di),
BTRFS_NAME_LEN);
} else {
len = name_len;
root->objectid,
key->type == BTRFS_DIR_ITEM_KEY ?
"DIR_ITEM" : "DIR_INDEX",
key->objectid, key->offset, len);
}
read_extent_buffer(node, namebuf, (unsigned long)(di + 1), 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) + 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));
out:
btrfs_release_path(&path);
return ret;
@ -4396,6 +4452,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;
@ -4403,7 +4460,12 @@ static int check_inode_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
u32 len;
u32 name_len;
u64 index;
int ret, err = 0;
int ret;
int 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);
@ -4440,14 +4502,14 @@ 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, 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, mode);
err |= ret;
len = sizeof(*ref) + name_len;
@ -4477,6 +4539,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;
@ -4488,6 +4551,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);
@ -4518,14 +4585,14 @@ 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, 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, mode);
err |= ret;
len = sizeof(*extref) + name_len;