From c8a5b1368e78979eda0420a95381fe6f61f1a086 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Wed, 8 Jul 2015 21:36:46 +0200 Subject: [PATCH 1/3] Make `std::env::current_dir` work for path names longer than 2048 bytes on non-Windows --- src/libstd/env.rs | 1 - src/libstd/sys/unix/mod.rs | 35 +++++++++++++++++++++++++++++++++++ src/libstd/sys/unix/os.rs | 23 ++++++++++++++--------- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 7c2cd615303c0..b65e065fe9148 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -36,7 +36,6 @@ use sys::os as os_imp; /// /// * Current directory does not exist. /// * There are insufficient permissions to access the current directory. -/// * The internal buffer is not large enough to hold the path. /// /// # Examples /// diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 6fd20b940bb52..90b7d5f471956 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -75,6 +75,41 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } +// Some system functions expect the user to pass a appropiately-sized buffer +// without specifying its size. They will only report back whether the buffer +// was large enough or not. +// +// The callback is yielded a (pointer, len) pair which can be +// passed to a syscall. The `ptr` is valid for `len` items (i8 in this case). +// The closure is expected to return `None` if the space was insufficient and +// `Some(r)` if the syscall did not fail due to insufficient space. +fn fill_bytes_buf(mut f: F) -> io::Result + where F: FnMut(*mut i8, libc::size_t) -> Option>, +{ + // Start off with a stack buf but then spill over to the heap if we end up + // needing more space. + let mut stack_buf = [0i8; os::BUF_BYTES]; + let mut heap_buf = Vec::new(); + unsafe { + let mut n = stack_buf.len(); + loop { + let buf = if n <= stack_buf.len() { + &mut stack_buf[..] + } else { + heap_buf.set_len(0); + heap_buf.reserve(n); + heap_buf.set_len(n); + &mut heap_buf[..] + }; + + match f(buf.as_mut_ptr(), n as libc::size_t) { + None => n *= 2, + Some(r) => return r, + } + } + } +} + pub fn cvt>(t: T) -> io::Result { let one: T = T::one(); if t == -one { diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 334dd6b5f18c1..6cf07ea077de6 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -22,15 +22,15 @@ use io; use iter; use libc::{self, c_int, c_char, c_void}; use mem; -use ptr; use path::{self, PathBuf}; +use ptr; use slice; use str; use sys::c; use sys::fd; use vec; -const BUF_BYTES: usize = 2048; +pub const BUF_BYTES: usize = 2048; const TMPBUF_SZ: usize = 128; fn bytes2path(b: &[u8]) -> PathBuf { @@ -102,14 +102,19 @@ pub fn error_string(errno: i32) -> String { } pub fn getcwd() -> io::Result { - let mut buf = [0 as c_char; BUF_BYTES]; - unsafe { - if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() { - Err(io::Error::last_os_error()) - } else { - Ok(bytes2path(CStr::from_ptr(buf.as_ptr()).to_bytes())) + super::fill_bytes_buf(|buf, len| { + unsafe { + Some(if !libc::getcwd(buf, len).is_null() { + Ok(bytes2path(CStr::from_ptr(buf).to_bytes())) + } else { + let error = io::Error::last_os_error(); + if error.raw_os_error().unwrap() == libc::ERANGE { + return None; + } + Err(error) + }) } - } + }) } pub fn chdir(p: &path::Path) -> io::Result<()> { From d99d4fbf70acce44c14448a572f7dce4cb883ce8 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Thu, 9 Jul 2015 15:03:10 +0200 Subject: [PATCH 2/3] Address some comments on the pull request --- src/libstd/sys/unix/mod.rs | 37 ++++++++++++------------------------- src/libstd/sys/unix/os.rs | 28 +++++++++++++--------------- 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 90b7d5f471956..5bf8c1e06a627 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -79,33 +79,20 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { // without specifying its size. They will only report back whether the buffer // was large enough or not. // -// The callback is yielded a (pointer, len) pair which can be -// passed to a syscall. The `ptr` is valid for `len` items (i8 in this case). -// The closure is expected to return `None` if the space was insufficient and -// `Some(r)` if the syscall did not fail due to insufficient space. +// The callback is yielded an uninitialized vector which can be passed to a +// syscall. The closure is expected to return `Err(v)` with the passed vector +// if the space was insufficient and `Ok(r)` if the syscall did not fail due to +// insufficient space. fn fill_bytes_buf(mut f: F) -> io::Result - where F: FnMut(*mut i8, libc::size_t) -> Option>, + where F: FnMut(Vec) -> Result,Vec>, { - // Start off with a stack buf but then spill over to the heap if we end up - // needing more space. - let mut stack_buf = [0i8; os::BUF_BYTES]; - let mut heap_buf = Vec::new(); - unsafe { - let mut n = stack_buf.len(); - loop { - let buf = if n <= stack_buf.len() { - &mut stack_buf[..] - } else { - heap_buf.set_len(0); - heap_buf.reserve(n); - heap_buf.set_len(n); - &mut heap_buf[..] - }; - - match f(buf.as_mut_ptr(), n as libc::size_t) { - None => n *= 2, - Some(r) => return r, - } + let mut buf = Vec::new(); + let mut n = os::BUF_BYTES; + loop { + buf.reserve(n); + match f(buf) { + Err(b) => { buf = b; n *= 2; } + Ok(r) => return r, } } } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 6cf07ea077de6..ac38d294a6f33 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -33,14 +33,6 @@ use vec; pub const BUF_BYTES: usize = 2048; const TMPBUF_SZ: usize = 128; -fn bytes2path(b: &[u8]) -> PathBuf { - PathBuf::from(::from_bytes(b)) -} - -fn os2path(os: OsString) -> PathBuf { - bytes2path(os.as_bytes()) -} - /// Returns the platform-specific value of errno pub fn errno() -> i32 { #[cfg(any(target_os = "macos", @@ -102,14 +94,17 @@ pub fn error_string(errno: i32) -> String { } pub fn getcwd() -> io::Result { - super::fill_bytes_buf(|buf, len| { + super::fill_bytes_buf(|mut buf| { unsafe { - Some(if !libc::getcwd(buf, len).is_null() { - Ok(bytes2path(CStr::from_ptr(buf).to_bytes())) + let ptr = buf.as_mut_ptr() as *mut libc::c_char; + Ok(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(); + buf.set_len(len); + Ok(PathBuf::from(OsString::from_bytes(buf).unwrap())) } else { let error = io::Error::last_os_error(); if error.raw_os_error().unwrap() == libc::ERANGE { - return None; + return Err(buf); } Err(error) }) @@ -134,11 +129,14 @@ pub struct SplitPaths<'a> { } pub fn split_paths<'a>(unparsed: &'a OsStr) -> SplitPaths<'a> { + fn bytes_to_path(b: &[u8]) -> PathBuf { + PathBuf::from(::from_bytes(b)) + } fn is_colon(b: &u8) -> bool { *b == b':' } let unparsed = unparsed.as_bytes(); SplitPaths { iter: unparsed.split(is_colon as fn(&u8) -> bool) - .map(bytes2path as fn(&'a [u8]) -> PathBuf) + .map(bytes_to_path as fn(&'a [u8]) -> PathBuf) } } @@ -449,7 +447,7 @@ pub fn page_size() -> usize { } pub fn temp_dir() -> PathBuf { - getenv("TMPDIR".as_ref()).map(os2path).unwrap_or_else(|| { + getenv("TMPDIR".as_ref()).map(PathBuf::from).unwrap_or_else(|| { if cfg!(target_os = "android") { PathBuf::from("/data/local/tmp") } else { @@ -461,7 +459,7 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { return getenv("HOME".as_ref()).or_else(|| unsafe { fallback() - }).map(os2path); + }).map(PathBuf::from); #[cfg(any(target_os = "android", target_os = "ios"))] From b83ec47808c219cb8e85eeebb370be6466b2d690 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Fri, 10 Jul 2015 12:33:10 +0200 Subject: [PATCH 3/3] Remove the generic `fill_bytes_buf` function --- src/libstd/sys/unix/mod.rs | 22 ---------------------- src/libstd/sys/unix/os.rs | 22 +++++++++++++--------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 5bf8c1e06a627..6fd20b940bb52 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -75,28 +75,6 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } -// Some system functions expect the user to pass a appropiately-sized buffer -// without specifying its size. They will only report back whether the buffer -// was large enough or not. -// -// The callback is yielded an uninitialized vector which can be passed to a -// syscall. The closure is expected to return `Err(v)` with the passed vector -// if the space was insufficient and `Ok(r)` if the syscall did not fail due to -// insufficient space. -fn fill_bytes_buf(mut f: F) -> io::Result - where F: FnMut(Vec) -> Result,Vec>, -{ - let mut buf = Vec::new(); - let mut n = os::BUF_BYTES; - loop { - buf.reserve(n); - match f(buf) { - Err(b) => { buf = b; n *= 2; } - Ok(r) => return r, - } - } -} - pub fn cvt>(t: T) -> io::Result { let one: T = T::one(); if t == -one { diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index ac38d294a6f33..2b6b50a1a56d7 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -30,7 +30,7 @@ use sys::c; use sys::fd; use vec; -pub const BUF_BYTES: usize = 2048; +const GETCWD_BUF_BYTES: usize = 2048; const TMPBUF_SZ: usize = 128; /// Returns the platform-specific value of errno @@ -94,22 +94,26 @@ pub fn error_string(errno: i32) -> String { } pub fn getcwd() -> io::Result { - super::fill_bytes_buf(|mut buf| { + let mut buf = Vec::new(); + let mut n = GETCWD_BUF_BYTES; + loop { unsafe { + buf.reserve(n); let ptr = buf.as_mut_ptr() as *mut libc::c_char; - Ok(if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() { + 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(); buf.set_len(len); - Ok(PathBuf::from(OsString::from_bytes(buf).unwrap())) + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); } else { let error = io::Error::last_os_error(); - if error.raw_os_error().unwrap() == libc::ERANGE { - return Err(buf); + if error.raw_os_error() != Some(libc::ERANGE) { + return Err(error); } - Err(error) - }) + } + n *= 2; } - }) + } } pub fn chdir(p: &path::Path) -> io::Result<()> {