Revert "Merge pull request #290 from aweinstock314/more-spec-structs"
This reverts commita1da6860ea
, reversing changes made to574bb3df17
.
This commit is contained in:
parent
a1da6860ea
commit
77294fd3ce
3 changed files with 126 additions and 396 deletions
73
src/read.rs
73
src/read.rs
|
@ -640,43 +640,70 @@ pub(crate) fn central_header_to_zip_file<R: Read + io::Seek>(
|
|||
archive_offset: u64,
|
||||
) -> ZipResult<ZipFileData> {
|
||||
let central_header_start = reader.seek(io::SeekFrom::Current(0))?;
|
||||
let central_header = spec::CentralDirectoryHeader::parse(reader)?;
|
||||
// Parse central header
|
||||
let signature = reader.read_u32::<LittleEndian>()?;
|
||||
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE {
|
||||
return Err(ZipError::InvalidArchive("Invalid Central Directory header"));
|
||||
}
|
||||
|
||||
let file_name = match central_header.flags.is_utf8() {
|
||||
true => String::from_utf8_lossy(&*central_header.file_name_raw).into_owned(),
|
||||
false => central_header.file_name_raw.clone().from_cp437(),
|
||||
let version_made_by = reader.read_u16::<LittleEndian>()?;
|
||||
let _version_to_extract = reader.read_u16::<LittleEndian>()?;
|
||||
let flags = reader.read_u16::<LittleEndian>()?;
|
||||
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 central_header.flags.is_utf8() {
|
||||
true => String::from_utf8_lossy(&*central_header.file_comment_raw).into_owned(),
|
||||
false => central_header.file_comment_raw.clone().from_cp437(),
|
||||
let file_comment = match is_utf8 {
|
||||
true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
|
||||
false => file_comment_raw.from_cp437(),
|
||||
};
|
||||
|
||||
// Construct the result
|
||||
let mut result = ZipFileData {
|
||||
system: System::from_u8((central_header.version_made_by >> 8) as u8),
|
||||
version_made_by: central_header.version_made_by as u8,
|
||||
encrypted: central_header.flags.encrypted(),
|
||||
using_data_descriptor: central_header.flags.using_data_descriptor(),
|
||||
system: System::from_u8((version_made_by >> 8) as u8),
|
||||
version_made_by: version_made_by as u8,
|
||||
encrypted,
|
||||
using_data_descriptor,
|
||||
compression_method: {
|
||||
#[allow(deprecated)]
|
||||
CompressionMethod::from_u16(central_header.compression_method)
|
||||
CompressionMethod::from_u16(compression_method)
|
||||
},
|
||||
compression_level: None,
|
||||
last_modified_time: DateTime::from_msdos(
|
||||
central_header.last_mod_date,
|
||||
central_header.last_mod_time,
|
||||
),
|
||||
crc32: central_header.crc32,
|
||||
compressed_size: central_header.compressed_size as u64,
|
||||
uncompressed_size: central_header.uncompressed_size as u64,
|
||||
last_modified_time: DateTime::from_msdos(last_mod_date, last_mod_time),
|
||||
crc32,
|
||||
compressed_size: compressed_size as u64,
|
||||
uncompressed_size: uncompressed_size as u64,
|
||||
file_name,
|
||||
file_name_raw: central_header.file_name_raw,
|
||||
extra_field: central_header.extra_field,
|
||||
file_name_raw,
|
||||
extra_field,
|
||||
file_comment,
|
||||
header_start: central_header.offset as u64,
|
||||
header_start: offset,
|
||||
central_header_start,
|
||||
data_start: AtomicU64::new(0),
|
||||
external_attributes: central_header.external_file_attributes,
|
||||
external_attributes: external_file_attributes,
|
||||
large_file: false,
|
||||
aes_mode: None,
|
||||
};
|
||||
|
|
319
src/spec.rs
319
src/spec.rs
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs, dead_code)]
|
||||
use crate::result::{ZipError, ZipResult};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::io;
|
||||
|
@ -9,12 +8,10 @@ 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 DATA_DESCRIPTOR_SIGNATURE: u32 = 0x08074b50;
|
||||
|
||||
pub const ZIP64_BYTES_THR: u64 = u32::MAX as u64;
|
||||
pub const ZIP64_ENTRY_THR: usize = u16::MAX as usize;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CentralDirectoryEnd {
|
||||
pub disk_number: u16,
|
||||
pub disk_with_central_directory: u16,
|
||||
|
@ -26,10 +23,6 @@ pub struct CentralDirectoryEnd {
|
|||
}
|
||||
|
||||
impl CentralDirectoryEnd {
|
||||
pub fn len(&self) -> usize {
|
||||
22 + self.zip_file_comment.len()
|
||||
}
|
||||
|
||||
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
|
||||
let magic = reader.read_u32::<LittleEndian>()?;
|
||||
if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
|
||||
|
@ -212,315 +205,3 @@ impl Zip64CentralDirectoryEnd {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
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, PartialEq)]
|
||||
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, PartialEq)]
|
||||
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, PartialEq)]
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
|
||||
writer.write_u32::<LittleEndian>(DATA_DESCRIPTOR_SIGNATURE)?;
|
||||
writer.write_u32::<LittleEndian>(self.crc32)?;
|
||||
writer.write_u32::<LittleEndian>(self.compressed_size)?;
|
||||
writer.write_u32::<LittleEndian>(self.uncompressed_size)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{
|
||||
CentralDirectoryHeader, DataDescriptor, GeneralPurposeBitFlags, LocalFileHeader, ZipResult,
|
||||
};
|
||||
use std::io::Cursor;
|
||||
#[test]
|
||||
fn test_cdh_roundtrip() -> ZipResult<()> {
|
||||
let cdh1 = CentralDirectoryHeader {
|
||||
version_made_by: 1,
|
||||
version_to_extract: 2,
|
||||
flags: GeneralPurposeBitFlags(3),
|
||||
compression_method: 4,
|
||||
last_mod_time: 5,
|
||||
last_mod_date: 6,
|
||||
crc32: 7,
|
||||
compressed_size: 8,
|
||||
uncompressed_size: 9,
|
||||
disk_number: 10,
|
||||
internal_file_attributes: 11,
|
||||
external_file_attributes: 12,
|
||||
offset: 13,
|
||||
file_name_raw: b"a".to_vec(),
|
||||
extra_field: b"bb".to_vec(),
|
||||
file_comment_raw: b"ccc".to_vec(),
|
||||
};
|
||||
let mut bytes = Vec::new();
|
||||
{
|
||||
let mut cursor = Cursor::new(&mut bytes);
|
||||
cdh1.write(&mut cursor)?;
|
||||
}
|
||||
let cdh2 = CentralDirectoryHeader::parse(&mut &bytes[..])?;
|
||||
assert_eq!(cdh1, cdh2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lfh_roundtrip() -> ZipResult<()> {
|
||||
let lfh1 = LocalFileHeader {
|
||||
version_to_extract: 1,
|
||||
flags: GeneralPurposeBitFlags(2),
|
||||
compression_method: 3,
|
||||
last_mod_time: 4,
|
||||
last_mod_date: 5,
|
||||
crc32: 6,
|
||||
compressed_size: 7,
|
||||
uncompressed_size: 8,
|
||||
file_name_raw: b"a".to_vec(),
|
||||
extra_field: b"bb".to_vec(),
|
||||
};
|
||||
let mut bytes = Vec::new();
|
||||
{
|
||||
let mut cursor = Cursor::new(&mut bytes);
|
||||
lfh1.write(&mut cursor)?;
|
||||
}
|
||||
let lfh2 = LocalFileHeader::parse(&mut &bytes[..])?;
|
||||
assert_eq!(lfh1, lfh2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dd_roundtrip() -> ZipResult<()> {
|
||||
let dd1 = DataDescriptor {
|
||||
crc32: 1,
|
||||
compressed_size: 2,
|
||||
uncompressed_size: 3,
|
||||
};
|
||||
let mut bytes = Vec::new();
|
||||
{
|
||||
let mut cursor = Cursor::new(&mut bytes);
|
||||
dd1.write(&mut cursor)?;
|
||||
}
|
||||
let dd2 = DataDescriptor::read(&mut &bytes[..])?;
|
||||
assert_eq!(dd1, dd2);
|
||||
let dd3 = DataDescriptor::read(&mut &bytes[4..])?;
|
||||
assert_eq!(dd1, dd3);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
130
src/write.rs
130
src/write.rs
|
@ -998,38 +998,44 @@ 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<()> {
|
||||
let flags = if !file.file_name.is_ascii() {
|
||||
// local file header signature
|
||||
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
|
||||
} else {
|
||||
0
|
||||
};
|
||||
writer.write_u16::<LittleEndian>(flag)?;
|
||||
// Compression method
|
||||
#[allow(deprecated)]
|
||||
let compression_method = file.compression_method.to_u16();
|
||||
let compressed_size = file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||
let uncompressed_size = file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||
|
||||
let mut extra_field = if file.large_file {
|
||||
let mut zip64_extra_field = vec![0; 20];
|
||||
write_local_zip64_extra_field(&mut zip64_extra_field, file)?;
|
||||
zip64_extra_field
|
||||
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?;
|
||||
// last mod file time and last mod file date
|
||||
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?;
|
||||
writer.write_u16::<LittleEndian>(file.last_modified_time.datepart())?;
|
||||
// crc-32
|
||||
writer.write_u32::<LittleEndian>(file.crc32)?;
|
||||
// compressed size and uncompressed size
|
||||
if file.large_file {
|
||||
writer.write_u32::<LittleEndian>(spec::ZIP64_BYTES_THR as u32)?;
|
||||
writer.write_u32::<LittleEndian>(spec::ZIP64_BYTES_THR as u32)?;
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
extra_field.extend_from_slice(&file.extra_field[..]);
|
||||
|
||||
let local_file_header = spec::LocalFileHeader {
|
||||
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,
|
||||
file_name_raw: file.file_name.as_bytes().to_vec(),
|
||||
extra_field,
|
||||
};
|
||||
local_file_header.write(writer)?;
|
||||
writer.write_u32::<LittleEndian>(file.compressed_size as u32)?;
|
||||
writer.write_u32::<LittleEndian>(file.uncompressed_size as u32)?;
|
||||
}
|
||||
// file name length
|
||||
writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16)?;
|
||||
// extra field length
|
||||
let extra_field_length = if file.large_file { 20 } else { 0 } + file.extra_field.len() as u16;
|
||||
writer.write_u16::<LittleEndian>(extra_field_length)?;
|
||||
// file name
|
||||
writer.write_all(file.file_name.as_bytes())?;
|
||||
// zip64 extra field
|
||||
if file.large_file {
|
||||
write_local_zip64_extra_field(writer, file)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1064,40 +1070,56 @@ fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData)
|
|||
let zip64_extra_field_length =
|
||||
write_central_zip64_extra_field(&mut zip64_extra_field.as_mut(), file)?;
|
||||
|
||||
let flags = if !file.file_name.is_ascii() {
|
||||
// central file header signature
|
||||
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
|
||||
} else {
|
||||
0
|
||||
};
|
||||
writer.write_u16::<LittleEndian>(flag)?;
|
||||
// compression method
|
||||
#[allow(deprecated)]
|
||||
let compression_method = file.compression_method.to_u16();
|
||||
let compressed_size = file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||
let uncompressed_size = file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
||||
let offset = file.header_start.min(spec::ZIP64_BYTES_THR) as u32;
|
||||
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?;
|
||||
// last mod file time + date
|
||||
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?;
|
||||
writer.write_u16::<LittleEndian>(file.last_modified_time.datepart())?;
|
||||
// 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>
|
||||
|
||||
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)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_extra_data(file: &ZipFileData) -> ZipResult<()> {
|
||||
|
|
Loading…
Add table
Reference in a new issue