diff --git a/src/basic/chase.c b/src/basic/chase.c index 43fad0d93f..b1e27b48c1 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -91,7 +91,6 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT)); assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME)); assert(!FLAGS_SET(flags, CHASE_TRAIL_SLASH|CHASE_EXTRACT_FILENAME)); - assert(!FLAGS_SET(flags, CHASE_MKDIR_0755) || (flags & (CHASE_NONEXISTENT | CHASE_PARENT)) != 0); assert(dir_fd >= 0 || dir_fd == AT_FDCWD); /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ @@ -370,13 +369,13 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (r != -ENOENT) return r; - if (!isempty(todo) && !path_is_safe(todo)) + if (!isempty(todo) && !path_is_safe(todo)) /* Refuse parent/mkdir handling if suffix contains ".." or something weird */ return r; - if (FLAGS_SET(flags, CHASE_MKDIR_0755) && !isempty(todo)) { + if (FLAGS_SET(flags, CHASE_MKDIR_0755) && (!isempty(todo) || !(flags & (CHASE_PARENT|CHASE_NONEXISTENT)))) { child = xopenat_full(fd, first, - O_DIRECTORY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC, + O_DIRECTORY|O_CREAT|O_EXCL|O_NOFOLLOW|O_PATH|O_CLOEXEC, /* xopen_flags = */ 0, 0755); if (child < 0) diff --git a/src/basic/chase.h b/src/basic/chase.h index cfc714b9f7..cedd7097fb 100644 --- a/src/basic/chase.h +++ b/src/basic/chase.h @@ -27,11 +27,7 @@ typedef enum ChaseFlags { * also points to the result path even if this flag is set. * When this specified, chase() will succeed with 1 even if the * file points to the last path component does not exist. */ - CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. This - * needs to be set with CHASE_NONEXISTENT and/or CHASE_PARENT. - * Note, chase_and_open() or friends always add CHASE_PARENT flag - * when internally call chase(), hence CHASE_MKDIR_0755 can be - * safely set without CHASE_NONEXISTENT and CHASE_PARENT. */ + CHASE_MKDIR_0755 = 1 << 11, /* Create any missing directories in the given path. */ CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved path */ } ChaseFlags; diff --git a/src/test/test-chase.c b/src/test/test-chase.c index c7ca3fd051..510264c547 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -10,6 +10,7 @@ #include "id128-util.h" #include "mkdir.h" #include "path-util.h" +#include "random-util.h" #include "rm-rf.h" #include "string-util.h" #include "tests.h" @@ -754,6 +755,34 @@ TEST(trailing_dot_dot) { assert_se(path_equal(fdpath, expected2)); } +TEST(use_chase_as_mkdir_p) { + _cleanup_free_ char *p = NULL; + ASSERT_OK_ERRNO(asprintf(&p, "/tmp/chasemkdir%" PRIu64 "/a/b/c", random_u64())); + + _cleanup_close_ int fd = -EBADF; + ASSERT_OK(chase(p, NULL, CHASE_PREFIX_ROOT|CHASE_MKDIR_0755, NULL, &fd)); + + ASSERT_OK_EQ(inode_same_at(AT_FDCWD, p, fd, NULL, AT_EMPTY_PATH), 1); + + _cleanup_close_ int fd2 = -EBADF; + ASSERT_OK(chase(p, p, CHASE_PREFIX_ROOT|CHASE_MKDIR_0755, NULL, &fd2)); + + _cleanup_free_ char *pp = ASSERT_PTR(path_join(p, p)); + + ASSERT_OK_EQ(inode_same_at(AT_FDCWD, pp, fd2, NULL, AT_EMPTY_PATH), 1); + + _cleanup_free_ char *f = NULL; + ASSERT_OK(path_extract_directory(p, &f)); + + _cleanup_free_ char *ff = NULL; + ASSERT_OK(path_extract_directory(f, &ff)); + + _cleanup_free_ char *fff = NULL; + ASSERT_OK(path_extract_directory(ff, &fff)); + + ASSERT_OK(rm_rf(fff, REMOVE_PHYSICAL)); +} + static int intro(void) { arg_test_dir = saved_argv[1]; return EXIT_SUCCESS;