diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 5cd806dc9e3f3..c4d2dc838942a 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -3920,6 +3920,8 @@ pub mod consts { pub const _SC_XBS5_ILP32_OFFBIG : c_int = 126; pub const _SC_XBS5_LPBIG_OFFBIG : c_int = 128; + pub const _PC_NAME_MAX: c_int = 3; + pub const _PC_PATH_MAX: c_int = 4; } #[cfg(target_os = "nacl")] pub mod sysconf { @@ -3928,6 +3930,9 @@ pub mod consts { pub static _SC_SENDMSG_MAX_SIZE : c_int = 0; pub static _SC_NPROCESSORS_ONLN : c_int = 1; pub static _SC_PAGESIZE : c_int = 2; + + pub const _PC_NAME_MAX: c_int = 3; + pub const _PC_PATH_MAX: c_int = 4; } #[cfg(target_os = "android")] @@ -3963,6 +3968,9 @@ pub mod consts { pub const _SC_STREAM_MAX : c_int = 27; pub const _SC_TZNAME_MAX : c_int = 28; pub const _SC_PAGESIZE : c_int = 39; + + pub const _PC_NAME_MAX: c_int = 4; + pub const _PC_PATH_MAX: c_int = 5; } } @@ -4433,6 +4441,9 @@ pub mod consts { pub const _SC_SEM_VALUE_MAX : c_int = 50; pub const _SC_SIGQUEUE_MAX : c_int = 51; pub const _SC_TIMER_MAX : c_int = 52; + + pub const _PC_NAME_MAX: c_int = 4; + pub const _PC_PATH_MAX: c_int = 5; } } @@ -4868,6 +4879,9 @@ pub mod consts { pub const _SC_SYNCHRONIZED_IO : c_int = 75; pub const _SC_TIMER_MAX : c_int = 93; pub const _SC_TIMERS : c_int = 94; + + pub const _PC_NAME_MAX: c_int = 4; + pub const _PC_PATH_MAX: c_int = 5; } } @@ -5379,6 +5393,9 @@ pub mod consts { pub const _SC_TRACE_SYS_MAX : c_int = 129; pub const _SC_TRACE_USER_EVENT_MAX : c_int = 130; pub const _SC_PASS_MAX : c_int = 131; + + pub const _PC_NAME_MAX: c_int = 4; + pub const _PC_PATH_MAX: c_int = 5; } } } @@ -5835,8 +5852,6 @@ pub mod funcs { use types::os::arch::posix88::{gid_t, off_t, pid_t}; use types::os::arch::posix88::{ssize_t, uid_t}; - pub const _PC_NAME_MAX: c_int = 4; - #[cfg(not(target_os = "nacl"))] extern { pub fn access(path: *const c_char, amode: c_int) -> c_int; diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index cbbdd223dc2b9..0eebe5af9197d 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -376,6 +376,11 @@ impl fmt::Debug for File { #[cfg(target_os = "macos")] fn get_path(fd: c_int) -> Option { + // FIXME: The use of PATH_MAX is generally not encouraged, but it + // is inevitable in this case because OS X defines `fcntl` with + // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no + // alternatives. If a better method is invented, it should be used + // instead. let mut buf = vec![0;libc::PATH_MAX as usize]; let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; if n == -1 { @@ -383,6 +388,7 @@ impl fmt::Debug for File { } let l = buf.iter().position(|&c| c == 0).unwrap(); buf.truncate(l as usize); + buf.shrink_to_fit(); Some(PathBuf::from(OsString::from_vec(buf))) } @@ -466,18 +472,27 @@ pub fn rmdir(p: &Path) -> io::Result<()> { pub fn readlink(p: &Path) -> io::Result { let c_path = try!(cstr(p)); let p = c_path.as_ptr(); - let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; - if len < 0 { - len = 1024; // FIXME: read PATH_MAX from C ffi? - } - let mut buf: Vec = Vec::with_capacity(len as usize); - unsafe { - let n = try!(cvt({ - libc::readlink(p, buf.as_ptr() as *mut c_char, len as size_t) - })); - buf.set_len(n as usize); + + let mut buf = Vec::with_capacity(256); + + loop { + let buf_read = try!(cvt(unsafe { + libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity() as libc::size_t) + })) as usize; + + unsafe { buf.set_len(buf_read); } + + if buf_read != buf.capacity() { + buf.shrink_to_fit(); + + return Ok(PathBuf::from(OsString::from_vec(buf))); + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. The length is guaranteed to be + // the same as the capacity due to the if statement above. + buf.reserve(1); } - Ok(PathBuf::from(OsString::from_vec(buf))) } pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { @@ -514,15 +529,15 @@ pub fn lstat(p: &Path) -> io::Result { pub fn canonicalize(p: &Path) -> io::Result { let path = try!(CString::new(p.as_os_str().as_bytes())); - let mut buf = vec![0u8; 16 * 1024]; + let buf; unsafe { - let r = c::realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _); + let r = c::realpath(path.as_ptr(), ptr::null_mut()); if r.is_null() { return Err(io::Error::last_os_error()) } + buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); } - let p = buf.iter().position(|i| *i == 0).unwrap(); - buf.truncate(p); Ok(PathBuf::from(OsString::from_vec(buf))) } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 2b6b50a1a56d7..fa31ac682d40b 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -30,7 +30,6 @@ use sys::c; use sys::fd; use vec; -const GETCWD_BUF_BYTES: usize = 2048; const TMPBUF_SZ: usize = 128; /// Returns the platform-specific value of errno @@ -94,11 +93,9 @@ pub fn error_string(errno: i32) -> String { } pub fn getcwd() -> io::Result { - let mut buf = Vec::new(); - let mut n = GETCWD_BUF_BYTES; + let mut buf = Vec::with_capacity(512); loop { unsafe { - buf.reserve(n); let ptr = buf.as_mut_ptr() as *mut libc::c_char; if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() { 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 { return Err(error); } } - n *= 2; + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); } } } diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 945057f86122e..37ce30d7066f7 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -341,7 +341,11 @@ const char * rust_current_exe() char **paths; size_t sz; int i; - char buf[2*PATH_MAX], exe[2*PATH_MAX]; + /* If `PATH_MAX` is defined on the platform, `realpath` will truncate the + * resolved path up to `PATH_MAX`. While this can make the resolution fail if + * the executable is placed in a deep path, the usage of a buffer whose + * length depends on `PATH_MAX` is still memory safe. */ + char buf[2*PATH_MAX], exe[PATH_MAX]; if (self != NULL) return self;