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