From 378a66dd1837a634ef714166f657cb7111c291ec Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Wed, 2 Feb 2022 19:40:50 -0800 Subject: Remove `PATH_MAX` restriction from `with_nix_path` Signed-off-by: Alex Saveau --- src/lib.rs | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index 0cfd3fb4..68093829 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,19 +156,11 @@ feature! { #[allow(missing_docs)] pub mod unistd; -/* - * - * ===== Result / Error ===== - * - */ - -use libc::PATH_MAX; - -use std::{ptr, result, slice}; -use std::ffi::{CStr, OsStr}; +use std::ffi::{CStr, CString, OsStr}; use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; use std::path::{Path, PathBuf}; +use std::{ptr, result, slice}; use errno::Errno; @@ -242,12 +234,9 @@ impl NixPath for CStr { } fn with_nix_path(&self, f: F) -> Result - where F: FnOnce(&CStr) -> T { - // Equivalence with the [u8] impl. - if self.len() >= PATH_MAX as usize { - return Err(Errno::ENAMETOOLONG) - } - + where + F: FnOnce(&CStr) -> T, + { Ok(f(self)) } } @@ -265,11 +254,19 @@ impl NixPath for [u8] { where F: FnOnce(&CStr) -> T, { - if self.len() >= PATH_MAX as usize { - return Err(Errno::ENAMETOOLONG); + // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path + // longer than ~300 bytes. See the the PR description to get stats for your own machine. + // https://github.com/nix-rust/nix/pull/1656 + // + // By being smaller than a memory page, we also avoid the compiler inserting a probe frame: + // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html + const MAX_STACK_ALLOCATION: usize = 1024; + + if self.len() >= MAX_STACK_ALLOCATION { + return with_nix_path_allocating(self, f); } - let mut buf = MaybeUninit::<[u8; PATH_MAX as usize]>::uninit(); + let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); let buf_ptr = buf.as_mut_ptr() as *mut u8; unsafe { @@ -284,6 +281,18 @@ impl NixPath for [u8] { } } +#[cold] +#[inline(never)] +fn with_nix_path_allocating(from: &[u8], f: F) -> Result +where + F: FnOnce(&CStr) -> T, +{ + match CString::new(from) { + Ok(s) => Ok(f(&s)), + Err(_) => Err(Errno::EINVAL), + } +} + impl NixPath for Path { fn is_empty(&self) -> bool { NixPath::is_empty(self.as_os_str()) -- cgit v1.2.3