diff options
author | Jeremy Allison <jra@samba.org> | 2003-11-21 23:01:37 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2003-11-21 23:01:37 +0000 |
commit | d66def408ea394fe6475e87cd2405173eb7c8c8c (patch) | |
tree | d5794dcde59174d886a9ef24c003dd7a21f8b120 | |
parent | bdea2e8a47f98d7146f5d1dc4e74b16097308d4c (diff) | |
download | samba-d66def408ea394fe6475e87cd2405173eb7c8c8c.tar.gz samba-d66def408ea394fe6475e87cd2405173eb7c8c8c.tar.bz2 samba-d66def408ea394fe6475e87cd2405173eb7c8c8c.zip |
Fix for rename across filesystems. Noticed by Rainer Link <link@foo.fh-furtwangen.de>.
Jeremy.
(This used to be commit 598d9d3e5f612133e0528a1a8907745d715b61ed)
-rw-r--r-- | source3/smbd/vfs-wrap.c | 100 |
1 files changed, 95 insertions, 5 deletions
diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c index a76a7a6abd..378a0a1e53 100644 --- a/source3/smbd/vfs-wrap.c +++ b/source3/smbd/vfs-wrap.c @@ -237,14 +237,104 @@ ssize_t vfswrap_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, return result; } +/********************************************************* + For rename across filesystems Patch from Warren Birnbaum + <warrenb@hpcvscdp.cv.hp.com> +**********************************************************/ + +static int copy_reg(const char *source, const char *dest) +{ + SMB_STRUCT_STAT source_stats; + int saved_errno; + int ifd = -1; + int ofd = -1; + + if (sys_lstat (source, &source_stats) == -1) + return -1; + + if (!S_ISREG (source_stats.st_mode)) + return -1; + + if((ifd = sys_open (source, O_RDONLY, 0)) < 0) + return -1; + + if (unlink (dest) && errno != ENOENT) + return -1; + +#ifdef O_NOFOLLOW + if((ofd = sys_open (dest, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0600)) < 0 ) +#else + if((ofd = sys_open (dest, O_WRONLY | O_CREAT | O_TRUNC , 0600)) < 0 ) +#endif + goto err; + + if (transfer_file(ifd, ofd, (size_t)-1) == -1) + goto err; + + /* + * 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 ((fchown(ofd, source_stats.st_uid, source_stats.st_gid) == -1) && (errno != EPERM)) + 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_mode & 07777)) +#else + if (chmod (dest, source_stats.st_mode & 07777)) +#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. */ + { + struct utimbuf tv; + + tv.actime = source_stats.st_atime; + tv.modtime = source_stats.st_mtime; + utime(dest, &tv); + } + + 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; +} + int vfswrap_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new) { - int result; + int result; - START_PROFILE(syscall_rename); - result = rename(old, new); - END_PROFILE(syscall_rename); - return result; + START_PROFILE(syscall_rename); + result = rename(old, new); + if (errno == EXDEV) { + /* Rename across filesystems needed. */ + result = copy_reg(old, new); + } + + END_PROFILE(syscall_rename); + return result; } int vfswrap_fsync(vfs_handle_struct *handle, files_struct *fsp, int fd) |