assert(path);
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(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 */
if ((flags & CHASE_STEP))
assert(!ret_fd);
+ if ((flags & CHASE_EXTRACT_FILENAME))
+ assert(ret_path);
+
if (isempty(path))
path = ".";
}
if (ret_path) {
+ if (FLAGS_SET(flags, CHASE_EXTRACT_FILENAME) && done) {
+ _cleanup_free_ char *f = NULL;
+
+ r = path_extract_filename(done, &f);
+ if (r < 0 && r != -EDESTADDRREQ)
+ return r;
+
+ /* If we get EDESTADDRREQ we clear done and it will get reinitialized by the next block. */
+ free_and_replace(done, f);
+ }
+
if (!done) {
done = strdup(append_trail_slash ? "./" : ".");
if (!done)
return r;
if (ret_path) {
- _cleanup_free_ char *q = NULL;
+ if (!FLAGS_SET(flags, CHASE_EXTRACT_FILENAME)) {
+ _cleanup_free_ char *q = NULL;
- q = path_join(empty_to_root(root), p);
- if (!q)
- return -ENOMEM;
+ q = path_join(empty_to_root(root), p);
+ if (!q)
+ return -ENOMEM;
- path_simplify(q);
+ path_simplify(q);
- if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/."))
- if (!strextend(&q, "/"))
- return -ENOMEM;
+ if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/."))
+ if (!strextend(&q, "/"))
+ return -ENOMEM;
- *ret_path = TAKE_PTR(q);
+ free_and_replace(p, q);
+ }
+
+ *ret_path = TAKE_PTR(p);
}
if (ret_fd)
* 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. */
+ CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved path */
} ChaseSymlinksFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);
assert_se(chase_symlinks_at(tfd, "i/../p", CHASE_MKDIR_0755, NULL, NULL) == -ENOENT);
+ /* Test CHASE_FILENAME */
+
+ assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
+ assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
+ assert_se(streq(result, "parent"));
+ fd = safe_close(fd);
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
+ assert_se(faccessat(fd, result, F_OK, 0) >= 0);
+ assert_se(streq(result, "chase"));
+ fd = safe_close(fd);
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
+ assert_se(streq(result, "."));
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
+ assert_se(streq(result, "."));
+ result = mfree(result);
+
/* Test chase_symlinks_at_and_open() */
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);