Skip to content

Commit 79dd92f

Browse files
committed
Auto merge of rust-lang#27930 - barosl:path_max, r=alexcrichton
This PR rewrites the code that previously relied on `PATH_MAX`. On my tests, even though the user gives the buffer length explicitly, both Linux's glibc and OS X's libc seems to obey the hard limit of `PATH_MAX` internally. So, to truly remove the limitation of `PATH_MAX`, the related system calls should be rewritten from scratch in Rust, which this PR does not try to do. However, eliminating the need of `PATH_MAX` is still a good idea for various reasons, such as: (1) they might change the implementation in the future, and (2) some platforms don't have a hard-coded `PATH_MAX`, such as GNU Hurd. More details are in the commit messages. Fixes rust-lang#27454. r? @alexcrichton
2 parents abfa081 + 6065678 commit 79dd92f

File tree

4 files changed

+59
-23
lines changed

4 files changed

+59
-23
lines changed

src/liblibc/lib.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -3920,6 +3920,8 @@ pub mod consts {
39203920
pub const _SC_XBS5_ILP32_OFFBIG : c_int = 126;
39213921
pub const _SC_XBS5_LPBIG_OFFBIG : c_int = 128;
39223922

3923+
pub const _PC_NAME_MAX: c_int = 3;
3924+
pub const _PC_PATH_MAX: c_int = 4;
39233925
}
39243926
#[cfg(target_os = "nacl")]
39253927
pub mod sysconf {
@@ -3928,6 +3930,9 @@ pub mod consts {
39283930
pub static _SC_SENDMSG_MAX_SIZE : c_int = 0;
39293931
pub static _SC_NPROCESSORS_ONLN : c_int = 1;
39303932
pub static _SC_PAGESIZE : c_int = 2;
3933+
3934+
pub const _PC_NAME_MAX: c_int = 3;
3935+
pub const _PC_PATH_MAX: c_int = 4;
39313936
}
39323937

39333938
#[cfg(target_os = "android")]
@@ -3963,6 +3968,9 @@ pub mod consts {
39633968
pub const _SC_STREAM_MAX : c_int = 27;
39643969
pub const _SC_TZNAME_MAX : c_int = 28;
39653970
pub const _SC_PAGESIZE : c_int = 39;
3971+
3972+
pub const _PC_NAME_MAX: c_int = 4;
3973+
pub const _PC_PATH_MAX: c_int = 5;
39663974
}
39673975
}
39683976

@@ -4433,6 +4441,9 @@ pub mod consts {
44334441
pub const _SC_SEM_VALUE_MAX : c_int = 50;
44344442
pub const _SC_SIGQUEUE_MAX : c_int = 51;
44354443
pub const _SC_TIMER_MAX : c_int = 52;
4444+
4445+
pub const _PC_NAME_MAX: c_int = 4;
4446+
pub const _PC_PATH_MAX: c_int = 5;
44364447
}
44374448
}
44384449

@@ -4868,6 +4879,9 @@ pub mod consts {
48684879
pub const _SC_SYNCHRONIZED_IO : c_int = 75;
48694880
pub const _SC_TIMER_MAX : c_int = 93;
48704881
pub const _SC_TIMERS : c_int = 94;
4882+
4883+
pub const _PC_NAME_MAX: c_int = 4;
4884+
pub const _PC_PATH_MAX: c_int = 5;
48714885
}
48724886
}
48734887

@@ -5379,6 +5393,9 @@ pub mod consts {
53795393
pub const _SC_TRACE_SYS_MAX : c_int = 129;
53805394
pub const _SC_TRACE_USER_EVENT_MAX : c_int = 130;
53815395
pub const _SC_PASS_MAX : c_int = 131;
5396+
5397+
pub const _PC_NAME_MAX: c_int = 4;
5398+
pub const _PC_PATH_MAX: c_int = 5;
53825399
}
53835400
}
53845401
}
@@ -5835,8 +5852,6 @@ pub mod funcs {
58355852
use types::os::arch::posix88::{gid_t, off_t, pid_t};
58365853
use types::os::arch::posix88::{ssize_t, uid_t};
58375854

5838-
pub const _PC_NAME_MAX: c_int = 4;
5839-
58405855
#[cfg(not(target_os = "nacl"))]
58415856
extern {
58425857
pub fn access(path: *const c_char, amode: c_int) -> c_int;

src/libstd/sys/unix/fs.rs

+30-15
Original file line numberDiff line numberDiff line change
@@ -376,13 +376,19 @@ impl fmt::Debug for File {
376376

377377
#[cfg(target_os = "macos")]
378378
fn get_path(fd: c_int) -> Option<PathBuf> {
379+
// FIXME: The use of PATH_MAX is generally not encouraged, but it
380+
// is inevitable in this case because OS X defines `fcntl` with
381+
// `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
382+
// alternatives. If a better method is invented, it should be used
383+
// instead.
379384
let mut buf = vec![0;libc::PATH_MAX as usize];
380385
let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
381386
if n == -1 {
382387
return None;
383388
}
384389
let l = buf.iter().position(|&c| c == 0).unwrap();
385390
buf.truncate(l as usize);
391+
buf.shrink_to_fit();
386392
Some(PathBuf::from(OsString::from_vec(buf)))
387393
}
388394

@@ -466,18 +472,27 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
466472
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
467473
let c_path = try!(cstr(p));
468474
let p = c_path.as_ptr();
469-
let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
470-
if len < 0 {
471-
len = 1024; // FIXME: read PATH_MAX from C ffi?
472-
}
473-
let mut buf: Vec<u8> = Vec::with_capacity(len as usize);
474-
unsafe {
475-
let n = try!(cvt({
476-
libc::readlink(p, buf.as_ptr() as *mut c_char, len as size_t)
477-
}));
478-
buf.set_len(n as usize);
475+
476+
let mut buf = Vec::with_capacity(256);
477+
478+
loop {
479+
let buf_read = try!(cvt(unsafe {
480+
libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity() as libc::size_t)
481+
})) as usize;
482+
483+
unsafe { buf.set_len(buf_read); }
484+
485+
if buf_read != buf.capacity() {
486+
buf.shrink_to_fit();
487+
488+
return Ok(PathBuf::from(OsString::from_vec(buf)));
489+
}
490+
491+
// Trigger the internal buffer resizing logic of `Vec` by requiring
492+
// more space than the current capacity. The length is guaranteed to be
493+
// the same as the capacity due to the if statement above.
494+
buf.reserve(1);
479495
}
480-
Ok(PathBuf::from(OsString::from_vec(buf)))
481496
}
482497

483498
pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
@@ -514,15 +529,15 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
514529

515530
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
516531
let path = try!(CString::new(p.as_os_str().as_bytes()));
517-
let mut buf = vec![0u8; 16 * 1024];
532+
let buf;
518533
unsafe {
519-
let r = c::realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _);
534+
let r = c::realpath(path.as_ptr(), ptr::null_mut());
520535
if r.is_null() {
521536
return Err(io::Error::last_os_error())
522537
}
538+
buf = CStr::from_ptr(r).to_bytes().to_vec();
539+
libc::free(r as *mut _);
523540
}
524-
let p = buf.iter().position(|i| *i == 0).unwrap();
525-
buf.truncate(p);
526541
Ok(PathBuf::from(OsString::from_vec(buf)))
527542
}
528543

src/libstd/sys/unix/os.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ use sys::c;
3030
use sys::fd;
3131
use vec;
3232

33-
const GETCWD_BUF_BYTES: usize = 2048;
3433
const TMPBUF_SZ: usize = 128;
3534

3635
/// Returns the platform-specific value of errno
@@ -94,11 +93,9 @@ pub fn error_string(errno: i32) -> String {
9493
}
9594

9695
pub fn getcwd() -> io::Result<PathBuf> {
97-
let mut buf = Vec::new();
98-
let mut n = GETCWD_BUF_BYTES;
96+
let mut buf = Vec::with_capacity(512);
9997
loop {
10098
unsafe {
101-
buf.reserve(n);
10299
let ptr = buf.as_mut_ptr() as *mut libc::c_char;
103100
if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() {
104101
let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
@@ -111,7 +108,12 @@ pub fn getcwd() -> io::Result<PathBuf> {
111108
return Err(error);
112109
}
113110
}
114-
n *= 2;
111+
112+
// Trigger the internal buffer resizing logic of `Vec` by requiring
113+
// more space than the current capacity.
114+
let cap = buf.capacity();
115+
buf.set_len(cap);
116+
buf.reserve(1);
115117
}
116118
}
117119
}

src/rt/rust_builtin.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,11 @@ const char * rust_current_exe()
341341
char **paths;
342342
size_t sz;
343343
int i;
344-
char buf[2*PATH_MAX], exe[2*PATH_MAX];
344+
/* If `PATH_MAX` is defined on the platform, `realpath` will truncate the
345+
* resolved path up to `PATH_MAX`. While this can make the resolution fail if
346+
* the executable is placed in a deep path, the usage of a buffer whose
347+
* length depends on `PATH_MAX` is still memory safe. */
348+
char buf[2*PATH_MAX], exe[PATH_MAX];
345349

346350
if (self != NULL)
347351
return self;

0 commit comments

Comments
 (0)