]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Keep existing directory timestamps intact when copying
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 29 Aug 2024 20:59:48 +0000 (22:59 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 30 Aug 2024 14:20:40 +0000 (15:20 +0100)
Otherwise, when merging multiple directory trees, the output becomes
unreproducible as the directory timestamps will be changed to the current
time when copying identical directories from the second tree.

We introduce a new copy flag to achieve this behavior.

src/partition/repart.c
src/shared/copy.c
src/shared/copy.h

index bca9f28372ea3ff29a32c3eaa3b5bd35f4bec294..e14c9a4750b018bac1d05c82d9337d2a2f4c49d3 100644 (file)
@@ -4961,14 +4961,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
                                                 sfd, ".",
                                                 pfd, fn,
                                                 UID_INVALID, GID_INVALID,
-                                                COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
+                                                COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
                                                 denylist, subvolumes_by_source_inode);
                         } else
                                 r = copy_tree_at(
                                                 sfd, ".",
                                                 tfd, ".",
                                                 UID_INVALID, GID_INVALID,
-                                                COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
+                                                COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
                                                 denylist, subvolumes_by_source_inode);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
index c5c5cf083d0233cc23e744f2053a91191f8f1c2b..8e240dc434f933295aabb52142bbf534c0a543e8 100644 (file)
@@ -1009,6 +1009,7 @@ static int fd_copy_directory(
 
         _cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
         _cleanup_closedir_ DIR *d = NULL;
+        struct stat dt_st;
         bool exists;
         int r;
 
@@ -1053,6 +1054,9 @@ static int fd_copy_directory(
         if (fdt < 0)
                 return fdt;
 
+        if (exists && FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS) && fstat(fdt, &dt_st) < 0)
+                return -errno;
+
         r = 0;
 
         if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) {
@@ -1152,7 +1156,9 @@ finish:
 
                 (void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags);
                 (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
-        }
+        } else if (FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS))
+                /* If the directory already exists, make sure the timestamps stay the same as before. */
+                (void) futimens(fdt, (struct timespec[]) { dt_st.st_atim, dt_st.st_mtim });
 
         if (copy_flags & COPY_FSYNC_FULL) {
                 if (fsync(fdt) < 0)
index b8fb28a09e48983b810aacc57429cb8e8d9f91fa..db95738b8056147abcbf05e3aafee45209d4f797 100644 (file)
 #include "set.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_SIGTERM       = 1 << 7,  /* ditto, but for SIGTERM */
-        COPY_MAC_CREATE    = 1 << 8,  /* Create files with the correct MAC label (currently SELinux only) */
-        COPY_HARDLINKS     = 1 << 9,  /* Try to reproduce hard links */
-        COPY_FSYNC         = 1 << 10, /* fsync() after we are done */
-        COPY_FSYNC_FULL    = 1 << 11, /* fsync_full() after we are done */
-        COPY_SYNCFS        = 1 << 12, /* syncfs() the *top-level* dir after we are done */
-        COPY_ALL_XATTRS    = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
-        COPY_HOLES         = 1 << 14, /* Copy holes */
-        COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
-        COPY_TRUNCATE      = 1 << 16, /* Truncate to current file offset after copying */
-        COPY_LOCK_BSD      = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
-        COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
+        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_SIGTERM                      = 1 << 7,  /* ditto, but for SIGTERM */
+        COPY_MAC_CREATE                   = 1 << 8,  /* Create files with the correct MAC label (currently SELinux only) */
+        COPY_HARDLINKS                    = 1 << 9,  /* Try to reproduce hard links */
+        COPY_FSYNC                        = 1 << 10, /* fsync() after we are done */
+        COPY_FSYNC_FULL                   = 1 << 11, /* fsync_full() after we are done */
+        COPY_SYNCFS                       = 1 << 12, /* syncfs() the *top-level* dir after we are done */
+        COPY_ALL_XATTRS                   = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
+        COPY_HOLES                        = 1 << 14, /* Copy holes */
+        COPY_GRACEFUL_WARN                = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
+        COPY_TRUNCATE                     = 1 << 16, /* Truncate to current file offset after copying */
+        COPY_LOCK_BSD                     = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
+        COPY_VERIFY_LINKED                = 1 << 18, /* Check the source file is still linked after copying. */
+        COPY_RESTORE_DIRECTORY_TIMESTAMPS = 1 << 19, /* Make sure existing directory timestamps don't change during copying. */
 } CopyFlags;
 
 typedef enum DenyType {