aboutsummaryrefslogtreecommitdiff
path: root/src/zip/driver.rs
diff options
context:
space:
mode:
authorIgor Tolmachev <me@igorek.dev>2024-06-15 03:30:50 +0900
committerIgor Tolmachev <me@igorek.dev>2024-06-23 15:34:34 +0900
commitf8c3c93824645a807d28b760855b4676ea479720 (patch)
tree1f91838c2abcb3b0683a061f892b8e2835be4fa1 /src/zip/driver.rs
parentbd77f62e99a5300dfa52aef3a7040414b28ebfd6 (diff)
downloadarchivator-f8c3c93824645a807d28b760855b4676ea479720.tar.gz
archivator-f8c3c93824645a807d28b760855b4676ea479720.zip
Add simple zip reader
Diffstat (limited to 'src/zip/driver.rs')
-rw-r--r--src/zip/driver.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
new file mode 100644
index 0000000..733d44b
--- /dev/null
+++ b/src/zip/driver.rs
@@ -0,0 +1,143 @@
1use crate::driver::{ArchiveRead, ArchiveWrite, Driver};
2use crate::zip::error::{ZipError, ZipResult};
3use crate::zip::structs::{EOCDR64Locator, CDR, EOCDR, EOCDR64};
4use crate::zip::ZipFile;
5use std::collections::HashMap as Map;
6use std::fs::File;
7use std::io::{Read, Seek, SeekFrom, Write};
8
9pub struct Zip<IO = File> {
10 io: IO,
11
12 files: Map<String, ZipFile>,
13 comment: String,
14}
15
16impl<IO> Driver for Zip<IO> {
17 type Error = ZipError;
18
19 type IO = IO;
20 type File = ZipFile;
21}
22
23impl<IO: Read + Seek> ArchiveRead for Zip<IO> {
24 fn read(mut io: Self::IO) -> ZipResult<Self> {
25 // Search eocdr
26 let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64;
27 let start = io.seek(SeekFrom::End(-limit))?;
28 let pos = start + {
29 let mut buf = vec![0; limit as usize];
30 io.read(&mut buf)?;
31 buf[..buf.len() - 18]
32 .windows(4)
33 .rposition(|v| u32::from_le_bytes(v.try_into().unwrap()) == 0x06054b50)
34 .ok_or(ZipError::EOCDRNotFound)? as u64
35 };
36
37 // Read eocdr
38 io.seek(SeekFrom::Start(pos + 4))?;
39 let buf = {
40 let mut buf = [0; 18];
41 io.read(&mut buf)?;
42 buf
43 };
44 let eocdr: EOCDR = bincode::deserialize(&buf).map_err(|_| ZipError::InvalidEOCDR)?;
45 let comment = {
46 let mut buf = vec![0; eocdr.comment_len as usize];
47 io.read(&mut buf)?;
48 String::from_utf8(buf).map_err(|_| ZipError::InvalidArchiveComment)?
49 };
50
51 // Try to find eocdr64locator
52 io.seek(SeekFrom::Start(pos - 20))?;
53 let buf = {
54 let mut buf = [0; 20];
55 io.read(&mut buf)?;
56 buf
57 };
58 let (cd_pointer, cd_size, cd_records) =
59 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) == 0x07064b50 {
60 let eocdr64locator: EOCDR64Locator =
61 bincode::deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64Locator)?;
62
63 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?;
64 let buf = {
65 let mut buf = [0; 56];
66 io.read(&mut buf)?;
67 buf
68 };
69 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 {
70 return Err(ZipError::InvalidEOCDR64Signature.into());
71 }
72 let eocdr64: EOCDR64 =
73 bincode::deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64)?;
74
75 (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records)
76 } else {
77 (
78 eocdr.cd_pointer as u64,
79 eocdr.cd_size as u64,
80 eocdr.cd_records as u64,
81 )
82 };
83
84 // Read cd records
85 let mut files = Map::with_capacity(cd_records as usize);
86 io.seek(SeekFrom::Start(cd_pointer))?;
87 let buf = {
88 let mut buf = vec![0; cd_size as usize];
89 io.read(&mut buf)?;
90 buf
91 };
92 let mut records = buf.as_slice();
93
94 for _ in 0..cd_records {
95 let buf = {
96 let mut buf = [0; 46];
97 records.read(&mut buf)?;
98 buf
99 };
100
101 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x02014b50 {
102 return Err(ZipError::InvalidCDRSignature.into());
103 }
104 let cdr: CDR = bincode::deserialize(&buf[4..]).map_err(|_| ZipError::InvalidCDR)?;
105 let name = {
106 let mut buf = vec![0; cdr.name_len as usize];
107 records.read(&mut buf)?;
108 String::from_utf8(buf).map_err(|_| ZipError::InvalidFileName)?
109 };
110 let extra_fields = {
111 let mut buf = vec![0; cdr.extra_field_len as usize];
112 records.read(&mut buf)?;
113 buf
114 };
115 let comment = {
116 let mut buf = vec![0; cdr.comment_len as usize];
117 records.read(&mut buf)?;
118 String::from_utf8(buf).map_err(|_| ZipError::InvalidFileComment)?
119 };
120
121 files.insert(
122 name.clone(),
123 ZipFile::new(
124 name,
125 cdr.dos_date,
126 cdr.dos_time,
127 cdr.compression_method,
128 cdr.compressed_size as u64,
129 cdr.size as u64,
130 comment,
131 ),
132 );
133 }
134
135 Ok(Self { io, files, comment })
136 }
137}
138
139impl<IO: Read + Seek> Zip<IO> {}
140
141impl<IO: Read + Write + Seek> ArchiveWrite for Zip<IO> {}
142
143impl<IO: Read + Write> Zip<IO> {}