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