1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
use crate::result::{ZipError, ZipResult};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io;
use std::io::prelude::*;
pub const LOCAL_FILE_HEADER_SIGNATURE: u32 = 0x04034b50;
pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE: u32 = 0x02014b50;
const CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06054b50;
pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06064b50;
const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE: u32 = 0x07064b50;
pub const ZIP64_BYTES_THR: u64 = u32::MAX as u64;
pub const ZIP64_ENTRY_THR: usize = u16::MAX as usize;
pub struct CentralDirectoryEnd {
pub disk_number: u16,
pub disk_with_central_directory: u16,
pub number_of_files_on_this_disk: u16,
pub number_of_files: u16,
pub central_directory_size: u32,
pub central_directory_offset: u32,
pub zip_file_comment: Vec<u8>,
}
impl CentralDirectoryEnd {
// Per spec 4.4.1.4 - a CentralDirectoryEnd field might be insufficient to hold the
// required data. In this case the file SHOULD contain a ZIP64 format record
// and the field of this record will be set to -1
pub(crate) fn record_too_small(&self) -> bool {
self.disk_number == 0xFFFF
|| self.disk_with_central_directory == 0xFFFF
|| self.number_of_files_on_this_disk == 0xFFFF
|| self.number_of_files == 0xFFFF
|| self.central_directory_size == 0xFFFFFFFF
|| self.central_directory_offset == 0xFFFFFFFF
}
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
let magic = reader.read_u32::<LittleEndian>()?;
if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
return Err(ZipError::InvalidArchive("Invalid digital signature header"));
}
let disk_number = reader.read_u16::<LittleEndian>()?;
let disk_with_central_directory = reader.read_u16::<LittleEndian>()?;
let number_of_files_on_this_disk = reader.read_u16::<LittleEndian>()?;
let number_of_files = reader.read_u16::<LittleEndian>()?;
let central_directory_size = reader.read_u32::<LittleEndian>()?;
let central_directory_offset = reader.read_u32::<LittleEndian>()?;
let zip_file_comment_length = reader.read_u16::<LittleEndian>()? as usize;
let mut zip_file_comment = vec![0; zip_file_comment_length];
reader.read_exact(&mut zip_file_comment)?;
Ok(CentralDirectoryEnd {
disk_number,
disk_with_central_directory,
number_of_files_on_this_disk,
number_of_files,
central_directory_size,
central_directory_offset,
zip_file_comment,
})
}
pub fn find_and_parse<T: Read + io::Seek>(
reader: &mut T,
) -> ZipResult<(CentralDirectoryEnd, u64)> {
const HEADER_SIZE: u64 = 22;
const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
let file_length = reader.seek(io::SeekFrom::End(0))?;
let search_upper_bound = file_length.saturating_sub(HEADER_SIZE + ::std::u16::MAX as u64);
if file_length < HEADER_SIZE {
return Err(ZipError::InvalidArchive("Invalid zip header"));
}
let mut pos = file_length - HEADER_SIZE;
while pos >= search_upper_bound {
reader.seek(io::SeekFrom::Start(pos))?;
if reader.read_u32::<LittleEndian>()? == CENTRAL_DIRECTORY_END_SIGNATURE {
reader.seek(io::SeekFrom::Current(
BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64,
))?;
let cde_start_pos = reader.seek(io::SeekFrom::Start(pos))?;
return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
}
pos = match pos.checked_sub(1) {
Some(p) => p,
None => break,
};
}
Err(ZipError::InvalidArchive(
"Could not find central directory end",
))
}
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE)?;
writer.write_u16::<LittleEndian>(self.disk_number)?;
writer.write_u16::<LittleEndian>(self.disk_with_central_directory)?;
writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk)?;
writer.write_u16::<LittleEndian>(self.number_of_files)?;
writer.write_u32::<LittleEndian>(self.central_directory_size)?;
writer.write_u32::<LittleEndian>(self.central_directory_offset)?;
writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16)?;
writer.write_all(&self.zip_file_comment)?;
Ok(())
}
}
pub struct Zip64CentralDirectoryEndLocator {
pub disk_with_central_directory: u32,
pub end_of_central_directory_offset: u64,
pub number_of_disks: u32,
}
impl Zip64CentralDirectoryEndLocator {
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator> {
let magic = reader.read_u32::<LittleEndian>()?;
if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE {
return Err(ZipError::InvalidArchive(
"Invalid zip64 locator digital signature header",
));
}
let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
let end_of_central_directory_offset = reader.read_u64::<LittleEndian>()?;
let number_of_disks = reader.read_u32::<LittleEndian>()?;
Ok(Zip64CentralDirectoryEndLocator {
disk_with_central_directory,
end_of_central_directory_offset,
number_of_disks,
})
}
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE)?;
writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?;
writer.write_u64::<LittleEndian>(self.end_of_central_directory_offset)?;
writer.write_u32::<LittleEndian>(self.number_of_disks)?;
Ok(())
}
}
pub struct Zip64CentralDirectoryEnd {
pub version_made_by: u16,
pub version_needed_to_extract: u16,
pub disk_number: u32,
pub disk_with_central_directory: u32,
pub number_of_files_on_this_disk: u64,
pub number_of_files: u64,
pub central_directory_size: u64,
pub central_directory_offset: u64,
//pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
}
impl Zip64CentralDirectoryEnd {
pub fn find_and_parse<T: Read + io::Seek>(
reader: &mut T,
nominal_offset: u64,
search_upper_bound: u64,
) -> ZipResult<(Zip64CentralDirectoryEnd, u64)> {
let mut pos = nominal_offset;
while pos <= search_upper_bound {
reader.seek(io::SeekFrom::Start(pos))?;
if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE {
let archive_offset = pos - nominal_offset;
let _record_size = reader.read_u64::<LittleEndian>()?;
// We would use this value if we did anything with the "zip64 extensible data sector".
let version_made_by = reader.read_u16::<LittleEndian>()?;
let version_needed_to_extract = reader.read_u16::<LittleEndian>()?;
let disk_number = reader.read_u32::<LittleEndian>()?;
let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
let number_of_files_on_this_disk = reader.read_u64::<LittleEndian>()?;
let number_of_files = reader.read_u64::<LittleEndian>()?;
let central_directory_size = reader.read_u64::<LittleEndian>()?;
let central_directory_offset = reader.read_u64::<LittleEndian>()?;
return Ok((
Zip64CentralDirectoryEnd {
version_made_by,
version_needed_to_extract,
disk_number,
disk_with_central_directory,
number_of_files_on_this_disk,
number_of_files,
central_directory_size,
central_directory_offset,
},
archive_offset,
));
}
pos += 1;
}
Err(ZipError::InvalidArchive(
"Could not find ZIP64 central directory end",
))
}
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE)?;
writer.write_u64::<LittleEndian>(44)?; // record size
writer.write_u16::<LittleEndian>(self.version_made_by)?;
writer.write_u16::<LittleEndian>(self.version_needed_to_extract)?;
writer.write_u32::<LittleEndian>(self.disk_number)?;
writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?;
writer.write_u64::<LittleEndian>(self.number_of_files_on_this_disk)?;
writer.write_u64::<LittleEndian>(self.number_of_files)?;
writer.write_u64::<LittleEndian>(self.central_directory_size)?;
writer.write_u64::<LittleEndian>(self.central_directory_offset)?;
Ok(())
}
}
|