From d1681bbde563dff88ad8f32592e7d373f62a038d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 12 Jan 2016 17:24:16 -0800 Subject: [PATCH] std: Expose SystemTime accessors on fs::Metadata These accessors are used to get at the last modification, last access, and creation time of the underlying file. Currently not all platforms provide the creation time, so that currently returns `Option`. --- src/libstd/fs.rs | 67 ++++++++++++++++++++++++++++++++ src/libstd/sys/unix/fs.rs | 62 +++++++++++++++++++++++++++++ src/libstd/sys/unix/time.rs | 12 ++++++ src/libstd/sys/windows/ext/fs.rs | 6 +-- src/libstd/sys/windows/fs.rs | 31 ++++++++++++--- src/libstd/sys/windows/time.rs | 6 +++ src/libstd/time/mod.rs | 7 ++++ 7 files changed, 183 insertions(+), 8 deletions(-) diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index b5a682955c0c0..343d71abb6429 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -25,6 +25,7 @@ use sys::fs as fs_imp; use sys_common::io::read_to_end_uninitialized; use sys_common::{AsInnerMut, FromInner, AsInner, IntoInner}; use vec::Vec; +use time::SystemTime; /// A reference to an open file on the filesystem. /// @@ -660,6 +661,52 @@ impl Metadata { pub fn permissions(&self) -> Permissions { Permissions(self.0.perm()) } + + /// Returns the last modification time listed in this metadata. + /// + /// The returned value corresponds to the `mtime` field of `stat` on Unix + /// platforms and the `ftLastWriteTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + #[unstable(feature = "fs_time", issue = "31399")] + pub fn modified(&self) -> io::Result { + self.0.modified().map(FromInner::from_inner) + } + + /// Returns the last access time of this metadata. + /// + /// The returned value corresponds to the `atime` field of `stat` on Unix + /// platforms and the `ftLastAccessTime` field on Windows platforms. + /// + /// Note that not all platforms will keep this field update in a file's + /// metadata, for example Windows has an option to disable updating this + /// time when files are accessed and Linux similarly has `noatime`. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + #[unstable(feature = "fs_time", issue = "31399")] + pub fn accessed(&self) -> io::Result { + self.0.accessed().map(FromInner::from_inner) + } + + /// Returns the creation time listed in the this metadata. + /// + /// The returned value corresponds to the `birthtime` field of `stat` on + /// Unix platforms and the `ftCreationTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + #[unstable(feature = "fs_time", issue = "31399")] + pub fn created(&self) -> io::Result { + self.0.created().map(FromInner::from_inner) + } } impl AsInner for Metadata { @@ -2468,4 +2515,24 @@ mod tests { assert!(link.is_dir()); assert!(d.exists()); } + + #[test] + fn metadata_access_times() { + let tmpdir = tmpdir(); + + let b = tmpdir.join("b"); + File::create(&b).unwrap(); + + let a = check!(fs::metadata(&tmpdir.path())); + let b = check!(fs::metadata(&b)); + + assert_eq!(check!(a.accessed()), check!(a.accessed())); + assert_eq!(check!(a.modified()), check!(a.modified())); + assert_eq!(check!(b.accessed()), check!(b.modified())); + + if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + check!(a.created()); + check!(b.created()); + } + } } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 2527c6774ff6d..e672d9f158666 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -22,6 +22,7 @@ use ptr; use sync::Arc; use sys::fd::FileDesc; use sys::platform::raw; +use sys::time::SystemTime; use sys::{cvt, cvt_r}; use sys_common::{AsInner, FromInner}; @@ -86,6 +87,67 @@ impl FileAttr { } } +#[cfg(any(target_os = "ios", target_os = "macos"))] +// FIXME: update SystemTime to store a timespec and don't lose precision +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(libc::timeval { + tv_sec: self.stat.st_mtime, + tv_usec: (self.stat.st_mtime_nsec / 1000) as libc::suseconds_t, + })) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(libc::timeval { + tv_sec: self.stat.st_atime, + tv_usec: (self.stat.st_atime_nsec / 1000) as libc::suseconds_t, + })) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(libc::timeval { + tv_sec: self.stat.st_birthtime, + tv_usec: (self.stat.st_birthtime_nsec / 1000) as libc::suseconds_t, + })) + } +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_mtime, + tv_nsec: self.stat.st_mtime_nsec as libc::c_long, + })) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_atime, + tv_nsec: self.stat.st_atime_nsec as libc::c_long, + })) + } + + #[cfg(any(target_os = "bitrig", + target_os = "freebsd", + target_os = "openbsd"))] + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_birthtime, + tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, + })) + } + + #[cfg(not(any(target_os = "bitrig", + target_os = "freebsd", + target_os = "openbsd")))] + pub fn created(&self) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, + "creation time is not available on this platform \ + currently")) + } +} + impl AsInner for FileAttr { fn as_inner(&self) -> &raw::stat { &self.stat } } diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index a07c30d964876..dd248416f84cf 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -146,6 +146,12 @@ mod inner { } } + impl From for SystemTime { + fn from(t: libc::timeval) -> SystemTime { + SystemTime { t: t } + } + } + impl PartialEq for SystemTime { fn eq(&self, other: &SystemTime) -> bool { self.t.tv_sec == other.t.tv_sec && self.t.tv_usec == other.t.tv_usec @@ -282,6 +288,12 @@ mod inner { } } + impl From for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t: t } } + } + } + impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("SystemTime") diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index d060c902fba2f..d378a6853f3c7 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -196,9 +196,9 @@ pub trait MetadataExt { #[stable(feature = "metadata_ext", since = "1.1.0")] impl MetadataExt for Metadata { fn file_attributes(&self) -> u32 { self.as_inner().attrs() } - fn creation_time(&self) -> u64 { self.as_inner().created() } - fn last_access_time(&self) -> u64 { self.as_inner().accessed() } - fn last_write_time(&self) -> u64 { self.as_inner().modified() } + fn creation_time(&self) -> u64 { self.as_inner().created_u64() } + fn last_access_time(&self) -> u64 { self.as_inner().accessed_u64() } + fn last_write_time(&self) -> u64 { self.as_inner().modified_u64() } fn file_size(&self) -> u64 { self.as_inner().size() } } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 8d921146653ee..16d337fcc7073 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -20,6 +20,7 @@ use ptr; use slice; use sync::Arc; use sys::handle::Handle; +use sys::time::SystemTime; use sys::{c, cvt}; use sys_common::FromInner; @@ -421,12 +422,28 @@ impl FileAttr { FileType::new(self.data.dwFileAttributes, self.reparse_tag) } - pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) } - pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) } - pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) } + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(self.data.ftLastWriteTime)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(self.data.ftLastAccessTime)) + } - fn to_u64(&self, ft: &c::FILETIME) -> u64 { - (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(self.data.ftCreationTime)) + } + + pub fn modified_u64(&self) -> u64 { + to_u64(&self.data.ftLastWriteTime) + } + + pub fn accessed_u64(&self) -> u64 { + to_u64(&self.data.ftLastAccessTime) + } + + pub fn created_u64(&self) -> u64 { + to_u64(&self.data.ftCreationTime) } fn is_reparse_point(&self) -> bool { @@ -434,6 +451,10 @@ impl FileAttr { } } +fn to_u64(ft: &c::FILETIME) -> u64 { + (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) +} + impl FilePermissions { pub fn readonly(&self) -> bool { self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index 058587b11dc19..ef8ed606526da 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -166,6 +166,12 @@ impl fmt::Debug for SystemTime { } } +impl From for SystemTime { + fn from(t: c::FILETIME) -> SystemTime { + SystemTime { t: t } + } +} + fn dur2intervals(d: &Duration) -> i64 { d.as_secs().checked_mul(INTERVALS_PER_SEC).and_then(|i| { i.checked_add(d.subsec_nanos() as u64 / 100) diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs index f885733c2d18f..a0cf443c0c3fc 100644 --- a/src/libstd/time/mod.rs +++ b/src/libstd/time/mod.rs @@ -16,6 +16,7 @@ use error::Error; use fmt; use ops::{Add, Sub}; use sys::time; +use sys_common::FromInner; #[stable(feature = "time", since = "1.3.0")] pub use self::duration::Duration; @@ -227,6 +228,12 @@ impl fmt::Display for SystemTimeError { } } +impl FromInner for SystemTime { + fn from_inner(time: time::SystemTime) -> SystemTime { + SystemTime(time) + } +} + #[cfg(test)] mod tests { use super::{Instant, SystemTime, Duration, UNIX_EPOCH};