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;
|
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) {
|
list_for_each_entry(backref, &rec->backrefs, list) {
|
||||||
if (backref->found_dir_index || backref->found_dir_item) {
|
if (backref->found_dir_index || backref->found_dir_item) {
|
||||||
*type = backref->filetype;
|
*type = backref->filetype;
|
||||||
|
@ -2098,6 +2104,149 @@ out:
|
||||||
return ret;
|
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)
|
static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
|
||||||
{
|
{
|
||||||
struct btrfs_trans_handle *trans;
|
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 |
|
if (!(rec->errors & (I_ERR_DIR_ISIZE_WRONG |
|
||||||
I_ERR_NO_ORPHAN_ITEM |
|
I_ERR_NO_ORPHAN_ITEM |
|
||||||
I_ERR_LINK_COUNT_WRONG)))
|
I_ERR_LINK_COUNT_WRONG |
|
||||||
|
I_ERR_NO_INODE_ITEM)))
|
||||||
return rec->errors;
|
return rec->errors;
|
||||||
|
|
||||||
path = btrfs_alloc_path();
|
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);
|
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);
|
ret = repair_inode_isize(trans, root, path, rec);
|
||||||
if (!ret && rec->errors & I_ERR_NO_ORPHAN_ITEM)
|
if (!ret && rec->errors & I_ERR_NO_ORPHAN_ITEM)
|
||||||
ret = repair_inode_orphan_item(trans, root, path, rec);
|
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)
|
if (rec->found_link != rec->nlink)
|
||||||
rec->errors |= I_ERR_LINK_COUNT_WRONG;
|
rec->errors |= I_ERR_LINK_COUNT_WRONG;
|
||||||
if (repair) {
|
if (repair) {
|
||||||
|
@ -2294,8 +2448,6 @@ static int check_inode_recs(struct btrfs_root *root,
|
||||||
}
|
}
|
||||||
|
|
||||||
error++;
|
error++;
|
||||||
if (!rec->found_inode_item)
|
|
||||||
rec->errors |= I_ERR_NO_INODE_ITEM;
|
|
||||||
print_inode_error(root, rec);
|
print_inode_error(root, rec);
|
||||||
list_for_each_entry(backref, &rec->backrefs, list) {
|
list_for_each_entry(backref, &rec->backrefs, list) {
|
||||||
if (!backref->found_dir_item)
|
if (!backref->found_dir_item)
|
||||||
|
|
2
ctree.h
2
ctree.h
|
@ -2445,6 +2445,8 @@ static inline int is_fstree(u64 rootid)
|
||||||
/* inode.c */
|
/* inode.c */
|
||||||
int check_dir_conflict(struct btrfs_root *root, char *name, int namelen,
|
int check_dir_conflict(struct btrfs_root *root, char *name, int namelen,
|
||||||
u64 dir, u64 index);
|
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,
|
int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||||
u64 ino, u64 parent_ino, char *name, int namelen,
|
u64 ino, u64 parent_ino, char *name, int namelen,
|
||||||
u8 type, u64 *index, int add_backref);
|
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.
|
* its backref.
|
||||||
* The backref is added by btrfs_add_link().
|
* The backref is added by btrfs_add_link().
|
||||||
*/
|
*/
|
||||||
static int btrfs_new_inode(struct btrfs_trans_handle *trans,
|
int btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||||
struct btrfs_root *root,
|
u64 ino, u32 mode)
|
||||||
u64 ino, u32 mode)
|
|
||||||
{
|
{
|
||||||
struct btrfs_inode_item inode_item = {0};
|
struct btrfs_inode_item inode_item = {0};
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
Loading…
Reference in New Issue