diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2023-01-09 12:29:12 +0100 |
---|---|---|
committer | Daan De Meyer <daan.j.demeyer@gmail.com> | 2023-03-06 13:42:43 +0100 |
commit | e864dfa6717fd63d290b867f44bd1c6d661a9047 (patch) | |
tree | 362ec7bd854fe660385c81c2d06f6d7ead00132b | |
parent | chase-symlinks: Add CHASE_PARENT (diff) | |
download | systemd-e864dfa6717fd63d290b867f44bd1c6d661a9047.tar.xz systemd-e864dfa6717fd63d290b867f44bd1c6d661a9047.zip |
chase-symlinks: Add CHASE_MKDIR_0755
-rw-r--r-- | src/basic/chase-symlinks.c | 38 | ||||
-rw-r--r-- | src/basic/chase-symlinks.h | 1 | ||||
-rw-r--r-- | src/test/test-fs-util.c | 14 |
3 files changed, 38 insertions, 15 deletions
diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c index cc0e263852..38fb582c88 100644 --- a/src/basic/chase-symlinks.c +++ b/src/basic/chase-symlinks.c @@ -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) diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h index 6aea8d6781..7308227c71 100644 --- a/src/basic/chase-symlinks.h +++ b/src/basic/chase-symlinks.h @@ -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); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 97b6bdabc8..ef7f41885a 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -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) { |