summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/fd-util.c27
-rw-r--r--src/basic/fd-util.h2
-rw-r--r--src/basic/fs-util.c99
-rw-r--r--src/basic/fs-util.h4
-rw-r--r--src/shared/bus-unit-util.c1
-rw-r--r--src/shared/dissect-image.c12
-rw-r--r--src/shared/install.c98
-rw-r--r--src/test/test-fs-util.c6
-rw-r--r--src/tmpfiles/tmpfiles.c5
9 files changed, 186 insertions, 68 deletions
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 <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
@@ -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;