Fix https://bugzilla.samba.org/show_bug.cgi?id=5051 --- old/generator.c +++ new/generator.c @@ -830,6 +830,40 @@ static int find_fuzzy(struct file_struct return lowest_j; } +/* Do a --copy-dest local copy. */ +static int do_local_copy(char *fname, struct file_struct *file, char *cmpbuf) +{ + char fnametmpbuf[MAXPATHLEN], *fnametmp; + int fd_w; + + if (inplace) { + /* Let copy_file open the destination in place. */ + fd_w = -1; + fnametmp = fname; + } else { + fd_w = open_tmpfile(fnametmpbuf, fname, file); + if (fd_w < 0) + return -1; + fnametmp = fnametmpbuf; + } + cleanup_set(fnametmp, NULL, NULL, -1, -1); + if (copy_file(cmpbuf, fnametmp, fd_w, file->mode, 0) < 0) { + /* Try to clean up. */ + unlink(fnametmp); + cleanup_disable(); + if (verbose) { + rsyserr(FINFO, errno, "copy_file %s => %s", + full_fname(cmpbuf), fnametmp); + } + return -1; + } + finish_transfer(fname, fnametmp, cmpbuf, + partial_dir ? partial_dir_fname(fname) : NULL, + file, 1, 0); + cleanup_disable(); + return 0; +} + /* This is only called for regular files. We return -2 if we've finished * handling the file, -1 if no dest-linking occurred, or a non-negative * value if we found an alternate basis file. */ @@ -904,14 +938,8 @@ static int try_dests_reg(struct file_str #ifdef SUPPORT_HARD_LINKS try_a_copy: /* Copy the file locally. */ #endif - if (!dry_run && copy_file(cmpbuf, fname, file->mode, 0) < 0) { - if (verbose) { - rsyserr(FINFO, errno, "copy_file %s => %s", - full_fname(cmpbuf), fname); - } + if (!dry_run && do_local_copy(fname, file, cmpbuf) < 0) return -1; - } - set_file_attrs(fname, file, NULL, cmpbuf, 0); if (itemizing) itemize(cmpbuf, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL); #ifdef SUPPORT_XATTRS @@ -1680,7 +1708,7 @@ static void recv_generator(char *fname, goto cleanup; if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) goto pretend_missing; - if (copy_file(fname, backupptr, back_file->mode, 1) < 0) { + if (copy_file(fname, backupptr, -1, back_file->mode, 1) < 0) { unmake_file(back_file); back_file = NULL; goto cleanup; --- old/receiver.c +++ new/receiver.c @@ -123,6 +123,42 @@ int get_tmpname(char *fnametmp, char *fn return 1; } +/* Opens a temporary file for writing. + * Success: Writes name into fnametmp, returns fd. + * Failure: Clobbers fnametmp, returns -1. + * cleanup_set is the caller's job. */ +int open_tmpfile(char *fnametmp, char *fname, struct file_struct *file) +{ + int fd; + + if (!get_tmpname(fnametmp,fname)) + return -1; + + /* we initially set the perms without the + * setuid/setgid bits to ensure that there is no race + * condition. They are then correctly updated after + * the lchown. Thanks to snabb@epipe.fi for pointing + * this out. We also set it initially without group + * access because of a similar race condition. */ + fd = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); + + /* in most cases parent directories will already exist + * because their information should have been previously + * transferred, but that may not be the case with -R */ + if (fd == -1 && relative_paths && errno == ENOENT + && create_directory_path(fnametmp) == 0) { + /* Get back to name with XXXXXX in it. */ + get_tmpname(fnametmp, fname); + fd = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); + } + if (fd == -1) { + rsyserr(FERROR, errno, "mkstemp %s failed", + full_fname(fnametmp)); + return -1; + } + + return fd; +} static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, const char *fname, int fd, OFF_T total_size) @@ -606,55 +642,22 @@ int recv_files(int f_in, char *local_nam /* We now check to see if we are writing the file "inplace" */ if (inplace) { fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600); - if (fd2 == -1) { + if (fd2 == -1) rsyserr(FERROR, errno, "open %s failed", full_fname(fname)); - discard_receive_data(f_in, F_LENGTH(file)); - if (fd1 != -1) - close(fd1); - if (inc_recurse) - send_msg_int(MSG_NO_SEND, ndx); - continue; - } } else { - if (!get_tmpname(fnametmp,fname)) { - discard_receive_data(f_in, F_LENGTH(file)); - if (fd1 != -1) - close(fd1); - if (inc_recurse) - send_msg_int(MSG_NO_SEND, ndx); - continue; - } - - /* we initially set the perms without the - * setuid/setgid bits to ensure that there is no race - * condition. They are then correctly updated after - * the lchown. Thanks to snabb@epipe.fi for pointing - * this out. We also set it initially without group - * access because of a similar race condition. */ - fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); - - /* in most cases parent directories will already exist - * because their information should have been previously - * transferred, but that may not be the case with -R */ - if (fd2 == -1 && relative_paths && errno == ENOENT - && create_directory_path(fnametmp) == 0) { - /* Get back to name with XXXXXX in it. */ - get_tmpname(fnametmp, fname); - fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); - } - if (fd2 == -1) { - rsyserr(FERROR, errno, "mkstemp %s failed", - full_fname(fnametmp)); - discard_receive_data(f_in, F_LENGTH(file)); - if (fd1 != -1) - close(fd1); - if (inc_recurse) - send_msg_int(MSG_NO_SEND, ndx); - continue; - } + fd2 = open_tmpfile(fnametmp, fname, file); + if (fd2 != -1) + cleanup_set(fnametmp, partialptr, file, fd1, fd2); + } - cleanup_set(fnametmp, partialptr, file, fd1, fd2); + if (fd2 == -1) { + discard_receive_data(f_in, F_LENGTH(file)); + if (fd1 != -1) + close(fd1); + if (inc_recurse) + send_msg_int(MSG_NO_SEND, ndx); + continue; } /* log the transfer */ --- old/util.c +++ new/util.c @@ -261,14 +261,15 @@ static int safe_read(int desc, char *ptr return n_chars; } -/** Copy a file. +/** Copy a file. If ofd == -1, copy_file unlinks and opens the file itself; + * otherwise it just writes to and closes the provided file descriptor. * - * This is used in conjunction with the --temp-dir, --backup, and - * --copy-dest options. */ -int copy_file(const char *source, const char *dest, mode_t mode, int create_bak_dir) + * This is used in conjunction with the --temp-dir, --backup, + * --copy-dest, and --no-tweak* options. */ +int copy_file(const char *source, const char *dest, int ofd, + mode_t mode, int create_bak_dir) { int ifd; - int ofd; char buf[1024 * 8]; int len; /* Number of bytes read into `buf'. */ @@ -277,17 +278,19 @@ int copy_file(const char *source, const return -1; } - if (robust_unlink(dest) && errno != ENOENT) { - rsyserr(FERROR, errno, "unlink %s", full_fname(dest)); - return -1; - } + if (ofd == -1) { + if (robust_unlink(dest) && errno != ENOENT) { + rsyserr(FERROR, errno, "unlink %s", full_fname(dest)); + return -1; + } - if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0 - && (!create_bak_dir || errno != ENOENT || make_bak_dir(dest) < 0 - || (ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0)) { - rsyserr(FERROR, errno, "open %s", full_fname(dest)); - close(ifd); - return -1; + if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0 + && (!create_bak_dir || errno != ENOENT || make_bak_dir(dest) < 0 + || (ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0)) { + rsyserr(FERROR, errno, "open %s", full_fname(dest)); + close(ifd); + return -1; + } } while ((len = safe_read(ifd, buf, sizeof buf)) > 0) { @@ -407,7 +410,7 @@ int robust_rename(const char *from, cons return -1; to = partialptr; } - if (copy_file(from, to, mode, 0) != 0) + if (copy_file(from, to, -1, mode, 0) != 0) return -2; do_unlink(from); return 1;