From: Lennart Poettering Date: Fri, 26 Feb 2021 09:27:00 +0000 (+0100) Subject: copy: add COPY_SYNCFS flag X-Git-Tag: v250-rc1~887^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=864e406256cd0eb2aed48cfe750efdec9c3d1e3a;p=thirdparty%2Fsystemd.git copy: add COPY_SYNCFS flag When copying large directory trees it should be a better idea to sync the whole fs once when we are done instead of individually for each file, hence add COPY_SYNCFS. As opposed to COPY_FSYNC/COPY_FSYNC_FULL this only really applies to the top-level directory, after completion of the whole copy. --- diff --git a/src/shared/copy.c b/src/shared/copy.c index 3ac904c6c97..e47b56ce915 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -984,7 +984,17 @@ int copy_tree_at_full( if (r < 0) return r; - if (copy_flags & COPY_FSYNC_FULL) { + if (S_ISDIR(st.st_mode) && (copy_flags & COPY_SYNCFS)) { + /* If the top-level inode is a directory run syncfs() now. */ + r = syncfs_path(fdt, to); + if (r < 0) + return r; + } else if ((copy_flags & (COPY_FSYNC_FULL|COPY_SYNCFS)) != 0) { + /* fsync() the parent dir of what we just copied if COPY_FSYNC_FULL is set. Also do this in + * case COPY_SYNCFS is set but the top-level inode wasn't actually a directory. We do this so that + * COPY_SYNCFS provides reasonable synchronization semantics on any kind of inode: when the + * copy operation is done the whole inode — regardless of its type — and all its children + * will be synchronized to disk. */ r = fsync_parent_at(fdt, to); if (r < 0) return r; @@ -993,6 +1003,16 @@ int copy_tree_at_full( return 0; } +static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) { + + if (copy_flags & COPY_SYNCFS) + return syncfs_path(AT_FDCWD, path); + if (copy_flags & COPY_FSYNC_FULL) + return fsync_parent_at(AT_FDCWD, path); + + return 0; +} + int copy_directory_fd_full( int dirfd, const char *to, @@ -1029,11 +1049,9 @@ int copy_directory_fd_full( if (r < 0) return r; - if (copy_flags & COPY_FSYNC_FULL) { - r = fsync_parent_at(AT_FDCWD, to); - if (r < 0) - return r; - } + r = sync_dir_by_flags(to, copy_flags); + if (r < 0) + return r; return 0; } @@ -1074,11 +1092,9 @@ int copy_directory_full( if (r < 0) return r; - if (copy_flags & COPY_FSYNC_FULL) { - r = fsync_parent_at(AT_FDCWD, to); - if (r < 0) - return r; - } + r = sync_dir_by_flags(to, copy_flags); + if (r < 0) + return r; return 0; } diff --git a/src/shared/copy.h b/src/shared/copy.h index 81990d37431..0c81760d8fd 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -21,6 +21,7 @@ typedef enum CopyFlags { 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 */ + COPY_SYNCFS = 1 << 11, /* syncfs() the *top-level* dir after we are done */ } CopyFlags; typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);