return r;
}
+int chase_symlinks_at_and_opendir(
+ int dir_fd,
+ const char *path,
+ ChaseSymlinksFlags chase_flags,
+ char **ret_path,
+ DIR **ret_dir) {
+
+ _cleanup_close_ int path_fd = -EBADF;
+ _cleanup_free_ char *p = NULL;
+ DIR *d;
+ int r;
+
+ assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)));
+ assert(ret_dir);
+
+ 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 */
+ d = opendir(path);
+ if (!d)
+ return -errno;
+
+ *ret_dir = d;
+ return 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);
+
+ d = xopendirat(path_fd, ".", O_NOFOLLOW);
+ if (!d)
+ return -errno;
+
+ if (ret_path)
+ *ret_path = TAKE_PTR(p);
+
+ *ret_dir = d;
+ return 0;
+}
+
+int chase_symlinks_at_and_stat(
+ int dir_fd,
+ const char *path,
+ ChaseSymlinksFlags chase_flags,
+ char **ret_path,
+ struct stat *ret_stat) {
+
+ _cleanup_close_ int path_fd = -EBADF;
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(path);
+ assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)));
+ assert(ret_stat);
+
+ 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(fstatat(AT_FDCWD, path, ret_stat,
+ FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_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);
+
+ if (fstat(path_fd, ret_stat) < 0)
+ return -errno;
+
+ if (ret_path)
+ *ret_path = TAKE_PTR(p);
+
+ return 0;
+}
+
+int chase_symlinks_at_and_access(
+ int dir_fd,
+ const char *path,
+ ChaseSymlinksFlags chase_flags,
+ int access_mode,
+ char **ret_path) {
+
+ _cleanup_close_ int path_fd = -EBADF;
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(path);
+ assert(!(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(faccessat(AT_FDCWD, path, access_mode,
+ FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_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 = access_fd(path_fd, access_mode);
+ if (r < 0)
+ return r;
+
+ if (ret_path)
+ *ret_path = TAKE_PTR(p);
+
+ return 0;
+}
+
+int chase_symlinks_at_and_fopen_unlocked(
+ int dir_fd,
+ const char *path,
+ ChaseSymlinksFlags chase_flags,
+ const char *open_flags,
+ char **ret_path,
+ FILE **ret_file) {
+
+ _cleanup_free_ char *final_path = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int mode_flags, r;
+
+ assert(path);
+ assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_PARENT)));
+ assert(open_flags);
+ assert(ret_file);
+
+ mode_flags = fopen_mode_to_flags(open_flags);
+ if (mode_flags < 0)
+ return mode_flags;
+
+ fd = chase_symlinks_at_and_open(dir_fd, path, chase_flags, mode_flags, ret_path ? &final_path : NULL);
+ if (fd < 0)
+ return fd;
+
+ r = take_fdopen_unlocked(&fd, open_flags, ret_file);
+ if (r < 0)
+ return r;
+
+ if (ret_path)
+ *ret_path = TAKE_PTR(final_path);
+
+ return 0;
+}
+
+int chase_symlinks_at_and_unlink(
+ int dir_fd,
+ const char *path,
+ ChaseSymlinksFlags chase_flags,
+ int unlink_flags,
+ char **ret_path) {
+
+ _cleanup_free_ char *p = NULL, *fname = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(path);
+ assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_PARENT)));
+
+ fd = chase_symlinks_at_and_open(dir_fd, path, chase_flags|CHASE_PARENT|CHASE_NOFOLLOW, O_PATH|O_DIRECTORY|O_CLOEXEC, &p);
+ if (fd < 0)
+ return fd;
+
+ 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(p);
+
+ return 0;
+}
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);
+int chase_symlinks_at_and_opendir(int dir_fd, const char *path, ChaseSymlinksFlags chase_flags, char **ret_path, DIR **ret_dir);
+int chase_symlinks_at_and_stat(int dir_fd, const char *path, ChaseSymlinksFlags chase_flags, char **ret_path, struct stat *ret_stat);
+int chase_symlinks_at_and_access(int dir_fd, const char *path, ChaseSymlinksFlags chase_flags, int access_mode, char **ret_path);
+int chase_symlinks_at_and_fopen_unlocked(int dir_fd, const char *path, ChaseSymlinksFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
+int chase_symlinks_at_and_unlink(int dir_fd, const char *path, ChaseSymlinksFlags chase_flags, int unlink_flags, char **ret_path);
#include "alloc-util.h"
#include "chase-symlinks.h"
#include "copy.h"
+#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
_cleanup_close_ int tfd = -EBADF, fd = -EBADF;
_cleanup_free_ char *result = NULL;
+ _cleanup_closedir_ DIR *dir = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ struct stat st;
const char *p;
assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0);
assert_se(fd >= 0);
assert_se(fd_verify_directory(fd) >= 0);
fd = safe_close(fd);
+
+ /* Test chase_symlinks_at_and_opendir() */
+
+ assert_se(chase_symlinks_at_and_opendir(tfd, "o/p/e/n/d/i", 0, &result, &dir) >= 0);
+ FOREACH_DIRENT(de, dir, assert_not_reached())
+ assert_se(streq(de->d_name, "r"));
+ assert_se(streq(result, "o/p/e/n/d/i"));
+ result = mfree(result);
+
+ /* Test chase_symlinks_at_and_stat() */
+
+ assert_se(chase_symlinks_at_and_stat(tfd, "o/p", 0, &result, &st) >= 0);
+ assert_se(stat_verify_directory(&st) >= 0);
+ assert_se(streq(result, "o/p"));
+ result = mfree(result);
+
+ /* Test chase_symlinks_at_and_access() */
+
+ assert_se(chase_symlinks_at_and_access(tfd, "o/p/e", 0, F_OK, &result) >= 0);
+ assert_se(streq(result, "o/p/e"));
+ result = mfree(result);
+
+ /* Test chase_symlinks_at_and_fopen_unlocked() */
+
+ assert_se(chase_symlinks_at_and_fopen_unlocked(tfd, "o/p/e/n/f/i/l/e", 0, "re", &result, &f) >= 0);
+ assert_se(fread(&(char[1]) {}, 1, 1, f) == 0);
+ assert_se(feof(f));
+ f = safe_fclose(f);
+ assert_se(streq(result, "o/p/e/n/f/i/l/e"));
+ result = mfree(result);
+
+ /* Test chase_symlinks_at_and_unlink() */
+
+ assert_se(chase_symlinks_at_and_unlink(tfd, "o/p/e/n/f/i/l/e", 0, 0, &result) >= 0);
+ assert_se(streq(result, "o/p/e/n/f/i/l/e"));
+ result = mfree(result);
}
TEST(readlink_and_make_absolute) {