diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 1ee0110e2f..540a1990ce 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -1624,7 +1624,7 @@ int btrfs_subvol_snapshot_fd_full( } else if (r < 0) return r; - r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT, progress_path, progress_bytes, userdata); + r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT|(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0), progress_path, progress_bytes, userdata); if (r < 0) goto fallback_fail; diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index 7d848a75d3..b15667bf2f 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -34,6 +34,7 @@ typedef enum BtrfsSnapshotFlags { BTRFS_SNAPSHOT_QUOTA = 1 << 3, 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 */ } BtrfsSnapshotFlags; typedef enum BtrfsRemoveFlags { diff --git a/src/basic/copy.c b/src/basic/copy.c index 36ea7821f2..ca311e021e 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -86,6 +86,22 @@ static int fd_is_nonblock_pipe(int fd) { return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE; } +static int sigint_pending(void) { + sigset_t ss; + + assert_se(sigemptyset(&ss) >= 0); + assert_se(sigaddset(&ss, SIGINT) >= 0); + + if (sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 }) < 0) { + if (errno == EAGAIN) + return false; + + return -errno; + } + + return true; +} + int copy_bytes_full( int fdf, int fdt, uint64_t max_bytes, @@ -174,6 +190,14 @@ 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 = sigint_pending(); + if (r < 0) + return r; + if (r > 0) + return -EINTR; + } + if (max_bytes != UINT64_MAX && m > max_bytes) m = max_bytes; @@ -559,6 +583,14 @@ static int fd_copy_directory( if (dot_or_dot_dot(de->d_name)) continue; + if (FLAGS_SET(copy_flags, COPY_SIGINT)) { + r = sigint_pending(); + if (r < 0) + return r; + if (r > 0) + return -EINTR; + } + if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { r = -errno; continue; @@ -618,9 +650,10 @@ static int fd_copy_directory( else q = -EOPNOTSUPP; + if (q == -EINTR) /* Propagate SIGINT up instantly */ + return q; if (q == -EEXIST && (copy_flags & COPY_MERGE)) q = 0; - if (q < 0) r = q; } diff --git a/src/basic/copy.h b/src/basic/copy.h index 51ea4d51eb..906c6f70ae 100644 --- a/src/basic/copy.h +++ b/src/basic/copy.h @@ -15,6 +15,7 @@ typedef enum CopyFlags { 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) */ } CopyFlags; typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 117a637699..67c2439339 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -4663,19 +4663,19 @@ static int initialize_rlimits(void) { } static int run(int argc, char *argv[]) { + bool secondary = false, remove_directory = false, remove_image = false, + veth_created = false, remove_tmprootdir = false; _cleanup_close_ int master = -1; _cleanup_fdset_free_ FDSet *fds = NULL; int r, n_fd_passed, ret = EXIT_SUCCESS; char veth_name[IFNAMSIZ] = ""; - bool secondary = false, remove_directory = false, remove_image = false; - pid_t pid = 0; union in_addr_union exposed = {}; _cleanup_(release_lock_file) LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; - bool veth_created = false, remove_tmprootdir = false; char tmprootdir[] = "/tmp/nspawn-root-XXXXXX"; _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL; _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; + pid_t pid = 0; log_parse_environment(); log_open(); @@ -4753,12 +4753,9 @@ static int run(int argc, char *argv[]) { if (r < 0) goto finish; - /* If the specified path is a mount point we - * generate the new snapshot immediately - * inside it under a random name. However if - * the specified is not a mount point we - * create the new snapshot in the parent - * directory, just next to it. */ + /* If the specified path is a mount point we generate the new snapshot immediately + * inside it under a random name. However if the specified is not a mount point we + * create the new snapshot in the parent directory, just next to it. */ r = path_is_mount_point(arg_directory, NULL, 0); if (r < 0) { log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory); @@ -4779,21 +4776,27 @@ static int run(int argc, char *argv[]) { goto finish; } - r = btrfs_subvol_snapshot(arg_directory, np, - (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | - BTRFS_SNAPSHOT_FALLBACK_COPY | - BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | - BTRFS_SNAPSHOT_RECURSIVE | - BTRFS_SNAPSHOT_QUOTA); + { + BLOCK_SIGNALS(SIGINT); + r = btrfs_subvol_snapshot(arg_directory, np, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA | + BTRFS_SNAPSHOT_SIGINT); + } + if (r == -EINTR) { + log_error_errno(r, "Interrupted while copying file system tree to %s, removed again.", np); + goto finish; + } if (r < 0) { log_error_errno(r, "Failed to create snapshot %s from %s: %m", np, arg_directory); goto finish; } free_and_replace(arg_directory, np); - remove_directory = true; - } else { r = chase_symlinks_and_update(&arg_directory, arg_template ? CHASE_NONEXISTENT : 0); if (r < 0) @@ -4814,17 +4817,24 @@ static int run(int argc, char *argv[]) { if (r < 0) goto finish; - r = btrfs_subvol_snapshot(arg_template, arg_directory, - (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | - BTRFS_SNAPSHOT_FALLBACK_COPY | - BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | - BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | - BTRFS_SNAPSHOT_RECURSIVE | - BTRFS_SNAPSHOT_QUOTA); + { + BLOCK_SIGNALS(SIGINT); + r = btrfs_subvol_snapshot(arg_template, arg_directory, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA | + BTRFS_SNAPSHOT_SIGINT); + } if (r == -EEXIST) log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Directory %s already exists, not populating from template %s.", arg_directory, arg_template); - else if (r < 0) { + else if (r == -EINTR) { + log_error_errno(r, "Interrupted while copying file system tree to %s, removed again.", arg_directory); + goto finish; + } else if (r < 0) { log_error_errno(r, "Couldn't create snapshot %s from %s: %m", arg_directory, arg_template); goto finish; } else @@ -4886,14 +4896,20 @@ static int run(int argc, char *argv[]) { goto finish; } - r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL, FS_NOCOW_FL, COPY_REFLINK|COPY_CRTIME); + { + BLOCK_SIGNALS(SIGINT); + r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL, FS_NOCOW_FL, COPY_REFLINK|COPY_CRTIME|COPY_SIGINT); + } + if (r == -EINTR) { + log_error_errno(r, "Interrupted while copying image file to %s, removed again.", np); + goto finish; + } if (r < 0) { r = log_error_errno(r, "Failed to copy image file: %m"); goto finish; } free_and_replace(arg_image, np); - remove_image = true; } else { r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);