about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/unix/fs.rs227
-rw-r--r--src/libstd/sys/vxworks/fs.rs26
-rw-r--r--src/libstd/sys/vxworks/rwlock.rs2
-rw-r--r--src/libstd/sys/windows/fs.rs2
4 files changed, 239 insertions, 18 deletions
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index c2064fca23e..4054cb1f9c5 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -41,11 +41,138 @@ pub use crate::sys_common::fs::remove_dir_all;
 
 pub struct File(FileDesc);
 
-#[derive(Clone)]
-pub struct FileAttr {
-    stat: stat64,
+// FIXME: This should be available on Linux with all `target_arch` and `target_env`.
+// https://github.com/rust-lang/libc/issues/1545
+macro_rules! cfg_has_statx {
+    ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
+        cfg_if::cfg_if! {
+            if #[cfg(all(target_os = "linux", target_env = "gnu", any(
+                target_arch = "x86",
+                target_arch = "arm",
+                // target_arch = "mips",
+                target_arch = "powerpc",
+                target_arch = "x86_64",
+                // target_arch = "aarch64",
+                target_arch = "powerpc64",
+                // target_arch = "mips64",
+                // target_arch = "s390x",
+                target_arch = "sparc64",
+            )))] {
+                $($then_tt)*
+            } else {
+                $($else_tt)*
+            }
+        }
+    };
+    ($($block_inner:tt)*) => {
+        #[cfg(all(target_os = "linux", target_env = "gnu", any(
+            target_arch = "x86",
+            target_arch = "arm",
+            // target_arch = "mips",
+            target_arch = "powerpc",
+            target_arch = "x86_64",
+            // target_arch = "aarch64",
+            target_arch = "powerpc64",
+            // target_arch = "mips64",
+            // target_arch = "s390x",
+            target_arch = "sparc64",
+        )))]
+        {
+            $($block_inner)*
+        }
+    };
 }
 
+cfg_has_statx! {{
+    #[derive(Clone)]
+    pub struct FileAttr {
+        stat: stat64,
+        statx_extra_fields: Option<StatxExtraFields>,
+    }
+
+    #[derive(Clone)]
+    struct StatxExtraFields {
+        // This is needed to check if btime is supported by the filesystem.
+        stx_mask: u32,
+        stx_btime: libc::statx_timestamp,
+    }
+
+    // We prefer `statx` on Linux if available, which contains file creation time.
+    // Default `stat64` contains no creation time.
+    unsafe fn try_statx(
+        fd: c_int,
+        path: *const libc::c_char,
+        flags: i32,
+        mask: u32,
+    ) -> Option<io::Result<FileAttr>> {
+        use crate::sync::atomic::{AtomicBool, Ordering};
+
+        // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
+        // We store the availability in a global to avoid unnecessary syscalls
+        static HAS_STATX: AtomicBool = AtomicBool::new(true);
+        syscall! {
+            fn statx(
+                fd: c_int,
+                pathname: *const libc::c_char,
+                flags: c_int,
+                mask: libc::c_uint,
+                statxbuf: *mut libc::statx
+            ) -> c_int
+        }
+
+        if !HAS_STATX.load(Ordering::Relaxed) {
+            return None;
+        }
+
+        let mut buf: libc::statx = mem::zeroed();
+        let ret = cvt(statx(fd, path, flags, mask, &mut buf));
+        match ret {
+            Err(err) => match err.raw_os_error() {
+                Some(libc::ENOSYS) => {
+                    HAS_STATX.store(false, Ordering::Relaxed);
+                    return None;
+                }
+                _ => return Some(Err(err)),
+            }
+            Ok(_) => {
+                // We cannot fill `stat64` exhaustively because of private padding fields.
+                let mut stat: stat64 = mem::zeroed();
+                // `c_ulong` on gnu-mips, `dev_t` otherwise
+                stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
+                stat.st_ino = buf.stx_ino as libc::ino64_t;
+                stat.st_nlink = buf.stx_nlink as libc::nlink_t;
+                stat.st_mode = buf.stx_mode as libc::mode_t;
+                stat.st_uid = buf.stx_uid as libc::uid_t;
+                stat.st_gid = buf.stx_gid as libc::gid_t;
+                stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
+                stat.st_size = buf.stx_size as off64_t;
+                stat.st_blksize = buf.stx_blksize as libc::blksize_t;
+                stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
+                stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
+                // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
+                stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
+                stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
+                stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
+                stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
+                stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
+
+                let extra = StatxExtraFields {
+                    stx_mask: buf.stx_mask,
+                    stx_btime: buf.stx_btime,
+                };
+
+                Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
+            }
+        }
+    }
+
+} else {
+    #[derive(Clone)]
+    pub struct FileAttr {
+        stat: stat64,
+    }
+}}
+
 // all DirEntry's will have a reference to this struct
 struct InnerReadDir {
     dirp: Dir,
@@ -97,6 +224,20 @@ pub struct FileType { mode: mode_t }
 #[derive(Debug)]
 pub struct DirBuilder { mode: mode_t }
 
+cfg_has_statx! {{
+    impl FileAttr {
+        fn from_stat64(stat: stat64) -> Self {
+            Self { stat, statx_extra_fields: None }
+        }
+    }
+} else {
+    impl FileAttr {
+        fn from_stat64(stat: stat64) -> Self {
+            Self { stat }
+        }
+    }
+}}
+
 impl FileAttr {
     pub fn size(&self) -> u64 { self.stat.st_size as u64 }
     pub fn perm(&self) -> FilePermissions {
@@ -164,6 +305,22 @@ impl FileAttr {
                   target_os = "macos",
                   target_os = "ios")))]
     pub fn created(&self) -> io::Result<SystemTime> {
+        cfg_has_statx! {
+            if let Some(ext) = &self.statx_extra_fields {
+                return if (ext.stx_mask & libc::STATX_BTIME) != 0 {
+                    Ok(SystemTime::from(libc::timespec {
+                        tv_sec: ext.stx_btime.tv_sec as libc::time_t,
+                        tv_nsec: ext.stx_btime.tv_nsec as _,
+                    }))
+                } else {
+                    Err(io::Error::new(
+                        io::ErrorKind::Other,
+                        "creation time is not available for the filesystem",
+                    ))
+                };
+            }
+        }
+
         Err(io::Error::new(io::ErrorKind::Other,
                            "creation time is not available on this platform \
                             currently"))
@@ -306,12 +463,25 @@ impl DirEntry {
 
     #[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.inner.dirp.0)})?;
+        let fd = cvt(unsafe { dirfd(self.dir.inner.dirp.0) })?;
+        let name = self.entry.d_name.as_ptr();
+
+        cfg_has_statx! {
+            if let Some(ret) = unsafe { try_statx(
+                fd,
+                name,
+                libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
+                libc::STATX_ALL,
+            ) } {
+                return ret;
+            }
+        }
+
         let mut stat: stat64 = unsafe { mem::zeroed() };
         cvt(unsafe {
-            fstatat64(fd, self.entry.d_name.as_ptr(), &mut stat, libc::AT_SYMLINK_NOFOLLOW)
+            fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW)
         })?;
-        Ok(FileAttr { stat })
+        Ok(FileAttr::from_stat64(stat))
     }
 
     #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
@@ -515,11 +685,24 @@ impl File {
     }
 
     pub fn file_attr(&self) -> io::Result<FileAttr> {
+        let fd = self.0.raw();
+
+        cfg_has_statx! {
+            if let Some(ret) = unsafe { try_statx(
+                fd,
+                b"\0" as *const _ as *const libc::c_char,
+                libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
+                libc::STATX_ALL,
+            ) } {
+                return ret;
+            }
+        }
+
         let mut stat: stat64 = unsafe { mem::zeroed() };
         cvt(unsafe {
-            fstat64(self.0.raw(), &mut stat)
+            fstat64(fd, &mut stat)
         })?;
-        Ok(FileAttr { stat })
+        Ok(FileAttr::from_stat64(stat))
     }
 
     pub fn fsync(&self) -> io::Result<()> {
@@ -796,20 +979,44 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
 
 pub fn stat(p: &Path) -> io::Result<FileAttr> {
     let p = cstr(p)?;
+
+    cfg_has_statx! {
+        if let Some(ret) = unsafe { try_statx(
+            libc::AT_FDCWD,
+            p.as_ptr(),
+            libc::AT_STATX_SYNC_AS_STAT,
+            libc::STATX_ALL,
+        ) } {
+            return ret;
+        }
+    }
+
     let mut stat: stat64 = unsafe { mem::zeroed() };
     cvt(unsafe {
         stat64(p.as_ptr(), &mut stat)
     })?;
-    Ok(FileAttr { stat })
+    Ok(FileAttr::from_stat64(stat))
 }
 
 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
     let p = cstr(p)?;
+
+    cfg_has_statx! {
+        if let Some(ret) = unsafe { try_statx(
+            libc::AT_FDCWD,
+            p.as_ptr(),
+            libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
+            libc::STATX_ALL,
+        ) } {
+            return ret;
+        }
+    }
+
     let mut stat: stat64 = unsafe { mem::zeroed() };
     cvt(unsafe {
         lstat64(p.as_ptr(), &mut stat)
     })?;
-    Ok(FileAttr { stat })
+    Ok(FileAttr::from_stat64(stat))
 }
 
 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
diff --git a/src/libstd/sys/vxworks/fs.rs b/src/libstd/sys/vxworks/fs.rs
index 51fdb1c0e55..adb08d8005a 100644
--- a/src/libstd/sys/vxworks/fs.rs
+++ b/src/libstd/sys/vxworks/fs.rs
@@ -400,13 +400,27 @@ impl FromInner<c_int> for File {
 
 impl fmt::Debug for File {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fn get_path(_fd: c_int) -> Option<PathBuf> {
-            // FIXME(#:(): implement this for VxWorks
-            None
+        fn get_path(fd: c_int) -> Option<PathBuf> {
+            let mut buf = vec![0;libc::PATH_MAX as usize];
+            let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
+            if n == -1 {
+                return None;
+            }
+            let l = buf.iter().position(|&c| c == 0).unwrap();
+            buf.truncate(l as usize);
+            Some(PathBuf::from(OsString::from_vec(buf)))
         }
-        fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
-            // FIXME(#:(): implement this for VxWorks
-            None
+        fn get_mode(fd: c_int) -> Option<(bool, bool)> {
+            let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
+            if mode == -1 {
+                return None;
+            }
+            match mode & libc::O_ACCMODE {
+                libc::O_RDONLY => Some((true, false)),
+                libc::O_RDWR => Some((true, true)),
+                libc::O_WRONLY => Some((false, true)),
+                _ => None
+            }
         }
 
         let fd = self.0.raw();
diff --git a/src/libstd/sys/vxworks/rwlock.rs b/src/libstd/sys/vxworks/rwlock.rs
index 718f422ed11..19b123f2b61 100644
--- a/src/libstd/sys/vxworks/rwlock.rs
+++ b/src/libstd/sys/vxworks/rwlock.rs
@@ -25,7 +25,7 @@ impl RWLock {
         let r = libc::pthread_rwlock_rdlock(self.inner.get());
         if r == libc::EAGAIN {
             panic!("rwlock maximum reader count exceeded");
-        } else if r == libc::EDEADLK || *self.write_locked.get() {
+        } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) {
             if r == 0 {
                 self.raw_unlock();
             }
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index 204f6af5fc1..4160123c9a2 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -412,7 +412,7 @@ impl File {
 
     pub fn duplicate(&self) -> io::Result<File> {
         Ok(File {
-            handle: self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)?,
+            handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)?,
         })
     }