diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 889d21cad65aa..c4d94259bd6db 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -25,6 +25,8 @@ use sys_common::{AsInner, FromInner}; #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64}; +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] +use libc::{fstatat, dirfd}; #[cfg(target_os = "android")] use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, lseek64, dirent as dirent64, open as open64}; @@ -48,11 +50,15 @@ pub struct FileAttr { stat: stat64, } -pub struct ReadDir { +// all DirEntry's will have a reference to this struct +struct InnerReadDir { dirp: Dir, - root: Arc, + root: PathBuf, } +#[derive(Clone)] +pub struct ReadDir(Arc); + struct Dir(*mut libc::DIR); unsafe impl Send for Dir {} @@ -60,8 +66,8 @@ unsafe impl Sync for Dir {} pub struct DirEntry { entry: dirent64, - root: Arc, - // We need to store an owned copy of the directory name + dir: ReadDir, + // We need to store an owned copy of the entry name // on Solaris and Fuchsia because a) it uses a zero-length // array to store the name, b) its lifetime between readdir // calls is not guaranteed. @@ -207,7 +213,7 @@ impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. // Thus the result will be e g 'ReadDir("/home")' - fmt::Debug::fmt(&*self.root, f) + fmt::Debug::fmt(&*self.0.root, f) } } @@ -223,7 +229,7 @@ impl Iterator for ReadDir { // is safe to use in threaded applications and it is generally preferred // over the readdir_r(3C) function. super::os::set_errno(0); - let entry_ptr = libc::readdir(self.dirp.0); + let entry_ptr = libc::readdir(self.0.dirp.0); if entry_ptr.is_null() { // NULL can mean either the end is reached or an error occurred. // So we had to clear errno beforehand to check for an error now. @@ -240,7 +246,7 @@ impl Iterator for ReadDir { entry: *entry_ptr, name: ::slice::from_raw_parts(name as *const u8, namelen as usize).to_owned().into_boxed_slice(), - root: self.root.clone() + dir: self.clone() }; if ret.name_bytes() != b"." && ret.name_bytes() != b".." { return Some(Ok(ret)) @@ -254,11 +260,11 @@ impl Iterator for ReadDir { unsafe { let mut ret = DirEntry { entry: mem::zeroed(), - root: self.root.clone() + dir: self.clone(), }; let mut entry_ptr = ptr::null_mut(); loop { - if readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { + if readdir64_r(self.0.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { return Some(Err(Error::last_os_error())) } if entry_ptr.is_null() { @@ -281,13 +287,27 @@ impl Drop for Dir { impl DirEntry { pub fn path(&self) -> PathBuf { - self.root.join(OsStr::from_bytes(self.name_bytes())) + self.dir.0.root.join(OsStr::from_bytes(self.name_bytes())) } pub fn file_name(&self) -> OsString { OsStr::from_bytes(self.name_bytes()).to_os_string() } + #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] + pub fn metadata(&self) -> io::Result { + let fd = cvt(unsafe {dirfd(self.dir.0.dirp.0)})?; + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { + fstatat(fd, + self.entry.d_name.as_ptr(), + &mut stat as *mut _ as *mut _, + libc::AT_SYMLINK_NOFOLLOW) + })?; + Ok(FileAttr { stat: stat }) + } + + #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] pub fn metadata(&self) -> io::Result { lstat(&self.path()) } @@ -664,14 +684,15 @@ impl fmt::Debug for File { } pub fn readdir(p: &Path) -> io::Result { - let root = Arc::new(p.to_path_buf()); + let root = p.to_path_buf(); let p = cstr(p)?; unsafe { let ptr = libc::opendir(p.as_ptr()); if ptr.is_null() { Err(Error::last_os_error()) } else { - Ok(ReadDir { dirp: Dir(ptr), root: root }) + let inner = InnerReadDir { dirp: Dir(ptr), root }; + Ok(ReadDir(Arc::new(inner))) } } }