]> git.proxmox.com Git - cargo.git/blobdiff - vendor/tar/src/archive.rs
New upstream version 0.63.1
[cargo.git] / vendor / tar / src / archive.rs
index 8c3333227db1088aac8a56eb9f781ea368871003..1bed5124909e9410bc488f925c8de5fd4ea1216c 100644 (file)
@@ -1,8 +1,9 @@
 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;
 
@@ -35,8 +36,12 @@ pub struct Entries<'a, R: 'a + Read> {
     _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,
@@ -71,7 +76,7 @@ impl<R: Read> Archive<R> {
     /// 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,
         })
@@ -143,8 +148,29 @@ impl<R: Read> Archive<R> {
     }
 }
 
-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 \
@@ -153,6 +179,7 @@ impl<'a> Archive<dyn Read + 'a> {
         }
         Ok(EntriesFields {
             archive: self,
+            seekable_archive,
             done: false,
             next: 0,
             raw: false,
@@ -162,7 +189,7 @@ impl<'a> Archive<dyn Read + 'a> {
     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 '\\?\'
@@ -176,7 +203,7 @@ impl<'a> Archive<dyn Read + 'a> {
         // 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);
@@ -190,19 +217,6 @@ impl<'a> Archive<dyn Read + 'a> {
 
         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> {
@@ -241,7 +255,7 @@ impl<'a> EntriesFields<'a> {
         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())? {
@@ -298,8 +312,13 @@ impl<'a> EntriesFields<'a> {
 
         // 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()))
     }
@@ -476,6 +495,26 @@ impl<'a> EntriesFields<'a> {
         }
         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> {
@@ -502,10 +541,17 @@ 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)
     }
 }