diff --git a/src/entry.rs b/src/entry.rs index 86b8f49a..724e4278 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -51,6 +51,18 @@ pub enum EntryIo<'a> { Data(io::Take<&'a ArchiveInner>), } +/// When unpacking items the unpacked thing is returned to allow custom +/// additional handling by users. Today the File is returned, in future +/// the enum may be extended with kinds for links, directories etc. +#[derive(Debug)] +pub enum Unpacked { + /// A file was unpacked. + File(std::fs::File), + /// A directory, hardlink, symlink, or other node was unpacked. + #[doc(hidden)] + __Nonexhaustive, +} + impl<'a, R: Read> Entry<'a, R> { /// Returns the path name for this entry. /// @@ -182,7 +194,7 @@ impl<'a, R: Read> Entry<'a, R> { /// file.unpack(format!("file-{}", i)).unwrap(); /// } /// ``` - pub fn unpack>(&mut self, dst: P) -> io::Result<()> { + pub fn unpack>(&mut self, dst: P) -> io::Result { self.fields.unpack(None, dst.as_ref()) } @@ -417,14 +429,25 @@ impl<'a> EntryFields<'a> { } /// Returns access to the header of this entry in the archive. - fn unpack(&mut self, target_base: Option<&Path>, dst: &Path) -> io::Result<()> { + fn unpack(&mut self, target_base: Option<&Path>, dst: &Path) -> io::Result { let kind = self.header.entry_type(); if kind.is_dir() { - return self - .unpack_dir(dst) - .and_then(|_| self.header.mode()) - .and_then(|mode| set_perms(dst, None, mode, self.preserve_permissions)); + self.unpack_dir(dst)?; + if let Ok(mode) = self.header.mode() { + set_perms(dst, None, mode, self.preserve_permissions).map_err(|e| { + TarError::new( + &format!( + "failed to set permissions to {:o} \ + for `{}`", + mode, + dst.display() + ), + e, + ) + })?; + return Ok(Unpacked::__Nonexhaustive); + } } else if kind.is_hard_link() || kind.is_symlink() { let src = match self.link_name()? { Some(name) => name, @@ -443,7 +466,7 @@ impl<'a> EntryFields<'a> { ))); } - return if kind.is_hard_link() { + if kind.is_hard_link() { let link_src = match target_base { // If we're unpacking within a directory then ensure that // the destination of this hard link is both present and @@ -473,7 +496,7 @@ impl<'a> EntryFields<'a> { dst.display() ), ) - }) + })?; } else { symlink(&src, dst).map_err(|err| { Error::new( @@ -485,8 +508,9 @@ impl<'a> EntryFields<'a> { dst.display() ), ) - }) + })?; }; + return Ok(Unpacked::__Nonexhaustive); #[cfg(target_arch = "wasm32")] #[allow(unused_variables)] @@ -508,14 +532,16 @@ impl<'a> EntryFields<'a> { || kind.is_gnu_longname() || kind.is_gnu_longlink() { - return Ok(()); + return Ok(Unpacked::__Nonexhaustive); }; // Old BSD-tar compatibility. // Names that have a trailing slash should be treated as a directory. // Only applies to old headers. if self.header.as_ustar().is_none() && self.path_bytes().ends_with(b"/") { - return self.unpack_dir(dst); + self.unpack_dir(dst)?; + // Why no permission setting? + return Ok(Unpacked::__Nonexhaustive); } // Note the lack of `else` clause above. According to the FreeBSD @@ -616,7 +642,7 @@ impl<'a> EntryFields<'a> { if self.unpack_xattrs { set_xattrs(self, dst)?; } - return Ok(()); + return Ok(Unpacked::File(f)); #[cfg(any(unix, target_os = "redox"))] fn set_perms( diff --git a/src/lib.rs b/src/lib.rs index 10adcb50..52251cd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ use std::io::{Error, ErrorKind}; pub use crate::archive::{Archive, Entries}; pub use crate::builder::Builder; -pub use crate::entry::Entry; +pub use crate::entry::{Entry, Unpacked}; pub use crate::entry_type::EntryType; pub use crate::header::GnuExtSparseHeader; pub use crate::header::{GnuHeader, GnuSparseHeader, Header, HeaderMode, OldHeader, UstarHeader};