chiark / gitweb /
fs-util: add new CHASE_NON_EXISTING flag to chase_symlinks()
[elogind.git] / src / basic / fs-util.c
index e47b050b0dd95c0239d89a8fa39fadeca845cf94..be4c6a9faf41343f46a710ea827b33edfc18b0b1 100644 (file)
@@ -607,6 +607,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
         _cleanup_close_ int fd = -1;
         unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+        bool exists = true;
         char *todo;
         int r;
 
@@ -712,8 +713,25 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                 /* Otherwise let's see what this is. */
                 child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
-                if (child < 0)
+                if (child < 0) {
+
+                        if (errno == ENOENT &&
+                            (flags & CHASE_NON_EXISTING) &&
+                            (isempty(todo) || path_is_safe(todo))) {
+
+                                /* If CHASE_NON_EXISTING is set, and the path does not exist, then that's OK, return
+                                 * what we got so far. But don't allow this if the remaining path contains "../ or "./"
+                                 * or something else weird. */
+
+                                if (!strextend(&done, first, todo, NULL))
+                                        return -ENOMEM;
+
+                                exists = false;
+                                break;
+                        }
+
                         return -errno;
+                }
 
                 if (fstat(child, &st) < 0)
                         return -errno;
@@ -798,6 +816,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         *ret = done;
         done = NULL;
 
-        return 0;
+        return exists;
 }
 #endif // 0