From f9f70e062da2557a1127dbd584c7dfae7defe149 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 13:42:39 +0200 Subject: [PATCH 01/14] copy: Merge copy_directory() and copy_directory_fd() into copy_directory_at() Let's merge these two into a single function that can handle both variants and more. --- src/dissect/dissect.c | 2 +- src/import/import-fs.c | 6 ++-- src/shared/btrfs-util.c | 5 ++-- src/shared/copy.c | 66 +++++++++-------------------------------- src/shared/copy.h | 11 ++----- src/test/test-execute.c | 2 +- 6 files changed, 25 insertions(+), 67 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 5c742497d5..25bdd68cd2 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -1286,7 +1286,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) { } /* Try to copy as directory? */ - r = copy_directory_fd(source_fd, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT|COPY_HARDLINKS); + r = copy_directory_at(source_fd, NULL, AT_FDCWD, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT|COPY_HARDLINKS); if (r >= 0) return 0; if (r != -ENOTDIR) diff --git a/src/import/import-fs.c b/src/import/import-fs.c index d81dd13ffd..221febea19 100644 --- a/src/import/import-fs.c +++ b/src/import/import-fs.c @@ -206,9 +206,9 @@ static int import_fs(int argc, char *argv[], void *userdata) { progress_bytes, &progress); else - r = copy_directory_fd_full( - fd, - dest, + r = copy_directory_at_full( + fd, NULL, + AT_FDCWD, dest, COPY_REFLINK| COPY_SAME_MOUNT| COPY_HARDLINKS| diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 16295a5823..03f9c0d0e6 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -1561,8 +1561,9 @@ int btrfs_subvol_snapshot_fd_full( } else if (r < 0) return r; - r = copy_directory_fd_full( - old_fd, new_path, + r = copy_directory_at_full( + old_fd, NULL, + AT_FDCWD, new_path, COPY_MERGE_EMPTY| COPY_REFLINK| COPY_SAME_MOUNT| diff --git a/src/shared/copy.c b/src/shared/copy.c index 14b9b61d8a..3216607ef8 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -1208,61 +1208,22 @@ int copy_tree_at_full( return 0; } -static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) { +static int sync_dir_by_flags(int dir_fd, const char *path, CopyFlags copy_flags) { + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); if (copy_flags & COPY_SYNCFS) - return syncfs_path(AT_FDCWD, path); + return syncfs_path(dir_fd, path); if (copy_flags & COPY_FSYNC_FULL) - return fsync_parent_at(AT_FDCWD, path); + return fsync_parent_at(dir_fd, path); return 0; } -int copy_directory_fd_full( - int dirfd, - const char *to, - CopyFlags copy_flags, - copy_progress_path_t progress_path, - copy_progress_bytes_t progress_bytes, - void *userdata) { - - struct stat st; - int r; - - assert(dirfd >= 0); - assert(to); - - if (fstat(dirfd, &st) < 0) - return -errno; - - r = stat_verify_directory(&st); - if (r < 0) - return r; - - r = fd_copy_directory( - dirfd, NULL, - &st, - AT_FDCWD, to, - st.st_dev, - COPY_DEPTH_MAX, - UID_INVALID, GID_INVALID, - copy_flags, - NULL, NULL, NULL, - progress_path, - progress_bytes, - userdata); - if (r < 0) - return r; - - r = sync_dir_by_flags(to, copy_flags); - if (r < 0) - return r; - - return 0; -} - -int copy_directory_full( +int copy_directory_at_full( + int dir_fdf, const char *from, + int dir_fdt, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, @@ -1272,10 +1233,11 @@ int copy_directory_full( struct stat st; int r; - assert(from); + assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD); + assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD); assert(to); - if (lstat(from, &st) < 0) + if (fstatat(dir_fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW|(isempty(from) ? AT_EMPTY_PATH : 0)) < 0) return -errno; r = stat_verify_directory(&st); @@ -1283,9 +1245,9 @@ int copy_directory_full( return r; r = fd_copy_directory( - AT_FDCWD, from, + dir_fdf, from, &st, - AT_FDCWD, to, + dir_fdt, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, @@ -1297,7 +1259,7 @@ int copy_directory_full( if (r < 0) return r; - r = sync_dir_by_flags(to, copy_flags); + r = sync_dir_by_flags(dir_fdt, to, copy_flags); if (r < 0) return r; diff --git a/src/shared/copy.h b/src/shared/copy.h index c4482eba7e..9a0df9f4f5 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -82,14 +82,9 @@ static inline int copy_tree(const char *from, const char *to, uid_t override_uid return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL); } -int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata); -static inline int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) { - return copy_directory_fd_full(dirfd, to, copy_flags, NULL, NULL, NULL); -} - -int copy_directory_full(const char *from, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata); -static inline int copy_directory(const char *from, const char *to, CopyFlags copy_flags) { - return copy_directory_full(from, to, copy_flags, NULL, NULL, NULL); +int copy_directory_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata); +static inline int copy_directory_at(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags) { + return copy_directory_at_full(dir_fdf, from, dir_fdt, to, copy_flags, NULL, NULL, NULL); } int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size, copy_progress_bytes_t progress, void *userdata); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index a07c837e3f..a63afa873b 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -1299,7 +1299,7 @@ static int prepare_ns(const char *process_name) { /* Copy unit files to make them accessible even when unprivileged. */ assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0); - assert_se(copy_directory(unit_dir, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0); + assert_se(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0); /* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */ FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") { From 23e335784232015dc4b903408903737089a6283b Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 13:56:05 +0200 Subject: [PATCH 02/14] fs-util: Allow passing NULL path to xopenat() We recently codified in the coding style that for openat() style APIs, an empty path can be passed both as the empty string and as NULL, so let's make sure we follow that style in xopenat(). --- src/basic/fs-util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 78e92609c6..1cf0f5b945 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -1097,7 +1097,6 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags int r; assert(dir_fd >= 0 || dir_fd == AT_FDCWD); - assert(path); if (isempty(path)) { assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL)); From f90c1959dbe6489a515978eba8a40ec3bf801258 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 13:57:04 +0200 Subject: [PATCH 03/14] stat-util: Follow coding style in xstatfsat() Allow passing NULL to indicate the empty path per coding style. --- src/basic/stat-util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 633d9479dd..4db11f9299 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -461,7 +461,6 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) { _cleanup_close_ int fd = -EBADF; assert(dir_fd >= 0 || dir_fd == AT_FDCWD); - assert(path); assert(ret); fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0); From b196e17ed40987b102d72993d15de3567be742cb Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 13:57:58 +0200 Subject: [PATCH 04/14] stat-util: Add is_fs_type_at() --- src/basic/stat-util.c | 17 +++++------------ src/basic/stat-util.h | 9 +++++++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 4db11f9299..3c999098e1 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -207,20 +207,13 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { return F_TYPE_EQUAL(s->f_type, magic_value); } -int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { +int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) { struct statfs s; + int r; - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_fs_type(&s, magic_value); -} - -int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { - struct statfs s; - - if (statfs(path, &s) < 0) - return -errno; + r = xstatfsat(dir_fd, path, &s); + if (r < 0) + return r; return is_fs_type(&s, magic_value); } diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 3ae8b3eeb1..ae0aaf8f51 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -48,8 +48,13 @@ static inline int inode_same(const char *filea, const char *fileb, int flags) { typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; -int fd_is_fs_type(int fd, statfs_f_type_t magic_value); -int path_is_fs_type(const char *path, statfs_f_type_t magic_value); +int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value); +static inline int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { + return is_fs_type_at(fd, NULL, magic_value); +} +static inline int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { + return is_fs_type_at(AT_FDCWD, path, magic_value); +} bool is_temporary_fs(const struct statfs *s) _pure_; bool is_network_fs(const struct statfs *s) _pure_; From 6d2fd8df0a355342f5a8049f86d0ac915dd563fb Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 13:58:29 +0200 Subject: [PATCH 05/14] btrfs-util: Add btrfs_is_subvol_at() --- src/shared/btrfs-util.c | 20 ++++---------------- src/shared/btrfs-util.h | 9 +++++++-- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 03f9c0d0e6..4ee1f494eb 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -70,32 +70,20 @@ static int extract_subvolume_name(const char *path, char **ret) { return 0; } -int btrfs_is_subvol_fd(int fd) { +int btrfs_is_subvol_at(int dir_fd, const char *path) { struct stat st; - assert(fd >= 0); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); /* On btrfs subvolumes always have the inode 256 */ - if (fstat(fd, &st) < 0) + if (fstatat(dir_fd, strempty(path), &st, (isempty(path) ? AT_EMPTY_PATH : 0)) < 0) return -errno; if (!btrfs_might_be_subvol(&st)) return 0; - return fd_is_fs_type(fd, BTRFS_SUPER_MAGIC); -} - -int btrfs_is_subvol(const char *path) { - _cleanup_close_ int fd = -EBADF; - - assert(path); - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return -errno; - - return btrfs_is_subvol_fd(fd); + return is_fs_type_at(dir_fd, path, BTRFS_SUPER_MAGIC); } int btrfs_subvol_make_fd(int fd, const char *subvolume) { diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index 0ce6d4b96a..cee27ae5a6 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -43,8 +43,13 @@ typedef enum BtrfsRemoveFlags { BTRFS_REMOVE_QUOTA = 1 << 1, } BtrfsRemoveFlags; -int btrfs_is_subvol_fd(int fd); -int btrfs_is_subvol(const char *path); +int btrfs_is_subvol_at(int dir_fd, const char *path); +static inline int btrfs_is_subvol_fd(int fd) { + return btrfs_is_subvol_at(fd, NULL); +} +static inline int btrfs_is_subvol(const char *path) { + return btrfs_is_subvol_at(AT_FDCWD, path); +} int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret); static inline int btrfs_get_block_device(const char *path, dev_t *ret) { From 8a65b0b2c945df0c8001299eff7b293efbd55995 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 14:26:35 +0200 Subject: [PATCH 06/14] fd-util: Add path_is_root_at() A generalization of dir_fd_is_root() that allows passing a path component. --- src/basic/fd-util.c | 13 +++++++++++-- src/basic/fd-util.h | 7 +++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 907bfeb600..ecbe58a9f8 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -891,12 +891,21 @@ int fd_get_diskseq(int fd, uint64_t *ret) { return 0; } -int dir_fd_is_root(int dir_fd) { +int path_is_root_at(int dir_fd, const char *path) { STRUCT_NEW_STATX_DEFINE(st); STRUCT_NEW_STATX_DEFINE(pst); + _cleanup_close_ int fd = -EBADF; int r; - assert(dir_fd >= 0); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + + if (!isempty(path)) { + fd = openat(dir_fd, path, O_PATH|O_CLOEXEC); + if (fd < 0) + return -errno; + + dir_fd = fd; + } r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx); if (r == -ENOTDIR) diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 2f59e334c5..c870a1b899 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -101,9 +101,12 @@ int fd_is_opath(int fd); int read_nr_open(void); int fd_get_diskseq(int fd, uint64_t *ret); -int dir_fd_is_root(int dir_fd); +int path_is_root_at(int dir_fd, const char *path); +static inline int dir_fd_is_root(int dir_fd) { + return path_is_root_at(dir_fd, NULL); +} static inline int dir_fd_is_root_or_cwd(int dir_fd) { - return dir_fd == AT_FDCWD ? true : dir_fd_is_root(dir_fd); + return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL); } /* The maximum length a buffer for a /proc/self/fd/ path needs */ From f5dc1adc7162f1bf6c4618b7863e7317f3f826bf Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 2 Jun 2023 10:55:24 +0200 Subject: [PATCH 07/14] test-chase: Fix comment --- src/test/test-chase.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/test-chase.c b/src/test/test-chase.c index 558f4109e3..5bbda2007b 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -548,7 +548,7 @@ TEST(chaseat) { assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT); - /* Test CHASE_FILENAME */ + /* Test CHASE_EXTRACT_FILENAME */ assert_se(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0); assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0); From 3c8f449cc84c5c3f583841360271d927d5e384fc Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 2 Jun 2023 11:00:48 +0200 Subject: [PATCH 08/14] chase: Allow passing NULL as the empty path to chaseat() Per coding style, we should accept NULL as the empty path. --- src/basic/chase.c | 1 - src/test/test-chase.c | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/basic/chase.c b/src/basic/chase.c index 600e2b9d33..5475e8f70d 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -81,7 +81,6 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int const char *todo; int r; - assert(path); assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT)); assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME)); assert(!FLAGS_SET(flags, CHASE_TRAIL_SLASH|CHASE_EXTRACT_FILENAME)); diff --git a/src/test/test-chase.c b/src/test/test-chase.c index 5bbda2007b..c62a14a777 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -570,6 +570,10 @@ TEST(chaseat) { assert_se(streq(result, ".")); result = mfree(result); + assert_se(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0); + assert_se(streq(result, ".")); + result = mfree(result); + /* Test chase_and_openat() */ fd = chase_and_openat(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL); From 8b85333cda91c3466b86918dfb214d08037b6f2f Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 2 Jun 2023 11:05:10 +0200 Subject: [PATCH 09/14] test-chase: Add one more test for chase_and_openat() --- src/test/test-chase.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/test-chase.c b/src/test/test-chase.c index c62a14a777..4f1573cec3 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -586,6 +586,12 @@ TEST(chaseat) { assert_se(fd_verify_directory(fd) >= 0); fd = safe_close(fd); + fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result); + assert_se(fd >= 0); + assert_se(streq(result, ".")); + fd = safe_close(fd); + result = mfree(result); + /* Test chase_and_openatdir() */ assert_se(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir) >= 0); From 24dbe6039a6f2ef6bd5db4f2ff4836cfffed3c31 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 14:32:39 +0200 Subject: [PATCH 10/14] btrfs-util: Add btrfs_subvol_remove_at() We also remove btrfs_subvol_remove_fd() because btrfs_subvol_remove_at() is more general. --- src/shared/btrfs-util.c | 15 ++++++--------- src/shared/btrfs-util.h | 6 ++++-- src/shared/rm-rf.c | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 4ee1f494eb..9f8fd024a4 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -17,6 +17,7 @@ #include "alloc-util.h" #include "blockdev-util.h" #include "btrfs-util.h" +#include "chase.h" #include "chattr-util.h" #include "copy.h" #include "fd-util.h" @@ -1123,25 +1124,21 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol return 0; } -int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) { +int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags) { _cleanup_free_ char *subvolume = NULL; _cleanup_close_ int fd = -EBADF; int r; assert(path); - r = extract_subvolume_name(path, &subvolume); - if (r < 0) - return r; - - fd = open_parent(path, O_CLOEXEC, 0); + fd = chase_and_openat(dir_fd, path, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_CLOEXEC, &subvolume); if (fd < 0) return fd; - return subvol_remove_children(fd, subvolume, 0, flags); -} + r = validate_subvolume_name(subvolume); + if (r < 0) + return r; -int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) { return subvol_remove_children(fd, subvolume, 0, flags); } diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index cee27ae5a6..f79bb6e7ef 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -84,8 +84,10 @@ static inline int btrfs_subvol_snapshot(const char *old_path, const char *new_pa return btrfs_subvol_snapshot_full(old_path, new_path, flags, NULL, NULL, NULL); } -int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags); -int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags); +int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags); +static inline int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) { + return btrfs_subvol_remove_at(AT_FDCWD, path, flags); +} int btrfs_subvol_set_read_only_fd(int fd, bool b); int btrfs_subvol_set_read_only(const char *path, bool b); diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index 8e459f9d69..2b39444851 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -228,7 +228,7 @@ static int rm_rf_inner_child( if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) { /* This could be a subvolume, try to remove it */ - r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + r = btrfs_subvol_remove_at(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); if (r < 0) { if (!IN_SET(r, -ENOTTY, -EINVAL)) return r; From 5124aa8c03ae4e6d0c33de9beea6e81554b22030 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 14:36:47 +0200 Subject: [PATCH 11/14] rm-rf: Add rm_rf_at() --- src/shared/rm-rf.c | 25 ++++++++++++++----------- src/shared/rm-rf.h | 6 +++++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index 2b39444851..3a34281bb9 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -425,11 +425,14 @@ static int rm_rf_children_impl( return ret; } -int rm_rf(const char *path, RemoveFlags flags) { +int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags) { mode_t old_mode; int fd, r, q = 0; - assert(path); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + + if (FLAGS_SET(flags, REMOVE_ROOT)) + assert(path && !dot_or_dot_dot(path)); /* unlinkat() does not support AT_EMPTY_PATH or "." so a path must be provided here. */ /* For now, don't support dropping subvols when also only dropping directories, since we can't do * this race-freely. */ @@ -438,14 +441,13 @@ int rm_rf(const char *path, RemoveFlags flags) { /* We refuse to clean the root file system with this call. This is extra paranoia to never cause a * really seriously broken system. */ - if (path_equal_or_inode_same(path, "/", AT_SYMLINK_NOFOLLOW)) + if (path_is_root_at(dir_fd, path) > 0) return log_error_errno(SYNTHETIC_ERRNO(EPERM), - "Attempted to remove entire root file system (\"%s\"), and we can't allow that.", - path); + "Attempted to remove entire root file system, and we can't allow that."); if (FLAGS_SET(flags, REMOVE_SUBVOLUME | REMOVE_ROOT | REMOVE_PHYSICAL)) { /* Try to remove as subvolume first */ - r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + r = btrfs_subvol_remove_at(dir_fd, path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); if (r >= 0) return r; @@ -458,13 +460,13 @@ int rm_rf(const char *path, RemoveFlags flags) { /* Not btrfs or not a subvolume */ } - fd = openat_harder(AT_FDCWD, path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, flags, &old_mode); + fd = openat_harder(dir_fd, path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, flags, &old_mode); if (fd >= 0) { /* We have a dir */ r = rm_rf_children_impl(fd, flags, NULL, old_mode); if (FLAGS_SET(flags, REMOVE_ROOT)) - q = RET_NERRNO(rmdir(path)); + q = RET_NERRNO(unlinkat(dir_fd, path, AT_REMOVEDIR)); } else { r = fd; if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT) @@ -479,8 +481,9 @@ int rm_rf(const char *path, RemoveFlags flags) { if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { struct statfs s; - if (statfs(path, &s) < 0) - return -errno; + r = xstatfsat(dir_fd, path, &s); + if (r < 0) + return r; if (is_physical_fs(&s)) return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Attempted to remove files from a disk file system under \"%s\", refusing.", @@ -488,7 +491,7 @@ int rm_rf(const char *path, RemoveFlags flags) { } r = 0; - q = RET_NERRNO(unlink(path)); + q = RET_NERRNO(unlinkat(dir_fd, path, 0)); } if (r < 0) diff --git a/src/shared/rm-rf.h b/src/shared/rm-rf.h index 24fd9a2aa2..9d93692e0a 100644 --- a/src/shared/rm-rf.h +++ b/src/shared/rm-rf.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include #include #include "alloc-util.h" @@ -26,7 +27,10 @@ int fstatat_harder(int dfd, int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev); int rm_rf_child(int fd, const char *name, RemoveFlags flags); -int rm_rf(const char *path, RemoveFlags flags); +int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags); +static inline int rm_rf(const char *path, RemoveFlags flags) { + return rm_rf_at(AT_FDCWD, path, flags); +} /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ static inline char *rm_rf_physical_and_free(char *p) { From 77c66be37b9b55c690cb9981775d369146b6a120 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 14:43:30 +0200 Subject: [PATCH 12/14] btrfs-util: Add btrfs_subvol_set_read_only_at() --- src/shared/btrfs-util.c | 19 +++++++------------ src/shared/btrfs-util.h | 10 ++++++++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 9f8fd024a4..a3e175768b 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -152,11 +152,16 @@ int btrfs_subvol_make_fallback(const char *path, mode_t mode) { return 0; /* plain directory */ } -int btrfs_subvol_set_read_only_fd(int fd, bool b) { +int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b) { + _cleanup_close_ int fd = -EBADF; uint64_t flags, nflags; struct stat st; - assert(fd >= 0); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + + fd = xopenat(dir_fd, path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0); + if (fd < 0) + return fd; if (fstat(fd, &st) < 0) return -errno; @@ -174,16 +179,6 @@ int btrfs_subvol_set_read_only_fd(int fd, bool b) { return RET_NERRNO(ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags)); } -int btrfs_subvol_set_read_only(const char *path, bool b) { - _cleanup_close_ int fd = -EBADF; - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return -errno; - - return btrfs_subvol_set_read_only_fd(fd, b); -} - int btrfs_subvol_get_read_only_fd(int fd) { uint64_t flags; struct stat st; diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index f79bb6e7ef..56a0169027 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -89,8 +89,14 @@ static inline int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) return btrfs_subvol_remove_at(AT_FDCWD, path, flags); } -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_set_read_only_at(int dir_fd, const char *path, bool b); +static inline int btrfs_subvol_set_read_only_fd(int fd, bool b) { + return btrfs_subvol_set_read_only_at(fd, NULL, b); +} +static inline int btrfs_subvol_set_read_only(const char *path, bool b) { + return btrfs_subvol_set_read_only_at(AT_FDCWD, path, b); +} + int btrfs_subvol_get_read_only_fd(int fd); int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret); From cf91b9155c20a57bfc756b2b7e1a8f401f2bf16d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 14:52:56 +0200 Subject: [PATCH 13/14] chattr-util: Make chattr_full() an openat() style function --- src/basic/chattr-util.c | 28 ++++++++++++++-------------- src/basic/chattr-util.h | 13 ++++++++----- src/home/homework-luks.c | 2 +- src/tmpfiles/tmpfiles.c | 2 +- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/basic/chattr-util.c b/src/basic/chattr-util.c index 3c66a3e0c8..c59fb8a84e 100644 --- a/src/basic/chattr-util.c +++ b/src/basic/chattr-util.c @@ -9,29 +9,29 @@ #include "chattr-util.h" #include "errno-util.h" #include "fd-util.h" +#include "fs-util.h" #include "macro.h" #include "string-util.h" -int chattr_full(const char *path, - int fd, - unsigned value, - unsigned mask, - unsigned *ret_previous, - unsigned *ret_final, - ChattrApplyFlags flags) { +int chattr_full( + int dir_fd, + const char *path, + unsigned value, + unsigned mask, + unsigned *ret_previous, + unsigned *ret_final, + ChattrApplyFlags flags) { - _cleanup_close_ int fd_will_close = -EBADF; + _cleanup_close_ int fd = -EBADF; unsigned old_attr, new_attr; int set_flags_errno = 0; struct stat st; - assert(path || fd >= 0); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); - if (fd < 0) { - fd = fd_will_close = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - } + fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, /* xopen_flags = */ 0, /* mode = */ 0); + if (fd < 0) + return -errno; if (fstat(fd, &st) < 0) return -errno; diff --git a/src/basic/chattr-util.h b/src/basic/chattr-util.h index 82f91c66d9..c1ee63b4fa 100644 --- a/src/basic/chattr-util.h +++ b/src/basic/chattr-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include #include #include #include @@ -39,13 +40,15 @@ typedef enum ChattrApplyFlags { CHATTR_WARN_UNSUPPORTED_FLAGS = 1 << 1, } ChattrApplyFlags; -int chattr_full(const char *path, int fd, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags); - +int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags); +static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *previous) { + return chattr_full(dir_fd, path, value, mask, previous, NULL, 0); +} static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) { - return chattr_full(NULL, fd, value, mask, previous, NULL, 0); + return chattr_full(fd, NULL, value, mask, previous, NULL, 0); } static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) { - return chattr_full(path, -1, value, mask, previous, NULL, 0); + return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0); } int read_attr_fd(int fd, unsigned *ret); @@ -57,5 +60,5 @@ int read_attr_path(const char *p, unsigned *ret); #define CHATTR_SECRET_FLAGS (FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL) static inline int chattr_secret(int fd, ChattrApplyFlags flags) { - return chattr_full(NULL, fd, CHATTR_SECRET_FLAGS, CHATTR_SECRET_FLAGS, NULL, NULL, flags|CHATTR_FALLBACK_BITWISE); + return chattr_full(fd, NULL, CHATTR_SECRET_FLAGS, CHATTR_SECRET_FLAGS, NULL, NULL, flags|CHATTR_FALLBACK_BITWISE); } diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index e1f0ff1dcb..2359699801 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -2281,7 +2281,7 @@ int home_create_luks( setup->temporary_image_path = TAKE_PTR(t); - r = chattr_full(t, setup->image_fd, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE); + r = chattr_full(setup->image_fd, NULL, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE); if (r < 0 && r != -ENOANO) /* ENOANO → some bits didn't work; which we skip logging about because chattr_full() already debug logs about those flags */ log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set file attributes on %s, ignoring: %m", setup->temporary_image_path); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index af1b904d32..e0087d997b 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1540,7 +1540,7 @@ static int fd_set_attribute( return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path); unsigned previous, current; - r = chattr_full(NULL, procfs_fd, f, item->attribute_mask, &previous, ¤t, CHATTR_FALLBACK_BITWISE); + r = chattr_full(procfs_fd, NULL, f, item->attribute_mask, &previous, ¤t, CHATTR_FALLBACK_BITWISE); if (r == -ENOANO) log_warning("Cannot set file attributes for '%s', maybe due to incompatibility in specified attributes, " "previous=0x%08x, current=0x%08x, expected=0x%08x, ignoring.", From fab4ef72625c067683bace339ad7ac208a330918 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 1 Jun 2023 14:59:59 +0200 Subject: [PATCH 14/14] btrfs-util: Add btrfs_subvol_snapshot_at() --- src/import/export-tar.c | 2 +- src/import/import-fs.c | 6 ++-- src/import/pull-tar.c | 6 ++-- src/nspawn/nspawn.c | 30 ++++++++-------- src/shared/btrfs-util.c | 71 +++++++++++++++---------------------- src/shared/btrfs-util.h | 11 ++---- src/shared/discover-image.c | 14 ++++---- src/test/test-btrfs.c | 12 ++++--- 8 files changed, 68 insertions(+), 84 deletions(-) diff --git a/src/import/export-tar.c b/src/import/export-tar.c index 4aa8204983..2f54cd8114 100644 --- a/src/import/export-tar.c +++ b/src/import/export-tar.c @@ -293,7 +293,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType return r; /* Let's try to make a snapshot, if we can, so that the export is atomic */ - r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE); + r = btrfs_subvol_snapshot_at(sfd, NULL, AT_FDCWD, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE); if (r < 0) { log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path); e->temp_path = mfree(e->temp_path); diff --git a/src/import/import-fs.c b/src/import/import-fs.c index 221febea19..fd79c8f01a 100644 --- a/src/import/import-fs.c +++ b/src/import/import-fs.c @@ -194,9 +194,9 @@ static int import_fs(int argc, char *argv[], void *userdata) { BLOCK_SIGNALS(SIGINT, SIGTERM); if (arg_btrfs_subvol) - r = btrfs_subvol_snapshot_fd_full( - fd, - dest, + r = btrfs_subvol_snapshot_at_full( + fd, NULL, + AT_FDCWD, dest, BTRFS_SNAPSHOT_FALLBACK_COPY| BTRFS_SNAPSHOT_FALLBACK_DIRECTORY| BTRFS_SNAPSHOT_RECURSIVE| diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index f22eb0e3a3..e474dffa72 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -237,9 +237,9 @@ static int tar_pull_make_local_copy(TarPull *i) { return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p); if (i->flags & PULL_BTRFS_SUBVOL) - r = btrfs_subvol_snapshot( - i->final_path, - t, + r = btrfs_subvol_snapshot_at( + AT_FDCWD, i->final_path, + AT_FDCWD, t, (i->flags & PULL_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)| BTRFS_SNAPSHOT_FALLBACK_COPY| BTRFS_SNAPSHOT_FALLBACK_DIRECTORY| diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index f54e955cae..ab61184d9f 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -5564,13 +5564,13 @@ static int run(int argc, char *argv[]) { { BLOCK_SIGNALS(SIGINT); - r = btrfs_subvol_snapshot(arg_directory, np, - (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | - BTRFS_SNAPSHOT_FALLBACK_COPY | - BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | - BTRFS_SNAPSHOT_RECURSIVE | - BTRFS_SNAPSHOT_QUOTA | - BTRFS_SNAPSHOT_SIGINT); + r = btrfs_subvol_snapshot_at(AT_FDCWD, arg_directory, AT_FDCWD, np, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA | + BTRFS_SNAPSHOT_SIGINT); } if (r == -EINTR) { log_error_errno(r, "Interrupted while copying file system tree to %s, removed again.", np); @@ -5605,14 +5605,14 @@ static int run(int argc, char *argv[]) { { BLOCK_SIGNALS(SIGINT); - r = btrfs_subvol_snapshot(arg_template, arg_directory, - (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | - BTRFS_SNAPSHOT_FALLBACK_COPY | - BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | - BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | - BTRFS_SNAPSHOT_RECURSIVE | - BTRFS_SNAPSHOT_QUOTA | - BTRFS_SNAPSHOT_SIGINT); + r = btrfs_subvol_snapshot_at(AT_FDCWD, arg_template, AT_FDCWD, arg_directory, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA | + BTRFS_SNAPSHOT_SIGINT); } if (r == -EEXIST) log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index a3e175768b..429e5afe11 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -1506,22 +1506,37 @@ static int subvol_snapshot_children( return 0; } -int btrfs_subvol_snapshot_fd_full( - int old_fd, - const char *new_path, +int btrfs_subvol_snapshot_at_full( + int dir_fdf, + const char *from, + int dir_fdt, + const char *to, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata) { _cleanup_free_ char *subvolume = NULL; - _cleanup_close_ int new_fd = -EBADF; + _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF; int r; - assert(old_fd >= 0); - assert(new_path); + assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD); + assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD); + assert(to); - r = btrfs_is_subvol_fd(old_fd); + old_fd = xopenat(dir_fdf, from, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0); + if (old_fd < 0) + return old_fd; + + new_fd = chase_and_openat(dir_fdt, to, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_CLOEXEC, &subvolume); + if (new_fd < 0) + return new_fd; + + r = validate_subvolume_name(subvolume); + if (r < 0) + return r; + + r = btrfs_is_subvol_at(dir_fdf, from); if (r < 0) return r; if (r == 0) { @@ -1531,10 +1546,10 @@ int btrfs_subvol_snapshot_fd_full( if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY)) return -EISDIR; - r = btrfs_subvol_make(new_path); + r = btrfs_subvol_make_fd(new_fd, subvolume); if (ERRNO_IS_NOT_SUPPORTED(r) && (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) + if (mkdirat(new_fd, subvolume, 0755) < 0) return -errno; plain_directory = true; @@ -1542,8 +1557,8 @@ int btrfs_subvol_snapshot_fd_full( return r; r = copy_directory_at_full( - old_fd, NULL, - AT_FDCWD, new_path, + dir_fdf, from, + new_fd, subvolume, COPY_MERGE_EMPTY| COPY_REFLINK| COPY_SAME_MOUNT| @@ -1564,9 +1579,9 @@ int btrfs_subvol_snapshot_fd_full( * 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, NULL); + (void) chattr_at(new_fd, subvolume, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL); } else { - r = btrfs_subvol_set_read_only(new_path, true); + r = btrfs_subvol_set_read_only_at(new_fd, subvolume, true); if (r < 0) goto fallback_fail; } @@ -1575,41 +1590,13 @@ int btrfs_subvol_snapshot_fd_full( return 0; fallback_fail: - (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + (void) rm_rf_at(new_fd, subvolume, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); return r; } - r = extract_subvolume_name(new_path, &subvolume); - if (r < 0) - return r; - - 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_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 = -EBADF; - - assert(old_path); - assert(new_path); - - old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (old_fd < 0) - return -errno; - - 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) { struct btrfs_ioctl_search_args args = { diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index 56a0169027..de38f1e45c 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -74,14 +74,9 @@ int btrfs_subvol_make_fd(int fd, const char *subvolume); int btrfs_subvol_make_fallback(const char *path, mode_t); -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); -static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) { - return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL); -} - -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); -static inline int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) { - return btrfs_subvol_snapshot_full(old_path, new_path, flags, NULL, NULL, NULL); +int btrfs_subvol_snapshot_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata); +static inline int btrfs_subvol_snapshot_at(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags) { + return btrfs_subvol_snapshot_at_full(dir_fdf, from, dir_fdt, to, flags, NULL, NULL, NULL); } int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags); diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 198c975c44..d93a1cc74c 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -923,13 +923,13 @@ int image_clone(Image *i, const char *new_name, bool read_only) { new_path = strjoina("/var/lib/machines/", new_name); - r = btrfs_subvol_snapshot(i->path, new_path, - (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | - BTRFS_SNAPSHOT_FALLBACK_COPY | - BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | - BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | - BTRFS_SNAPSHOT_RECURSIVE | - BTRFS_SNAPSHOT_QUOTA); + r = btrfs_subvol_snapshot_at(AT_FDCWD, i->path, AT_FDCWD, new_path, + (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA); if (r >= 0) /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ (void) btrfs_subvol_auto_qgroup(new_path, 0, true); diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c index 67acba23a7..ccb1661a91 100644 --- a/src/test/test-btrfs.c +++ b/src/test/test-btrfs.c @@ -54,11 +54,11 @@ int main(int argc, char *argv[]) { if (r < 0) log_error_errno(r, "Failed to write file: %m"); - r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest2", 0); + r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest2", 0); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); - r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY); + r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); @@ -74,7 +74,8 @@ int main(int argc, char *argv[]) { if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); - r = btrfs_subvol_snapshot("/etc", "/etc2", BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY); + r = btrfs_subvol_snapshot_at(AT_FDCWD, "/etc", AT_FDCWD, "/etc2", + BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); @@ -115,7 +116,7 @@ int main(int argc, char *argv[]) { if (mkdir("/xxxrectest/mnt", 0755) < 0) log_error_errno(errno, "Failed to make directory: %m"); - r = btrfs_subvol_snapshot("/xxxrectest", "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE); + r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxrectest", AT_FDCWD, "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE); if (r < 0) log_error_errno(r, "Failed to snapshot subvolume: %m"); @@ -151,7 +152,8 @@ int main(int argc, char *argv[]) { if (r < 0) log_error_errno(r, "Failed to set up quota limit: %m"); - r = btrfs_subvol_snapshot("/xxxquotatest", "/xxxquotatest2", BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA); + r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxquotatest", AT_FDCWD, "/xxxquotatest2", + BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA); if (r < 0) log_error_errno(r, "Failed to set up snapshot: %m");