@@ -26,6 +26,7 @@ use std::mem;
26
26
use std:: mem:: size_of;
27
27
use std:: path:: { Path , PathBuf } ;
28
28
use std:: rc:: Rc ;
29
+ use std:: slice;
29
30
use std:: sync:: { Arc , OnceLock } ;
30
31
31
32
mod config;
@@ -90,9 +91,8 @@ pub(crate) mod zip_archive {
90
91
///
91
92
/// ```no_run
92
93
/// use std::io::prelude::*;
93
- /// use zip::read::ArchiveEntry;
94
94
/// fn list_zip_contents(reader: impl Read + Seek) -> zip::result::ZipResult<()> {
95
- /// use zip::HasZipMetadata ;
95
+ /// use zip::EntryData ;
96
96
/// let mut zip = zip::ZipArchive::new(reader)?;
97
97
///
98
98
/// for i in 0..zip.len() {
@@ -120,11 +120,11 @@ use crate::types::ffi::S_IFLNK;
120
120
use crate :: unstable:: { path_to_string, LittleEndianReadExt } ;
121
121
122
122
use crate :: crc32:: non_crypto:: Crc32Reader as NewCrc32Reader ;
123
+ pub use crate :: unstable:: read:: ZipEntry ;
123
124
use crate :: unstable:: read:: {
124
125
construct_decompressing_reader, find_entry_content_range, CryptoEntryReader ,
125
126
CryptoKeyValidationSource ,
126
127
} ;
127
- pub use crate :: unstable:: read:: { ArchiveEntry , ZipEntry } ;
128
128
129
129
pub use zip_archive:: ZipArchive ;
130
130
@@ -1638,14 +1638,10 @@ pub trait HasZipMetadata {
1638
1638
fn get_metadata ( & self ) -> & ZipFileData ;
1639
1639
}
1640
1640
1641
- /// Methods for retrieving information on zip files
1642
- impl < ' a > ZipFile < ' a > {
1643
- pub ( crate ) fn take_raw_reader ( & mut self ) -> io:: Result < io:: Take < & ' a mut dyn Read > > {
1644
- mem:: replace ( & mut self . reader , ZipFileReader :: NoReader ) . into_inner ( )
1645
- }
1646
-
1641
+ /// Trait to expose attributes of a single zip file entry.
1642
+ pub trait EntryData : HasZipMetadata {
1647
1643
/// Get the version of the file
1648
- pub fn version_made_by ( & self ) -> ( u8 , u8 ) {
1644
+ fn version_made_by ( & self ) -> ( u8 , u8 ) {
1649
1645
(
1650
1646
self . get_metadata ( ) . version_made_by / 10 ,
1651
1647
self . get_metadata ( ) . version_made_by % 10 ,
@@ -1664,17 +1660,137 @@ impl<'a> ZipFile<'a> {
1664
1660
///
1665
1661
/// You can use the [`ZipFile::enclosed_name`] method to validate the name
1666
1662
/// as a safe path.
1667
- pub fn name ( & self ) -> & str {
1663
+ fn name ( & self ) -> & str {
1668
1664
& self . get_metadata ( ) . file_name
1669
1665
}
1670
1666
1671
1667
/// Get the name of the file, in the raw (internal) byte representation.
1672
1668
///
1673
1669
/// The encoding of this data is currently undefined.
1674
- pub fn name_raw ( & self ) -> & [ u8 ] {
1670
+ fn name_raw ( & self ) -> & [ u8 ] {
1675
1671
& self . get_metadata ( ) . file_name_raw
1676
1672
}
1677
1673
1674
+ /// Rewrite the path, ignoring any path components with special meaning.
1675
+ ///
1676
+ /// - Absolute paths are made relative
1677
+ /// - [`ParentDir`]s are ignored
1678
+ /// - Truncates the filename at a NULL byte
1679
+ ///
1680
+ /// This is appropriate if you need to be able to extract *something* from
1681
+ /// any archive, but will easily misrepresent trivial paths like
1682
+ /// `foo/../bar` as `foo/bar` (instead of `bar`). Because of this,
1683
+ /// [`ZipFile::enclosed_name`] is the better option in most scenarios.
1684
+ ///
1685
+ /// [`ParentDir`]: `Component::ParentDir`
1686
+ fn mangled_name ( & self ) -> PathBuf {
1687
+ self . get_metadata ( ) . file_name_sanitized ( )
1688
+ }
1689
+
1690
+ /// Ensure the file path is safe to use as a [`Path`].
1691
+ ///
1692
+ /// - It can't contain NULL bytes
1693
+ /// - It can't resolve to a path outside the current directory
1694
+ /// > `foo/../bar` is fine, `foo/../../bar` is not.
1695
+ /// - It can't be an absolute path
1696
+ ///
1697
+ /// This will read well-formed ZIP files correctly, and is resistant
1698
+ /// to path-based exploits. It is recommended over
1699
+ /// [`ZipFile::mangled_name`].
1700
+ fn enclosed_name ( & self ) -> Option < PathBuf > {
1701
+ self . get_metadata ( ) . enclosed_name ( )
1702
+ }
1703
+
1704
+ /// Get the comment of the file
1705
+ fn comment ( & self ) -> & str {
1706
+ & self . get_metadata ( ) . file_comment
1707
+ }
1708
+
1709
+ /// Get the compression method used to store the file
1710
+ fn compression ( & self ) -> CompressionMethod {
1711
+ self . get_metadata ( ) . compression_method
1712
+ }
1713
+
1714
+ /// Get the size of the file, in bytes, in the archive
1715
+ fn compressed_size ( & self ) -> u64 {
1716
+ self . get_metadata ( ) . compressed_size
1717
+ }
1718
+
1719
+ /// Get the size of the file, in bytes, when uncompressed
1720
+ fn size ( & self ) -> u64 {
1721
+ self . get_metadata ( ) . uncompressed_size
1722
+ }
1723
+
1724
+ /// Get if the files is encrypted or not
1725
+ fn encrypted ( & self ) -> bool {
1726
+ self . get_metadata ( ) . encrypted
1727
+ }
1728
+
1729
+ /// Get the time the file was last modified
1730
+ fn last_modified ( & self ) -> Option < DateTime > {
1731
+ self . get_metadata ( ) . last_modified_time
1732
+ }
1733
+
1734
+ /// Returns whether the file is actually a directory
1735
+ fn is_dir ( & self ) -> bool {
1736
+ self . get_metadata ( ) . is_dir ( )
1737
+ }
1738
+
1739
+ /// Returns whether the file is actually a symbolic link
1740
+ fn is_symlink ( & self ) -> bool {
1741
+ self . unix_mode ( )
1742
+ . is_some_and ( |mode| mode & S_IFLNK == S_IFLNK )
1743
+ }
1744
+
1745
+ /// Returns whether the file is a normal file (i.e. not a directory or symlink)
1746
+ fn is_file ( & self ) -> bool {
1747
+ !self . is_dir ( ) && !self . is_symlink ( )
1748
+ }
1749
+
1750
+ /// Get unix mode for the file
1751
+ fn unix_mode ( & self ) -> Option < u32 > {
1752
+ self . get_metadata ( ) . unix_mode ( )
1753
+ }
1754
+
1755
+ /// Get the CRC32 hash of the original file
1756
+ fn crc32 ( & self ) -> u32 {
1757
+ self . get_metadata ( ) . crc32
1758
+ }
1759
+
1760
+ /// Get the extra data of the zip header for this file
1761
+ fn extra_data ( & self ) -> Option < & [ u8 ] > {
1762
+ self . get_metadata ( )
1763
+ . extra_field
1764
+ . as_deref ( )
1765
+ . map ( |v| v. as_ref ( ) )
1766
+ }
1767
+
1768
+ /// Get the starting offset of the data of the compressed file
1769
+ fn data_start ( & self ) -> u64 {
1770
+ * self . get_metadata ( ) . data_start . get ( ) . unwrap ( )
1771
+ }
1772
+
1773
+ /// Get the starting offset of the zip header for this file
1774
+ fn header_start ( & self ) -> u64 {
1775
+ self . get_metadata ( ) . header_start
1776
+ }
1777
+ /// Get the starting offset of the zip header in the central directory for this file
1778
+ fn central_header_start ( & self ) -> u64 {
1779
+ self . get_metadata ( ) . central_header_start
1780
+ }
1781
+
1782
+ /// iterate through all extra fields
1783
+ fn extra_data_fields ( & self ) -> slice:: Iter < ' _ , ExtraField > {
1784
+ self . get_metadata ( ) . extra_fields . iter ( )
1785
+ }
1786
+ }
1787
+
1788
+ /// Methods for retrieving information on zip files
1789
+ impl < ' a > ZipFile < ' a > {
1790
+ pub ( crate ) fn take_raw_reader ( & mut self ) -> io:: Result < io:: Take < & ' a mut dyn Read > > {
1791
+ mem:: replace ( & mut self . reader , ZipFileReader :: NoReader ) . into_inner ( )
1792
+ }
1793
+
1678
1794
/// Get the name of the file in a sanitized form. It truncates the name to the first NULL byte,
1679
1795
/// removes a leading '/' and removes '..' parts.
1680
1796
#[ deprecated(
@@ -1685,6 +1801,32 @@ impl<'a> ZipFile<'a> {
1685
1801
pub fn sanitized_name ( & self ) -> PathBuf {
1686
1802
self . mangled_name ( )
1687
1803
}
1804
+ }
1805
+
1806
+ /// Duplicated reimplementation of [`EntryData`] for backwards compatibility.
1807
+ impl < ' a > ZipFile < ' a > {
1808
+ /// Get the name of the file
1809
+ ///
1810
+ /// # Warnings
1811
+ ///
1812
+ /// It is dangerous to use this name directly when extracting an archive.
1813
+ /// It may contain an absolute path (`/etc/shadow`), or break out of the
1814
+ /// current directory (`../runtime`). Carelessly writing to these paths
1815
+ /// allows an attacker to craft a ZIP archive that will overwrite critical
1816
+ /// files.
1817
+ ///
1818
+ /// You can use the [`ZipFile::enclosed_name`] method to validate the name
1819
+ /// as a safe path.
1820
+ pub fn name ( & self ) -> & str {
1821
+ EntryData :: name ( self )
1822
+ }
1823
+
1824
+ /// Get the name of the file, in the raw (internal) byte representation.
1825
+ ///
1826
+ /// The encoding of this data is currently undefined.
1827
+ pub fn name_raw ( & self ) -> & [ u8 ] {
1828
+ EntryData :: name_raw ( self )
1829
+ }
1688
1830
1689
1831
/// Rewrite the path, ignoring any path components with special meaning.
1690
1832
///
@@ -1697,9 +1839,9 @@ impl<'a> ZipFile<'a> {
1697
1839
/// `foo/../bar` as `foo/bar` (instead of `bar`). Because of this,
1698
1840
/// [`ZipFile::enclosed_name`] is the better option in most scenarios.
1699
1841
///
1700
- /// [`ParentDir`]: `PathBuf:: Component::ParentDir`
1842
+ /// [`ParentDir`]: `Component::ParentDir`
1701
1843
pub fn mangled_name ( & self ) -> PathBuf {
1702
- self . get_metadata ( ) . file_name_sanitized ( )
1844
+ EntryData :: mangled_name ( self )
1703
1845
}
1704
1846
1705
1847
/// Ensure the file path is safe to use as a [`Path`].
@@ -1713,93 +1855,86 @@ impl<'a> ZipFile<'a> {
1713
1855
/// to path-based exploits. It is recommended over
1714
1856
/// [`ZipFile::mangled_name`].
1715
1857
pub fn enclosed_name ( & self ) -> Option < PathBuf > {
1716
- self . get_metadata ( ) . enclosed_name ( )
1858
+ EntryData :: enclosed_name ( self )
1717
1859
}
1718
1860
1719
1861
/// Get the comment of the file
1720
1862
pub fn comment ( & self ) -> & str {
1721
- & self . get_metadata ( ) . file_comment
1863
+ EntryData :: comment ( self )
1722
1864
}
1723
1865
1724
1866
/// Get the compression method used to store the file
1725
1867
pub fn compression ( & self ) -> CompressionMethod {
1726
- self . get_metadata ( ) . compression_method
1727
- }
1728
-
1729
- /// Get if the files is encrypted or not
1730
- pub fn encrypted ( & self ) -> bool {
1731
- self . data . encrypted
1868
+ EntryData :: compression ( self )
1732
1869
}
1733
1870
1734
1871
/// Get the size of the file, in bytes, in the archive
1735
1872
pub fn compressed_size ( & self ) -> u64 {
1736
- self . get_metadata ( ) . compressed_size
1873
+ EntryData :: compressed_size ( self )
1737
1874
}
1738
1875
1739
1876
/// Get the size of the file, in bytes, when uncompressed
1740
1877
pub fn size ( & self ) -> u64 {
1741
- self . get_metadata ( ) . uncompressed_size
1878
+ EntryData :: size ( self )
1879
+ }
1880
+
1881
+ /// Get if the files is encrypted or not
1882
+ pub fn encrypted ( & self ) -> bool {
1883
+ EntryData :: encrypted ( self )
1742
1884
}
1743
1885
1744
1886
/// Get the time the file was last modified
1745
1887
pub fn last_modified ( & self ) -> Option < DateTime > {
1746
- self . data . last_modified_time
1888
+ EntryData :: last_modified ( self )
1747
1889
}
1748
1890
1749
1891
/// Returns whether the file is actually a directory
1750
1892
pub fn is_dir ( & self ) -> bool {
1751
- self . data . is_dir ( )
1893
+ EntryData :: is_dir ( self )
1752
1894
}
1753
1895
1754
1896
/// Returns whether the file is actually a symbolic link
1755
1897
pub fn is_symlink ( & self ) -> bool {
1756
- self . unix_mode ( )
1757
- . is_some_and ( |mode| mode & S_IFLNK == S_IFLNK )
1898
+ EntryData :: is_symlink ( self )
1758
1899
}
1759
1900
1760
1901
/// Returns whether the file is a normal file (i.e. not a directory or symlink)
1761
1902
pub fn is_file ( & self ) -> bool {
1762
- ! self . is_dir ( ) && ! self . is_symlink ( )
1903
+ EntryData :: is_file ( self )
1763
1904
}
1764
1905
1765
1906
/// Get unix mode for the file
1766
1907
pub fn unix_mode ( & self ) -> Option < u32 > {
1767
- self . get_metadata ( ) . unix_mode ( )
1908
+ EntryData :: unix_mode ( self )
1768
1909
}
1769
1910
1770
1911
/// Get the CRC32 hash of the original file
1771
1912
pub fn crc32 ( & self ) -> u32 {
1772
- self . get_metadata ( ) . crc32
1913
+ EntryData :: crc32 ( self )
1773
1914
}
1774
1915
1775
1916
/// Get the extra data of the zip header for this file
1776
1917
pub fn extra_data ( & self ) -> Option < & [ u8 ] > {
1777
- self . get_metadata ( )
1778
- . extra_field
1779
- . as_deref ( )
1780
- . map ( |v| v. as_ref ( ) )
1918
+ EntryData :: extra_data ( self )
1781
1919
}
1782
1920
1783
1921
/// Get the starting offset of the data of the compressed file
1784
1922
pub fn data_start ( & self ) -> u64 {
1785
- * self . data . data_start . get ( ) . unwrap ( )
1923
+ EntryData :: data_start ( self )
1786
1924
}
1787
1925
1788
1926
/// Get the starting offset of the zip header for this file
1789
1927
pub fn header_start ( & self ) -> u64 {
1790
- self . get_metadata ( ) . header_start
1928
+ EntryData :: header_start ( self )
1791
1929
}
1792
1930
/// Get the starting offset of the zip header in the central directory for this file
1793
1931
pub fn central_header_start ( & self ) -> u64 {
1794
- self . get_metadata ( ) . central_header_start
1932
+ EntryData :: central_header_start ( self )
1795
1933
}
1796
- }
1797
1934
1798
- /// Methods for retrieving information on zip files
1799
- impl < ' a > ZipFile < ' a > {
1800
1935
/// iterate through all extra fields
1801
- pub fn extra_data_fields ( & self ) -> impl Iterator < Item = & ExtraField > {
1802
- self . data . extra_fields . iter ( )
1936
+ pub fn extra_data_fields ( & self ) -> slice :: Iter < ' _ , ExtraField > {
1937
+ EntryData :: extra_data_fields ( self )
1803
1938
}
1804
1939
}
1805
1940
@@ -1809,6 +1944,8 @@ impl<'a> HasZipMetadata for ZipFile<'a> {
1809
1944
}
1810
1945
}
1811
1946
1947
+ impl < ' a > EntryData for ZipFile < ' a > { }
1948
+
1812
1949
impl < ' a > Read for ZipFile < ' a > {
1813
1950
fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
1814
1951
self . reader . read ( buf)
0 commit comments