copy: optionally fsync() files after copying them

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.
This commit is contained in:
Lennart Poettering
2021-08-02 17:24:03 +02:00
parent 2f78204498
commit 06a40b52d9
2 changed files with 118 additions and 16 deletions
+107 -7
View 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:
+11 -9
View File
@@ -10,15 +10,17 @@
#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);