diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index 0bc8b30c8..ee50c213e 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -14,6 +14,8 @@ use clap::{App, Arg}; use std::collections::HashSet; use std::env; use std::fs; +#[cfg(not(windows))] +use std::fs::Metadata; use std::io::{stderr, ErrorKind, Result, Write}; use std::iter; #[cfg(not(windows))] @@ -92,7 +94,7 @@ struct Stat { size: u64, blocks: u64, inode: Option, - created: u64, + created: Option, accessed: u64, modified: u64, } @@ -113,7 +115,7 @@ impl Stat { size: metadata.len(), blocks: metadata.blocks() as u64, inode: Some(file_info), - created: metadata.mtime() as u64, + created: birth_u64(&metadata), accessed: metadata.atime() as u64, modified: metadata.mtime() as u64, }); @@ -129,7 +131,7 @@ impl Stat { size: metadata.len(), blocks: size_on_disk / 1024 * 2, inode: file_info, - created: windows_time_to_unix_time(metadata.creation_time()), + created: windows_creation_time_to_unix_time(metadata.creation_time()), accessed: windows_time_to_unix_time(metadata.last_access_time()), modified: windows_time_to_unix_time(metadata.last_write_time()), }) @@ -137,10 +139,24 @@ impl Stat { } #[cfg(windows)] -// https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html#tymethod.creation_time +// https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html#tymethod.last_access_time // "The returned 64-bit value [...] which represents the number of 100-nanosecond intervals since January 1, 1601 (UTC)." +// "If the underlying filesystem does not support last access time, the returned value is 0." fn windows_time_to_unix_time(win_time: u64) -> u64 { - win_time / 10_000_000 - 11_644_473_600 + (win_time / 10_000_000).saturating_sub(11_644_473_600) +} + +#[cfg(windows)] +fn windows_creation_time_to_unix_time(win_time: u64) -> Option { + (win_time / 10_000_000).checked_sub(11_644_473_600) +} + +#[cfg(not(windows))] +fn birth_u64(meta: &Metadata) -> Option { + meta.created() + .ok() + .and_then(|t| t.duration_since(UNIX_EPOCH).ok()) + .map(|e| e.as_secs() as u64) } #[cfg(windows)] @@ -539,10 +555,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .value_name("WORD") .require_equals(true) .min_values(0) + .possible_values(&["atime", "access", "use", "ctime", "status", "birth", "creation"]) .help( "show time of the last modification of any file in the \ directory, or any of its subdirectories. If WORD is given, show time as WORD instead \ - of modification time: atime, access, use, ctime or status" + of modification time: atime, access, use, ctime, status, birth or creation" ) ) .arg( @@ -667,19 +684,22 @@ Try '{} --help' for more information.", let secs = { match matches.value_of(options::TIME) { Some(s) => match s { - "accessed" => stat.accessed, - "created" => stat.created, - "modified" => stat.modified, - _ => { - show_error!( - "invalid argument 'modified' for '--time' - Valid arguments are: - - 'accessed', 'created', 'modified' - Try '{} --help' for more information.", - NAME - ); - return 1; + "ctime" | "status" => stat.modified, + "access" | "atime" | "use" => stat.accessed, + "birth" | "creation" => { + if let Some(time) = stat.created { + time + } else { + show_error!( + "Invalid argument ‘{}‘ for --time. +‘birth‘ and ‘creation‘ arguments are not supported on this platform.", + s + ); + return 1; + } } + // below should never happen as clap already restricts the values. + _ => unreachable!("Invalid field for --time"), }, None => stat.modified, } diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index 9e585b03e..3c177c6bf 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -240,18 +240,37 @@ fn test_du_time() { scene .ccmd("touch") .arg("-a") - .arg("-m") .arg("-t") .arg("201505150000") .arg("date_test") .succeeds(); scene - .ucmd() - .arg("--time") + .ccmd("touch") + .arg("-m") + .arg("-t") + .arg("201606160000") .arg("date_test") - .succeeds() - .stdout_only("0\t2015-05-15 00:00\tdate_test\n"); + .succeeds(); + + let result = scene.ucmd().arg("--time").arg("date_test").succeeds(); + result.stdout_only("0\t2016-06-16 00:00\tdate_test\n"); + + let result = scene.ucmd().arg("--time=atime").arg("date_test").succeeds(); + result.stdout_only("0\t2015-05-15 00:00\tdate_test\n"); + + let result = scene.ucmd().arg("--time=ctime").arg("date_test").succeeds(); + result.stdout_only("0\t2016-06-16 00:00\tdate_test\n"); + + #[cfg(not(target_env = "musl"))] + { + use regex::Regex; + + let re_birth = + Regex::new(r"0\t[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}\tdate_test").unwrap(); + let result = scene.ucmd().arg("--time=birth").arg("date_test").succeeds(); + result.stdout_matches(&re_birth); + } } #[cfg(not(target_os = "windows"))]