1380 lines · 43.1 KB · C
RawDownload
/* * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t