Merge pull request #26784 from DaanDeMeyer/chase-fix

Allow creating files and directories with chase_symlinks_open() and further improvements
This commit is contained in:
Daan De Meyer
2023-03-15 15:51:47 +01:00
committed by GitHub
14 changed files with 210 additions and 139 deletions
+77 -76
View File
@@ -92,26 +92,15 @@ int chase_symlinks_at(
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_NONEXISTENT) && ret_fd)
return -EINVAL;
if ((flags & CHASE_NONEXISTENT))
assert(!ret_fd);
if ((flags & CHASE_STEP) && ret_fd)
return -EINVAL;
if (FLAGS_SET(flags, CHASE_MKDIR_0755|CHASE_NONEXISTENT))
return -EINVAL;
if ((flags & CHASE_STEP))
assert(!ret_fd);
if (isempty(path))
path = ".";
if (flags & CHASE_PARENT) {
r = path_extract_directory(path, &buffer);
if (r == -EDESTADDRREQ)
path = "."; /* If we don't have a parent directory, fall back to the dir_fd directory. */
else if (r < 0)
return r;
}
/* This function resolves symlinks of the path relative to the given directory file descriptor. If
* CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified and a directory file descriptor is provided, symlinks
* are resolved relative to the given directory file descriptor. Otherwise, they are resolved
@@ -292,6 +281,9 @@ int chase_symlinks_at(
previous_stat = st;
}
if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo))
break;
close_and_replace(fd, fd_parent);
continue;
@@ -306,20 +298,23 @@ int chase_symlinks_at(
if (!isempty(todo) && !path_is_safe(todo))
return r;
if (flags & CHASE_NONEXISTENT) {
if (FLAGS_SET(flags, CHASE_MKDIR_0755) && !isempty(todo)) {
child = open_mkdir_at(fd, first, O_CLOEXEC|O_PATH|O_EXCL, 0755);
if (child < 0)
return child;
} else if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo)) {
if (!path_extend(&done, first))
return -ENOMEM;
break;
} else if (flags & CHASE_NONEXISTENT) {
if (!path_extend(&done, first, todo))
return -ENOMEM;
exists = false;
break;
}
if (!(flags & CHASE_MKDIR_0755))
} else
return r;
child = open_mkdir_at(fd, first, O_CLOEXEC|O_PATH|O_EXCL, 0755);
if (child < 0)
return child;
}
if (fstat(child, &st) < 0)
@@ -394,11 +389,14 @@ int chase_symlinks_at(
if (!path_extend(&done, first))
return -ENOMEM;
if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo))
break;
/* And iterate again, but go one directory further down. */
close_and_replace(fd, child);
}
if (flags & (CHASE_PARENT|CHASE_MKDIR_0755)) {
if (flags & CHASE_PARENT) {
r = fd_verify_directory(fd);
if (r < 0)
return r;
@@ -522,7 +520,9 @@ int chase_symlinks(
return r;
if (ret_path) {
char *q = path_join(empty_to_root(root), p);
_cleanup_free_ char *q = NULL;
q = path_join(empty_to_root(root), p);
if (!q)
return -ENOMEM;
@@ -549,28 +549,37 @@ int chase_symlinks_and_open(
char **ret_path) {
_cleanup_close_ int path_fd = -EBADF;
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *p = NULL, *fname = NULL;
mode_t mode = open_flags & O_DIRECTORY ? 0755 : 0644;
const char *q;
int r;
if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))
return -EINVAL;
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 */
r = open(path, open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (r < 0)
return -errno;
return RET_NERRNO(xopenat(AT_FDCWD, path,
open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
mode));
return r;
}
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
r = chase_symlinks(path, root, CHASE_PARENT|chase_flags, &p, &path_fd);
if (r < 0)
return r;
assert(path_fd >= 0);
r = fd_reopen(path_fd, open_flags);
assert_se(q = path_startswith(p, empty_to_root(root)));
if (isempty(q))
q = ".";
r = path_extract_filename(q, &fname);
if (r < 0 && r != -EADDRNOTAVAIL)
return r;
if (FLAGS_SET(chase_flags, CHASE_PARENT) || r == -EADDRNOTAVAIL)
r = fd_reopen(path_fd, open_flags);
else
r = xopenat(path_fd, fname, open_flags|O_NOFOLLOW, mode);
if (r < 0)
return r;
@@ -592,10 +601,8 @@ int chase_symlinks_and_opendir(
DIR *d;
int r;
if (!ret_dir)
return -EINVAL;
if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))
return -EINVAL;
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)));
assert(ret_dir);
if (empty_or_root(root) && !ret_path &&
(chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0) {
@@ -629,21 +636,18 @@ int chase_symlinks_and_stat(
const char *root,
ChaseSymlinksFlags chase_flags,
char **ret_path,
struct stat *ret_stat,
int *ret_fd) {
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 (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))
return -EINVAL;
if (empty_or_root(root) && !ret_path &&
(chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0 && !ret_fd) {
(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)
@@ -662,8 +666,6 @@ int chase_symlinks_and_stat(
if (ret_path)
*ret_path = TAKE_PTR(p);
if (ret_fd)
*ret_fd = TAKE_FD(path_fd);
return 1;
}
@@ -673,20 +675,17 @@ int chase_symlinks_and_access(
const char *root,
ChaseSymlinksFlags chase_flags,
int access_mode,
char **ret_path,
int *ret_fd) {
char **ret_path) {
_cleanup_close_ int path_fd = -EBADF;
_cleanup_free_ char *p = NULL;
int r;
assert(path);
if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))
return -EINVAL;
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 && !ret_fd) {
(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)
@@ -706,8 +705,6 @@ int chase_symlinks_and_access(
if (ret_path)
*ret_path = TAKE_PTR(p);
if (ret_fd)
*ret_fd = TAKE_FD(path_fd);
return 1;
}
@@ -725,6 +722,7 @@ int chase_symlinks_and_fopen_unlocked(
int mode_flags, r;
assert(path);
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_PARENT)));
assert(open_flags);
assert(ret_file);
@@ -753,31 +751,26 @@ int chase_symlinks_and_unlink(
int unlink_flags,
char **ret_path) {
_cleanup_free_ char *p = NULL, *rp = NULL, *fname = NULL;
_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)));
r = path_extract_filename(path, &fname);
if (r < 0)
return r;
fd = chase_symlinks_and_open(path, root, chase_flags|CHASE_PARENT, O_PATH|O_DIRECTORY|O_CLOEXEC, ret_path ? &p : NULL);
fd = chase_symlinks_and_open(path, root, chase_flags|CHASE_PARENT|CHASE_NOFOLLOW, O_PATH|O_DIRECTORY|O_CLOEXEC, &p);
if (fd < 0)
return fd;
if (p) {
rp = path_join(p, fname);
if (!rp)
return -ENOMEM;
}
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(rp);
*ret_path = TAKE_PTR(p);
return 0;
}
@@ -790,23 +783,31 @@ int chase_symlinks_at_and_open(
char **ret_path) {
_cleanup_close_ int path_fd = -EBADF;
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *p = NULL, *fname = NULL;
mode_t mode = open_flags & O_DIRECTORY ? 0755 : 0644;
int r;
if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))
return -EINVAL;
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(openat(dir_fd, path, open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)));
return RET_NERRNO(xopenat(dir_fd, path,
open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0),
mode));
r = chase_symlinks_at(dir_fd, path, chase_flags, ret_path ? &p : NULL, &path_fd);
r = chase_symlinks_at(dir_fd, path, chase_flags|CHASE_PARENT, &p, &path_fd);
if (r < 0)
return r;
assert(path_fd >= 0);
r = fd_reopen(path_fd, open_flags);
r = path_extract_filename(p, &fname);
if (r < 0 && r != -EDESTADDRREQ)
return r;
if (FLAGS_SET(chase_flags, CHASE_PARENT) || r == -EDESTADDRREQ)
r = fd_reopen(path_fd, open_flags);
else
r = xopenat(path_fd, fname, open_flags|O_NOFOLLOW, mode);
if (r < 0)
return r;
+6 -4
View File
@@ -20,8 +20,10 @@ typedef enum ChaseSymlinksFlags {
CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
* relative to the given directory fd instead of root. */
CHASE_PROHIBIT_SYMLINKS = 1 << 9, /* Refuse all symlinks */
CHASE_PARENT = 1 << 10, /* Chase the parent directory of the given path. */
CHASE_MKDIR_0755 = 1 << 11, /* Create any missing directories in the given path. */
CHASE_PARENT = 1 << 10, /* Chase the parent directory of the given path. Note that the
* 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. */
} ChaseSymlinksFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);
@@ -33,8 +35,8 @@ int chase_symlinks(const char *path_with_prefix, const char *root, ChaseSymlinks
int chase_symlinks_and_open(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int open_flags, char **ret_path);
int chase_symlinks_and_opendir(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, DIR **ret_dir);
int chase_symlinks_and_stat(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
int chase_symlinks_and_access(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int access_mode, char **ret_path, int *ret_fd);
int chase_symlinks_and_stat(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, struct stat *ret_stat);
int chase_symlinks_and_access(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int access_mode, char **ret_path);
int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, ChaseSymlinksFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
int chase_symlinks_and_unlink(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int unlink_flags, char **ret_path);
+46 -27
View File
@@ -988,7 +988,6 @@ int parse_cifs_service(
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF, parent_fd = -EBADF;
_cleanup_free_ char *fname = NULL;
bool made;
int r;
/* Creates a directory with mkdirat() and then opens it, in the "most atomic" fashion we can
@@ -1028,33 +1027,11 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
path = fname;
}
r = RET_NERRNO(mkdirat(dirfd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(flags, O_EXCL))
return -EEXIST;
made = false;
} else if (r < 0)
return r;
else
made = true;
fd = RET_NERRNO(openat(dirfd, path, (flags & ~O_EXCL)|O_DIRECTORY|O_NOFOLLOW));
if (fd < 0) {
if (fd == -ENOENT) /* We got ENOENT? then someone else immediately removed it after we
* created it. In that case let's return immediately without unlinking
* anything, because there simply isn't anything to unlink anymore. */
return -ENOENT;
if (fd == -ELOOP) /* is a symlink? exists already → created by someone else, don't unlink */
return -EEXIST;
if (fd == -ENOTDIR) /* not a directory? exists already → created by someone else, don't unlink */
return -EEXIST;
if (made)
(void) unlinkat(dirfd, path, AT_REMOVEDIR);
fd = xopenat(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, mode);
if (IN_SET(fd, -ELOOP, -ENOTDIR))
return -EEXIST;
if (fd < 0)
return fd;
}
return TAKE_FD(fd);
}
@@ -1105,3 +1082,45 @@ int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, b
return -EEXIST;
}
}
int xopenat(int dir_fd, const char *path, int flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF;
bool made = false;
int r;
if (FLAGS_SET(flags, O_DIRECTORY|O_CREAT)) {
r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(flags, O_EXCL))
return -EEXIST;
made = false;
} else if (r < 0)
return r;
else
made = true;
flags &= ~(O_EXCL|O_CREAT);
}
fd = RET_NERRNO(openat(dir_fd, path, flags, mode));
if (fd < 0) {
if (IN_SET(fd,
/* We got ENOENT? then someone else immediately removed it after we
* created it. In that case let's return immediately without unlinking
* anything, because there simply isn't anything to unlink anymore. */
-ENOENT,
/* is a symlink? exists already → created by someone else, don't unlink */
-ELOOP,
/* not a directory? exists already → created by someone else, don't unlink */
-ENOTDIR))
return fd;
if (made)
(void) unlinkat(dir_fd, path, AT_REMOVEDIR);
return fd;
}
return TAKE_FD(fd);
}
+2
View File
@@ -127,3 +127,5 @@ int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
int xopenat(int dir_fd, const char *path, int flags, mode_t mode);
+1 -1
View File
@@ -145,7 +145,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) {
if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null"))
return true;
r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
if (r < 0)
return r;
+1 -1
View File
@@ -664,7 +664,7 @@ static int install_variables(
return 0;
}
r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
+1 -1
View File
@@ -560,7 +560,7 @@ static void deref_unlink_file(Hashmap *known_files, const char *fn, const char *
return;
if (arg_dry_run) {
r = chase_symlinks_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, &path, NULL);
r = chase_symlinks_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, &path);
if (r < 0)
log_info_errno(r, "Unable to determine whether \"%s\" exists, ignoring: %m", fn);
else
+1 -1
View File
@@ -1768,7 +1768,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Path to init binary '%s' not absolute.", init);
r = chase_symlinks_and_access(init, root, CHASE_PREFIX_ROOT, X_OK, NULL, NULL);
r = chase_symlinks_and_access(init, root, CHASE_PREFIX_ROOT, X_OK, NULL);
if (r == -EACCES)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Init binary %s is not executable.", init);
+1 -1
View File
@@ -981,7 +981,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
return r;
assert(r > 0);
r = chase_symlinks_and_access(filename, arg_root, CHASE_PREFIX_ROOT, F_OK, &resolved, NULL);
r = chase_symlinks_and_access(filename, arg_root, CHASE_PREFIX_ROOT, F_OK, &resolved);
if (r < 0)
return log_error_errno(r, "Cannot access \"%s%s\": %m", strempty(arg_root), filename);
+1 -1
View File
@@ -926,7 +926,7 @@ static int mount_inaccessible(const char *dest, CustomMount *m) {
assert(dest);
assert(m);
r = chase_symlinks_and_stat(m->destination, dest, CHASE_PREFIX_ROOT, &where, &st, NULL);
r = chase_symlinks_and_stat(m->destination, dest, CHASE_PREFIX_ROOT, &where, &st);
if (r < 0) {
log_full_errno(m->graceful ? LOG_DEBUG : LOG_ERR, r, "Failed to resolve %s/%s: %m", dest, m->destination);
return m->graceful ? 0 : r;
+1 -1
View File
@@ -4021,7 +4021,7 @@ static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type)
if (!st)
return log_oom();
r = chase_symlinks_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st, NULL);
r = chase_symlinks_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st);
if (r == -ENOENT)
return 0;
if (r < 0)
+1 -1
View File
@@ -1269,7 +1269,7 @@ static void boot_entry_file_list(
assert(p);
assert(ret_status);
int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL);
/* Note that this shows two '/' between the root and the file. This is intentional to highlight (in
* the absence of color support) to the user that the boot loader is only interested in the second
+2 -2
View File
@@ -21,11 +21,11 @@ static int same_file_in_root(
struct stat sta, stb;
int r;
r = chase_symlinks_and_stat(a, root, CHASE_PREFIX_ROOT, NULL, &sta, NULL);
r = chase_symlinks_and_stat(a, root, CHASE_PREFIX_ROOT, NULL, &sta);
if (r < 0)
return r;
r = chase_symlinks_and_stat(b, root, CHASE_PREFIX_ROOT, NULL, &stb, NULL);
r = chase_symlinks_and_stat(b, root, CHASE_PREFIX_ROOT, NULL, &stb);
if (r < 0)
return r;
+69 -22
View File
@@ -331,10 +331,10 @@ TEST(chase_symlinks) {
}
assert_se(lstat(p, &st) >= 0);
r = chase_symlinks_and_unlink(p, NULL, 0, 0, &result);
r = chase_symlinks_and_unlink(p, NULL, 0, 0, &result);
assert_se(r == 0);
assert_se(path_equal(result, p));
result = mfree(result);
assert_se(r == 0);
assert_se(lstat(p, &st) == -1 && errno == ENOENT);
/* Test CHASE_NOFOLLOW */
@@ -426,18 +426,6 @@ TEST(chase_symlinks) {
assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
/* Test CHASE_PARENT */
assert_se(chase_symlinks("/chase/parent", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, &result, NULL) >= 0);
p = strjoina(temp, "/chase");
assert_se(streq(p, result));
result = mfree(result);
assert_se(chase_symlinks("/chase", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, &result, NULL) >= 0);
assert_se(streq(temp, result));
result = mfree(result);
assert_se(chase_symlinks("/", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, NULL, NULL) == -EADDRNOTAVAIL);
assert_se(chase_symlinks(".", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, NULL, NULL) == -EADDRNOTAVAIL);
cleanup:
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -479,23 +467,51 @@ TEST(chase_symlinks_at) {
/* Test CHASE_PARENT */
assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
assert_se(streq(result, "chase"));
assert_se((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)) >= 0);
assert_se(symlinkat("/def", fd, "parent") >= 0);
fd = safe_close(fd);
/* Make sure that when we chase a symlink parent directory, that we chase the parent directory of the
* symlink target and not the symlink itself. But if we add CHASE_NOFOLLOW, we get the parent
* directory of the symlink itself. */
assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
assert_se(faccessat(fd, "def", F_OK, 0) >= 0);
assert_se(streq(result, "def"));
fd = safe_close(fd);
result = mfree(result);
assert_se(chase_symlinks_at(tfd, "chase", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd) >= 0);
assert_se(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
assert_se(streq(result, "chase/parent"));
fd = safe_close(fd);
result = mfree(result);
assert_se(chase_symlinks_at(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
assert_se(faccessat(fd, "chase", 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, &result, NULL) >= 0);
assert_se(streq(result, "."));
result = mfree(result);
assert_se(chase_symlinks_at(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
assert_se(streq(result, "."));
result = mfree(result);
/* Test CHASE_MKDIR_0755 */
assert_se(chase_symlinks_at(tfd, "m/k/d/i/r", CHASE_MKDIR_0755, &result, NULL) >= 0);
assert_se(faccessat(tfd, "m/k/d/i/r", F_OK, 0) >= 0);
assert_se(chase_symlinks_at(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
assert_se(faccessat(tfd, "m/k/d/i", F_OK, 0) >= 0);
assert_se(RET_NERRNO(faccessat(tfd, "m/k/d/i/r", F_OK, 0)) == -ENOENT);
assert_se(streq(result, "m/k/d/i/r"));
result = mfree(result);
assert_se(chase_symlinks_at(tfd, "m/../q", CHASE_MKDIR_0755, &result, NULL) >= 0);
assert_se(faccessat(tfd, "q", F_OK, 0) >= 0);
assert_se(chase_symlinks_at(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
assert_se(faccessat(tfd, "m", F_OK, 0) >= 0);
assert_se(RET_NERRNO(faccessat(tfd, "q", F_OK, 0)) == -ENOENT);
assert_se(streq(result, "q"));
result = mfree(result);
@@ -503,8 +519,14 @@ TEST(chase_symlinks_at) {
/* Test chase_symlinks_at_and_open() */
fd = chase_symlinks_at_and_open(tfd, "o/p/e/n", CHASE_MKDIR_0755, O_CLOEXEC, NULL);
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);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) >= 0);
fd = safe_close(fd);
fd = chase_symlinks_at_and_open(tfd, "o/p/e/n/d/i/r", CHASE_MKDIR_0755, O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, NULL);
assert_se(fd >= 0);
assert_se(fd_verify_directory(fd) >= 0);
fd = safe_close(fd);
}
@@ -1136,6 +1158,31 @@ TEST(openat_report_new) {
assert_se(b);
}
TEST(xopenat) {
_cleanup_close_ int tfd = -EBADF, fd = -EBADF;
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0);
/* Test that xopenat() creates directories if O_DIRECTORY is specified. */
assert_se((fd = xopenat(tfd, "abc", O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, 0755)) >= 0);
assert_se((fd_verify_directory(fd) >= 0));
fd = safe_close(fd);
assert_se(xopenat(tfd, "abc", O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, 0755) == -EEXIST);
assert_se((fd = xopenat(tfd, "abc", O_DIRECTORY|O_CREAT|O_CLOEXEC, 0755)) >= 0);
assert_se((fd_verify_directory(fd) >= 0));
fd = safe_close(fd);
/* Test that xopenat() creates regular files if O_DIRECTORY is not specified. */
assert_se((fd = xopenat(tfd, "def", O_CREAT|O_EXCL|O_CLOEXEC, 0644)) >= 0);
assert_se(fd_verify_regular(fd) >= 0);
fd = safe_close(fd);
}
static int intro(void) {
arg_test_dir = saved_argv[1];
return EXIT_SUCCESS;