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,
|
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))?;
|
||||||
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() {
|
let version_made_by = reader.read_u16::<LittleEndian>()?;
|
||||||
true => String::from_utf8_lossy(&*central_header.file_name_raw).into_owned(),
|
let _version_to_extract = reader.read_u16::<LittleEndian>()?;
|
||||||
false => central_header.file_name_raw.clone().from_cp437(),
|
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() {
|
let file_comment = match is_utf8 {
|
||||||
true => String::from_utf8_lossy(&*central_header.file_comment_raw).into_owned(),
|
true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
|
||||||
false => central_header.file_comment_raw.clone().from_cp437(),
|
false => file_comment_raw.from_cp437(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Construct the result
|
// Construct the result
|
||||||
let mut result = ZipFileData {
|
let mut result = ZipFileData {
|
||||||
system: System::from_u8((central_header.version_made_by >> 8) as u8),
|
system: System::from_u8((version_made_by >> 8) as u8),
|
||||||
version_made_by: central_header.version_made_by as u8,
|
version_made_by: version_made_by as u8,
|
||||||
encrypted: central_header.flags.encrypted(),
|
encrypted,
|
||||||
using_data_descriptor: central_header.flags.using_data_descriptor(),
|
using_data_descriptor,
|
||||||
compression_method: {
|
compression_method: {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
CompressionMethod::from_u16(central_header.compression_method)
|
CompressionMethod::from_u16(compression_method)
|
||||||
},
|
},
|
||||||
compression_level: None,
|
compression_level: None,
|
||||||
last_modified_time: DateTime::from_msdos(
|
last_modified_time: DateTime::from_msdos(last_mod_date, last_mod_time),
|
||||||
central_header.last_mod_date,
|
crc32,
|
||||||
central_header.last_mod_time,
|
compressed_size: compressed_size as u64,
|
||||||
),
|
uncompressed_size: uncompressed_size as u64,
|
||||||
crc32: central_header.crc32,
|
|
||||||
compressed_size: central_header.compressed_size as u64,
|
|
||||||
uncompressed_size: central_header.uncompressed_size as u64,
|
|
||||||
file_name,
|
file_name,
|
||||||
file_name_raw: central_header.file_name_raw,
|
file_name_raw,
|
||||||
extra_field: central_header.extra_field,
|
extra_field,
|
||||||
file_comment,
|
file_comment,
|
||||||
header_start: central_header.offset as u64,
|
header_start: offset,
|
||||||
central_header_start,
|
central_header_start,
|
||||||
data_start: AtomicU64::new(0),
|
data_start: AtomicU64::new(0),
|
||||||
external_attributes: central_header.external_file_attributes,
|
external_attributes: external_file_attributes,
|
||||||
large_file: false,
|
large_file: false,
|
||||||
aes_mode: None,
|
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 crate::result::{ZipError, ZipResult};
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -9,12 +8,10 @@ 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, PartialEq)]
|
|
||||||
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,
|
||||||
|
@ -26,10 +23,6 @@ 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 {
|
||||||
|
@ -212,315 +205,3 @@ impl Zip64CentralDirectoryEnd {
|
||||||
Ok(())
|
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<()> {
|
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
|
1u16 << 11
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
writer.write_u16::<LittleEndian>(flag)?;
|
||||||
|
// Compression method
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let compression_method = file.compression_method.to_u16();
|
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?;
|
||||||
let compressed_size = file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
// last mod file time and last mod file date
|
||||||
let uncompressed_size = file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?;
|
||||||
|
writer.write_u16::<LittleEndian>(file.last_modified_time.datepart())?;
|
||||||
let mut extra_field = if file.large_file {
|
// crc-32
|
||||||
let mut zip64_extra_field = vec![0; 20];
|
writer.write_u32::<LittleEndian>(file.crc32)?;
|
||||||
write_local_zip64_extra_field(&mut zip64_extra_field, file)?;
|
// compressed size and uncompressed size
|
||||||
zip64_extra_field
|
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 {
|
} else {
|
||||||
Vec::new()
|
writer.write_u32::<LittleEndian>(file.compressed_size as u32)?;
|
||||||
};
|
writer.write_u32::<LittleEndian>(file.uncompressed_size as u32)?;
|
||||||
extra_field.extend_from_slice(&file.extra_field[..]);
|
}
|
||||||
|
// file name length
|
||||||
let local_file_header = spec::LocalFileHeader {
|
writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16)?;
|
||||||
version_to_extract: file.version_needed(),
|
// extra field length
|
||||||
flags: spec::GeneralPurposeBitFlags(flags),
|
let extra_field_length = if file.large_file { 20 } else { 0 } + file.extra_field.len() as u16;
|
||||||
compression_method,
|
writer.write_u16::<LittleEndian>(extra_field_length)?;
|
||||||
last_mod_time: file.last_modified_time.timepart(),
|
// file name
|
||||||
last_mod_date: file.last_modified_time.datepart(),
|
writer.write_all(file.file_name.as_bytes())?;
|
||||||
crc32: file.crc32,
|
// zip64 extra field
|
||||||
compressed_size,
|
if file.large_file {
|
||||||
uncompressed_size,
|
write_local_zip64_extra_field(writer, file)?;
|
||||||
file_name_raw: file.file_name.as_bytes().to_vec(),
|
}
|
||||||
extra_field,
|
|
||||||
};
|
|
||||||
local_file_header.write(writer)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1064,40 +1070,56 @@ 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)?;
|
||||||
|
|
||||||
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
|
1u16 << 11
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
writer.write_u16::<LittleEndian>(flag)?;
|
||||||
|
// compression method
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let compression_method = file.compression_method.to_u16();
|
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?;
|
||||||
let compressed_size = file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
// last mod file time + date
|
||||||
let uncompressed_size = file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32;
|
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?;
|
||||||
let offset = file.header_start.min(spec::ZIP64_BYTES_THR) as u32;
|
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();
|
Ok(())
|
||||||
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