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

Use statx to get creation time of files on Linux #591

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
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
28 changes: 14 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ datetime = "0.4.7"
env_logger = "0.6.1"
glob = "0.3.0"
lazy_static = "1.3.0"
libc = "0.2.51"
libc = "^0.2.57"
locale = "0.2.2"
log = "0.4.6"
natord = "1.0.9"
Expand Down
42 changes: 39 additions & 3 deletions src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::fs::{self, metadata};
use std::io::Error as IOError;
use std::io::Result as IOResult;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{MetadataExt, PermissionsExt, FileTypeExt};
use std::path::{Path, PathBuf};
use std::time::{UNIX_EPOCH, Duration};
Expand All @@ -11,6 +12,9 @@ use fs::dir::Dir;
use fs::fields as f;
use options::Misfire;

extern crate libc;
use libc::{syscall, SYS_statx, statx, STATX_BTIME};


/// A **File** is a wrapper around one of Rust's Path objects, along with
/// associated data about the file.
Expand Down Expand Up @@ -343,7 +347,16 @@ impl<'dir> File<'dir> {

/// This file’s created timestamp.
pub fn created_time(&self) -> Duration {
self.metadata.created().unwrap().duration_since(UNIX_EPOCH).unwrap()
if cfg!(target_os = "linux") {
let statx_data = statx_creation_time(&self.path).unwrap();
if statx_data.stx_btime.tv_sec < 0 {
Duration::from_secs(0)
} else {
Duration::new(statx_data.stx_btime.tv_sec as u64, statx_data.stx_btime.tv_nsec)
}
} else {
self.metadata.created().unwrap().duration_since(UNIX_EPOCH).unwrap()
}
}

/// This file’s ‘type’.
Expand Down Expand Up @@ -474,11 +487,19 @@ impl PlatformMetadata {
// Call the functions that return a Result to see if it works
PlatformMetadata::AccessedTime => metadata(temp_dir()).unwrap().accessed(),
PlatformMetadata::ModifiedTime => metadata(temp_dir()).unwrap().modified(),
PlatformMetadata::CreatedTime => metadata(temp_dir()).unwrap().created(),
PlatformMetadata::CreatedTime if cfg!(target_os = "linux") => {
if statx_creation_time(&temp_dir()).is_some() {
return Ok(());
}
return Err(Misfire::Unsupported(
"creation time is not available on this platform currently \
(needs Linux >= 4.11)".to_string()));
},
PlatformMetadata::CreatedTime => metadata(temp_dir()).unwrap().created(),
// We use the Unix API so we know it’s not available elsewhere
PlatformMetadata::ChangedTime => {
if cfg!(target_family = "unix") {
return Ok(())
return Ok(());
} else {
return Err(Misfire::Unsupported(
// for consistency, this error message similar to the one Rust
Expand All @@ -494,6 +515,21 @@ impl PlatformMetadata {
}
}

fn statx_creation_time(path: &PathBuf) -> Option<statx> {
let mut statx_data: statx = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
let path_name = std::ffi::CString::new(path
.canonicalize()
.unwrap()
.as_os_str()
.as_bytes()).unwrap();
let result = unsafe { syscall(SYS_statx, 0, path_name.as_ptr(), 0, STATX_BTIME, &mut statx_data) };
if result == 0 {
Some(statx_data)
} else {
None
}
}


/// More readable aliases for the permission bits exposed by libc.
#[allow(trivial_numeric_casts)]
Expand Down
2 changes: 0 additions & 2 deletions src/options/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,6 @@ mod test {
// Created
#[cfg(not(target_os = "linux"))]
test!(cr: TimeTypes <- ["--created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
#[cfg(target_os = "linux")]
test!(cr: TimeTypes <- ["--created"]; Both => err Misfire::Unsupported("creation time is not available on this platform currently".to_string()));
#[cfg(not(target_os = "linux"))]
test!(c: TimeTypes <- ["-U"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
#[cfg(not(target_os = "linux"))]
Expand Down