Impl AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH
AT_SYMLINK_NOFOLLOW acts on the symlink itself rather than following it. AT_EMPTY_PATH acts on the directory if path is empty. Currently, this is implemented in fstatat itself but the code is general enough to be refactored once we get the other *at functions. It could likely just live in openat itself.
This commit is contained in:
@@ -30,3 +30,5 @@ pub const O_NDELAY: c_int = O_NONBLOCK;
|
||||
|
||||
// Flags for capability based "at" functions
|
||||
pub const AT_FDCWD: c_int = -100;
|
||||
pub const AT_SYMLINK_NOFOLLOW: c_int = 0x0200;
|
||||
pub const AT_EMPTY_PATH: c_int = 0x4000;
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::{
|
||||
EBADF, EBADFD, EBADR, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS,
|
||||
EOPNOTSUPP, EPERM, ERANGE,
|
||||
},
|
||||
fcntl::{self, AT_FDCWD, O_RDONLY},
|
||||
fcntl::{self, AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW, O_NOFOLLOW, O_PATH, O_RDONLY},
|
||||
limits,
|
||||
sys_mman::{MAP_ANONYMOUS, MAP_FAILED, PROT_READ, PROT_WRITE},
|
||||
sys_random,
|
||||
@@ -292,19 +292,16 @@ impl Pal for Sys {
|
||||
) -> Result<()> {
|
||||
let path =
|
||||
match CStr::from_nullable_ptr(path).and_then(|cs| str::from_utf8(cs.to_bytes()).ok()) {
|
||||
// TODO: AT_EMPTY_PATH
|
||||
Some(path) if !path.is_empty() => Ok(path),
|
||||
None | Some(_) => Err(Errno(ENOENT)),
|
||||
_ if flags & AT_EMPTY_PATH == AT_EMPTY_PATH => return Sys::fstat(fildes, buf),
|
||||
_ => Err(Errno(ENOENT)),
|
||||
}?;
|
||||
// TODO: We need an AT_SYMLINK_NOFOLLOW constant for Redox. Unlike AT_FDCWD, it varies
|
||||
// across OSes, so I don't want to define it here myself.
|
||||
// O_NOFOLLOW may be incorrect too (see the rename.c unit test).
|
||||
// let oflags = if flags & AT_SYMLINK_NOFOLLOW {
|
||||
// O_RDONLY | O_NOFOLLOW
|
||||
// } else {
|
||||
// O_RDONLY
|
||||
// };
|
||||
let oflags = O_RDONLY;
|
||||
|
||||
let oflags = if flags & AT_SYMLINK_NOFOLLOW == AT_SYMLINK_NOFOLLOW {
|
||||
O_RDONLY | O_PATH | O_NOFOLLOW
|
||||
} else {
|
||||
O_RDONLY
|
||||
};
|
||||
|
||||
// Absolute paths are passed to fstat without processing.
|
||||
// canonicalize_using_cwd checks that path is absolute so a third branch that does so here
|
||||
@@ -328,7 +325,6 @@ impl Pal for Sys {
|
||||
let path = CString::new(path).map_err(|_| Errno(ENOENT))?;
|
||||
|
||||
// TODO:
|
||||
// * If AT_SYMLINK_NOFOLLOW is set, call lstat instead.
|
||||
// * Switch open to openat.
|
||||
let file = File::open(path.as_c_str().into(), oflags)?;
|
||||
Sys::fstat(*file, buf)
|
||||
|
||||
+90
-15
@@ -1,3 +1,5 @@
|
||||
#define _GNU_SOURCE // Linux for CI
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
@@ -49,7 +51,7 @@ static const char* mktestfile(
|
||||
return path;
|
||||
}
|
||||
|
||||
// Create a link, `dir/name`, to `target` and return its path.
|
||||
// Create a symlink, `dir/name`, to `target` and return its path.
|
||||
__attribute__((nonnull))
|
||||
static const char* mktestlink(
|
||||
const char dir[],
|
||||
@@ -66,8 +68,8 @@ static const char* mktestlink(
|
||||
strcat(path, "/");
|
||||
strcat(path, name);
|
||||
|
||||
if (link(target, path) == -1) {
|
||||
perror("link");
|
||||
if (symlink(target, path) == -1) {
|
||||
perror("symlink");
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
@@ -81,14 +83,15 @@ static int run_test(
|
||||
int flags,
|
||||
const char name[],
|
||||
const char path[],
|
||||
size_t expected
|
||||
size_t expected,
|
||||
mode_t mode
|
||||
) {
|
||||
struct stat stat = {0};
|
||||
|
||||
if (fstatat(fd, name, &stat, flags) == -1) {
|
||||
perror("fstatat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t) stat.st_size != expected) {
|
||||
fprintf(
|
||||
stderr,
|
||||
@@ -99,6 +102,32 @@ static int run_test(
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
if ((stat.st_mode & S_IFMT) != mode) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"fstatat invalid mode\n\tFile: %s\n",
|
||||
path
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We create the temp dirs/files therefore we should own them.
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
if (stat.st_uid != uid || stat.st_gid != gid) {
|
||||
fputs("fstatat invalid UID or GID", stderr);
|
||||
fprintf(
|
||||
stderr,
|
||||
"\n\tExpected UID: %u\n\tActual UID: %u\n",
|
||||
uid, stat.st_uid
|
||||
);
|
||||
fprintf(
|
||||
stderr,
|
||||
"\n\tExpected GID: %u\n\tActual GID: %u\n",
|
||||
gid, stat.st_gid
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -157,24 +186,37 @@ int main(void) {
|
||||
|
||||
// fstatat works (basic)
|
||||
size_t len_cont_A = strlen(cont_A);
|
||||
if (run_test(dir_a_fd, 0, name, file_A, len_cont_A) == -1) {
|
||||
if (run_test(dir_a_fd, 0, name, file_A, len_cont_A, S_IFREG) == -1) {
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// fstatat follows symlinks (same dir)
|
||||
if (run_test(dir_a_fd, 0, link_A_name, link_A, len_cont_A) == -1) {
|
||||
if (run_test(dir_a_fd, 0, link_A_name, link_A, len_cont_A, S_IFREG) == -1) {
|
||||
fprintf(stderr, "Context: link %s -> %s\n", link_A, file_A);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// fstatat follows symlinks (to diff dir)
|
||||
size_t len_cont_B = strlen(cont_B);
|
||||
if (run_test(dir_a_fd, 0, link_B_name, link_B, len_cont_B) == -1) {
|
||||
if (run_test(dir_a_fd, 0, link_B_name, link_B, len_cont_B, S_IFREG) == -1) {
|
||||
fprintf(stderr, "Context: link %s -> %s\n", link_B, file_B);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// TODO: AT_SYMLINK_NOFOLLOW (no Redox support)
|
||||
// AT_SYMLINK_NOFOLLOW
|
||||
if (
|
||||
run_test(
|
||||
dir_a_fd,
|
||||
AT_SYMLINK_NOFOLLOW,
|
||||
link_A_name,
|
||||
link_A,
|
||||
strlen(file_A),
|
||||
S_IFLNK
|
||||
) == -1
|
||||
) {
|
||||
fprintf(stderr, "Context: link %s (no follow)\n", link_A);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// TODO: O_SEARCH (no Redox support)
|
||||
|
||||
@@ -188,7 +230,7 @@ int main(void) {
|
||||
perror("chdir");
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
if (run_test(AT_FDCWD, 0, name, "./", len_cont_A) == -1) {
|
||||
if (run_test(AT_FDCWD, 0, name, "./", len_cont_A, S_IFREG) == -1) {
|
||||
fputs("Context: AT_FDCWD\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
@@ -198,7 +240,7 @@ int main(void) {
|
||||
}
|
||||
|
||||
// Absolute path
|
||||
if (run_test(dir_a_fd, 0, file_A, file_A, len_cont_A) == -1) {
|
||||
if (run_test(dir_a_fd, 0, file_A, file_A, len_cont_A, S_IFREG) == -1) {
|
||||
fprintf(stderr, "Context: absolute path %s\n", file_A);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
@@ -228,7 +270,16 @@ int main(void) {
|
||||
}
|
||||
char rel_nested[sizeof(name) + 5] = "../";
|
||||
strcat(rel_nested, name);
|
||||
if (run_test(nested_fd, 0, rel_nested, rel_nested, len_cont_A) == -1) {
|
||||
if (
|
||||
run_test(
|
||||
nested_fd,
|
||||
0,
|
||||
rel_nested,
|
||||
rel_nested,
|
||||
len_cont_A,
|
||||
S_IFREG
|
||||
) == -1
|
||||
) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Context: relative path from %s to ../%s\n",
|
||||
@@ -240,13 +291,37 @@ int main(void) {
|
||||
|
||||
// TODO: AT_RESOLVE_BENEATH
|
||||
|
||||
// TODO: AT_EMPTY_PATH
|
||||
// AT_EMPTY_PATH (empty path)
|
||||
struct stat stat = {0};
|
||||
if (fstatat(dir_a_fd, "", &stat, AT_EMPTY_PATH) == -1) {
|
||||
fputs("Context: AT_EMPTY_PATH with empty path\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
if ((stat.st_mode & S_IFMT) != S_IFDIR) {
|
||||
fputs("Context: AT_EMPTY_PATH should stat dir (empty path)\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// AT_EMPTY_PATH (non-empty path)
|
||||
if (
|
||||
run_test(
|
||||
dir_a_fd,
|
||||
AT_EMPTY_PATH,
|
||||
link_A_name,
|
||||
link_A,
|
||||
len_cont_A,
|
||||
S_IFREG
|
||||
) == -1
|
||||
) {
|
||||
fputs("Context: AT_EMPTY_PATH with path should stat path\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// TODO: Swapped directories resolves correctly
|
||||
|
||||
// Failure conditions:
|
||||
// Empty path
|
||||
struct stat stat = {0};
|
||||
// Empty path without AT_EMPTY_PATH
|
||||
stat = (struct stat) {0};
|
||||
if (fstatat(dir_a_fd, "", &stat, 0) == 0) {
|
||||
fputs("Context: empty path should fail\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
|
||||
Reference in New Issue
Block a user