aboutsummaryrefslogtreecommitdiff
path: root/src/zip
diff options
context:
space:
mode:
Diffstat (limited to 'src/zip')
-rw-r--r--src/zip/archive.rs4
-rw-r--r--src/zip/driver.rs38
-rw-r--r--src/zip/error.rs8
-rw-r--r--src/zip/file_driver.rs99
-rw-r--r--src/zip/file_info.rs (renamed from src/zip/file.rs)11
-rw-r--r--src/zip/mod.rs6
-rw-r--r--src/zip/tests.rs2
7 files changed, 142 insertions, 26 deletions
diff --git a/src/zip/archive.rs b/src/zip/archive.rs
index 9a244fc..79e8ca1 100644
--- a/src/zip/archive.rs
+++ b/src/zip/archive.rs
@@ -1,10 +1,10 @@
1use crate::{Archive, Zip}; 1use crate::{Archive, Zip};
2use std::io::{Read, Seek, Write}; 2use std::io::{Read, Seek, Write};
3 3
4impl<IO: Read + Seek> Archive<Zip<IO>> { 4impl<Io: Read + Seek> Archive<Zip<Io>> {
5 pub fn comment(&self) -> &String { 5 pub fn comment(&self) -> &String {
6 self.driver.comment() 6 self.driver.comment()
7 } 7 }
8} 8}
9 9
10impl<IO: Read + Write + Seek> Archive<Zip<IO>> {} 10impl<Io: Read + Write + Seek> Archive<Zip<Io>> {}
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
index 650344e..0db845d 100644
--- a/src/zip/driver.rs
+++ b/src/zip/driver.rs
@@ -1,27 +1,27 @@
1use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; 1use crate::driver::{ArchiveRead, ArchiveWrite, Driver};
2use crate::zip::file::{BitFlag, CompressionMethod};
3use crate::zip::structs::{deserialize, EOCDR64Locator, ExtraHeader, CDR, EOCDR, EOCDR64}; 2use crate::zip::structs::{deserialize, EOCDR64Locator, ExtraHeader, CDR, EOCDR, EOCDR64};
4use crate::zip::{ZipError, ZipFile, ZipResult}; 3use crate::zip::{BitFlag, CompressionMethod, ZipError, ZipFile, ZipFileInfo, ZipResult};
5use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime}; 4use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime};
6use std::collections::HashMap as Map; 5use std::collections::HashMap as Map;
7use std::io::{Read, Seek, SeekFrom, Write}; 6use std::io::{Read, Seek, SeekFrom, Write};
8 7
9pub struct Zip<IO> { 8pub struct Zip<Io> {
10 io: IO, 9 io: Io,
11 10
12 files: Map<String, ZipFile>, 11 files: Map<String, ZipFileInfo>,
13 comment: String, 12 comment: String,
14} 13}
15 14
16impl<IO> Driver for Zip<IO> { 15impl<Io> Driver for Zip<Io> {
17 type Error = ZipError; 16 type Error = ZipError;
18 17
19 type IO = IO; 18 type Io = Io;
20 type File = ZipFile; 19 type FileDriver<'d> = ZipFile<'d, Self::Io> where Self::Io: 'd;
20 type FileInfo = ZipFileInfo;
21} 21}
22 22
23impl<IO: Read + Seek> ArchiveRead for Zip<IO> { 23impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
24 fn read(mut io: Self::IO) -> ZipResult<Self> { 24 fn read(mut io: Self::Io) -> ZipResult<Self> {
25 // Search eocdr 25 // Search eocdr
26 let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; 26 let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64;
27 let start = io.seek(SeekFrom::End(-limit))?; 27 let start = io.seek(SeekFrom::End(-limit))?;
@@ -137,7 +137,7 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> {
137 137
138 files.insert( 138 files.insert(
139 name.clone(), 139 name.clone(),
140 ZipFile::new( 140 ZipFileInfo::new(
141 CompressionMethod::from_struct_id(cdr.compression_method)?, 141 CompressionMethod::from_struct_id(cdr.compression_method)?,
142 BitFlag::new(cdr.bit_flag), 142 BitFlag::new(cdr.bit_flag),
143 NaiveDateTime::new( 143 NaiveDateTime::new(
@@ -169,23 +169,27 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> {
169 Ok(Self { io, files, comment }) 169 Ok(Self { io, files, comment })
170 } 170 }
171 171
172 fn files(&self) -> Vec<&Self::File> { 172 fn files(&self) -> Vec<&Self::FileInfo> {
173 let mut files: Vec<&Self::File> = self.files.values().collect(); 173 let mut files: Vec<&Self::FileInfo> = self.files.values().collect();
174 files.sort_by_key(|f| &f.name); 174 files.sort_by_key(|f| &f.name);
175 files 175 files
176 } 176 }
177 177
178 fn get_file(&self, name: &str) -> Option<&Self::File> { 178 fn get_file_info(&self, name: &str) -> Option<&Self::FileInfo> {
179 self.files.get(name) 179 self.files.get(name)
180 } 180 }
181
182 fn get_file_reader<'d>(&'d mut self, name: &str) -> Option<Self::FileDriver<'d>> {
183 Some(ZipFile::new(&mut self.io, self.files.get(name)?).unwrap())
184 }
181} 185}
182 186
183impl<IO: Read + Seek> Zip<IO> { 187impl<Io: Read + Seek> Zip<Io> {
184 pub fn comment(&self) -> &String { 188 pub fn comment(&self) -> &String {
185 &self.comment 189 &self.comment
186 } 190 }
187} 191}
188 192
189impl<IO: Read + Write + Seek> ArchiveWrite for Zip<IO> {} 193impl<Io: Read + Write + Seek> ArchiveWrite for Zip<Io> {}
190 194
191impl<IO: Read + Write + Seek> Zip<IO> {} 195impl<Io: Read + Write + Seek> Zip<Io> {}
diff --git a/src/zip/error.rs b/src/zip/error.rs
index 1c5527c..d82de78 100644
--- a/src/zip/error.rs
+++ b/src/zip/error.rs
@@ -8,6 +8,7 @@ pub type ZipResult<T> = ArchiveResult<T, ZipError>;
8pub enum ZipError { 8pub enum ZipError {
9 EOCDRNotFound, 9 EOCDRNotFound,
10 InvalidEOCDR64Signature, 10 InvalidEOCDR64Signature,
11 InvalidFileHeaderSignature,
11 InvalidCDRSignature, 12 InvalidCDRSignature,
12 13
13 InvalidArchiveComment, 14 InvalidArchiveComment,
@@ -17,6 +18,8 @@ pub enum ZipError {
17 InvalidTime, 18 InvalidTime,
18 InvalidFileName, 19 InvalidFileName,
19 InvalidFileComment, 20 InvalidFileComment,
21
22 NegativeFileOffset,
20} 23}
21 24
22impl From<ZipError> for ArchiveError<ZipError> { 25impl From<ZipError> for ArchiveError<ZipError> {
@@ -38,6 +41,9 @@ impl Display for ZipError {
38 "Invalid signature of zip64 end of central directory record" 41 "Invalid signature of zip64 end of central directory record"
39 ) 42 )
40 } 43 }
44 Self::InvalidFileHeaderSignature => {
45 write!(f, "Invalid file header signature")
46 }
41 Self::InvalidCDRSignature => { 47 Self::InvalidCDRSignature => {
42 write!(f, "Invalid signature of central directory record") 48 write!(f, "Invalid signature of central directory record")
43 } 49 }
@@ -49,6 +55,8 @@ impl Display for ZipError {
49 Self::InvalidTime => write!(f, "Invalid time"), 55 Self::InvalidTime => write!(f, "Invalid time"),
50 Self::InvalidFileName => write!(f, "Invalid file name"), 56 Self::InvalidFileName => write!(f, "Invalid file name"),
51 Self::InvalidFileComment => write!(f, "Invalid file comment"), 57 Self::InvalidFileComment => write!(f, "Invalid file comment"),
58
59 Self::NegativeFileOffset => write!(f, "Negative file offset"),
52 } 60 }
53 } 61 }
54} 62}
diff --git a/src/zip/file_driver.rs b/src/zip/file_driver.rs
new file mode 100644
index 0000000..47b4242
--- /dev/null
+++ b/src/zip/file_driver.rs
@@ -0,0 +1,99 @@
1use crate::driver::FileDriver;
2use crate::zip::{ZipError, ZipFileInfo, ZipResult};
3use std::io::{
4 Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, Take,
5 Write,
6};
7
8pub struct ZipFile<'d, Io> {
9 io: &'d mut Io,
10 info: &'d ZipFileInfo,
11
12 bounds: (u64, u64),
13 cursor: u64,
14}
15
16impl<'d, Io> FileDriver for ZipFile<'d, Io> {
17 type Io = Io;
18 type FileInfo = ZipFileInfo;
19
20 fn info(&self) -> &Self::FileInfo {
21 self.info
22 }
23}
24
25impl<'d, Io: Read + Seek> ZipFile<'d, Io> {
26 pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult<Self> {
27 io.seek(SeekFrom::Start(info.header_pointer))?;
28 let buf = {
29 let mut buf = [0; 30];
30 io.read(&mut buf)?;
31 buf
32 };
33 if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 {
34 return Err(ZipError::InvalidFileHeaderSignature.into());
35 }
36 let data_pointer = info.header_pointer
37 + 30
38 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64
39 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64;
40 io.seek(SeekFrom::Start(data_pointer))?;
41
42 Ok(Self {
43 io,
44 info,
45
46 bounds: (data_pointer, data_pointer + info.compressed_size),
47 cursor: data_pointer,
48 })
49 }
50}
51
52impl<'d, Io: Read> Read for ZipFile<'d, Io> {
53 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
54 let upper = buf.len().min((self.bounds.1 - self.cursor) as usize);
55 self.cursor += upper as u64;
56 self.io.read(&mut buf[..upper])
57 }
58}
59
60impl<'d, Io: Write> Write for ZipFile<'d, Io> {
61 fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
62 todo!()
63 }
64
65 fn flush(&mut self) -> IoResult<()> {
66 todo!()
67 }
68}
69
70impl<'d, Io: Seek> Seek for ZipFile<'d, Io> {
71 fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
72 self.cursor = match pos {
73 SeekFrom::Start(offset) => self.bounds.0 + offset,
74 SeekFrom::End(offset) => {
75 let cursor = self.bounds.1.saturating_add_signed(offset);
76 if cursor < self.bounds.0 {
77 return Err(IoError::new(
78 IoErrorKind::InvalidInput,
79 ZipError::NegativeFileOffset,
80 ));
81 }
82 cursor
83 }
84 SeekFrom::Current(offset) => {
85 let cursor = self.cursor.saturating_add_signed(offset);
86 if cursor < self.bounds.0 {
87 return Err(IoError::new(
88 IoErrorKind::InvalidInput,
89 ZipError::NegativeFileOffset,
90 ));
91 }
92 cursor
93 }
94 }
95 .min(self.bounds.1);
96
97 Ok(self.io.seek(SeekFrom::Start(self.cursor))? - self.bounds.0)
98 }
99}
diff --git a/src/zip/file.rs b/src/zip/file_info.rs
index 5b0723f..88322be 100644
--- a/src/zip/file.rs
+++ b/src/zip/file_info.rs
@@ -1,7 +1,8 @@
1use crate::driver::ArchiveFile; 1use crate::driver::ArchiveFileInfo;
2use crate::zip::{ZipError, ZipResult}; 2use crate::zip::{ZipError, ZipResult};
3use chrono::{DateTime, Local}; 3use chrono::{DateTime, Local};
4 4
5#[derive(Debug)]
5pub enum CompressionMethod { 6pub enum CompressionMethod {
6 Store, 7 Store,
7 Deflate, 8 Deflate,
@@ -28,6 +29,7 @@ impl CompressionMethod {
28 } 29 }
29} 30}
30 31
32#[derive(Debug)]
31pub struct BitFlag { 33pub struct BitFlag {
32 flag: u16, 34 flag: u16,
33} 35}
@@ -119,7 +121,8 @@ impl BitFlag {
119 } 121 }
120} 122}
121 123
122pub struct ZipFile { 124#[derive(Debug)]
125pub struct ZipFileInfo {
123 pub compression_method: CompressionMethod, 126 pub compression_method: CompressionMethod,
124 pub bit_flag: BitFlag, 127 pub bit_flag: BitFlag,
125 pub datetime: DateTime<Local>, 128 pub datetime: DateTime<Local>,
@@ -131,7 +134,7 @@ pub struct ZipFile {
131 pub comment: String, 134 pub comment: String,
132} 135}
133 136
134impl ZipFile { 137impl ZipFileInfo {
135 pub fn new( 138 pub fn new(
136 compression_method: CompressionMethod, 139 compression_method: CompressionMethod,
137 bit_flag: BitFlag, 140 bit_flag: BitFlag,
@@ -157,4 +160,4 @@ impl ZipFile {
157 } 160 }
158} 161}
159 162
160impl ArchiveFile for ZipFile {} 163impl ArchiveFileInfo for ZipFileInfo {}
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index 89d748b..3fe8384 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -1,12 +1,14 @@
1mod archive; 1mod archive;
2mod driver; 2mod driver;
3mod error; 3mod error;
4mod file; 4mod file_driver;
5mod file_info;
5mod structs; 6mod structs;
6 7
7pub use driver::Zip; 8pub use driver::Zip;
8pub use error::{ZipError, ZipResult}; 9pub use error::{ZipError, ZipResult};
9pub use file::{bit, BitFlag, CompressionMethod, ZipFile}; 10pub use file_driver::ZipFile;
11pub use file_info::{bit, BitFlag, CompressionMethod, ZipFileInfo};
10 12
11#[cfg(test)] 13#[cfg(test)]
12mod tests; 14mod tests;
diff --git a/src/zip/tests.rs b/src/zip/tests.rs
index d64e626..05e076d 100644
--- a/src/zip/tests.rs
+++ b/src/zip/tests.rs
@@ -1,4 +1,4 @@
1use crate::zip::file::{bit::DeflateMode, BitFlag}; 1use crate::zip::file_info::{bit::DeflateMode, BitFlag};
2 2
3#[test] 3#[test]
4fn test_bit_flag() { 4fn test_bit_flag() {