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