1 use crate::result::{ZipError, ZipResult};
2 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
3 use std::io;
4 use std::io::prelude::*;
5 
6 pub const LOCAL_FILE_HEADER_SIGNATURE: u32 = 0x04034b50;
7 pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE: u32 = 0x02014b50;
8 const CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06054b50;
9 pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06064b50;
10 const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE: u32 = 0x07064b50;
11 
12 pub const ZIP64_BYTES_THR: u64 = u32::MAX as u64;
13 pub const ZIP64_ENTRY_THR: usize = u16::MAX as usize;
14 
15 pub struct CentralDirectoryEnd {
16     pub disk_number: u16,
17     pub disk_with_central_directory: u16,
18     pub number_of_files_on_this_disk: u16,
19     pub number_of_files: u16,
20     pub central_directory_size: u32,
21     pub central_directory_offset: u32,
22     pub zip_file_comment: Vec<u8>,
23 }
24 
25 impl CentralDirectoryEnd {
26     // Per spec 4.4.1.4 - a CentralDirectoryEnd field might be insufficient to hold the
27     // required data. In this case the file SHOULD contain a ZIP64 format record
28     // and the field of this record will be set to -1
record_too_small(&self) -> bool29     pub(crate) fn record_too_small(&self) -> bool {
30         self.disk_number == 0xFFFF
31             || self.disk_with_central_directory == 0xFFFF
32             || self.number_of_files_on_this_disk == 0xFFFF
33             || self.number_of_files == 0xFFFF
34             || self.central_directory_size == 0xFFFFFFFF
35             || self.central_directory_offset == 0xFFFFFFFF
36     }
37 
parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>38     pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
39         let magic = reader.read_u32::<LittleEndian>()?;
40         if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
41             return Err(ZipError::InvalidArchive("Invalid digital signature header"));
42         }
43         let disk_number = reader.read_u16::<LittleEndian>()?;
44         let disk_with_central_directory = reader.read_u16::<LittleEndian>()?;
45         let number_of_files_on_this_disk = reader.read_u16::<LittleEndian>()?;
46         let number_of_files = reader.read_u16::<LittleEndian>()?;
47         let central_directory_size = reader.read_u32::<LittleEndian>()?;
48         let central_directory_offset = reader.read_u32::<LittleEndian>()?;
49         let zip_file_comment_length = reader.read_u16::<LittleEndian>()? as usize;
50         let mut zip_file_comment = vec![0; zip_file_comment_length];
51         reader.read_exact(&mut zip_file_comment)?;
52 
53         Ok(CentralDirectoryEnd {
54             disk_number,
55             disk_with_central_directory,
56             number_of_files_on_this_disk,
57             number_of_files,
58             central_directory_size,
59             central_directory_offset,
60             zip_file_comment,
61         })
62     }
63 
find_and_parse<T: Read + io::Seek>( reader: &mut T, ) -> ZipResult<(CentralDirectoryEnd, u64)>64     pub fn find_and_parse<T: Read + io::Seek>(
65         reader: &mut T,
66     ) -> ZipResult<(CentralDirectoryEnd, u64)> {
67         const HEADER_SIZE: u64 = 22;
68         const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
69         let file_length = reader.seek(io::SeekFrom::End(0))?;
70 
71         let search_upper_bound = file_length.saturating_sub(HEADER_SIZE + ::std::u16::MAX as u64);
72 
73         if file_length < HEADER_SIZE {
74             return Err(ZipError::InvalidArchive("Invalid zip header"));
75         }
76 
77         let mut pos = file_length - HEADER_SIZE;
78         while pos >= search_upper_bound {
79             reader.seek(io::SeekFrom::Start(pos))?;
80             if reader.read_u32::<LittleEndian>()? == CENTRAL_DIRECTORY_END_SIGNATURE {
81                 reader.seek(io::SeekFrom::Current(
82                     BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64,
83                 ))?;
84                 let cde_start_pos = reader.seek(io::SeekFrom::Start(pos))?;
85                 return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
86             }
87             pos = match pos.checked_sub(1) {
88                 Some(p) => p,
89                 None => break,
90             };
91         }
92         Err(ZipError::InvalidArchive(
93             "Could not find central directory end",
94         ))
95     }
96 
write<T: Write>(&self, writer: &mut T) -> ZipResult<()>97     pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
98         writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE)?;
99         writer.write_u16::<LittleEndian>(self.disk_number)?;
100         writer.write_u16::<LittleEndian>(self.disk_with_central_directory)?;
101         writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk)?;
102         writer.write_u16::<LittleEndian>(self.number_of_files)?;
103         writer.write_u32::<LittleEndian>(self.central_directory_size)?;
104         writer.write_u32::<LittleEndian>(self.central_directory_offset)?;
105         writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16)?;
106         writer.write_all(&self.zip_file_comment)?;
107         Ok(())
108     }
109 }
110 
111 pub struct Zip64CentralDirectoryEndLocator {
112     pub disk_with_central_directory: u32,
113     pub end_of_central_directory_offset: u64,
114     pub number_of_disks: u32,
115 }
116 
117 impl Zip64CentralDirectoryEndLocator {
parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator>118     pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator> {
119         let magic = reader.read_u32::<LittleEndian>()?;
120         if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE {
121             return Err(ZipError::InvalidArchive(
122                 "Invalid zip64 locator digital signature header",
123             ));
124         }
125         let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
126         let end_of_central_directory_offset = reader.read_u64::<LittleEndian>()?;
127         let number_of_disks = reader.read_u32::<LittleEndian>()?;
128 
129         Ok(Zip64CentralDirectoryEndLocator {
130             disk_with_central_directory,
131             end_of_central_directory_offset,
132             number_of_disks,
133         })
134     }
135 
write<T: Write>(&self, writer: &mut T) -> ZipResult<()>136     pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
137         writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE)?;
138         writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?;
139         writer.write_u64::<LittleEndian>(self.end_of_central_directory_offset)?;
140         writer.write_u32::<LittleEndian>(self.number_of_disks)?;
141         Ok(())
142     }
143 }
144 
145 pub struct Zip64CentralDirectoryEnd {
146     pub version_made_by: u16,
147     pub version_needed_to_extract: u16,
148     pub disk_number: u32,
149     pub disk_with_central_directory: u32,
150     pub number_of_files_on_this_disk: u64,
151     pub number_of_files: u64,
152     pub central_directory_size: u64,
153     pub central_directory_offset: u64,
154     //pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
155 }
156 
157 impl Zip64CentralDirectoryEnd {
find_and_parse<T: Read + io::Seek>( reader: &mut T, nominal_offset: u64, search_upper_bound: u64, ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>158     pub fn find_and_parse<T: Read + io::Seek>(
159         reader: &mut T,
160         nominal_offset: u64,
161         search_upper_bound: u64,
162     ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)> {
163         let mut pos = nominal_offset;
164 
165         while pos <= search_upper_bound {
166             reader.seek(io::SeekFrom::Start(pos))?;
167 
168             if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE {
169                 let archive_offset = pos - nominal_offset;
170 
171                 let _record_size = reader.read_u64::<LittleEndian>()?;
172                 // We would use this value if we did anything with the "zip64 extensible data sector".
173 
174                 let version_made_by = reader.read_u16::<LittleEndian>()?;
175                 let version_needed_to_extract = reader.read_u16::<LittleEndian>()?;
176                 let disk_number = reader.read_u32::<LittleEndian>()?;
177                 let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
178                 let number_of_files_on_this_disk = reader.read_u64::<LittleEndian>()?;
179                 let number_of_files = reader.read_u64::<LittleEndian>()?;
180                 let central_directory_size = reader.read_u64::<LittleEndian>()?;
181                 let central_directory_offset = reader.read_u64::<LittleEndian>()?;
182 
183                 return Ok((
184                     Zip64CentralDirectoryEnd {
185                         version_made_by,
186                         version_needed_to_extract,
187                         disk_number,
188                         disk_with_central_directory,
189                         number_of_files_on_this_disk,
190                         number_of_files,
191                         central_directory_size,
192                         central_directory_offset,
193                     },
194                     archive_offset,
195                 ));
196             }
197 
198             pos += 1;
199         }
200 
201         Err(ZipError::InvalidArchive(
202             "Could not find ZIP64 central directory end",
203         ))
204     }
205 
write<T: Write>(&self, writer: &mut T) -> ZipResult<()>206     pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
207         writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE)?;
208         writer.write_u64::<LittleEndian>(44)?; // record size
209         writer.write_u16::<LittleEndian>(self.version_made_by)?;
210         writer.write_u16::<LittleEndian>(self.version_needed_to_extract)?;
211         writer.write_u32::<LittleEndian>(self.disk_number)?;
212         writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?;
213         writer.write_u64::<LittleEndian>(self.number_of_files_on_this_disk)?;
214         writer.write_u64::<LittleEndian>(self.number_of_files)?;
215         writer.write_u64::<LittleEndian>(self.central_directory_size)?;
216         writer.write_u64::<LittleEndian>(self.central_directory_offset)?;
217         Ok(())
218     }
219 }
220