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_PARENT) {
- r = path_extract_directory(path, &buffer);
- if (r == -EDESTADDRREQ)
- path = "."; /* If we don't have a parent directory, fall back to the dir_fd directory. */
- else if (r < 0)
- return r;
- }
-
/* This function resolves symlinks of the path relative to the given directory file descriptor. If
* CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified and a directory file descriptor is provided, symlinks
* are resolved relative to the given directory file descriptor. Otherwise, they are resolved
previous_stat = st;
}
+ if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo))
+ break;
+
close_and_replace(fd, fd_parent);
continue;
if (!isempty(todo) && !path_is_safe(todo))
return r;
- if (flags & CHASE_NONEXISTENT) {
+ if (FLAGS_SET(flags, CHASE_MKDIR_0755) && !isempty(todo)) {
+ child = open_mkdir_at(fd, first, O_CLOEXEC|O_PATH|O_EXCL, 0755);
+ if (child < 0)
+ return child;
+ } else if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo)) {
+ if (!path_extend(&done, first))
+ return -ENOMEM;
+
+ break;
+ } else if (flags & CHASE_NONEXISTENT) {
if (!path_extend(&done, first, todo))
return -ENOMEM;
exists = false;
break;
- }
-
- if (!(flags & CHASE_MKDIR_0755))
+ } else
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)
if (!path_extend(&done, first))
return -ENOMEM;
+ if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo))
+ break;
+
/* And iterate again, but go one directory further down. */
close_and_replace(fd, child);
}
- if (flags & (CHASE_PARENT|CHASE_MKDIR_0755)) {
+ if (flags & CHASE_PARENT) {
r = fd_verify_directory(fd);
if (r < 0)
return r;
char **ret_path) {
_cleanup_close_ int path_fd = -EBADF;
- _cleanup_free_ char *p = NULL;
+ _cleanup_free_ char *p = NULL, *fname = NULL;
+ mode_t mode = open_flags & O_DIRECTORY ? 0755 : 0644;
+ const char *q;
int r;
if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))
return -EINVAL;
if (empty_or_root(root) && !ret_path &&
- (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 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 -errno;
-
- return r;
- }
+ return RET_NERRNO(xopenat(AT_FDCWD, path,
+ open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
+ mode));
- r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
+ r = chase_symlinks(path, root, CHASE_PARENT|chase_flags, &p, &path_fd);
if (r < 0)
return r;
assert(path_fd >= 0);
- r = fd_reopen(path_fd, open_flags);
+ assert_se(q = path_startswith(p, empty_to_root(root)));
+ if (isempty(q))
+ q = ".";
+
+ r = path_extract_filename(q, &fname);
+ if (r < 0 && r != -EADDRNOTAVAIL)
+ return r;
+
+ if (FLAGS_SET(chase_flags, CHASE_PARENT) || r == -EADDRNOTAVAIL)
+ r = fd_reopen(path_fd, open_flags);
+ else
+ r = xopenat(path_fd, fname, open_flags|O_NOFOLLOW, mode);
if (r < 0)
return r;
int unlink_flags,
char **ret_path) {
- _cleanup_free_ char *p = NULL, *rp = NULL, *fname = NULL;
+ _cleanup_free_ char *p = NULL, *fname = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(path);
- r = path_extract_filename(path, &fname);
- if (r < 0)
- return r;
-
- fd = chase_symlinks_and_open(path, root, chase_flags|CHASE_PARENT, O_PATH|O_DIRECTORY|O_CLOEXEC, ret_path ? &p : NULL);
+ fd = chase_symlinks_and_open(path, root, chase_flags|CHASE_PARENT|CHASE_NOFOLLOW, O_PATH|O_DIRECTORY|O_CLOEXEC, &p);
if (fd < 0)
return fd;
- if (p) {
- rp = path_join(p, fname);
- if (!rp)
- return -ENOMEM;
- }
+ r = path_extract_filename(p, &fname);
+ if (r < 0)
+ return r;
if (unlinkat(fd, fname, unlink_flags) < 0)
return -errno;
if (ret_path)
- *ret_path = TAKE_PTR(rp);
+ *ret_path = TAKE_PTR(p);
return 0;
}
char **ret_path) {
_cleanup_close_ int path_fd = -EBADF;
- _cleanup_free_ char *p = NULL;
+ _cleanup_free_ char *p = NULL, *fname = NULL;
+ mode_t mode = open_flags & O_DIRECTORY ? 0755 : 0644;
int r;
if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))
if (dir_fd == AT_FDCWD && !ret_path &&
(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 */
- return RET_NERRNO(openat(dir_fd, path, open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)));
+ return RET_NERRNO(xopenat(dir_fd, path,
+ open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
+ mode));
- r = chase_symlinks_at(dir_fd, path, chase_flags, ret_path ? &p : NULL, &path_fd);
+ r = chase_symlinks_at(dir_fd, path, chase_flags|CHASE_PARENT, &p, &path_fd);
if (r < 0)
return r;
- assert(path_fd >= 0);
- r = fd_reopen(path_fd, open_flags);
+ r = path_extract_filename(p, &fname);
+ if (r < 0 && r != -EDESTADDRREQ)
+ return r;
+
+ if (FLAGS_SET(chase_flags, CHASE_PARENT) || r == -EDESTADDRREQ)
+ r = fd_reopen(path_fd, open_flags);
+ else
+ r = xopenat(path_fd, fname, open_flags|O_NOFOLLOW, mode);
if (r < 0)
return r;
CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
* 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. */
+ CHASE_PARENT = 1 << 10, /* Chase the parent directory of the given path. Note that the
+ * full path is still stored in ret_path and only the returned
+ * file descriptor will point to the parent directory. */
+ CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. */
} ChaseSymlinksFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);
}
assert_se(lstat(p, &st) >= 0);
- r = chase_symlinks_and_unlink(p, NULL, 0, 0, &result);
+ r = chase_symlinks_and_unlink(p, NULL, 0, 0, &result);
+ assert_se(r == 0);
assert_se(path_equal(result, p));
result = mfree(result);
- assert_se(r == 0);
assert_se(lstat(p, &st) == -1 && errno == ENOENT);
/* Test CHASE_NOFOLLOW */
assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
- /* Test CHASE_PARENT */
-
- assert_se(chase_symlinks("/chase/parent", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, &result, NULL) >= 0);
- p = strjoina(temp, "/chase");
- assert_se(streq(p, result));
- result = mfree(result);
- assert_se(chase_symlinks("/chase", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, &result, NULL) >= 0);
- assert_se(streq(temp, result));
- result = mfree(result);
- assert_se(chase_symlinks("/", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, NULL, NULL) == -EADDRNOTAVAIL);
- assert_se(chase_symlinks(".", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, NULL, NULL) == -EADDRNOTAVAIL);
-
cleanup:
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
/* Test CHASE_PARENT */
- assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
+ assert_se((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)) >= 0);
+ assert_se(symlinkat("/def", fd, "parent") >= 0);
+ fd = safe_close(fd);
+
+ /* Make sure that when we chase a symlink parent directory, that we chase the parent directory of the
+ * symlink target and not the symlink itself. But if we add CHASE_NOFOLLOW, we get the parent
+ * directory of the symlink itself. */
+
+ assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
+ assert_se(faccessat(fd, "def", F_OK, 0) >= 0);
+ assert_se(streq(result, "def"));
+ fd = safe_close(fd);
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd) >= 0);
+ assert_se(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
+ assert_se(streq(result, "chase/parent"));
+ fd = safe_close(fd);
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
+ assert_se(faccessat(fd, "chase", F_OK, 0) >= 0);
assert_se(streq(result, "chase"));
+ fd = safe_close(fd);
result = mfree(result);
- assert_se(chase_symlinks_at(tfd, "chase", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
+ assert_se(chase_symlinks_at(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
+ assert_se(streq(result, "."));
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &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(chase_symlinks_at(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
+ assert_se(faccessat(tfd, "m/k/d/i", F_OK, 0) >= 0);
+ assert_se(RET_NERRNO(faccessat(tfd, "m/k/d/i/r", F_OK, 0)) == -ENOENT);
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(chase_symlinks_at(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
+ assert_se(faccessat(tfd, "m", F_OK, 0) >= 0);
+ assert_se(RET_NERRNO(faccessat(tfd, "q", F_OK, 0)) == -ENOENT);
assert_se(streq(result, "q"));
result = mfree(result);
/* Test chase_symlinks_at_and_open() */
- fd = chase_symlinks_at_and_open(tfd, "o/p/e/n", CHASE_MKDIR_0755, O_CLOEXEC, NULL);
+ fd = chase_symlinks_at_and_open(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);
+ assert_se(fd >= 0);
+ assert_se(fd_verify_regular(fd) >= 0);
+ fd = safe_close(fd);
+
+ fd = chase_symlinks_at_and_open(tfd, "o/p/e/n/d/i/r", CHASE_MKDIR_0755, O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, NULL);
assert_se(fd >= 0);
+ assert_se(fd_verify_directory(fd) >= 0);
fd = safe_close(fd);
}