if ((flags & CHASE_STEP) && ret_fd)
return -EINVAL;
+ if (FLAGS_SET(flags, CHASE_MKDIR_0755|CHASE_NONEXISTENT))
+ return -EINVAL;
+
if (isempty(path))
path = ".";
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
}
/* 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;
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)
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;
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)
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)
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)
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)
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) {