X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-check.c;h=e746ee7b281dd897c9a1340e613de5582e8ccabf;hb=3f99647f8ed7d3e90dcef65eae4a308558a04621;hp=4528b93234e07aa503b3b19d0acb014134659b59;hpb=c955dbd7fe67df576667d03f2cd58ff06334524e;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-check.c b/cmds-check.c index 4528b93..e746ee7 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -2149,7 +2149,12 @@ static int need_check(struct btrfs_root *root, struct ulist *roots) struct rb_node *node; struct ulist_node *u; - if (roots->nnodes == 1) + /* + * @roots can be empty if it belongs to tree reloc tree + * In that case, we should always check the leaf, as we can't use + * the tree owner to ensure some other root will check it. + */ + if (roots->nnodes == 1 || roots->nnodes == 0) return 1; node = rb_first(&roots->root); @@ -5867,8 +5872,9 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey, *end, fkey->offset - *end); if (!repair || ret) { err |= FILE_EXTENT_ERROR; - error("root %llu EXTENT_DATA[%llu %llu] interrupt", - root->objectid, fkey->objectid, fkey->offset); + error( + "root %llu EXTENT_DATA[%llu %llu] interrupt, should start at %llu", + root->objectid, fkey->objectid, fkey->offset, *end); } } @@ -6622,8 +6628,6 @@ out: return ret; } -static int pin_metadata_blocks(struct btrfs_fs_info *fs_info); - /* * Iterate all items in the tree and call check_inode_item() to check. * @@ -11238,6 +11242,66 @@ static int check_device_used(struct device_record *dev_rec, } } +/* + * Extra (optional) check for dev_item size to report possbile problem on a new + * kernel. + */ +static void check_dev_size_alignment(u64 devid, u64 total_bytes, u32 sectorsize) +{ + if (!IS_ALIGNED(total_bytes, sectorsize)) { + warning( +"unaligned total_bytes detected for devid %llu, have %llu should be aligned to %u", + devid, total_bytes, sectorsize); + warning( +"this is OK for older kernel, but may cause kernel warning for newer kernels"); + warning("this can be fixed by 'btrfs rescue fix-device-size'"); + } +} + +/* + * Unlike device size alignment check above, some super total_bytes check + * failure can lead to mount failure for newer kernel. + * + * So this function will return the error for a fatal super total_bytes problem. + */ +static bool is_super_size_valid(struct btrfs_fs_info *fs_info) +{ + struct btrfs_device *dev; + struct list_head *dev_list = &fs_info->fs_devices->devices; + u64 total_bytes = 0; + u64 super_bytes = btrfs_super_total_bytes(fs_info->super_copy); + + list_for_each_entry(dev, dev_list, dev_list) + total_bytes += dev->total_bytes; + + /* Important check, which can cause unmountable fs */ + if (super_bytes < total_bytes) { + error("super total bytes %llu smaller than real device(s) size %llu", + super_bytes, total_bytes); + error("mounting this fs may fail for newer kernels"); + error("this can be fixed by 'btrfs rescue fix-device-size'"); + return false; + } + + /* + * Optional check, just to make everything aligned and match with each + * other. + * + * For a btrfs-image restored fs, we don't need to check it anyway. + */ + if (btrfs_super_flags(fs_info->super_copy) & + (BTRFS_SUPER_FLAG_METADUMP | BTRFS_SUPER_FLAG_METADUMP_V2)) + return true; + if (!IS_ALIGNED(super_bytes, fs_info->sectorsize) || + !IS_ALIGNED(total_bytes, fs_info->sectorsize) || + super_bytes != total_bytes) { + warning("minor unaligned/mismatch device size detected"); + warning( + "recommended to use 'btrfs rescue fix-device-size' to fix it"); + } + return true; +} + /* check btrfs_dev_item -> btrfs_dev_extent */ static int check_devices(struct rb_root *dev_cache, struct device_extent_tree *dev_extent_cache) @@ -11255,6 +11319,8 @@ static int check_devices(struct rb_root *dev_cache, if (err) ret = err; + check_dev_size_alignment(dev_rec->devid, dev_rec->total_byte, + global_info->sectorsize); dev_node = rb_next(dev_node); } list_for_each_entry(dext_rec, &dev_extent_cache->no_device_orphans, @@ -11580,6 +11646,29 @@ loop: goto again; } +static int check_extent_inline_ref(struct extent_buffer *eb, + struct btrfs_key *key, struct btrfs_extent_inline_ref *iref) +{ + int ret; + u8 type = btrfs_extent_inline_ref_type(eb, iref); + + switch (type) { + case BTRFS_TREE_BLOCK_REF_KEY: + case BTRFS_EXTENT_DATA_REF_KEY: + case BTRFS_SHARED_BLOCK_REF_KEY: + case BTRFS_SHARED_DATA_REF_KEY: + ret = 0; + break; + default: + error("extent[%llu %u %llu] has unknown ref type: %d", + key->objectid, key->type, key->offset, type); + ret = UNKNOWN_TYPE; + break; + } + + return ret; +} + /* * Check backrefs of a tree block given by @bytenr or @eb. * @@ -11611,16 +11700,12 @@ static int check_tree_block_ref(struct btrfs_root *root, u32 nodesize = root->fs_info->nodesize; u32 item_size; u64 offset; - int tree_reloc_root = 0; int found_ref = 0; int err = 0; int ret; int strict = 1; int parent = 0; - if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID && - btrfs_header_bytenr(root->node) == bytenr) - tree_reloc_root = 1; btrfs_init_path(&path); key.objectid = bytenr; if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) @@ -11714,6 +11799,11 @@ static int check_tree_block_ref(struct btrfs_root *root, type = btrfs_extent_inline_ref_type(leaf, iref); offset = btrfs_extent_inline_ref_offset(leaf, iref); + ret = check_extent_inline_ref(leaf, &key, iref); + if (ret) { + err |= ret; + break; + } if (type == BTRFS_TREE_BLOCK_REF_KEY) { if (offset == root->objectid) found_ref = 1; @@ -11723,8 +11813,12 @@ static int check_tree_block_ref(struct btrfs_root *root, /* * Backref of tree reloc root points to itself, no need * to check backref any more. + * + * This may be an error of loop backref, but extent tree + * checker should have already handled it. + * Here we only need to avoid infinite iteration. */ - if (tree_reloc_root) { + if (offset == bytenr) { found_ref = 1; } else { /* @@ -11756,6 +11850,30 @@ static int check_tree_block_ref(struct btrfs_root *root, if (!ret) found_ref = 1; } + /* + * Finally check SHARED BLOCK REF, any found will be good + * Here we're not doing comprehensive extent backref checking, + * only need to ensure there is some extent referring to this + * tree block. + */ + if (!found_ref) { + btrfs_release_path(&path); + key.objectid = bytenr; + key.type = BTRFS_SHARED_BLOCK_REF_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); + if (ret < 0) { + err |= BACKREF_MISSING; + goto out; + } + ret = btrfs_previous_extent_item(extent_root, &path, bytenr); + if (ret) { + err |= BACKREF_MISSING; + goto out; + } + found_ref = 1; + } if (!found_ref) err |= BACKREF_MISSING; out: @@ -11911,11 +12029,11 @@ static int check_extent_data_item(struct btrfs_root *root, u64 disk_num_bytes; u64 extent_num_bytes; u64 extent_flags; + u64 offset; u32 item_size; unsigned long end; unsigned long ptr; int type; - u64 ref_root; int found_dbackref = 0; int slot = pathp->slots[0]; int err = 0; @@ -11933,6 +12051,7 @@ static int check_extent_data_item(struct btrfs_root *root, disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi); disk_num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi); extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi); + offset = btrfs_file_extent_offset(eb, fi); /* Check unaligned disk_num_bytes and num_bytes */ if (!IS_ALIGNED(disk_num_bytes, root->fs_info->sectorsize)) { @@ -11987,15 +12106,31 @@ static int check_extent_data_item(struct btrfs_root *root, strict = should_check_extent_strictly(root, nrefs, -1); while (ptr < end) { + u64 ref_root; + u64 ref_objectid; + u64 ref_offset; + bool match = false; + iref = (struct btrfs_extent_inline_ref *)ptr; type = btrfs_extent_inline_ref_type(leaf, iref); dref = (struct btrfs_extent_data_ref *)(&iref->offset); + ret = check_extent_inline_ref(leaf, &dbref_key, iref); + if (ret) { + err |= ret; + break; + } if (type == BTRFS_EXTENT_DATA_REF_KEY) { ref_root = btrfs_extent_data_ref_root(leaf, dref); - if (ref_root == root->objectid) + ref_objectid = btrfs_extent_data_ref_objectid(leaf, dref); + ref_offset = btrfs_extent_data_ref_offset(leaf, dref); + + if (ref_objectid == fi_key.objectid && + ref_offset == fi_key.offset - offset) + match = true; + if (ref_root == root->objectid && match) found_dbackref = 1; - else if (!strict && owner == ref_root) + else if (!strict && owner == ref_root && match) found_dbackref = 1; } else if (type == BTRFS_SHARED_DATA_REF_KEY) { found_dbackref = !check_tree_block_ref(root, NULL, @@ -12015,7 +12150,7 @@ static int check_extent_data_item(struct btrfs_root *root, dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi); dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY; dbref_key.offset = hash_extent_data_ref(root->objectid, - fi_key.objectid, fi_key.offset); + fi_key.objectid, fi_key.offset - offset); ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &dbref_key, &path, 0, 0); @@ -12372,7 +12507,8 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info, leaf = path.nodes[0]; slot = path.slots[0]; - if (slot >= btrfs_header_nritems(leaf)) + if (slot >= btrfs_header_nritems(leaf) || + btrfs_header_owner(leaf) != root_id) goto next; btrfs_item_key_to_cpu(leaf, &key, slot); if (key.objectid != objectid || key.type != BTRFS_EXTENT_DATA_KEY) @@ -12382,11 +12518,17 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info, * Except normal disk bytenr and disk num bytes, we still * need to do extra check on dbackref offset as * dbackref offset = file_offset - file_extent_offset + * + * Also, we must check the leaf owner. + * In case of shared tree blocks (snapshots) we can inherit + * leaves from source snapshot. + * In that case, reference from source snapshot should not + * count. */ if (btrfs_file_extent_disk_bytenr(leaf, fi) == bytenr && btrfs_file_extent_disk_num_bytes(leaf, fi) == len && (u64)(key.offset - btrfs_file_extent_offset(leaf, fi)) == - offset) + offset && btrfs_header_owner(leaf) == root_id) found_count++; next: @@ -12724,6 +12866,7 @@ static int check_dev_item(struct btrfs_fs_info *fs_info, struct btrfs_path path; struct btrfs_key key; struct btrfs_dev_extent *ptr; + u64 total_bytes; u64 dev_id; u64 used; u64 total = 0; @@ -12732,6 +12875,7 @@ static int check_dev_item(struct btrfs_fs_info *fs_info, dev_item = btrfs_item_ptr(eb, slot, struct btrfs_dev_item); dev_id = btrfs_device_id(eb, dev_item); used = btrfs_device_bytes_used(eb, dev_item); + total_bytes = btrfs_device_total_bytes(eb, dev_item); key.objectid = dev_id; key.type = BTRFS_DEV_EXTENT_KEY; @@ -12776,6 +12920,8 @@ next: BTRFS_DEV_EXTENT_KEY, dev_id); return ACCOUNTING_MISMATCH; } + check_dev_size_alignment(dev_id, total_bytes, fs_info->sectorsize); + return 0; } @@ -13205,8 +13351,6 @@ out: return err; } -static int pin_metadata_blocks(struct btrfs_fs_info *fs_info); - /* * Low memory usage version check_chunks_and_extents. */ @@ -13225,12 +13369,6 @@ static int check_chunks_and_extents_v2(struct btrfs_fs_info *fs_info) root = fs_info->fs_root; if (repair) { - /* pin every tree block to avoid extent overwrite */ - ret = pin_metadata_blocks(fs_info); - if (ret) { - error("failed to pin metadata blocks"); - return ret; - } trans = btrfs_start_transaction(fs_info->extent_root, 1); if (IS_ERR(trans)) { error("failed to start transaction before check"); @@ -13320,6 +13458,12 @@ static int do_check_chunks_and_extents(struct btrfs_fs_info *fs_info) else ret = check_chunks_and_extents(fs_info); + /* Also repair device size related problems */ + if (repair && !ret) { + ret = btrfs_fix_device_and_super_size(fs_info); + if (ret > 0) + ret = 0; + } return ret; } @@ -14839,6 +14983,9 @@ int cmd_check(int argc, char **argv) error( "errors found in extent allocation tree or chunk allocation"); + /* Only re-check super size after we checked and repaired the fs */ + err |= !is_super_size_valid(info); + if (!ctx.progress_enabled) { if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) fprintf(stderr, "checking free space tree\n");