]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
copy: optionally fsync() files after copying them
authorLennart Poettering <lennart@poettering.net>
Mon, 1 Feb 2021 16:48:32 +0000 (17:48 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 2 Aug 2021 15:24:03 +0000 (17:24 +0200)
As a safety precaution it makes sense to fsync() files after copying
them, and maybe even the directories they are contained in. Let's add a
flag for these two cases.

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

index bf02a89b5149b48125ee25861d1e9e99b5c659be..3ac904c6c972b1d2c40558f181bbc697e663a4af 100644 (file)
@@ -641,6 +641,13 @@ static int fd_copy_regular(
         (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
         (void) copy_xattr(fdf, fdt);
 
+        if (copy_flags & COPY_FSYNC) {
+                if (fsync(fdt) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+        }
+
         q = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
         if (q < 0) {
                 r = q;
@@ -933,6 +940,11 @@ static int fd_copy_directory(
                 (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
         }
 
+        if (copy_flags & COPY_FSYNC_FULL) {
+                if (fsync(fdt) < 0)
+                        return -errno;
+        }
+
         return r;
 }
 
@@ -949,6 +961,7 @@ int copy_tree_at_full(
                 void *userdata) {
 
         struct stat st;
+        int r;
 
         assert(from);
         assert(to);
@@ -957,17 +970,27 @@ int copy_tree_at_full(
                 return -errno;
 
         if (S_ISREG(st.st_mode))
-                return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
+                r = fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
         else if (S_ISDIR(st.st_mode))
-                return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+                r = fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
         else if (S_ISLNK(st.st_mode))
-                return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
+                r = fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
         else if (S_ISFIFO(st.st_mode))
-                return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
+                r = fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
-                return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
+                r = fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
         else
                 return -EOPNOTSUPP;
+        if (r < 0)
+                return r;
+
+        if (copy_flags & COPY_FSYNC_FULL) {
+                r = fsync_parent_at(fdt, to);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
 }
 
 int copy_directory_fd_full(
@@ -991,7 +1014,28 @@ int copy_directory_fd_full(
         if (r < 0)
                 return r;
 
-        return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+        r = fd_copy_directory(
+                        dirfd, NULL,
+                        &st,
+                        AT_FDCWD, to,
+                        st.st_dev,
+                        COPY_DEPTH_MAX,
+                        UID_INVALID, GID_INVALID,
+                        copy_flags,
+                        NULL, NULL,
+                        progress_path,
+                        progress_bytes,
+                        userdata);
+        if (r < 0)
+                return r;
+
+        if (copy_flags & COPY_FSYNC_FULL) {
+                r = fsync_parent_at(AT_FDCWD, to);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
 }
 
 int copy_directory_full(
@@ -1015,7 +1059,28 @@ int copy_directory_full(
         if (r < 0)
                 return r;
 
-        return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+        r = fd_copy_directory(
+                        AT_FDCWD, from,
+                        &st,
+                        AT_FDCWD, to,
+                        st.st_dev,
+                        COPY_DEPTH_MAX,
+                        UID_INVALID, GID_INVALID,
+                        copy_flags,
+                        NULL, NULL,
+                        progress_path,
+                        progress_bytes,
+                        userdata);
+        if (r < 0)
+                return r;
+
+        if (copy_flags & COPY_FSYNC_FULL) {
+                r = fsync_parent_at(AT_FDCWD, to);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
 }
 
 int copy_file_fd_full(
@@ -1052,6 +1117,15 @@ int copy_file_fd_full(
                 (void) copy_xattr(fdf, fdt);
         }
 
+        if (copy_flags & COPY_FSYNC_FULL) {
+                r = fsync_full(fdt);
+                if (r < 0)
+                        return r;
+        } else if (copy_flags & COPY_FSYNC) {
+                if (fsync(fdt) < 0)
+                        return -errno;
+        }
+
         return 0;
 }
 
@@ -1117,10 +1191,23 @@ int copy_file_full(
         if (chattr_mask != 0)
                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
 
+        if (copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL)) {
+                if (fsync(fdt) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+        }
+
         r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
         if (r < 0)
                 goto fail;
 
+        if (copy_flags & COPY_FSYNC_FULL) {
+                r = fsync_parent_at(AT_FDCWD, to);
+                if (r < 0)
+                        goto fail;
+        }
+
         return 0;
 
 fail:
@@ -1196,6 +1283,12 @@ int copy_file_atomic_full(
         if (fchmod(fdt, mode) < 0)
                 return -errno;
 
+        if ((copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL))) {
+                /* Sync the file */
+                if (fsync(fdt) < 0)
+                        return -errno;
+        }
+
         if (copy_flags & COPY_REPLACE) {
                 if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0)
                         return -errno;
@@ -1214,6 +1307,13 @@ int copy_file_atomic_full(
         if (r < 0)
                 goto fail;
 
+        if (copy_flags & COPY_FSYNC_FULL) {
+                /* Sync the parent directory */
+                r = fsync_parent_at(AT_FDCWD, to);
+                if (r < 0)
+                        goto fail;
+        }
+
         return 0;
 
 fail:
index b36ddfcb0157bfd8ff7482be8834afe987deecac..81990d37431361f28e44437f3e07b85080dbec04 100644 (file)
 #include <sys/types.h>
 
 typedef enum CopyFlags {
-        COPY_REFLINK     = 1 << 0, /* Try to reflink */
-        COPY_MERGE       = 1 << 1, /* Merge existing trees with our new one to copy */
-        COPY_REPLACE     = 1 << 2, /* Replace an existing file if there's one */
-        COPY_SAME_MOUNT  = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
-        COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
-        COPY_CRTIME      = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
-        COPY_SIGINT      = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
-        COPY_MAC_CREATE  = 1 << 7, /* Create files with the correct MAC label (currently SELinux only) */
-        COPY_HARDLINKS   = 1 << 8, /* Try to reproduce hard links */
+        COPY_REFLINK     = 1 << 0,  /* Try to reflink */
+        COPY_MERGE       = 1 << 1,  /* Merge existing trees with our new one to copy */
+        COPY_REPLACE     = 1 << 2,  /* Replace an existing file if there's one */
+        COPY_SAME_MOUNT  = 1 << 3,  /* Don't descend recursively into other file systems, across mount point boundaries */
+        COPY_MERGE_EMPTY = 1 << 4,  /* Merge an existing, empty directory with our new tree to copy */
+        COPY_CRTIME      = 1 << 5,  /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
+        COPY_SIGINT      = 1 << 6,  /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
+        COPY_MAC_CREATE  = 1 << 7,  /* Create files with the correct MAC label (currently SELinux only) */
+        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 */
 } CopyFlags;
 
 typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);