From bf819d3ac2b015139a3571a953e0b361632ebb34 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 14:03:26 +0200 Subject: [PATCH 1/5] basic: split out sync() family of calls from fs-util.[ch] into new c/h file No changes in code, just some splitting out. --- src/basic/fileio.c | 6 +- src/basic/fs-util.c | 166 --------------------- src/basic/fs-util.h | 8 -- src/basic/meson.build | 2 + src/basic/sync-util.c | 175 +++++++++++++++++++++++ src/basic/sync-util.h | 11 ++ src/boot/bless-boot.c | 1 + src/boot/bootctl.c | 8 +- src/coredump/coredump.c | 8 +- src/home/homed-manager.c | 1 + src/home/homework-luks.c | 1 + src/import/pull-job.c | 2 +- src/libsystemd/sd-id128/id128-util.c | 7 +- src/libsystemd/sd-journal/journal-file.c | 9 +- src/partition/repart.c | 1 + src/random-seed/random-seed.c | 1 + src/shared/copy.c | 1 + src/shared/install-file.c | 1 + src/shared/machine-id-setup.c | 2 +- src/shutdown/umount.c | 2 +- src/sysusers/sysusers.c | 1 + src/test/test-fs-util.c | 1 + 22 files changed, 216 insertions(+), 199 deletions(-) create mode 100644 src/basic/sync-util.c create mode 100644 src/basic/sync-util.h diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 601e1a61e9..a268a41add 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -25,6 +25,7 @@ #include "socket-util.h" #include "stdio-util.h" #include "string-util.h" +#include "sync-util.h" #include "tmpfile-util.h" /* The maximum size of the file we'll read in one go in read_full_file() (64M). */ @@ -1195,10 +1196,7 @@ int fflush_sync_and_check(FILE *f) { if (fd < 0) return 0; - if (fsync(fd) < 0) - return -errno; - - r = fsync_directory_of_file(fd); + r = fsync_full(fd); if (r < 0) return r; diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 6c3ef972fe..2412df197a 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -1349,172 +1349,6 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { return 0; } -int fsync_directory_of_file(int fd) { - _cleanup_close_ int dfd = -1; - struct stat st; - int r; - - assert(fd >= 0); - - /* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check - * for the inode type first */ - if (fstat(fd, &st) < 0) - return -errno; - - if (S_ISDIR(st.st_mode)) { - dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0); - if (dfd < 0) - return -errno; - - } else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other - * types check O_PATH flag */ - int flags; - - flags = fcntl(fd, F_GETFL); - if (flags < 0) - return -errno; - - if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case - * we can sensibly do what is requested. Otherwise this refers - * to a socket, fifo or device node, where the concept of a - * containing directory doesn't make too much sense. */ - return -ENOTTY; - } - - if (dfd < 0) { - _cleanup_free_ char *path = NULL; - - r = fd_get_path(fd, &path); - if (r < 0) { - log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", - fd, - r == -ENOSYS ? ", ignoring" : ""); - - if (r == -ENOSYS) - /* If /proc is not available, we're most likely running in some - * chroot environment, and syncing the directory is not very - * important in that case. Let's just silently do nothing. */ - return 0; - - return r; - } - - if (!path_is_absolute(path)) - return -EINVAL; - - dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0); - if (dfd < 0) - return dfd; - } - - if (fsync(dfd) < 0) - return -errno; - - return 0; -} - -int fsync_full(int fd) { - int r, q; - - /* Sync both the file and the directory */ - - r = fsync(fd) < 0 ? -errno : 0; - - q = fsync_directory_of_file(fd); - if (r < 0) /* Return earlier error */ - return r; - if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a - * parent dir */ - return 0; - return q; -} - -int fsync_path_at(int at_fd, const char *path) { - _cleanup_close_ int opened_fd = -1; - int fd; - - if (isempty(path)) { - if (at_fd == AT_FDCWD) { - opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - fd = opened_fd; - } else - fd = at_fd; - } else { - opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); - if (opened_fd < 0) - return -errno; - - fd = opened_fd; - } - - if (fsync(fd) < 0) - return -errno; - - return 0; -} - -int fsync_parent_at(int at_fd, const char *path) { - _cleanup_close_ int opened_fd = -1; - - if (isempty(path)) { - if (at_fd != AT_FDCWD) - return fsync_directory_of_file(at_fd); - - opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - if (fsync(opened_fd) < 0) - return -errno; - - return 0; - } - - opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW); - if (opened_fd < 0) - return -errno; - - return fsync_directory_of_file(opened_fd); -} - -int fsync_path_and_parent_at(int at_fd, const char *path) { - _cleanup_close_ int opened_fd = -1; - - if (isempty(path)) { - if (at_fd != AT_FDCWD) - return fsync_full(at_fd); - - opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - } else - opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - return fsync_full(opened_fd); -} - -int syncfs_path(int atfd, const char *path) { - _cleanup_close_ int fd = -1; - - if (isempty(path)) { - if (atfd != AT_FDCWD) - return syncfs(atfd) < 0 ? -errno : 0; - - fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - } else - fd = openat(atfd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return -errno; - - if (syncfs(fd) < 0) - return -errno; - - return 0; -} - int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; int fd, r; diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index d612cbe404..7120ecd99d 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -137,14 +137,6 @@ typedef enum UnlinkDeallocateFlags { int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags); -int fsync_directory_of_file(int fd); -int fsync_full(int fd); -int fsync_path_at(int at_fd, const char *path); -int fsync_parent_at(int at_fd, const char *path); -int fsync_path_and_parent_at(int at_fd, const char *path); - -int syncfs_path(int atfd, const char *path); - int open_parent(const char *path, int flags, mode_t mode); int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); diff --git a/src/basic/meson.build b/src/basic/meson.build index 82f42df929..7b42ac428c 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -215,6 +215,8 @@ basic_sources = files(''' strv.h strxcpyx.c strxcpyx.h + sync-util.c + sync-util.h sysctl-util.c sysctl-util.h syslog-util.c diff --git a/src/basic/sync-util.c b/src/basic/sync-util.c new file mode 100644 index 0000000000..479ff73c63 --- /dev/null +++ b/src/basic/sync-util.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "fd-util.h" +#include "fs-util.h" +#include "path-util.h" +#include "sync-util.h" + +int fsync_directory_of_file(int fd) { + _cleanup_close_ int dfd = -1; + struct stat st; + int r; + + assert(fd >= 0); + + /* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check + * for the inode type first */ + if (fstat(fd, &st) < 0) + return -errno; + + if (S_ISDIR(st.st_mode)) { + dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0); + if (dfd < 0) + return -errno; + + } else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other + * types check O_PATH flag */ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags < 0) + return -errno; + + if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case + * we can sensibly do what is requested. Otherwise this refers + * to a socket, fifo or device node, where the concept of a + * containing directory doesn't make too much sense. */ + return -ENOTTY; + } + + if (dfd < 0) { + _cleanup_free_ char *path = NULL; + + r = fd_get_path(fd, &path); + if (r < 0) { + log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", + fd, + r == -ENOSYS ? ", ignoring" : ""); + + if (r == -ENOSYS) + /* If /proc is not available, we're most likely running in some + * chroot environment, and syncing the directory is not very + * important in that case. Let's just silently do nothing. */ + return 0; + + return r; + } + + if (!path_is_absolute(path)) + return -EINVAL; + + dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0); + if (dfd < 0) + return dfd; + } + + if (fsync(dfd) < 0) + return -errno; + + return 0; +} + +int fsync_full(int fd) { + int r, q; + + /* Sync both the file and the directory */ + + r = fsync(fd) < 0 ? -errno : 0; + + q = fsync_directory_of_file(fd); + if (r < 0) /* Return earlier error */ + return r; + if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a + * parent dir */ + return 0; + return q; +} + +int fsync_path_at(int at_fd, const char *path) { + _cleanup_close_ int opened_fd = -1; + int fd; + + if (isempty(path)) { + if (at_fd == AT_FDCWD) { + opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + } else + fd = at_fd; + } else { + opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + } + + if (fsync(fd) < 0) + return -errno; + + return 0; +} + +int fsync_parent_at(int at_fd, const char *path) { + _cleanup_close_ int opened_fd = -1; + + if (isempty(path)) { + if (at_fd != AT_FDCWD) + return fsync_directory_of_file(at_fd); + + opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + if (fsync(opened_fd) < 0) + return -errno; + + return 0; + } + + opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (opened_fd < 0) + return -errno; + + return fsync_directory_of_file(opened_fd); +} + +int fsync_path_and_parent_at(int at_fd, const char *path) { + _cleanup_close_ int opened_fd = -1; + + if (isempty(path)) { + if (at_fd != AT_FDCWD) + return fsync_full(at_fd); + + opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + } else + opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + return fsync_full(opened_fd); +} + +int syncfs_path(int at_fd, const char *path) { + _cleanup_close_ int fd = -1; + + if (isempty(path)) { + if (at_fd != AT_FDCWD) + return syncfs(at_fd) < 0 ? -errno : 0; + + fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + } else + fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return -errno; + + if (syncfs(fd) < 0) + return -errno; + + return 0; +} diff --git a/src/basic/sync-util.h b/src/basic/sync-util.h new file mode 100644 index 0000000000..e449440a53 --- /dev/null +++ b/src/basic/sync-util.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int fsync_directory_of_file(int fd); +int fsync_full(int fd); + +int fsync_path_at(int at_fd, const char *path); +int fsync_parent_at(int at_fd, const char *path); +int fsync_path_and_parent_at(int at_fd, const char *path); + +int syncfs_path(int at_fd, const char *path); diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c index 7d5a52841e..cb06886727 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -14,6 +14,7 @@ #include "parse-util.h" #include "path-util.h" #include "pretty-print.h" +#include "sync-util.h" #include "terminal-util.h" #include "util.h" #include "verbs.h" diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 27b0086406..91b36e91ba 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -38,6 +38,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "terminal-util.h" #include "tmpfile-util.h" #include "umask-util.h" @@ -569,13 +570,12 @@ static int copy_file_with_version_check(const char *from, const char *to, bool f (void) copy_times(fd_from, fd_to, 0); - if (fsync(fd_to) < 0) { + r = fsync_full(fd_to); + if (r < 0) { (void) unlink_noerrno(t); - return log_error_errno(errno, "Failed to copy data from \"%s\" to \"%s\": %m", from, t); + return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", from, t); } - (void) fsync_directory_of_file(fd_to); - if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0) { (void) unlink_noerrno(t); return log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", t, to); diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 444b9ec374..d7eab3d36c 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -48,6 +48,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "tmpfile-util.h" #include "user-record.h" #include "user-util.h" @@ -261,10 +262,9 @@ static int fix_permissions( (void) fix_acl(fd, uid); (void) fix_xattr(fd, context); - if (fsync(fd) < 0) - return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename)); - - (void) fsync_directory_of_file(fd); + r = fsync_full(fd); + if (r < 0) + return log_error_errno(r, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename)); r = link_tmpfile(fd, filename, target); if (r < 0) diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 6549e6bd4e..070fd97d69 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -38,6 +38,7 @@ #include "socket-util.h" #include "stat-util.h" #include "strv.h" +#include "sync-util.h" #include "tmpfile-util.h" #include "udev-util.h" #include "user-record-sign.h" diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 2321a31021..8af9223d57 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -36,6 +36,7 @@ #include "resize-fs.h" #include "stat-util.h" #include "strv.h" +#include "sync-util.h" #include "tmpfile-util.h" /* Round down to the nearest 4K size. Given that newer hardware generally prefers 4K sectors, let's align our diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 4e37dce33f..34b116a8f8 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -7,7 +7,6 @@ #include "alloc-util.h" #include "fd-util.h" #include "format-util.h" -#include "fs-util.h" #include "gcrypt-util.h" #include "hexdecoct.h" #include "import-util.h" @@ -18,6 +17,7 @@ #include "pull-job.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "xattr-util.h" void pull_job_close_disk_fd(PullJob *j) { diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 2074771a41..19937110b0 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -5,12 +5,12 @@ #include #include "fd-util.h" -#include "fs-util.h" #include "hexdecoct.h" #include "id128-util.h" #include "io-util.h" #include "stdio-util.h" #include "string-util.h" +#include "sync-util.h" char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) { unsigned n, k = 0; @@ -167,10 +167,7 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { return r; if (do_sync) { - if (fsync(fd) < 0) - return -errno; - - r = fsync_directory_of_file(fd); + r = fsync_full(fd); if (r < 0) return r; } diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 9fecc84495..d5ceba2f8f 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -32,6 +32,7 @@ #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "xattr-util.h" #define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem)) @@ -474,11 +475,9 @@ static int journal_file_refresh_header(JournalFile *f) { r = journal_file_set_online(f); - /* Sync the online state to disk */ - (void) fsync(f->fd); - - /* We likely just created a new file, also sync the directory this file is located in. */ - (void) fsync_directory_of_file(f->fd); + /* Sync the online state to disk; likely just created a new file, also sync the directory this file + * is located in. */ + (void) fsync_full(f->fd); return r; } diff --git a/src/partition/repart.c b/src/partition/repart.c index 564ee4835b..3203b48d8c 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -61,6 +61,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "terminal-util.h" #include "tpm2-util.h" #include "user-util.h" diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c index e003ca60e3..7724e0365e 100644 --- a/src/random-seed/random-seed.c +++ b/src/random-seed/random-seed.c @@ -25,6 +25,7 @@ #include "parse-util.h" #include "random-util.h" #include "string-util.h" +#include "sync-util.h" #include "util.h" #include "xattr-util.h" diff --git a/src/shared/copy.c b/src/shared/copy.c index f27ce252ee..9053fce333 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -29,6 +29,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "time-util.h" #include "tmpfile-util.h" #include "umask-util.h" diff --git a/src/shared/install-file.c b/src/shared/install-file.c index 664f028ad1..5187d71bec 100644 --- a/src/shared/install-file.c +++ b/src/shared/install-file.c @@ -10,6 +10,7 @@ #include "install-file.h" #include "missing_syscall.h" #include "rm-rf.h" +#include "sync-util.h" int fs_make_very_read_only(int fd) { struct stat st; diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c index d2f989272d..e483675a75 100644 --- a/src/shared/machine-id-setup.c +++ b/src/shared/machine-id-setup.c @@ -9,7 +9,6 @@ #include "alloc-util.h" #include "fd-util.h" -#include "fs-util.h" #include "id128-util.h" #include "io-util.h" #include "log.h" @@ -23,6 +22,7 @@ #include "process-util.h" #include "stat-util.h" #include "string-util.h" +#include "sync-util.h" #include "umask-util.h" #include "util.h" #include "virt.h" diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c index c2a26242c0..09fc339ab1 100644 --- a/src/shutdown/umount.c +++ b/src/shutdown/umount.c @@ -23,7 +23,6 @@ #include "device-util.h" #include "escape.h" #include "fd-util.h" -#include "fs-util.h" #include "fstab-util.h" #include "libmount-util.h" #include "mount-setup.h" @@ -34,6 +33,7 @@ #include "signal-util.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "umount.h" #include "util.h" #include "virt.h" diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 7daebdbe55..c55f7f8b7e 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -29,6 +29,7 @@ #include "specifier.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "tmpfile-util-label.h" #include "uid-range.h" #include "user-record.h" diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index ac7ee5d3bf..f0cac447c3 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -16,6 +16,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "sync-util.h" #include "tests.h" #include "tmpfile-util.h" #include "umask-util.h" From d8e32c471f6a52407184e421243b174f7480d180 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 14:21:57 +0200 Subject: [PATCH 2/5] basic: split out glyph/emoji related calls from locale-util.[ch] into glyph-util.[ch] These functions are used pretty much independently of locale, i.e. the only info relevant is whether th locale is UTF-8 or not. Hence let's give this its own pair of .c/.h files. --- src/basic/fs-util.c | 2 +- src/basic/glyph-util.c | 129 +++++++++++++++++++++++++ src/basic/glyph-util.h | 56 +++++++++++ src/basic/locale-util.c | 123 ----------------------- src/basic/locale-util.h | 51 +--------- src/basic/meson.build | 2 + src/basic/unit-def.h | 2 +- src/boot/bootctl.c | 2 +- src/busctl/busctl.c | 2 +- src/cryptenroll/cryptenroll-recovery.c | 2 +- src/delta/delta.c | 2 +- src/firstboot/firstboot.c | 1 + src/home/homectl-recovery-key.c | 2 +- src/home/homectl.c | 1 + src/libsystemd/sd-bus/bus-dump.c | 2 +- src/login/sysfs-show.c | 2 +- src/partition/repart.c | 2 +- src/shared/ask-password-api.c | 2 +- src/shared/bus-unit-procs.c | 2 +- src/shared/format-table.c | 2 +- src/shared/libfido2-util.c | 2 +- src/shared/logs-show.c | 1 + src/test/test-locale-util.c | 1 + 23 files changed, 206 insertions(+), 187 deletions(-) create mode 100644 src/basic/glyph-util.c create mode 100644 src/basic/glyph-util.h diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 2412df197a..bf771f2e2f 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -12,7 +12,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "locale-util.h" +#include "glyph-util.h" #include "log.h" #include "macro.h" #include "missing_fcntl.h" diff --git a/src/basic/glyph-util.c b/src/basic/glyph-util.c new file mode 100644 index 0000000000..8810738fc3 --- /dev/null +++ b/src/basic/glyph-util.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "env-util.h" +#include "glyph-util.h" +#include "locale-util.h" +#include "strv.h" + +bool emoji_enabled(void) { + static int cached_emoji_enabled = -1; + + if (cached_emoji_enabled < 0) { + int val; + + val = getenv_bool("SYSTEMD_EMOJI"); + if (val < 0) + cached_emoji_enabled = + is_locale_utf8() && + !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux"); + else + cached_emoji_enabled = val; + } + + return cached_emoji_enabled; +} + +const char *special_glyph(SpecialGlyph code) { + + /* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be + * conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still + * works reasonably well on the Linux console. For details see: + * + * http://git.altlinux.org/people/legion/packages/kbd.git?p=kbd.git;a=blob;f=data/consolefonts/README.eurlatgr + */ + + static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { + /* ASCII fallback */ + [false] = { + [SPECIAL_GLYPH_TREE_VERTICAL] = "| ", + [SPECIAL_GLYPH_TREE_BRANCH] = "|-", + [SPECIAL_GLYPH_TREE_RIGHT] = "`-", + [SPECIAL_GLYPH_TREE_SPACE] = " ", + [SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">", + [SPECIAL_GLYPH_BLACK_CIRCLE] = "*", + [SPECIAL_GLYPH_WHITE_CIRCLE] = "*", + [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "x", + [SPECIAL_GLYPH_CIRCLE_ARROW] = "*", + [SPECIAL_GLYPH_BULLET] = "*", + [SPECIAL_GLYPH_MU] = "u", + [SPECIAL_GLYPH_CHECK_MARK] = "+", + [SPECIAL_GLYPH_CROSS_MARK] = "-", + [SPECIAL_GLYPH_LIGHT_SHADE] = "-", + [SPECIAL_GLYPH_DARK_SHADE] = "X", + [SPECIAL_GLYPH_SIGMA] = "S", + [SPECIAL_GLYPH_ARROW] = "->", + [SPECIAL_GLYPH_ELLIPSIS] = "...", + [SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]", + [SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]", + [SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}", + [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)", + [SPECIAL_GLYPH_NEUTRAL_SMILEY] = ":-|", + [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(", + [SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{", + [SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[", + [SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,", + [SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */ + [SPECIAL_GLYPH_RECYCLING] = "~", + [SPECIAL_GLYPH_DOWNLOAD] = "\\", + [SPECIAL_GLYPH_SPARKLES] = "*", + }, + + /* UTF-8 */ + [true] = { + /* The following are multiple glyphs in both ASCII and in UNICODE */ + [SPECIAL_GLYPH_TREE_VERTICAL] = "\342\224\202 ", /* │ */ + [SPECIAL_GLYPH_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ + [SPECIAL_GLYPH_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ + [SPECIAL_GLYPH_TREE_SPACE] = " ", /* */ + + /* Single glyphs in both cases */ + [SPECIAL_GLYPH_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ + [SPECIAL_GLYPH_BLACK_CIRCLE] = "\342\227\217", /* ● */ + [SPECIAL_GLYPH_WHITE_CIRCLE] = "\u25CB", /* ○ */ + [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "\u00D7", /* × */ + [SPECIAL_GLYPH_CIRCLE_ARROW] = "\u21BB", /* ↻ */ + [SPECIAL_GLYPH_BULLET] = "\342\200\242", /* • */ + [SPECIAL_GLYPH_MU] = "\316\274", /* μ (actually called: GREEK SMALL LETTER MU) */ + [SPECIAL_GLYPH_CHECK_MARK] = "\342\234\223", /* ✓ */ + [SPECIAL_GLYPH_CROSS_MARK] = "\342\234\227", /* ✗ (actually called: BALLOT X) */ + [SPECIAL_GLYPH_LIGHT_SHADE] = "\342\226\221", /* ░ */ + [SPECIAL_GLYPH_DARK_SHADE] = "\342\226\223", /* ▒ */ + [SPECIAL_GLYPH_SIGMA] = "\316\243", /* Σ */ + + /* Single glyph in Unicode, two in ASCII */ + [SPECIAL_GLYPH_ARROW] = "\342\206\222", /* → (actually called: RIGHTWARDS ARROW) */ + + /* Single glyph in Unicode, three in ASCII */ + [SPECIAL_GLYPH_ELLIPSIS] = "\342\200\246", /* … (actually called: HORIZONTAL ELLIPSIS) */ + + /* Three glyphs in Unicode, five in ASCII */ + [SPECIAL_GLYPH_EXTERNAL_LINK] = "[\360\237\241\225]", /* 🡕 (actually called: NORTH EAST SANS-SERIF ARROW, enclosed in []) */ + + /* These smileys are a single glyph in Unicode, and three in ASCII */ + [SPECIAL_GLYPH_ECSTATIC_SMILEY] = "\360\237\230\207", /* 😇 (actually called: SMILING FACE WITH HALO) */ + [SPECIAL_GLYPH_HAPPY_SMILEY] = "\360\237\230\200", /* 😀 (actually called: GRINNING FACE) */ + [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = "\360\237\231\202", /* 🙂 (actually called: SLIGHTLY SMILING FACE) */ + [SPECIAL_GLYPH_NEUTRAL_SMILEY] = "\360\237\230\220", /* 😐 (actually called: NEUTRAL FACE) */ + [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = "\360\237\231\201", /* 🙁 (actually called: SLIGHTLY FROWNING FACE) */ + [SPECIAL_GLYPH_UNHAPPY_SMILEY] = "\360\237\230\250", /* 😨 (actually called: FEARFUL FACE) */ + [SPECIAL_GLYPH_DEPRESSED_SMILEY] = "\360\237\244\242", /* 🤢 (actually called: NAUSEATED FACE) */ + + /* This emoji is a single character cell glyph in Unicode, and three in ASCII */ + [SPECIAL_GLYPH_LOCK_AND_KEY] = "\360\237\224\220", /* 🔐 (actually called: CLOSED LOCK WITH KEY) */ + + /* This emoji is a single character cell glyph in Unicode, and two in ASCII */ + [SPECIAL_GLYPH_TOUCH] = "\360\237\221\206", /* 👆 (actually called: BACKHAND INDEX POINTING UP) */ + + /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */ + [SPECIAL_GLYPH_RECYCLING] = "\u267B\uFE0F ", /* ♻️ (actually called: UNIVERSAL RECYCLNG SYMBOL) */ + [SPECIAL_GLYPH_DOWNLOAD] = "\u2935\uFE0F ", /* ⤵️ (actually called: RIGHT ARROW CURVING DOWN) */ + [SPECIAL_GLYPH_SPARKLES] = "\u2728", /* ✨ */ + }, + }; + + if (code < 0) + return NULL; + + assert(code < _SPECIAL_GLYPH_MAX); + return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code]; +} diff --git a/src/basic/glyph-util.h b/src/basic/glyph-util.h new file mode 100644 index 0000000000..ddee210041 --- /dev/null +++ b/src/basic/glyph-util.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include + +#include "macro.h" + +typedef enum SpecialGlyph { + SPECIAL_GLYPH_TREE_VERTICAL, + SPECIAL_GLYPH_TREE_BRANCH, + SPECIAL_GLYPH_TREE_RIGHT, + SPECIAL_GLYPH_TREE_SPACE, + SPECIAL_GLYPH_TRIANGULAR_BULLET, + SPECIAL_GLYPH_BLACK_CIRCLE, + SPECIAL_GLYPH_WHITE_CIRCLE, + SPECIAL_GLYPH_MULTIPLICATION_SIGN, + SPECIAL_GLYPH_CIRCLE_ARROW, + SPECIAL_GLYPH_BULLET, + SPECIAL_GLYPH_MU, + SPECIAL_GLYPH_CHECK_MARK, + SPECIAL_GLYPH_CROSS_MARK, + SPECIAL_GLYPH_ARROW, + SPECIAL_GLYPH_ELLIPSIS, + SPECIAL_GLYPH_LIGHT_SHADE, + SPECIAL_GLYPH_DARK_SHADE, + SPECIAL_GLYPH_SIGMA, + SPECIAL_GLYPH_EXTERNAL_LINK, + _SPECIAL_GLYPH_FIRST_EMOJI, + SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI, + SPECIAL_GLYPH_HAPPY_SMILEY, + SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY, + SPECIAL_GLYPH_NEUTRAL_SMILEY, + SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY, + SPECIAL_GLYPH_UNHAPPY_SMILEY, + SPECIAL_GLYPH_DEPRESSED_SMILEY, + SPECIAL_GLYPH_LOCK_AND_KEY, + SPECIAL_GLYPH_TOUCH, + SPECIAL_GLYPH_RECYCLING, + SPECIAL_GLYPH_DOWNLOAD, + SPECIAL_GLYPH_SPARKLES, + _SPECIAL_GLYPH_MAX, + _SPECIAL_GLYPH_INVALID = -EINVAL, +} SpecialGlyph; + +const char *special_glyph(SpecialGlyph code) _const_; + +bool emoji_enabled(void); + +static inline const char *special_glyph_check_mark(bool b) { + return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK); +} + +static inline const char *special_glyph_check_mark_space(bool b) { + return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " "; +} diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index 007e3a091e..b6f9a760a4 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -317,129 +317,6 @@ out: return (bool) cached_answer; } -bool emoji_enabled(void) { - static int cached_emoji_enabled = -1; - - if (cached_emoji_enabled < 0) { - int val; - - val = getenv_bool("SYSTEMD_EMOJI"); - if (val < 0) - cached_emoji_enabled = - is_locale_utf8() && - !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux"); - else - cached_emoji_enabled = val; - } - - return cached_emoji_enabled; -} - -const char *special_glyph(SpecialGlyph code) { - - /* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be - * conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still - * works reasonably well on the Linux console. For details see: - * - * http://git.altlinux.org/people/legion/packages/kbd.git?p=kbd.git;a=blob;f=data/consolefonts/README.eurlatgr - */ - - static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { - /* ASCII fallback */ - [false] = { - [SPECIAL_GLYPH_TREE_VERTICAL] = "| ", - [SPECIAL_GLYPH_TREE_BRANCH] = "|-", - [SPECIAL_GLYPH_TREE_RIGHT] = "`-", - [SPECIAL_GLYPH_TREE_SPACE] = " ", - [SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">", - [SPECIAL_GLYPH_BLACK_CIRCLE] = "*", - [SPECIAL_GLYPH_WHITE_CIRCLE] = "*", - [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "x", - [SPECIAL_GLYPH_CIRCLE_ARROW] = "*", - [SPECIAL_GLYPH_BULLET] = "*", - [SPECIAL_GLYPH_MU] = "u", - [SPECIAL_GLYPH_CHECK_MARK] = "+", - [SPECIAL_GLYPH_CROSS_MARK] = "-", - [SPECIAL_GLYPH_LIGHT_SHADE] = "-", - [SPECIAL_GLYPH_DARK_SHADE] = "X", - [SPECIAL_GLYPH_SIGMA] = "S", - [SPECIAL_GLYPH_ARROW] = "->", - [SPECIAL_GLYPH_ELLIPSIS] = "...", - [SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]", - [SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]", - [SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}", - [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)", - [SPECIAL_GLYPH_NEUTRAL_SMILEY] = ":-|", - [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(", - [SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{", - [SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[", - [SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,", - [SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */ - [SPECIAL_GLYPH_RECYCLING] = "~", - [SPECIAL_GLYPH_DOWNLOAD] = "\\", - [SPECIAL_GLYPH_SPARKLES] = "*", - }, - - /* UTF-8 */ - [true] = { - /* The following are multiple glyphs in both ASCII and in UNICODE */ - [SPECIAL_GLYPH_TREE_VERTICAL] = "\342\224\202 ", /* │ */ - [SPECIAL_GLYPH_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ - [SPECIAL_GLYPH_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ - [SPECIAL_GLYPH_TREE_SPACE] = " ", /* */ - - /* Single glyphs in both cases */ - [SPECIAL_GLYPH_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ - [SPECIAL_GLYPH_BLACK_CIRCLE] = "\342\227\217", /* ● */ - [SPECIAL_GLYPH_WHITE_CIRCLE] = "\u25CB", /* ○ */ - [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "\u00D7", /* × */ - [SPECIAL_GLYPH_CIRCLE_ARROW] = "\u21BB", /* ↻ */ - [SPECIAL_GLYPH_BULLET] = "\342\200\242", /* • */ - [SPECIAL_GLYPH_MU] = "\316\274", /* μ (actually called: GREEK SMALL LETTER MU) */ - [SPECIAL_GLYPH_CHECK_MARK] = "\342\234\223", /* ✓ */ - [SPECIAL_GLYPH_CROSS_MARK] = "\342\234\227", /* ✗ (actually called: BALLOT X) */ - [SPECIAL_GLYPH_LIGHT_SHADE] = "\342\226\221", /* ░ */ - [SPECIAL_GLYPH_DARK_SHADE] = "\342\226\223", /* ▒ */ - [SPECIAL_GLYPH_SIGMA] = "\316\243", /* Σ */ - - /* Single glyph in Unicode, two in ASCII */ - [SPECIAL_GLYPH_ARROW] = "\342\206\222", /* → (actually called: RIGHTWARDS ARROW) */ - - /* Single glyph in Unicode, three in ASCII */ - [SPECIAL_GLYPH_ELLIPSIS] = "\342\200\246", /* … (actually called: HORIZONTAL ELLIPSIS) */ - - /* Three glyphs in Unicode, five in ASCII */ - [SPECIAL_GLYPH_EXTERNAL_LINK] = "[\360\237\241\225]", /* 🡕 (actually called: NORTH EAST SANS-SERIF ARROW, enclosed in []) */ - - /* These smileys are a single glyph in Unicode, and three in ASCII */ - [SPECIAL_GLYPH_ECSTATIC_SMILEY] = "\360\237\230\207", /* 😇 (actually called: SMILING FACE WITH HALO) */ - [SPECIAL_GLYPH_HAPPY_SMILEY] = "\360\237\230\200", /* 😀 (actually called: GRINNING FACE) */ - [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = "\360\237\231\202", /* 🙂 (actually called: SLIGHTLY SMILING FACE) */ - [SPECIAL_GLYPH_NEUTRAL_SMILEY] = "\360\237\230\220", /* 😐 (actually called: NEUTRAL FACE) */ - [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = "\360\237\231\201", /* 🙁 (actually called: SLIGHTLY FROWNING FACE) */ - [SPECIAL_GLYPH_UNHAPPY_SMILEY] = "\360\237\230\250", /* 😨 (actually called: FEARFUL FACE) */ - [SPECIAL_GLYPH_DEPRESSED_SMILEY] = "\360\237\244\242", /* 🤢 (actually called: NAUSEATED FACE) */ - - /* This emoji is a single character cell glyph in Unicode, and three in ASCII */ - [SPECIAL_GLYPH_LOCK_AND_KEY] = "\360\237\224\220", /* 🔐 (actually called: CLOSED LOCK WITH KEY) */ - - /* This emoji is a single character cell glyph in Unicode, and two in ASCII */ - [SPECIAL_GLYPH_TOUCH] = "\360\237\221\206", /* 👆 (actually called: BACKHAND INDEX POINTING UP) */ - - /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */ - [SPECIAL_GLYPH_RECYCLING] = "\u267B\uFE0F ", /* ♻️ (actually called: UNIVERSAL RECYCLNG SYMBOL) */ - [SPECIAL_GLYPH_DOWNLOAD] = "\u2935\uFE0F ", /* ⤵️ (actually called: RIGHT ARROW CURVING DOWN) */ - [SPECIAL_GLYPH_SPARKLES] = "\u2728", /* ✨ */ - }, - }; - - if (code < 0) - return NULL; - - assert(code < _SPECIAL_GLYPH_MAX); - return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code]; -} - void locale_variables_free(char *l[_VARIABLE_LC_MAX]) { if (!l) return; diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h index 3430eb6ee1..bab927146b 100644 --- a/src/basic/locale-util.h +++ b/src/basic/locale-util.h @@ -2,8 +2,8 @@ #pragma once #include -#include #include +#include #include "macro.h" @@ -39,47 +39,6 @@ void init_gettext(void); bool is_locale_utf8(void); -typedef enum SpecialGlyph { - SPECIAL_GLYPH_TREE_VERTICAL, - SPECIAL_GLYPH_TREE_BRANCH, - SPECIAL_GLYPH_TREE_RIGHT, - SPECIAL_GLYPH_TREE_SPACE, - SPECIAL_GLYPH_TRIANGULAR_BULLET, - SPECIAL_GLYPH_BLACK_CIRCLE, - SPECIAL_GLYPH_WHITE_CIRCLE, - SPECIAL_GLYPH_MULTIPLICATION_SIGN, - SPECIAL_GLYPH_CIRCLE_ARROW, - SPECIAL_GLYPH_BULLET, - SPECIAL_GLYPH_MU, - SPECIAL_GLYPH_CHECK_MARK, - SPECIAL_GLYPH_CROSS_MARK, - SPECIAL_GLYPH_ARROW, - SPECIAL_GLYPH_ELLIPSIS, - SPECIAL_GLYPH_LIGHT_SHADE, - SPECIAL_GLYPH_DARK_SHADE, - SPECIAL_GLYPH_SIGMA, - SPECIAL_GLYPH_EXTERNAL_LINK, - _SPECIAL_GLYPH_FIRST_EMOJI, - SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI, - SPECIAL_GLYPH_HAPPY_SMILEY, - SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY, - SPECIAL_GLYPH_NEUTRAL_SMILEY, - SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY, - SPECIAL_GLYPH_UNHAPPY_SMILEY, - SPECIAL_GLYPH_DEPRESSED_SMILEY, - SPECIAL_GLYPH_LOCK_AND_KEY, - SPECIAL_GLYPH_TOUCH, - SPECIAL_GLYPH_RECYCLING, - SPECIAL_GLYPH_DOWNLOAD, - SPECIAL_GLYPH_SPARKLES, - _SPECIAL_GLYPH_MAX, - _SPECIAL_GLYPH_INVALID = -EINVAL, -} SpecialGlyph; - -const char *special_glyph(SpecialGlyph code) _const_; - -bool emoji_enabled(void); - const char* locale_variable_to_string(LocaleVariable i) _const_; LocaleVariable locale_variable_from_string(const char *s) _pure_; @@ -94,11 +53,3 @@ void locale_variables_free(char* l[_VARIABLE_LC_MAX]); static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) { locale_variables_free(*l); } - -static inline const char *special_glyph_check_mark(bool b) { - return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK); -} - -static inline const char *special_glyph_check_mark_space(bool b) { - return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " "; -} diff --git a/src/basic/meson.build b/src/basic/meson.build index 7b42ac428c..ac084ce60a 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -58,6 +58,8 @@ basic_sources = files(''' fs-util.h glob-util.c glob-util.h + glyph-util.c + glyph-util.h gunicode.c gunicode.h hash-funcs.c diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index 08651efa57..f80e554d2b 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -4,7 +4,7 @@ #include #include "errno-list.h" -#include "locale-util.h" +#include "glyph-util.h" #include "macro.h" /* The enum order is used to order unit jobs in the job queue diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 91b36e91ba..bb3627ed6e 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -25,7 +25,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "locale-util.h" +#include "glyph-util.h" #include "main-func.h" #include "mkdir.h" #include "pager.h" diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index fb91fb41b6..27210e9389 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -16,8 +16,8 @@ #include "fd-util.h" #include "fileio.h" #include "format-table.h" +#include "glyph-util.h" #include "json.h" -#include "locale-util.h" #include "log.h" #include "main-func.h" #include "pager.h" diff --git a/src/cryptenroll/cryptenroll-recovery.c b/src/cryptenroll/cryptenroll-recovery.c index 3204c463a5..3fed4bcdd2 100644 --- a/src/cryptenroll/cryptenroll-recovery.c +++ b/src/cryptenroll/cryptenroll-recovery.c @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "cryptenroll-recovery.h" +#include "glyph-util.h" #include "json.h" -#include "locale-util.h" #include "memory-util.h" #include "qrcode-util.h" #include "recovery-key.h" diff --git a/src/delta/delta.c b/src/delta/delta.c index 282168a584..51fe4b4bed 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -9,8 +9,8 @@ #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" +#include "glyph-util.h" #include "hashmap.h" -#include "locale-util.h" #include "log.h" #include "main-func.h" #include "nulstr-util.h" diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 6fdba66f57..1727649ae1 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -16,6 +16,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "glyph-util.h" #include "hostname-util.h" #include "kbd-util.h" #include "libcrypt-util.h" diff --git a/src/home/homectl-recovery-key.c b/src/home/homectl-recovery-key.c index f1a180baca..da1899ae14 100644 --- a/src/home/homectl-recovery-key.c +++ b/src/home/homectl-recovery-key.c @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "errno-util.h" +#include "glyph-util.h" #include "homectl-recovery-key.h" #include "libcrypt-util.h" -#include "locale-util.h" #include "memory-util.h" #include "qrcode-util.h" #include "random-util.h" diff --git a/src/home/homectl.c b/src/home/homectl.c index 227c499c9c..d5b699a242 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -14,6 +14,7 @@ #include "fd-util.h" #include "fileio.h" #include "format-table.h" +#include "glyph-util.h" #include "home-util.h" #include "homectl-fido2.h" #include "homectl-pkcs11.h" diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index d50cd1eedd..0a4b3cbf68 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -11,7 +11,7 @@ #include "capability-util.h" #include "fileio.h" #include "format-util.h" -#include "locale-util.h" +#include "glyph-util.h" #include "macro.h" #include "string-util.h" #include "strv.h" diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c index 5c3af06cac..d79f753c9a 100644 --- a/src/login/sysfs-show.c +++ b/src/login/sysfs-show.c @@ -6,7 +6,7 @@ #include "alloc-util.h" #include "device-enumerator-private.h" -#include "locale-util.h" +#include "glyph-util.h" #include "path-util.h" #include "string-util.h" #include "sysfs-show.h" diff --git a/src/partition/repart.c b/src/partition/repart.c index 3203b48d8c..5422d5e1e5 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -34,12 +34,12 @@ #include "format-table.h" #include "format-util.h" #include "fs-util.h" +#include "glyph-util.h" #include "gpt.h" #include "hexdecoct.h" #include "id128-util.h" #include "json.h" #include "list.h" -#include "locale-util.h" #include "loop-util.h" #include "main-func.h" #include "mkdir.h" diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 8b840ac84c..a60ccee4d8 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -26,8 +26,8 @@ #include "fileio.h" #include "format-util.h" #include "fs-util.h" +#include "glyph-util.h" #include "io-util.h" -#include "locale-util.h" #include "log.h" #include "macro.h" #include "memory-util.h" diff --git a/src/shared/bus-unit-procs.c b/src/shared/bus-unit-procs.c index 6f4a71138c..3bdcba325f 100644 --- a/src/shared/bus-unit-procs.c +++ b/src/shared/bus-unit-procs.c @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "bus-unit-procs.h" +#include "glyph-util.h" #include "hashmap.h" #include "list.h" -#include "locale-util.h" #include "macro.h" #include "path-util.h" #include "process-util.h" diff --git a/src/shared/format-table.c b/src/shared/format-table.c index e524aa095e..81a7949a7f 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -12,10 +12,10 @@ #include "format-table.h" #include "format-util.h" #include "fs-util.h" +#include "glyph-util.h" #include "gunicode.h" #include "id128-util.h" #include "in-addr-util.h" -#include "locale-util.h" #include "memory-util.h" #include "pager.h" #include "parse-util.h" diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 6d18178b68..a3356c139a 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -7,7 +7,7 @@ #include "ask-password-api.h" #include "dlfcn-util.h" #include "format-table.h" -#include "locale-util.h" +#include "glyph-util.h" #include "log.h" #include "memory-util.h" #include "random-util.h" diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 82fd13db75..75fe4f34f7 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -15,6 +15,7 @@ #include "alloc-util.h" #include "fd-util.h" #include "format-util.h" +#include "glyph-util.h" #include "hashmap.h" #include "hostname-util.h" #include "id128-util.h" diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c index e22481b1b6..17a8c2b985 100644 --- a/src/test/test-locale-util.c +++ b/src/test/test-locale-util.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "glyph-util.h" #include "kbd-util.h" #include "locale-util.h" #include "macro.h" From 9e5fd717994a4935490a0a0b04599d0ccbc66855 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 14:44:17 +0200 Subject: [PATCH 3/5] =?UTF-8?q?basic:=20split=20out=20inotify-related=20ca?= =?UTF-8?q?lls=20from=20fs-util.h=20=E2=86=92=20inotify-util.h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/basic/fs-util.c | 25 ---------------- src/basic/fs-util.h | 16 ---------- src/basic/inotify-util.c | 29 +++++++++++++++++++ src/basic/inotify-util.h | 22 ++++++++++++++ src/basic/meson.build | 2 ++ src/basic/terminal-util.c | 1 + src/core/cgroup.c | 2 +- src/core/manager.c | 2 +- src/core/path.c | 2 +- src/libsystemd/sd-event/event-source.h | 2 +- src/libsystemd/sd-journal/sd-journal.c | 1 + src/libsystemd/sd-network/sd-network.c | 1 + src/timesync/wait-sync.c | 2 +- .../tty-ask-password-agent.c | 2 +- src/udev/udevd.c | 1 + 15 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 src/basic/inotify-util.c create mode 100644 src/basic/inotify-util.h diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index bf771f2e2f..a83f35a4c9 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -682,31 +682,6 @@ int unlink_or_warn(const char *filename) { return 0; } -int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - int wd; - - /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ - wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask); - if (wd < 0) - return -errno; - - return wd; -} - -int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { - int wd; - - wd = inotify_add_watch(fd, pathname, mask); - if (wd < 0) { - if (errno == ENOSPC) - return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); - - return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); - } - - return wd; -} - bool unsafe_transition(const struct stat *a, const struct stat *b) { /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 7120ecd99d..61a6a81bf3 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -67,21 +66,6 @@ int var_tmp_dir(const char **ret); int unlink_or_warn(const char *filename); -#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) - -#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ - for ((e) = &buffer.ev; \ - (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ - (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) - -union inotify_event_buffer { - struct inotify_event ev; - uint8_t raw[INOTIFY_EVENT_MAX]; -}; - -int inotify_add_watch_fd(int fd, int what, uint32_t mask); -int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask); - enum { CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */ CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */ diff --git a/src/basic/inotify-util.c b/src/basic/inotify-util.c new file mode 100644 index 0000000000..848f8590fa --- /dev/null +++ b/src/basic/inotify-util.c @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "fd-util.h" +#include "inotify-util.h" + +int inotify_add_watch_fd(int fd, int what, uint32_t mask) { + int wd; + + /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ + wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask); + if (wd < 0) + return -errno; + + return wd; +} + +int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { + int wd; + + wd = inotify_add_watch(fd, pathname, mask); + if (wd < 0) { + if (errno == ENOSPC) + return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); + + return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); + } + + return wd; +} diff --git a/src/basic/inotify-util.h b/src/basic/inotify-util.h new file mode 100644 index 0000000000..88af08688f --- /dev/null +++ b/src/basic/inotify-util.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include +#include + +#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1) + +#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ + for ((e) = &buffer.ev; \ + (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ + (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) + +union inotify_event_buffer { + struct inotify_event ev; + uint8_t raw[INOTIFY_EVENT_MAX]; +}; + +int inotify_add_watch_fd(int fd, int what, uint32_t mask); +int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask); diff --git a/src/basic/meson.build b/src/basic/meson.build index ac084ce60a..0d8100a391 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -72,6 +72,8 @@ basic_sources = files(''' hostname-util.h in-addr-util.c in-addr-util.h + inotify-util.c + inotify-util.h io-util.c io-util.h khash.c diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index d769423d6e..33743f8c4d 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -26,6 +26,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "inotify-util.h" #include "io-util.h" #include "log.h" #include "macro.h" diff --git a/src/core/cgroup.c b/src/core/cgroup.c index c19454e8bd..a5770c0332 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -18,8 +18,8 @@ #include "cgroup.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "in-addr-prefix-util.h" +#include "inotify-util.h" #include "io-util.h" #include "ip-protocol-list.h" #include "limits-util.h" diff --git a/src/core/manager.c b/src/core/manager.c index 2e7db509d1..0b2e29ae14 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -44,9 +44,9 @@ #include "exit-status.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "generator-setup.h" #include "hashmap.h" +#include "inotify-util.h" #include "install.h" #include "io-util.h" #include "label.h" diff --git a/src/core/path.c b/src/core/path.c index 693636b0ee..3dd0206e7a 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -11,8 +11,8 @@ #include "dbus-unit.h" #include "escape.h" #include "fd-util.h" -#include "fs-util.h" #include "glob-util.h" +#include "inotify-util.h" #include "macro.h" #include "mkdir.h" #include "path.h" diff --git a/src/libsystemd/sd-event/event-source.h b/src/libsystemd/sd-event/event-source.h index d2dc21470e..08b409fef1 100644 --- a/src/libsystemd/sd-event/event-source.h +++ b/src/libsystemd/sd-event/event-source.h @@ -7,8 +7,8 @@ #include "sd-event.h" -#include "fs-util.h" #include "hashmap.h" +#include "inotify-util.h" #include "list.h" #include "prioq.h" #include "ratelimit.h" diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c index fbd2fb38bb..8b7415f0db 100644 --- a/src/libsystemd/sd-journal/sd-journal.c +++ b/src/libsystemd/sd-journal/sd-journal.c @@ -25,6 +25,7 @@ #include "hashmap.h" #include "hostname-util.h" #include "id128-util.h" +#include "inotify-util.h" #include "io-util.h" #include "journal-def.h" #include "journal-file.h" diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index ee93dae9e3..dc48e91837 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -10,6 +10,7 @@ #include "env-file.h" #include "fd-util.h" #include "fs-util.h" +#include "inotify-util.h" #include "macro.h" #include "parse-util.h" #include "stdio-util.h" diff --git a/src/timesync/wait-sync.c b/src/timesync/wait-sync.c index 892746d5ee..606b0160a4 100644 --- a/src/timesync/wait-sync.c +++ b/src/timesync/wait-sync.c @@ -14,7 +14,7 @@ #include "sd-event.h" #include "fd-util.h" -#include "fs-util.h" +#include "inotify-util.h" #include "main-func.h" #include "signal-util.h" #include "time-util.h" diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 839df8d6d2..37cfd8bb72 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -24,8 +24,8 @@ #include "exit-status.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "hashmap.h" +#include "inotify-util.h" #include "io-util.h" #include "macro.h" #include "main-func.h" diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 6df580d629..b73f4776e7 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -40,6 +40,7 @@ #include "format-util.h" #include "fs-util.h" #include "hashmap.h" +#include "inotify-util.h" #include "io-util.h" #include "limits-util.h" #include "list.h" From f435195925ccdf96cf8413898f9b9590f8b5f760 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 15:12:27 +0200 Subject: [PATCH 4/5] =?UTF-8?q?basic:=20spit=20out=20chase=5Fsymlinks()=20?= =?UTF-8?q?from=20fs-util.[ch]=20=E2=86=92=20chase-symlinks.[ch]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/basic/chase-symlinks.c | 517 ++++++++++++++++++++++++++ src/basic/chase-symlinks.h | 30 ++ src/basic/fileio.c | 1 + src/basic/fs-util.c | 505 ------------------------- src/basic/fs-util.h | 23 -- src/basic/meson.build | 2 + src/basic/mkdir.c | 1 + src/basic/mountpoint-util.c | 1 + src/basic/os-util.c | 1 + src/basic/path-util.c | 1 + src/basic/stat-util.c | 3 +- src/basic/unit-file.c | 1 + src/core/dbus-manager.c | 2 +- src/core/execute.c | 2 +- src/core/namespace.c | 2 +- src/core/service.c | 2 +- src/core/socket.c | 2 +- src/core/unit.c | 1 + src/delta/delta.c | 1 + src/dissect/dissect.c | 1 + src/firstboot/firstboot.c | 1 + src/fstab-generator/fstab-generator.c | 2 +- src/journal/journalctl.c | 1 + src/libsystemd/sd-device/sd-device.c | 1 + src/mount/mount-tool.c | 1 + src/nspawn/nspawn-bind-user.c | 2 +- src/nspawn/nspawn-mount.c | 1 + src/nspawn/nspawn.c | 1 + src/partition/repart.c | 1 + src/portable/portable.c | 1 + src/portable/portablectl.c | 3 +- src/shared/discover-image.c | 1 + src/shared/dissect-image.c | 1 + src/shared/dropin.c | 2 +- src/shared/install.c | 1 + src/shared/mount-util.c | 1 + src/shared/specifier.c | 1 + src/shared/switch-root.c | 2 +- src/shared/user-record.c | 1 + src/sysext/sysext.c | 1 + src/systemctl/systemctl-util.c | 1 + src/test/test-chase-symlinks.c | 2 +- src/test/test-copy.c | 1 + src/test/test-fs-util.c | 1 + src/tmpfiles/offline-passwd.c | 2 +- src/tmpfiles/tmpfiles.c | 1 + src/udev/udev-builtin-net_id.c | 2 +- src/volatile-root/volatile-root.c | 2 +- 48 files changed, 594 insertions(+), 543 deletions(-) create mode 100644 src/basic/chase-symlinks.c create mode 100644 src/basic/chase-symlinks.h diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c new file mode 100644 index 0000000000..dd3a217203 --- /dev/null +++ b/src/basic/chase-symlinks.c @@ -0,0 +1,517 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "alloc-util.h" +#include "chase-symlinks.h" +#include "fd-util.h" +#include "fs-util.h" +#include "glyph-util.h" +#include "log.h" +#include "path-util.h" +#include "string-util.h" +#include "user-util.h" + +bool unsafe_transition(const struct stat *a, const struct stat *b) { + /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to + * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files + * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ + + if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ + return false; + + return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */ +} + +static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { + _cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL; + struct stat st; + + if (!FLAGS_SET(flags, CHASE_WARN)) + return -ENOLINK; + + (void) fd_get_path(a, &n1); + (void) fd_get_path(b, &n2); + + if (fstat(a, &st) == 0) + user_a = uid_to_name(st.st_uid); + if (fstat(b, &st) == 0) + user_b = uid_to_name(st.st_uid); + + return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), + "Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.", + strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path); +} + +static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { + _cleanup_free_ char *n1 = NULL; + + if (!FLAGS_SET(flags, CHASE_WARN)) + return -EREMOTE; + + (void) fd_get_path(fd, &n1); + + return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), + "Detected autofs mount point %s during canonicalization of %s.", + strna(n1), path); +} + +int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) { + _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; + _cleanup_close_ int fd = -1; + unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ + bool exists = true, append_trail_slash = false; + struct stat previous_stat; + const char *todo; + int r; + + assert(path); + + /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ + if ((flags & CHASE_NONEXISTENT) && ret_fd) + return -EINVAL; + + if ((flags & CHASE_STEP) && ret_fd) + return -EINVAL; + + if (isempty(path)) + return -EINVAL; + + /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following + * symlinks relative to a root directory, instead of the root of the host. + * + * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following + * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is + * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first + * prefixed accordingly. + * + * Algorithmically this operates on two path buffers: "done" are the components of the path we already + * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to + * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning + * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no + * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races + * to a minimum. + * + * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got + * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this + * function what to do when encountering a symlink with an absolute path as directory: prefix it by the + * specified path. + * + * There are five ways to invoke this function: + * + * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is + * returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0 + * is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is + * returned if the destination was found, -ENOENT if it wasn't. + * + * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file + * descriptor is returned as return value. This is useful to open files relative to some root + * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using + * fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with + * CHASE_NONEXISTENT. + * + * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first + * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if + * a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the + * path is fully normalized, and == 0 for each normalization step. This may be combined with + * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. + * + * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from + * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If + * CHASE_WARN is also set, a warning describing the unsafe transition is emitted. + * + * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization + * is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of + * the mount point is emitted. + */ + + /* A root directory of "/" or "" is identical to none */ + if (empty_or_root(original_root)) + original_root = NULL; + + if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) { + /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set + * and doesn't care about any of the other special features we provide either. */ + r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); + if (r < 0) + return -errno; + + *ret_fd = r; + return 0; + } + + if (original_root) { + r = path_make_absolute_cwd(original_root, &root); + if (r < 0) + return r; + + /* Simplify the root directory, so that it has no duplicate slashes and nothing at the + * end. While we won't resolve the root path we still simplify it. Note that dropping the + * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY + * anyway. Moreover at the end of this function after processing everything we'll always turn + * the empty string back to "/". */ + delete_trailing_chars(root, "/"); + path_simplify(root); + + if (flags & CHASE_PREFIX_ROOT) { + /* We don't support relative paths in combination with a root directory */ + if (!path_is_absolute(path)) + return -EINVAL; + + path = prefix_roota(root, path); + } + } + + r = path_make_absolute_cwd(path, &buffer); + if (r < 0) + return r; + + fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); + if (fd < 0) + return -errno; + + if (flags & CHASE_SAFE) + if (fstat(fd, &previous_stat) < 0) + return -errno; + + if (flags & CHASE_TRAIL_SLASH) + append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/."); + + if (root) { + /* If we are operating on a root directory, let's take the root directory as it is. */ + + todo = path_startswith(buffer, root); + if (!todo) + return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, + SYNTHETIC_ERRNO(ECHRNG), + "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", + path, root); + + done = strdup(root); + } else { + todo = buffer; + done = strdup("/"); + } + + for (;;) { + _cleanup_free_ char *first = NULL; + _cleanup_close_ int child = -1; + struct stat st; + const char *e; + + r = path_find_first_component(&todo, true, &e); + if (r < 0) + return r; + if (r == 0) { /* We reached the end. */ + if (append_trail_slash) + if (!strextend(&done, "/")) + return -ENOMEM; + break; + } + + first = strndup(e, r); + if (!first) + return -ENOMEM; + + /* Two dots? Then chop off the last bit of what we already found out. */ + if (path_equal(first, "..")) { + _cleanup_free_ char *parent = NULL; + _cleanup_close_ int fd_parent = -1; + + /* If we already are at the top, then going up will not change anything. This is in-line with + * how the kernel handles this. */ + if (empty_or_root(done)) + continue; + + parent = dirname_malloc(done); + if (!parent) + return -ENOMEM; + + /* Don't allow this to leave the root dir. */ + if (root && + path_startswith(done, root) && + !path_startswith(parent, root)) + continue; + + free_and_replace(done, parent); + + if (flags & CHASE_STEP) + goto chased_one; + + fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd_parent < 0) + return -errno; + + if (flags & CHASE_SAFE) { + if (fstat(fd_parent, &st) < 0) + return -errno; + + if (unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(fd, fd_parent, path, flags); + + previous_stat = st; + } + + safe_close(fd); + fd = TAKE_FD(fd_parent); + + continue; + } + + /* Otherwise let's see what this is. */ + child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (child < 0) { + if (errno == ENOENT && + (flags & CHASE_NONEXISTENT) && + (isempty(todo) || path_is_safe(todo))) { + /* If CHASE_NONEXISTENT is set, and the path does not exist, then + * that's OK, return what we got so far. But don't allow this if the + * remaining path contains "../" or something else weird. */ + + if (!path_extend(&done, first, todo)) + return -ENOMEM; + + exists = false; + break; + } + + return -errno; + } + + if (fstat(child, &st) < 0) + return -errno; + if ((flags & CHASE_SAFE) && + unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(fd, child, path, flags); + + previous_stat = st; + + if ((flags & CHASE_NO_AUTOFS) && + fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) + return log_autofs_mount_point(child, path, flags); + + if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { + _cleanup_free_ char *destination = NULL; + + /* This is a symlink, in this case read the destination. But let's make sure we + * don't follow symlinks without bounds. */ + if (--max_follow <= 0) + return -ELOOP; + + r = readlinkat_malloc(fd, first, &destination); + if (r < 0) + return r; + if (isempty(destination)) + return -EINVAL; + + if (path_is_absolute(destination)) { + + /* An absolute destination. Start the loop from the beginning, but use the root + * directory as base. */ + + safe_close(fd); + fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); + if (fd < 0) + return -errno; + + if (flags & CHASE_SAFE) { + if (fstat(fd, &st) < 0) + return -errno; + + if (unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(child, fd, path, flags); + + previous_stat = st; + } + + /* Note that we do not revalidate the root, we take it as is. */ + r = free_and_strdup(&done, empty_to_root(root)); + if (r < 0) + return r; + } + + /* Prefix what's left to do with what we just read, and start the loop again, but + * remain in the current directory. */ + if (!path_extend(&destination, todo)) + return -ENOMEM; + + free_and_replace(buffer, destination); + todo = buffer; + + if (flags & CHASE_STEP) + goto chased_one; + + continue; + } + + /* If this is not a symlink, then let's just add the name we read to what we already verified. */ + if (!path_extend(&done, first)) + return -ENOMEM; + + /* And iterate again, but go one directory further down. */ + safe_close(fd); + fd = TAKE_FD(child); + } + + if (ret_path) + *ret_path = TAKE_PTR(done); + + if (ret_fd) { + /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a + * proper fd by opening /proc/self/fd/xyz. */ + + assert(fd >= 0); + *ret_fd = TAKE_FD(fd); + } + + if (flags & CHASE_STEP) + return 1; + + return exists; + +chased_one: + if (ret_path) { + const char *e; + + /* todo may contain slashes at the beginning. */ + r = path_find_first_component(&todo, true, &e); + if (r < 0) + return r; + if (r == 0) + *ret_path = TAKE_PTR(done); + else { + char *c; + + c = path_join(done, e); + if (!c) + return -ENOMEM; + + *ret_path = c; + } + } + + return 0; +} + +int chase_symlinks_and_open( + const char *path, + const char *root, + unsigned chase_flags, + int open_flags, + char **ret_path) { + + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + int r; + + if (chase_flags & CHASE_NONEXISTENT) + return -EINVAL; + + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { + /* Shortcut this call if none of the special features of this call are requested */ + r = open(path, open_flags); + if (r < 0) + return -errno; + + return r; + } + + r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); + if (r < 0) + return r; + assert(path_fd >= 0); + + r = fd_reopen(path_fd, open_flags); + if (r < 0) + return r; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + return r; +} + +int chase_symlinks_and_opendir( + const char *path, + const char *root, + unsigned chase_flags, + char **ret_path, + DIR **ret_dir) { + + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + DIR *d; + int r; + + if (!ret_dir) + return -EINVAL; + if (chase_flags & CHASE_NONEXISTENT) + return -EINVAL; + + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { + /* Shortcut this call if none of the special features of this call are requested */ + d = opendir(path); + if (!d) + return -errno; + + *ret_dir = d; + return 0; + } + + r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); + if (r < 0) + return r; + assert(path_fd >= 0); + + d = opendir(FORMAT_PROC_FD_PATH(path_fd)); + if (!d) + return -errno; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + *ret_dir = d; + return 0; +} + +int chase_symlinks_and_stat( + const char *path, + const char *root, + unsigned chase_flags, + char **ret_path, + struct stat *ret_stat, + int *ret_fd) { + + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + int r; + + assert(path); + assert(ret_stat); + + if (chase_flags & CHASE_NONEXISTENT) + return -EINVAL; + + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { + /* Shortcut this call if none of the special features of this call are requested */ + if (stat(path, ret_stat) < 0) + return -errno; + + return 1; + } + + r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); + if (r < 0) + return r; + assert(path_fd >= 0); + + if (fstat(path_fd, ret_stat) < 0) + return -errno; + + if (ret_path) + *ret_path = TAKE_PTR(p); + if (ret_fd) + *ret_fd = TAKE_FD(path_fd); + + return 1; +} diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h new file mode 100644 index 0000000000..7191a45768 --- /dev/null +++ b/src/basic/chase-symlinks.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include + +#include "stat-util.h" + +enum { + CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */ + CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */ + CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */ + CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */ + CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ + CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ + CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's + * right-most component refers to symlink, return O_PATH fd of the symlink. */ + CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */ +}; + +bool unsafe_transition(const struct stat *a, const struct stat *b); + +/* How many iterations to execute before returning -ELOOP */ +#define CHASE_SYMLINKS_MAX 32 + +int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd); + +int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); +int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); +int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index a268a41add..4911ac7c6d 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -13,6 +13,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index a83f35a4c9..847ebd1841 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -12,7 +12,6 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "glyph-util.h" #include "log.h" #include "macro.h" #include "missing_fcntl.h" @@ -682,510 +681,6 @@ int unlink_or_warn(const char *filename) { return 0; } -bool unsafe_transition(const struct stat *a, const struct stat *b) { - /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to - * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files - * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ - - if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ - return false; - - return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */ -} - -static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { - _cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL; - struct stat st; - - if (!FLAGS_SET(flags, CHASE_WARN)) - return -ENOLINK; - - (void) fd_get_path(a, &n1); - (void) fd_get_path(b, &n2); - - if (fstat(a, &st) == 0) - user_a = uid_to_name(st.st_uid); - if (fstat(b, &st) == 0) - user_b = uid_to_name(st.st_uid); - - return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), - "Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.", - strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path); -} - -static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { - _cleanup_free_ char *n1 = NULL; - - if (!FLAGS_SET(flags, CHASE_WARN)) - return -EREMOTE; - - (void) fd_get_path(fd, &n1); - - return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), - "Detected autofs mount point %s during canonicalization of %s.", - strna(n1), path); -} - -int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) { - _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; - _cleanup_close_ int fd = -1; - unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ - bool exists = true, append_trail_slash = false; - struct stat previous_stat; - const char *todo; - int r; - - assert(path); - - /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ - if ((flags & CHASE_NONEXISTENT) && ret_fd) - return -EINVAL; - - if ((flags & CHASE_STEP) && ret_fd) - return -EINVAL; - - if (isempty(path)) - return -EINVAL; - - /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following - * symlinks relative to a root directory, instead of the root of the host. - * - * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following - * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is - * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first - * prefixed accordingly. - * - * Algorithmically this operates on two path buffers: "done" are the components of the path we already - * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to - * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning - * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no - * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races - * to a minimum. - * - * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got - * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this - * function what to do when encountering a symlink with an absolute path as directory: prefix it by the - * specified path. - * - * There are five ways to invoke this function: - * - * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is - * returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0 - * is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is - * returned if the destination was found, -ENOENT if it wasn't. - * - * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file - * descriptor is returned as return value. This is useful to open files relative to some root - * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using - * fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with - * CHASE_NONEXISTENT. - * - * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first - * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if - * a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the - * path is fully normalized, and == 0 for each normalization step. This may be combined with - * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. - * - * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from - * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If - * CHASE_WARN is also set, a warning describing the unsafe transition is emitted. - * - * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization - * is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of - * the mount point is emitted. - */ - - /* A root directory of "/" or "" is identical to none */ - if (empty_or_root(original_root)) - original_root = NULL; - - if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) { - /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set - * and doesn't care about any of the other special features we provide either. */ - r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); - if (r < 0) - return -errno; - - *ret_fd = r; - return 0; - } - - if (original_root) { - r = path_make_absolute_cwd(original_root, &root); - if (r < 0) - return r; - - /* Simplify the root directory, so that it has no duplicate slashes and nothing at the - * end. While we won't resolve the root path we still simplify it. Note that dropping the - * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY - * anyway. Moreover at the end of this function after processing everything we'll always turn - * the empty string back to "/". */ - delete_trailing_chars(root, "/"); - path_simplify(root); - - if (flags & CHASE_PREFIX_ROOT) { - /* We don't support relative paths in combination with a root directory */ - if (!path_is_absolute(path)) - return -EINVAL; - - path = prefix_roota(root, path); - } - } - - r = path_make_absolute_cwd(path, &buffer); - if (r < 0) - return r; - - fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) - if (fstat(fd, &previous_stat) < 0) - return -errno; - - if (flags & CHASE_TRAIL_SLASH) - append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/."); - - if (root) { - /* If we are operating on a root directory, let's take the root directory as it is. */ - - todo = path_startswith(buffer, root); - if (!todo) - return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, - SYNTHETIC_ERRNO(ECHRNG), - "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", - path, root); - - done = strdup(root); - } else { - todo = buffer; - done = strdup("/"); - } - - for (;;) { - _cleanup_free_ char *first = NULL; - _cleanup_close_ int child = -1; - struct stat st; - const char *e; - - r = path_find_first_component(&todo, true, &e); - if (r < 0) - return r; - if (r == 0) { /* We reached the end. */ - if (append_trail_slash) - if (!strextend(&done, "/")) - return -ENOMEM; - break; - } - - first = strndup(e, r); - if (!first) - return -ENOMEM; - - /* Two dots? Then chop off the last bit of what we already found out. */ - if (path_equal(first, "..")) { - _cleanup_free_ char *parent = NULL; - _cleanup_close_ int fd_parent = -1; - - /* If we already are at the top, then going up will not change anything. This is in-line with - * how the kernel handles this. */ - if (empty_or_root(done)) - continue; - - parent = dirname_malloc(done); - if (!parent) - return -ENOMEM; - - /* Don't allow this to leave the root dir. */ - if (root && - path_startswith(done, root) && - !path_startswith(parent, root)) - continue; - - free_and_replace(done, parent); - - if (flags & CHASE_STEP) - goto chased_one; - - fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd_parent < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd_parent, &st) < 0) - return -errno; - - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(fd, fd_parent, path, flags); - - previous_stat = st; - } - - safe_close(fd); - fd = TAKE_FD(fd_parent); - - continue; - } - - /* Otherwise let's see what this is. */ - child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (child < 0) { - if (errno == ENOENT && - (flags & CHASE_NONEXISTENT) && - (isempty(todo) || path_is_safe(todo))) { - /* If CHASE_NONEXISTENT is set, and the path does not exist, then - * that's OK, return what we got so far. But don't allow this if the - * remaining path contains "../" or something else weird. */ - - if (!path_extend(&done, first, todo)) - return -ENOMEM; - - exists = false; - break; - } - - return -errno; - } - - if (fstat(child, &st) < 0) - return -errno; - if ((flags & CHASE_SAFE) && - unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(fd, child, path, flags); - - previous_stat = st; - - if ((flags & CHASE_NO_AUTOFS) && - fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) - return log_autofs_mount_point(child, path, flags); - - if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { - _cleanup_free_ char *destination = NULL; - - /* This is a symlink, in this case read the destination. But let's make sure we - * don't follow symlinks without bounds. */ - if (--max_follow <= 0) - return -ELOOP; - - r = readlinkat_malloc(fd, first, &destination); - if (r < 0) - return r; - if (isempty(destination)) - return -EINVAL; - - if (path_is_absolute(destination)) { - - /* An absolute destination. Start the loop from the beginning, but use the root - * directory as base. */ - - safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &st) < 0) - return -errno; - - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(child, fd, path, flags); - - previous_stat = st; - } - - /* Note that we do not revalidate the root, we take it as is. */ - r = free_and_strdup(&done, empty_to_root(root)); - if (r < 0) - return r; - } - - /* Prefix what's left to do with what we just read, and start the loop again, but - * remain in the current directory. */ - if (!path_extend(&destination, todo)) - return -ENOMEM; - - free_and_replace(buffer, destination); - todo = buffer; - - if (flags & CHASE_STEP) - goto chased_one; - - continue; - } - - /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!path_extend(&done, first)) - return -ENOMEM; - - /* And iterate again, but go one directory further down. */ - safe_close(fd); - fd = TAKE_FD(child); - } - - if (ret_path) - *ret_path = TAKE_PTR(done); - - if (ret_fd) { - /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a - * proper fd by opening /proc/self/fd/xyz. */ - - assert(fd >= 0); - *ret_fd = TAKE_FD(fd); - } - - if (flags & CHASE_STEP) - return 1; - - return exists; - -chased_one: - if (ret_path) { - const char *e; - - /* todo may contain slashes at the beginning. */ - r = path_find_first_component(&todo, true, &e); - if (r < 0) - return r; - if (r == 0) - *ret_path = TAKE_PTR(done); - else { - char *c; - - c = path_join(done, e); - if (!c) - return -ENOMEM; - - *ret_path = c; - } - } - - return 0; -} - -int chase_symlinks_and_open( - const char *path, - const char *root, - unsigned chase_flags, - int open_flags, - char **ret_path) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - r = open(path, open_flags); - if (r < 0) - return -errno; - - return r; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - r = fd_reopen(path_fd, open_flags); - if (r < 0) - return r; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - return r; -} - -int chase_symlinks_and_opendir( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - DIR **ret_dir) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - DIR *d; - int r; - - if (!ret_dir) - return -EINVAL; - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - d = opendir(path); - if (!d) - return -errno; - - *ret_dir = d; - return 0; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - d = opendir(FORMAT_PROC_FD_PATH(path_fd)); - if (!d) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - *ret_dir = d; - return 0; -} - -int chase_symlinks_and_stat( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - struct stat *ret_stat, - int *ret_fd) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - assert(path); - assert(ret_stat); - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - if (stat(path, ret_stat) < 0) - return -errno; - - return 1; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - if (fstat(path_fd, ret_stat) < 0) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - if (ret_fd) - *ret_fd = TAKE_FD(path_fd); - - return 1; -} - int access_fd(int fd, int mode) { /* Like access() but operates on an already open fd */ diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 61a6a81bf3..f8a7657a07 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -66,29 +66,6 @@ int var_tmp_dir(const char **ret); int unlink_or_warn(const char *filename); -enum { - CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */ - CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */ - CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ - CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's - * right-most component refers to symlink, return O_PATH fd of the symlink. */ - CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */ -}; - -bool unsafe_transition(const struct stat *a, const struct stat *b); - -/* How many iterations to execute before returning -ELOOP */ -#define CHASE_SYMLINKS_MAX 32 - -int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd); - -int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); -int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); -int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); - /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ static inline char *rmdir_and_free(char *p) { PROTECT_ERRNO; diff --git a/src/basic/meson.build b/src/basic/meson.build index 0d8100a391..ade45bdfc2 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -25,6 +25,8 @@ basic_sources = files(''' capability-util.h cgroup-util.c cgroup-util.h + chase-symlinks.c + chase-symlinks.h chattr-util.c chattr-util.h conf-files.c diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 78f540809f..60d08a9493 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -5,6 +5,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "fd-util.h" #include "format-util.h" #include "fs-util.h" diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index e7a5a99551..f07534267f 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -5,6 +5,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 1d69ecc586..d161f8d33c 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "chase-symlinks.h" #include "dirent-util.h" #include "env-file.h" #include "env-util.h" diff --git a/src/basic/path-util.c b/src/basic/path-util.c index eee07b6ea5..4c952d863c 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -13,6 +13,7 @@ #undef basename #include "alloc-util.h" +#include "chase-symlinks.h" #include "extract-word.h" #include "fd-util.h" #include "fs-util.h" diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 72a7e4a48b..45864e9e62 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -8,10 +8,11 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "dirent-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "macro.h" #include "missing_fs.h" #include "missing_magic.h" diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c index d1e997ec9f..00be0601dc 100644 --- a/src/basic/unit-file.c +++ b/src/basic/unit-file.c @@ -2,6 +2,7 @@ #include "sd-id128.h" +#include "chase-symlinks.h" #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 99b75598b5..5a294f07b3 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -11,6 +11,7 @@ #include "bus-common-errors.h" #include "bus-get-properties.h" #include "bus-log-control-api.h" +#include "chase-symlinks.h" #include "data-fd-util.h" #include "dbus-cgroup.h" #include "dbus-execute.h" @@ -24,7 +25,6 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" -#include "fs-util.h" #include "install.h" #include "log.h" #include "manager-dump.h" diff --git a/src/core/execute.c b/src/core/execute.c index e7034ce696..d68e31eb7d 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -44,6 +44,7 @@ #include "cap-list.h" #include "capability-util.h" #include "cgroup-setup.h" +#include "chase-symlinks.h" #include "chown-recursive.h" #include "cpu-set-util.h" #include "creds-util.h" @@ -58,7 +59,6 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" -#include "fs-util.h" #include "glob-util.h" #include "hexdecoct.h" #include "io-util.h" diff --git a/src/core/namespace.c b/src/core/namespace.c index 038da6c554..48f11f9906 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -10,13 +10,13 @@ #include "alloc-util.h" #include "base-filesystem.h" +#include "chase-symlinks.h" #include "dev-setup.h" #include "env-util.h" #include "escape.h" #include "extension-release.h" #include "fd-util.h" #include "format-util.h" -#include "fs-util.h" #include "label.h" #include "list.h" #include "loop-util.h" diff --git a/src/core/service.c b/src/core/service.c index 9299813d45..f331c45de0 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -12,6 +12,7 @@ #include "bus-error.h" #include "bus-kernel.h" #include "bus-util.h" +#include "chase-symlinks.h" #include "dbus-service.h" #include "dbus-unit.h" #include "def.h" @@ -21,7 +22,6 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" -#include "fs-util.h" #include "load-dropin.h" #include "load-fragment.h" #include "log.h" diff --git a/src/core/socket.c b/src/core/socket.c index 177068eed4..81178e3de2 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -14,6 +14,7 @@ #include "bpf-firewall.h" #include "bus-error.h" #include "bus-util.h" +#include "chase-symlinks.h" #include "copy.h" #include "dbus-socket.h" #include "dbus-unit.h" @@ -22,7 +23,6 @@ #include "exit-status.h" #include "fd-util.h" #include "format-util.h" -#include "fs-util.h" #include "in-addr-util.h" #include "io-util.h" #include "ip-protocol-list.h" diff --git a/src/core/unit.c b/src/core/unit.c index 304f67dbf1..05341a64f5 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -17,6 +17,7 @@ #include "bus-util.h" #include "cgroup-setup.h" #include "cgroup-util.h" +#include "chase-symlinks.h" #include "core-varlink.h" #include "dbus-unit.h" #include "dbus.h" diff --git a/src/delta/delta.c b/src/delta/delta.c index 51fe4b4bed..ad6b9a195a 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -6,6 +6,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index c145c27611..03c1e89cc5 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -8,6 +8,7 @@ #include #include "architecture.h" +#include "chase-symlinks.h" #include "copy.h" #include "dissect-image.h" #include "fd-util.h" diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 1727649ae1..d28a416e5d 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "ask-password-api.h" +#include "chase-symlinks.h" #include "copy.h" #include "creds-util.h" #include "dissect-image.h" diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 8a4340c5ba..1e6ad432dc 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -5,9 +5,9 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "fstab-util.h" #include "generator.h" #include "log.h" diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 73e4fafdff..0eb27b88b2 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -27,6 +27,7 @@ #include "bus-error.h" #include "bus-util.h" #include "catalog.h" +#include "chase-symlinks.h" #include "chattr-util.h" #include "def.h" #include "dissect-image.h" diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 7e11ea2d39..1c0dce7b07 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -8,6 +8,7 @@ #include "sd-device.h" #include "alloc-util.h" +#include "chase-symlinks.h" #include "device-internal.h" #include "device-private.h" #include "device-util.h" diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index 8b1cb247e9..d8034202dc 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -9,6 +9,7 @@ #include "bus-locator.h" #include "bus-unit-util.h" #include "bus-wait-for-jobs.h" +#include "chase-symlinks.h" #include "device-util.h" #include "dirent-util.h" #include "escape.h" diff --git a/src/nspawn/nspawn-bind-user.c b/src/nspawn/nspawn-bind-user.c index 3250cd9768..0220d71c2b 100644 --- a/src/nspawn/nspawn-bind-user.c +++ b/src/nspawn/nspawn-bind-user.c @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "chase-symlinks.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" -#include "fs-util.h" #include "nspawn-bind-user.h" #include "nspawn.h" #include "path-util.h" diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index c59151685c..035fe287ee 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -4,6 +4,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "escape.h" #include "fd-util.h" #include "format-util.h" diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 3694841ba8..7b767fb296 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -33,6 +33,7 @@ #include "cap-list.h" #include "capability-util.h" #include "cgroup-util.h" +#include "chase-symlinks.h" #include "copy.h" #include "cpu-set-util.h" #include "creds-util.h" diff --git a/src/partition/repart.c b/src/partition/repart.c index 5422d5e1e5..e4ea0f04b2 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -22,6 +22,7 @@ #include "blkid-util.h" #include "blockdev-util.h" #include "btrfs-util.h" +#include "chase-symlinks.h" #include "conf-files.h" #include "conf-parser.h" #include "cryptsetup-util.h" diff --git a/src/portable/portable.c b/src/portable/portable.c index 4daafea3d3..48d99c0ca2 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -4,6 +4,7 @@ #include "bus-common-errors.h" #include "bus-error.h" +#include "chase-symlinks.h" #include "conf-files.h" #include "copy.h" #include "data-fd-util.h" diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index c2d79ec3a2..ec625161a4 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -10,6 +10,7 @@ #include "bus-locator.h" #include "bus-unit-util.h" #include "bus-wait-for-jobs.h" +#include "chase-symlinks.h" #include "def.h" #include "dirent-util.h" #include "env-file.h" @@ -24,8 +25,8 @@ #include "parse-argument.h" #include "parse-util.h" #include "path-util.h" -#include "pretty-print.h" #include "portable.h" +#include "pretty-print.h" #include "spawn-polkit-agent.h" #include "string-util.h" #include "strv.h" diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index b96a082aa3..129db21eb0 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -14,6 +14,7 @@ #include "alloc-util.h" #include "btrfs-util.h" +#include "chase-symlinks.h" #include "chattr-util.h" #include "copy.h" #include "dirent-util.h" diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index de75340c9d..ccbe1aced4 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -24,6 +24,7 @@ #include "ask-password-api.h" #include "blkid-util.h" #include "blockdev-util.h" +#include "chase-symlinks.h" #include "conf-files.h" #include "copy.h" #include "cryptsetup-util.h" diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 89f4b8ad89..eb016eb114 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -6,13 +6,13 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "conf-files.h" #include "dirent-util.h" #include "dropin.h" #include "escape.h" #include "fd-util.h" #include "fileio-label.h" -#include "fs-util.h" #include "hashmap.h" #include "log.h" #include "macro.h" diff --git a/src/shared/install.c b/src/shared/install.c index a348f0c572..268cbd9602 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -10,6 +10,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "conf-files.h" #include "conf-parser.h" #include "def.h" diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 9a3f33915e..e6b6a7e89a 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -10,6 +10,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "dissect-image.h" #include "exec-util.h" #include "extract-word.h" diff --git a/src/shared/specifier.c b/src/shared/specifier.c index cb4d6daf6a..1fd76b1d15 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "architecture.h" +#include "chase-symlinks.h" #include "fd-util.h" #include "format-util.h" #include "fs-util.h" diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index e0dd17a2f8..7edb9d7ff2 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -9,8 +9,8 @@ #include #include "base-filesystem.h" +#include "chase-symlinks.h" #include "fd-util.h" -#include "fs-util.h" #include "log.h" #include "missing_syscall.h" #include "mkdir.h" diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 4c54303d13..f34a48b656 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -3,6 +3,7 @@ #include #include "cgroup-util.h" +#include "chase-symlinks.h" #include "dns-domain.h" #include "env-util.h" #include "fd-util.h" diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 67112c4e14..509cb9bf31 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -6,6 +6,7 @@ #include #include "capability-util.h" +#include "chase-symlinks.h" #include "discover-image.h" #include "dissect-image.h" #include "env-util.h" diff --git a/src/systemctl/systemctl-util.c b/src/systemctl/systemctl-util.c index 9f3174d106..d0c514e467 100644 --- a/src/systemctl/systemctl-util.c +++ b/src/systemctl/systemctl-util.c @@ -10,6 +10,7 @@ #include "bus-locator.h" #include "bus-map-properties.h" #include "bus-unit-util.h" +#include "chase-symlinks.h" #include "dropin.h" #include "env-util.h" #include "exit-status.h" diff --git a/src/test/test-chase-symlinks.c b/src/test/test-chase-symlinks.c index 635c44675b..5f53eba101 100644 --- a/src/test/test-chase-symlinks.c +++ b/src/test/test-chase-symlinks.c @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#include "chase-symlinks.h" #include "fd-util.h" -#include "fs-util.h" #include "log.h" #include "main-func.h" diff --git a/src/test/test-copy.c b/src/test/test-copy.c index 7b8f8fa1b2..362f210e6a 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -4,6 +4,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "copy.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index f0cac447c3..cd024ef7f1 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -3,6 +3,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "copy.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/tmpfiles/offline-passwd.c b/src/tmpfiles/offline-passwd.c index b607aacf57..8ba3fea984 100644 --- a/src/tmpfiles/offline-passwd.c +++ b/src/tmpfiles/offline-passwd.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "chase-symlinks.h" #include "fd-util.h" -#include "fs-util.h" #include "offline-passwd.h" #include "path-util.h" #include "user-util.h" diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 8a02e6c9dd..6b2f75e248 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -21,6 +21,7 @@ #include "alloc-util.h" #include "btrfs-util.h" #include "capability-util.h" +#include "chase-symlinks.h" #include "chattr-util.h" #include "conf-files.h" #include "copy.h" diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 5a7c94207b..db155421b2 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -23,11 +23,11 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "device-util.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "netif-naming-scheme.h" #include "parse-util.h" #include "proc-cmdline.h" diff --git a/src/volatile-root/volatile-root.c b/src/volatile-root/volatile-root.c index 1a6593f9c4..90065b410b 100644 --- a/src/volatile-root/volatile-root.c +++ b/src/volatile-root/volatile-root.c @@ -4,8 +4,8 @@ #include "alloc-util.h" #include "blockdev-util.h" +#include "chase-symlinks.h" #include "escape.h" -#include "fs-util.h" #include "main-func.h" #include "mkdir.h" #include "mount-util.h" From 01bebba3de3aaf884a5537ea134b490cd3b330cb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 15:23:29 +0200 Subject: [PATCH 5/5] =?UTF-8?q?basic:=20move=20chase=5Fsymlinks=5Fand=5Ffo?= =?UTF-8?q?pen=5Funlocked()=20=E2=86=92=20chase-symlinks.[ch]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/basic/chase-symlinks.c | 35 +++++++++++++++++++++++++++++++ src/basic/chase-symlinks.h | 2 ++ src/basic/fileio.c | 39 ++++------------------------------- src/basic/fileio.h | 10 ++------- src/nspawn/nspawn-bind-user.c | 4 ++-- src/shared/user-record.c | 2 +- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c index dd3a217203..344e7e1bb7 100644 --- a/src/basic/chase-symlinks.c +++ b/src/basic/chase-symlinks.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "chase-symlinks.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "glyph-util.h" #include "log.h" @@ -515,3 +516,37 @@ int chase_symlinks_and_stat( return 1; } + +int chase_symlinks_and_fopen_unlocked( + const char *path, + const char *root, + unsigned chase_flags, + const char *open_flags, + char **ret_path, + FILE **ret_file) { + + _cleanup_free_ char *final_path = NULL; + _cleanup_close_ int fd = -1; + int mode_flags, r; + + assert(path); + assert(open_flags); + assert(ret_file); + + mode_flags = fopen_mode_to_flags(open_flags); + if (mode_flags < 0) + return mode_flags; + + fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL); + if (fd < 0) + return fd; + + r = take_fdopen_unlocked(&fd, open_flags, ret_file); + if (r < 0) + return r; + + if (ret_path) + *ret_path = TAKE_PTR(final_path); + + return 0; +} diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h index 7191a45768..e2b22fe041 100644 --- a/src/basic/chase-symlinks.h +++ b/src/basic/chase-symlinks.h @@ -28,3 +28,5 @@ int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flag int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); + +int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, unsigned chase_flags, const char *open_flags, char **ret_path, FILE **ret_file); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 4911ac7c6d..cced1dd564 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -941,10 +941,12 @@ DIR *xopendirat(int fd, const char *name, int flags) { return d; } -static int mode_to_flags(const char *mode) { +int fopen_mode_to_flags(const char *mode) { const char *p; int flags; + assert(mode); + if ((p = startswith(mode, "r+"))) flags = O_RDWR; else if ((p = startswith(mode, "r"))) @@ -997,7 +999,7 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r } else { int fd, mode_flags; - mode_flags = mode_to_flags(mode); + mode_flags = fopen_mode_to_flags(mode); if (mode_flags < 0) return mode_flags; @@ -1137,39 +1139,6 @@ int search_and_fopen_nulstr( return search_and_fopen_internal(filename, mode, root, s, ret, ret_path); } -int chase_symlinks_and_fopen_unlocked( - const char *path, - const char *root, - unsigned chase_flags, - const char *open_flags, - FILE **ret_file, - char **ret_path) { - - _cleanup_close_ int fd = -1; - _cleanup_free_ char *final_path = NULL; - int mode_flags, r; - - assert(path); - assert(open_flags); - assert(ret_file); - - mode_flags = mode_to_flags(open_flags); - if (mode_flags < 0) - return mode_flags; - - fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL); - if (fd < 0) - return fd; - - r = take_fdopen_unlocked(&fd, open_flags, ret_file); - if (r < 0) - return r; - - if (ret_path) - *ret_path = TAKE_PTR(final_path); - return 0; -} - int fflush_and_check(FILE *f) { assert(f); diff --git a/src/basic/fileio.h b/src/basic/fileio.h index c25a638104..899def946b 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -91,14 +91,6 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path); -int chase_symlinks_and_fopen_unlocked( - const char *path, - const char *root, - unsigned chase_flags, - const char *open_flags, - FILE **ret_file, - char **ret_path); - int fflush_and_check(FILE *f); int fflush_sync_and_check(FILE *f); @@ -126,3 +118,5 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) { int safe_fgetc(FILE *f, char *ret); int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); + +int fopen_mode_to_flags(const char *mode); diff --git a/src/nspawn/nspawn-bind-user.c b/src/nspawn/nspawn-bind-user.c index 0220d71c2b..a5d866da95 100644 --- a/src/nspawn/nspawn-bind-user.c +++ b/src/nspawn/nspawn-bind-user.c @@ -24,7 +24,7 @@ static int check_etc_passwd_collisions( assert(directory); assert(name || uid_is_valid(uid)); - r = chase_symlinks_and_fopen_unlocked("/etc/passwd", directory, CHASE_PREFIX_ROOT, "re", &f, NULL); + r = chase_symlinks_and_fopen_unlocked("/etc/passwd", directory, CHASE_PREFIX_ROOT, "re", NULL, &f); if (r == -ENOENT) return 0; /* no user database? then no user, hence no collision */ if (r < 0) @@ -57,7 +57,7 @@ static int check_etc_group_collisions( assert(directory); assert(name || gid_is_valid(gid)); - r = chase_symlinks_and_fopen_unlocked("/etc/group", directory, CHASE_PREFIX_ROOT, "re", &f, NULL); + r = chase_symlinks_and_fopen_unlocked("/etc/group", directory, CHASE_PREFIX_ROOT, "re", NULL, &f); if (r == -ENOENT) return 0; /* no group database? then no group, hence no collision */ if (r < 0) diff --git a/src/shared/user-record.c b/src/shared/user-record.c index f34a48b656..fd6b37b908 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -55,7 +55,7 @@ int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char if (!path) path = "/etc/login.defs"; - r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &f, NULL); + r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", NULL, &f); if (r == -ENOENT) goto assign; if (r < 0)