mirror of
https://github.com/ouch-org/ouch.git
synced 2025-07-23 01:50:14 +00:00
Merge pull request #250 from ouch-org/extracted-file-metadata
feat: recover last modified time when unpacking zip archives
This commit is contained in:
commit
20e2044bd0
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -477,6 +477,15 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "number_prefix"
|
name = "number_prefix"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -523,6 +532,7 @@ dependencies = [
|
|||||||
"tar",
|
"tar",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"test-strategy",
|
"test-strategy",
|
||||||
|
"time",
|
||||||
"xz2",
|
"xz2",
|
||||||
"zip",
|
"zip",
|
||||||
"zstd",
|
"zstd",
|
||||||
@ -911,6 +921,16 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"num_threads",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -31,6 +31,9 @@ tempfile = "3.2.0"
|
|||||||
ignore = "0.4.18"
|
ignore = "0.4.18"
|
||||||
indicatif = "0.16.2"
|
indicatif = "0.16.2"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
time = { version = "0.3.7", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
clap = { version = "3.0.4", features = ["derive", "env"] }
|
clap = { version = "3.0.4", features = ["derive", "env"] }
|
||||||
clap_complete = "3.0.2"
|
clap_complete = "3.0.2"
|
||||||
|
@ -33,7 +33,9 @@ where
|
|||||||
D: Write,
|
D: Write,
|
||||||
{
|
{
|
||||||
assert!(output_folder.read_dir().expect("dir exists").count() == 0);
|
assert!(output_folder.read_dir().expect("dir exists").count() == 0);
|
||||||
let mut unpacked_files = vec![];
|
|
||||||
|
let mut unpacked_files = Vec::with_capacity(archive.len());
|
||||||
|
|
||||||
for idx in 0..archive.len() {
|
for idx in 0..archive.len() {
|
||||||
let mut file = archive.by_index(idx)?;
|
let mut file = archive.by_index(idx)?;
|
||||||
let file_path = match file.enclosed_name() {
|
let file_path = match file.enclosed_name() {
|
||||||
@ -67,13 +69,15 @@ where
|
|||||||
|
|
||||||
let mut output_file = fs::File::create(&file_path)?;
|
let mut output_file = fs::File::create(&file_path)?;
|
||||||
io::copy(&mut file, &mut output_file)?;
|
io::copy(&mut file, &mut output_file)?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
set_last_modified_time(&output_file, &file)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
__unix_set_permissions(&file_path, &file)?;
|
__unix_set_permissions(&file_path, &file)?;
|
||||||
|
|
||||||
let file_path = fs::canonicalize(&file_path)?;
|
|
||||||
unpacked_files.push(file_path);
|
unpacked_files.push(file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +208,47 @@ fn check_for_comments(file: &ZipFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
/// Attempts to convert a [`zip::DateTime`] to a [`libc::timespec`].
|
||||||
|
fn convert_zip_date_time(date_time: zip::DateTime) -> Option<libc::timespec> {
|
||||||
|
use time::{Date, Month, PrimitiveDateTime, Time};
|
||||||
|
|
||||||
|
// Safety: time::Month is repr(u8) and goes from 1 to 12
|
||||||
|
let month: Month = unsafe { std::mem::transmute(date_time.month()) };
|
||||||
|
|
||||||
|
let date = Date::from_calendar_date(date_time.year() as _, month, date_time.day()).ok()?;
|
||||||
|
|
||||||
|
let time = Time::from_hms(date_time.hour(), date_time.minute(), date_time.second()).ok()?;
|
||||||
|
|
||||||
|
let date_time = PrimitiveDateTime::new(date, time);
|
||||||
|
let timestamp = date_time.assume_utc().unix_timestamp();
|
||||||
|
|
||||||
|
Some(libc::timespec { tv_sec: timestamp, tv_nsec: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn set_last_modified_time(file: &fs::File, zip_file: &ZipFile) -> crate::Result<()> {
|
||||||
|
use std::os::unix::prelude::AsRawFd;
|
||||||
|
|
||||||
|
use libc::UTIME_NOW;
|
||||||
|
|
||||||
|
let now = libc::timespec { tv_sec: 0, tv_nsec: UTIME_NOW };
|
||||||
|
|
||||||
|
let last_modified = zip_file.last_modified();
|
||||||
|
let last_modified = convert_zip_date_time(last_modified).unwrap_or(now);
|
||||||
|
|
||||||
|
// The first value is the last accessed time, which we'll set as being right now.
|
||||||
|
// The second value is the last modified time, which we'll copy over from the zip archive
|
||||||
|
let times = [now, last_modified];
|
||||||
|
|
||||||
|
let output_fd = file.as_raw_fd();
|
||||||
|
|
||||||
|
// TODO: check for -1
|
||||||
|
unsafe { libc::futimens(output_fd, × as *const _) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn __unix_set_permissions(file_path: &Path, file: &ZipFile) -> crate::Result<()> {
|
fn __unix_set_permissions(file_path: &Path, file: &ZipFile) -> crate::Result<()> {
|
||||||
use std::{fs::Permissions, os::unix::fs::PermissionsExt};
|
use std::{fs::Permissions, os::unix::fs::PermissionsExt};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user