From 6e00c73bcebe9f1ce285af2a15cb9f5c6440c0a9 Mon Sep 17 00:00:00 2001 From: Solra Bizna Date: Sun, 3 May 2020 17:22:50 -0600 Subject: [PATCH] Changes requested by @asomers on #1231 --- src/fcntl.rs | 61 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/fcntl.rs b/src/fcntl.rs index 97e198b674..e27e16db94 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -180,7 +180,7 @@ pub fn renameat(old_dirfd: Option, res: ssize_t) -> Result { +fn wrap_readlink_result(mut v: Vec, res: ssize_t) -> Result { match Errno::result(res) { Err(err) => Err(err), Ok(len) => { @@ -191,48 +191,60 @@ fn wrap_readlink_result(v: &mut Vec, res: ssize_t) -> Result { } } -fn inner_readlink(dirfd: Option, path: &P) -> Result { - let mut v = Vec::with_capacity(libc::PATH_MAX as usize); - // simple case: result is strictly less than `PATH_MAX` - let res = path.with_nix_path(|cstr| { +fn readlink_maybe_at(dirfd: Option, path: &P, + v: &mut Vec) + -> Result { + path.with_nix_path(|cstr| { unsafe { match dirfd { - Some(dirfd) => libc::readlinkat(dirfd, cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t), - None => libc::readlink(cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t), + Some(dirfd) => libc::readlinkat(dirfd, cstr.as_ptr(), + v.as_mut_ptr() as *mut c_char, + v.capacity() as size_t), + None => libc::readlink(cstr.as_ptr(), + v.as_mut_ptr() as *mut c_char, + v.capacity() as size_t), } } - })?; + }) +} + +fn inner_readlink(dirfd: Option, path: &P) + -> Result { + let mut v = Vec::with_capacity(libc::PATH_MAX as usize); + // simple case: result is strictly less than `PATH_MAX` + let res = readlink_maybe_at(dirfd, path, &mut v)?; let len = Errno::result(res)?; debug_assert!(len >= 0); if (len as usize) < v.capacity() { - return wrap_readlink_result(&mut v, res); + return wrap_readlink_result(v, res); } // Uh oh, the result is too long... // Let's try to ask lstat how many bytes to allocate. - let reported_size = super::sys::stat::lstat(path).and_then(|x| Ok(x.st_size)).unwrap_or(0); - let mut try_size = if reported_size > 0 { reported_size as usize + 1 } - // If lstat doesn't cooperate, or reports an error, be a little less precise. - else { (libc::PATH_MAX as usize).max(128) << 1 }; + let reported_size = super::sys::stat::lstat(path) + .and_then(|x| Ok(x.st_size)).unwrap_or(0); + let mut try_size = if reported_size > 0 { + // Note: even if `lstat`'s apparently valid answer turns out to be + // wrong, we will still read the full symlink no matter what. + reported_size as usize + 1 + } else { + // If lstat doesn't cooperate, or reports an error, be a little less + // precise. + (libc::PATH_MAX as usize).max(128) << 1 + }; loop { v.reserve_exact(try_size); - let res = path.with_nix_path(|cstr| { - unsafe { - match dirfd { - Some(dirfd) => libc::readlinkat(dirfd, cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t), - None => libc::readlink(cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t), - } - } - })?; + let res = readlink_maybe_at(dirfd, path, &mut v)?; let len = Errno::result(res)?; debug_assert!(len >= 0); if (len as usize) < v.capacity() { - break wrap_readlink_result(&mut v, res); + break wrap_readlink_result(v, res); } else { // Ugh! Still not big enough! let next_size = try_size << 1; if next_size < try_size { - // It's absurd that this would happen, but handle it sanely anyway. + // It's absurd that this would happen, but handle it sanely + // anyway. break Err(super::Error::Sys(Errno::ENAMETOOLONG)) } else { @@ -247,7 +259,8 @@ pub fn readlink(path: &P) -> Result { } -pub fn readlinkat(dirfd: RawFd, path: &P) -> Result { +pub fn readlinkat(dirfd: RawFd, path: &P) + -> Result { inner_readlink(Some(dirfd), path) }