1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

install: Implement --preserve-timestamps (-p)

Last access and last modify timestamps are extracted from the
existing file metadata and are applied to the newly created file.
This commit is contained in:
Hari 2021-03-12 16:51:47 -05:00
parent 5ced3a670b
commit 68ec2ed0f3
No known key found for this signature in database
GPG key ID: 3C13EE62349616E4
4 changed files with 1019 additions and 982 deletions

1957
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,7 @@ path = "src/install.rs"
[dependencies] [dependencies]
clap = "2.33" clap = "2.33"
filetime = "0.2"
libc = ">= 0.2" libc = ">= 0.2"
uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["mode", "perms", "entries"] } uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["mode", "perms", "entries"] }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }

View file

@ -13,6 +13,7 @@ mod mode;
extern crate uucore; extern crate uucore;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use filetime::{FileTime, set_file_times};
use uucore::entries::{grp2gid, usr2uid}; use uucore::entries::{grp2gid, usr2uid};
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity}; use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
@ -32,6 +33,7 @@ pub struct Behavior {
owner: String, owner: String,
group: String, group: String,
verbose: bool, verbose: bool,
preserve_timestamps: bool,
} }
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
@ -154,11 +156,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.takes_value(true) .takes_value(true)
) )
.arg( .arg(
// TODO implement flag
Arg::with_name(OPT_PRESERVE_TIMESTAMPS) Arg::with_name(OPT_PRESERVE_TIMESTAMPS)
.short("p") .short("p")
.long(OPT_PRESERVE_TIMESTAMPS) .long(OPT_PRESERVE_TIMESTAMPS)
.help("(unimplemented) apply access/modification times of SOURCE files to corresponding destination files") .help("apply access/modification times of SOURCE files to corresponding destination files")
) )
.arg( .arg(
// TODO implement flag // TODO implement flag
@ -265,8 +266,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
Err("--compare, -C") Err("--compare, -C")
} else if matches.is_present(OPT_CREATED) { } else if matches.is_present(OPT_CREATED) {
Err("-D") Err("-D")
} else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
Err("--preserve-timestamps, -p")
} else if matches.is_present(OPT_STRIP) { } else if matches.is_present(OPT_STRIP) {
Err("--strip, -s") Err("--strip, -s")
} else if matches.is_present(OPT_STRIP_PROGRAM) { } else if matches.is_present(OPT_STRIP_PROGRAM) {
@ -338,6 +337,7 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(), owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(),
group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(), group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(),
verbose: matches.is_present(OPT_VERBOSE), verbose: matches.is_present(OPT_VERBOSE),
preserve_timestamps: matches.is_present(OPT_PRESERVE_TIMESTAMPS)
}) })
} }
@ -555,6 +555,22 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
} }
} }
if b.preserve_timestamps {
let meta = match fs::metadata(from) {
Ok(meta) => meta,
Err(f) => crash!(1, "{}", f.to_string()),
};
let modified_time = FileTime::from_last_modification_time(&meta);
let accessed_time = FileTime::from_last_access_time(&meta);
match set_file_times(to.as_path(), accessed_time, modified_time) {
Ok(_) => {},
Err(e) => show_info!("{}", e)
}
}
if b.verbose { if b.verbose {
show_info!("'{}' -> '{}'", from.display(), to.display()); show_info!("'{}' -> '{}'", from.display(), to.display());
} }

View file

@ -309,6 +309,25 @@ fn test_install_target_new_file_failing_nonexistent_parent() {
assert!(err.contains("not a directory")) assert!(err.contains("not a directory"))
} }
#[test]
fn test_install_preserve_timestamps() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
at.touch(file1);
ucmd.arg(file1).arg(file2).arg("-p").succeeds().no_stderr();
assert!(at.file_exists(file1));
assert!(at.file_exists(file2));
let file1_metadata = at.metadata(file1);
let file2_metadata = at.metadata(file2);
assert_eq!(file1_metadata.accessed().ok(), file2_metadata.accessed().ok());
assert_eq!(file1_metadata.modified().ok(), file2_metadata.modified().ok());
}
// These two tests are failing but should work // These two tests are failing but should work
#[test] #[test]
fn test_install_copy_file() { fn test_install_copy_file() {