• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }
121 
122 pub struct Zip64CentralDirectoryEnd {
123     pub version_made_by: u16,
124     pub version_needed_to_extract: u16,
125     pub disk_number: u32,
126     pub disk_with_central_directory: u32,
127     pub number_of_files_on_this_disk: u64,
128     pub number_of_files: u64,
129     pub central_directory_size: u64,
130     pub central_directory_offset: u64,
131     //pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
132 }
133 
134 impl Zip64CentralDirectoryEnd {
find_and_parse<T: Read + io::Seek>( reader: &mut T, nominal_offset: u64, search_upper_bound: u64, ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>135     pub fn find_and_parse<T: Read + io::Seek>(
136         reader: &mut T,
137         nominal_offset: u64,
138         search_upper_bound: u64,
139     ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)> {
140         let mut pos = nominal_offset;
141 
142         while pos <= search_upper_bound {
143             reader.seek(io::SeekFrom::Start(pos))?;
144 
145             if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE {
146                 let archive_offset = pos - nominal_offset;
147 
148                 let _record_size = reader.read_u64::<LittleEndian>()?;
149                 // We would use this value if we did anything with the "zip64 extensible data sector".
150 
151                 let version_made_by = reader.read_u16::<LittleEndian>()?;
152                 let version_needed_to_extract = reader.read_u16::<LittleEndian>()?;
153                 let disk_number = reader.read_u32::<LittleEndian>()?;
154                 let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
155                 let number_of_files_on_this_disk = reader.read_u64::<LittleEndian>()?;
156                 let number_of_files = reader.read_u64::<LittleEndian>()?;
157                 let central_directory_size = reader.read_u64::<LittleEndian>()?;
158                 let central_directory_offset = reader.read_u64::<LittleEndian>()?;
159 
160                 return Ok((
161                     Zip64CentralDirectoryEnd {
162                         version_made_by,
163                         version_needed_to_extract,
164                         disk_number,
165                         disk_with_central_directory,
166                         number_of_files_on_this_disk,
167                         number_of_files,
168                         central_directory_size,
169                         central_directory_offset,
170                     },
171                     archive_offset,
172                 ));
173             }
174 
175             pos += 1;
176         }
177 
178         Err(ZipError::InvalidArchive(
179             "Could not find ZIP64 central directory end",
180         ))
181     }
182 }
183