This patch adds the --preallocate option that asks rsync to preallocate the copied files. This slows down the copy, but should reduce fragmentation on systems that need that. To use this patch, run these commands for a successful build: patch -p1 0) { + /* Preallocate enough space for file's eventual length if + * possible; seems to reduce fragmentation on Windows. */ + if (posix_fallocate(fd, 0, total_size) == 0) + preallocated_len = total_size; + else + rsyserr(FINFO, errno, "preallocate %s", full_fname(fname)); + } +#endif + read_sum_head(f_in, &sum); if (fd_r >= 0 && size_r > 0) { @@ -244,8 +258,18 @@ static int receive_data(int f_in, char * goto report_write_error; #ifdef HAVE_FTRUNCATE - if (inplace && fd != -1) - ftruncate(fd, offset); + /* 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 SUPPORT_PREALLOCATION + || preallocated_len > offset +#endif + ) && fd != -1) + if (ftruncate(fd, offset) < 0) + /* If we fail to truncate, the dest file may be wrong, so we + * must trigger the "partial transfer" error. */ + rsyserr(FERROR, errno, "ftruncate %s", full_fname(fname)); #endif if (do_progress) --- old/rsync.h +++ new/rsync.h @@ -493,6 +493,10 @@ struct idev { #define IN_LOOPBACKNET 127 #endif +#if defined HAVE_FTRUNCATE && defined HAVE_POSIX_FALLOCATE +#define SUPPORT_PREALLOCATION 1 +#endif + #define GID_NONE ((gid_t)-1) #define HL_CHECK_MASTER 0 --- old/t_stub.c +++ new/t_stub.c @@ -23,6 +23,7 @@ #include "rsync.h" int modify_window = 0; +int preallocate_files = 0; int module_id = -1; int relative_paths = 0; int human_readable = 0; --- old/util.c +++ new/util.c @@ -25,6 +25,7 @@ extern int verbose; extern int dry_run; +extern int preallocate_files; extern int module_id; extern int modify_window; extern int relative_paths; @@ -270,6 +271,10 @@ int copy_file(const char *source, const int ofd; char buf[1024 * 8]; int len; /* Number of bytes read into `buf'. */ +#ifdef SUPPORT_PREALLOCATION + int preallocated_len = 0; + int offset = 0; +#endif ifd = do_open(source, O_RDONLY, 0); if (ifd == -1) { @@ -289,7 +294,27 @@ int copy_file(const char *source, const return -1; } +#ifdef SUPPORT_PREALLOCATION + if (preallocate_files) { + /* Preallocate enough space for file's eventual length if + * possible; seems to reduce fragmentation on Windows. */ + STRUCT_STAT srcst; + if (do_fstat(ifd, &srcst) == 0) { + if (srcst.st_size > 0) { + if (posix_fallocate(ofd, 0, srcst.st_size) == 0) + preallocated_len = srcst.st_size; + else + rsyserr(FINFO, errno, "posix_fallocate %s", full_fname(dest)); + } + } else + rsyserr(FINFO, errno, "fstat %s", full_fname(source)); + } +#endif + while ((len = safe_read(ifd, buf, sizeof buf)) > 0) { +#ifdef SUPPORT_PREALLOCATION + offset += len; +#endif if (full_write(ofd, buf, len) < 0) { rsyserr(FERROR, errno, "write %s", full_fname(dest)); close(ifd); @@ -310,6 +335,16 @@ int copy_file(const char *source, const full_fname(source)); } +#ifdef SUPPORT_PREALLOCATION + /* Source file might have shrunk since we fstatted it. + * Cut off any extra preallocated zeros from dest file. */ + if (preallocated_len > offset) + if (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, errno, "ftruncate %s", full_fname(dest)); +#endif + if (close(ofd) < 0) { rsyserr(FERROR, errno, "close failed on %s", full_fname(dest));