unsigned max_follow = CHASE_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
bool exists = true, append_trail_slash = false;
struct stat st; /* stat obtained from fd */
+ bool need_absolute = false; /* allocate early to avoid compiler warnings around goto */
const char *todo;
int r;
_cleanup_close_ int _dir_fd = -EBADF;
if (dir_fd == XAT_FDROOT) {
+
+ /* Shortcut the common case where no root dir is specified, and no special flags are given to
+ * a regular open() */
+ if (!ret_path &&
+ (flags & (CHASE_STEP|CHASE_NO_AUTOFS|CHASE_NONEXISTENT|CHASE_SAFE|CHASE_WARN|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0) {
+ _cleanup_free_ char *slash_path = NULL;
+
+ if (!path_is_absolute(path)) {
+ slash_path = strjoin("/", path);
+ if (!slash_path)
+ return -ENOMEM;
+ }
+
+ /* We use open_tree() rather than regular open() here, because it gives us direct
+ * control over automount behaviour, and otherwise is equivalent to open() with
+ * O_PATH */
+ fd = open_tree(-EBADF, slash_path ?: path, OPEN_TREE_CLOEXEC|(FLAGS_SET(flags, CHASE_TRIGGER_AUTOFS) ? 0 : AT_NO_AUTOMOUNT));
+ if (fd < 0)
+ return -errno;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ exists = true;
+ goto success;
+ }
+
_dir_fd = open("/", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (_dir_fd < 0)
return -errno;
if (r < 0)
return r;
- bool need_absolute = r;
+ need_absolute = r;
if (need_absolute) {
done = strdup("/");
if (!done)
close_and_replace(fd, child);
}
+success:
if (exists) {
if (FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY)) {
r = stat_verify_directory(&st);
fd = safe_close(fd);
/* Same but with XAT_FDROOT */
- ASSERT_OK(chaseat(XAT_FDROOT, p, 0, &result, NULL));
+ _cleanup_close_ int found_fd1 = -EBADF;
+ ASSERT_OK(chaseat(XAT_FDROOT, p, 0, &result, &found_fd1));
ASSERT_STREQ(result, "/usr");
result = mfree(result);
- ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+ _cleanup_close_ int found_fd2 = -EBADF;
+ ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, &result, &found_fd2));
ASSERT_STREQ(result, "/usr");
result = mfree(result);
-
- fd = safe_close(fd);
+ assert(fd_inode_same(found_fd1, found_fd2) > 0);
+
+ /* Do the same XAT_FDROOT tests again, this time without querying the path, so that the open_tree()
+ * shortcut can work */
+ _cleanup_close_ int found_fd3 = -EBADF;
+ ASSERT_OK(chaseat(XAT_FDROOT, p, 0, NULL, &found_fd3));
+ assert(fd_inode_same(found_fd1, found_fd3) > 0);
+ assert(fd_inode_same(found_fd2, found_fd3) > 0);
+
+ _cleanup_close_ int found_fd4 = -EBADF;
+ ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, NULL, &found_fd4));
+ assert(fd_inode_same(found_fd1, found_fd4) > 0);
+ assert(fd_inode_same(found_fd2, found_fd4) > 0);
+ assert(fd_inode_same(found_fd3, found_fd4) > 0);
+
+ found_fd1 = safe_close(found_fd1);
+ found_fd2 = safe_close(found_fd2);
+ found_fd3 = safe_close(found_fd3);
+ found_fd4 = safe_close(found_fd4);
/* If the file descriptor does not point to the root directory, the result will be relative
* unless the result is outside of the specified file descriptor. */