s3: VFS: crossrename. Use real dirfsp for SMB_VFS_RENAMEAT()
authorJeremy Allison <jra@samba.org>
Thu, 17 Jun 2021 04:01:33 +0000 (21:01 -0700)
committerNoel Power <npower@samba.org>
Tue, 22 Jun 2021 13:44:34 +0000 (13:44 +0000)
Finally fix the promise from the docs that this module is stackable. Re-use copy_internals().

This is a horrible module that must be removed !

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Noel Power <npower@samba.org>
source3/modules/vfs_crossrename.c

index 4e512655c33f030579c57cf8063507429b0466db..52b8af9d3f61a1997bbe0b15d89cab5eca44b3e3 100644 (file)
@@ -48,123 +48,94 @@ static int crossrename_connect(
  <warrenb@hpcvscdp.cv.hp.com>
 **********************************************************/
 
-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;
-
-       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((ifd = open (source, O_RDONLY, 0)) < 0)
-               return -1;
+       NTSTATUS status;
+       struct smb_filename *full_fname_src = NULL;
+       struct smb_filename *full_fname_dst = NULL;
+       int ret;
 
-       if (unlink (dest) && errno != ENOENT) {
-               close(ifd);
-               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;
        }
 
-#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;
-
-       if (transfer_file(ifd, ofd, source_stats.st_ex_size) == -1)
-               goto err;
+       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;
+       }
 
-       /*
-        * 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.
-        */
+       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;
+       }
 
-#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;
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                                   dstfsp,
+                                   dest,
+                                   0);
+       if (ret == -1) {
+               status = map_nt_error_from_unix(errno);
+               goto out;
+       }
 
        /*
-        * fchown turns off set[ug]id bits for non-root,
-        * so do the chmod last.
+        * 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."
         */
-
-#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);
-       }
-#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
+       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_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);
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                                   srcfsp,
+                                   source,
+                                   0);
+       if (ret == -1) {
+               status = map_nt_error_from_unix(errno);
+               goto out;
        }
-#endif
-
-       if (unlink (source) == -1)
-               return -1;
 
-       return 0;
-
-  err:
+  out:
 
-       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: