diff options
-rw-r--r-- | src/basic/fs-util.c | 121 | ||||
-rw-r--r-- | src/basic/fs-util.h | 4 |
2 files changed, 100 insertions, 25 deletions
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 8617968c1..d43aca249 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -469,10 +469,8 @@ int get_files_in_directory(const char *path, char ***list) { n++; } - if (list) { - *list = l; - l = NULL; /* avoid freeing */ - } + if (list) + *list = TAKE_PTR(l); return n; } @@ -594,6 +592,10 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { } #endif // 0 +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 @@ -644,7 +646,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) { @@ -749,8 +751,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, } safe_close(fd); - fd = fd_parent; - fd_parent = -1; + fd = TAKE_FD(fd_parent); continue; } @@ -855,10 +856,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, } /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!done) { - done = first; - first = NULL; - } else { + if (!done) + done = TAKE_PTR(first); + else { /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ if (streq(done, "/")) *done = '\0'; @@ -869,8 +869,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, /* And iterate again, but go one directory further down. */ safe_close(fd); - fd = child; - child = -1; + fd = TAKE_FD(child); } if (!done) { @@ -880,27 +879,100 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -ENOMEM; } - if (ret) { - *ret = done; - done = NULL; - } + if (ret) + *ret = TAKE_PTR(done); if (flags & CHASE_OPEN) { - int q; - /* 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); - q = fd; - fd = -1; - - return q; + return TAKE_FD(fd); } 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; @@ -908,10 +980,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 97bcd1a13..d04b467d5 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> @@ -107,6 +108,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; |