summaryrefslogtreecommitdiff
path: root/cmds-check.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmds-check.c')
-rw-r--r--cmds-check.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/cmds-check.c b/cmds-check.c
index f42e3c5c..79149838 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -1727,6 +1727,61 @@ static int delete_dir_index(struct btrfs_root *root,
return ret;
}
+static int create_inode_item(struct btrfs_root *root,
+ struct inode_record *rec,
+ struct inode_backref *backref, int root_dir)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_inode_item inode_item;
+ time_t now = time(NULL);
+ int ret;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ return ret;
+ }
+
+ fprintf(stderr, "root %llu inode %llu recreating inode item, this may "
+ "be incomplete, please check permissions and content after "
+ "the fsck completes.\n", (unsigned long long)root->objectid,
+ (unsigned long long)rec->ino);
+
+ memset(&inode_item, 0, sizeof(inode_item));
+ btrfs_set_stack_inode_generation(&inode_item, trans->transid);
+ if (root_dir)
+ btrfs_set_stack_inode_nlink(&inode_item, 1);
+ else
+ btrfs_set_stack_inode_nlink(&inode_item, rec->found_link);
+ btrfs_set_stack_inode_nbytes(&inode_item, rec->found_size);
+ if (rec->found_dir_item) {
+ if (rec->found_file_extent)
+ fprintf(stderr, "root %llu inode %llu has both a dir "
+ "item and extents, unsure if it is a dir or a "
+ "regular file so setting it as a directory\n",
+ (unsigned long long)root->objectid,
+ (unsigned long long)rec->ino);
+ btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755);
+ btrfs_set_stack_inode_size(&inode_item, rec->found_size);
+ } else if (!rec->found_dir_item) {
+ btrfs_set_stack_inode_size(&inode_item, rec->extent_end);
+ btrfs_set_stack_inode_mode(&inode_item, S_IFREG | 0755);
+ }
+ btrfs_set_stack_timespec_sec(&inode_item.atime, now);
+ btrfs_set_stack_timespec_nsec(&inode_item.atime, 0);
+ btrfs_set_stack_timespec_sec(&inode_item.ctime, now);
+ btrfs_set_stack_timespec_nsec(&inode_item.ctime, 0);
+ btrfs_set_stack_timespec_sec(&inode_item.mtime, now);
+ btrfs_set_stack_timespec_nsec(&inode_item.mtime, 0);
+ btrfs_set_stack_timespec_sec(&inode_item.otime, 0);
+ btrfs_set_stack_timespec_nsec(&inode_item.otime, 0);
+
+ ret = btrfs_insert_inode(trans, root, rec->ino, &inode_item);
+ BUG_ON(ret);
+ btrfs_commit_transaction(trans, root);
+ return 0;
+}
+
static int repair_inode_backrefs(struct btrfs_root *root,
struct inode_record *rec,
struct cache_tree *inode_cache,
@@ -1738,6 +1793,15 @@ static int repair_inode_backrefs(struct btrfs_root *root,
int repaired = 0;
list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) {
+ if (!delete && rec->ino == root_dirid) {
+ if (!rec->found_inode_item) {
+ ret = create_inode_item(root, rec, backref, 1);
+ if (ret)
+ break;
+ repaired++;
+ }
+ }
+
/* Index 0 for root dir's are special, don't mess with it */
if (rec->ino == root_dirid && backref->index == 0)
continue;
@@ -1799,6 +1863,18 @@ static int repair_inode_backrefs(struct btrfs_root *root,
btrfs_commit_transaction(trans, root);
repaired++;
}
+
+ if (!delete && (backref->found_inode_ref &&
+ backref->found_dir_index &&
+ backref->found_dir_item &&
+ !(backref->errors & REF_ERR_INDEX_UNMATCH) &&
+ !rec->found_inode_item)) {
+ ret = create_inode_item(root, rec, backref, 0);
+ if (ret)
+ break;
+ repaired++;
+ }
+
}
return ret ? ret : repaired;
}