btrfs-util: Add BTRFS_SNAPSHOT_LOCK_BSD

When making ephemeral snapshots of subvolumes whose cleanup depends on
whether they're locked or not, it's necessary to have the lock from the
very beginning, so let's support that with a new BTRFS_SNAPSHOT_LOCK_BSD
flag.
This commit is contained in:
Daan De Meyer
2023-03-28 12:32:51 +02:00
parent 01428141ba
commit d54f60c2af
3 changed files with 61 additions and 8 deletions

View File

@@ -1363,9 +1363,26 @@ static int subvol_snapshot_children(
if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
return -errno;
if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
subvolume_fd = xopenat_lock(new_fd, subvolume,
O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
/* xopen_flags = */ 0,
/* mode = */ 0,
LOCK_BSD,
LOCK_EX);
if (subvolume_fd < 0)
return subvolume_fd;
r = btrfs_is_subvol_fd(subvolume_fd);
if (r < 0)
return r;
if (r == 0)
return -EEXIST;
}
if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) &&
!(flags & BTRFS_SNAPSHOT_QUOTA))
return 0;
return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
if (old_subvol_id == 0) {
r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
@@ -1385,7 +1402,7 @@ static int subvol_snapshot_children(
if (flags & BTRFS_SNAPSHOT_QUOTA)
(void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
return 0;
return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
}
args.key.min_offset = args.key.max_offset = old_subvol_id;
@@ -1480,7 +1497,8 @@ static int subvol_snapshot_children(
return k;
}
r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid,
flags & ~(BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_LOCK_BSD));
/* Restore the readonly flag */
if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
@@ -1503,7 +1521,7 @@ static int subvol_snapshot_children(
if (flags & BTRFS_SNAPSHOT_QUOTA)
(void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
return 0;
return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
}
int btrfs_subvol_snapshot_at_full(
@@ -1517,7 +1535,7 @@ int btrfs_subvol_snapshot_at_full(
void *userdata) {
_cleanup_free_ char *subvolume = NULL;
_cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF;
_cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF, subvolume_fd = -EBADF;
int r;
assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
@@ -1556,6 +1574,25 @@ int btrfs_subvol_snapshot_at_full(
} else if (r < 0)
return r;
if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
subvolume_fd = xopenat_lock(new_fd, subvolume,
O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
/* xopen_flags = */ 0,
/* mode = */ 0,
LOCK_BSD,
LOCK_EX);
if (subvolume_fd < 0)
return subvolume_fd;
if (!plain_directory) {
r = btrfs_is_subvol_fd(subvolume_fd);
if (r < 0)
return r;
if (r == 0)
return -EEXIST;
}
}
r = copy_directory_at_full(
dir_fdf, from,
new_fd, subvolume,
@@ -1587,7 +1624,7 @@ int btrfs_subvol_snapshot_at_full(
}
}
return 0;
return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
fallback_fail:
(void) rm_rf_at(new_fd, subvolume, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);

View File

@@ -36,6 +36,7 @@ typedef enum BtrfsSnapshotFlags {
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 */
BTRFS_SNAPSHOT_LOCK_BSD = 1 << 8, /* Return a BSD exclusively locked file descriptor referring to snapshot subvolume/directory. */
} BtrfsSnapshotFlags;
typedef enum BtrfsRemoveFlags {

View File

@@ -4,6 +4,7 @@
#include "btrfs-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "fileio.h"
#include "format-util.h"
#include "log.h"
@@ -62,6 +63,14 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest4", BTRFS_SNAPSHOT_LOCK_BSD);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
if (r >= 0)
assert_se(xopenat_lock(AT_FDCWD, "/xxxtest4", 0, 0, 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
safe_close(r);
r = btrfs_subvol_remove("/xxxtest", BTRFS_REMOVE_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
@@ -74,6 +83,10 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
r = btrfs_subvol_remove("/xxxtest4", BTRFS_REMOVE_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
r = btrfs_subvol_snapshot_at(AT_FDCWD, "/etc", AT_FDCWD, "/etc2",
BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY);
if (r < 0)
@@ -161,13 +174,15 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to query quota: %m");
assert_se(quota.referenced_max == 4ULL * 1024 * 1024 * 1024);
if (r >= 0)
assert_se(quota.referenced_max == 4ULL * 1024 * 1024 * 1024);
r = btrfs_subvol_get_subtree_quota("/xxxquotatest2", 0, &quota);
if (r < 0)
log_error_errno(r, "Failed to query quota: %m");
assert_se(quota.referenced_max == 5ULL * 1024 * 1024 * 1024);
if (r >= 0)
assert_se(quota.referenced_max == 5ULL * 1024 * 1024 * 1024);
r = btrfs_subvol_remove("/xxxquotatest", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE);
if (r < 0)