summaryrefslogtreecommitdiffstats
path: root/source/smbd/vfs-wrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/smbd/vfs-wrap.c')
-rw-r--r--source/smbd/vfs-wrap.c101
1 files changed, 92 insertions, 9 deletions
diff --git a/source/smbd/vfs-wrap.c b/source/smbd/vfs-wrap.c
index 1fd8d07d048..8e579634249 100644
--- a/source/smbd/vfs-wrap.c
+++ b/source/smbd/vfs-wrap.c
@@ -235,21 +235,104 @@ SMB_OFF_T vfswrap_lseek(files_struct *fsp, int filedes, SMB_OFF_T offset, int wh
return result;
}
-int vfswrap_rename(connection_struct *conn, char *old, char *new)
+/*********************************************************
+ For rename across filesystems Patch from Warren Birnbaum
+ <warrenb@hpcvscdp.cv.hp.com>
+**********************************************************/
+
+static int copy_reg(char *source, const char *dest)
{
- int result;
+ SMB_STRUCT_STAT source_stats;
+ int ifd;
+ int ofd;
+
+ if (sys_lstat (source, &source_stats) == -1)
+ return -1;
+
+ if (!S_ISREG (source_stats.st_mode))
+ return -1;
+
+ if (unlink (dest) && errno != ENOENT)
+ return -1;
+
+ if((ifd = sys_open (source, O_RDONLY, 0)) < 0)
+ return -1;
+
+ if((ofd = sys_open (dest, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0 ) {
+ int saved_errno = errno;
+ close (ifd);
+ errno = saved_errno;
+ return -1;
+ }
+
+ if (transfer_file(ifd, ofd, (size_t)-1) == -1) {
+ int saved_errno = errno;
+ close (ifd);
+ close (ofd);
+ unlink (dest);
+ errno = saved_errno;
+ return -1;
+ }
+
+ if (close (ifd) == -1) {
+ int saved_errno = errno;
+ close (ofd);
+ errno = saved_errno;
+ return -1;
+ }
+ if (close (ofd) == -1)
+ return -1;
+
+ /*
+ * chown turns off set[ug]id bits for non-root,
+ * so do the chmod last.
+ */
+
+ /* Try to copy the old file's modtime and access time. */
+ {
+ struct utimbuf tv;
- START_PROFILE(syscall_rename);
+ tv.actime = source_stats.st_atime;
+ tv.modtime = source_stats.st_mtime;
+ utime (dest, &tv);
+ }
+
+ /*
+ * 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.
+ */
+
+ if ((chown(dest, source_stats.st_uid, source_stats.st_gid) == -1) && (errno != EPERM))
+ return -1;
+
+ if (chmod (dest, source_stats.st_mode & 07777))
+ return -1;
+
+ if (unlink (source) == -1)
+ return -1;
+
+ return 0;
+}
+
+int vfswrap_rename(connection_struct *conn, char *oldname, char *newname)
+{
+ int result;
+
+ START_PROFILE(syscall_rename);
#ifdef VFS_CHECK_NULL
- if ((old == NULL) || (new == NULL)) {
- smb_panic("NULL pointer passed to vfswrap_rename()\n");
- }
+ if ((oldname == NULL) || (newname == NULL)) {
+ smb_panic("NULL pointer passed to vfswrap_rename()\n");
+ }
#endif
- result = rename(old, new);
- END_PROFILE(syscall_rename);
- return result;
+ result = rename(oldname, newname);
+ if (errno == EXDEV) {
+ /* Rename across filesystems needed. */
+ result = copy_reg(oldname, newname);
+ }
+ END_PROFILE(syscall_rename);
+ return result;
}
int vfswrap_fsync(files_struct *fsp, int fd)