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 | 598d9d3e5f612133e0528a1a8907745d715b61ed (patch) | |
tree | 9eb8a75268aa0ddb4c67d6a8c79413bf0b80dd73 /source/smbd/vfs-wrap.c | |
parent | 48153f7a07cc04b849a79778fdc3e76af6c6eb13 (diff) | |
download | samba-598d9d3e5f612133e0528a1a8907745d715b61ed.tar.gz samba-598d9d3e5f612133e0528a1a8907745d715b61ed.tar.xz samba-598d9d3e5f612133e0528a1a8907745d715b61ed.zip |
Fix for rename across filesystems. Noticed by Rainer Link <link@foo.fh-furtwangen.de>.
Jeremy.
Diffstat (limited to 'source/smbd/vfs-wrap.c')
-rw-r--r-- | source/smbd/vfs-wrap.c | 100 |
1 files changed, 95 insertions, 5 deletions
diff --git a/source/smbd/vfs-wrap.c b/source/smbd/vfs-wrap.c index a76a7a6abdc..378a0a1e53c 100644 --- a/source/smbd/vfs-wrap.c +++ b/source/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) |