summaryrefslogtreecommitdiff
path: root/src/basic/btrfs-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/btrfs-util.c')
-rw-r--r--src/basic/btrfs-util.c226
1 files changed, 84 insertions, 142 deletions
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index 6d2490f3d7..d08e7546d0 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -15,19 +15,15 @@
#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"
#include "device-nodes.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
#include "missing.h"
@@ -59,23 +55,6 @@ static int validate_subvolume_name(const char *name) {
return 0;
}
-static int open_parent(const char *path, int flags) {
- _cleanup_free_ char *parent = NULL;
- int fd;
-
- assert(path);
-
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
-
- fd = open(parent, flags);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
static int extract_subvolume_name(const char *path, const char **subvolume) {
const char *fn;
int r;
@@ -132,21 +111,30 @@ int btrfs_is_subvol(const char *path) {
return btrfs_is_subvol_fd(fd);
}
-int btrfs_subvol_make(const char *path) {
+int btrfs_subvol_make_fd(int fd, const char *subvolume) {
struct btrfs_ioctl_vol_args args = {};
- _cleanup_close_ int fd = -1;
- const char *subvolume;
+ _cleanup_close_ int real_fd = -1;
int r;
- assert(path);
+ assert(subvolume);
- r = extract_subvolume_name(path, &subvolume);
+ r = validate_subvolume_name(subvolume);
if (r < 0)
return r;
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return fd;
+ r = fcntl(fd, F_GETFL);
+ if (r < 0)
+ return -errno;
+ if (FLAGS_SET(r, O_PATH)) {
+ /* An O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal with
+ * O_PATH. */
+
+ real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (real_fd < 0)
+ return real_fd;
+
+ fd = real_fd;
+ }
strncpy(args.name, subvolume, sizeof(args.name)-1);
@@ -156,6 +144,24 @@ int btrfs_subvol_make(const char *path) {
return 0;
}
+int btrfs_subvol_make(const char *path) {
+ _cleanup_close_ int fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(path);
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ fd = open_parent(path, O_CLOEXEC, 0);
+ if (fd < 0)
+ return fd;
+
+ return btrfs_subvol_make_fd(fd, subvolume);
+}
+
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
uint64_t flags, nflags;
struct stat st;
@@ -393,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) \
@@ -864,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")];
- _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_sys_block_path(p, NULL, dev);
- loop_fd = open(p, 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);
@@ -1172,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)
@@ -1283,7 +1206,7 @@ int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
if (r < 0)
return r;
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ fd = open_parent(path, O_CLOEXEC, 0);
if (fd < 0)
return fd;
@@ -1485,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,
@@ -1665,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;
@@ -1693,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;
@@ -1704,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)
@@ -1723,14 +1658,21 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
if (r < 0)
return r;
- new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ new_fd = open_parent(new_path, O_CLOEXEC, 0);
if (new_fd < 0)
return new_fd;
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);
@@ -1740,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) {