Merge branch 'fchmodat' into 'master'
Implement fchmodat See merge request redox-os/relibc!714
This commit is contained in:
@@ -82,6 +82,19 @@ pub extern "C" fn fchmod(fildes: c_int, mode: mode_t) -> c_int {
|
||||
Sys::fchmod(fildes, mode).map(|()| 0).or_minus_one_errno()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fchmodat(
|
||||
dirfd: c_int,
|
||||
path: *const c_char,
|
||||
mode: mode_t,
|
||||
flags: c_int,
|
||||
) -> c_int {
|
||||
let path = CStr::from_nullable_ptr(path);
|
||||
Sys::fchmodat(dirfd, path, mode, flags)
|
||||
.map(|()| 0)
|
||||
.or_minus_one_errno()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
|
||||
let buf = Out::nonnull(buf);
|
||||
|
||||
@@ -171,6 +171,19 @@ impl Pal for Sys {
|
||||
e_raw(unsafe { syscall!(FCHMOD, fildes, mode) }).map(|_| ())
|
||||
}
|
||||
|
||||
fn fchmodat(dirfd: c_int, path: Option<CStr>, mode: mode_t, flags: c_int) -> Result<()> {
|
||||
e_raw(unsafe {
|
||||
syscall!(
|
||||
FCHMODAT,
|
||||
dirfd,
|
||||
path.map_or(core::ptr::null(), |p| p.as_ptr()),
|
||||
mode,
|
||||
flags
|
||||
)
|
||||
})
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()> {
|
||||
e_raw(unsafe { syscall!(FCHOWN, fildes, owner, group) }).map(|_| ())
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ pub trait Pal {
|
||||
fn fchdir(fildes: c_int) -> Result<()>;
|
||||
|
||||
fn fchmod(fildes: c_int, mode: mode_t) -> Result<()>;
|
||||
fn fchmodat(dirfd: c_int, path: Option<CStr>, mode: mode_t, flags: c_int) -> Result<()>;
|
||||
|
||||
fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()>;
|
||||
|
||||
|
||||
@@ -259,6 +259,19 @@ impl Pal for Sys {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fchmodat(dirfd: c_int, path: Option<CStr>, mode: mode_t, flags: c_int) -> Result<()> {
|
||||
const MASK: c_int = !(fcntl::AT_SYMLINK_NOFOLLOW | fcntl::AT_EMPTY_PATH);
|
||||
if MASK & flags != 0 {
|
||||
return Err(Errno(EOPNOTSUPP));
|
||||
}
|
||||
let path = path
|
||||
.and_then(|cs| str::from_utf8(cs.to_bytes()).ok())
|
||||
.ok_or(Errno(ENOENT))?;
|
||||
let file = cap_path_at(dirfd, path, flags, 0)?;
|
||||
syscall::fchmod(*file as usize, mode as u16)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> Result<()> {
|
||||
syscall::fchown(fd as usize, owner as u32, group as u32)?;
|
||||
Ok(())
|
||||
|
||||
@@ -109,6 +109,7 @@ EXPECT_NAMES=\
|
||||
string/stpcpy \
|
||||
string/stpncpy \
|
||||
strings \
|
||||
sys_stat/chmod \
|
||||
sys_stat/lstat \
|
||||
sys_stat/fstatat \
|
||||
sys_syslog/syslog \
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
#define _GNU_SOURCE // Linux to run locally
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* #if defined(__linux__) && !defined(__redox__) */
|
||||
/* #include <linux/version.h> */
|
||||
/* #else */
|
||||
/* #define KERNEL_VERSION(x,y,z) (0) */
|
||||
/* #endif */
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
static bool check_mode(
|
||||
int dir,
|
||||
const char path[],
|
||||
int flags,
|
||||
mode_t expected
|
||||
) {
|
||||
struct stat stat = {0};
|
||||
if (fstatat(dir, path, &stat, flags) == -1) {
|
||||
perror("fstatat");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((stat.st_mode & 0777) != expected) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Mode mismatch\n\tExpected: %o\n\tActual: %o\n",
|
||||
expected,
|
||||
stat.st_mode
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
char template[] = "/tmp/chmtest.XXXXXX";
|
||||
if (!mkdtemp(template)) {
|
||||
perror("mkdtemp");
|
||||
goto bye;
|
||||
}
|
||||
size_t len = sizeof(template) - 1;
|
||||
|
||||
// Create a file and a link to chmod.
|
||||
const char file_name[] = "king_crimson";
|
||||
char file_path[PATH_MAX] = {0};
|
||||
memcpy(file_path, template, len);
|
||||
file_path[len] = '/';
|
||||
memcpy(&file_path[len + 1], file_name, sizeof(file_name));
|
||||
|
||||
int filefd = open(file_path, O_CREAT);
|
||||
if (filefd == -1) {
|
||||
perror("open (file, O_CREAT)");
|
||||
goto rmtempdir;
|
||||
}
|
||||
|
||||
const char link_name[] = "red";
|
||||
char link_path[PATH_MAX] = {0};
|
||||
memcpy(link_path, template, len);
|
||||
link_path[len] = '/';
|
||||
memcpy(&link_path[len + 1], link_name, sizeof(link_name));
|
||||
|
||||
if (symlink(file_path, link_path) == -1) {
|
||||
perror("symlink");
|
||||
goto rmfiles;
|
||||
}
|
||||
#ifdef __redox__
|
||||
int linkfd = open(link_path, O_PATH | O_NOFOLLOW | O_SYMLINK);
|
||||
if (linkfd == -1) {
|
||||
perror("open (link, O_NOFOLLOW)");
|
||||
goto rmfiles;
|
||||
}
|
||||
#endif
|
||||
|
||||
int dir = open(template, O_DIRECTORY);
|
||||
if (dir == -1) {
|
||||
perror("open (temp dir)");
|
||||
goto closelink;
|
||||
}
|
||||
|
||||
// chmod
|
||||
|
||||
// File
|
||||
if (chmod(file_path, 0666) == -1) {
|
||||
perror("chmod file 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0666)) {
|
||||
fprintf(stderr, "Context: chmod %s\n", file_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Link (deferenced)
|
||||
if (chmod(link_path, 0777) == -1) {
|
||||
perror("chmod link 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0777)) {
|
||||
fprintf(stderr, "Context: chmod %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Dir
|
||||
if (chmod(template, 0766) == -1) {
|
||||
perror("chmod directory 0766");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, "", AT_EMPTY_PATH, 0766)) {
|
||||
fprintf(stderr, "Context: chmod %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// fchmod
|
||||
|
||||
// File
|
||||
if (fchmod(filefd, 0644) == -1) {
|
||||
perror("fchmod file 0644");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0644)) {
|
||||
fprintf(stderr, "Context: fchmod %s\n", file_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Link (not followed)
|
||||
#ifdef __redox__
|
||||
if (fchmod(linkfd, 0666) == -1) {
|
||||
perror("fchmod link 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, link_name, AT_SYMLINK_NOFOLLOW, 0666)) {
|
||||
fprintf(stderr, "Context: fchmod %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Directory
|
||||
if (fchmod(dir, 0777) == -1) {
|
||||
perror("fchmod directory 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, "", AT_EMPTY_PATH, 0777)) {
|
||||
fprintf(stderr, "Context: fchmod %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// fchmodat
|
||||
|
||||
// File (relative)
|
||||
if (fchmodat(dir, file_name, 0666, 0) == -1) {
|
||||
perror("fchmodat directory 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0666)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", file_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// File (absolute)
|
||||
if (fchmodat(dir, file_path, 0777, 0) == -1) {
|
||||
perror("fchmodat file 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0777)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", file_path);
|
||||
}
|
||||
|
||||
// Link (followed)
|
||||
if (fchmodat(dir, link_name, 0666, 0) == -1) {
|
||||
perror("fchmodat link 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0666)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Link (not followed)
|
||||
#ifdef __redox__
|
||||
if (fchmodat(dir, link_name, 0777, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
perror("fchmodat link 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, link_name, AT_SYMLINK_NOFOLLOW, 0777)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Directory
|
||||
// AT_EMPTY_PATH support is relatively new so this fails in CI.
|
||||
#if defined(__redox__) // || LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)
|
||||
if (fchmodat(dir, "", 0700, AT_EMPTY_PATH) == -1) {
|
||||
perror("fchmodat directory 0700");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, "", AT_EMPTY_PATH, 0700)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Directory (cwd)
|
||||
char old_cwd[PATH_MAX] = {0};
|
||||
if (!getcwd(old_cwd, PATH_MAX)) {
|
||||
perror("getcwd");
|
||||
goto closetemp;
|
||||
}
|
||||
if (chdir(template) == -1) {
|
||||
perror("chdir (temp dir)");
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
if (fchmodat(AT_FDCWD, "", 0777, AT_EMPTY_PATH) == -1) {
|
||||
perror("fchmodat cwd 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(AT_FDCWD, "", AT_EMPTY_PATH, 0777)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
if (chdir(old_cwd) == -1) {
|
||||
perror("chdir (old cwd)");
|
||||
goto closetemp;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = EXIT_SUCCESS;
|
||||
closetemp:
|
||||
close(dir);
|
||||
closelink:
|
||||
#ifdef __redox__
|
||||
close(linkfd);
|
||||
#endif
|
||||
rmfiles:
|
||||
close(filefd);
|
||||
unlink(file_path);
|
||||
unlink(link_path);
|
||||
rmtempdir:
|
||||
rmdir(template);
|
||||
bye:
|
||||
return status;
|
||||
}
|
||||
Reference in New Issue
Block a user