Skip to content

Commit 378a66d

Browse files
committed
Remove PATH_MAX restriction from with_nix_path
Signed-off-by: Alex Saveau <[email protected]>
1 parent 15af9b6 commit 378a66d

File tree

2 files changed

+31
-19
lines changed

2 files changed

+31
-19
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6464
Because of this change, you now need `use std::iter::Extend` to call `extend`
6565
on a `SigSet`.
6666
(#[1553](https://github.com/nix-rust/nix/pull/1553))
67+
- Removed the the `PATH_MAX` restriction from APIs accepting paths. Paths
68+
will now be allocated on the heap if they are too long. In addition, large
69+
instruction count improvements (~30x) were made to path handling.
6770

6871
### Fixed
6972

src/lib.rs

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -156,19 +156,11 @@ feature! {
156156
#[allow(missing_docs)]
157157
pub mod unistd;
158158

159-
/*
160-
*
161-
* ===== Result / Error =====
162-
*
163-
*/
164-
165-
use libc::PATH_MAX;
166-
167-
use std::{ptr, result, slice};
168-
use std::ffi::{CStr, OsStr};
159+
use std::ffi::{CStr, CString, OsStr};
169160
use std::mem::MaybeUninit;
170161
use std::os::unix::ffi::OsStrExt;
171162
use std::path::{Path, PathBuf};
163+
use std::{ptr, result, slice};
172164

173165
use errno::Errno;
174166

@@ -242,12 +234,9 @@ impl NixPath for CStr {
242234
}
243235

244236
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
245-
where F: FnOnce(&CStr) -> T {
246-
// Equivalence with the [u8] impl.
247-
if self.len() >= PATH_MAX as usize {
248-
return Err(Errno::ENAMETOOLONG)
249-
}
250-
237+
where
238+
F: FnOnce(&CStr) -> T,
239+
{
251240
Ok(f(self))
252241
}
253242
}
@@ -265,11 +254,19 @@ impl NixPath for [u8] {
265254
where
266255
F: FnOnce(&CStr) -> T,
267256
{
268-
if self.len() >= PATH_MAX as usize {
269-
return Err(Errno::ENAMETOOLONG);
257+
// The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
258+
// longer than ~300 bytes. See the the PR description to get stats for your own machine.
259+
// https://github.com/nix-rust/nix/pull/1656
260+
//
261+
// By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
262+
// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
263+
const MAX_STACK_ALLOCATION: usize = 1024;
264+
265+
if self.len() >= MAX_STACK_ALLOCATION {
266+
return with_nix_path_allocating(self, f);
270267
}
271268

272-
let mut buf = MaybeUninit::<[u8; PATH_MAX as usize]>::uninit();
269+
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
273270
let buf_ptr = buf.as_mut_ptr() as *mut u8;
274271

275272
unsafe {
@@ -284,6 +281,18 @@ impl NixPath for [u8] {
284281
}
285282
}
286283

284+
#[cold]
285+
#[inline(never)]
286+
fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
287+
where
288+
F: FnOnce(&CStr) -> T,
289+
{
290+
match CString::new(from) {
291+
Ok(s) => Ok(f(&s)),
292+
Err(_) => Err(Errno::EINVAL),
293+
}
294+
}
295+
287296
impl NixPath for Path {
288297
fn is_empty(&self) -> bool {
289298
NixPath::is_empty(self.as_os_str())

0 commit comments

Comments
 (0)