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

Add a force_mtime() function to allow overriding all mtime fields #337

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
72 changes: 58 additions & 14 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct Builder<W: Write> {
follow: bool,
finished: bool,
obj: Option<W>,
force_mtime: Option<u64>,
}

impl<W: Write> Builder<W> {
Expand All @@ -28,6 +29,7 @@ impl<W: Write> Builder<W> {
follow: true,
finished: false,
obj: Some(obj),
force_mtime: None,
}
}

Expand All @@ -44,6 +46,11 @@ impl<W: Write> Builder<W> {
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()
Expand Down Expand Up @@ -237,7 +244,15 @@ impl<W: Write> Builder<W> {
pub fn append_path<P: AsRef<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.
Expand Down Expand Up @@ -275,12 +290,14 @@ impl<W: Write> Builder<W> {
) -> 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,
)
}

Expand Down Expand Up @@ -312,7 +329,8 @@ impl<W: Write> Builder<W> {
/// ```
pub fn append_file<P: AsRef<Path>>(&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
Expand Down Expand Up @@ -349,7 +367,8 @@ impl<W: Write> Builder<W> {
Q: AsRef<Path>,
{
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
Expand Down Expand Up @@ -381,12 +400,14 @@ impl<W: Write> Builder<W> {
{
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,
)
}

Expand Down Expand Up @@ -426,6 +447,7 @@ fn append_path_with_name(
name: Option<&Path>,
mode: HeaderMode,
follow: bool,
force_mtime: Option<u64>,
) -> io::Result<()> {
let stat = if follow {
fs::metadata(path).map_err(|err| {
Expand All @@ -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(
Expand All @@ -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))]
{
Expand All @@ -475,6 +514,7 @@ fn append_special(
path: &Path,
stat: &fs::Metadata,
mode: HeaderMode,
force_mtime: Option<u64>,
) -> io::Result<()> {
use ::std::os::unix::fs::{FileTypeExt, MetadataExt};

Expand All @@ -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);
Expand All @@ -518,19 +558,21 @@ fn append_file(
path: &Path,
file: &mut fs::File,
mode: HeaderMode,
force_mtime: Option<u64>,
) -> 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(
dst: &mut dyn Write,
path: &Path,
src_path: &Path,
mode: HeaderMode,
force_mtime: Option<u64>,
) -> 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 {
Expand Down Expand Up @@ -605,11 +647,12 @@ fn append_fs(
read: &mut dyn Read,
mode: HeaderMode,
link_name: Option<&Path>,
force_mtime: Option<u64>,
) -> 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)?;
}
Expand All @@ -623,6 +666,7 @@ fn append_dir_all(
src_path: &Path,
mode: HeaderMode,
follow: bool,
force_mtime: Option<u64>,
) -> io::Result<()> {
let mut stack = vec![(src_path.to_path_buf(), true, false)];
while let Some((src, is_dir, is_symlink)) = stack.pop() {
Expand All @@ -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(())
Expand Down
21 changes: 13 additions & 8 deletions src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64>) {
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<u64>) {
self.fill_from(meta, mode, force_mtime);
}

/// Returns the size of entry's data this header represents.
Expand Down Expand Up @@ -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<u64>) {
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
Expand All @@ -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<u64>,
) {
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);
Expand Down
6 changes: 3 additions & 3 deletions tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn large_filename() {
let filename = repeat("abcd/").take(50).collect::<String>();
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::<String>();
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -751,7 +751,7 @@ fn backslash_treated_well() {
// Unpack an archive with a backslash in the name
let mut ar = Builder::new(Vec::<u8>::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;
Expand Down
2 changes: 1 addition & 1 deletion tests/header/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Loading