diff options
Diffstat (limited to 'net/rsync/patches/003-support-sparse-combined-with.patch')
-rw-r--r-- | net/rsync/patches/003-support-sparse-combined-with.patch | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/net/rsync/patches/003-support-sparse-combined-with.patch b/net/rsync/patches/003-support-sparse-combined-with.patch new file mode 100644 index 000000000..259f41108 --- /dev/null +++ b/net/rsync/patches/003-support-sparse-combined-with.patch @@ -0,0 +1,593 @@ +commit f3873b3d88b61167b106e7b9227a20147f8f6197 +Author: Wayne Davison <wayned@samba.org> +Date: Mon Oct 10 11:49:50 2016 -0700 + + Support --sparse combined with --preallocate or --inplace. + + The new code tries to punch holes in the destination file using newer + Linux fallocate features. It also supports a --whole-file + --sparse + + --inplace copy on any filesystem by truncating the destination file. + +diff --git a/configure.ac b/configure.ac +index b5e4049..e01e124 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -614,6 +614,36 @@ if test x"$rsync_cv_have_fallocate" = x"yes"; then + AC_DEFINE(HAVE_FALLOCATE, 1, [Define to 1 if you have the fallocate function and it compiles and links without error]) + fi + ++AC_MSG_CHECKING([for FALLOC_FL_PUNCH_HOLE]) ++AC_PREPROC_IFELSE([AC_LANG_SOURCE([[ ++ #define _GNU_SOURCE 1 ++ #include <linux/falloc.h> ++ #ifndef FALLOC_FL_PUNCH_HOLE ++ #error FALLOC_FL_PUNCH_HOLE is missing ++ #endif ++ ]])], [ ++ AC_MSG_RESULT([yes]) ++ AC_DEFINE([HAVE_FALLOC_FL_PUNCH_HOLE], [1], [Define if FALLOC_FL_PUNCH_HOLE is available.]) ++ ], [ ++ AC_MSG_RESULT([no]) ++ ] ++) ++ ++AC_MSG_CHECKING([for FALLOC_FL_ZERO_RANGE]) ++AC_PREPROC_IFELSE([AC_LANG_SOURCE([[ ++ #define _GNU_SOURCE 1 ++ #include <linux/falloc.h> ++ #ifndef FALLOC_FL_ZERO_RANGE ++ #error FALLOC_FL_ZERO_RANGE is missing ++ #endif ++ ]])], [ ++ AC_MSG_RESULT([yes]) ++ AC_DEFINE([HAVE_FALLOC_FL_ZERO_RANGE], [1], [Define if FALLOC_FL_ZERO_RANGE is available.]) ++ ], [ ++ AC_MSG_RESULT([no]) ++ ] ++) ++ + AC_CACHE_CHECK([for SYS_fallocate],rsync_cv_have_sys_fallocate,[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/syscall.h> + #include <sys/types.h>]], [[syscall(SYS_fallocate, 0, 0, (loff_t)0, (loff_t)0);]])],[rsync_cv_have_sys_fallocate=yes],[rsync_cv_have_sys_fallocate=no])]) +diff --git a/fileio.c b/fileio.c +index 70e079d..1e8a562 100644 +--- a/fileio.c ++++ b/fileio.c +@@ -35,7 +35,10 @@ + + extern int sparse_files; + ++OFF_T preallocated_len = 0; ++ + static OFF_T sparse_seek = 0; ++static OFF_T sparse_past_write = 0; + + int sparse_end(int f, OFF_T size) + { +@@ -63,8 +66,10 @@ int sparse_end(int f, OFF_T size) + return ret; + } + +- +-static int write_sparse(int f, char *buf, int len) ++/* Note that the offset is just the caller letting us know where ++ * the current file position is in the file. The use_seek arg tells ++ * us that we should seek over matching data instead of writing it. */ ++static int write_sparse(int f, int use_seek, OFF_T offset, const char *buf, int len) + { + int l1 = 0, l2 = 0; + int ret; +@@ -77,9 +82,24 @@ static int write_sparse(int f, char *buf, int len) + if (l1 == len) + return len; + +- if (sparse_seek) +- do_lseek(f, sparse_seek, SEEK_CUR); ++ if (sparse_seek) { ++ if (sparse_past_write >= preallocated_len) { ++ if (do_lseek(f, sparse_seek, SEEK_CUR) < 0) ++ return -1; ++ } else if (do_punch_hole(f, sparse_past_write, sparse_seek) < 0) { ++ sparse_seek = 0; ++ return -1; ++ } ++ } + sparse_seek = l2; ++ sparse_past_write = offset + len - l2; ++ ++ if (use_seek) { ++ /* The in-place data already matches. */ ++ if (do_lseek(f, len - (l1+l2), SEEK_CUR) < 0) ++ return -1; ++ return len; ++ } + + while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) { + if (ret < 0 && errno == EINTR) +@@ -96,7 +116,6 @@ static int write_sparse(int f, char *buf, int len) + return len; + } + +- + static char *wf_writeBuf; + static size_t wf_writeBufSize; + static size_t wf_writeBufCnt; +@@ -118,12 +137,10 @@ int flush_write_file(int f) + return ret; + } + +- +-/* +- * write_file does not allow incomplete writes. It loops internally +- * until len bytes are written or errno is set. +- */ +-int write_file(int f, char *buf, int len) ++/* write_file does not allow incomplete writes. It loops internally ++ * until len bytes are written or errno is set. Note that use_seek and ++ * offset are only used in sparse processing (see write_sparse()). */ ++int write_file(int f, int use_seek, OFF_T offset, const char *buf, int len) + { + int ret = 0; + +@@ -131,7 +148,8 @@ int write_file(int f, char *buf, int len) + int r1; + if (sparse_files > 0) { + int len1 = MIN(len, SPARSE_WRITE_SIZE); +- r1 = write_sparse(f, buf, len1); ++ r1 = write_sparse(f, use_seek, offset, buf, len1); ++ offset += r1; + } else { + if (!wf_writeBuf) { + wf_writeBufSize = WRITE_SIZE * 8; +@@ -164,6 +182,30 @@ int write_file(int f, char *buf, int len) + return ret; + } + ++/* An in-place update found identical data at an identical location. We either ++ * just seek past it, or (for an in-place sparse update), we give the data to ++ * the sparse processor with the use_seek flag set. */ ++int skip_matched(int fd, OFF_T offset, const char *buf, int len) ++{ ++ OFF_T pos; ++ ++ if (sparse_files > 0) { ++ if (write_file(fd, 1, offset, buf, len) != len) ++ return -1; ++ return 0; ++ } ++ ++ if (flush_write_file(fd) < 0) ++ return -1; ++ ++ if ((pos = do_lseek(fd, len, SEEK_CUR)) != offset + len) { ++ rsyserr(FERROR_XFER, errno, "lseek returned %s, not %s", ++ big_num(pos), big_num(offset)); ++ return -1; ++ } ++ ++ return 0; ++} + + /* This provides functionality somewhat similar to mmap() but using read(). + * It gives sliding window access to a file. mmap() is not used because of +@@ -271,7 +313,6 @@ char *map_ptr(struct map_struct *map, OFF_T offset, int32 len) + return map->p + align_fudge; + } + +- + int unmap_file(struct map_struct *map) + { + int ret; +diff --git a/options.c b/options.c +index 308443b..6ba13b7 100644 +--- a/options.c ++++ b/options.c +@@ -714,7 +714,7 @@ void usage(enum logcode F) + #ifdef SUPPORT_XATTRS + rprintf(F," --fake-super store/recover privileged attrs using xattrs\n"); + #endif +- rprintf(F," -S, --sparse handle sparse files efficiently\n"); ++ rprintf(F," -S, --sparse turn sequences of nulls into sparse blocks\n"); + #ifdef SUPPORT_PREALLOCATION + rprintf(F," --preallocate allocate dest files before writing them\n"); + #else +@@ -2237,14 +2237,6 @@ int parse_arguments(int *argc_p, const char ***argv_p) + bwlimit_writemax = 512; + } + +- if (sparse_files && inplace) { +- /* Note: we don't check for this below, because --append is +- * OK with --sparse (as long as redos are handled right). */ +- snprintf(err_buf, sizeof err_buf, +- "--sparse cannot be used with --inplace\n"); +- return 0; +- } +- + if (append_mode) { + if (whole_file > 0) { + snprintf(err_buf, sizeof err_buf, +diff --git a/receiver.c b/receiver.c +index f9b97dd..bed5328 100644 +--- a/receiver.c ++++ b/receiver.c +@@ -49,6 +49,7 @@ 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 allowed_lull; + extern int delay_updates; +@@ -61,6 +62,9 @@ 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; ++#ifdef SUPPORT_PREALLOCATION ++extern OFF_T preallocated_len; ++#endif + + static struct bitbag *delayed_bits = NULL; + static int phase = 0, redoing = 0; +@@ -241,22 +245,25 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, + char *data; + int32 i; + char *map = NULL; +-#ifdef SUPPORT_PREALLOCATION +-#ifdef PREALLOCATE_NEEDS_TRUNCATE +- OFF_T preallocated_len = 0; +-#endif + ++#ifdef SUPPORT_PREALLOCATION + if (preallocate_files && fd != -1 && total_size > 0 && (!inplace || 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 (do_fallocate(fd, 0, total_size) == 0) { +-#ifdef PREALLOCATE_NEEDS_TRUNCATE +- preallocated_len = total_size; +-#endif +- } else ++ if ((preallocated_len = do_fallocate(fd, 0, total_size)) < 0) + rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(fname)); +- } ++ } else ++#endif ++ if (inplace) { ++#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); + +@@ -318,7 +325,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, + + sum_update(data, i); + +- if (fd != -1 && write_file(fd,data,i) != i) ++ if (fd != -1 && write_file(fd, 0, offset, data, i) != i) + goto report_write_error; + offset += i; + continue; +@@ -348,37 +355,33 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, + + if (updating_basis_or_equiv) { + if (offset == offset2 && fd != -1) { +- OFF_T pos; +- if (flush_write_file(fd) < 0) ++ if (skip_matched(fd, offset, map, len) < 0) + goto report_write_error; + offset += len; +- if ((pos = do_lseek(fd, len, SEEK_CUR)) != offset) { +- rsyserr(FERROR_XFER, errno, +- "lseek of %s returned %s, not %s", +- full_fname(fname), +- big_num(pos), big_num(offset)); +- exit_cleanup(RERR_FILEIO); +- } + continue; + } + } +- if (fd != -1 && map && write_file(fd, map, len) != (int)len) ++ if (fd != -1 && map && write_file(fd, 0, offset, map, len) != (int)len) + goto report_write_error; + offset += len; + } + +- if (flush_write_file(fd) < 0) +- goto report_write_error; ++ if (fd != -1 && offset > 0) { ++ if (sparse_files > 0) { ++ if (sparse_end(fd, offset) != 0) ++ goto report_write_error; ++ } else if (flush_write_file(fd) < 0) { ++ report_write_error: ++ rsyserr(FERROR_XFER, errno, "write failed on %s", full_fname(fname)); ++ exit_cleanup(RERR_FILEIO); ++ } ++ } + + #ifdef HAVE_FTRUNCATE + /* inplace: New data could be shorter than old data. + * preallocate_files: total_size could have been an overestimate. + * Cut off any extra preallocated zeros from dest file. */ +- if ((inplace +-#ifdef PREALLOCATE_NEEDS_TRUNCATE +- || preallocated_len > offset +-#endif +- ) && fd != -1 && do_ftruncate(fd, offset) < 0) { ++ if ((inplace || preallocated_len > offset) && fd != -1 && do_ftruncate(fd, offset) < 0) { + rsyserr(FERROR_XFER, errno, "ftruncate failed on %s", + full_fname(fname)); + } +@@ -387,13 +390,6 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, + if (INFO_GTE(PROGRESS, 1)) + end_progress(total_size); + +- if (fd != -1 && offset > 0 && sparse_end(fd, offset) != 0) { +- report_write_error: +- rsyserr(FERROR_XFER, errno, "write failed on %s", +- full_fname(fname)); +- exit_cleanup(RERR_FILEIO); +- } +- + checksum_len = sum_end(file_sum1); + + if (mapbuf) +diff --git a/rsync.yo b/rsync.yo +index bfe43b9..d1e6fdf 100644 +--- a/rsync.yo ++++ b/rsync.yo +@@ -376,7 +376,7 @@ to the detailed description below for a complete description. verb( + -J, --omit-link-times omit symlinks from --times + --super receiver attempts super-user activities + --fake-super store/recover privileged attrs using xattrs +- -S, --sparse handle sparse files efficiently ++ -S, --sparse turn sequences of nulls into sparse blocks + --preallocate allocate dest files before writing + -n, --dry-run perform a trial run with no changes made + -W, --whole-file copy files whole (w/o delta-xfer algorithm) +@@ -873,9 +873,7 @@ the same or longer than the size on the sender, the file is skipped. This + does not interfere with the updating of a file's non-content attributes + (e.g. permissions, ownership, etc.) when the file does not need to be + transferred, nor does it affect the updating of any non-regular files. +-Implies bf(--inplace), +-but does not conflict with bf(--sparse) (since it is always extending a +-file's length). ++Implies bf(--inplace). + + The use of bf(--append) can be dangerous if you aren't 100% sure that the files + that are longer have only grown by the appending of data onto the end. You +@@ -1252,20 +1250,30 @@ This option is overridden by both bf(--super) and bf(--no-super). + See also the "fake super" setting in the daemon's rsyncd.conf file. + + dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take +-up less space on the destination. Conflicts with bf(--inplace) because it's +-not possible to overwrite data in a sparse fashion. ++up less space on the destination. If combined with bf(--inplace) the ++file created might not end up with sparse blocks with some combinations ++of kernel version and/or filesystem type. If bf(--whole-file) is in ++effect (e.g. for a local copy) then it will always work because rsync ++truncates the file prior to writing out the updated version. ++ ++Note that versions of rsync older than 3.1.3 will reject the combination of ++bf(--sparse) and bf(--inplace). + + dit(bf(--preallocate)) This tells the receiver to allocate each destination +-file to its eventual size before writing data to the file. Rsync will only use +-the real filesystem-level preallocation support provided by Linux's ++file to its eventual size before writing data to the file. Rsync will only ++use the real filesystem-level preallocation support provided by Linux's + bf(fallocate)(2) system call or Cygwin's bf(posix_fallocate)(3), not the slow +-glibc implementation that writes a zero byte into each block. ++glibc implementation that writes a null byte into each block. + + Without this option, larger files may not be entirely contiguous on the + filesystem, but with this option rsync will probably copy more slowly. If the + destination is not an extent-supporting filesystem (such as ext4, xfs, NTFS, + etc.), this option may have no positive effect at all. + ++If combined with bf(--sparse), the file will only have sparse blocks (as ++opposed to allocated sequences of null bytes) if the kernel version and ++filesystem type support creating holes in the allocated data. ++ + dit(bf(-n, --dry-run)) This makes rsync perform a trial run that doesn't + make any changes (and produces mostly the same output as a real run). It + is most commonly used in combination with the bf(-v, --verbose) and/or +diff --git a/syscall.c b/syscall.c +index ecca2f1..fa53b63 100644 +--- a/syscall.c ++++ b/syscall.c +@@ -38,6 +38,8 @@ extern int am_root; + extern int am_sender; + extern int read_only; + extern int list_only; ++extern int inplace; ++extern int preallocate_files; + extern int preserve_perms; + extern int preserve_executability; + +@@ -423,27 +425,80 @@ int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec)) + #endif + + #ifdef SUPPORT_PREALLOCATION +-int do_fallocate(int fd, OFF_T offset, OFF_T length) +-{ + #ifdef FALLOC_FL_KEEP_SIZE + #define DO_FALLOC_OPTIONS FALLOC_FL_KEEP_SIZE + #else + #define DO_FALLOC_OPTIONS 0 + #endif ++ ++OFF_T do_fallocate(int fd, OFF_T offset, OFF_T length) ++{ ++ int opts = inplace || preallocate_files ? 0 : DO_FALLOC_OPTIONS; ++ int ret; + RETURN_ERROR_IF(dry_run, 0); + RETURN_ERROR_IF_RO_OR_LO; ++ if (length & 1) /* make the length not match the desired length */ ++ length++; ++ else ++ length--; + #if defined HAVE_FALLOCATE +- return fallocate(fd, DO_FALLOC_OPTIONS, offset, length); ++ ret = fallocate(fd, opts, offset, length); + #elif defined HAVE_SYS_FALLOCATE +- return syscall(SYS_fallocate, fd, DO_FALLOC_OPTIONS, (loff_t)offset, (loff_t)length); ++ ret = syscall(SYS_fallocate, fd, opts, (loff_t)offset, (loff_t)length); + #elif defined HAVE_EFFICIENT_POSIX_FALLOCATE +- return posix_fallocate(fd, offset, length); ++ ret = posix_fallocate(fd, offset, length); + #else + #error Coding error in SUPPORT_PREALLOCATION logic. + #endif ++ if (ret < 0) ++ return ret; ++ if (opts == 0) { ++ STRUCT_STAT st; ++ if (do_fstat(fd, &st) < 0) ++ return length; ++ return st.st_blocks * 512; ++ } ++ return 0; + } + #endif + ++/* Punch a hole at pos for len bytes. The current file position must be at pos and will be ++ * changed to be at pos + len. */ ++int do_punch_hole(int fd, UNUSED(OFF_T pos), int len) ++{ ++#ifdef HAVE_FALLOCATE ++# ifdef HAVE_FALLOC_FL_PUNCH_HOLE ++ if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, pos, len) == 0) { ++ if (do_lseek(fd, len, SEEK_CUR) != pos + len) ++ return -1; ++ return 0; ++ } ++# endif ++# ifdef HAVE_FALLOC_FL_ZERO_RANGE ++ if (fallocate(fd, FALLOC_FL_ZERO_RANGE, pos, len) == 0) { ++ if (do_lseek(fd, len, SEEK_CUR) != pos + len) ++ return -1; ++ return 0; ++ } ++# endif ++#endif ++ { ++ char zeros[4096]; ++ memset(zeros, 0, sizeof zeros); ++ while (len > 0) { ++ int chunk = len > (int)sizeof zeros ? (int)sizeof zeros : len; ++ int wrote = write(fd, zeros, chunk); ++ if (wrote <= 0) { ++ if (wrote < 0 && errno == EINTR) ++ continue; ++ return -1; ++ } ++ len -= wrote; ++ } ++ } ++ return 0; ++} ++ + int do_open_nofollow(const char *pathname, int flags) + { + #ifndef O_NOFOLLOW +diff --git a/t_stub.c b/t_stub.c +index 26951a6..fc1ee3b 100644 +--- a/t_stub.c ++++ b/t_stub.c +@@ -21,6 +21,7 @@ + + #include "rsync.h" + ++int inplace = 0; + int modify_window = 0; + int preallocate_files = 0; + int protect_args = 0; +diff --git a/tls.c b/tls.c +index 45d1e10..d5a2896 100644 +--- a/tls.c ++++ b/tls.c +@@ -51,6 +51,8 @@ int link_owner = 0; + int nsec_times = 0; + int preserve_perms = 0; + int preserve_executability = 0; ++int preallocate_files = 0; ++int inplace = 0; + + #ifdef SUPPORT_XATTRS + +diff --git a/trimslash.c b/trimslash.c +index 207eaf2..5db6f3e 100644 +--- a/trimslash.c ++++ b/trimslash.c +@@ -28,6 +28,8 @@ int read_only = 1; + int list_only = 0; + int preserve_perms = 0; + int preserve_executability = 0; ++int preallocate_files = 0; ++int inplace = 0; + + int + main(int argc, char **argv) +diff --git a/util.c b/util.c +index ca38f3e..49c5b71 100644 +--- a/util.c ++++ b/util.c +@@ -323,9 +323,7 @@ int copy_file(const char *source, const char *dest, int ofd, mode_t mode) + int ifd; + char buf[1024 * 8]; + int len; /* Number of bytes read into `buf'. */ +-#ifdef PREALLOCATE_NEEDS_TRUNCATE +- OFF_T preallocated_len = 0, offset = 0; +-#endif ++ OFF_T prealloc_len = 0, offset = 0; + + if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { + int save_errno = errno; +@@ -365,11 +363,8 @@ int copy_file(const char *source, const char *dest, int ofd, mode_t mode) + if (do_fstat(ifd, &srcst) < 0) + rsyserr(FWARNING, errno, "fstat %s", full_fname(source)); + else if (srcst.st_size > 0) { +- if (do_fallocate(ofd, 0, srcst.st_size) == 0) { +-#ifdef PREALLOCATE_NEEDS_TRUNCATE +- preallocated_len = srcst.st_size; +-#endif +- } else ++ prealloc_len = do_fallocate(ofd, 0, srcst.st_size); ++ if (prealloc_len < 0) + rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(dest)); + } + } +@@ -384,9 +379,7 @@ int copy_file(const char *source, const char *dest, int ofd, mode_t mode) + errno = save_errno; + return -1; + } +-#ifdef PREALLOCATE_NEEDS_TRUNCATE + offset += len; +-#endif + } + + if (len < 0) { +@@ -403,15 +396,13 @@ int copy_file(const char *source, const char *dest, int ofd, mode_t mode) + full_fname(source)); + } + +-#ifdef PREALLOCATE_NEEDS_TRUNCATE + /* Source file might have shrunk since we fstatted it. + * Cut off any extra preallocated zeros from dest file. */ +- if (offset < preallocated_len && do_ftruncate(ofd, offset) < 0) { ++ if (offset < prealloc_len && do_ftruncate(ofd, offset) < 0) { + /* If we fail to truncate, the dest file may be wrong, so we + * must trigger the "partial transfer" error. */ + rsyserr(FERROR_XFER, errno, "ftruncate %s", full_fname(dest)); + } +-#endif + + if (close(ofd) < 0) { + int save_errno = errno; |