From: Ralph Boehme Date: Thu, 24 Jun 2021 14:21:42 +0000 (+0200) Subject: vfs_default: use copy_file_range() X-Git-Tag: tevent-0.11.0~108 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=accaa2f1f67a7f064a4ce03a120d7b2f8e847ccf;p=thirdparty%2Fsamba.git vfs_default: use copy_file_range() Original file on an XFS filesystem: $ ls -l /mnt/test/1048578-file -rw-rw-r--. 1 slow slow 1048578 Jun 25 11:40 /mnt/test/1048578-file $ xfs_bmap /mnt/test/1048578-file /mnt/test/1048578-file: 0: [0..2055]: 192..2247 Copy created with cp --reflink=never: $ xfs_bmap /mnt/test/1048578-file-reflink-never /mnt/test/1048578-file-reflink-never: 0: [0..2055]: 2248..4303 Copy created with cp --reflink=always $ xfs_bmap /mnt/test/1048578-file-reflink-always /mnt/test/1048578-file-reflink-always: 0: [0..2055]: 192..2247 Copy done from a Windows client: $ xfs_bmap /mnt/test/1048578-file\ -\ Copy /mnt/test/1048578-file - Copy: 0: [0..2055]: 192..2247 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12033 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Wed Jun 30 17:40:23 UTC 2021 on sn-devel-184 --- diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index b4791285d3f..0681c396ca4 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -2050,6 +2050,7 @@ static void vfswrap_offload_write_cleanup(struct tevent_req *req, state->dst_fsp = NULL; } +static NTSTATUS vfswrap_offload_copy_file_range(struct tevent_req *req); static NTSTATUS vfswrap_offload_write_loop(struct tevent_req *req); static struct tevent_req *vfswrap_offload_write_send( @@ -2189,6 +2190,16 @@ static struct tevent_req *vfswrap_offload_write_send( return tevent_req_post(req, ev); } + status = vfswrap_offload_copy_file_range(req); + if (NT_STATUS_IS_OK(status)) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + state->buf = talloc_array(state, uint8_t, num); if (tevent_req_nomem(state->buf, req)) { return tevent_req_post(req, ev); @@ -2203,6 +2214,129 @@ static struct tevent_req *vfswrap_offload_write_send( return req; } +static NTSTATUS vfswrap_offload_copy_file_range(struct tevent_req *req) +{ + struct vfswrap_offload_write_state *state = tevent_req_data( + req, struct vfswrap_offload_write_state); + struct lock_struct lck; + ssize_t nwritten; + NTSTATUS status; + bool same_file; + bool ok; + +#ifndef USE_COPY_FILE_RANGE + return NT_STATUS_MORE_PROCESSING_REQUIRED; +#endif + + same_file = file_id_equal(&state->src_fsp->file_id, + &state->dst_fsp->file_id); + if (same_file && + sys_io_ranges_overlap(state->remaining, + state->src_off, + state->remaining, + state->dst_off)) + { + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + if (is_named_stream(state->src_fsp->fsp_name) || + is_named_stream(state->dst_fsp->fsp_name)) + { + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + init_strict_lock_struct(state->src_fsp, + state->src_fsp->op->global->open_persistent_id, + state->src_off, + state->remaining, + READ_LOCK, + &lck); + + ok = SMB_VFS_STRICT_LOCK_CHECK(state->src_fsp->conn, + state->src_fsp, + &lck); + if (!ok) { + return NT_STATUS_FILE_LOCK_CONFLICT; + } + + ok = change_to_user_and_service_by_fsp(state->dst_fsp); + if (!ok) { + return NT_STATUS_INTERNAL_ERROR; + } + + init_strict_lock_struct(state->dst_fsp, + state->dst_fsp->op->global->open_persistent_id, + state->dst_off, + state->remaining, + WRITE_LOCK, + &lck); + + ok = SMB_VFS_STRICT_LOCK_CHECK(state->dst_fsp->conn, + state->dst_fsp, + &lck); + if (!ok) { + return NT_STATUS_FILE_LOCK_CONFLICT; + } + + while (state->remaining > 0) { + nwritten = copy_file_range(fsp_get_pathref_fd(state->src_fsp), + &state->src_off, + fsp_get_pathref_fd(state->dst_fsp), + &state->dst_off, + state->remaining, + 0); + if (nwritten == -1) { + DBG_DEBUG("copy_file_range src [%s]:[%jd] dst [%s]:[%jd] " + "n [%jd] failed: %s\n", + fsp_str_dbg(state->src_fsp), + (intmax_t)state->src_off, + fsp_str_dbg(state->dst_fsp), + (intmax_t)state->dst_off, + (intmax_t)state->remaining, + strerror(errno)); + switch (errno) { + case EXDEV: + status = NT_STATUS_MORE_PROCESSING_REQUIRED; + break; + default: + status = map_nt_error_from_unix(errno); + if (NT_STATUS_EQUAL( + status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) + { + /* Avoid triggering the fallback */ + status = NT_STATUS_INTERNAL_ERROR; + } + break; + } + return status; + } + + if (state->remaining < nwritten) { + DBG_DEBUG("copy_file_range src [%s] dst [%s] " + "n [%jd] remaining [%jd]\n", + fsp_str_dbg(state->src_fsp), + fsp_str_dbg(state->dst_fsp), + (intmax_t)nwritten, + (intmax_t)state->remaining); + return NT_STATUS_INTERNAL_ERROR; + } + + if (nwritten == 0) { + break; + } + state->copied += nwritten; + state->remaining -= nwritten; + } + + /* + * Tell the req cleanup function there's no need to call + * change_to_user_and_service_by_fsp() on the dst handle. + */ + state->dst_fsp = NULL; + return NT_STATUS_OK; +} + static void vfswrap_offload_write_read_done(struct tevent_req *subreq); static NTSTATUS vfswrap_offload_write_loop(struct tevent_req *req)