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