diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c index 38fb582c88a..8a769e08e81 100644 --- a/src/basic/chase-symlinks.c +++ b/src/basic/chase-symlinks.c @@ -781,3 +781,38 @@ int chase_symlinks_and_unlink( return 0; } + +int chase_symlinks_at_and_open( + int dir_fd, + const char *path, + ChaseSymlinksFlags chase_flags, + int open_flags, + char **ret_path) { + + _cleanup_close_ int path_fd = -EBADF; + _cleanup_free_ char *p = NULL; + int r; + + if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)) + return -EINVAL; + + 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))); + + r = chase_symlinks_at(dir_fd, path, chase_flags, ret_path ? &p : NULL, &path_fd); + if (r < 0) + return r; + assert(path_fd >= 0); + + r = fd_reopen(path_fd, open_flags); + if (r < 0) + return r; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + return r; +} + diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h index 7308227c711..19acf9e4b56 100644 --- a/src/basic/chase-symlinks.h +++ b/src/basic/chase-symlinks.h @@ -39,3 +39,4 @@ int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, ChaseS int chase_symlinks_and_unlink(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int unlink_flags, char **ret_path); int chase_symlinks_at(int dir_fd, const char *path, ChaseSymlinksFlags flags, char **ret_path, int *ret_fd); +int chase_symlinks_at_and_open(int dir_fd, const char *path, ChaseSymlinksFlags chase_flags, int open_flags, char **ret_path); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index ef7f41885a9..c009424a2bc 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -500,6 +500,12 @@ TEST(chase_symlinks_at) { result = mfree(result); assert_se(chase_symlinks_at(tfd, "i/../p", CHASE_MKDIR_0755, NULL, NULL) == -ENOENT); + + /* Test chase_symlinks_at_and_open() */ + + fd = chase_symlinks_at_and_open(tfd, "o/p/e/n", CHASE_MKDIR_0755, O_CLOEXEC, NULL); + assert_se(fd >= 0); + fd = safe_close(fd); } TEST(unlink_noerrno) {