diff --git a/src/process/chdir.rs b/src/process/chdir.rs index 8c3739799..fb58112cb 100644 --- a/src/process/chdir.rs +++ b/src/process/chdir.rs @@ -96,3 +96,48 @@ fn _getcwd(mut buffer: Vec) -> io::Result { } } } + +/// Whether `getcwd_raw` uses the existing allocation-free Linux-raw syscall. +/// +/// This is build availability only, not a cwd, resource, or authority witness. +#[cfg(all(feature = "alloc", feature = "fs"))] +pub const GETCWD_RAW_SUPPORTED: bool = cfg!(linux_raw); + +/// Read the current directory into caller-provided storage without allocating. +/// +/// On Linux-raw, returns the sealed buffer output for bytes including the NUL. +/// Initialized byte slices return their initialized byte count. +/// Empty or insufficient storage is an error. This never grows the buffer or +/// falls back through libc or procfs. Other backends return `Errno::NOSYS`. +#[cfg(all(feature = "alloc", feature = "fs"))] +#[allow(unsafe_code)] +#[inline] +pub fn getcwd_raw>(buf: Buf) -> io::Result { + #[cfg(linux_raw)] + { + let mut buf = buf; + let (ptr, capacity) = buf.parts_mut(); + if capacity == 0 { + return Err(io::Errno::RANGE); + } + // SAFETY: the sealed Buffer implementation supplies a valid uniquely + // borrowed byte region for capacity bytes. MaybeUninit has the same + // layout/alignment as u8; this borrow neither reads nor owns its bytes. + let uninit = unsafe { + core::slice::from_raw_parts_mut(ptr.cast::>(), capacity) + }; + let initialized = backend::process::syscalls::getcwd(uninit)?; + if initialized == 0 || initialized > capacity { + return Err(io::Errno::IO); + } + // SAFETY: successful Linux getcwd initialized exactly this bounded + // prefix, including NUL. The syscall borrow ended before this sealed + // Buffer operation publishes its initialized prefix; no growth occurs. + unsafe { Ok(buf.assume_init(initialized)) } + } + #[cfg(not(linux_raw))] + { + let _ = buf; + Err(io::Errno::NOSYS) + } +} diff --git a/tests/process/working_directory.rs b/tests/process/working_directory.rs index afa7fe584..174e4e5ba 100644 --- a/tests/process/working_directory.rs +++ b/tests/process/working_directory.rs @@ -48,3 +48,23 @@ fn test_changing_working_directory() { "The cwd wasn't changed back to the its original position" ); } + +#[test] +fn test_getcwd_raw() { + let mut buf = [0u8; 4096]; + if rustix::process::GETCWD_RAW_SUPPORTED { + let len = rustix::process::getcwd_raw(&mut buf[..]).expect("getcwd_raw should succeed"); + let expected = rustix::process::getcwd(Vec::new()).expect("getcwd should succeed"); + assert_eq!(&buf[..len], expected.to_bytes_with_nul()); + let mut short = [0u8; 1]; + assert_eq!( + rustix::process::getcwd_raw(&mut short[..]), + Err(rustix::io::Errno::RANGE) + ); + } else { + assert_eq!( + rustix::process::getcwd_raw(&mut buf[..]), + Err(rustix::io::Errno::NOSYS) + ); + } +}