@@ -26,6 +26,7 @@ use std::mem;
2626use std:: mem:: size_of;
2727use std:: path:: { Path , PathBuf } ;
2828use std:: rc:: Rc ;
29+ use std:: slice;
2930use std:: sync:: { Arc , OnceLock } ;
3031
3132mod config;
@@ -90,9 +91,8 @@ pub(crate) mod zip_archive {
9091 ///
9192 /// ```no_run
9293 /// use std::io::prelude::*;
93- /// use zip::read::ArchiveEntry;
9494 /// fn list_zip_contents(reader: impl Read + Seek) -> zip::result::ZipResult<()> {
95- /// use zip::HasZipMetadata ;
95+ /// use zip::EntryData ;
9696 /// let mut zip = zip::ZipArchive::new(reader)?;
9797 ///
9898 /// for i in 0..zip.len() {
@@ -120,11 +120,11 @@ use crate::types::ffi::S_IFLNK;
120120use crate :: unstable:: { path_to_string, LittleEndianReadExt } ;
121121
122122use crate :: crc32:: non_crypto:: Crc32Reader as NewCrc32Reader ;
123+ pub use crate :: unstable:: read:: ZipEntry ;
123124use crate :: unstable:: read:: {
124125 construct_decompressing_reader, find_entry_content_range, CryptoEntryReader ,
125126 CryptoKeyValidationSource ,
126127} ;
127- pub use crate :: unstable:: read:: { ArchiveEntry , ZipEntry } ;
128128
129129pub use zip_archive:: ZipArchive ;
130130
@@ -1638,14 +1638,10 @@ pub trait HasZipMetadata {
16381638 fn get_metadata ( & self ) -> & ZipFileData ;
16391639}
16401640
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 {
16471643 /// Get the version of the file
1648- pub fn version_made_by ( & self ) -> ( u8 , u8 ) {
1644+ fn version_made_by ( & self ) -> ( u8 , u8 ) {
16491645 (
16501646 self . get_metadata ( ) . version_made_by / 10 ,
16511647 self . get_metadata ( ) . version_made_by % 10 ,
@@ -1664,17 +1660,137 @@ impl<'a> ZipFile<'a> {
16641660 ///
16651661 /// You can use the [`ZipFile::enclosed_name`] method to validate the name
16661662 /// as a safe path.
1667- pub fn name ( & self ) -> & str {
1663+ fn name ( & self ) -> & str {
16681664 & self . get_metadata ( ) . file_name
16691665 }
16701666
16711667 /// Get the name of the file, in the raw (internal) byte representation.
16721668 ///
16731669 /// The encoding of this data is currently undefined.
1674- pub fn name_raw ( & self ) -> & [ u8 ] {
1670+ fn name_raw ( & self ) -> & [ u8 ] {
16751671 & self . get_metadata ( ) . file_name_raw
16761672 }
16771673
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+
16781794 /// Get the name of the file in a sanitized form. It truncates the name to the first NULL byte,
16791795 /// removes a leading '/' and removes '..' parts.
16801796 #[ deprecated(
@@ -1685,6 +1801,32 @@ impl<'a> ZipFile<'a> {
16851801 pub fn sanitized_name ( & self ) -> PathBuf {
16861802 self . mangled_name ( )
16871803 }
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+ }
16881830
16891831 /// Rewrite the path, ignoring any path components with special meaning.
16901832 ///
@@ -1697,9 +1839,9 @@ impl<'a> ZipFile<'a> {
16971839 /// `foo/../bar` as `foo/bar` (instead of `bar`). Because of this,
16981840 /// [`ZipFile::enclosed_name`] is the better option in most scenarios.
16991841 ///
1700- /// [`ParentDir`]: `PathBuf:: Component::ParentDir`
1842+ /// [`ParentDir`]: `Component::ParentDir`
17011843 pub fn mangled_name ( & self ) -> PathBuf {
1702- self . get_metadata ( ) . file_name_sanitized ( )
1844+ EntryData :: mangled_name ( self )
17031845 }
17041846
17051847 /// Ensure the file path is safe to use as a [`Path`].
@@ -1713,93 +1855,86 @@ impl<'a> ZipFile<'a> {
17131855 /// to path-based exploits. It is recommended over
17141856 /// [`ZipFile::mangled_name`].
17151857 pub fn enclosed_name ( & self ) -> Option < PathBuf > {
1716- self . get_metadata ( ) . enclosed_name ( )
1858+ EntryData :: enclosed_name ( self )
17171859 }
17181860
17191861 /// Get the comment of the file
17201862 pub fn comment ( & self ) -> & str {
1721- & self . get_metadata ( ) . file_comment
1863+ EntryData :: comment ( self )
17221864 }
17231865
17241866 /// Get the compression method used to store the file
17251867 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 )
17321869 }
17331870
17341871 /// Get the size of the file, in bytes, in the archive
17351872 pub fn compressed_size ( & self ) -> u64 {
1736- self . get_metadata ( ) . compressed_size
1873+ EntryData :: compressed_size ( self )
17371874 }
17381875
17391876 /// Get the size of the file, in bytes, when uncompressed
17401877 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 )
17421884 }
17431885
17441886 /// Get the time the file was last modified
17451887 pub fn last_modified ( & self ) -> Option < DateTime > {
1746- self . data . last_modified_time
1888+ EntryData :: last_modified ( self )
17471889 }
17481890
17491891 /// Returns whether the file is actually a directory
17501892 pub fn is_dir ( & self ) -> bool {
1751- self . data . is_dir ( )
1893+ EntryData :: is_dir ( self )
17521894 }
17531895
17541896 /// Returns whether the file is actually a symbolic link
17551897 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 )
17581899 }
17591900
17601901 /// Returns whether the file is a normal file (i.e. not a directory or symlink)
17611902 pub fn is_file ( & self ) -> bool {
1762- ! self . is_dir ( ) && ! self . is_symlink ( )
1903+ EntryData :: is_file ( self )
17631904 }
17641905
17651906 /// Get unix mode for the file
17661907 pub fn unix_mode ( & self ) -> Option < u32 > {
1767- self . get_metadata ( ) . unix_mode ( )
1908+ EntryData :: unix_mode ( self )
17681909 }
17691910
17701911 /// Get the CRC32 hash of the original file
17711912 pub fn crc32 ( & self ) -> u32 {
1772- self . get_metadata ( ) . crc32
1913+ EntryData :: crc32 ( self )
17731914 }
17741915
17751916 /// Get the extra data of the zip header for this file
17761917 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 )
17811919 }
17821920
17831921 /// Get the starting offset of the data of the compressed file
17841922 pub fn data_start ( & self ) -> u64 {
1785- * self . data . data_start . get ( ) . unwrap ( )
1923+ EntryData :: data_start ( self )
17861924 }
17871925
17881926 /// Get the starting offset of the zip header for this file
17891927 pub fn header_start ( & self ) -> u64 {
1790- self . get_metadata ( ) . header_start
1928+ EntryData :: header_start ( self )
17911929 }
17921930 /// Get the starting offset of the zip header in the central directory for this file
17931931 pub fn central_header_start ( & self ) -> u64 {
1794- self . get_metadata ( ) . central_header_start
1932+ EntryData :: central_header_start ( self )
17951933 }
1796- }
17971934
1798- /// Methods for retrieving information on zip files
1799- impl < ' a > ZipFile < ' a > {
18001935 /// 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 )
18031938 }
18041939}
18051940
@@ -1809,6 +1944,8 @@ impl<'a> HasZipMetadata for ZipFile<'a> {
18091944 }
18101945}
18111946
1947+ impl < ' a > EntryData for ZipFile < ' a > { }
1948+
18121949impl < ' a > Read for ZipFile < ' a > {
18131950 fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
18141951 self . reader . read ( buf)
0 commit comments