forked from Mirrors/btrfs-progs
btrfs-progs: Add inode item rebuild function.
Add a basic inode item rebuild function for I_ERR_NO_INODE_ITEM. The main use case is to repair btrfs which fs root has corrupted leaf, but it is already working for case if the corrupteed fs root leaf/node contains no inode extent_data. The repair needs 3 elements for inode rebuild: 1. inode number This is quite easy, existing inode_record codes will detect it quite well. 2. inode type This is the trick part. The only reliable method is to recovery it from parent's dir_index/item. The remaining method will search for regular file extent for FILE type or child's backref for DIR(todo). Fallback will be FILE. Inode name(inode_ref) will be recoverd by nlink repair function. This is just a fundamental implement, some advanced recovery can be improved later with btrfs-progs infrastructure change. Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Signed-off-by: David Sterba <dsterba@suse.cz>master
parent
1581d7e5db
commit
55fea7186c
160
cmds-check.c
160
cmds-check.c
|
@ -1905,6 +1905,12 @@ static int find_file_type(struct inode_record *rec, u8 *type)
|
|||
{
|
||||
struct inode_backref *backref;
|
||||
|
||||
/* For inode item recovered case */
|
||||
if (rec->found_inode_item) {
|
||||
*type = imode_to_type(rec->imode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each_entry(backref, &rec->backrefs, list) {
|
||||
if (backref->found_dir_index || backref->found_dir_item) {
|
||||
*type = backref->filetype;
|
||||
|
@ -2098,6 +2104,149 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if there is any normal(reg or prealloc) file extent for given
|
||||
* ino.
|
||||
* This is used to determine the file type when neither its dir_index/item or
|
||||
* inode_item exists.
|
||||
*
|
||||
* This will *NOT* report error, if any error happens, just consider it does
|
||||
* not have any normal file extent.
|
||||
*/
|
||||
static int find_normal_file_extent(struct btrfs_root *root, u64 ino)
|
||||
{
|
||||
struct btrfs_path *path;
|
||||
struct btrfs_key key;
|
||||
struct btrfs_key found_key;
|
||||
struct btrfs_file_extent_item *fi;
|
||||
u8 type;
|
||||
int ret = 0;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
goto out;
|
||||
key.objectid = ino;
|
||||
key.type = BTRFS_EXTENT_DATA_KEY;
|
||||
key.offset = 0;
|
||||
|
||||
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
||||
if (ret < 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (ret && path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
|
||||
ret = btrfs_next_leaf(root, path);
|
||||
if (ret) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
while (1) {
|
||||
btrfs_item_key_to_cpu(path->nodes[0], &found_key,
|
||||
path->slots[0]);
|
||||
if (found_key.objectid != ino ||
|
||||
found_key.type != BTRFS_EXTENT_DATA_KEY)
|
||||
break;
|
||||
fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
||||
struct btrfs_file_extent_item);
|
||||
type = btrfs_file_extent_type(path->nodes[0], fi);
|
||||
if (type != BTRFS_FILE_EXTENT_INLINE)
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 btrfs_type_to_imode(u8 type)
|
||||
{
|
||||
static u32 imode_by_btrfs_type[] = {
|
||||
[BTRFS_FT_REG_FILE] = S_IFREG,
|
||||
[BTRFS_FT_DIR] = S_IFDIR,
|
||||
[BTRFS_FT_CHRDEV] = S_IFCHR,
|
||||
[BTRFS_FT_BLKDEV] = S_IFBLK,
|
||||
[BTRFS_FT_FIFO] = S_IFIFO,
|
||||
[BTRFS_FT_SOCK] = S_IFSOCK,
|
||||
[BTRFS_FT_SYMLINK] = S_IFLNK,
|
||||
};
|
||||
|
||||
return imode_by_btrfs_type[(type)];
|
||||
}
|
||||
|
||||
static int repair_inode_no_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
struct inode_record *rec)
|
||||
{
|
||||
u8 filetype;
|
||||
u32 mode = 0700;
|
||||
int type_recovered = 0;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* 1. salvage data from existing file extent and
|
||||
* punch hole to keep fi ext consistent.
|
||||
* 2. salvage data from extent tree
|
||||
*/
|
||||
printf("Trying to rebuild inode:%llu\n", rec->ino);
|
||||
|
||||
type_recovered = !find_file_type(rec, &filetype);
|
||||
|
||||
/*
|
||||
* Try to determine inode type if type not found.
|
||||
*
|
||||
* For found regular file extent, it must be FILE.
|
||||
* For found dir_item/index, it must be DIR.
|
||||
*
|
||||
* For undetermined one, use FILE as fallback.
|
||||
*
|
||||
* TODO:
|
||||
* 1. If found extent belong to it in extent tree, it must be FILE
|
||||
* Need extra hook in extent tree scan.
|
||||
* 2. If found backref(inode_index/item is already handled) to it,
|
||||
* it must be DIR.
|
||||
* Need new inode-inode ref structure to allow search for that.
|
||||
*/
|
||||
if (!type_recovered) {
|
||||
if (rec->found_file_extent &&
|
||||
find_normal_file_extent(root, rec->ino)) {
|
||||
type_recovered = 1;
|
||||
filetype = BTRFS_FT_REG_FILE;
|
||||
} else if (rec->found_dir_item) {
|
||||
type_recovered = 1;
|
||||
filetype = BTRFS_FT_DIR;
|
||||
} else {
|
||||
printf("Can't determint the filetype for inode %llu, assume it is a normal file\n",
|
||||
rec->ino);
|
||||
type_recovered = 1;
|
||||
filetype = BTRFS_FT_REG_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btrfs_new_inode(trans, root, rec->ino,
|
||||
mode | btrfs_type_to_imode(filetype));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Here inode rebuild is done, we only rebuild the inode item,
|
||||
* don't repair the nlink(like move to lost+found).
|
||||
* That is the job of nlink repair.
|
||||
*
|
||||
* We just fill the record and return
|
||||
*/
|
||||
rec->found_dir_item = 1;
|
||||
rec->imode = mode | btrfs_type_to_imode(filetype);
|
||||
rec->nlink = 0;
|
||||
rec->errors &= ~I_ERR_NO_INODE_ITEM;
|
||||
/* Ensure the inode_nlinks repair function will be called */
|
||||
rec->errors |= I_ERR_LINK_COUNT_WRONG;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
|
||||
{
|
||||
struct btrfs_trans_handle *trans;
|
||||
|
@ -2106,7 +2255,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
|
|||
|
||||
if (!(rec->errors & (I_ERR_DIR_ISIZE_WRONG |
|
||||
I_ERR_NO_ORPHAN_ITEM |
|
||||
I_ERR_LINK_COUNT_WRONG)))
|
||||
I_ERR_LINK_COUNT_WRONG |
|
||||
I_ERR_NO_INODE_ITEM)))
|
||||
return rec->errors;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
|
@ -2126,7 +2276,9 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
|
|||
return PTR_ERR(trans);
|
||||
}
|
||||
|
||||
if (rec->errors & I_ERR_DIR_ISIZE_WRONG)
|
||||
if (rec->errors & I_ERR_NO_INODE_ITEM)
|
||||
ret = repair_inode_no_item(trans, root, path, rec);
|
||||
if (!ret && rec->errors & I_ERR_DIR_ISIZE_WRONG)
|
||||
ret = repair_inode_isize(trans, root, path, rec);
|
||||
if (!ret && rec->errors & I_ERR_NO_ORPHAN_ITEM)
|
||||
ret = repair_inode_orphan_item(trans, root, path, rec);
|
||||
|
@ -2282,6 +2434,8 @@ static int check_inode_recs(struct btrfs_root *root,
|
|||
}
|
||||
}
|
||||
|
||||
if (!rec->found_inode_item)
|
||||
rec->errors |= I_ERR_NO_INODE_ITEM;
|
||||
if (rec->found_link != rec->nlink)
|
||||
rec->errors |= I_ERR_LINK_COUNT_WRONG;
|
||||
if (repair) {
|
||||
|
@ -2294,8 +2448,6 @@ static int check_inode_recs(struct btrfs_root *root,
|
|||
}
|
||||
|
||||
error++;
|
||||
if (!rec->found_inode_item)
|
||||
rec->errors |= I_ERR_NO_INODE_ITEM;
|
||||
print_inode_error(root, rec);
|
||||
list_for_each_entry(backref, &rec->backrefs, list) {
|
||||
if (!backref->found_dir_item)
|
||||
|
|
2
ctree.h
2
ctree.h
|
@ -2445,6 +2445,8 @@ static inline int is_fstree(u64 rootid)
|
|||
/* inode.c */
|
||||
int check_dir_conflict(struct btrfs_root *root, char *name, int namelen,
|
||||
u64 dir, u64 index);
|
||||
int btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
u64 ino, u32 mode);
|
||||
int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
u64 ino, u64 parent_ino, char *name, int namelen,
|
||||
u8 type, u64 *index, int add_backref);
|
||||
|
|
5
inode.c
5
inode.c
|
@ -458,9 +458,8 @@ static void fill_inode_item(struct btrfs_trans_handle *trans,
|
|||
* its backref.
|
||||
* The backref is added by btrfs_add_link().
|
||||
*/
|
||||
static int btrfs_new_inode(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 ino, u32 mode)
|
||||
int btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
u64 ino, u32 mode)
|
||||
{
|
||||
struct btrfs_inode_item inode_item = {0};
|
||||
int ret = 0;
|
||||
|
|
Loading…
Reference in New Issue