Skip to content

Commit

Permalink
repart: Keep existing directory timestamps intact when copying
Browse files Browse the repository at this point in the history
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.

(cherry picked from commit d850a544bc1f895decb452160c97a884a20b12b7)
(cherry picked from commit d5640c4f8583de2752a7f4e03006a1fa74942da1)
  • Loading branch information
DaanDeMeyer authored and bluca committed Sep 10, 2024
1 parent 4a86d30 commit 7a3b3ad
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/partition/repart.c
Original file line number Diff line number Diff line change
Expand Up @@ -4667,14 +4667,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",
Expand Down
8 changes: 7 additions & 1 deletion src/shared/copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,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;

Expand Down Expand Up @@ -1025,6 +1026,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) {
Expand Down Expand Up @@ -1124,7 +1128,9 @@ static int fd_copy_directory(

(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)
Expand Down
39 changes: 20 additions & 19 deletions src/shared/copy.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,26 @@
#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 {
Expand Down

0 comments on commit 7a3b3ad

Please sign in to comment.