Skip to content

Commit 87cc4d9

Browse files
DaanDeMeyerbluca
authored andcommitted
repart: Keep existing directory timestamps intact when copying
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 d850a54) (cherry picked from commit d5640c4) (cherry picked from commit 7a3b3ad)
1 parent 7a6da61 commit 87cc4d9

File tree

3 files changed

+29
-22
lines changed

3 files changed

+29
-22
lines changed

src/partition/repart.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4202,14 +4202,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
42024202
sfd, ".",
42034203
pfd, fn,
42044204
UID_INVALID, GID_INVALID,
4205-
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
4205+
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
42064206
denylist);
42074207
} else
42084208
r = copy_tree_at(
42094209
sfd, ".",
42104210
tfd, ".",
42114211
UID_INVALID, GID_INVALID,
4212-
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
4212+
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
42134213
denylist);
42144214
if (r < 0)
42154215
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",

src/shared/copy.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,7 @@ static int fd_copy_directory(
985985

986986
_cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
987987
_cleanup_closedir_ DIR *d = NULL;
988+
struct stat dt_st;
988989
bool exists;
989990
int r;
990991

@@ -1029,6 +1030,9 @@ static int fd_copy_directory(
10291030
if (fdt < 0)
10301031
return fdt;
10311032

1033+
if (exists && FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS) && fstat(fdt, &dt_st) < 0)
1034+
return -errno;
1035+
10321036
r = 0;
10331037

10341038
if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) {
@@ -1128,7 +1132,9 @@ static int fd_copy_directory(
11281132

11291133
(void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags);
11301134
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
1131-
}
1135+
} else if (FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS))
1136+
/* If the directory already exists, make sure the timestamps stay the same as before. */
1137+
(void) futimens(fdt, (struct timespec[]) { dt_st.st_atim, dt_st.st_mtim });
11321138

11331139
if (copy_flags & COPY_FSYNC_FULL) {
11341140
if (fsync(fdt) < 0)

src/shared/copy.h

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,26 @@
1212
#include "set.h"
1313

1414
typedef enum CopyFlags {
15-
COPY_REFLINK = 1 << 0, /* Try to reflink */
16-
COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
17-
COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
18-
COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
19-
COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
20-
COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
21-
COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
22-
COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
23-
COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
24-
COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
25-
COPY_FSYNC = 1 << 10, /* fsync() after we are done */
26-
COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
27-
COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
28-
COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
29-
COPY_HOLES = 1 << 14, /* Copy holes */
30-
COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
31-
COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
32-
COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
33-
COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
15+
COPY_REFLINK = 1 << 0, /* Try to reflink */
16+
COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
17+
COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
18+
COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
19+
COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
20+
COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
21+
COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
22+
COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
23+
COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
24+
COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
25+
COPY_FSYNC = 1 << 10, /* fsync() after we are done */
26+
COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
27+
COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
28+
COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
29+
COPY_HOLES = 1 << 14, /* Copy holes */
30+
COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
31+
COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
32+
COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
33+
COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
34+
COPY_RESTORE_DIRECTORY_TIMESTAMPS = 1 << 19, /* Make sure existing directory timestamps don't change during copying. */
3435
} CopyFlags;
3536

3637
typedef enum DenyType {

0 commit comments

Comments
 (0)