]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
copy: add COPY_SYNCFS flag
authorLennart Poettering <lennart@poettering.net>
Fri, 26 Feb 2021 09:27:00 +0000 (10:27 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 2 Aug 2021 15:24:09 +0000 (17:24 +0200)
When copying large directory trees it should be a better idea to sync
the whole fs once when we are done instead of individually for each
file, hence add COPY_SYNCFS.

As opposed to COPY_FSYNC/COPY_FSYNC_FULL this only really applies to the
top-level directory, after completion of the whole copy.

src/shared/copy.c
src/shared/copy.h

index 3ac904c6c972b1d2c40558f181bbc697e663a4af..e47b56ce915f2e97de8671767b3d08d7d51be865 100644 (file)
@@ -984,7 +984,17 @@ int copy_tree_at_full(
         if (r < 0)
                 return r;
 
-        if (copy_flags & COPY_FSYNC_FULL) {
+        if (S_ISDIR(st.st_mode) && (copy_flags & COPY_SYNCFS)) {
+                /* If the top-level inode is a directory run syncfs() now. */
+                r = syncfs_path(fdt, to);
+                if (r < 0)
+                        return r;
+        } else if ((copy_flags & (COPY_FSYNC_FULL|COPY_SYNCFS)) != 0) {
+                /* fsync() the parent dir of what we just copied if COPY_FSYNC_FULL is set. Also do this in
+                 * case COPY_SYNCFS is set but the top-level inode wasn't actually a directory. We do this so that
+                 * COPY_SYNCFS provides reasonable synchronization semantics on any kind of inode: when the
+                 * copy operation is done the whole inode — regardless of its type — and all its children
+                 * will be synchronized to disk. */
                 r = fsync_parent_at(fdt, to);
                 if (r < 0)
                         return r;
@@ -993,6 +1003,16 @@ int copy_tree_at_full(
         return 0;
 }
 
+static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) {
+
+        if (copy_flags & COPY_SYNCFS)
+                return syncfs_path(AT_FDCWD, path);
+        if (copy_flags & COPY_FSYNC_FULL)
+                return fsync_parent_at(AT_FDCWD, path);
+
+        return 0;
+}
+
 int copy_directory_fd_full(
                 int dirfd,
                 const char *to,
@@ -1029,11 +1049,9 @@ int copy_directory_fd_full(
         if (r < 0)
                 return r;
 
-        if (copy_flags & COPY_FSYNC_FULL) {
-                r = fsync_parent_at(AT_FDCWD, to);
-                if (r < 0)
-                        return r;
-        }
+        r = sync_dir_by_flags(to, copy_flags);
+        if (r < 0)
+                return r;
 
         return 0;
 }
@@ -1074,11 +1092,9 @@ int copy_directory_full(
         if (r < 0)
                 return r;
 
-        if (copy_flags & COPY_FSYNC_FULL) {
-                r = fsync_parent_at(AT_FDCWD, to);
-                if (r < 0)
-                        return r;
-        }
+        r = sync_dir_by_flags(to, copy_flags);
+        if (r < 0)
+                return r;
 
         return 0;
 }
index 81990d37431361f28e44437f3e07b85080dbec04..0c81760d8fd2b0221c6b73f6b47e49b2ec842f77 100644 (file)
@@ -21,6 +21,7 @@ typedef enum CopyFlags {
         COPY_HARDLINKS   = 1 << 8,  /* Try to reproduce hard links */
         COPY_FSYNC       = 1 << 9,  /* fsync() after we are done */
         COPY_FSYNC_FULL  = 1 << 10, /* fsync_full() after we are done */
+        COPY_SYNCFS      = 1 << 11, /* syncfs() the *top-level* dir after we are done */
 } CopyFlags;
 
 typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);