/* * 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. Load more
/* * 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 }