-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#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 "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 "io-util.h"
#include "macro.h"
#include "missing.h"
#include "path-util.h"
-#include "selinux-util.h"
+#include "rm-rf.h"
#include "smack-util.h"
#include "sparse-endian.h"
#include "stat-util.h"
return 0;
}
-int btrfs_subvol_make_label(const char *path) {
- int r;
-
- assert(path);
-
- r = mac_selinux_create_file_prepare(path, S_IFDIR);
- if (r < 0)
- return r;
-
- r = btrfs_subvol_make(path);
- mac_selinux_create_file_clear();
-
- if (r < 0)
- return r;
-
- return mac_smack_fix(path, false, false);
-}
-
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
uint64_t flags, nflags;
struct stat st;
}
int btrfs_reflink(int infd, int outfd) {
- struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
- /* Make sure we invoke the ioctl on a regular file, so that no
- * device driver accidentally gets it. */
-
- if (fstat(outfd, &st) < 0)
- return -errno;
+ /* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
- if (!S_ISREG(st.st_mode))
- return -EINVAL;
-
- r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
+ r = fd_verify_regular(outfd);
if (r < 0)
+ return r;
+
+ if (ioctl(outfd, BTRFS_IOC_CLONE, infd) < 0)
return -errno;
return 0;
.src_length = sz,
.dest_offset = out_offset,
};
- struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
assert(sz > 0);
- if (fstat(outfd, &st) < 0)
- return -errno;
-
- if (!S_ISREG(st.st_mode))
- return -EINVAL;
-
- r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
+ r = fd_verify_regular(outfd);
if (r < 0)
+ return r;
+
+ if (ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args) < 0)
return -errno;
return 0;
}
int btrfs_defrag_fd(int fd) {
- struct stat st;
+ int r;
assert(fd >= 0);
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISREG(st.st_mode))
- return -EINVAL;
+ r = fd_verify_regular(fd);
+ if (r < 0)
+ return r;
if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
return -errno;
int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
struct btrfs_ioctl_vol_args args = {};
- _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
+ 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;
if (r == 0)
return -ENODEV;
- if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
- return -ENOMEM;
+ xsprintf_sys_block_path(p, "/loop/backing_file", dev);
r = read_one_line_file(p, &backing);
if (r == -ENOENT)
return -ENODEV;
if (grow_only && new_size < (uint64_t) st.st_size)
return -EINVAL;
- if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
- return -ENOMEM;
- loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ xsprintf_sys_block_path(p, NULL, dev);
+ loop_fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY);
if (loop_fd < 0)
return -errno;
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;
* 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;
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;
* 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;
}
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);
return 0;
}
- *ret = items;
- items = NULL;
+ *ret = TAKE_PTR(items);
return (int) n_items;
}