From 197578b4d6923c8839debc1d23b69fada784a844 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pavel=20Filipensk=C3=BD?= Date: Thu, 28 Nov 2024 18:39:53 +0100 Subject: [PATCH] s3:vfs_crossrename: avoid locking panic in copy_reg() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Use low level backend functions that don't go through the FSA layer. Done via calling transfer_file() as it was in version before 5c18f07 BUG: https://bugzilla.samba.org/show_bug.cgi?id=15724 Signed-off-by: Pavel Filipenský Reviewed-by: Ralph Boehme (cherry picked from commit 0a5da82f75a43838be3419cab10a50750fa500d7) --- source3/modules/vfs_crossrename.c | 125 ++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 41 deletions(-) diff --git a/source3/modules/vfs_crossrename.c b/source3/modules/vfs_crossrename.c index 042144bfc4d..7c0ae94d257 100644 --- a/source3/modules/vfs_crossrename.c +++ b/source3/modules/vfs_crossrename.c @@ -54,10 +54,12 @@ static NTSTATUS copy_reg(vfs_handle_struct *handle, struct files_struct *dstfsp, const struct smb_filename *dest) { - NTSTATUS status; - struct smb_filename *full_fname_src = NULL; - struct smb_filename *full_fname_dst = NULL; + NTSTATUS status = NT_STATUS_OK; int ret; + off_t off; + int ifd = -1; + int ofd = -1; + struct timespec ts[2]; if (!VALID_STAT(source->st)) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; @@ -79,21 +81,6 @@ static NTSTATUS copy_reg(vfs_handle_struct *handle, goto out; } - full_fname_src = full_path_from_dirfsp_atname(talloc_tos(), - srcfsp, - source); - if (full_fname_src == NULL) { - status = NT_STATUS_NO_MEMORY; - goto out; - } - full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(), - dstfsp, - dest); - if (full_fname_dst == NULL) { - status = NT_STATUS_NO_MEMORY; - goto out; - } - ret = SMB_VFS_NEXT_UNLINKAT(handle, dstfsp, dest, @@ -103,40 +90,96 @@ static NTSTATUS copy_reg(vfs_handle_struct *handle, goto out; } + ifd = openat(fsp_get_pathref_fd(srcfsp), + source->base_name, + O_RDONLY, + 0); + if (ifd < 0) { + status = map_nt_error_from_unix(errno); + goto out; + } + + ofd = openat(fsp_get_pathref_fd(dstfsp), + dest->base_name, + O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, + 0600); + if (ofd < 0) { + status = map_nt_error_from_unix(errno); + goto out; + } + + off = transfer_file(ifd, ofd, source->st.st_ex_size); + if (off == -1) { + status = map_nt_error_from_unix(errno); + goto out; + } + + ret = fchown(ofd, source->st.st_ex_uid, source->st.st_ex_gid); + if (ret == -1 && errno != EPERM) { + status = map_nt_error_from_unix(errno); + goto out; + } + /* - * copy_internals() takes attribute values from the NTrename call. - * - * From MS-CIFS: - * - * "If the attribute is 0x0000, then only normal files are renamed. - * If the system file or hidden attributes are specified, then the - * rename is inclusive of both special types." + * fchown turns off set[ug]id bits for non-root, + * so do the chmod last. */ - status = copy_internals(talloc_tos(), - handle->conn, - NULL, - srcfsp, /* src_dirfsp */ - full_fname_src, - dstfsp, /* dst_dirfsp */ - full_fname_dst, - FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); - if (!NT_STATUS_IS_OK(status)) { + ret = fchmod(ofd, source->st.st_ex_mode & 07777); + if (ret == -1 && errno != EPERM) { + status = map_nt_error_from_unix(errno); goto out; } - ret = SMB_VFS_NEXT_UNLINKAT(handle, - srcfsp, - source, - 0); + /* Try to copy the old file's modtime and access time. */ + ts[0] = source->st.st_ex_atime; + ts[1] = source->st.st_ex_mtime; + ret = futimens(ofd, ts); + if (ret == -1) { + DBG_DEBUG("Updating the time stamp on destinaton '%s' failed " + "with '%s'. Rename operation can continue.\n", + dest->base_name, + strerror(errno)); + } + + ret = close(ifd); + if (ret == -1) { + status = map_nt_error_from_unix(errno); + goto out; + } + ifd = -1; + + ret = close(ofd); if (ret == -1) { status = map_nt_error_from_unix(errno); goto out; } + ofd = -1; + + ret = SMB_VFS_NEXT_UNLINKAT(handle, srcfsp, source, 0); + if (ret == -1) { + status = map_nt_error_from_unix(errno); + } - out: +out: + if (ifd != -1) { + ret = close(ifd); + if (ret == -1) { + DBG_DEBUG("Failed to close %s (%d): %s.\n", + source->base_name, + ifd, + strerror(errno)); + } + } + if (ofd != -1) { + ret = close(ofd); + if (ret == -1) { + DBG_DEBUG("Failed to close %s (%d): %s.\n", + dest->base_name, + ofd, + strerror(errno)); + } + } - TALLOC_FREE(full_fname_src); - TALLOC_FREE(full_fname_dst); return status; } -- 2.47.2