Applying the preallocate patch.
[rsync/rsync.git] / util.c
diff --git a/util.c b/util.c
index a53ea29..3f611d1 100644 (file)
--- a/util.c
+++ b/util.c
@@ -30,6 +30,7 @@ extern int modify_window;
 extern int relative_paths;
 extern int preserve_times;
 extern int preserve_xattrs;
+extern int preallocate_files;
 extern char *module_dir;
 extern unsigned int module_dirlen;
 extern char *partial_dir;
@@ -315,6 +316,9 @@ 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
 
        if ((ifd = do_open(source, O_RDONLY, 0)) < 0) {
                int save_errno = errno;
@@ -345,6 +349,25 @@ int copy_file(const char *source, const char *dest, int ofd, mode_t mode)
                }
        }
 
+#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) {
+                       if (do_fallocate(ofd, 0, srcst.st_size) == 0) {
+#ifdef PREALLOCATE_NEEDS_TRUNCATE
+                               preallocated_len = srcst.st_size;
+#endif
+                       } else
+                               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;
@@ -354,6 +377,9 @@ 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) {
@@ -370,6 +396,16 @@ 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 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;
                rsyserr(FERROR_XFER, errno, "close failed on %s",