diff --git a/cmds-check.c b/cmds-check.c index c0a4d630..45e3d216 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -112,6 +112,7 @@ struct extent_record { unsigned int owner_ref_checked:1; unsigned int is_root:1; unsigned int metadata:1; + unsigned int flag_block_full_backref:1; }; struct inode_backref { @@ -5153,6 +5154,127 @@ static int is_dropped_key(struct btrfs_key *key, return 0; } +static int calc_extent_flag(struct btrfs_root *root, + struct cache_tree *extent_cache, + struct extent_buffer *buf, + struct root_item_record *ri, + u64 *flags) +{ + int i; + int nritems = btrfs_header_nritems(buf); + struct btrfs_key key; + struct extent_record *rec; + struct cache_extent *cache; + struct data_backref *dback; + struct tree_backref *tback; + struct extent_buffer *new_buf; + u64 owner = 0; + u64 bytenr; + u64 offset; + u64 ptr; + int size; + int ret; + u8 level; + + /* + * Except file/reloc tree, we can not have + * FULL BACKREF MODE + */ + if (ri->objectid < BTRFS_FIRST_FREE_OBJECTID) + goto normal; + /* + * root node + */ + if (buf->start == ri->bytenr) + goto normal; + if (btrfs_is_leaf(buf)) { + /* + * we are searching from original root, world + * peace is achieved, we use normal backref. + */ + owner = btrfs_header_owner(buf); + if (owner == ri->objectid) + goto normal; + /* + * we check every eb here, and if any of + * eb dosen't have original root refers + * to this eb, we set full backref flag for + * this extent, otherwise normal backref. + */ + for (i = 0; i < nritems; i++) { + struct btrfs_file_extent_item *fi; + btrfs_item_key_to_cpu(buf, &key, i); + + if (key.type != BTRFS_EXTENT_DATA_KEY) + continue; + fi = btrfs_item_ptr(buf, i, + struct btrfs_file_extent_item); + if (btrfs_file_extent_type(buf, fi) == + BTRFS_FILE_EXTENT_INLINE) + continue; + if (btrfs_file_extent_disk_bytenr(buf, fi) == 0) + continue; + bytenr = btrfs_file_extent_disk_bytenr(buf, fi); + cache = lookup_cache_extent(extent_cache, bytenr, 1); + if (!cache) + goto full_backref; + offset = btrfs_file_extent_offset(buf, fi); + rec = container_of(cache, struct extent_record, cache); + dback = find_data_backref(rec, 0, ri->objectid, owner, + key.offset - offset, 1, bytenr, bytenr); + if (!dback) + goto full_backref; + } + goto full_backref; + } else { + level = btrfs_header_level(buf); + for (i = 0; i < nritems; i++) { + ptr = btrfs_node_blockptr(buf, i); + size = btrfs_level_size(root, level); + if (i == 0) { + new_buf = read_tree_block(root, ptr, size, 0); + if (!extent_buffer_uptodate(new_buf)) { + free_extent_buffer(new_buf); + ret = -EIO; + return ret; + } + /* + * we are searching from origin root, world + * peace is achieved, we use normal backref. + */ + owner = btrfs_header_owner(new_buf); + free_extent_buffer(new_buf); + if (owner == ri->objectid) + goto normal; + } + cache = lookup_cache_extent(extent_cache, ptr, size); + if (!cache) + goto full_backref; + rec = container_of(cache, struct extent_record, cache); + tback = find_tree_backref(rec, 0, owner); + if (!tback) + goto full_backref; + } + + } +normal: + *flags = 0; + cache = lookup_cache_extent(extent_cache, buf->start, 1); + /* we have added this extent before */ + BUG_ON(!cache); + rec = container_of(cache, struct extent_record, cache); + rec->flag_block_full_backref = 0; + return 0; +full_backref: + *flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; + cache = lookup_cache_extent(extent_cache, buf->start, 1); + /* we have added this extent before */ + BUG_ON(!cache); + rec = container_of(cache, struct extent_record, cache); + rec->flag_block_full_backref = 1; + return 0; +} + static int run_next_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct block_info *bits, @@ -5247,9 +5369,12 @@ static int run_next_block(struct btrfs_trans_handle *trans, btrfs_header_level(buf), 1, NULL, &flags); if (ret < 0) - flags = 0; + goto out; } else { flags = 0; + ret = calc_extent_flag(root, extent_cache, buf, ri, &flags); + if (ret < 0) + goto out; } if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { @@ -6439,9 +6564,10 @@ static int fixup_extent_refs(struct btrfs_trans_handle *trans, rec->start, rec->max_size, rec->metadata, NULL, &flags); if (ret < 0) - flags = 0; + return ret; } else { - flags = 0; + if (rec->flag_block_full_backref) + flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; } path = btrfs_alloc_path();