From 21c692e9bf8c81437b9aa3a7d37c88d7fca0997d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Mar 2018 14:15:43 +0200 Subject: [PATCH] fs-util: add calls that combine chase_symlinks() and open()/opendir() in one This is useful when opening files within disk images, as we'll then take the relative root directory properly into account. --- src/basic/fs-util.c | 89 +++++++++++++++++++++++++++++++++++++++++++-- src/basic/fs-util.h | 4 ++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 0531504b58..ef9ce5e932 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,7 +631,7 @@ 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) { @@ -874,6 +878,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 +965,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;