Skip to content

Commit

Permalink
Implement consistent, configurable overwriting behaviour for files an…
Browse files Browse the repository at this point in the history
…d symlinks (#229)

* implement consistent overwrite behaviour for files and symlinks

* added tests for overwriting behaviour (file & symlink)

* removed unnecessary line break
  • Loading branch information
moschroe committed Jun 17, 2020
1 parent 0b18e42 commit 57c5aae
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 12 deletions.
8 changes: 8 additions & 0 deletions src/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct ArchiveInner<R: ?Sized> {
unpack_xattrs: bool,
preserve_permissions: bool,
preserve_mtime: bool,
overwrite: bool,
ignore_zeros: bool,
obj: RefCell<R>,
}
Expand All @@ -48,6 +49,7 @@ impl<R: Read> Archive<R> {
unpack_xattrs: false,
preserve_permissions: false,
preserve_mtime: true,
overwrite: true,
ignore_zeros: false,
obj: RefCell::new(obj),
pos: Cell::new(0),
Expand Down Expand Up @@ -118,6 +120,11 @@ impl<R: Read> Archive<R> {
self.inner.preserve_permissions = preserve;
}

/// Indicate whether files and symlinks should be overwritten on extraction.
pub fn set_overwrite(&mut self, overwrite: bool) {
self.inner.overwrite = overwrite;
}

/// Indicate whether access time information is preserved when unpacking
/// this entry.
///
Expand Down Expand Up @@ -268,6 +275,7 @@ impl<'a> EntriesFields<'a> {
unpack_xattrs: self.archive.inner.unpack_xattrs,
preserve_permissions: self.archive.inner.preserve_permissions,
preserve_mtime: self.archive.inner.preserve_mtime,
overwrite: self.archive.inner.overwrite,
};

// Store where the next entry is, rounding up by 512 bytes (the size of
Expand Down
36 changes: 24 additions & 12 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub struct EntryFields<'a> {
pub unpack_xattrs: bool,
pub preserve_permissions: bool,
pub preserve_mtime: bool,
pub overwrite: bool,
}

pub enum EntryIo<'a> {
Expand Down Expand Up @@ -484,17 +485,26 @@ impl<'a> EntryFields<'a> {
)
})?;
} else {
symlink(&src, dst).map_err(|err| {
Error::new(
err.kind(),
format!(
"{} when symlinking {} to {}",
err,
src.display(),
dst.display()
),
)
})?;
symlink(&src, dst)
.or_else(|err_io| {
if err_io.kind() == io::ErrorKind::AlreadyExists && self.overwrite {
// remove dest and try once more
std::fs::remove_file(dst).and_then(|()| symlink(&src, dst))
} else {
Err(err_io)
}
})
.map_err(|err| {
Error::new(
err.kind(),
format!(
"{} when symlinking {} to {}",
err,
src.display(),
dst.display()
),
)
})?;
};
return Ok(Unpacked::__Nonexhaustive);

Expand Down Expand Up @@ -550,12 +560,14 @@ impl<'a> EntryFields<'a> {
let mut f = open(dst).or_else(|err| {
if err.kind() != ErrorKind::AlreadyExists {
Err(err)
} else {
} else if self.overwrite {
match fs::remove_file(dst) {
Ok(()) => open(dst),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => open(dst),
Err(e) => Err(e),
}
} else {
Err(err)
}
})?;
for io in self.data.drain(..) {
Expand Down
70 changes: 70 additions & 0 deletions tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,76 @@ fn extracting_directories() {
check_dirtree(&td);
}

#[test]
fn extracting_duplicate_file_fail() {
let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
let path_present = td.path().join("a");
t!(File::create(path_present));

let rdr = Cursor::new(tar!("reading_files.tar"));
let mut ar = Archive::new(rdr);
ar.set_overwrite(false);
if let Err(err) = ar.unpack(td.path()) {
if err.kind() == std::io::ErrorKind::AlreadyExists {
// as expected with overwrite false
return;
}
panic!("unexpected error: {:?}", err);
}
panic!(
"unpack() should have returned an error of kind {:?}, returned Ok",
std::io::ErrorKind::AlreadyExists
)
}

#[test]
fn extracting_duplicate_file_succeed() {
let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
let path_present = td.path().join("a");
t!(File::create(path_present));

let rdr = Cursor::new(tar!("reading_files.tar"));
let mut ar = Archive::new(rdr);
ar.set_overwrite(true);
t!(ar.unpack(td.path()));
}

#[test]
#[cfg(unix)]
fn extracting_duplicate_link_fail() {
let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
let path_present = td.path().join("lnk");
t!(std::os::unix::fs::symlink("file", path_present));

let rdr = Cursor::new(tar!("link.tar"));
let mut ar = Archive::new(rdr);
ar.set_overwrite(false);
if let Err(err) = ar.unpack(td.path()) {
if err.kind() == std::io::ErrorKind::AlreadyExists {
// as expected with overwrite false
return;
}
panic!("unexpected error: {:?}", err);
}
panic!(
"unpack() should have returned an error of kind {:?}, returned Ok",
std::io::ErrorKind::AlreadyExists
)
}

#[test]
#[cfg(unix)]
fn extracting_duplicate_link_succeed() {
let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
let path_present = td.path().join("lnk");
t!(std::os::unix::fs::symlink("file", path_present));

let rdr = Cursor::new(tar!("link.tar"));
let mut ar = Archive::new(rdr);
ar.set_overwrite(true);
t!(ar.unpack(td.path()));
}

#[test]
#[cfg(all(unix, feature = "xattr"))]
fn xattrs() {
Expand Down

0 comments on commit 57c5aae

Please sign in to comment.