diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index afdcbe7844e0..a2858bc678f4 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -5269,6 +5269,22 @@ static int send_capabilities(struct send_ctx *sctx) return ret; } +/* + * If we are cloning from the file we are currently processing, and using the + * send root as the clone root, we must stop once the current clone offset + * reaches the current eof of the file at the receiver, otherwise we would issue + * an invalid clone operation (source range going beyond eof) and cause the + * receiver to fail. So if we reach the current eof, bail out and fallback to a + * regular write. + */ +static inline bool abort_self_clone(const struct send_ctx *sctx, + const struct clone_root *clone_root) +{ + return (clone_root->root == sctx->send_root && + clone_root->ino == sctx->cur_ino && + clone_root->offset >= sctx->cur_inode_next_write_offset); +} + static int clone_range(struct send_ctx *sctx, struct clone_root *clone_root, const u64 disk_byte, @@ -5404,6 +5420,8 @@ static int clone_range(struct send_ctx *sctx, break; offset += hole_len; clone_root->offset += hole_len; + if (abort_self_clone(sctx, clone_root)) + break; data_offset += hole_len; } @@ -5427,6 +5445,8 @@ static int clone_range(struct send_ctx *sctx, ext_len -= extent_offset; clone_data_offset += extent_offset; clone_root->offset += extent_offset; + if (abort_self_clone(sctx, clone_root)) + break; } } @@ -5486,21 +5506,8 @@ static int clone_range(struct send_ctx *sctx, break; offset += clone_len; clone_root->offset += clone_len; - - /* - * If we are cloning from the file we are currently processing, - * and using the send root as the clone root, we must stop once - * the current clone offset reaches the current eof of the file - * at the receiver, otherwise we would issue an invalid clone - * operation (source range going beyond eof) and cause the - * receiver to fail. So if we reach the current eof, bail out - * and fallback to a regular write. - */ - if (clone_root->root == sctx->send_root && - clone_root->ino == sctx->cur_ino && - clone_root->offset >= sctx->cur_inode_next_write_offset) + if (abort_self_clone(sctx, clone_root)) break; - data_offset += clone_len; next: path->slots[0]++;