diff --git a/src/header/fcntl/mod.rs b/src/header/fcntl/mod.rs index 28455c94cf..ec37906cec 100644 --- a/src/header/fcntl/mod.rs +++ b/src/header/fcntl/mod.rs @@ -2,14 +2,18 @@ //! //! See . -use core::num::NonZeroU64; +use core::{num::NonZeroU64, slice}; use crate::{ c_str::CStr, - error::ResultExt, + error::{Errno, ResultExt}, + header::errno::ENAMETOOLONG, platform::{ Pal, Sys, - types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t}, + types::{ + c_char, c_int, c_short, c_ulonglong, gid_t, mode_t, off_t, pid_t, size_t, ssize_t, + uid_t, + }, }, }; @@ -122,3 +126,98 @@ pub unsafe extern "C" fn posix_fallocate(fd: c_int, offset: off_t, length: off_t .map(|e| e.0) .unwrap_or_default() } + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn readlinkat( + dirfd: c_int, + pathname: *const c_char, + buf: *mut c_char, + len: size_t, +) -> ssize_t { + let pathname = unsafe { CStr::from_ptr(pathname) }; + let buf = unsafe { slice::from_raw_parts_mut(buf.cast(), len) }; + Sys::readlinkat(dirfd, pathname, buf) + .map(|read| { + read.try_into() + .map_err(|_| Errno(ENAMETOOLONG)) + .or_minus_one_errno() + }) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn linkat( + fd1: c_int, + path1: *const c_char, + fd2: c_int, + path2: *const c_char, + flags: c_int, +) -> c_int { + let path1 = unsafe { CStr::from_ptr(path1) }; + let path2 = unsafe { CStr::from_ptr(path2) }; + Sys::linkat(fd1, path1, fd2, path2, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn symlinkat(path1: *const c_char, fd: c_int, path2: *const c_char) -> c_int { + let path1 = unsafe { CStr::from_ptr(path1) }; + let path2 = unsafe { CStr::from_ptr(path2) }; + Sys::symlinkat(path1, fd, path2) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn unlinkat(fd: c_int, path: *const c_char, flags: c_int) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::unlinkat(fd, path, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fexecve( + fd: c_int, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + unsafe { Sys::fexecve(fd, argv, envp) } + .map(|()| unreachable!()) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn faccessat( + fd: c_int, + path: *const c_char, + mode: c_int, + flags: c_int, +) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::faccessat(fd, path, mode, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fchownat( + fd: c_int, + path: *const c_char, + owner: uid_t, + group: gid_t, + flags: c_int, +) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::fchownat(fd, path, owner, group, flags) + .map(|()| 0) + .or_minus_one_errno() +} diff --git a/src/header/fcntl/redox.rs b/src/header/fcntl/redox.rs index c374743d51..0d54c33883 100644 --- a/src/header/fcntl/redox.rs +++ b/src/header/fcntl/redox.rs @@ -30,7 +30,11 @@ 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_SYMLINK_FOLLOW: c_int = 0x2000; -pub const AT_EMPTY_PATH: c_int = 0x4000; +pub const AT_SYMLINK_NOFOLLOW: c_int = 0x200; pub const AT_REMOVEDIR: c_int = 0x200; +// Used by linkat() +pub const AT_SYMLINK_FOLLOW: c_int = 0x2000; +// nonstandard extension, but likely to be in a future standard +pub const AT_EMPTY_PATH: c_int = 0x4000; +// only used for faccessat() +pub const AT_EACCESS: c_int = 0x400; diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index 21df741655..e0075bb7de 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -17,8 +17,7 @@ use crate::{ header::{ bits_timespec::timespec, crypt::{crypt_data, crypt_r}, - errno::{self, ENAMETOOLONG}, - fcntl, limits, + errno, fcntl, limits, stdlib::getenv, sys_ioctl, sys_resource, sys_select::timeval, @@ -45,13 +44,14 @@ pub use crate::header::stdio::ctermid; #[allow(deprecated)] pub use crate::header::stdio::cuserid; -// TODO: implement and reexport fcntl functions: -//pub use crate::header::fcntl::{faccessat, fchownat, fexecve, linkat, readlinkat, symlinkat, unlinkat}; - use super::{ errno::{E2BIG, EINVAL, ENOMEM}, stdio::snprintf, }; +// Export *at functions because unistd.h includes them. +pub use crate::header::fcntl::{ + faccessat, fchownat, fexecve, linkat, readlinkat, symlinkat, unlinkat, +}; use crate::header::signal::{sigprocmask, sigset_t, sigsuspend}; @@ -337,18 +337,6 @@ pub unsafe extern "C" fn execve( .or_minus_one_errno() } -/// See . -#[unsafe(no_mangle)] -pub unsafe extern "C" fn fexecve( - fd: c_int, - argv: *const *mut c_char, - envp: *const *mut c_char, -) -> c_int { - unsafe { Sys::fexecve(fd, argv, envp) } - .map(|()| unreachable!()) - .or_minus_one_errno() -} - const PATH_SEPARATOR: u8 = b':'; /// See . @@ -712,22 +700,6 @@ pub unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_i Sys::link(path1, path2).map(|()| 0).or_minus_one_errno() } -/// See . -#[unsafe(no_mangle)] -pub unsafe extern "C" fn linkat( - fd1: c_int, - path1: *const c_char, - fd2: c_int, - path2: *const c_char, - flags: c_int, -) -> c_int { - let path1 = unsafe { CStr::from_ptr(path1) }; - let path2 = unsafe { CStr::from_ptr(path2) }; - Sys::linkat(fd1, path1, fd2, path2, flags) - .map(|()| 0) - .or_minus_one_errno() -} - /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c_int { @@ -876,25 +848,6 @@ pub unsafe extern "C" fn readlink( .or_minus_one_errno() } -/// See . -#[unsafe(no_mangle)] -pub unsafe extern "C" fn readlinkat( - dirfd: c_int, - pathname: *const c_char, - buf: *mut c_char, - len: size_t, -) -> ssize_t { - let pathname = unsafe { CStr::from_ptr(pathname) }; - let buf = unsafe { slice::from_raw_parts_mut(buf.cast(), len) }; - Sys::readlinkat(dirfd, pathname, buf) - .map(|read| { - read.try_into() - .map_err(|_| Errno(ENAMETOOLONG)) - .or_minus_one_errno() - }) - .or_minus_one_errno() -} - /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int { @@ -1031,16 +984,6 @@ pub unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> Sys::symlink(path1, path2).map(|()| 0).or_minus_one_errno() } -/// See . -#[unsafe(no_mangle)] -pub unsafe extern "C" fn symlinkat(path1: *const c_char, fd: c_int, path2: *const c_char) -> c_int { - let path1 = unsafe { CStr::from_ptr(path1) }; - let path2 = unsafe { CStr::from_ptr(path2) }; - Sys::symlinkat(path1, fd, path2) - .map(|()| 0) - .or_minus_one_errno() -} - /// See . #[unsafe(no_mangle)] pub extern "C" fn sync() { @@ -1155,15 +1098,6 @@ pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int { Sys::unlink(path).map(|()| 0).or_minus_one_errno() } -/// See . -#[unsafe(no_mangle)] -pub unsafe extern "C" fn unlinkat(fd: c_int, path: *const c_char, flags: c_int) -> c_int { - let path = unsafe { CStr::from_ptr(path) }; - Sys::unlinkat(fd, path, flags) - .map(|()| 0) - .or_minus_one_errno() -} - /// See . /// /// # Deprecation diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 59a6adf60a..350818f02f 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -81,14 +81,12 @@ impl Sys { } impl Pal for Sys { - #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] fn access(path: CStr, mode: c_int) -> Result<()> { - e_raw(unsafe { syscall!(ACCESS, path.as_ptr(), mode) }).map(|_| ()) + Sys::faccessat(AT_FDCWD, path, mode, 0) } - #[cfg(target_arch = "aarch64")] - fn access(path: CStr, mode: c_int) -> Result<()> { - e_raw(unsafe { syscall!(FACCESSAT, AT_FDCWD, path.as_ptr(), mode, 0) }).map(|_| ()) + fn faccessat(fd: c_int, path: CStr, amode: c_int, flags: c_int) -> Result<()> { + e_raw(unsafe { syscall!(FACCESSAT, fd, path.as_ptr(), amode, flags) }).map(|_| ()) } unsafe fn brk(addr: *mut c_void) -> Result<*mut c_void> { @@ -104,11 +102,14 @@ impl Pal for Sys { } fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> { - let flags: c_int = 0; + Sys::fchownat(AT_FDCWD, path, owner, group, 0) + } + + fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()> { e_raw(unsafe { syscall!( FCHOWNAT, - AT_FDCWD, + fildes, path.as_ptr(), owner as u32, group as u32, diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index 3ef0dc1cac..677b3924b4 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -37,6 +37,9 @@ pub trait Pal { /// Platform implementation of [`access()`](crate::header::unistd::access) from [`unistd.h`](crate::header::unistd). fn access(path: CStr, mode: c_int) -> Result<()>; + /// Platform implementation of [`faccessat()`](crate::header::unistd::faccessat) from [`unistd.h`](crate::header::unistd). + fn faccessat(fd: c_int, path: CStr, amode: c_int, flags: c_int) -> Result<()>; + /// Platform implementation of [`brk()`](crate::header::unistd::brk) from [`unistd.h`](crate::header::unistd). unsafe fn brk(addr: *mut c_void) -> Result<*mut c_void>; @@ -95,6 +98,9 @@ pub trait Pal { /// Platform implementation of [`fchown()`](crate::header::unistd::fchown) from [`unistd.h`](crate::header::unistd). fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()>; + /// Platform implementation of [`fchownat()`](crate::header::unistd::fchownat) from [`unistd.h`](crate::header::unistd). + fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()>; + /// Platform implementation of [`fdatasync()`](crate::header::unistd::fdatasync) from [`unistd.h`](crate::header::unistd). fn fdatasync(fildes: c_int) -> Result<()>; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index f04aa84416..ea1281a11a 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -32,8 +32,9 @@ use crate::{ ENOSYS, EOPNOTSUPP, EPERM, }, fcntl::{ - self, AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_FOLLOW, AT_SYMLINK_NOFOLLOW, - F_GETLK, F_OFD_GETLK, F_OFD_SETLK, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, flock, + self, AT_EACCESS, AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_FOLLOW, + AT_SYMLINK_NOFOLLOW, F_GETLK, F_OFD_GETLK, F_OFD_SETLK, F_RDLCK, F_SETLK, F_SETLKW, + F_UNLCK, F_WRLCK, flock, }, limits, pthread::{pthread_cancel, pthread_create}, @@ -107,7 +108,15 @@ pub struct Sys; impl Pal for Sys { fn access(path: CStr, mode: c_int) -> Result<()> { - let fd = FdGuard::new(Sys::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC, 0)? as usize); + Sys::faccessat(AT_FDCWD, path, mode, 0) + } + + fn faccessat(fd: c_int, path: CStr, mode: c_int, flags: c_int) -> Result<()> { + let fd = FdGuard::new(Sys::openat(fd, path, fcntl::O_PATH | fcntl::O_CLOEXEC, 0)? as usize); + + if (flags & !(AT_EACCESS)) != 0 { + return Err(Errno(EINVAL)); + } if mode == F_OK { return Ok(()); @@ -117,11 +126,22 @@ impl Pal for Sys { fd.fstat(&mut stat)?; - let Resugid { ruid, rgid, .. } = redox_rt::sys::posix_getresugid(); + let Resugid { + ruid, + rgid, + euid, + egid, + .. + } = redox_rt::sys::posix_getresugid(); + let (uid, gid) = if (flags & AT_EACCESS) == AT_EACCESS { + (euid, egid) + } else { + (ruid, rgid) + }; - let perms = (if stat.st_uid == ruid { + let perms = (if stat.st_uid == uid { stat.st_mode >> (3 * 2) - } else if stat.st_gid == rgid { + } else if stat.st_gid == gid { stat.st_mode >> (3 * 1) } else { stat.st_mode @@ -277,7 +297,7 @@ impl Pal for Sys { fn fchmodat(dirfd: c_int, path: Option, 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)); + return Err(Errno(EINVAL)); } let mut path = path .and_then(|cs| str::from_utf8(cs.to_bytes()).ok()) @@ -306,6 +326,21 @@ impl Pal for Sys { Ok(()) } + fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()> { + const MASK: c_int = !(fcntl::AT_SYMLINK_NOFOLLOW | fcntl::AT_EMPTY_PATH); + if MASK & flags != 0 { + return Err(Errno(EINVAL)); + } + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + let file = openat2(fildes, path, flags, 0)?; + libredox::fchown( + *file as usize, + owner.try_into().map_err(|_| Errno(EINVAL))?, + group.try_into().map_err(|_| Errno(EINVAL))?, + )?; + Ok(()) + } + fn fcntl(fd: c_int, cmd: c_int, args: c_ulonglong) -> Result { match cmd { F_SETLK | F_OFD_SETLK => {