]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/btrfs-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / basic / btrfs-util.c
index 03c7609c928edc66971ab04297ed10d8d24b482d..b7e237fc0dfbda75d93e57f88d632f0a30aded38 100644 (file)
@@ -1,5 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -22,6 +21,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/fs.h>
 #include <linux/loop.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <sys/sysmacros.h>
 #include <unistd.h>
 
-#ifdef HAVE_LINUX_BTRFS_H
+#if HAVE_LINUX_BTRFS_H
 #include <linux/btrfs.h>
 #endif
 
 #include "alloc-util.h"
 #include "btrfs-ctree.h"
 #include "btrfs-util.h"
+#include "chattr-util.h"
 #include "copy.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -47,6 +48,7 @@
 #include "macro.h"
 #include "missing.h"
 #include "path-util.h"
+#include "rm-rf.h"
 #include "selinux-util.h"
 #include "smack-util.h"
 #include "sparse-endian.h"
@@ -1211,7 +1213,7 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
         if (!S_ISDIR(st.st_mode))
                 return -EINVAL;
 
-        subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+        subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
         if (subvol_fd < 0)
                 return -errno;
 
@@ -1291,7 +1293,7 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
                                  * hence we need to open the
                                  * containing directory first */
 
-                                child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+                                child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
                                 if (child_fd < 0)
                                         return -errno;
 
@@ -1640,15 +1642,15 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
                         if (!c)
                                 return -ENOMEM;
 
-                        old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+                        old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
                         if (old_child_fd < 0)
                                 return -errno;
 
-                        np = strjoin(subvolume, "/", ino_args.name, NULL);
+                        np = strjoin(subvolume, "/", ino_args.name);
                         if (!np)
                                 return -ENOMEM;
 
-                        new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+                        new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
                         if (new_child_fd < 0)
                                 return -errno;
 
@@ -1659,7 +1661,7 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
                                  * into place. */
 
                                 if (subvolume_fd < 0) {
-                                        subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+                                        subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
                                         if (subvolume_fd < 0)
                                                 return -errno;
                                 }
@@ -1720,28 +1722,46 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
         if (r < 0)
                 return r;
         if (r == 0) {
+                bool plain_directory = false;
+
+                /* If the source isn't a proper subvolume, fail unless fallback is requested */
                 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
                         return -EISDIR;
 
                 r = btrfs_subvol_make(new_path);
-                if (r < 0)
-                        return r;
+                if (r == -ENOTTY && (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 r;
 
-                r = copy_directory_fd(old_fd, new_path, true);
-                if (r < 0) {
-                        (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
+                        plain_directory = true;
+                } else if (r < 0)
                         return r;
-                }
+
+                r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
+                if (r < 0)
+                        goto fallback_fail;
 
                 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
-                        r = btrfs_subvol_set_read_only(new_path, true);
-                        if (r < 0) {
-                                (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
-                                return r;
+
+                        if (plain_directory) {
+                                /* Plain directories have no recursive read-only flag, but something pretty close to
+                                 * it: the IMMUTABLE bit. Let's use this here, if this is requested. */
+
+                                if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
+                                        (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
+                        } else {
+                                r = btrfs_subvol_set_read_only(new_path, true);
+                                if (r < 0)
+                                        goto fallback_fail;
                         }
                 }
 
                 return 0;
+
+        fallback_fail:
+                (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+                return r;
         }
 
         r = extract_subvolume_name(new_path, &subvolume);