diff --git a/src/basic/chase.c b/src/basic/chase.c index 02b52791ce..a4d9edb4c9 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/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/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 */ 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)); diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 633d9479dd..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); } @@ -461,7 +454,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); 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_; 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/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/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 d81dd13ffd..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| @@ -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/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 16295a5823..429e5afe11 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" @@ -70,32 +71,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) { @@ -163,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; @@ -185,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; @@ -1135,25 +1119,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); } @@ -1526,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) { @@ -1551,18 +1546,19 @@ 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; } else if (r < 0) return r; - r = copy_directory_fd_full( - old_fd, new_path, + r = copy_directory_at_full( + dir_fdf, from, + new_fd, subvolume, COPY_MERGE_EMPTY| COPY_REFLINK| COPY_SAME_MOUNT| @@ -1583,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; } @@ -1594,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 0ce6d4b96a..de38f1e45c 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) { @@ -69,21 +74,24 @@ 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_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_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_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_remove(const char *path, BtrfsRemoveFlags flags); -int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags); +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_set_read_only_fd(int fd, bool b); -int btrfs_subvol_set_read_only(const char *path, bool b); int btrfs_subvol_get_read_only_fd(int fd); int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret); 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/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/shared/rm-rf.c b/src/shared/rm-rf.c index 8e459f9d69..3a34281bb9 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; @@ -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) { 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"); diff --git a/src/test/test-chase.c b/src/test/test-chase.c index bee33bc8b2..75c508970e 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -574,7 +574,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); @@ -596,6 +596,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); @@ -608,6 +612,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); 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") { 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.",