]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
btrfs-util: Add BTRFS_SNAPSHOT_LOCK_BSD 27863/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 28 Mar 2023 10:32:51 +0000 (12:32 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 20 Jun 2023 11:42:41 +0000 (13:42 +0200)
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.

src/shared/btrfs-util.c
src/shared/btrfs-util.h
src/test/test-btrfs.c

index f86645e4eed91859a1afe2857c84984d4cdbef88..5128b308abf85b1630adc3066a5858ab65600a48 100644 (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);
index de38f1e45c39a8578f5e6e99c2399712ad10c083..c972cc07446e83774dfcf1f42ed888cab66938a3 100644 (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 {
index ccb1661a9142eeeab43da6bbe664ae8917eed0a0..ee4772b983c6c03065d1743d82e368ee84aae52e 100644 (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)