Skip to content

Commit 96ce56d

Browse files
committed
std::fs::DirEntry.metadata(): use fstatat instead of lstat when possible
1 parent a76bff8 commit 96ce56d

File tree

1 file changed

+32
-11
lines changed

1 file changed

+32
-11
lines changed

src/libstd/sys/unix/fs.rs

+32-11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use sys_common::{AsInner, FromInner};
2525

2626
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
2727
use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64};
28+
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
29+
use libc::{fstatat, dirfd};
2830
#[cfg(target_os = "android")]
2931
use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, lseek64,
3032
dirent as dirent64, open as open64};
@@ -48,20 +50,24 @@ pub struct FileAttr {
4850
stat: stat64,
4951
}
5052

51-
pub struct ReadDir {
53+
// all DirEntry's will have a reference to this struct
54+
struct InnerReadDir {
5255
dirp: Dir,
53-
root: Arc<PathBuf>,
56+
root: PathBuf,
5457
}
5558

59+
#[derive(Clone)]
60+
pub struct ReadDir(Arc<InnerReadDir>);
61+
5662
struct Dir(*mut libc::DIR);
5763

5864
unsafe impl Send for Dir {}
5965
unsafe impl Sync for Dir {}
6066

6167
pub struct DirEntry {
6268
entry: dirent64,
63-
root: Arc<PathBuf>,
64-
// We need to store an owned copy of the directory name
69+
dir: ReadDir,
70+
// We need to store an owned copy of the entry name
6571
// on Solaris and Fuchsia because a) it uses a zero-length
6672
// array to store the name, b) its lifetime between readdir
6773
// calls is not guaranteed.
@@ -207,7 +213,7 @@ impl fmt::Debug for ReadDir {
207213
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208214
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
209215
// Thus the result will be e g 'ReadDir("/home")'
210-
fmt::Debug::fmt(&*self.root, f)
216+
fmt::Debug::fmt(&*self.0.root, f)
211217
}
212218
}
213219

@@ -240,7 +246,7 @@ impl Iterator for ReadDir {
240246
entry: *entry_ptr,
241247
name: ::slice::from_raw_parts(name as *const u8,
242248
namelen as usize).to_owned().into_boxed_slice(),
243-
root: self.root.clone()
249+
dir: self.clone()
244250
};
245251
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
246252
return Some(Ok(ret))
@@ -254,11 +260,11 @@ impl Iterator for ReadDir {
254260
unsafe {
255261
let mut ret = DirEntry {
256262
entry: mem::zeroed(),
257-
root: self.root.clone()
263+
dir: self.clone(),
258264
};
259265
let mut entry_ptr = ptr::null_mut();
260266
loop {
261-
if readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
267+
if readdir64_r(self.0.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
262268
return Some(Err(Error::last_os_error()))
263269
}
264270
if entry_ptr.is_null() {
@@ -281,13 +287,27 @@ impl Drop for Dir {
281287

282288
impl DirEntry {
283289
pub fn path(&self) -> PathBuf {
284-
self.root.join(OsStr::from_bytes(self.name_bytes()))
290+
self.dir.0.root.join(OsStr::from_bytes(self.name_bytes()))
285291
}
286292

287293
pub fn file_name(&self) -> OsString {
288294
OsStr::from_bytes(self.name_bytes()).to_os_string()
289295
}
290296

297+
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
298+
pub fn metadata(&self) -> io::Result<FileAttr> {
299+
let fd = cvt(unsafe {dirfd(self.dir.0.dirp.0)})?;
300+
let mut stat: stat64 = unsafe { mem::zeroed() };
301+
cvt(unsafe {
302+
fstatat(fd,
303+
self.entry.d_name.as_ptr(),
304+
&mut stat as *mut _ as *mut _,
305+
libc::AT_SYMLINK_NOFOLLOW)
306+
})?;
307+
Ok(FileAttr { stat: stat })
308+
}
309+
310+
#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
291311
pub fn metadata(&self) -> io::Result<FileAttr> {
292312
lstat(&self.path())
293313
}
@@ -664,14 +684,15 @@ impl fmt::Debug for File {
664684
}
665685

666686
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
667-
let root = Arc::new(p.to_path_buf());
687+
let root = p.to_path_buf();
668688
let p = cstr(p)?;
669689
unsafe {
670690
let ptr = libc::opendir(p.as_ptr());
671691
if ptr.is_null() {
672692
Err(Error::last_os_error())
673693
} else {
674-
Ok(ReadDir { dirp: Dir(ptr), root: root })
694+
let inner = InnerReadDir { dirp: Dir(ptr), root };
695+
Ok(ReadDir(Arc::new(inner)))
675696
}
676697
}
677698
}

0 commit comments

Comments
 (0)