chiark / gitweb /
fs-util: add calls that combine chase_symlinks() and open()/opendir() in one
authorLennart Poettering <lennart@poettering.net>
Mon, 26 Mar 2018 12:15:43 +0000 (14:15 +0200)
committerSven Eden <yamakuzure@gmx.net>
Fri, 24 Aug 2018 14:47:08 +0000 (16:47 +0200)
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
src/basic/fs-util.h

index 8617968c15f91f525655d3cccad9ff1affbac073..d43aca249776c87d105d4679c9ab247f9a4d9291 100644 (file)
@@ -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;
 }
index 97bcd1a13be3f645b98717cabd9edf9e4392825f..d04b467d57740b01026ec7e46394bfa0659eee89 100644 (file)
@@ -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;