Merge branch 'unlinkat-symlinkat-linkat' into 'master'

add unlinkat(2), symlinkat(2), linkat(2) and expose openat(2)

See merge request redox-os/relibc!1212
This commit is contained in:
Jeremy Soller
2026-04-20 11:31:10 -06:00
6 changed files with 116 additions and 18 deletions
+12 -1
View File
@@ -80,6 +80,17 @@ pub unsafe extern "C" fn fcntl(fildes: c_int, cmd: c_int, mut __valist: ...) ->
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/open.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn open(path: *const c_char, oflag: c_int, mut __valist: ...) -> c_int {
unsafe { openat(AT_FDCWD, path, oflag, __valist) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/openat.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn openat(
fd: c_int,
path: *const c_char,
oflag: c_int,
mut __valist: ...
) -> c_int {
let mode = if oflag & O_CREAT == O_CREAT
/* || oflag & O_TMPFILE == O_TMPFILE */
{
@@ -89,7 +100,7 @@ pub unsafe extern "C" fn open(path: *const c_char, oflag: c_int, mut __valist: .
};
let path = unsafe { CStr::from_ptr(path) };
Sys::open(path, oflag, mode).or_minus_one_errno()
Sys::openat(fd, path, oflag, mode).or_minus_one_errno()
}
#[unsafe(no_mangle)]
+1
View File
@@ -31,5 +31,6 @@ 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_REMOVEDIR: c_int = 0x200;
+36 -1
View File
@@ -577,7 +577,7 @@ pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) ->
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getlogin.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getlogin() -> *mut c_char {
const LOGIN_LEN: usize = 256;
const LOGIN_LEN: usize = limits::LOGIN_NAME_MAX as usize;
static mut LOGIN: [c_char; LOGIN_LEN] = [0; LOGIN_LEN];
if getlogin_r((&raw mut LOGIN).cast(), LOGIN_LEN) == 0 {
(&raw mut LOGIN).cast()
@@ -707,6 +707,22 @@ 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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/linkat.html>.
#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lockf.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c_int {
@@ -1010,6 +1026,16 @@ 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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/symlinkat.html>.
#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sync.html>.
#[unsafe(no_mangle)]
pub extern "C" fn sync() {
@@ -1123,6 +1149,15 @@ pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
Sys::unlink(path).map(|()| 0).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlinkat.html>.
#[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 <https://pubs.opengroup.org/onlinepubs/009695399/functions/usleep.html>.
///
/// # Deprecation
+16 -13
View File
@@ -487,17 +487,12 @@ impl Pal for Sys {
}
fn link(path1: CStr, path2: CStr) -> Result<()> {
e_raw(unsafe {
syscall!(
LINKAT,
AT_FDCWD,
path1.as_ptr(),
AT_FDCWD,
path2.as_ptr(),
0
)
})
.map(|_| ())
Sys::linkat(AT_FDCWD, path1, AT_FDCWD, path2, 0)
}
fn linkat(fd1: c_int, path1: CStr, fd2: c_int, path2: CStr, flags: c_int) -> Result<()> {
e_raw(unsafe { syscall!(LINKAT, fd1, path1.as_ptr(), fd2, path2.as_ptr(), flags) })
.map(|_| ())
}
fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result<off_t> {
@@ -817,7 +812,11 @@ impl Pal for Sys {
}
fn symlink(path1: CStr, path2: CStr) -> Result<()> {
e_raw(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), AT_FDCWD, path2.as_ptr()) }).map(|_| ())
Sys::symlinkat(path1, AT_FDCWD, path2)
}
fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()> {
e_raw(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), fd, path2.as_ptr()) }).map(|_| ())
}
fn sync() -> Result<()> {
@@ -874,7 +873,11 @@ impl Pal for Sys {
}
fn unlink(path: CStr) -> Result<()> {
e_raw(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), 0) }).map(|_| ())
Sys::unlinkat(AT_FDCWD, path, 0)
}
fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()> {
e_raw(unsafe { syscall!(UNLINKAT, fd, path.as_ptr(), flags) }).map(|_| ())
}
fn waitpid(pid: pid_t, stat_loc: Option<Out<c_int>>, options: c_int) -> Result<pid_t> {
+9
View File
@@ -225,6 +225,9 @@ pub trait Pal {
/// Platform implementation of [`link()`](crate::header::unistd::link) from [`unistd.h`](crate::header::unistd).
fn link(path1: CStr, path2: CStr) -> Result<()>;
/// Platform implementation of [`linkat()`](crate::header::unistd::linkat) from [`unistd.h`](crate::header::unistd).
fn linkat(fd1: c_int, oldpath: CStr, fd2: c_int, newpath: CStr, flags: c_int) -> Result<()>;
/// Platform implementation of [`lseek()`](crate::header::unistd::lseek) from [`unistd.h`](crate::header::unistd).
fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result<off_t>;
@@ -370,6 +373,9 @@ pub trait Pal {
/// Platform implementation of [`symlink()`](crate::header::unistd::symlink) from [`unistd.h`](crate::header::unistd).
fn symlink(path1: CStr, path2: CStr) -> Result<()>;
/// Platform implementation of [`symlinkat()`](crate::header::unistd::symlinkat) from [`unistd.h`](crate::header::unistd).
fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()>;
/// Platform implementation of [`sync()`](crate::header::unistd::sync) from [`unistd.h`](crate::header::unistd).
fn sync() -> Result<()>;
@@ -400,6 +406,9 @@ pub trait Pal {
/// Platform implementation of [`unlink()`](crate::header::unistd::unlink) from [`unistd.h`](crate::header::unistd).
fn unlink(path: CStr) -> Result<()>;
/// Platform implementation of [`unlinkat()`](crate::header::unistd::unlinkat) from [`unistd.h`](crate::header::unistd).
fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()>;
/// Platform implementation of [`waitpid()`](crate::header::sys_wait::waitpid) from [`sys/wait.h`](crate::header::sys_wait).
fn waitpid(pid: pid_t, stat_loc: Option<Out<c_int>>, options: c_int) -> Result<pid_t>;
+42 -3
View File
@@ -32,8 +32,8 @@ use crate::{
ENOSYS, EOPNOTSUPP, EPERM,
},
fcntl::{
self, AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW, F_GETLK, F_OFD_GETLK, F_OFD_SETLK,
F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, flock,
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,
},
limits,
pthread::{pthread_cancel, pthread_create},
@@ -772,6 +772,29 @@ impl Pal for Sys {
Ok(())
}
fn linkat(fd1: c_int, oldpath: CStr, fd2: c_int, newpath: CStr, flags: c_int) -> Result<()> {
// make sure the flags passed are valid.
// valid states: AT_SYMLINK_FOLLOW, or 0.
if (flags & !(AT_SYMLINK_FOLLOW)) != 0 {
return Err(Errno(EINVAL));
}
let newpath = newpath.to_str().map_err(|_| Errno(EINVAL))?;
// By default, we don't follow the symlink if there is one.
// We only follow it if AT_SYMLINK_FOLLOW is passed in flags.
// We represent this by setting O_NOFOLLOW by default, and clearing it
// if AT_SYMLINK_FOLLOW is present.
let mut oflags = fcntl::O_PATH | fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW;
if (flags & AT_SYMLINK_FOLLOW) == AT_SYMLINK_FOLLOW {
oflags &= !fcntl::O_NOFOLLOW;
}
let file = File::openat(fd1, oldpath, oflags)?;
let newpath = openat2_path(fd2, newpath, 0)?;
syscall::flink(*file as usize, newpath)?;
Ok(())
}
fn lseek(fd: c_int, offset: off_t, whence: c_int) -> Result<off_t> {
Ok(syscall::lseek(fd as usize, offset as isize, whence as usize)? as off_t)
}
@@ -1240,7 +1263,12 @@ impl Pal for Sys {
}
fn symlink(path1: CStr, path2: CStr) -> Result<()> {
let mut file = File::create(
Sys::symlinkat(path1, AT_FDCWD, path2)
}
fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()> {
let mut file = File::createat(
fd,
path2,
fcntl::O_WRONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
0o777,
@@ -1511,6 +1539,17 @@ impl Pal for Sys {
Ok(())
}
fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()> {
if (flags & !AT_REMOVEDIR) != 0 {
return Err(Errno(EINVAL));
}
let path = path.to_str().map_err(|_| Errno(EINVAL))?;
let path = openat2_path(fd, path, 0)?;
let canon = canonicalize(&path)?;
redox_rt::sys::unlink(&canon, flags.try_into().map_err(|_| Errno(EINVAL))?)?;
Ok(())
}
fn waitpid(pid: pid_t, stat_loc: Option<Out<'_, c_int>>, options: c_int) -> Result<pid_t> {
let res = None;
let mut status = 0;