]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
chase-symlinks: Add CHASE_MKDIR_0755
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 9 Jan 2023 11:29:12 +0000 (12:29 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 6 Mar 2023 12:42:43 +0000 (13:42 +0100)
src/basic/chase-symlinks.c
src/basic/chase-symlinks.h
src/test/test-fs-util.c

index cc0e263852b314c53a74de00f130358ad48af287..38fb582c88a432bd55284d009fa3deb0ae820a33 100644 (file)
@@ -98,6 +98,9 @@ int chase_symlinks_at(
         if ((flags & CHASE_STEP) && ret_fd)
                 return -EINVAL;
 
+        if (FLAGS_SET(flags, CHASE_MKDIR_0755|CHASE_NONEXISTENT))
+                return -EINVAL;
+
         if (isempty(path))
                 path = ".";
 
@@ -172,7 +175,7 @@ int chase_symlinks_at(
 
         if (!(flags &
               (CHASE_AT_RESOLVE_IN_ROOT|CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP|
-               CHASE_PROHIBIT_SYMLINKS)) &&
+               CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755)) &&
             !ret_path && ret_fd) {
 
                 /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root
@@ -295,15 +298,15 @@ int chase_symlinks_at(
                 }
 
                 /* Otherwise let's see what this is. */
-                child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH);
-                if (child < 0) {
-                        if (errno == ENOENT &&
-                            (flags & CHASE_NONEXISTENT) &&
-                            (isempty(todo) || path_is_safe(todo))) {
-                                /* If CHASE_NONEXISTENT 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 something else weird. */
+                child = r = RET_NERRNO(openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH));
+                if (r < 0) {
+                        if (r != -ENOENT)
+                                return r;
+
+                        if (!isempty(todo) && !path_is_safe(todo))
+                                return r;
 
+                        if (flags & CHASE_NONEXISTENT) {
                                 if (!path_extend(&done, first, todo))
                                         return -ENOMEM;
 
@@ -311,7 +314,12 @@ int chase_symlinks_at(
                                 break;
                         }
 
-                        return -errno;
+                        if (!(flags & CHASE_MKDIR_0755))
+                                return r;
+
+                        child = open_mkdir_at(fd, first, O_CLOEXEC|O_PATH|O_EXCL, 0755);
+                        if (child < 0)
+                                return child;
                 }
 
                 if (fstat(child, &st) < 0)
@@ -390,7 +398,7 @@ int chase_symlinks_at(
                 close_and_replace(fd, child);
         }
 
-        if (flags & CHASE_PARENT) {
+        if (flags & (CHASE_PARENT|CHASE_MKDIR_0755)) {
                 r = fd_verify_directory(fd);
                 if (r < 0)
                         return r;
@@ -548,7 +556,7 @@ int chase_symlinks_and_open(
                 return -EINVAL;
 
         if (empty_or_root(root) && !ret_path &&
-            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0) {
+            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0) {
                 /* Shortcut this call if none of the special features of this call are requested */
                 r = open(path, open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
                 if (r < 0)
@@ -590,7 +598,7 @@ int chase_symlinks_and_opendir(
                 return -EINVAL;
 
         if (empty_or_root(root) && !ret_path &&
-            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0) {
+            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0) {
                 /* Shortcut this call if none of the special features of this call are requested */
                 d = opendir(path);
                 if (!d)
@@ -635,7 +643,7 @@ int chase_symlinks_and_stat(
                 return -EINVAL;
 
         if (empty_or_root(root) && !ret_path &&
-            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0 && !ret_fd) {
+            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0 && !ret_fd) {
                 /* Shortcut this call if none of the special features of this call are requested */
 
                 if (fstatat(AT_FDCWD, path, ret_stat, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0)
@@ -678,7 +686,7 @@ int chase_symlinks_and_access(
                 return -EINVAL;
 
         if (empty_or_root(root) && !ret_path &&
-            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0 && !ret_fd) {
+            (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0 && !ret_fd) {
                 /* Shortcut this call if none of the special features of this call are requested */
 
                 if (faccessat(AT_FDCWD, path, access_mode, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0)
index 6aea8d6781300949db996b2bde61c179b0c545e2..7308227c711334a3a205dfe1994092d993cb2caa 100644 (file)
@@ -21,6 +21,7 @@ typedef enum ChaseSymlinksFlags {
                                              * relative to the given directory fd instead of root. */
         CHASE_PROHIBIT_SYMLINKS  = 1 << 9,  /* Refuse all symlinks */
         CHASE_PARENT             = 1 << 10, /* Chase the parent directory of the given path. */
+        CHASE_MKDIR_0755         = 1 << 11, /* Create any missing directories in the given path. */
 } ChaseSymlinksFlags;
 
 bool unsafe_transition(const struct stat *a, const struct stat *b);
index 97b6bdabc8090b408826d1d05140045e93973c15..ef7f41885a9cfb2489bc32b68fca9e57947f8156 100644 (file)
@@ -486,6 +486,20 @@ TEST(chase_symlinks_at) {
         assert_se(chase_symlinks_at(tfd, "chase", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
         assert_se(streq(result, "."));
         result = mfree(result);
+
+        /* Test CHASE_MKDIR_0755 */
+
+        assert_se(chase_symlinks_at(tfd, "m/k/d/i/r", CHASE_MKDIR_0755, &result, NULL) >= 0);
+        assert_se(faccessat(tfd, "m/k/d/i/r", F_OK, 0) >= 0);
+        assert_se(streq(result, "m/k/d/i/r"));
+        result = mfree(result);
+
+        assert_se(chase_symlinks_at(tfd, "m/../q", CHASE_MKDIR_0755, &result, NULL) >= 0);
+        assert_se(faccessat(tfd, "q", F_OK, 0) >= 0);
+        assert_se(streq(result, "q"));
+        result = mfree(result);
+
+        assert_se(chase_symlinks_at(tfd, "i/../p", CHASE_MKDIR_0755, NULL, NULL) == -ENOENT);
 }
 
 TEST(unlink_noerrno) {