diff --git a/src/header/fcntl/mod.rs b/src/header/fcntl/mod.rs index 6a4db2fafc..28455c94cf 100644 --- a/src/header/fcntl/mod.rs +++ b/src/header/fcntl/mod.rs @@ -80,6 +80,17 @@ pub unsafe extern "C" fn fcntl(fildes: c_int, cmd: c_int, mut __valist: ...) -> /// See . #[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 . +#[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)] diff --git a/src/header/fcntl/redox.rs b/src/header/fcntl/redox.rs index 7ac6b33e22..c374743d51 100644 --- a/src/header/fcntl/redox.rs +++ b/src/header/fcntl/redox.rs @@ -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; diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index d3bc890f48..90d544f567 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -575,7 +575,7 @@ pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> /// See . #[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() @@ -705,6 +705,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 . +#[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 { @@ -1008,6 +1024,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 . +#[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() { @@ -1121,6 +1147,15 @@ 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 b53c95b5c6..59a6adf60a 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -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 { @@ -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>, options: c_int) -> Result { diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index 3b8c119f85..3ef0dc1cac 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -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; @@ -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>, options: c_int) -> Result; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index dc04059870..f04aa84416 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -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 { 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>, options: c_int) -> Result { let res = None; let mut status = 0;