]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/btrfs-util.c
Merge pull request #10984 from fbuihuu/tmpfiles-be-more-explicit-with-unsafe-transition
[thirdparty/systemd.git] / src / basic / btrfs-util.c
index 0308048b7f8da9c49026d6d6e66d8f0a38d541af..d08e7546d0173f63ddbfd127a6df8216f391701d 100644 (file)
 #include <sys/sysmacros.h>
 #include <unistd.h>
 
-#if HAVE_LINUX_BTRFS_H
-#include <linux/btrfs.h>
-#endif
-
 #include "alloc-util.h"
 #include "blockdev-util.h"
-#include "btrfs-ctree.h"
 #include "btrfs-util.h"
 #include "chattr-util.h"
 #include "copy.h"
@@ -404,26 +399,21 @@ static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, co
 }
 
 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
+        int r;
+
         assert(args);
 
         /* Compare min and max */
 
-        if (args->key.min_objectid < args->key.max_objectid)
-                return -1;
-        if (args->key.min_objectid > args->key.max_objectid)
-                return 1;
-
-        if (args->key.min_type < args->key.max_type)
-                return -1;
-        if (args->key.min_type > args->key.max_type)
-                return 1;
+        r = CMP(args->key.min_objectid, args->key.max_objectid);
+        if (r != 0)
+                return r;
 
-        if (args->key.min_offset < args->key.max_offset)
-                return -1;
-        if (args->key.min_offset > args->key.max_offset)
-                return 1;
+        r = CMP(args->key.min_type, args->key.max_type);
+        if (r != 0)
+                return r;
 
-        return 0;
+        return CMP(args->key.min_offset, args->key.max_offset);
 }
 
 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
@@ -875,96 +865,6 @@ int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, u
         return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
 }
 
-int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
-        struct btrfs_ioctl_vol_args args = {};
-        char p[SYS_BLOCK_PATH_MAX("/loop/backing_file")], q[DEV_NUM_PATH_MAX];
-        _cleanup_free_ char *backing = NULL;
-        _cleanup_close_ int loop_fd = -1, backing_fd = -1;
-        struct stat st;
-        dev_t dev = 0;
-        int r;
-
-        /* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */
-        if (!FILE_SIZE_VALID(new_size))
-                return -EINVAL;
-
-        /* btrfs cannot handle file systems < 16M, hence use this as minimum */
-        if (new_size < 16*1024*1024)
-                new_size = 16*1024*1024;
-
-        r = btrfs_get_block_device_fd(fd, &dev);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -ENODEV;
-
-        xsprintf_sys_block_path(p, "/loop/backing_file", dev);
-        r = read_one_line_file(p, &backing);
-        if (r == -ENOENT)
-                return -ENODEV;
-        if (r < 0)
-                return r;
-        if (isempty(backing) || !path_is_absolute(backing))
-                return -ENODEV;
-
-        backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
-        if (backing_fd < 0)
-                return -errno;
-
-        if (fstat(backing_fd, &st) < 0)
-                return -errno;
-        if (!S_ISREG(st.st_mode))
-                return -ENODEV;
-
-        if (new_size == (uint64_t) st.st_size)
-                return 0;
-
-        if (grow_only && new_size < (uint64_t) st.st_size)
-                return -EINVAL;
-
-        xsprintf_dev_num_path(q, "block", dev);
-        loop_fd = open(q, O_RDWR|O_CLOEXEC|O_NOCTTY);
-        if (loop_fd < 0)
-                return -errno;
-
-        if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
-                return -EINVAL;
-
-        if (new_size < (uint64_t) st.st_size) {
-                /* Decrease size: first decrease btrfs size, then shorten loopback */
-                if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
-                        return -errno;
-        }
-
-        if (ftruncate(backing_fd, new_size) < 0)
-                return -errno;
-
-        if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
-                return -errno;
-
-        if (new_size > (uint64_t) st.st_size) {
-                /* Increase size: first enlarge loopback, then increase btrfs size */
-                if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
-                        return -errno;
-        }
-
-        /* Make sure the free disk space is correctly updated for both file systems */
-        (void) fsync(fd);
-        (void) fsync(backing_fd);
-
-        return 1;
-}
-
-int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
-        _cleanup_close_ int fd = -1;
-
-        fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        return btrfs_resize_loopback_fd(fd, new_size, grow_only);
-}
-
 int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
         assert(ret);
 
@@ -1183,6 +1083,18 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
         if (subvol_fd < 0)
                 return -errno;
 
+        /* Let's check if this is actually a subvolume. Note that this is mostly redundant, as BTRFS_IOC_SNAP_DESTROY
+         * would fail anyway if it is not. However, it's a good thing to check this ahead of time so that we can return
+         * ENOTTY unconditionally in this case. This is different from the ioctl() which will return EPERM/EACCES if we
+         * don't have the privileges to remove subvolumes, regardless if the specified directory is actually a
+         * subvolume or not. In order to make it easy for callers to cover the "this is not a btrfs subvolume" case
+         * let's prefer ENOTTY over EPERM/EACCES though. */
+        r = btrfs_is_subvol_fd(subvol_fd);
+        if (r < 0)
+                return r;
+        if (r == 0) /* Not a btrfs subvolume */
+                return -ENOTTY;
+
         if (subvol_id == 0) {
                 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
                 if (r < 0)
@@ -1496,7 +1408,12 @@ static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_s
         return changed;
 }
 
-static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_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,
@@ -1676,7 +1593,14 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
         return 0;
 }
 
-int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
+int btrfs_subvol_snapshot_fd_full(
+                int old_fd,
+                const char *new_path,
+                BtrfsSnapshotFlags flags,
+                copy_progress_path_t progress_path,
+                copy_progress_bytes_t progress_bytes,
+                void *userdata) {
+
         _cleanup_close_ int new_fd = -1;
         const char *subvolume;
         int r;
@@ -1704,7 +1628,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
                 } else if (r < 0)
                         return r;
 
-                r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
+                r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK, progress_path, progress_bytes, userdata);
                 if (r < 0)
                         goto fallback_fail;
 
@@ -1715,7 +1639,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
                                  * 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);
+                                        (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
                         } else {
                                 r = btrfs_subvol_set_read_only(new_path, true);
                                 if (r < 0)
@@ -1741,7 +1665,14 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
         return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
 }
 
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
+int btrfs_subvol_snapshot_full(
+                const char *old_path,
+                const char *new_path,
+                BtrfsSnapshotFlags flags,
+                copy_progress_path_t progress_path,
+                copy_progress_bytes_t progress_bytes,
+                void *userdata) {
+
         _cleanup_close_ int old_fd = -1;
 
         assert(old_path);
@@ -1751,7 +1682,7 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnaps
         if (old_fd < 0)
                 return -errno;
 
-        return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
+        return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, progress_path, progress_bytes, userdata);
 }
 
 int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {