diff --git a/src/entry.rs b/src/entry.rs index fbdec52c..d426e837 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -482,7 +482,16 @@ impl<'a> EntryFields<'a> { // As a result if we don't recognize the kind we just write out the file // as we would normally. - fs::File::create(dst).and_then(|mut f| { + // Remove an existing file, if any, to avoid writing through + // symlinks/hardlinks to weird locations. The tar archive says this is a + // regular file, so let's make it a regular file. + (|| -> io::Result<()> { + match fs::remove_file(dst) { + Ok(()) => {} + Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} + Err(e) => return Err(e) + } + let mut f = fs::File::create(dst)?; for io in self.data.drain(..) { match io { EntryIo::Data(mut d) => { @@ -500,7 +509,7 @@ impl<'a> EntryFields<'a> { } } Ok(()) - }).map_err(|e| { + })().map_err(|e| { let header = self.header.path_bytes(); TarError::new(&format!("failed to unpack `{}` into `{}`", String::from_utf8_lossy(&header), diff --git a/tests/entry.rs b/tests/entry.rs index 58f96aaa..f0e04572 100644 --- a/tests/entry.rs +++ b/tests/entry.rs @@ -283,3 +283,38 @@ fn modify_hard_link_just_created() { t!(t!(File::open(&test)).read_to_end(&mut contents)); assert_eq!(contents.len(), 0); } + +#[test] +fn modify_symlink_just_created() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("foo")); + t!(header.set_link_name("../test")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(1); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo")); + header.set_cksum(); + t!(ar.append(&header, &b"x"[..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(TempDir::new("tar")); + + let test = td.path().join("test"); + t!(File::create(&test)); + + let dir = td.path().join("dir"); + t!(ar.unpack(&dir)); + + let mut contents = Vec::new(); + t!(t!(File::open(&test)).read_to_end(&mut contents)); + assert_eq!(contents.len(), 0); +}