Applying the preallocate patch.
[rsync/rsync.git] / util.c
diff --git a/util.c b/util.c
index ee65f54..3f611d1 100644 (file)
--- a/util.c
+++ b/util.c
 #include "itypes.h"
 #include "inums.h"
 
-extern int dry_run;
 extern int module_id;
 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;
@@ -123,10 +123,11 @@ NORETURN void overflow_exit(const char *str)
        exit_cleanup(RERR_MALLOC);
 }
 
+/* This returns 0 for success, 1 for a symlink if symlink time-setting
+ * is not possible, or -1 for any other error. */
 int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
 {
-       if (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(mode))
-               return 1;
+       static int switch_step = 0;
 
        if (DEBUG_GTE(TIME, 1)) {
                rprintf(FINFO, "set modtime of %s to (%ld) %s",
@@ -134,42 +135,49 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
                        asctime(localtime(&modtime)));
        }
 
-       if (dry_run)
-               return 0;
-
-       {
+       switch (switch_step) {
 #ifdef HAVE_UTIMENSAT
-               struct timespec t[2];
-               t[0].tv_sec = 0;
-               t[0].tv_nsec = UTIME_NOW;
-               t[1].tv_sec = modtime;
-               t[1].tv_nsec = mod_nsec;
-               return utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW);
-#elif defined HAVE_UTIMES || defined HAVE_LUTIMES
-               struct timeval t[2];
-               t[0].tv_sec = time(NULL);
-               t[0].tv_usec = 0;
-               t[1].tv_sec = modtime;
-               t[1].tv_usec = mod_nsec / 1000;
-# ifdef HAVE_LUTIMES
-               return lutimes(fname, t);
-# else
-               return utimes(fname, t);
-# endif
-#elif defined HAVE_STRUCT_UTIMBUF
-               struct utimbuf tbuf;
-               tbuf.actime = time(NULL);
-               tbuf.modtime = modtime;
-               return utime(fname,&tbuf);
-#elif defined HAVE_UTIME
-               time_t t[2];
-               t[0] = time(NULL);
-               t[1] = modtime;
-               return utime(fname,t);
+#include "case_N.h"
+               if (do_utimensat(fname, modtime, mod_nsec) == 0)
+                       break;
+               if (errno != ENOSYS)
+                       return -1;
+               switch_step++;
+               /* FALLTHROUGH */
+#endif
+
+#ifdef HAVE_LUTIMES
+#include "case_N.h"
+               if (do_lutimes(fname, modtime, mod_nsec) == 0)
+                       break;
+               if (errno != ENOSYS)
+                       return -1;
+               switch_step++;
+               /* FALLTHROUGH */
+#endif
+
+#include "case_N.h"
+               switch_step++;
+               if (preserve_times & PRESERVE_LINK_TIMES) {
+                       preserve_times &= ~PRESERVE_LINK_TIMES;
+                       if (S_ISLNK(mode))
+                               return 1;
+               }
+               /* FALLTHROUGH */
+
+#include "case_N.h"
+#ifdef HAVE_UTIMES
+               if (do_utimes(fname, modtime, mod_nsec) == 0)
+                       break;
 #else
-#error No file-time-modification routine found!
+               if (do_utime(fname, modtime, mod_nsec) == 0)
+                       break;
 #endif
+
+               return -1;
        }
+
+       return 0;
 }
 
 /* Create any necessary directories in fname.  Any missing directories are
@@ -308,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;
@@ -338,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;
@@ -347,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) {
@@ -363,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",