X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=check%2Fmode-common.c;h=e857d44defa444314db3b9bd2eb2263f2d97fc43;hb=6cedd688115d85e6f14a43f7653378749ef31624;hp=1b56a9684a85f8a1e74f99653c4995e96d7bffe7;hpb=6d3fea8f46568ae9f54bd50f9de2aeeb76e0cd23;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/check/mode-common.c b/check/mode-common.c index 1b56a96..e857d44 100644 --- a/check/mode-common.c +++ b/check/mode-common.c @@ -24,6 +24,262 @@ #include "check/mode-common.h" /* + * Check if the inode referenced by the given data reference uses the extent + * at disk_bytenr as a non-prealloc extent. + * + * Returns 1 if true, 0 if false and < 0 on error. + */ +static int check_prealloc_data_ref(struct btrfs_fs_info *fs_info, + u64 disk_bytenr, + struct btrfs_extent_data_ref *dref, + struct extent_buffer *eb) +{ + u64 rootid = btrfs_extent_data_ref_root(eb, dref); + u64 objectid = btrfs_extent_data_ref_objectid(eb, dref); + u64 offset = btrfs_extent_data_ref_offset(eb, dref); + struct btrfs_root *root; + struct btrfs_key key; + struct btrfs_path path; + int ret; + + btrfs_init_path(&path); + key.objectid = rootid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + root = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto out; + } + + key.objectid = objectid; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = offset; + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret > 0) { + fprintf(stderr, + "Missing file extent item for inode %llu, root %llu, offset %llu", + objectid, rootid, offset); + ret = -ENOENT; + } + if (ret < 0) + goto out; + + while (true) { + struct btrfs_file_extent_item *fi; + int extent_type; + + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(root, &path); + if (ret < 0) + goto out; + if (ret > 0) + break; + } + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.objectid != objectid || + key.type != BTRFS_EXTENT_DATA_KEY) + break; + + fi = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_file_extent_item); + extent_type = btrfs_file_extent_type(path.nodes[0], fi); + if (extent_type != BTRFS_FILE_EXTENT_REG && + extent_type != BTRFS_FILE_EXTENT_PREALLOC) + goto next; + + if (btrfs_file_extent_disk_bytenr(path.nodes[0], fi) != + disk_bytenr) + break; + + if (extent_type == BTRFS_FILE_EXTENT_REG) { + ret = 1; + goto out; + } +next: + path.slots[0]++; + } + ret = 0; + out: + btrfs_release_path(&path); + return ret; +} + +/* + * Check if a shared data reference points to a node that has a file extent item + * pointing to the extent at @disk_bytenr that is not of type prealloc. + * + * Returns 1 if true, 0 if false and < 0 on error. + */ +static int check_prealloc_shared_data_ref(struct btrfs_fs_info *fs_info, + u64 parent, u64 disk_bytenr) +{ + struct extent_buffer *eb; + u32 nr; + int i; + int ret = 0; + + eb = read_tree_block(fs_info, parent, 0); + if (!extent_buffer_uptodate(eb)) { + ret = -EIO; + goto out; + } + + nr = btrfs_header_nritems(eb); + for (i = 0; i < nr; i++) { + struct btrfs_key key; + struct btrfs_file_extent_item *fi; + int extent_type; + + btrfs_item_key_to_cpu(eb, &key, i); + if (key.type != BTRFS_EXTENT_DATA_KEY) + continue; + + fi = btrfs_item_ptr(eb, i, struct btrfs_file_extent_item); + extent_type = btrfs_file_extent_type(eb, fi); + if (extent_type != BTRFS_FILE_EXTENT_REG && + extent_type != BTRFS_FILE_EXTENT_PREALLOC) + continue; + + if (btrfs_file_extent_disk_bytenr(eb, fi) == disk_bytenr && + extent_type == BTRFS_FILE_EXTENT_REG) { + ret = 1; + break; + } + } + out: + free_extent_buffer(eb); + return ret; +} + +/* + * Check if a prealloc extent is shared by multiple inodes and if any inode has + * already written to that extent. This is to avoid emitting invalid warnings + * about odd csum items (a inode has an extent entirely marked as prealloc + * but another inode shares it and has already written to it). + * + * Note: right now it does not check if the number of checksum items in the + * csum tree matches the number of bytes written into the ex-prealloc extent. + * It's complex to deal with that because the prealloc extent might have been + * partially written through multiple inodes and we would have to keep track of + * ranges, merging them and notice ranges that fully or partially overlap, to + * avoid false reports of csum items missing for areas of the prealloc extent + * that were not written to - for example if we have a 1M prealloc extent, we + * can have only the first half of it written, but 2 different inodes refer to + * the its first half (through reflinks/cloning), so keeping a counter of bytes + * covered by checksum items is not enough, as the correct value would be 512K + * and not 1M (whence the need to track ranges). + * + * Returns 0 if the prealloc extent was not written yet by any inode, 1 if + * at least one other inode has written to it, and < 0 on error. + */ +int check_prealloc_extent_written(struct btrfs_fs_info *fs_info, + u64 disk_bytenr, u64 num_bytes) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + struct btrfs_extent_item *ei; + u32 item_size; + unsigned long ptr; + unsigned long end; + + key.objectid = disk_bytenr; + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = num_bytes; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, &path, 0, 0); + if (ret > 0) { + fprintf(stderr, + "Missing extent item in extent tree for disk_bytenr %llu, num_bytes %llu\n", + disk_bytenr, num_bytes); + ret = -ENOENT; + } + if (ret < 0) + goto out; + + /* First check all inline refs. */ + ei = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_extent_item); + item_size = btrfs_item_size_nr(path.nodes[0], path.slots[0]); + ptr = (unsigned long)(ei + 1); + end = (unsigned long)ei + item_size; + while (ptr < end) { + struct btrfs_extent_inline_ref *iref; + int type; + + iref = (struct btrfs_extent_inline_ref *)ptr; + type = btrfs_extent_inline_ref_type(path.nodes[0], iref); + ASSERT(type == BTRFS_EXTENT_DATA_REF_KEY || + type == BTRFS_SHARED_DATA_REF_KEY); + + if (type == BTRFS_EXTENT_DATA_REF_KEY) { + struct btrfs_extent_data_ref *dref; + + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + ret = check_prealloc_data_ref(fs_info, disk_bytenr, + dref, path.nodes[0]); + if (ret != 0) + goto out; + } else if (type == BTRFS_SHARED_DATA_REF_KEY) { + u64 parent; + + parent = btrfs_extent_inline_ref_offset(path.nodes[0], + iref); + ret = check_prealloc_shared_data_ref(fs_info, + parent, + disk_bytenr); + if (ret != 0) + goto out; + } + + ptr += btrfs_extent_inline_ref_size(type); + } + + /* Now check if there are any non-inlined refs. */ + path.slots[0]++; + while (true) { + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_leaf(fs_info->extent_root, &path); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + break; + } + } + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.objectid != disk_bytenr) + break; + + if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { + struct btrfs_extent_data_ref *dref; + + dref = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_extent_data_ref); + ret = check_prealloc_data_ref(fs_info, disk_bytenr, + dref, path.nodes[0]); + if (ret != 0) + goto out; + } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) { + ret = check_prealloc_shared_data_ref(fs_info, + key.offset, + disk_bytenr); + if (ret != 0) + goto out; + } + + path.slots[0]++; + } +out: + btrfs_release_path(&path); + return ret; +} + +/* * Search in csum tree to find how many bytes of range [@start, @start + @len) * has the corresponding csum item. *