From 5c9d961e79adaa0f4dc2e3b1e5deac0f29633723 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 1 Feb 2021 17:37:11 +0100 Subject: [PATCH 1/6] copy: move to single clean-up path (This might not look like a big improvement, but will shortly, when we add fsync() support to the copy logic, at which point there are more error paths we can unify that way.) While we are at it, tweak a clean-up path: only unlink a copied file if we are definitely the ones who created them, i.e. if O_EXCL is set. --- src/shared/copy.c | 54 +++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/shared/copy.c b/src/shared/copy.c index 65d75a15d5..27aa3b2938 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -627,10 +627,8 @@ static int fd_copy_regular( return -errno; r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata); - if (r < 0) { - (void) unlinkat(dt, to, 0); - return r; - } + if (r < 0) + goto fail; if (fchown(fdt, uid_is_valid(override_uid) ? override_uid : st->st_uid, @@ -643,16 +641,18 @@ static int fd_copy_regular( (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); (void) copy_xattr(fdf, fdt); - q = close(fdt); - fdt = -1; - + q = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */ if (q < 0) { - r = -errno; - (void) unlinkat(dt, to, 0); + r = q; + goto fail; } (void) memorize_hardlink(hardlink_context, st, dt, to); return r; + +fail: + (void) unlinkat(dt, to, 0); + return r; } static int fd_copy_fifo( @@ -1054,9 +1054,9 @@ int copy_file_full( copy_progress_bytes_t progress_bytes, void *userdata) { - _cleanup_close_ int fdf = -1; + _cleanup_close_ int fdf = -1, fdt = -1; struct stat st; - int r, fdt = -1; /* avoid false maybe-uninitialized warning */ + int r; assert(from); assert(to); @@ -1087,11 +1087,8 @@ int copy_file_full( (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL); r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata); - if (r < 0) { - close(fdt); - (void) unlink(to); - return r; - } + if (r < 0) + goto fail; (void) copy_times(fdf, fdt, copy_flags); (void) copy_xattr(fdf, fdt); @@ -1099,12 +1096,18 @@ int copy_file_full( if (chattr_mask != 0) (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); - if (close(fdt) < 0) { - unlink_noerrno(to); - return -errno; - } + r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */ + if (r < 0) + goto fail; return 0; + +fail: + /* Only unlink if we definitely are the ones who created the file */ + if (FLAGS_SET(flags, O_EXCL)) + (void) unlink(to); + + return r; } int copy_file_atomic_full( @@ -1181,11 +1184,20 @@ int copy_file_atomic_full( return r; } + t = mfree(t); + if (chattr_mask != 0) (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); - t = mfree(t); + r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */ + if (r < 0) + goto fail; + return 0; + +fail: + (void) unlink(to); + return r; } int copy_times(int fdf, int fdt, CopyFlags flags) { From 2f782044986a30bf73f1fe00209dbd204b3efe33 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 1 Feb 2021 17:45:25 +0100 Subject: [PATCH 2/6] copy: tighten destination checks when copying files let's make sure we only operate on regular files when copying files. Also, make sure to copy file attributes only over if target is a regular file (so that copying a file to /dev/null won't alter the access mode/ownership of that device node...) --- src/shared/copy.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/shared/copy.c b/src/shared/copy.c index 27aa3b2938..bf02a89b51 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -1026,6 +1026,7 @@ int copy_file_fd_full( void *userdata) { _cleanup_close_ int fdf = -1; + struct stat st; int r; assert(from); @@ -1035,12 +1036,23 @@ int copy_file_fd_full( if (fdf < 0) return -errno; + r = fd_verify_regular(fdf); + if (r < 0) + return r; + + if (fstat(fdt, &st) < 0) + return -errno; + r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata); + if (r < 0) + return r; - (void) copy_times(fdf, fdt, copy_flags); - (void) copy_xattr(fdf, fdt); + if (S_ISREG(fdt)) { + (void) copy_times(fdf, fdt, copy_flags); + (void) copy_xattr(fdf, fdt); + } - return r; + return 0; } int copy_file_full( @@ -1065,9 +1077,12 @@ int copy_file_full( if (fdf < 0) return -errno; - if (mode == MODE_INVALID) - if (fstat(fdf, &st) < 0) - return -errno; + if (fstat(fdf, &st) < 0) + return -errno; + + r = stat_verify_regular(&st); + if (r < 0) + return r; RUN_WITH_UMASK(0000) { if (copy_flags & COPY_MAC_CREATE) { @@ -1083,6 +1098,12 @@ int copy_file_full( return -errno; } + if (!FLAGS_SET(flags, O_EXCL)) { /* if O_EXCL was used we created the thing as regular file, no need to check again */ + r = fd_verify_regular(fdt); + if (r < 0) + goto fail; + } + if (chattr_mask != 0) (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL); From 06a40b52d966143aa2d8e61e7fb6336f3f20e515 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 1 Feb 2021 17:48:32 +0100 Subject: [PATCH 3/6] 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. --- src/shared/copy.c | 114 +++++++++++++++++++++++++++++++++++++++++++--- src/shared/copy.h | 20 ++++---- 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/src/shared/copy.c b/src/shared/copy.c index bf02a89b51..3ac904c6c9 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 b36ddfcb01..81990d3743 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); From 864e406256cd0eb2aed48cfe750efdec9c3d1e3a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 26 Feb 2021 10:27:00 +0100 Subject: [PATCH 4/6] 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. --- src/shared/copy.c | 38 +++++++++++++++++++++++++++----------- src/shared/copy.h | 1 + 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/shared/copy.c b/src/shared/copy.c index 3ac904c6c9..e47b56ce91 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 81990d3743..0c81760d8f 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); From 1ac404cac095f1aaf581b107895e8b06fa8db6d0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 26 Feb 2021 15:10:24 +0100 Subject: [PATCH 5/6] copy: add COPY_SIGTERM, matching the existing COPY_SIGINT --- src/shared/copy.c | 39 ++++++++++++++++++++++++--------------- src/shared/copy.h | 11 ++++++----- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/shared/copy.c b/src/shared/copy.c index e47b56ce91..a01b6df1b2 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -88,6 +88,23 @@ static int fd_is_nonblock_pipe(int fd) { return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE; } +static int look_for_signals(CopyFlags copy_flags) { + int r; + + if ((copy_flags & (COPY_SIGINT|COPY_SIGTERM)) == 0) + return 0; + + r = pop_pending_signal(copy_flags & COPY_SIGINT ? SIGINT : 0, + copy_flags & COPY_SIGTERM ? SIGTERM : 0); + if (r < 0) + return r; + if (r != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EINTR), + "Got %s, cancelling copy operation.", signal_to_string(r)); + + return 0; +} + int copy_bytes_full( int fdf, int fdt, uint64_t max_bytes, @@ -176,13 +193,9 @@ int copy_bytes_full( if (max_bytes <= 0) return 1; /* return > 0 if we hit the max_bytes limit */ - if (FLAGS_SET(copy_flags, COPY_SIGINT)) { - r = pop_pending_signal(SIGINT); - if (r < 0) - return r; - if (r > 0) - return -EINTR; - } + r = look_for_signals(copy_flags); + if (r < 0) + return r; if (max_bytes != UINT64_MAX && m > max_bytes) m = max_bytes; @@ -852,13 +865,9 @@ static int fd_copy_directory( if (dot_or_dot_dot(de->d_name)) continue; - if (FLAGS_SET(copy_flags, COPY_SIGINT)) { - r = pop_pending_signal(SIGINT); - if (r < 0) - return r; - if (r > 0) - return -EINTR; - } + r = look_for_signals(copy_flags); + if (r < 0) + return r; if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { r = -errno; @@ -919,7 +928,7 @@ static int fd_copy_directory( else q = -EOPNOTSUPP; - if (q == -EINTR) /* Propagate SIGINT up instantly */ + if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */ return q; if (q == -EEXIST && (copy_flags & COPY_MERGE)) q = 0; diff --git a/src/shared/copy.h b/src/shared/copy.h index 0c81760d8f..f8992843e4 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -17,11 +17,12 @@ typedef enum CopyFlags { 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 */ - COPY_SYNCFS = 1 << 11, /* syncfs() the *top-level* dir after we are done */ + 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 */ } CopyFlags; typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata); From 28ba7e36d0b6b3f5b9dc4922d689fd8c27303b61 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 26 Feb 2021 15:00:29 +0100 Subject: [PATCH 6/6] btrfs-util: expose COPY_SIGTERM for btrfs_snapshot() too --- src/shared/btrfs-util.c | 13 ++++++++++--- src/shared/btrfs-util.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index f4e291385b..00a6a37ff7 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -1613,7 +1613,7 @@ int btrfs_subvol_snapshot_fd_full( return -EISDIR; r = btrfs_subvol_make(new_path); - if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) { + if (ERRNO_IS_NOT_SUPPORTED(r) && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) { /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */ if (mkdir(new_path, 0755) < 0) return -errno; @@ -1624,8 +1624,15 @@ int btrfs_subvol_snapshot_fd_full( r = copy_directory_fd_full( old_fd, new_path, - COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT|COPY_HARDLINKS|(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0), - progress_path, progress_bytes, userdata); + COPY_MERGE_EMPTY| + COPY_REFLINK| + COPY_SAME_MOUNT| + COPY_HARDLINKS| + (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0)| + (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGTERM) ? COPY_SIGTERM : 0), + progress_path, + progress_bytes, + userdata); if (r < 0) goto fallback_fail; diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index 7b18f57719..b67a4c10fe 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -35,6 +35,7 @@ typedef enum BtrfsSnapshotFlags { BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 1 << 4, /* If the destination doesn't support subvolumes, reflink/copy instead */ BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 1 << 5, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */ BTRFS_SNAPSHOT_SIGINT = 1 << 6, /* Check for SIGINT regularly, and return EINTR if seen */ + BTRFS_SNAPSHOT_SIGTERM = 1 << 7, /* Ditto, but for SIGTERM */ } BtrfsSnapshotFlags; typedef enum BtrfsRemoveFlags {