2069 lines · 55.1 KB · C
RawDownload
/* * Utility routines used in rsync. * * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org> * Copyright (C) 2003-2022 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 "ifuncs.h"#include "itypes.h"#include "inums.h"#ifdef SUPPORT_IDN#include <idn2.h>#endif extern int dry_run;extern int module_id;extern int do_fsync;extern int protect_args;extern int modify_window;extern int relative_paths;extern int preserve_xattrs;extern int omit_link_times;extern int preallocate_files;extern int operator_path_resolve;extern char *module_dir;extern unsigned int module_dirlen;extern char *partial_dir;extern filter_rule_list daemon_filter_list; int sanitize_paths = 0; extern char curr_dir[MAXPATHLEN];   /* defined in syscall.c */extern unsigned int curr_dir_len;int curr_dir_depth; /* This is only set for a sanitizing daemon. */ /* Set a fd into nonblocking mode. */void set_nonblocking(int fd){	int val; 	if ((val = fcntl(fd, F_GETFL)) == -1)		return;	if (!(val & NONBLOCK_FLAG)) {		val |= NONBLOCK_FLAG;		fcntl(fd, F_SETFL, val);	}} /* Set a fd into blocking mode. */void set_blocking(int fd){	int val; 	if ((val = fcntl(fd, F_GETFL)) == -1)		return;	if (val & NONBLOCK_FLAG) {		val &= ~NONBLOCK_FLAG;		fcntl(fd, F_SETFL, val);	}} /** * Create a file descriptor pair - like pipe() but use socketpair if * possible (because of blocking issues on pipes). * * Always set non-blocking. */int fd_pair(int fd[2]){	int ret; #ifdef HAVE_SOCKETPAIR	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);#else	ret = pipe(fd);#endif 	if (ret == 0) {		set_nonblocking(fd[0]);		set_nonblocking(fd[1]);	} 	return ret;} void print_child_argv(const char *prefix, char **cmd){	int cnt = 0;	rprintf(FCLIENT, "%s ", prefix);	for (; *cmd; cmd++) {		/* Look for characters that ought to be quoted.  This		* is not a great quoting algorithm, but it's		* sufficient for a log message. */		if (strspn(*cmd, "abcdefghijklmnopqrstuvwxyz"			   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"			   "0123456789"			   ",.-_=+@/") != strlen(*cmd)) {			rprintf(FCLIENT, "\"%s\" ", *cmd);		} else {			rprintf(FCLIENT, "%s ", *cmd);		}		cnt++;	}	rprintf(FCLIENT, " (%d args)\n", cnt);} /* This returns 0 for success, 1 for a symlink if symlink time-setting * is not possible, or -1 for any other error. */int set_times(const char *fname, STRUCT_STAT *stp){	static int switch_step = 0; 	if (DEBUG_GTE(TIME, 1)) {		rprintf(FINFO,			"set modtime, atime of %s to (%ld) %s, (%ld) %s\n",			fname, (long)stp->st_mtime,			timestring(stp->st_mtime), (long)stp->st_atime, timestring(stp->st_atime));	} 	switch (switch_step) {#ifdef HAVE_SETATTRLIST#include "case_N.h"		if (do_setattrlist_times(fname, stp) == 0)			break;		if (errno != ENOSYS)			return -1;		switch_step++;#endif #ifdef HAVE_UTIMENSAT#include "case_N.h"		if (do_utimensat_at(fname, stp) == 0)			break;		if (errno != ENOSYS)			return -1;		switch_step++;#endif #ifdef HAVE_LUTIMES#include "case_N.h"		if (do_lutimes(fname, stp) == 0)			break;		if (errno != ENOSYS)			return -1;		switch_step++;#endif #include "case_N.h"		switch_step++;		if (!omit_link_times) {			omit_link_times = 1;			if (S_ISLNK(stp->st_mode))				return 1;		} #include "case_N.h"#ifdef HAVE_UTIMES		if (do_utimes(fname, stp) == 0)			break;#else		if (do_utime(fname, stp) == 0)			break;#endif 		return -1;	} 	return 0;} /* Held-dirfd variant of set_times(): set `name`'s times relative to dir fd * `dfd`.  Returns 0/-1 like set_times(), or -2 when this platform's active * time-setting tier has no dfd-relative form (caller must fall back to * set_times(fname, stp)).  Only the utimensat tier has an at-on-dfd form, so * macOS (setattrlist) and pre-utimensat systems take the -2 path. */int set_times_at(int dfd, const char *name, STRUCT_STAT *stp){#if defined HAVE_UTIMENSAT && !defined HAVE_SETATTRLIST	int r = do_utimensat_atfd(dfd, name, stp);	if (r == 0)		return 0;	if (errno == ENOSYS)		return -2;	/* fall back to the full set_times() tier walk */	return -1;#else	(void)dfd; (void)name; (void)stp;	return -2;#endif} /* Create any necessary directories in fname.  Any missing directories are * created with default permissions.  Returns < 0 on error, or the number * of directories created. */int make_path(char *fname, int flags){	char *end, *p;	int ret = 0; 	if (flags & MKP_SKIP_SLASH) {		while (*fname == '/')			fname++;	} 	while (*fname == '.' && fname[1] == '/')		fname += 2; 	if (flags & MKP_DROP_NAME) {		end = strrchr(fname, '/');		if (!end || end == fname)			return 0;		*end = '\0';	} else		end = fname + strlen(fname); 	/* Try to find an existing dir, starting from the deepest dir. */	for (p = end; ; ) {		if (dry_run) {			STRUCT_STAT st;			if (do_stat(fname, &st) == 0) {				if (S_ISDIR(st.st_mode))					errno = EEXIST;				else					errno = ENOTDIR;			}		} else if (do_mkdir_at(fname, ACCESSPERMS) == 0) {			ret++;			break;		} 		if (errno != ENOENT) {			STRUCT_STAT st;			if (errno != EEXIST || (do_stat(fname, &st) == 0 && !S_ISDIR(st.st_mode)))				ret = -ret - 1;			break;		}		while (1) {			if (p == fname) {				/* We got a relative path that doesn't exist, so assume that '.'				 * is there and just break out and create the whole thing. */				p = NULL;				goto double_break;			}			if (*--p == '/') {				if (p == fname) {					/* We reached the "/" dir, which we assume is there. */					goto double_break;				}				*p = '\0';				break;			}		}	}  double_break: 	/* Make all the dirs that we didn't find on the way here. */	while (p != end) {		if (p)			*p = '/';		else			p = fname;		p += strlen(p);		if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */			continue;		if (do_mkdir_at(fname, ACCESSPERMS) < 0)			ret = -ret - 1;		else			ret++;	} 	if (flags & MKP_DROP_NAME)		*end = '/'; 	return ret;} /** * Write @p len bytes at @p ptr to descriptor @p desc, retrying if * interrupted. * * @retval len upon success * * @retval <0 write's (negative) error code * * Derived from GNU C's cccp.c. */int full_write(int desc, const char *ptr, size_t len){	int total_written; 	total_written = 0;	while (len > 0) {		int written = write(desc, ptr, len);		if (written < 0)  {			if (errno == EINTR)				continue;			return written;		}		total_written += written;		ptr += written;		len -= written;	}	return total_written;} /** * Read @p len bytes at @p ptr from descriptor @p desc, retrying if * interrupted. * * @retval >0 the actual number of bytes read * * @retval 0 for EOF * * @retval <0 for an error. * * Derived from GNU C's cccp.c. */static int safe_read(int desc, char *ptr, size_t len){	int n_chars; 	if (len == 0)		return len; 	do {		n_chars = read(desc, ptr, len);	} while (n_chars < 0 && errno == EINTR); 	return n_chars;} /* Remove existing file @dest and reopen, creating a new file with @mode */static int unlink_and_reopen(const char *dest, mode_t mode){	int ofd; 	if (robust_unlink(dest) && errno != ENOENT) {		int save_errno = errno;		rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest));		errno = save_errno;		return -1;	} #ifdef SUPPORT_XATTRS	if (preserve_xattrs)		mode |= S_IWUSR;#endif	mode &= INITACCESSPERMS;	/* Use do_open_at so the create/truncate goes through a secure	 * parent dirfd in the daemon-no-chroot deployment. Otherwise	 * an attacker could swap a parent component with a symlink in	 * the window between robust_unlink (which uses do_unlink_at,	 * already secure) and the create here, and redirect the new	 * file outside the module. */	if ((ofd = do_open_at(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {		int save_errno = errno;		rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest));		errno = save_errno;		return -1;	}	return ofd;} /* Copy contents of file @source to file @dest with mode @mode. * * If @tmpfilefd is < 0, copy_file unlinks @dest and then opens a new * file with name @dest. * * Otherwise, copy_file writes to and closes the provided file * descriptor. * * In either case, if --xattrs are being preserved, the dest file will * have its xattrs set from the source file. * * This is used in conjunction with the --temp-dir, --backup, and * --copy-dest options. */int copy_file(const char *source, const char *dest, int tmpfilefd, mode_t mode){	int ifd, ofd;	char buf[1024 * 8];	int len;   /* Number of bytes read into `buf'. */	OFF_T prealloc_len = 0, offset = 0; 	/* For any hardened (non-chrooted) receiver, route the source open through	 * secure_relative_open so a parent-symlink on the source path (e.g.	 * --copy-dest=cd where cd is a symlink to an outside directory) cannot	 * redirect the read to a file the attacker should not see.  Plain	 * do_open_nofollow only refuses a final-component symlink; parents are	 * still followed.  An ABSOLUTE source is an operator basis (e.g. an absolute	 * --copy-dest): confine its parents via the ownership walk -- a foreign-owned	 * parent symlink is refused, the operator's own dirs/uid0/euid symlinks	 * followed -- so a flipped parent can't redirect the basis read out of tree.	 * operator_path_resolve is set only across the walk (so module-exclude is	 * enforced) and restored, leaving the caller's value for the dest side -- this	 * is why confining the source here does not re-open the copy_xattrs dest	 * race the way wrapping the whole copy_altdest_file would. */	if (secure_relpath_active() && source && *source && source[0] != '/')		ifd = secure_relative_open(NULL, source, O_RDONLY | O_NOFOLLOW, 0);#if defined AT_FDCWD && defined O_NOFOLLOW && defined O_DIRECTORY	else if (secure_relpath_active() && source && source[0] == '/'	      && !symlink_optout_allowed()) {		int save = operator_path_resolve, dfd, e;		const char *leaf;		operator_path_resolve = 1;		dfd = owner_walk_parent(source, &leaf);		operator_path_resolve = save;		if (dfd < 0)			ifd = -1;		else {			ifd = openat(dfd, leaf, O_RDONLY | O_NOFOLLOW);			e = errno;			close(dfd);			errno = e;		}	}#endif	else		ifd = do_open_nofollow(source, O_RDONLY);	if (ifd < 0) {		int save_errno = errno;		rsyserr(FERROR_XFER, errno, "open %s", full_fname(source));		errno = save_errno;		return -1;	} 	if (tmpfilefd >= 0) {		ofd = tmpfilefd;	} else {		ofd = unlink_and_reopen(dest, mode);		if (ofd < 0) {			int save_errno = errno;			close(ifd);			errno = save_errno;			return -1;		}	} #ifdef SUPPORT_PREALLOCATION	if (preallocate_files) {		STRUCT_STAT srcst; 		/* Try to preallocate enough space for file's eventual length.  Can		 * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */		if (do_fstat(ifd, &srcst) < 0)			rsyserr(FWARNING, errno, "fstat %s", full_fname(source));		else if (srcst.st_size > 0) {			prealloc_len = do_fallocate(ofd, 0, srcst.st_size);			if (prealloc_len < 0)				rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(dest));		}	}#endif 	while ((len = safe_read(ifd, buf, sizeof buf)) > 0) {		if (full_write(ofd, buf, len) < 0) {			int save_errno = errno;			rsyserr(FERROR_XFER, errno, "write %s", full_fname(dest));			close(ifd);			close(ofd);			errno = save_errno;			return -1;		}		offset += len;	} 	if (len < 0) {		int save_errno = errno;		rsyserr(FERROR_XFER, errno, "read %s", full_fname(source));		close(ifd);		close(ofd);		errno = save_errno;		return -1;	} 	/* Source file might have shrunk since we fstatted it.	 * Cut off any extra preallocated zeros from dest file. */	if (offset < prealloc_len) {#ifdef HAVE_FTRUNCATE		/* If we fail to truncate, the dest file may be wrong, so we		 * must trigger the "partial transfer" error. */		if (do_ftruncate(ofd, offset) < 0)			rsyserr(FERROR_XFER, errno, "ftruncate %s", full_fname(dest));#else		rprintf(FERROR_XFER, "no ftruncate for over-long pre-alloc: %s", full_fname(dest));#endif	}

Lines 1–500 of 2069.