Skip to content

Commit

Permalink
Btrfs: fix btrfs send for inline items and compression
Browse files Browse the repository at this point in the history
The btrfs send code was assuming the offset of the file item into the
extent translated to bytes on disk.  If we're compressed, this isn't
true, and so it was off into extents owned by other files.

It was also improperly handling inline extents.  This solves a crash
where we may have gone past the end of the file extent item by not
testing early enough for an inline extent.  It also solves problems
where we have a whole between the end of the inline item and the start
of the full extent.

Signed-off-by: Chris Mason <chris.mason@fusionio.com>
  • Loading branch information
Chris Mason committed Oct 1, 2012
1 parent 6d85ed0 commit 74dd17f
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 15 deletions.
5 changes: 4 additions & 1 deletion fs/btrfs/ctree.c
Original file line number Diff line number Diff line change
Expand Up @@ -5073,6 +5073,7 @@ static void tree_move_down(struct btrfs_root *root,
struct btrfs_path *path,
int *level, int root_level)
{
BUG_ON(*level == 0);
path->nodes[*level - 1] = read_node_slot(root, path->nodes[*level],
path->slots[*level]);
path->slots[*level - 1] = 0;
Expand All @@ -5089,7 +5090,7 @@ static int tree_move_next_or_upnext(struct btrfs_root *root,

path->slots[*level]++;

while (path->slots[*level] == nritems) {
while (path->slots[*level] >= nritems) {
if (*level == root_level)
return -1;

Expand Down Expand Up @@ -5433,9 +5434,11 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
goto out;
advance_right = ADVANCE;
} else {
WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
ret = tree_compare_item(left_root, left_path,
right_path, tmp_buf);
if (ret) {
WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
ret = changed_cb(left_root, right_root,
left_path, right_path,
&left_key,
Expand Down
1 change: 0 additions & 1 deletion fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -4332,7 +4332,6 @@ static int release_extent_buffer(struct extent_buffer *eb, gfp_t mask)

/* Should be safe to release our pages at this point */
btrfs_release_extent_buffer_page(eb, 0);

call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
return 1;
}
Expand Down
46 changes: 33 additions & 13 deletions fs/btrfs/send.c
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,7 @@ static int find_extent_clone(struct send_ctx *sctx,
int ret;
int extent_type;
u64 logical;
u64 disk_byte;
u64 num_bytes;
u64 extent_item_pos;
struct btrfs_file_extent_item *fi;
Expand All @@ -1157,6 +1158,7 @@ static int find_extent_clone(struct send_ctx *sctx,
struct clone_root *cur_clone_root;
struct btrfs_key found_key;
struct btrfs_path *tmp_path;
int compressed;
u32 i;

tmp_path = alloc_path_for_send();
Expand Down Expand Up @@ -1186,17 +1188,18 @@ static int find_extent_clone(struct send_ctx *sctx,
ret = -ENOENT;
goto out;
}
compressed = btrfs_file_extent_compression(eb, fi);

num_bytes = btrfs_file_extent_num_bytes(eb, fi);
logical = btrfs_file_extent_disk_bytenr(eb, fi);
if (logical == 0) {
disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
if (disk_byte == 0) {
ret = -ENOENT;
goto out;
}
logical += btrfs_file_extent_offset(eb, fi);
logical = disk_byte + btrfs_file_extent_offset(eb, fi);

ret = extent_from_logical(sctx->send_root->fs_info,
logical, tmp_path, &found_key);
disk_byte, tmp_path, &found_key);
btrfs_release_path(tmp_path);

if (ret < 0)
Expand Down Expand Up @@ -1234,10 +1237,16 @@ static int find_extent_clone(struct send_ctx *sctx,
/*
* Now collect all backrefs.
*/
if (compressed == BTRFS_COMPRESS_NONE)
extent_item_pos = logical - found_key.objectid;
else
extent_item_pos = 0;

extent_item_pos = logical - found_key.objectid;
ret = iterate_extent_inodes(sctx->send_root->fs_info,
found_key.objectid, extent_item_pos, 1,
__iterate_backrefs, backref_ctx);

if (ret < 0)
goto out;

Expand All @@ -1246,8 +1255,8 @@ static int find_extent_clone(struct send_ctx *sctx,
ret = -EIO;
printk(KERN_ERR "btrfs: ERROR did not find backref in "
"send_root. inode=%llu, offset=%llu, "
"logical=%llu\n",
ino, data_offset, logical);
"disk_byte=%llu found extent=%llu\n",
ino, data_offset, disk_byte, found_key.objectid);
goto out;
}

Expand Down Expand Up @@ -3678,10 +3687,17 @@ static int send_write_or_clone(struct send_ctx *sctx,
ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_file_extent_item);
type = btrfs_file_extent_type(path->nodes[0], ei);
if (type == BTRFS_FILE_EXTENT_INLINE)
if (type == BTRFS_FILE_EXTENT_INLINE) {
len = btrfs_file_extent_inline_len(path->nodes[0], ei);
else
/*
* it is possible the inline item won't cover the whole page,
* but there may be items after this page. Make
* sure to send the whole thing
*/
len = PAGE_CACHE_ALIGN(len);
} else {
len = btrfs_file_extent_num_bytes(path->nodes[0], ei);
}

if (offset + len > sctx->cur_inode_size)
len = sctx->cur_inode_size - offset;
Expand Down Expand Up @@ -3729,6 +3745,8 @@ static int is_extent_unchanged(struct send_ctx *sctx,
u64 left_offset_fixed;
u64 left_len;
u64 right_len;
u64 left_gen;
u64 right_gen;
u8 left_type;
u8 right_type;

Expand All @@ -3738,17 +3756,17 @@ static int is_extent_unchanged(struct send_ctx *sctx,

eb = left_path->nodes[0];
slot = left_path->slots[0];

ei = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
left_type = btrfs_file_extent_type(eb, ei);
left_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
left_len = btrfs_file_extent_num_bytes(eb, ei);
left_offset = btrfs_file_extent_offset(eb, ei);

if (left_type != BTRFS_FILE_EXTENT_REG) {
ret = 0;
goto out;
}
left_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
left_len = btrfs_file_extent_num_bytes(eb, ei);
left_offset = btrfs_file_extent_offset(eb, ei);
left_gen = btrfs_file_extent_generation(eb, ei);

/*
* Following comments will refer to these graphics. L is the left
Expand Down Expand Up @@ -3804,6 +3822,7 @@ static int is_extent_unchanged(struct send_ctx *sctx,
right_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
right_len = btrfs_file_extent_num_bytes(eb, ei);
right_offset = btrfs_file_extent_offset(eb, ei);
right_gen = btrfs_file_extent_generation(eb, ei);

if (right_type != BTRFS_FILE_EXTENT_REG) {
ret = 0;
Expand Down Expand Up @@ -3832,7 +3851,8 @@ static int is_extent_unchanged(struct send_ctx *sctx,
* Check if we have the same extent.
*/
if (left_disknr != right_disknr ||
left_offset_fixed != right_offset) {
left_offset_fixed != right_offset ||
left_gen != right_gen) {
ret = 0;
goto out;
}
Expand Down

0 comments on commit 74dd17f

Please sign in to comment.