diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 0726aac38c..8bb8c9a629 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -427,7 +427,6 @@ int move_fd(int from, int to, int cloexec) { int acquire_data_fd(const void *data, size_t size, unsigned flags) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_pair_ int pipefds[2] = { -1, -1 }; char pattern[] = "/dev/shm/data-fd-XXXXXX"; _cleanup_close_ int fd = -1; @@ -537,12 +536,7 @@ try_dev_shm: return -EIO; /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - r = open(procfs_path, O_RDONLY|O_CLOEXEC); - if (r < 0) - return -errno; - - return r; + return fd_reopen(fd, O_RDONLY|O_CLOEXEC); } try_dev_shm_without_o_tmpfile: @@ -725,3 +719,22 @@ finish: return r; } + +int fd_reopen(int fd, int flags) { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + int new_fd; + + /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to + * turn O_RDWR fds into O_RDONLY fds. + * + * This doesn't work on sockets (since they cannot be open()ed, ever). + * + * This implicitly resets the file read index to 0. */ + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + new_fd = open(procfs_path, flags); + if (new_fd < 0) + return -errno; + + return new_fd; +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 007580b48f..163b096b1a 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -113,3 +113,5 @@ static inline int make_null_stdio(void) { (fd) = -1; \ _fd_; \ }) + +int fd_reopen(int fd, int flags); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 0531504b58..112aedd027 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -577,6 +577,10 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } +static bool noop_root(const char *root) { + return isempty(root) || path_equal(root, "/"); +} + static bool safe_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 @@ -627,9 +631,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * specified path. */ /* A root directory of "/" or "" is identical to none */ - if (isempty(original_root) || path_equal(original_root, "/")) + if (noop_root(original_root)) original_root = NULL; + if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN)) == CHASE_OPEN) { + /* Shortcut the CHASE_OPEN 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); + if (r < 0) + return -errno; + + return r; + } + if (original_root) { r = path_make_absolute_cwd(original_root, &root); if (r < 0) @@ -874,6 +888,86 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return exists; } +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 (noop_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; + } + + path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); + if (path_fd < 0) + return path_fd; + + 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) { + + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + DIR *d; + + if (!ret_dir) + return -EINVAL; + if (chase_flags & CHASE_NONEXISTENT) + return -EINVAL; + + if (noop_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; + } + + path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); + if (path_fd < 0) + return path_fd; + + xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); + d = opendir(procfs_path); + if (!d) + return -errno; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + *ret_dir = d; + return 0; +} + int access_fd(int fd, int mode) { char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; int r; @@ -881,10 +975,9 @@ int access_fd(int fd, int mode) { /* Like access() but operates on an already open fd */ xsprintf(p, "/proc/self/fd/%i", fd); - r = access(p, mode); if (r < 0) - r = -errno; + return -errno; return r; } diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 5298a41f0e..2225b7e74b 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -20,6 +20,7 @@ along with systemd; If not, see . ***/ +#include #include #include #include @@ -91,6 +92,9 @@ enum { int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); +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); + /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ static inline void rmdir_and_free(char *p) { PROTECT_ERRNO; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 23e05daf48..8e1e16d73b 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -460,7 +460,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons return bus_append_safe_atou64(m, field, eq); return bus_append_parse_size(m, field, eq, 1024); - } if (streq(field, "CPUQuota")) { diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 0f8d89ffb6..95a98341ff 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1299,18 +1299,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) { fds[2*k] = safe_close(fds[2*k]); NULSTR_FOREACH(p, paths[k]) { - _cleanup_free_ char *q = NULL; - - r = chase_symlinks(p, t, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &q); - if (r < 0) - continue; - - fd = open(q, O_RDONLY|O_CLOEXEC|O_NOCTTY); + fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); if (fd >= 0) break; } - if (fd < 0) + if (fd < 0) { + log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]); continue; + } r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0); if (r < 0) diff --git a/src/shared/install.c b/src/shared/install.c index 7506018d41..477e15a4da 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -59,8 +59,9 @@ #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 typedef enum SearchFlags { - SEARCH_LOAD = 1, - SEARCH_FOLLOW_CONFIG_SYMLINKS = 2, + SEARCH_LOAD = 1U << 0, + SEARCH_FOLLOW_CONFIG_SYMLINKS = 1U << 1, + SEARCH_DROPIN = 1U << 2, } SearchFlags; typedef struct { @@ -1228,6 +1229,7 @@ static int unit_file_load( InstallContext *c, UnitFileInstallInfo *info, const char *path, + const char *root_dir, SearchFlags flags) { const ConfigTableItem items[] = { @@ -1248,40 +1250,57 @@ static int unit_file_load( assert(info); assert(path); - type = unit_name_to_type(info->name); - if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && - !unit_type_may_template(type)) - return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); + if (!(flags & SEARCH_DROPIN)) { + /* Loading or checking for the main unit file… */ - if (!(flags & SEARCH_LOAD)) { - r = lstat(path, &st); - if (r < 0) + type = unit_name_to_type(info->name); + if (type < 0) + return -EINVAL; + if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type)) { + log_error("Unit type %s cannot be templated.", unit_type_to_string(type)); + return -EINVAL; + } + + if (!(flags & SEARCH_LOAD)) { + r = lstat(path, &st); + if (r < 0) + return -errno; + + if (null_or_empty(&st)) + info->type = UNIT_FILE_TYPE_MASKED; + else if (S_ISREG(st.st_mode)) + info->type = UNIT_FILE_TYPE_REGULAR; + else if (S_ISLNK(st.st_mode)) + return -ELOOP; + else if (S_ISDIR(st.st_mode)) + return -EISDIR; + else + return -ENOTTY; + + return 0; + } + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) return -errno; + } else { + /* Operating on a drop-in file. If we aren't supposed to load the unit file drop-ins don't matter, let's hence shortcut this. */ - if (null_or_empty(&st)) - info->type = UNIT_FILE_TYPE_MASKED; - else if (S_ISREG(st.st_mode)) - info->type = UNIT_FILE_TYPE_REGULAR; - else if (S_ISLNK(st.st_mode)) - return -ELOOP; - else if (S_ISDIR(st.st_mode)) - return -EISDIR; - else - return -ENOTTY; + if (!(flags & SEARCH_LOAD)) + return 0; - return 0; + fd = chase_symlinks_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); + if (fd < 0) + return fd; } - /* c is only needed if we actually load the file */ - assert(c); - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; if (fstat(fd, &st) < 0) return -errno; + if (null_or_empty(&st)) { - info->type = UNIT_FILE_TYPE_MASKED; + if ((flags & SEARCH_DROPIN) == 0) + info->type = UNIT_FILE_TYPE_MASKED; + return 0; } @@ -1294,6 +1313,9 @@ static int unit_file_load( return -errno; fd = -1; + /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */ + assert(c); + r = config_parse(info->name, path, f, NULL, config_item_table_lookup, items, @@ -1301,7 +1323,8 @@ static int unit_file_load( if (r < 0) return log_debug_errno(r, "Failed to parse %s: %m", info->name); - info->type = UNIT_FILE_TYPE_REGULAR; + if ((flags & SEARCH_DROPIN) == 0) + info->type = UNIT_FILE_TYPE_REGULAR; return (int) strv_length(info->aliases) + @@ -1319,8 +1342,8 @@ static int unit_file_load_or_readlink( _cleanup_free_ char *target = NULL; int r; - r = unit_file_load(c, info, path, flags); - if (r != -ELOOP) + r = unit_file_load(c, info, path, root_dir, flags); + if (r != -ELOOP || (flags & SEARCH_DROPIN)) return r; /* This is a symlink, let's read it. */ @@ -1383,16 +1406,12 @@ static int unit_file_search( const LookupPaths *paths, SearchFlags flags) { + const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL; + _cleanup_strv_free_ char **dirs = NULL, **files = NULL; _cleanup_free_ char *template = NULL; - _cleanup_strv_free_ char **dirs = NULL; - _cleanup_strv_free_ char **files = NULL; - const char *dropin_dir_name = NULL; - const char *dropin_template_dir_name = NULL; - - char **p; - int r; - int result; bool found_unit = false; + int r, result; + char **p; assert(info); assert(paths); @@ -1420,7 +1439,6 @@ static int unit_file_search( return -ENOMEM; r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); - if (r >= 0) { info->path = TAKE_PTR(path); result = r; @@ -1499,7 +1517,7 @@ static int unit_file_search( return log_debug_errno(r, "Failed to get list of conf files: %m"); STRV_FOREACH(p, files) { - r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags); + r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags | SEARCH_DROPIN); if (r < 0) return log_debug_errno(r, "Failed to load conf file %s: %m", *p); } diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 9fe79502c8..7a7541f272 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -270,17 +270,13 @@ static void test_chase_symlinks(void) { pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL); if (pfd != -ENOENT) { - char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(pfd) + 1]; _cleanup_close_ int fd = -1; sd_id128_t a, b; assert_se(pfd >= 0); - xsprintf(procfs, "/proc/self/fd/%i", pfd); - - fd = open(procfs, O_RDONLY|O_CLOEXEC); + fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC); assert_se(fd >= 0); - safe_close(pfd); assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index d04f24065b..299012fc03 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1186,7 +1186,6 @@ static int parse_attribute_from_arg(Item *item) { } static int fd_set_attribute(Item *item, int fd, const struct stat *st) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int procfs_fd = -1; _cleanup_free_ char *path = NULL; unsigned f; @@ -1213,9 +1212,7 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) { if (!S_ISDIR(st->st_mode)) f &= ~FS_DIRSYNC_FL; - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - - procfs_fd = open(procfs_path, O_RDONLY|O_CLOEXEC|O_NOATIME); + procfs_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOATIME); if (procfs_fd < 0) return -errno;