diff --git a/check/main.c b/check/main.c index db055ae19..880827e11 100644 --- a/check/main.c +++ b/check/main.c @@ -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 { @@ -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; }; } @@ -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, @@ -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 && @@ -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; } diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index ea4d40178..dcec6cc06 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c @@ -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. @@ -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; } } diff --git a/check/mode-lowmem.h b/check/mode-lowmem.h index cd0355465..ec199b4c8 100644 --- a/check/mode-lowmem.h +++ b/check/mode-lowmem.h @@ -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. diff --git a/tests/fsck-tests/069-unknown-fs-tree-key/test.sh b/tests/fsck-tests/069-unknown-fs-tree-key/test.sh new file mode 100755 index 000000000..ea4f04e28 --- /dev/null +++ b/tests/fsck-tests/069-unknown-fs-tree-key/test.sh @@ -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 diff --git a/tests/fsck-tests/069-unknown-fs-tree-key/unknown_key_empty.img.xz b/tests/fsck-tests/069-unknown-fs-tree-key/unknown_key_empty.img.xz new file mode 100644 index 000000000..300a7cc92 Binary files /dev/null and b/tests/fsck-tests/069-unknown-fs-tree-key/unknown_key_empty.img.xz differ diff --git a/tests/fsck-tests/070-missing-inode-ref/.lowmem_repairable b/tests/fsck-tests/070-missing-inode-ref/.lowmem_repairable new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fsck-tests/070-missing-inode-ref/default.img.xz b/tests/fsck-tests/070-missing-inode-ref/default.img.xz new file mode 100644 index 000000000..15d95eed3 Binary files /dev/null and b/tests/fsck-tests/070-missing-inode-ref/default.img.xz differ