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;
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,
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;
}
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;
}
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);