]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
btrfs-util: when snapshotting make sure we don't descent into subvolumes we just... 208/head
authorLennart Poettering <lennart@poettering.net>
Mon, 15 Jun 2015 15:53:50 +0000 (17:53 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 15 Jun 2015 16:11:11 +0000 (18:11 +0200)
We already had a safety check in place that we don't end up descending
to the original subvolume again, but we also should avoid descending in
the newly created one.

This is particularly important if we make a snapshot below its source,
like we do in "systemd-nspawn --ephemeral -D /".

Closes https://bugs.freedesktop.org/show_bug.cgi?id=90803

src/basic/btrfs-util.c
src/basic/btrfs-util.h

index 49528dbf0152e119d6ee1d9f3ed7acaa0f067b41..074deeccdaa2154138b808dba138fbec574d51dd 100644 (file)
@@ -352,6 +352,19 @@ int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
         return 0;
 }
 
+int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
+        _cleanup_close_ int subvol_fd = -1;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+        if (subvol_fd < 0)
+                return -errno;
+
+        return btrfs_subvol_get_id_fd(subvol_fd, ret);
+}
+
 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
         assert(args);
 
@@ -937,7 +950,7 @@ int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
         return subvol_remove_children(fd, subvolume, 0, recursive);
 }
 
-static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
+static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
 
         struct btrfs_ioctl_search_args args = {
                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
@@ -956,8 +969,9 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
                 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
                 .fd = old_fd,
         };
-        int r;
         _cleanup_close_ int subvolume_fd = -1;
+        uint64_t new_subvol_id;
+        int r;
 
         assert(old_fd >= 0);
         assert(new_fd >= 0);
@@ -972,13 +986,17 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
         if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
                 return 0;
 
-        if (subvol_id == 0) {
-                r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
+        if (old_subvol_id == 0) {
+                r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
                 if (r < 0)
                         return r;
         }
 
-        args.key.min_offset = args.key.max_offset = subvol_id;
+        r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
+        if (r < 0)
+                return r;
+
+        args.key.min_offset = args.key.max_offset = old_subvol_id;
 
         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
                 const struct btrfs_ioctl_search_header *sh;
@@ -1001,17 +1019,24 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
 
                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
                                 continue;
-                        if (sh->offset != subvol_id)
+
+                        /* Avoid finding the source subvolume a second
+                         * time */
+                        if (sh->offset != old_subvol_id)
                                 continue;
 
-                        ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+                        /* Avoid running into loops if the new
+                         * subvolume is below the old one. */
+                        if (sh->objectid == new_subvol_id)
+                                continue;
 
+                        ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
                         if (!p)
                                 return -ENOMEM;
 
                         zero(ino_args);
-                        ino_args.treeid = subvol_id;
+                        ino_args.treeid = old_subvol_id;
                         ino_args.objectid = htole64(ref->dirid);
 
                         if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
@@ -1056,7 +1081,7 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
                         }
 
                         /* When btrfs clones the subvolumes, child
-                         * subvolumes appear as directories. Remove
+                         * subvolumes appear as empty directories. Remove
                          * them, so that we can create a new snapshot
                          * in their place */
                         if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
index a7eb895c93f348ac7dcd8b6dd24da872de9faf1b..8632c3638c227f0dad1eead3ffadcfdc673aba47 100644 (file)
@@ -61,6 +61,7 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnaps
 int btrfs_subvol_set_read_only_fd(int fd, bool b);
 int btrfs_subvol_set_read_only(const char *path, bool b);
 int btrfs_subvol_get_read_only_fd(int fd);
+int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret);
 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);
 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);