chiark / gitweb /
fs-util: add new CHASE_NON_EXISTING flag to chase_symlinks()
authorLennart Poettering <lennart@poettering.net>
Tue, 29 Nov 2016 17:02:45 +0000 (18:02 +0100)
committerSven Eden <yamakuzure@gmx.net>
Mon, 17 Jul 2017 15:58:35 +0000 (17:58 +0200)
This new flag controls whether to consider a problem if the referenced path
doesn't actually exist. If specified it's OK if the final file doesn't exist.

Note that this permits one or more final components of the path not to exist,
but these must not contain "../" for safety reasons (or, to be extra safe,
neither "./" and a couple of others, i.e. what path_is_safe() permits).

This new flag is useful when resolving paths before issuing an mkdir() or
open(O_CREAT) on a path, as it permits that the file or directory is created
later.

The return code of chase_symlinks() is changed to return 1 if the file exists,
and 0 if it doesn't. The latter is only returned in case CHASE_NON_EXISTING is
set.

src/basic/fs-util.c
src/basic/fs-util.h

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
index b5883b6c5cc630db8742546d222072405e17a67f..e206a382ec7d94e6fce0f2fcaee18a2d93f80bc5 100644 (file)
@@ -93,6 +93,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask);
 #endif // 0
 enum {
         CHASE_PREFIX_ROOT = 1,   /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+        CHASE_NON_EXISTING = 2,  /* If set, it's OK if the path doesn't actually exist. */
 };
 
 int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);