Implement mkdirat

Linux's variant uses the syscall as intended. Redox's variant uses fpath to build a path to pass to mkdir from the file descriptor plus the file name, which isn't atomic due to the fpath lookup being subject to TOCTOU when paired with mkdir.
This commit is contained in:
Connor-GH
2026-01-08 17:56:49 -06:00
parent d5bf6db1b6
commit 75ca2cf29e
4 changed files with 34 additions and 1 deletions
+9
View File
@@ -160,6 +160,15 @@ pub unsafe extern "C" fn lstat(path: *const c_char, buf: *mut stat) -> c_int {
res
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdirat.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mkdirat(dirfd: c_int, path: *const c_char, mode: mode_t) -> c_int {
let path = CStr::from_ptr(path);
Sys::mkdirat(dirfd, path, mode)
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdir.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mkdir(path: *const c_char, mode: mode_t) -> c_int {
+5 -1
View File
@@ -502,8 +502,12 @@ impl Pal for Sys {
e_raw(unsafe { syscall!(LSEEK, fildes, offset, whence) }).map(|o| o as off_t)
}
fn mkdirat(dir_fildes: c_int, path: CStr, mode: mode_t) -> Result<()> {
e_raw(unsafe { syscall!(MKDIRAT, dir_fildes, path.as_ptr(), mode) }).map(|_| ())
}
fn mkdir(path: CStr, mode: mode_t) -> Result<()> {
e_raw(unsafe { syscall!(MKDIRAT, AT_FDCWD, path.as_ptr(), mode) }).map(|_| ())
Sys::mkdirat(AT_FDCWD, path, mode)
}
fn mknodat(dir_fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> Result<()> {
+2
View File
@@ -170,6 +170,8 @@ pub trait Pal {
fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result<off_t>;
fn mkdirat(fildes: c_int, path: CStr, mode: mode_t) -> Result<()>;
fn mkdir(path: CStr, mode: mode_t) -> Result<()>;
fn mkfifo(path: CStr, mode: mode_t) -> Result<()>;
+18
View File
@@ -680,6 +680,24 @@ impl Pal for Sys {
Ok(syscall::lseek(fd as usize, offset as isize, whence as usize)? as off_t)
}
fn mkdirat(dir_fd: c_int, path_name: CStr, mode: mode_t) -> Result<()> {
let mut dir_path_buf = [0; 4096];
let res = Sys::fpath(dir_fd, &mut dir_path_buf)?;
let dir_path = str::from_utf8(&dir_path_buf[..res as usize]).map_err(|_| Errno(EBADR))?;
let resource_path =
path::canonicalize_using_cwd(Some(&dir_path), &path_name.to_string_lossy())
// Since parent_dir_path is resolved by fpath, it is more likely that
// the problem was with path.
.ok_or(Errno(ENOENT))?;
Sys::mkdir(
CStr::borrow(&CString::new(resource_path.as_bytes()).unwrap()),
mode,
)
}
fn mkdir(path: CStr, mode: mode_t) -> Result<()> {
File::create(
path,