/* * Routines only used by the receiving process. * * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2003-2023 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h"#include "inums.h" extern int dry_run;extern int do_xfers;extern int am_root;extern int am_daemon;extern int am_server;extern int inc_recurse;extern int log_before_transfer;extern int stdout_format_has_i;extern int logfile_format_has_i;extern int want_xattr_optim;extern int csum_length;extern int read_batch;extern int write_batch;extern int batch_gen_fd;extern int protocol_version;extern int relative_paths;extern int preserve_hard_links;extern int preserve_perms;extern int write_devices;extern int preserve_xattrs;extern int do_fsync;extern int basis_dir_cnt;extern int make_backups;extern int cleanup_got_literal;extern int remove_source_files;extern int append_mode;extern int sparse_files;extern int preallocate_files;extern int keep_partial;extern int checksum_seed;extern int whole_file;extern int inplace;extern int inplace_partial;extern int allowed_lull;extern int delay_updates;extern BOOL want_progress_now;extern mode_t orig_umask;extern struct stats stats;extern char *tmpdir;extern char *partial_dir;extern char *basis_dir[MAX_BASIS_DIRS+1];extern char sender_file_sum[MAX_DIGEST_LEN];extern struct file_list *cur_flist, *first_flist, *dir_flist;extern filter_rule_list daemon_filter_list;extern OFF_T preallocated_len;extern int fuzzy_basis; extern struct name_num_item *xfer_sum_nni;extern int xfer_sum_len;extern int use_secure_symlinks;extern int operator_path_resolve; static struct bitbag *delayed_bits = NULL;static int phase = 0, redoing = 0;static flist_ndx_list batch_redo_list;/* This is non-0 when we are updating the basis file or an identical copy: */static int updating_basis_or_equiv; #define TMPNAME_SUFFIX ".XXXXXX"#define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1)#define MAX_UNIQUE_NUMBER 999999#define MAX_UNIQUE_LOOP 100 /* Open a basis/output path that may legitimately be an operator-trusted * ABSOLUTE path -- e.g. an absolute --partial-dir ("a directory reserved for * partial-dir work") or --backup-dir. secure_relative_open() deliberately * rejects an absolute relpath, so feeding it the whole absolute partialptr * (with a NULL basedir) returns EINVAL: the basis fd is then -1, no basis is * mapped, and receive_data() omits every matched block from the whole-file * verification checksum -> a spurious "failed verification" that strands the * (correct) data in the partial-dir forever. * * The operator's directory is trusted; only the leaf basename is peer-supplied. * So when basedir is NULL and relpath is absolute, split it into its directory * (trusted) and leaf and confine just the leaf -- exactly how secure_relative_ * open already trusts an absolute basedir while O_NOFOLLOW-confining the leaf. * Anything else is a straight pass-through that preserves the strict contract. */static int secure_basis_open(const char *basedir, const char *relpath, int flags, mode_t mode){ extern int am_daemon, am_chrooted; extern unsigned int module_dirlen; /* "insecure links = yes": restore the 3.2.7 plain open so an operator/peer * alt-dest basis follows symlinks like legacy rsync, the same opt-out the * other daemon symlink sites honour. */ if (symlink_optout_allowed()) { if (basedir) { char fullpath[MAXPATHLEN]; if (pathjoin(fullpath, sizeof fullpath, basedir, relpath) >= sizeof fullpath) { errno = ENAMETOOLONG; return -1; } return do_open(fullpath, flags, mode); } return do_open(relpath, flags, mode); } /* A peer-supplied --partial-dir basis/staging path (operator_path_resolve set * by recv_files) may be absolute (module_dir-prefixed on a non-chroot daemon) * and traverse a symlink the secure_relative_open path can't confine: resolve * it with the ownership walk, which follows a uid0/euid-owned symlink but * refuses a foreign one AND (via abspath_excluded_by_module) refuses a target * the module's exclude hides -- closing the partial-dir exclude bypass. */ if (operator_path_resolve) { char fullpath[MAXPATHLEN]; const char *p = relpath; if (basedir) { if (pathjoin(fullpath, sizeof fullpath, basedir, relpath) >= sizeof fullpath) { errno = ENAMETOOLONG; return -1; } p = fullpath; } return open_no_attacker_symlinks(p, flags, mode); } /* The confined resolver is needed for the sanitizing daemon * (am_daemon && !am_chrooted) and for a /./ inner-module chroot * (am_chrooted && module_dirlen) -- in the latter the kernel chroot confines * only the outer path, so a peer-chosen alt-dest basis index (fnamecmp_type) * could otherwise reach an outside-inner-module file through a symlinked * parent. Local / remote-shell mode has no module boundary, and a plain * "use chroot = yes" makes the kernel root the boundary, so there an alt-dest * basis like --link-dest=../01 must resolve against the cwd as a bare open did * before the hardening (confining it would reject the legitimate sibling * "..", #915). The re-anchoring in secure_relative_open() covers the * in-module ".." climb for the inner-module case too. */ if (!am_daemon || (am_chrooted && !module_dirlen)) { if (basedir) { char fullpath[MAXPATHLEN]; if (pathjoin(fullpath, sizeof fullpath, basedir, relpath) >= sizeof fullpath) { errno = ENAMETOOLONG; return -1; } return do_open(fullpath, flags, mode); } return do_open(relpath, flags, mode); } if (!basedir && relpath && *relpath == '/') { const char *slash = strrchr(relpath, '/'); const char *leaf = slash + 1; char dirbuf[MAXPATHLEN]; const char *dir; if (slash == relpath) dir = "/"; else { size_t dlen = slash - relpath; if (dlen >= sizeof dirbuf) { errno = ENAMETOOLONG; return -1; } memcpy(dirbuf, relpath, dlen); dirbuf[dlen] = '\0'; dir = dirbuf; } return secure_relative_open(dir, leaf, flags, mode); } return secure_relative_open(basedir, relpath, flags, mode);} /* Keep the ownership policy for every attempt to open a one-inplace partial * output. In particular, Linux's protected_regular compatibility retries * must not downgrade an operator-path open to the ordinary path resolver. */static int secure_recv_open(const char *path, int flags, mode_t mode, int owner_walk){ int fd, save = operator_path_resolve; if (owner_walk) operator_path_resolve = 1; fd = secure_basis_open(NULL, path, flags, mode); operator_path_resolve = save; return fd;} /* Open a read-only regular file for an in-place update without leaving its * mode relaxed while protocol data is received. Once a writable descriptor is * open, later writes no longer depend on the pathname's permission bits, so * restore the exact prior mode before returning to the transfer loop -- an * abort (peer EOF, checksum failure, a signal) must not strand the file at * 0600. Requires a regular file: O_NOFOLLOW refuses a symlink at the leaf but * not a directory swapped in after the type probe. */static int open_readonly_inplace(const char *fname, int one_inplace){ STRUCT_STAT cst; mode_t prior_mode; int cfd = -1, fd = -1; int open_errno, restore_errno; if (use_secure_symlinks || one_inplace) {#ifdef O_NOFOLLOW cfd = secure_recv_open(fname, O_RDONLY|O_NOFOLLOW, 0, one_inplace); if (cfd < 0) goto failed; if (do_fstat(cfd, &cst) < 0 || !S_ISREG(cst.st_mode)) { errno = EACCES; /* refused: not the read-only regular file we recover */ goto failed; } prior_mode = cst.st_mode & CHMOD_BITS; if (prior_mode & S_IWUSR) { /* Already owner-writable, so adding S_IWUSR cannot be what an * EACCES is about (an ACL, or the parent dir). Don't touch the * mode for nothing: each chmod risks losing a special bit. */ errno = EACCES; goto failed; } if (fchmod(cfd, prior_mode | S_IWUSR) < 0) goto failed; fd = secure_recv_open(fname, O_WRONLY, 0600, one_inplace); open_errno = errno; if (fchmod(cfd, prior_mode) < 0) { restore_errno = errno; if (fd >= 0) close(fd); fd = -1; open_errno = restore_errno; } close(cfd); errno = open_errno; return fd;#else /* Without O_NOFOLLOW the resolver's oldest fallback would follow a * raced symlink, so fail closed rather than chmod through it. */ errno = EACCES; return -1;#endif } /* Local and chrooted transfers retain the existing pathname semantics. * Note the S_ISREG test here is a type check on a stable path, NOT race * protection: do_stat() follows a leaf symlink and each call below * re-resolves the name. The fd-based branch above is the one that * pins an inode; a chroot is what confines this one. */ if (do_stat(fname, &cst) < 0) { errno = EACCES; return -1; } if (!S_ISREG(cst.st_mode) || (cst.st_mode & CHMOD_BITS & S_IWUSR)) { errno = EACCES; return -1; } prior_mode = cst.st_mode & CHMOD_BITS; if (do_chmod_at(fname, prior_mode | S_IWUSR) < 0) return -1; fd = do_open(fname, O_WRONLY, 0600); open_errno = errno; if (do_chmod_at(fname, prior_mode) < 0) { restore_errno = errno; if (fd >= 0) close(fd); fd = -1; open_errno = restore_errno; } errno = open_errno; return fd; failed: open_errno = errno; if (cfd >= 0) close(cfd); errno = open_errno; return -1;} /* get_tmpname() - create a tmp filename for a given filename * * If a tmpdir is defined, use that as the directory to put it in. Otherwise, * the tmp filename is in the same directory as the given name. Note that * there may be no directory at all in the given name! * * The tmp filename is basically the given filename with a dot prepended, and * .XXXXXX appended (for mkstemp() to put its unique gunk in). We take care * to not exceed either the MAXPATHLEN or NAME_MAX, especially the last, as * the basename basically becomes 8 characters longer. In such a case, the * original name is shortened sufficiently to make it all fit. * * If the make_unique arg is True, the XXXXXX string is replaced with a unique * string that doesn't exist at the time of the check. This is intended to be * used for creating hard links, symlinks, devices, and special files, since * normal files should be handled by mkstemp() for safety. * * Of course, the only reason the file is based on the original name is to * make it easier to figure out what purpose a temp file is serving when a * transfer is in progress. */int get_tmpname(char *fnametmp, const char *fname, BOOL make_unique){ int maxname, length = 0; const char *f; char *suf; if (tmpdir) { /* Note: this can't overflow, so the return value is safe */ length = strlcpy(fnametmp, tmpdir, MAXPATHLEN - 2); fnametmp[length++] = '/'; } if ((f = strrchr(fname, '/')) != NULL) { ++f; if (!tmpdir) { length = f - fname; /* copy up to and including the slash */ strlcpy(fnametmp, fname, length + 1); } } else f = fname; if (!tmpdir) { /* using a tmpdir avoids the leading dot on our temp names */ if (*f == '.') /* avoid an extra leading dot for OS X's sake */ f++; fnametmp[length++] = '.'; } /* The maxname value is bufsize, and includes space for the '\0'. * NAME_MAX needs an extra -1 for the name's leading dot. */ maxname = MIN(MAXPATHLEN - length - TMPNAME_SUFFIX_LEN, NAME_MAX - 1 - TMPNAME_SUFFIX_LEN); if (maxname < 0) { rprintf(FERROR_XFER, "temporary filename too long: %s\n", fname); fnametmp[0] = '\0'; return 0; } if (maxname) { int added = strlcpy(fnametmp + length, f, maxname); if (added >= maxname) added = maxname - 1; suf = fnametmp + length + added; /* Trim any dangling high-bit chars if the first-trimmed char (if any) is * also a high-bit char, just in case we cut into a multi-byte sequence. * We are guaranteed to stop because of the leading '.' we added. */ if ((int)f[added] & 0x80) { while ((int)suf[-1] & 0x80) suf--; } /* trim one trailing dot before our suffix's dot */ if (suf[-1] == '.') suf--; } else suf = fnametmp + length - 1; /* overwrite the leading dot with suffix's dot */ if (make_unique) { static unsigned counter_limit; unsigned counter; if (!counter_limit) { counter_limit = (unsigned)getpid() + MAX_UNIQUE_LOOP; if (counter_limit > MAX_UNIQUE_NUMBER || counter_limit < MAX_UNIQUE_LOOP) counter_limit = MAX_UNIQUE_LOOP; } counter = counter_limit - MAX_UNIQUE_LOOP; /* This doesn't have to be very good because we don't need * to worry about someone trying to guess the values: all * a conflict will do is cause a device, special file, hard * link, or symlink to fail to be created. Also: avoid * using mktemp() due to gcc's annoying warning. */ while (1) { snprintf(suf, TMPNAME_SUFFIX_LEN+1, ".%d", counter); if (access(fnametmp, 0) < 0) break; if (++counter >= counter_limit) return 0; } } else memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1); return 1;} /* Opens a temporary file for writing. * Success: Writes name into fnametmp, returns fd. * Failure: Clobbers fnametmp, returns -1. * Calling cleanup_set() is the caller's job. */int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file){ int fd; mode_t added_perms; if (!get_tmpname(fnametmp, fname, False)) return -1; if (am_root < 0) { /* For --fake-super, the file must be useable by the copying * user, just like it would be for root. */ added_perms = S_IRUSR|S_IWUSR; } else { /* For a normal copy, we need to be able to tweak things like xattrs. */ added_perms = S_IWUSR; } /* We initially set the perms without the setuid/setgid bits or group * access to ensure that there is no race condition. They will be * correctly updated after the right owner and group info is set. * (Thanks to snabb@epipe.fi for pointing this out.) */ /* For any non-chrooted receiver (secure_relpath_active()), create the * temp file securely so a parent-symlink race can't redirect it. When * the temp lives in the entry's own dir (the common case, no --temp-dir) * use the cached held dir fd; otherwise fall back to secure_mkstemp. An * operator-supplied --temp-dir (tmpdir) gets the ownership-walk resolver * (it may legitimately point outside the tree); the deep-entry-dir fallback, * when the held-dirfd cache declines, gets the strict transfer-path one. */ if (secure_relpath_active()) { int dfd = held_dfd_for(fnametmp, file); if (dfd >= 0) { char *slash = strrchr(fnametmp, '/'); fd = do_mkstemp_atfd(dfd, slash ? slash + 1 : fnametmp, (file->mode|added_perms) & INITACCESSPERMS); } else fd = secure_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS, tmpdir != NULL); } else fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS); #if 0 /* 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 && make_path(fnametmp, MKP_SKIP_SLASH | MKP_DROP_NAME) == 0) { /* Get back to name with XXXXXX in it. */ get_tmpname(fnametmp, fname, False); fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS); }#endif if (fd == -1) { rsyserr(FERROR_XFER, 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, struct file_struct *file, int inplace_sizing){ static char file_sum1[MAX_DIGEST_LEN]; struct map_struct *mapbuf; struct sum_struct sum; int32 len; OFF_T total_size = F_LENGTH(file); OFF_T offset = 0; OFF_T offset2; char *data; int32 i; char *map = NULL; #ifdef SUPPORT_PREALLOCATION if (preallocate_files && fd != -1 && total_size > 0 && (!inplace_sizing || total_size > size_r)) { /* Try to preallocate enough space for file's eventual length. Can * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */ if ((preallocated_len = do_fallocate(fd, 0, total_size)) < 0) rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(fname)); } else#endif if (inplace_sizing) {#ifdef HAVE_FTRUNCATE /* The most compatible way to create a sparse file is to start with no length. */ if (sparse_files > 0 && whole_file && fd >= 0 && do_ftruncate(fd, 0) == 0) preallocated_len = 0; else#endif preallocated_len = size_r; } else preallocated_len = 0; read_sum_head(f_in, &sum); if (fd_r >= 0 && size_r > 0) { int32 read_size = MAX(sum.blength * 2, 16*1024); mapbuf = map_file(fd_r, size_r, read_size, sum.blength); if (DEBUG_GTE(DELTASUM, 2)) { rprintf(FINFO, "recv mapped %s of size %s\n",Lines 1–500 of 1380. Load more
/* * Routines only used by the receiving process. * * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2003-2023 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h" #include "inums.h" extern int dry_run; extern int do_xfers; extern int am_root; extern int am_daemon; extern int am_server; extern int inc_recurse; extern int log_before_transfer; extern int stdout_format_has_i; extern int logfile_format_has_i; extern int want_xattr_optim; extern int csum_length; extern int read_batch; extern int write_batch; extern int batch_gen_fd; extern int protocol_version; extern int relative_paths; extern int preserve_hard_links; extern int preserve_perms; extern int write_devices; extern int preserve_xattrs; extern int do_fsync; extern int basis_dir_cnt; extern int make_backups; extern int cleanup_got_literal; extern int remove_source_files; extern int append_mode; extern int sparse_files; extern int preallocate_files; extern int keep_partial; extern int checksum_seed; extern int whole_file; extern int inplace; extern int inplace_partial; extern int allowed_lull; extern int delay_updates; extern BOOL want_progress_now; extern mode_t orig_umask; extern struct stats stats; extern char *tmpdir; extern char *partial_dir; extern char *basis_dir[MAX_BASIS_DIRS+1]; extern char sender_file_sum[MAX_DIGEST_LEN]; extern struct file_list *cur_flist, *first_flist, *dir_flist; extern filter_rule_list daemon_filter_list; extern OFF_T preallocated_len; extern int fuzzy_basis; extern struct name_num_item *xfer_sum_nni; extern int xfer_sum_len; extern int use_secure_symlinks; extern int operator_path_resolve; static struct bitbag *delayed_bits = NULL; static int phase = 0, redoing = 0; static flist_ndx_list batch_redo_list; /* This is non-0 when we are updating the basis file or an identical copy: */ static int updating_basis_or_equiv; #define TMPNAME_SUFFIX ".XXXXXX" #define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1) #define MAX_UNIQUE_NUMBER 999999 #define MAX_UNIQUE_LOOP 100 /* Open a basis/output path that may legitimately be an operator-trusted * ABSOLUTE path -- e.g. an absolute --partial-dir ("a directory reserved for * partial-dir work") or --backup-dir. secure_relative_open() deliberately * rejects an absolute relpath, so feeding it the whole absolute partialptr * (with a NULL basedir) returns EINVAL: the basis fd is then -1, no basis is * mapped, and receive_data() omits every matched block from the whole-file * verification checksum -> a spurious "failed verification" that strands the * (correct) data in the partial-dir forever. * * The operator's directory is trusted; only the leaf basename is peer-supplied. * So when basedir is NULL and relpath is absolute, split it into its directory * (trusted) and leaf and confine just the leaf -- exactly how secure_relative_ * open already trusts an absolute basedir while O_NOFOLLOW-confining the leaf. * Anything else is a straight pass-through that preserves the strict contract. */ static int secure_basis_open(const char *basedir, const char *relpath, int flags, mode_t mode) { extern int am_daemon, am_chrooted; extern unsigned int module_dirlen; /* "insecure links = yes": restore the 3.2.7 plain open so an operator/peer * alt-dest basis follows symlinks like legacy rsync, the same opt-out the * other daemon symlink sites honour. */ if (symlink_optout_allowed()) { if (basedir) { char fullpath[MAXPATHLEN]; if (pathjoin(fullpath, sizeof fullpath, basedir, relpath) >= sizeof fullpath) { errno = ENAMETOOLONG; return -1; } return do_open(fullpath, flags, mode); } return do_open(relpath, flags, mode); } /* A peer-supplied --partial-dir basis/staging path (operator_path_resolve set * by recv_files) may be absolute (module_dir-prefixed on a non-chroot daemon) * and traverse a symlink the secure_relative_open path can't confine: resolve * it with the ownership walk, which follows a uid0/euid-owned symlink but * refuses a foreign one AND (via abspath_excluded_by_module) refuses a target * the module's exclude hides -- closing the partial-dir exclude bypass. */ if (operator_path_resolve) { char fullpath[MAXPATHLEN]; const char *p = relpath; if (basedir) { if (pathjoin(fullpath, sizeof fullpath, basedir, relpath) >= sizeof fullpath) { errno = ENAMETOOLONG; return -1; } p = fullpath; } return open_no_attacker_symlinks(p, flags, mode); } /* The confined resolver is needed for the sanitizing daemon * (am_daemon && !am_chrooted) and for a /./ inner-module chroot * (am_chrooted && module_dirlen) -- in the latter the kernel chroot confines * only the outer path, so a peer-chosen alt-dest basis index (fnamecmp_type) * could otherwise reach an outside-inner-module file through a symlinked * parent. Local / remote-shell mode has no module boundary, and a plain * "use chroot = yes" makes the kernel root the boundary, so there an alt-dest * basis like --link-dest=../01 must resolve against the cwd as a bare open did * before the hardening (confining it would reject the legitimate sibling * "..", #915). The re-anchoring in secure_relative_open() covers the * in-module ".." climb for the inner-module case too. */ if (!am_daemon || (am_chrooted && !module_dirlen)) { if (basedir) { char fullpath[MAXPATHLEN]; if (pathjoin(fullpath, sizeof fullpath, basedir, relpath) >= sizeof fullpath) { errno = ENAMETOOLONG; return -1; } return do_open(fullpath, flags, mode); } return do_open(relpath, flags, mode); } if (!basedir && relpath && *relpath == '/') { const char *slash = strrchr(relpath, '/'); const char *leaf = slash + 1; char dirbuf[MAXPATHLEN]; const char *dir; if (slash == relpath) dir = "/"; else { size_t dlen = slash - relpath; if (dlen >= sizeof dirbuf) { errno = ENAMETOOLONG; return -1; } memcpy(dirbuf, relpath, dlen); dirbuf[dlen] = '\0'; dir = dirbuf; } return secure_relative_open(dir, leaf, flags, mode); } return secure_relative_open(basedir, relpath, flags, mode); } /* Keep the ownership policy for every attempt to open a one-inplace partial * output. In particular, Linux's protected_regular compatibility retries * must not downgrade an operator-path open to the ordinary path resolver. */ static int secure_recv_open(const char *path, int flags, mode_t mode, int owner_walk) { int fd, save = operator_path_resolve; if (owner_walk) operator_path_resolve = 1; fd = secure_basis_open(NULL, path, flags, mode); operator_path_resolve = save; return fd; } /* Open a read-only regular file for an in-place update without leaving its * mode relaxed while protocol data is received. Once a writable descriptor is * open, later writes no longer depend on the pathname's permission bits, so * restore the exact prior mode before returning to the transfer loop -- an * abort (peer EOF, checksum failure, a signal) must not strand the file at * 0600. Requires a regular file: O_NOFOLLOW refuses a symlink at the leaf but * not a directory swapped in after the type probe. */ static int open_readonly_inplace(const char *fname, int one_inplace) { STRUCT_STAT cst; mode_t prior_mode; int cfd = -1, fd = -1; int open_errno, restore_errno; if (use_secure_symlinks || one_inplace) { #ifdef O_NOFOLLOW cfd = secure_recv_open(fname, O_RDONLY|O_NOFOLLOW, 0, one_inplace); if (cfd < 0) goto failed; if (do_fstat(cfd, &cst) < 0 || !S_ISREG(cst.st_mode)) { errno = EACCES; /* refused: not the read-only regular file we recover */ goto failed; } prior_mode = cst.st_mode & CHMOD_BITS; if (prior_mode & S_IWUSR) { /* Already owner-writable, so adding S_IWUSR cannot be what an * EACCES is about (an ACL, or the parent dir). Don't touch the * mode for nothing: each chmod risks losing a special bit. */ errno = EACCES; goto failed; } if (fchmod(cfd, prior_mode | S_IWUSR) < 0) goto failed; fd = secure_recv_open(fname, O_WRONLY, 0600, one_inplace); open_errno = errno; if (fchmod(cfd, prior_mode) < 0) { restore_errno = errno; if (fd >= 0) close(fd); fd = -1; open_errno = restore_errno; } close(cfd); errno = open_errno; return fd; #else /* Without O_NOFOLLOW the resolver's oldest fallback would follow a * raced symlink, so fail closed rather than chmod through it. */ errno = EACCES; return -1; #endif } /* Local and chrooted transfers retain the existing pathname semantics. * Note the S_ISREG test here is a type check on a stable path, NOT race * protection: do_stat() follows a leaf symlink and each call below * re-resolves the name. The fd-based branch above is the one that * pins an inode; a chroot is what confines this one. */ if (do_stat(fname, &cst) < 0) { errno = EACCES; return -1; } if (!S_ISREG(cst.st_mode) || (cst.st_mode & CHMOD_BITS & S_IWUSR)) { errno = EACCES; return -1; } prior_mode = cst.st_mode & CHMOD_BITS; if (do_chmod_at(fname, prior_mode | S_IWUSR) < 0) return -1; fd = do_open(fname, O_WRONLY, 0600); open_errno = errno; if (do_chmod_at(fname, prior_mode) < 0) { restore_errno = errno; if (fd >= 0) close(fd); fd = -1; open_errno = restore_errno; } errno = open_errno; return fd; failed: open_errno = errno; if (cfd >= 0) close(cfd); errno = open_errno; return -1; } /* get_tmpname() - create a tmp filename for a given filename * * If a tmpdir is defined, use that as the directory to put it in. Otherwise, * the tmp filename is in the same directory as the given name. Note that * there may be no directory at all in the given name! * * The tmp filename is basically the given filename with a dot prepended, and * .XXXXXX appended (for mkstemp() to put its unique gunk in). We take care * to not exceed either the MAXPATHLEN or NAME_MAX, especially the last, as * the basename basically becomes 8 characters longer. In such a case, the * original name is shortened sufficiently to make it all fit. * * If the make_unique arg is True, the XXXXXX string is replaced with a unique * string that doesn't exist at the time of the check. This is intended to be * used for creating hard links, symlinks, devices, and special files, since * normal files should be handled by mkstemp() for safety. * * Of course, the only reason the file is based on the original name is to * make it easier to figure out what purpose a temp file is serving when a * transfer is in progress. */ int get_tmpname(char *fnametmp, const char *fname, BOOL make_unique) { int maxname, length = 0; const char *f; char *suf; if (tmpdir) { /* Note: this can't overflow, so the return value is safe */ length = strlcpy(fnametmp, tmpdir, MAXPATHLEN - 2); fnametmp[length++] = '/'; } if ((f = strrchr(fname, '/')) != NULL) { ++f; if (!tmpdir) { length = f - fname; /* copy up to and including the slash */ strlcpy(fnametmp, fname, length + 1); } } else f = fname; if (!tmpdir) { /* using a tmpdir avoids the leading dot on our temp names */ if (*f == '.') /* avoid an extra leading dot for OS X's sake */ f++; fnametmp[length++] = '.'; } /* The maxname value is bufsize, and includes space for the '\0'. * NAME_MAX needs an extra -1 for the name's leading dot. */ maxname = MIN(MAXPATHLEN - length - TMPNAME_SUFFIX_LEN, NAME_MAX - 1 - TMPNAME_SUFFIX_LEN); if (maxname < 0) { rprintf(FERROR_XFER, "temporary filename too long: %s\n", fname); fnametmp[0] = '\0'; return 0; } if (maxname) { int added = strlcpy(fnametmp + length, f, maxname); if (added >= maxname) added = maxname - 1; suf = fnametmp + length + added; /* Trim any dangling high-bit chars if the first-trimmed char (if any) is * also a high-bit char, just in case we cut into a multi-byte sequence. * We are guaranteed to stop because of the leading '.' we added. */ if ((int)f[added] & 0x80) { while ((int)suf[-1] & 0x80) suf--; } /* trim one trailing dot before our suffix's dot */ if (suf[-1] == '.') suf--; } else suf = fnametmp + length - 1; /* overwrite the leading dot with suffix's dot */ if (make_unique) { static unsigned counter_limit; unsigned counter; if (!counter_limit) { counter_limit = (unsigned)getpid() + MAX_UNIQUE_LOOP; if (counter_limit > MAX_UNIQUE_NUMBER || counter_limit < MAX_UNIQUE_LOOP) counter_limit = MAX_UNIQUE_LOOP; } counter = counter_limit - MAX_UNIQUE_LOOP; /* This doesn't have to be very good because we don't need * to worry about someone trying to guess the values: all * a conflict will do is cause a device, special file, hard * link, or symlink to fail to be created. Also: avoid * using mktemp() due to gcc's annoying warning. */ while (1) { snprintf(suf, TMPNAME_SUFFIX_LEN+1, ".%d", counter); if (access(fnametmp, 0) < 0) break; if (++counter >= counter_limit) return 0; } } else memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1); return 1; } /* Opens a temporary file for writing. * Success: Writes name into fnametmp, returns fd. * Failure: Clobbers fnametmp, returns -1. * Calling cleanup_set() is the caller's job. */ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file) { int fd; mode_t added_perms; if (!get_tmpname(fnametmp, fname, False)) return -1; if (am_root < 0) { /* For --fake-super, the file must be useable by the copying * user, just like it would be for root. */ added_perms = S_IRUSR|S_IWUSR; } else { /* For a normal copy, we need to be able to tweak things like xattrs. */ added_perms = S_IWUSR; } /* We initially set the perms without the setuid/setgid bits or group * access to ensure that there is no race condition. They will be * correctly updated after the right owner and group info is set. * (Thanks to snabb@epipe.fi for pointing this out.) */ /* For any non-chrooted receiver (secure_relpath_active()), create the * temp file securely so a parent-symlink race can't redirect it. When * the temp lives in the entry's own dir (the common case, no --temp-dir) * use the cached held dir fd; otherwise fall back to secure_mkstemp. An * operator-supplied --temp-dir (tmpdir) gets the ownership-walk resolver * (it may legitimately point outside the tree); the deep-entry-dir fallback, * when the held-dirfd cache declines, gets the strict transfer-path one. */ if (secure_relpath_active()) { int dfd = held_dfd_for(fnametmp, file); if (dfd >= 0) { char *slash = strrchr(fnametmp, '/'); fd = do_mkstemp_atfd(dfd, slash ? slash + 1 : fnametmp, (file->mode|added_perms) & INITACCESSPERMS); } else fd = secure_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS, tmpdir != NULL); } else fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS); #if 0 /* 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 && make_path(fnametmp, MKP_SKIP_SLASH | MKP_DROP_NAME) == 0) { /* Get back to name with XXXXXX in it. */ get_tmpname(fnametmp, fname, False); fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS); } #endif if (fd == -1) { rsyserr(FERROR_XFER, 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, struct file_struct *file, int inplace_sizing) { static char file_sum1[MAX_DIGEST_LEN]; struct map_struct *mapbuf; struct sum_struct sum; int32 len; OFF_T total_size = F_LENGTH(file); OFF_T offset = 0; OFF_T offset2; char *data; int32 i; char *map = NULL; #ifdef SUPPORT_PREALLOCATION if (preallocate_files && fd != -1 && total_size > 0 && (!inplace_sizing || total_size > size_r)) { /* Try to preallocate enough space for file's eventual length. Can * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */ if ((preallocated_len = do_fallocate(fd, 0, total_size)) < 0) rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(fname)); } else #endif if (inplace_sizing) { #ifdef HAVE_FTRUNCATE /* The most compatible way to create a sparse file is to start with no length. */ if (sparse_files > 0 && whole_file && fd >= 0 && do_ftruncate(fd, 0) == 0) preallocated_len = 0; else #endif preallocated_len = size_r; } else preallocated_len = 0; read_sum_head(f_in, &sum); if (fd_r >= 0 && size_r > 0) { int32 read_size = MAX(sum.blength * 2, 16*1024); mapbuf = map_file(fd_r, size_r, read_size, sum.blength); if (DEBUG_GTE(DELTASUM, 2)) { rprintf(FINFO, "recv mapped %s of size %s\n",