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",
_cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
_cleanup_closedir_ DIR *d = NULL;
+ struct stat dt_st;
bool exists;
int r;
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) {
(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)
#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 {