diff options
| -rw-r--r-- | fs/btrfs/send.c | 88 |
1 files changed, 86 insertions, 2 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 712922ea64d2..456c8901489b 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
| @@ -5306,6 +5306,81 @@ out: | |||
| 5306 | return ret; | 5306 | return ret; |
| 5307 | } | 5307 | } |
| 5308 | 5308 | ||
| 5309 | static int range_is_hole_in_parent(struct send_ctx *sctx, | ||
| 5310 | const u64 start, | ||
| 5311 | const u64 end) | ||
| 5312 | { | ||
| 5313 | struct btrfs_path *path; | ||
| 5314 | struct btrfs_key key; | ||
| 5315 | struct btrfs_root *root = sctx->parent_root; | ||
| 5316 | u64 search_start = start; | ||
| 5317 | int ret; | ||
| 5318 | |||
| 5319 | path = alloc_path_for_send(); | ||
| 5320 | if (!path) | ||
| 5321 | return -ENOMEM; | ||
| 5322 | |||
| 5323 | key.objectid = sctx->cur_ino; | ||
| 5324 | key.type = BTRFS_EXTENT_DATA_KEY; | ||
| 5325 | key.offset = search_start; | ||
| 5326 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | ||
| 5327 | if (ret < 0) | ||
| 5328 | goto out; | ||
| 5329 | if (ret > 0 && path->slots[0] > 0) | ||
| 5330 | path->slots[0]--; | ||
| 5331 | |||
| 5332 | while (search_start < end) { | ||
| 5333 | struct extent_buffer *leaf = path->nodes[0]; | ||
| 5334 | int slot = path->slots[0]; | ||
| 5335 | struct btrfs_file_extent_item *fi; | ||
| 5336 | u64 extent_end; | ||
| 5337 | |||
| 5338 | if (slot >= btrfs_header_nritems(leaf)) { | ||
| 5339 | ret = btrfs_next_leaf(root, path); | ||
| 5340 | if (ret < 0) | ||
| 5341 | goto out; | ||
| 5342 | else if (ret > 0) | ||
| 5343 | break; | ||
| 5344 | continue; | ||
| 5345 | } | ||
| 5346 | |||
| 5347 | btrfs_item_key_to_cpu(leaf, &key, slot); | ||
| 5348 | if (key.objectid < sctx->cur_ino || | ||
| 5349 | key.type < BTRFS_EXTENT_DATA_KEY) | ||
| 5350 | goto next; | ||
| 5351 | if (key.objectid > sctx->cur_ino || | ||
| 5352 | key.type > BTRFS_EXTENT_DATA_KEY || | ||
| 5353 | key.offset >= end) | ||
| 5354 | break; | ||
| 5355 | |||
| 5356 | fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); | ||
| 5357 | if (btrfs_file_extent_type(leaf, fi) == | ||
| 5358 | BTRFS_FILE_EXTENT_INLINE) { | ||
| 5359 | u64 size = btrfs_file_extent_inline_len(leaf, slot, fi); | ||
| 5360 | |||
| 5361 | extent_end = ALIGN(key.offset + size, | ||
| 5362 | root->fs_info->sectorsize); | ||
| 5363 | } else { | ||
| 5364 | extent_end = key.offset + | ||
| 5365 | btrfs_file_extent_num_bytes(leaf, fi); | ||
| 5366 | } | ||
| 5367 | if (extent_end <= start) | ||
| 5368 | goto next; | ||
| 5369 | if (btrfs_file_extent_disk_bytenr(leaf, fi) == 0) { | ||
| 5370 | search_start = extent_end; | ||
| 5371 | goto next; | ||
| 5372 | } | ||
| 5373 | ret = 0; | ||
| 5374 | goto out; | ||
| 5375 | next: | ||
| 5376 | path->slots[0]++; | ||
| 5377 | } | ||
| 5378 | ret = 1; | ||
| 5379 | out: | ||
| 5380 | btrfs_free_path(path); | ||
| 5381 | return ret; | ||
| 5382 | } | ||
| 5383 | |||
| 5309 | static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path, | 5384 | static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path, |
| 5310 | struct btrfs_key *key) | 5385 | struct btrfs_key *key) |
| 5311 | { | 5386 | { |
| @@ -5350,8 +5425,17 @@ static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path, | |||
| 5350 | return ret; | 5425 | return ret; |
| 5351 | } | 5426 | } |
| 5352 | 5427 | ||
| 5353 | if (sctx->cur_inode_last_extent < key->offset) | 5428 | if (sctx->cur_inode_last_extent < key->offset) { |
| 5354 | ret = send_hole(sctx, key->offset); | 5429 | ret = range_is_hole_in_parent(sctx, |
| 5430 | sctx->cur_inode_last_extent, | ||
| 5431 | key->offset); | ||
| 5432 | if (ret < 0) | ||
| 5433 | return ret; | ||
| 5434 | else if (ret == 0) | ||
| 5435 | ret = send_hole(sctx, key->offset); | ||
| 5436 | else | ||
| 5437 | ret = 0; | ||
| 5438 | } | ||
| 5355 | sctx->cur_inode_last_extent = extent_end; | 5439 | sctx->cur_inode_last_extent = extent_end; |
| 5356 | return ret; | 5440 | return ret; |
| 5357 | } | 5441 | } |
