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 comment_length = reader.read_u16::<LittleEndian>()? as u64; 70 if file_length - pos - HEADER_SIZE == comment_length { 71 let cde_start_pos = reader.seek(io::SeekFrom::Start(pos as u64))?; 72 return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos)); 73 } 74 } 75 pos = match pos.checked_sub(1) { 76 Some(p) => p, 77 None => break, 78 }; 79 } 80 Err(ZipError::InvalidArchive( 81 "Could not find central directory end", 82 )) 83 } 84 write<T: Write>(&self, writer: &mut T) -> ZipResult<()>85 pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> { 86 writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE)?; 87 writer.write_u16::<LittleEndian>(self.disk_number)?; 88 writer.write_u16::<LittleEndian>(self.disk_with_central_directory)?; 89 writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk)?; 90 writer.write_u16::<LittleEndian>(self.number_of_files)?; 91 writer.write_u32::<LittleEndian>(self.central_directory_size)?; 92 writer.write_u32::<LittleEndian>(self.central_directory_offset)?; 93 writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16)?; 94 writer.write_all(&self.zip_file_comment)?; 95 Ok(()) 96 } 97 } 98 99 pub struct Zip64CentralDirectoryEndLocator { 100 pub disk_with_central_directory: u32, 101 pub end_of_central_directory_offset: u64, 102 pub number_of_disks: u32, 103 } 104 105 impl Zip64CentralDirectoryEndLocator { parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator>106 pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator> { 107 let magic = reader.read_u32::<LittleEndian>()?; 108 if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE { 109 return Err(ZipError::InvalidArchive( 110 "Invalid zip64 locator digital signature header", 111 )); 112 } 113 let disk_with_central_directory = reader.read_u32::<LittleEndian>()?; 114 let end_of_central_directory_offset = reader.read_u64::<LittleEndian>()?; 115 let number_of_disks = reader.read_u32::<LittleEndian>()?; 116 117 Ok(Zip64CentralDirectoryEndLocator { 118 disk_with_central_directory, 119 end_of_central_directory_offset, 120 number_of_disks, 121 }) 122 } 123 } 124 125 pub struct Zip64CentralDirectoryEnd { 126 pub version_made_by: u16, 127 pub version_needed_to_extract: u16, 128 pub disk_number: u32, 129 pub disk_with_central_directory: u32, 130 pub number_of_files_on_this_disk: u64, 131 pub number_of_files: u64, 132 pub central_directory_size: u64, 133 pub central_directory_offset: u64, 134 //pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment. 135 } 136 137 impl Zip64CentralDirectoryEnd { find_and_parse<T: Read + io::Seek>( reader: &mut T, nominal_offset: u64, search_upper_bound: u64, ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>138 pub fn find_and_parse<T: Read + io::Seek>( 139 reader: &mut T, 140 nominal_offset: u64, 141 search_upper_bound: u64, 142 ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)> { 143 let mut pos = nominal_offset; 144 145 while pos <= search_upper_bound { 146 reader.seek(io::SeekFrom::Start(pos))?; 147 148 if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE { 149 let archive_offset = pos - nominal_offset; 150 151 let _record_size = reader.read_u64::<LittleEndian>()?; 152 // We would use this value if we did anything with the "zip64 extensible data sector". 153 154 let version_made_by = reader.read_u16::<LittleEndian>()?; 155 let version_needed_to_extract = reader.read_u16::<LittleEndian>()?; 156 let disk_number = reader.read_u32::<LittleEndian>()?; 157 let disk_with_central_directory = reader.read_u32::<LittleEndian>()?; 158 let number_of_files_on_this_disk = reader.read_u64::<LittleEndian>()?; 159 let number_of_files = reader.read_u64::<LittleEndian>()?; 160 let central_directory_size = reader.read_u64::<LittleEndian>()?; 161 let central_directory_offset = reader.read_u64::<LittleEndian>()?; 162 163 return Ok(( 164 Zip64CentralDirectoryEnd { 165 version_made_by, 166 version_needed_to_extract, 167 disk_number, 168 disk_with_central_directory, 169 number_of_files_on_this_disk, 170 number_of_files, 171 central_directory_size, 172 central_directory_offset, 173 }, 174 archive_offset, 175 )); 176 } 177 178 pos += 1; 179 } 180 181 Err(ZipError::InvalidArchive( 182 "Could not find ZIP64 central directory end", 183 )) 184 } 185 } 186