Add zip::spec::{CentralDirectoryHeader, DataDescriptor, LocalFileHeader}
, and partially integrate them into zip::{read, write}
.
This commit is contained in:
parent
574bb3df17
commit
993cbcdc5c
3 changed files with 308 additions and 129 deletions
74
src/read.rs
74
src/read.rs
|
@ -640,70 +640,40 @@ pub(crate) fn central_header_to_zip_file<R: Read + io::Seek>(
|
||||||
archive_offset: u64,
|
archive_offset: u64,
|
||||||
) -> ZipResult<ZipFileData> {
|
) -> ZipResult<ZipFileData> {
|
||||||
let central_header_start = reader.seek(io::SeekFrom::Current(0))?;
|
let central_header_start = reader.seek(io::SeekFrom::Current(0))?;
|
||||||
// Parse central header
|
let central_header = spec::CentralDirectoryHeader::parse(reader)?;
|
||||||
let signature = reader.read_u32::<LittleEndian>()?;
|
|
||||||
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE {
|
|
||||||
return Err(ZipError::InvalidArchive("Invalid Central Directory header"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let version_made_by = reader.read_u16::<LittleEndian>()?;
|
let file_name = match central_header.flags.is_utf8() {
|
||||||
let _version_to_extract = reader.read_u16::<LittleEndian>()?;
|
true => String::from_utf8_lossy(&*central_header.file_name_raw).into_owned(),
|
||||||
let flags = reader.read_u16::<LittleEndian>()?;
|
false => central_header.file_name_raw.clone().from_cp437(),
|
||||||
let encrypted = flags & 1 == 1;
|
|
||||||
let is_utf8 = flags & (1 << 11) != 0;
|
|
||||||
let using_data_descriptor = flags & (1 << 3) != 0;
|
|
||||||
let compression_method = reader.read_u16::<LittleEndian>()?;
|
|
||||||
let last_mod_time = reader.read_u16::<LittleEndian>()?;
|
|
||||||
let last_mod_date = reader.read_u16::<LittleEndian>()?;
|
|
||||||
let crc32 = reader.read_u32::<LittleEndian>()?;
|
|
||||||
let compressed_size = reader.read_u32::<LittleEndian>()?;
|
|
||||||
let uncompressed_size = reader.read_u32::<LittleEndian>()?;
|
|
||||||
let file_name_length = reader.read_u16::<LittleEndian>()? as usize;
|
|
||||||
let extra_field_length = reader.read_u16::<LittleEndian>()? as usize;
|
|
||||||
let file_comment_length = reader.read_u16::<LittleEndian>()? as usize;
|
|
||||||
let _disk_number = reader.read_u16::<LittleEndian>()?;
|
|
||||||
let _internal_file_attributes = reader.read_u16::<LittleEndian>()?;
|
|
||||||
let external_file_attributes = reader.read_u32::<LittleEndian>()?;
|
|
||||||
let offset = reader.read_u32::<LittleEndian>()? as u64;
|
|
||||||
let mut file_name_raw = vec![0; file_name_length];
|
|
||||||
reader.read_exact(&mut file_name_raw)?;
|
|
||||||
let mut extra_field = vec![0; extra_field_length];
|
|
||||||
reader.read_exact(&mut extra_field)?;
|
|
||||||
let mut file_comment_raw = vec![0; file_comment_length];
|
|
||||||
reader.read_exact(&mut file_comment_raw)?;
|
|
||||||
|
|
||||||
let file_name = match is_utf8 {
|
|
||||||
true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
|
|
||||||
false => file_name_raw.clone().from_cp437(),
|
|
||||||
};
|
};
|
||||||
let file_comment = match is_utf8 {
|
let file_comment = match central_header.flags.is_utf8() {
|
||||||
true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
|
true => String::from_utf8_lossy(&*central_header.file_comment_raw).into_owned(),
|
||||||
false => file_comment_raw.from_cp437(),
|
false => central_header.file_comment_raw.clone().from_cp437(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Construct the result
|
// Construct the result
|
||||||
let mut result = ZipFileData {
|
let mut result = ZipFileData {
|
||||||
system: System::from_u8((version_made_by >> 8) as u8),
|
system: System::from_u8((central_header.version_made_by >> 8) as u8),
|
||||||
version_made_by: version_made_by as u8,
|
version_made_by: central_header.version_made_by as u8,
|
||||||
encrypted,
|
encrypted: central_header.flags.encrypted(),
|
||||||
using_data_descriptor,
|
using_data_descriptor: central_header.flags.using_data_descriptor(),
|
||||||
compression_method: {
|
compression_method: {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
CompressionMethod::from_u16(compression_method)
|
CompressionMethod::from_u16(central_header.compression_method)
|
||||||
},
|
},
|
||||||
compression_level: None,
|
compression_level: None,
|
||||||
last_modified_time: DateTime::from_msdos(last_mod_date, last_mod_time),
|
last_modified_time: DateTime::from_msdos(central_header.last_mod_date, central_header.last_mod_time),
|
||||||
crc32,
|
crc32: central_header.crc32,
|
||||||
compressed_size: compressed_size as u64,
|
compressed_size: central_header.compressed_size as u64,
|
||||||
uncompressed_size: uncompressed_size as u64,
|
uncompressed_size: central_header.uncompressed_size as u64,
|
||||||
file_name,
|
file_name: file_name,
|
||||||
file_name_raw,
|
file_name_raw: central_header.file_name_raw,
|
||||||
extra_field,
|
extra_field: central_header.extra_field,
|
||||||
file_comment,
|
file_comment: file_comment,
|
||||||
header_start: offset,
|
header_start: central_header.offset as u64,
|
||||||
central_header_start,
|
central_header_start,
|
||||||
data_start: AtomicU64::new(0),
|
data_start: AtomicU64::new(0),
|
||||||
external_attributes: external_file_attributes,
|
external_attributes: central_header.external_file_attributes,
|
||||||
large_file: false,
|
large_file: false,
|
||||||
aes_mode: None,
|
aes_mode: None,
|
||||||
};
|
};
|
||||||
|
|
231
src/spec.rs
231
src/spec.rs
|
@ -1,3 +1,4 @@
|
||||||
|
#![allow(missing_docs, dead_code)]
|
||||||
use crate::result::{ZipError, ZipResult};
|
use crate::result::{ZipError, ZipResult};
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -8,10 +9,12 @@ pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE: u32 = 0x02014b50;
|
||||||
const CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06054b50;
|
const CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06054b50;
|
||||||
pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06064b50;
|
pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06064b50;
|
||||||
const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE: u32 = 0x07064b50;
|
const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE: u32 = 0x07064b50;
|
||||||
|
pub const DATA_DESCRIPTOR_SIGNATURE: u32 = 0x08074b50;
|
||||||
|
|
||||||
pub const ZIP64_BYTES_THR: u64 = u32::MAX as u64;
|
pub const ZIP64_BYTES_THR: u64 = u32::MAX as u64;
|
||||||
pub const ZIP64_ENTRY_THR: usize = u16::MAX as usize;
|
pub const ZIP64_ENTRY_THR: usize = u16::MAX as usize;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CentralDirectoryEnd {
|
pub struct CentralDirectoryEnd {
|
||||||
pub disk_number: u16,
|
pub disk_number: u16,
|
||||||
pub disk_with_central_directory: u16,
|
pub disk_with_central_directory: u16,
|
||||||
|
@ -23,6 +26,10 @@ pub struct CentralDirectoryEnd {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CentralDirectoryEnd {
|
impl CentralDirectoryEnd {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
22 + self.zip_file_comment.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
|
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
|
||||||
let magic = reader.read_u32::<LittleEndian>()?;
|
let magic = reader.read_u32::<LittleEndian>()?;
|
||||||
if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
|
if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
|
||||||
|
@ -205,3 +212,227 @@ impl Zip64CentralDirectoryEnd {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct GeneralPurposeBitFlags(pub u16);
|
||||||
|
|
||||||
|
impl GeneralPurposeBitFlags {
|
||||||
|
#[inline]
|
||||||
|
pub fn encrypted(&self) -> bool {
|
||||||
|
self.0 & 1 == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_utf8(&self) -> bool {
|
||||||
|
self.0 & (1 << 11) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn using_data_descriptor(&self) -> bool {
|
||||||
|
self.0 & (1 << 3) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_using_data_descriptor(&mut self, b: bool) {
|
||||||
|
self.0 &= !(1 << 3);
|
||||||
|
if b {
|
||||||
|
self.0 |= 1 << 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CentralDirectoryHeader {
|
||||||
|
pub version_made_by: u16,
|
||||||
|
pub version_to_extract: u16,
|
||||||
|
pub flags: GeneralPurposeBitFlags,
|
||||||
|
pub compression_method: u16,
|
||||||
|
pub last_mod_time: u16,
|
||||||
|
pub last_mod_date: u16,
|
||||||
|
pub crc32: u32,
|
||||||
|
pub compressed_size: u32,
|
||||||
|
pub uncompressed_size: u32,
|
||||||
|
pub disk_number: u16,
|
||||||
|
pub internal_file_attributes: u16,
|
||||||
|
pub external_file_attributes: u32,
|
||||||
|
pub offset: u32,
|
||||||
|
pub file_name_raw: Vec<u8>,
|
||||||
|
pub extra_field: Vec<u8>,
|
||||||
|
pub file_comment_raw: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CentralDirectoryHeader {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
46 + self.file_name_raw.len() + self.extra_field.len() + self.file_comment_raw.len()
|
||||||
|
}
|
||||||
|
pub fn parse<R: Read>(reader: &mut R) -> ZipResult<CentralDirectoryHeader> {
|
||||||
|
let signature = reader.read_u32::<LittleEndian>()?;
|
||||||
|
if signature != CENTRAL_DIRECTORY_HEADER_SIGNATURE {
|
||||||
|
return Err(ZipError::InvalidArchive("Invalid Central Directory header"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let version_made_by = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let version_to_extract = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let flags = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let compression_method = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let last_mod_time = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let last_mod_date = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let crc32 = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let compressed_size = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let uncompressed_size = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let file_name_length = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let extra_field_length = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let file_comment_length = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let disk_number = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let internal_file_attributes = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let external_file_attributes = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let offset = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let mut file_name_raw = vec![0; file_name_length as usize];
|
||||||
|
reader.read_exact(&mut file_name_raw)?;
|
||||||
|
let mut extra_field = vec![0; extra_field_length as usize];
|
||||||
|
reader.read_exact(&mut extra_field)?;
|
||||||
|
let mut file_comment_raw = vec![0; file_comment_length as usize];
|
||||||
|
reader.read_exact(&mut file_comment_raw)?;
|
||||||
|
|
||||||
|
Ok(CentralDirectoryHeader {
|
||||||
|
version_made_by,
|
||||||
|
version_to_extract,
|
||||||
|
flags: GeneralPurposeBitFlags(flags),
|
||||||
|
compression_method,
|
||||||
|
last_mod_time,
|
||||||
|
last_mod_date,
|
||||||
|
crc32,
|
||||||
|
compressed_size,
|
||||||
|
uncompressed_size,
|
||||||
|
disk_number,
|
||||||
|
internal_file_attributes,
|
||||||
|
external_file_attributes,
|
||||||
|
offset,
|
||||||
|
file_name_raw,
|
||||||
|
extra_field,
|
||||||
|
file_comment_raw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
|
||||||
|
writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_HEADER_SIGNATURE)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.version_made_by)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.version_to_extract)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.flags.0)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.compression_method)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.last_mod_time)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.last_mod_date)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.crc32)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.compressed_size)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.uncompressed_size)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.file_name_raw.len() as u16)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.extra_field.len() as u16)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.file_comment_raw.len() as u16)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.disk_number)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.internal_file_attributes)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.external_file_attributes)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.offset)?;
|
||||||
|
writer.write_all(&self.file_name_raw)?;
|
||||||
|
writer.write_all(&self.extra_field)?;
|
||||||
|
writer.write_all(&self.file_comment_raw)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct LocalFileHeader {
|
||||||
|
pub version_to_extract: u16,
|
||||||
|
pub flags: GeneralPurposeBitFlags,
|
||||||
|
pub compression_method: u16,
|
||||||
|
pub last_mod_time: u16,
|
||||||
|
pub last_mod_date: u16,
|
||||||
|
pub crc32: u32,
|
||||||
|
pub compressed_size: u32,
|
||||||
|
pub uncompressed_size: u32,
|
||||||
|
pub file_name_raw: Vec<u8>,
|
||||||
|
pub extra_field: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalFileHeader {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
30 + self.file_name_raw.len() + self.extra_field.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<R: Read>(reader: &mut R) -> ZipResult<LocalFileHeader> {
|
||||||
|
let signature = reader.read_u32::<LittleEndian>()?;
|
||||||
|
if signature != LOCAL_FILE_HEADER_SIGNATURE {
|
||||||
|
return Err(ZipError::InvalidArchive("Invalid local file header"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let version_to_extract = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let flags = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let compression_method = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let last_mod_time = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let last_mod_date = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let crc32 = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let compressed_size = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let uncompressed_size = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let file_name_length = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let extra_field_length = reader.read_u16::<LittleEndian>()?;
|
||||||
|
|
||||||
|
let mut file_name_raw = vec![0; file_name_length as usize];
|
||||||
|
reader.read_exact(&mut file_name_raw)?;
|
||||||
|
let mut extra_field = vec![0; extra_field_length as usize];
|
||||||
|
reader.read_exact(&mut extra_field)?;
|
||||||
|
|
||||||
|
Ok(LocalFileHeader {
|
||||||
|
version_to_extract,
|
||||||
|
flags: GeneralPurposeBitFlags(flags),
|
||||||
|
compression_method,
|
||||||
|
last_mod_time,
|
||||||
|
last_mod_date,
|
||||||
|
crc32,
|
||||||
|
compressed_size,
|
||||||
|
uncompressed_size,
|
||||||
|
file_name_raw,
|
||||||
|
extra_field,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
|
||||||
|
writer.write_u32::<LittleEndian>(LOCAL_FILE_HEADER_SIGNATURE)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.version_to_extract)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.flags.0)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.compression_method)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.last_mod_time)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.last_mod_date)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.crc32)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.compressed_size)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.uncompressed_size)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.file_name_raw.len() as u16)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.extra_field.len() as u16)?;
|
||||||
|
writer.write_all(&self.file_name_raw)?;
|
||||||
|
writer.write_all(&self.extra_field)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct DataDescriptor {
|
||||||
|
pub crc32: u32,
|
||||||
|
pub compressed_size: u32,
|
||||||
|
pub uncompressed_size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataDescriptor {
|
||||||
|
pub fn read<T: Read>(reader: &mut T) -> ZipResult<DataDescriptor> {
|
||||||
|
let first_word = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let crc32 = if first_word == DATA_DESCRIPTOR_SIGNATURE {
|
||||||
|
reader.read_u32::<LittleEndian>()?
|
||||||
|
} else {
|
||||||
|
first_word
|
||||||
|
};
|
||||||
|
let compressed_size = reader.read_u32::<LittleEndian>()?;
|
||||||
|
let uncompressed_size = reader.read_u32::<LittleEndian>()?;
|
||||||
|
Ok(DataDescriptor {
|
||||||
|
crc32,
|
||||||
|
compressed_size,
|
||||||
|
uncompressed_size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
132
src/write.rs
132
src/write.rs
|
@ -998,44 +998,38 @@ fn clamp_opt<T: Ord + Copy>(value: T, range: RangeInclusive<T>) -> Option<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> {
|
fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> {
|
||||||
// local file header signature
|
let flags = if !file.file_name.is_ascii() {
|
||||||
writer.write_u32::<LittleEndian>(spec::LOCAL_FILE_HEADER_SIGNATURE)?;
|
|
||||||
// version needed to extract
|
|
||||||
writer.write_u16::<LittleEndian>(file.version_needed())?;
|
|
||||||
// general purpose bit flag
|
|
||||||
let flag = if !file.file_name.is_ascii() {
|
|
||||||
1u16 << 11
|
1u16 << 11
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
writer.write_u16::<LittleEndian>(flag)?;
|
|
||||||
// Compression method
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?;
|
let compression_method = file.compression_method.to_u16();
|
||||||
// last mod file time and last mod file date
|
let compressed_size = file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||||
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?;
|
let uncompressed_size = file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||||
writer.write_u16::<LittleEndian>(file.last_modified_time.datepart())?;
|
|
||||||
// crc-32
|
let mut extra_field = if file.large_file {
|
||||||
writer.write_u32::<LittleEndian>(file.crc32)?;
|
let mut zip64_extra_field = vec![0; 20];
|
||||||
// compressed size and uncompressed size
|
write_local_zip64_extra_field(&mut zip64_extra_field, file)?;
|
||||||
if file.large_file {
|
zip64_extra_field
|
||||||
writer.write_u32::<LittleEndian>(spec::ZIP64_BYTES_THR as u32)?;
|
} else {
|
||||||
writer.write_u32::<LittleEndian>(spec::ZIP64_BYTES_THR as u32)?;
|
Vec::new()
|
||||||
} else {
|
};
|
||||||
writer.write_u32::<LittleEndian>(file.compressed_size as u32)?;
|
extra_field.extend_from_slice(&file.extra_field[..]);
|
||||||
writer.write_u32::<LittleEndian>(file.uncompressed_size as u32)?;
|
|
||||||
}
|
let local_file_header = spec::LocalFileHeader {
|
||||||
// file name length
|
version_to_extract: file.version_needed(),
|
||||||
writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16)?;
|
flags: spec::GeneralPurposeBitFlags(flags),
|
||||||
// extra field length
|
compression_method,
|
||||||
let extra_field_length = if file.large_file { 20 } else { 0 } + file.extra_field.len() as u16;
|
last_mod_time: file.last_modified_time.timepart(),
|
||||||
writer.write_u16::<LittleEndian>(extra_field_length)?;
|
last_mod_date: file.last_modified_time.datepart(),
|
||||||
// file name
|
crc32: file.crc32,
|
||||||
writer.write_all(file.file_name.as_bytes())?;
|
compressed_size,
|
||||||
// zip64 extra field
|
uncompressed_size,
|
||||||
if file.large_file {
|
file_name_raw: file.file_name.as_bytes().to_vec(),
|
||||||
write_local_zip64_extra_field(writer, file)?;
|
extra_field,
|
||||||
}
|
};
|
||||||
|
local_file_header.write(writer)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1070,56 +1064,40 @@ fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData)
|
||||||
let zip64_extra_field_length =
|
let zip64_extra_field_length =
|
||||||
write_central_zip64_extra_field(&mut zip64_extra_field.as_mut(), file)?;
|
write_central_zip64_extra_field(&mut zip64_extra_field.as_mut(), file)?;
|
||||||
|
|
||||||
// central file header signature
|
let flags = if !file.file_name.is_ascii() {
|
||||||
writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE)?;
|
|
||||||
// version made by
|
|
||||||
let version_made_by = (file.system as u16) << 8 | (file.version_made_by as u16);
|
|
||||||
writer.write_u16::<LittleEndian>(version_made_by)?;
|
|
||||||
// version needed to extract
|
|
||||||
writer.write_u16::<LittleEndian>(file.version_needed())?;
|
|
||||||
// general puprose bit flag
|
|
||||||
let flag = if !file.file_name.is_ascii() {
|
|
||||||
1u16 << 11
|
1u16 << 11
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
writer.write_u16::<LittleEndian>(flag)?;
|
|
||||||
// compression method
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?;
|
let compression_method = file.compression_method.to_u16();
|
||||||
// last mod file time + date
|
let compressed_size = file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||||
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?;
|
let uncompressed_size = file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||||
writer.write_u16::<LittleEndian>(file.last_modified_time.datepart())?;
|
let offset = file.header_start.min(spec::ZIP64_BYTES_THR) as u32;
|
||||||
// crc-32
|
|
||||||
writer.write_u32::<LittleEndian>(file.crc32)?;
|
|
||||||
// compressed size
|
|
||||||
writer.write_u32::<LittleEndian>(file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32)?;
|
|
||||||
// uncompressed size
|
|
||||||
writer.write_u32::<LittleEndian>(file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32)?;
|
|
||||||
// file name length
|
|
||||||
writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16)?;
|
|
||||||
// extra field length
|
|
||||||
writer.write_u16::<LittleEndian>(zip64_extra_field_length + file.extra_field.len() as u16)?;
|
|
||||||
// file comment length
|
|
||||||
writer.write_u16::<LittleEndian>(0)?;
|
|
||||||
// disk number start
|
|
||||||
writer.write_u16::<LittleEndian>(0)?;
|
|
||||||
// internal file attribytes
|
|
||||||
writer.write_u16::<LittleEndian>(0)?;
|
|
||||||
// external file attributes
|
|
||||||
writer.write_u32::<LittleEndian>(file.external_attributes)?;
|
|
||||||
// relative offset of local header
|
|
||||||
writer.write_u32::<LittleEndian>(file.header_start.min(spec::ZIP64_BYTES_THR) as u32)?;
|
|
||||||
// file name
|
|
||||||
writer.write_all(file.file_name.as_bytes())?;
|
|
||||||
// zip64 extra field
|
|
||||||
writer.write_all(&zip64_extra_field[..zip64_extra_field_length as usize])?;
|
|
||||||
// extra field
|
|
||||||
writer.write_all(&file.extra_field)?;
|
|
||||||
// file comment
|
|
||||||
// <none>
|
|
||||||
|
|
||||||
Ok(())
|
let mut extra_field = zip64_extra_field[..zip64_extra_field_length as usize].to_vec();
|
||||||
|
extra_field.extend_from_slice(&file.extra_field[..]);
|
||||||
|
|
||||||
|
let header = spec::CentralDirectoryHeader {
|
||||||
|
version_made_by: (file.system as u16) << 8 | (file.version_made_by as u16),
|
||||||
|
version_to_extract: file.version_needed(),
|
||||||
|
flags: spec::GeneralPurposeBitFlags(flags),
|
||||||
|
compression_method,
|
||||||
|
last_mod_time: file.last_modified_time.timepart(),
|
||||||
|
last_mod_date: file.last_modified_time.datepart(),
|
||||||
|
crc32: file.crc32,
|
||||||
|
compressed_size,
|
||||||
|
uncompressed_size,
|
||||||
|
disk_number: 0,
|
||||||
|
internal_file_attributes: 0,
|
||||||
|
external_file_attributes: file.external_attributes,
|
||||||
|
offset,
|
||||||
|
file_name_raw: file.file_name.as_bytes().to_vec(),
|
||||||
|
extra_field,
|
||||||
|
file_comment_raw: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
header.write(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_extra_data(file: &ZipFileData) -> ZipResult<()> {
|
fn validate_extra_data(file: &ZipFileData) -> ZipResult<()> {
|
||||||
|
|
Loading…
Add table
Reference in a new issue