Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::fs::DirEntry.metadata(): use fstatat instead of lstat when possible #51050

Merged
merged 2 commits into from
May 31, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions src/libstd/sys/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -48,20 +50,24 @@ pub struct FileAttr {
stat: stat64,
}

pub struct ReadDir {
// all DirEntry's will have a reference to this struct
struct InnerReadDir {
dirp: Dir,
root: Arc<PathBuf>,
root: PathBuf,
}

#[derive(Clone)]
pub struct ReadDir(Arc<InnerReadDir>);

struct Dir(*mut libc::DIR);

unsafe impl Send for Dir {}
unsafe impl Sync for Dir {}

pub struct DirEntry {
entry: dirent64,
root: Arc<PathBuf>,
// 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.
Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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.
Expand All @@ -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))
Expand All @@ -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() {
Expand All @@ -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<FileAttr> {
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<FileAttr> {
lstat(&self.path())
}
Expand Down Expand Up @@ -664,14 +684,15 @@ impl fmt::Debug for File {
}

pub fn readdir(p: &Path) -> io::Result<ReadDir> {
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)))
}
}
}
Expand Down