From b25211be672398a005b39061ee35d28a2a3d7e8a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 22 Aug 2023 16:13:07 +0200 Subject: [PATCH] Add a force_mtime() function to allow overriding all mtime fields --- src/builder.rs | 72 ++++++++++++++++++++++++++++++++++++--------- src/header.rs | 21 ++++++++----- tests/all.rs | 6 ++-- tests/header/mod.rs | 2 +- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index a26eb31d..6378a67e 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -16,6 +16,7 @@ pub struct Builder { follow: bool, finished: bool, obj: Option, + force_mtime: Option, } impl Builder { @@ -28,6 +29,7 @@ impl Builder { follow: true, finished: false, obj: Some(obj), + force_mtime: None, } } @@ -44,6 +46,11 @@ impl Builder { self.follow = follow; } + /// Force all files to have the specified mtime. + pub fn force_mtime(&mut self, mtime: u64) { + self.force_mtime = Some(mtime); + } + /// Gets shared reference to the underlying object. pub fn get_ref(&self) -> &W { self.obj.as_ref().unwrap() @@ -237,7 +244,15 @@ impl Builder { pub fn append_path>(&mut self, path: P) -> io::Result<()> { let mode = self.mode.clone(); let follow = self.follow; - append_path_with_name(self.get_mut(), path.as_ref(), None, mode, follow) + let force_mtime = self.force_mtime; + append_path_with_name( + self.get_mut(), + path.as_ref(), + None, + mode, + follow, + force_mtime, + ) } /// Adds a file on the local filesystem to this archive under another name. @@ -275,12 +290,14 @@ impl Builder { ) -> io::Result<()> { let mode = self.mode.clone(); let follow = self.follow; + let force_mtime = self.force_mtime; append_path_with_name( self.get_mut(), path.as_ref(), Some(name.as_ref()), mode, follow, + force_mtime, ) } @@ -312,7 +329,8 @@ impl Builder { /// ``` pub fn append_file>(&mut self, path: P, file: &mut fs::File) -> io::Result<()> { let mode = self.mode.clone(); - append_file(self.get_mut(), path.as_ref(), file, mode) + let force_mtime = self.force_mtime; + append_file(self.get_mut(), path.as_ref(), file, mode, force_mtime) } /// Adds a directory to this archive with the given path as the name of the @@ -349,7 +367,8 @@ impl Builder { Q: AsRef, { let mode = self.mode.clone(); - append_dir(self.get_mut(), path.as_ref(), src_path.as_ref(), mode) + let force_mtime = self.force_mtime; + append_dir(self.get_mut(), path.as_ref(), src_path.as_ref(), mode, force_mtime) } /// Adds a directory and all of its contents (recursively) to this archive @@ -381,12 +400,14 @@ impl Builder { { let mode = self.mode.clone(); let follow = self.follow; + let force_mtime = self.force_mtime; append_dir_all( self.get_mut(), path.as_ref(), src_path.as_ref(), mode, follow, + force_mtime, ) } @@ -426,6 +447,7 @@ fn append_path_with_name( name: Option<&Path>, mode: HeaderMode, follow: bool, + force_mtime: Option, ) -> io::Result<()> { let stat = if follow { fs::metadata(path).map_err(|err| { @@ -444,9 +466,25 @@ fn append_path_with_name( }; let ar_name = name.unwrap_or(path); if stat.is_file() { - append_fs(dst, ar_name, &stat, &mut fs::File::open(path)?, mode, None) + append_fs( + dst, + ar_name, + &stat, + &mut fs::File::open(path)?, + mode, + None, + force_mtime, + ) } else if stat.is_dir() { - append_fs(dst, ar_name, &stat, &mut io::empty(), mode, None) + append_fs( + dst, + ar_name, + &stat, + &mut io::empty(), + mode, + None, + force_mtime, + ) } else if stat.file_type().is_symlink() { let link_name = fs::read_link(path)?; append_fs( @@ -456,11 +494,12 @@ fn append_path_with_name( &mut io::empty(), mode, Some(&link_name), + force_mtime, ) } else { #[cfg(unix)] { - append_special(dst, path, &stat, mode) + append_special(dst, path, &stat, mode, force_mtime) } #[cfg(not(unix))] { @@ -475,6 +514,7 @@ fn append_special( path: &Path, stat: &fs::Metadata, mode: HeaderMode, + force_mtime: Option, ) -> io::Result<()> { use ::std::os::unix::fs::{FileTypeExt, MetadataExt}; @@ -497,7 +537,7 @@ fn append_special( } let mut header = Header::new_gnu(); - header.set_metadata_in_mode(stat, mode); + header.set_metadata_in_mode(stat, mode, force_mtime); prepare_header_path(dst, &mut header, path)?; header.set_entry_type(entry_type); @@ -518,9 +558,10 @@ fn append_file( path: &Path, file: &mut fs::File, mode: HeaderMode, + force_mtime: Option, ) -> io::Result<()> { let stat = file.metadata()?; - append_fs(dst, path, &stat, file, mode, None) + append_fs(dst, path, &stat, file, mode, None, force_mtime) } fn append_dir( @@ -528,9 +569,10 @@ fn append_dir( path: &Path, src_path: &Path, mode: HeaderMode, + force_mtime: Option, ) -> io::Result<()> { let stat = fs::metadata(src_path)?; - append_fs(dst, path, &stat, &mut io::empty(), mode, None) + append_fs(dst, path, &stat, &mut io::empty(), mode, None, force_mtime) } fn prepare_header(size: u64, entry_type: u8) -> Header { @@ -605,11 +647,12 @@ fn append_fs( read: &mut dyn Read, mode: HeaderMode, link_name: Option<&Path>, + force_mtime: Option, ) -> io::Result<()> { let mut header = Header::new_gnu(); prepare_header_path(dst, &mut header, path)?; - header.set_metadata_in_mode(meta, mode); + header.set_metadata_in_mode(meta, mode, force_mtime); if let Some(link_name) = link_name { prepare_header_link(dst, &mut header, link_name)?; } @@ -623,6 +666,7 @@ fn append_dir_all( src_path: &Path, mode: HeaderMode, follow: bool, + force_mtime: Option, ) -> io::Result<()> { let mut stack = vec![(src_path.to_path_buf(), true, false)]; while let Some((src, is_dir, is_symlink)) = stack.pop() { @@ -635,22 +679,22 @@ fn append_dir_all( stack.push((entry.path(), file_type.is_dir(), file_type.is_symlink())); } if dest != Path::new("") { - append_dir(dst, &dest, &src, mode)?; + append_dir(dst, &dest, &src, mode, force_mtime)?; } } else if !follow && is_symlink { let stat = fs::symlink_metadata(&src)?; let link_name = fs::read_link(&src)?; - append_fs(dst, &dest, &stat, &mut io::empty(), mode, Some(&link_name))?; + append_fs(dst, &dest, &stat, &mut io::empty(), mode, Some(&link_name), force_mtime)?; } else { #[cfg(unix)] { let stat = fs::metadata(&src)?; if !stat.is_file() { - append_special(dst, &dest, &stat, mode)?; + append_special(dst, &dest, &stat, mode, force_mtime)?; continue; } } - append_file(dst, &dest, &mut fs::File::open(src)?, mode)?; + append_file(dst, &dest, &mut fs::File::open(src)?, mode, force_mtime)?; } } Ok(()) diff --git a/src/header.rs b/src/header.rs index 7e507fc7..638c4860 100644 --- a/src/header.rs +++ b/src/header.rs @@ -279,14 +279,14 @@ impl Header { /// This is useful for initializing a `Header` from the OS's metadata from a /// file. By default, this will use `HeaderMode::Complete` to include all /// metadata. - pub fn set_metadata(&mut self, meta: &fs::Metadata) { - self.fill_from(meta, HeaderMode::Complete); + pub fn set_metadata(&mut self, meta: &fs::Metadata, force_mtime: Option) { + self.fill_from(meta, HeaderMode::Complete, force_mtime); } /// Sets only the metadata relevant to the given HeaderMode in this header /// from the metadata argument provided. - pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) { - self.fill_from(meta, mode); + pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode, force_mtime: Option) { + self.fill_from(meta, mode, force_mtime); } /// Returns the size of entry's data this header represents. @@ -714,8 +714,8 @@ impl Header { .fold(0, |a, b| a + (*b as u32)) } - fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) { - self.fill_platform_from(meta, mode); + fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode, force_mtime: Option) { + self.fill_platform_from(meta, mode, force_mtime); // Set size of directories to zero self.set_size(if meta.is_dir() || meta.file_type().is_symlink() { 0 @@ -739,10 +739,15 @@ impl Header { } #[cfg(unix)] - fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) { + fn fill_platform_from( + &mut self, + meta: &fs::Metadata, + mode: HeaderMode, + force_mtime: Option, + ) { match mode { HeaderMode::Complete => { - self.set_mtime(meta.mtime() as u64); + self.set_mtime(force_mtime.unwrap_or(meta.mtime() as u64)); self.set_uid(meta.uid() as u64); self.set_gid(meta.gid() as u64); self.set_mode(meta.mode() as u32); diff --git a/tests/all.rs b/tests/all.rs index a7954e1f..f9066d7e 100644 --- a/tests/all.rs +++ b/tests/all.rs @@ -164,7 +164,7 @@ fn large_filename() { let filename = repeat("abcd/").take(50).collect::(); let mut header = Header::new_ustar(); header.set_path(&filename).unwrap(); - header.set_metadata(&t!(fs::metadata(&path))); + header.set_metadata(&t!(fs::metadata(&path)), None); header.set_cksum(); t!(ar.append(&header, &b"test"[..])); let too_long = repeat("abcd").take(200).collect::(); @@ -541,7 +541,7 @@ fn handling_incorrect_file_size() { let mut file = t!(File::open(&path)); let mut header = Header::new_old(); t!(header.set_path("somepath")); - header.set_metadata(&t!(file.metadata())); + header.set_metadata(&t!(file.metadata()), None); header.set_size(2048); // past the end of file null blocks header.set_cksum(); t!(ar.append(&header, &mut file)); @@ -751,7 +751,7 @@ fn backslash_treated_well() { // Unpack an archive with a backslash in the name let mut ar = Builder::new(Vec::::new()); let mut header = Header::new_gnu(); - header.set_metadata(&t!(fs::metadata(td.path()))); + header.set_metadata(&t!(fs::metadata(td.path())), None); header.set_size(0); for (a, b) in header.as_old_mut().name.iter_mut().zip(b"foo\\bar\x00") { *a = *b; diff --git a/tests/header/mod.rs b/tests/header/mod.rs index 86692e33..5d05ee66 100644 --- a/tests/header/mod.rs +++ b/tests/header/mod.rs @@ -188,7 +188,7 @@ fn set_metadata_deterministic() { perms.set_readonly(readonly); t!(fs::set_permissions(path, perms)); let mut h = Header::new_ustar(); - h.set_metadata_in_mode(&t!(path.metadata()), HeaderMode::Deterministic); + h.set_metadata_in_mode(&t!(path.metadata()), HeaderMode::Deterministic, None); Ok(h) }