Skip to content

Commit

Permalink
Auto merge of #27930 - barosl:path_max, r=alexcrichton
Browse files Browse the repository at this point in the history
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 #27454.

r? @alexcrichton
  • Loading branch information
bors committed Aug 27, 2015
2 parents abfa081 + 6065678 commit 79dd92f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 23 deletions.
19 changes: 17 additions & 2 deletions src/liblibc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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")]
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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;
}
}
}
Expand Down Expand Up @@ -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;
Expand Down
45 changes: 30 additions & 15 deletions src/libstd/sys/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,13 +376,19 @@ impl fmt::Debug for File {

#[cfg(target_os = "macos")]
fn get_path(fd: c_int) -> Option<PathBuf> {
// 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 {
return None;
}
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)))
}

Expand Down Expand Up @@ -466,18 +472,27 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
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<u8> = 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<()> {
Expand Down Expand Up @@ -514,15 +529,15 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {

pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
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)))
}

Expand Down
12 changes: 7 additions & 5 deletions src/libstd/sys/unix/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -94,11 +93,9 @@ pub fn error_string(errno: i32) -> String {
}

pub fn getcwd() -> io::Result<PathBuf> {
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();
Expand All @@ -111,7 +108,12 @@ pub fn getcwd() -> io::Result<PathBuf> {
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);
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/rt/rust_builtin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 79dd92f

Please sign in to comment.