Merge pull request #26866 from DaanDeMeyer/chase-fix
chase-symlinks: Add CHASE_FILENAME and more at() helpers
This commit is contained in:
+215
-26
@@ -89,6 +89,8 @@ int chase_symlinks_at(
|
||||
|
||||
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 */
|
||||
@@ -98,6 +100,9 @@ int chase_symlinks_at(
|
||||
if ((flags & CHASE_STEP))
|
||||
assert(!ret_fd);
|
||||
|
||||
if ((flags & CHASE_EXTRACT_FILENAME))
|
||||
assert(ret_path);
|
||||
|
||||
if (isempty(path))
|
||||
path = ".";
|
||||
|
||||
@@ -403,6 +408,17 @@ int chase_symlinks_at(
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -520,19 +536,23 @@ int chase_symlinks(
|
||||
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;
|
||||
|
||||
path_simplify(q);
|
||||
|
||||
if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/."))
|
||||
if (!strextend(&q, "/"))
|
||||
q = path_join(empty_to_root(root), p);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_path = TAKE_PTR(q);
|
||||
path_simplify(q);
|
||||
|
||||
if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/."))
|
||||
if (!strextend(&q, "/"))
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(p, q);
|
||||
}
|
||||
|
||||
*ret_path = TAKE_PTR(p);
|
||||
}
|
||||
|
||||
if (ret_fd)
|
||||
@@ -647,14 +667,10 @@ int chase_symlinks_and_stat(
|
||||
assert(ret_stat);
|
||||
|
||||
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 */
|
||||
|
||||
if (fstatat(AT_FDCWD, path, ret_stat, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
return RET_NERRNO(fstatat(AT_FDCWD, path, ret_stat,
|
||||
FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0));
|
||||
|
||||
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
|
||||
if (r < 0)
|
||||
@@ -667,7 +683,7 @@ int chase_symlinks_and_stat(
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_access(
|
||||
@@ -685,14 +701,10 @@ int chase_symlinks_and_access(
|
||||
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)));
|
||||
|
||||
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 */
|
||||
|
||||
if (faccessat(AT_FDCWD, path, access_mode, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
return RET_NERRNO(faccessat(AT_FDCWD, path, access_mode,
|
||||
FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0));
|
||||
|
||||
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
|
||||
if (r < 0)
|
||||
@@ -706,7 +718,7 @@ int chase_symlinks_and_access(
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_fopen_unlocked(
|
||||
@@ -817,3 +829,180 @@ int chase_symlinks_at_and_open(
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef enum ChaseSymlinksFlags {
|
||||
* 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);
|
||||
@@ -41,4 +42,10 @@ 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);
|
||||
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);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#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"
|
||||
@@ -434,6 +435,9 @@ TEST(chase_symlinks_at) {
|
||||
_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);
|
||||
@@ -517,6 +521,28 @@ TEST(chase_symlinks_at) {
|
||||
|
||||
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);
|
||||
@@ -528,6 +554,42 @@ TEST(chase_symlinks_at) {
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user