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 + #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 + #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 #include ]], [[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..7b9caf7 100644 --- a/fileio.c +++ b/fileio.c @@ -34,8 +34,11 @@ #define ALIGNED_LENGTH(len) ((((len) - 1) | (ALIGN_BOUNDRY-1)) + 1) extern int sparse_files; +extern int inplace; +extern int preallocate_files; static OFF_T sparse_seek = 0; +static OFF_T sparse_last_write = 0; int sparse_end(int f, OFF_T size) { @@ -63,8 +66,9 @@ 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. */ +static int write_sparse(int f, OFF_T offset, const char *buf, int len) { int l1 = 0, l2 = 0; int ret; @@ -77,9 +81,14 @@ 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 (!inplace && !preallocate_files) + do_lseek(f, sparse_seek, SEEK_CUR); + else if (do_punch_hole(f, sparse_last_write, sparse_seek) < 0) + return -1; + } sparse_seek = l2; + sparse_last_write = offset + len - l2; while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) { if (ret < 0 && errno == EINTR) @@ -121,9 +130,11 @@ int flush_write_file(int f) /* * write_file does not allow incomplete writes. It loops internally - * until len bytes are written or errno is set. + * until len bytes are written or errno is set. Note that "offset" + * cannot be used to set a different location in the file -- it is + * just used to interface with write_sparse(). */ -int write_file(int f, char *buf, int len) +int write_file(int f, OFF_T offset, char *buf, int len) { int ret = 0; @@ -131,7 +142,7 @@ 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, offset, buf, len1); } else { if (!wf_writeBuf) { wf_writeBufSize = WRITE_SIZE * 8; diff --git a/options.c b/options.c index 308443b..85b1f08 100644 --- a/options.c +++ b/options.c @@ -2237,13 +2237,15 @@ int parse_arguments(int *argc_p, const char ***argv_p) bwlimit_writemax = 512; } +#if !defined HAVE_FALLOCATE || !defined HAVE_FALLOC_FL_PUNCH_HOLE 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"); + "This rsync cannot combine --sparse with --inplace\n"); return 0; } +#endif if (append_mode) { if (whole_file > 0) { diff --git a/receiver.c b/receiver.c index f9b97dd..d702603 100644 --- a/receiver.c +++ b/receiver.c @@ -318,7 +318,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, offset, data, i) != i) goto report_write_error; offset += i; continue; @@ -362,7 +362,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, continue; } } - if (fd != -1 && map && write_file(fd, map, len) != (int)len) + if (fd != -1 && map && write_file(fd, offset, map, len) != (int)len) goto report_write_error; offset += len; } diff --git a/syscall.c b/syscall.c index ecca2f1..ae5eeff 100644 --- a/syscall.c +++ b/syscall.c @@ -444,6 +444,40 @@ int do_fallocate(int fd, OFF_T offset, OFF_T length) } #endif +/* NOTE: the offset must be the same as the current file position! */ +int do_punch_hole(int fd, OFF_T offset, int len) +{ +#ifdef HAVE_FALLOCATE +# ifdef HAVE_FALLOC_FL_PUNCH_HOLE + if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, len) == 0) { + do_lseek(fd, offset + len, SEEK_SET); + return 0; + } +# endif +# ifdef HAVE_FALLOC_FL_ZERO_RANGE + if (fallocate(fd, FALLOC_FL_ZERO_RANGE, offset, len) == 0) { + do_lseek(fd, offset + len, SEEK_SET); + 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