use std::cell::{Cell, RefCell};
use std::cmp;
+use std::convert::TryFrom;
use std::fs;
-use std::io;
use std::io::prelude::*;
+use std::io::{self, SeekFrom};
use std::marker;
use std::path::Path;
_ignored: marker::PhantomData<&'a Archive<R>>,
}
+trait SeekRead: Read + Seek {}
+impl<R: Read + Seek> SeekRead for R {}
+
struct EntriesFields<'a> {
archive: &'a Archive<dyn Read + 'a>,
+ seekable_archive: Option<&'a Archive<dyn SeekRead + 'a>>,
next: u64,
done: bool,
raw: bool,
/// corrupted.
pub fn entries(&mut self) -> io::Result<Entries<R>> {
let me: &mut Archive<dyn Read> = self;
- me._entries().map(|fields| Entries {
+ me._entries(None).map(|fields| Entries {
fields: fields,
_ignored: marker::PhantomData,
})
}
}
-impl<'a> Archive<dyn Read + 'a> {
- fn _entries(&mut self) -> io::Result<EntriesFields> {
+impl<R: Seek + Read> Archive<R> {
+ /// Construct an iterator over the entries in this archive for a seekable
+ /// reader. Seek will be used to efficiently skip over file contents.
+ ///
+ /// Note that care must be taken to consider each entry within an archive in
+ /// sequence. If entries are processed out of sequence (from what the
+ /// iterator returns), then the contents read for each entry may be
+ /// corrupted.
+ pub fn entries_with_seek(&mut self) -> io::Result<Entries<R>> {
+ let me: &Archive<dyn Read> = self;
+ let me_seekable: &Archive<dyn SeekRead> = self;
+ me._entries(Some(me_seekable)).map(|fields| Entries {
+ fields: fields,
+ _ignored: marker::PhantomData,
+ })
+ }
+}
+
+impl Archive<dyn Read + '_> {
+ fn _entries<'a>(
+ &'a self,
+ seekable_archive: Option<&'a Archive<dyn SeekRead + 'a>>,
+ ) -> io::Result<EntriesFields<'a>> {
if self.inner.pos.get() != 0 {
return Err(other(
"cannot call entries unless archive is at \
}
Ok(EntriesFields {
archive: self,
+ seekable_archive,
done: false,
next: 0,
raw: false,
fn _unpack(&mut self, dst: &Path) -> io::Result<()> {
if dst.symlink_metadata().is_err() {
fs::create_dir_all(&dst)
- .map_err(|e| TarError::new(&format!("failed to create `{}`", dst.display()), e))?;
+ .map_err(|e| TarError::new(format!("failed to create `{}`", dst.display()), e))?;
}
// Canonicalizing the dst directory will prepend the path with '\\?\'
// descendants), to ensure that directory permissions do not interfer with descendant
// extraction.
let mut directories = Vec::new();
- for entry in self._entries()? {
+ for entry in self._entries(None)? {
let mut file = entry.map_err(|e| TarError::new("failed to iterate over archive", e))?;
if file.header().entry_type() == crate::EntryType::Directory {
directories.push(file);
Ok(())
}
-
- fn skip(&self, mut amt: u64) -> io::Result<()> {
- let mut buf = [0u8; 4096 * 8];
- while amt > 0 {
- let n = cmp::min(amt, buf.len() as u64);
- let n = (&self.inner).read(&mut buf[..n as usize])?;
- if n == 0 {
- return Err(other("unexpected EOF during skip"));
- }
- amt -= n as u64;
- }
- Ok(())
- }
}
impl<'a, R: Read> Entries<'a, R> {
loop {
// Seek to the start of the next header in the archive
let delta = self.next - self.archive.inner.pos.get();
- self.archive.skip(delta)?;
+ self.skip(delta)?;
// EOF is an indicator that we are at the end of the archive.
if !try_read_all(&mut &self.archive.inner, header.as_mut_bytes())? {
// Store where the next entry is, rounding up by 512 bytes (the size of
// a header);
- let size = (size + 511) & !(512 - 1);
- self.next += size;
+ let size = size
+ .checked_add(511)
+ .ok_or_else(|| other("size overflow"))?;
+ self.next = self
+ .next
+ .checked_add(size & !(512 - 1))
+ .ok_or_else(|| other("size overflow"))?;
Ok(Some(ret.into_entry()))
}
}
Ok(())
}
+
+ fn skip(&mut self, mut amt: u64) -> io::Result<()> {
+ if let Some(seekable_archive) = self.seekable_archive {
+ let pos = io::SeekFrom::Current(
+ i64::try_from(amt).map_err(|_| other("seek position out of bounds"))?,
+ );
+ (&seekable_archive.inner).seek(pos)?;
+ } else {
+ let mut buf = [0u8; 4096 * 8];
+ while amt > 0 {
+ let n = cmp::min(amt, buf.len() as u64);
+ let n = (&self.archive.inner).read(&mut buf[..n as usize])?;
+ if n == 0 {
+ return Err(other("unexpected EOF during skip"));
+ }
+ amt -= n as u64;
+ }
+ }
+ Ok(())
+ }
}
impl<'a> Iterator for EntriesFields<'a> {
impl<'a, R: ?Sized + Read> Read for &'a ArchiveInner<R> {
fn read(&mut self, into: &mut [u8]) -> io::Result<usize> {
- self.obj.borrow_mut().read(into).map(|i| {
- self.pos.set(self.pos.get() + i as u64);
- i
- })
+ let i = self.obj.borrow_mut().read(into)?;
+ self.pos.set(self.pos.get() + i as u64);
+ Ok(i)
+ }
+}
+
+impl<'a, R: ?Sized + Seek> Seek for &'a ArchiveInner<R> {
+ fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+ let pos = self.obj.borrow_mut().seek(pos)?;
+ self.pos.set(pos);
+ Ok(pos)
}
}