diff --git a/source3/modules/vfs_crossrename.c b/source3/modules/vfs_crossrename.c index 4e512655c33..52b8af9d3f6 100644 --- a/source3/modules/vfs_crossrename.c +++ b/source3/modules/vfs_crossrename.c @@ -48,123 +48,94 @@ static int crossrename_connect( **********************************************************/ -static int copy_reg(const char *source, const char *dest) +static NTSTATUS copy_reg(vfs_handle_struct *handle, + struct files_struct *srcfsp, + const struct smb_filename *source, + struct files_struct *dstfsp, + const struct smb_filename *dest) { - SMB_STRUCT_STAT source_stats; - int saved_errno; - int ifd = -1; - int ofd = -1; + NTSTATUS status; + struct smb_filename *full_fname_src = NULL; + struct smb_filename *full_fname_dst = NULL; + int ret; - if (sys_lstat(source, &source_stats, false) == -1) - return -1; - - if (!S_ISREG (source_stats.st_ex_mode)) - return -1; - - if (source_stats.st_ex_size > module_sizelimit) { - DEBUG(5, - ("%s: size of %s larger than sizelimit (%lld > %lld), rename prohititted\n", - MODULE, source, - (long long)source_stats.st_ex_size, - (long long)module_sizelimit)); - return -1; + if (!VALID_STAT(source->st)) { + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto out; + } + if (!S_ISREG(source->st.st_ex_mode)) { + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto out; } - if((ifd = open (source, O_RDONLY, 0)) < 0) - return -1; - - if (unlink (dest) && errno != ENOENT) { - close(ifd); - return -1; + if (source->st.st_ex_size > module_sizelimit) { + DBG_INFO("%s: size of %s larger than sizelimit (%lld > %lld), " + "rename prohibited\n", + MODULE, + source->base_name, + (long long)source->st.st_ex_size, + (long long)module_sizelimit); + status = NT_STATUS_INVALID_PARAMETER; + goto out; } -#ifdef O_NOFOLLOW - if((ofd = open (dest, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0600)) < 0 ) -#else - if((ofd = open (dest, O_WRONLY | O_CREAT | O_TRUNC , 0600)) < 0 ) -#endif - goto err; + full_fname_src = full_path_from_dirfsp_atname(talloc_tos(), + srcfsp, + source); + if (full_fname_dst == 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; + } - if (transfer_file(ifd, ofd, source_stats.st_ex_size) == -1) - goto err; + ret = SMB_VFS_NEXT_UNLINKAT(handle, + dstfsp, + dest, + 0); + if (ret == -1) { + status = map_nt_error_from_unix(errno); + goto out; + } /* - * Try to preserve ownership. For non-root it might fail, but that's ok. - * But root probably wants to know, e.g. if NFS disallows it. + * 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." */ - -#ifdef HAVE_FCHOWN - if ((fchown(ofd, source_stats.st_ex_uid, source_stats.st_ex_gid) == -1) && (errno != EPERM)) -#else - if ((chown(dest, source_stats.st_ex_uid, source_stats.st_ex_gid) == -1) && (errno != EPERM)) -#endif - goto err; - - /* - * fchown turns off set[ug]id bits for non-root, - * so do the chmod last. - */ - -#if defined(HAVE_FCHMOD) - if ((fchmod (ofd, source_stats.st_ex_mode & 07777) == -1) && - (errno != EPERM)) -#else - if ((chmod (dest, source_stats.st_ex_mode & 07777) == -1) && - (errno != EPERM)) -#endif - goto err; - - if (close (ifd) == -1) - goto err; - - if (close (ofd) == -1) - return -1; - - /* Try to copy the old file's modtime and access time. */ -#if defined(HAVE_UTIMENSAT) - { - struct timespec ts[2]; - - ts[0] = source_stats.st_ex_atime; - ts[1] = source_stats.st_ex_mtime; - utimensat(AT_FDCWD, dest, ts, AT_SYMLINK_NOFOLLOW); + status = copy_internals(talloc_tos(), + handle->conn, + NULL, + full_fname_src, + full_fname_dst, + FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + if (!NT_STATUS_IS_OK(status)) { + goto out; } -#elif defined(HAVE_UTIMES) - { - struct timeval tv[2]; - tv[0] = convert_timespec_to_timeval(source_stats.st_ex_atime); - tv[1] = convert_timespec_to_timeval(source_stats.st_ex_mtime); -#ifdef HAVE_LUTIMES - lutimes(dest, tv); -#else - utimes(dest, tv); -#endif + ret = SMB_VFS_NEXT_UNLINKAT(handle, + srcfsp, + source, + 0); + if (ret == -1) { + status = map_nt_error_from_unix(errno); + goto out; } -#elif defined(HAVE_UTIME) - { - struct utimbuf tv; - tv.actime = convert_timespec_to_time_t(source_stats.st_ex_atime); - tv.modtime = convert_timespec_to_time_t(source_stats.st_ex_mtime); - utime(dest, &tv); - } -#endif + out: - if (unlink (source) == -1) - return -1; - - return 0; - - err: - - saved_errno = errno; - if (ifd != -1) - close(ifd); - if (ofd != -1) - close(ofd); - errno = saved_errno; - return -1; + TALLOC_FREE(full_fname_src); + TALLOC_FREE(full_fname_dst); + return status; } static int crossrename_renameat(vfs_handle_struct *handle, @@ -182,11 +153,23 @@ static int crossrename_renameat(vfs_handle_struct *handle, goto out; } - result = rename(smb_fname_src->base_name, smb_fname_dst->base_name); + result = SMB_VFS_NEXT_RENAMEAT(handle, + srcfsp, + smb_fname_src, + dstfsp, + smb_fname_dst); + if ((result == -1) && (errno == EXDEV)) { /* Rename across filesystems needed. */ - result = copy_reg(smb_fname_src->base_name, - smb_fname_dst->base_name); + NTSTATUS status = copy_reg(handle, + srcfsp, + smb_fname_src, + dstfsp, + smb_fname_dst); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + result = -1; + } } out: