Skip to content
58 changes: 58 additions & 0 deletions check/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ bool is_free_space_tree = false;
bool init_extent_tree = false;
bool check_data_csum = false;
static bool found_free_ino_cache = false;
static bool found_unknown_key = false;
struct cache_tree *roots_info_cache = NULL;

enum btrfs_check_mode {
Expand Down Expand Up @@ -1895,6 +1896,10 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
ret = process_xattr_item(eb, i, &key, active_node);
break;
default:
error("Unknown key (%llu %u %llu) found in leaf %llu",
key.objectid, key.type, key.offset,
eb->start);
found_unknown_key = true;
break;
};
}
Expand Down Expand Up @@ -2346,6 +2351,42 @@ static int create_inode_item(struct btrfs_root *root,
return 0;
}

static int create_inode_ref(struct btrfs_root *root,
struct inode_record *i_rec,
struct inode_backref *backref)
{
struct btrfs_trans_handle *trans;
int ret;

trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
errno = -ret;
error_msg(ERROR_MSG_START_TRANS, "%m");
return ret;
}

ret = btrfs_insert_inode_ref(trans, root, backref->name, backref->namelen,
i_rec->ino, backref->dir, backref->index);
if (ret < 0) {
btrfs_commit_transaction(trans, root);
return ret;
}
ret = btrfs_commit_transaction(trans, root);
if (ret < 0) {
ret = PTR_ERR(trans);
errno = -ret;
error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
return ret;
}
backref->found_inode_ref = 1;
backref->errors &= ~REF_ERR_NO_INODE_REF;
printf("Added INODE_REF for root %lld ino %llu parent %llu index %llu name %.*s\n",
btrfs_root_id(root), i_rec->ino, backref->dir, backref->index,
backref->namelen, backref->name);
return 0;
}

static int repair_inode_backrefs(struct btrfs_root *root,
struct inode_record *rec,
struct cache_tree *inode_cache,
Expand All @@ -2370,6 +2411,21 @@ static int repair_inode_backrefs(struct btrfs_root *root,
if (rec->ino == root_dirid && backref->index == 0)
continue;

/*
* Have DIR_INDEX, DIR_ITEM and INODE_ITEM, and even nlinks
* matches with only missing INODE_REF.
*/
if (!backref->found_inode_ref && backref->found_dir_item &&
backref->found_dir_index && rec->found_inode_item &&
rec->found_link == rec->nlink) {
ret = create_inode_ref(root, rec, backref);
if (ret)
break;
repaired++;
list_del(&backref->list);
free(backref);
continue;
}
if (delete &&
((backref->found_dir_index && !backref->found_inode_ref) ||
(backref->found_dir_index && backref->found_inode_ref &&
Expand Down Expand Up @@ -4027,6 +4083,8 @@ static int check_fs_roots(struct cache_tree *root_cache)
free_extent_cache_tree(&wc.shared);
if (!cache_tree_empty(&wc.shared))
fprintf(stderr, "warning line %d\n", __LINE__);
if (!err && found_unknown_key)
err = 1;

return err;
}
Expand Down
16 changes: 14 additions & 2 deletions check/mode-lowmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,17 @@ static int repair_ternary_lowmem(struct btrfs_root *root, u64 dir_ino, u64 ino,
int stage = 0;
int ret = 0;

/*
* We miss an INODE_REF, and we're checking DIR_ITEM and hasn't yet
* find the DIR_INDEX, thus there is no reliable index.
* Try to locate one, this can be slow as we need to locate the DIR_INDEX
* item from the directory.
*/
if (index == (u64)-1 && (err & INODE_REF_MISSING)) {
ret = find_dir_index(root, dir_ino, ino, &index, name, name_len, filetype);
if (ret < 0)
err |= DIR_INDEX_MISSING;
}
/*
* stage shall be one of following valild values:
* 0: Fine, nothing to do.
Expand Down Expand Up @@ -2808,8 +2819,9 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path)
case BTRFS_XATTR_ITEM_KEY:
break;
default:
error("ITEM[%llu %u %llu] UNKNOWN TYPE",
key.objectid, key.type, key.offset);
error("ITEM[%llu %u %llu] UNKNOWN TYPE in leaf %llu",
key.objectid, key.type, key.offset, node->start);
err |= UNKNOWN_KEY;
}
}

Expand Down
1 change: 1 addition & 0 deletions check/mode-lowmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#define INVALID_GENERATION (1U << 26) /* Generation is too new */
#define SUPER_BYTES_USED_ERROR (1U << 27) /* Super bytes_used is invalid */
#define DUP_FILENAME_ERROR (1U << 28) /* DIR_ITEM contains duplicate names */
#define UNKNOWN_KEY (1U << 29) /* Found unknown key type in fs trees */

/*
* Error bit for low memory mode check.
Expand Down
14 changes: 14 additions & 0 deletions tests/fsck-tests/069-unknown-fs-tree-key/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
#
# Verify that check can report unknown key types in subvolume trees

source "$TEST_TOP/common" || exit

check_prereq btrfs

check_image() {
run_mustfail "unknown keys in subvolume trees not reported as error" \
"$TOP/btrfs" check "$1"
}

check_all_images
Binary file not shown.
Empty file.
Binary file not shown.