From: Lennart Poettering Date: Mon, 1 Feb 2021 16:48:32 +0000 (+0100) Subject: copy: optionally fsync() files after copying them X-Git-Tag: v250-rc1~887^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=06a40b52d966143aa2d8e61e7fb6336f3f20e515;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/shared/copy.c b/src/shared/copy.c index bf02a89b514..3ac904c6c97 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -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: diff --git a/src/shared/copy.h b/src/shared/copy.h index b36ddfcb015..81990d37431 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -10,15 +10,17 @@ #include 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);