1use crate::raw::{
2 ArchiveExtraDataRecord, ArchiveExtraDataRecordFixed, CentralDirectoryHeader, CentralDirectoryHeaderFixed,
3 DigitalSignature, DigitalSignatureFixed, EndOfCentralDirectory, EndOfCentralDirectoryFixed, ExtensibleData,
4 ExtensibleDataFixed, LocalFileHeader, LocalFileHeaderFixed, Zip64EndOfCentralDirectory,
5 Zip64EndOfCentralDirectoryFixed,
6};
7
8pub trait PartialRecord {
10 type Partial;
11
12 fn get_partial(&self) -> &Self::Partial;
13 fn is_valid_sizes(&self) -> bool;
16}
17
18impl PartialRecord for LocalFileHeader {
19 type Partial = LocalFileHeaderFixed;
20
21 fn get_partial(&self) -> &Self::Partial { &self.fixed }
22
23 fn is_valid_sizes(&self) -> bool {
24 self.file_name.len() == self.fixed.file_name_length as usize
25 && self.extra_field.len() == self.fixed.extra_field_length as usize
26 }
27}
28
29impl PartialRecord for ArchiveExtraDataRecord {
30 type Partial = ArchiveExtraDataRecordFixed;
31
32 fn get_partial(&self) -> &Self::Partial { &self.fixed }
33
34 fn is_valid_sizes(&self) -> bool { self.extra_field_data.len() == self.fixed.extra_field_length as usize }
35}
36
37impl PartialRecord for CentralDirectoryHeader {
38 type Partial = CentralDirectoryHeaderFixed;
39
40 fn get_partial(&self) -> &Self::Partial { &self.fixed }
41
42 fn is_valid_sizes(&self) -> bool {
43 self.file_name.len() == self.fixed.file_name_length as usize
44 && self.extra_field.len() == self.fixed.extra_field_length as usize
45 && self.file_comment.len() == self.fixed.file_comment_length as usize
46 }
47}
48
49impl PartialRecord for DigitalSignature {
50 type Partial = DigitalSignatureFixed;
51
52 fn get_partial(&self) -> &Self::Partial { &self.fixed }
53
54 fn is_valid_sizes(&self) -> bool { self.signature_data.len() == self.fixed.size_of_data as usize }
55}
56
57impl PartialRecord for Zip64EndOfCentralDirectory {
58 type Partial = Zip64EndOfCentralDirectoryFixed;
59
60 fn get_partial(&self) -> &Self::Partial { &self.fixed }
61
62 fn is_valid_sizes(&self) -> bool {
63 const MIN_SIZE: usize = Zip64EndOfCentralDirectoryFixed::SIZE_IN_BYTES - 12;
66 if (self.fixed.size_of_zip64_end_of_central_directory_record as usize) < MIN_SIZE {
67 return false;
68 }
69 let length = self.fixed.size_of_zip64_end_of_central_directory_record as usize - MIN_SIZE;
70 self.zip64_extensible_data_sector.len() == length
71 }
72}
73
74impl PartialRecord for EndOfCentralDirectory {
75 type Partial = EndOfCentralDirectoryFixed;
76
77 fn get_partial(&self) -> &Self::Partial { &self.fixed }
78
79 fn is_valid_sizes(&self) -> bool { self.zip_file_comment.len() == self.fixed.zip_file_comment_length as usize }
80}
81
82impl PartialRecord for ExtensibleData {
83 type Partial = ExtensibleDataFixed;
84
85 fn get_partial(&self) -> &Self::Partial { &self.fixed }
86
87 fn is_valid_sizes(&self) -> bool { self.data.len() == self.fixed.data_size as usize }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn partial_local_file_header() {
96 let file_name = vec![48, 49, 32, 50];
97 let extra_field = vec![61, 62, 32, 63, 64];
98 let lf = LocalFileHeader {
99 fixed: LocalFileHeaderFixed {
100 local_file_header_signature: LocalFileHeaderFixed::LOCAL_FILE_HEADER_SIGNATURE,
101 version_needed_to_extract: 1,
102 general_purpose_bit_flag: 2,
103 compression_method: 3,
104 last_mod_file_time: 4,
105 last_mod_file_date: 5,
106 crc_32: 6,
107 compressed_size: 7,
108 uncompressed_size: 8,
109 file_name_length: file_name.len() as u16,
110 extra_field_length: extra_field.len() as u16,
111 },
112 file_name: file_name.clone(),
113 extra_field: extra_field.clone(),
114 };
115 assert!(lf.is_valid_sizes());
116 let _ = lf.get_partial();
117 }
118
119 #[test]
120 fn partial_archive_extra_data_record() {
121 let extra_field_data = vec![48, 49, 32, 50];
122 let ad = ArchiveExtraDataRecord {
123 fixed: ArchiveExtraDataRecordFixed {
124 archive_extra_data_signature: 1,
125 extra_field_length: extra_field_data.len() as u64,
126 },
127 extra_field_data: extra_field_data.clone(),
128 };
129 assert!(ad.is_valid_sizes());
130 let _ = ad.get_partial();
131 }
132
133 #[test]
134 fn partial_central_directory_header() {
135 let file_name = vec![48, 49, 32, 50];
136 let extra_field = vec![61, 62, 32, 63, 64];
137 let file_comment = vec![51, 52, 53, 32, 54, 55];
138 let cd = CentralDirectoryHeader {
139 fixed: CentralDirectoryHeaderFixed {
140 central_file_header_signature: 1,
141 version_made_by: 2,
142 version_needed_to_extract: 3,
143 general_purpose_bit_flag: 4,
144 compression_method: 5,
145 last_mod_file_time: 6,
146 last_mod_file_date: 7,
147 crc_32: 8,
148 compressed_size: 9,
149 uncompressed_size: 10,
150 file_name_length: file_name.len() as u16,
151 extra_field_length: extra_field.len() as u16,
152 file_comment_length: file_comment.len() as u16,
153 disk_number_start: 11,
154 internal_file_attributes: 12,
155 external_file_attributes: 13,
156 relative_offset_of_local_header: 14,
157 },
158 file_name: file_name.clone(),
159 extra_field: extra_field.clone(),
160 file_comment: file_comment.clone(),
161 };
162 assert!(cd.is_valid_sizes());
163 let _ = cd.get_partial();
164 }
165
166 #[test]
167 fn partial_digital_signature() {
168 let signature_data = vec![48, 49, 32, 50];
169 let ds = DigitalSignature {
170 fixed: DigitalSignatureFixed {
171 header_signature: DigitalSignatureFixed::HEADER_SIGNATURE,
172 size_of_data: signature_data.len() as u16,
173 },
174 signature_data: signature_data.clone(),
175 };
176 assert!(ds.is_valid_sizes());
177 let _ = ds.get_partial();
178 }
179
180 #[test]
181 fn partial_zip64_end_of_central_directory() {
182 let mut directory = Zip64EndOfCentralDirectory {
183 fixed: Zip64EndOfCentralDirectoryFixed {
184 zip64_end_of_central_dir_signature: 1,
185 size_of_zip64_end_of_central_directory_record: Zip64EndOfCentralDirectoryFixed::SIZE_IN_BYTES as u64
186 - 8,
187 version_made_by: 3,
188 version_needed_to_extract: 4,
189 number_of_this_disk: 5,
190 number_of_the_disk_with_the_start_of_the_central_directory: 6,
191 total_number_of_entries_in_the_central_directory_on_this_disk: 7,
192 total_number_of_entries_in_the_central_directory: 8,
193 size_of_the_central_directory: 9,
194 offset_of_start_of_central_directory_with_respect_to_the_starting_disk_number: 10,
195 },
196 zip64_extensible_data_sector: vec![48, 49, 50, 51],
197 };
198
199 assert!(directory.is_valid_sizes());
200 let _ = directory.get_partial();
201 directory.fixed.size_of_zip64_end_of_central_directory_record =
202 Zip64EndOfCentralDirectoryFixed::SIZE_IN_BYTES as u64 - 13;
203 assert!(!directory.is_valid_sizes());
204 }
205
206 #[test]
207 fn partial_end_of_central_directory() {
208 let zip_file_comment = vec![48, 49, 32, 50];
209 let cd = EndOfCentralDirectory {
210 fixed: EndOfCentralDirectoryFixed {
211 end_of_central_dir_signature: 1,
212 number_of_this_disk: 2,
213 number_of_the_disk_with_the_start_of_the_central_directory: 3,
214 total_number_of_entries_in_the_central_directory_on_this_disk: 4,
215 total_number_of_entries_in_the_central_directory: 5,
216 size_of_the_central_directory: 6,
217 offset_of_start_of_central_directory_with_respect_to_the_starting_disk_number: 7,
218 zip_file_comment_length: zip_file_comment.len() as u16,
219 },
220 zip_file_comment: zip_file_comment.clone(),
221 };
222 assert!(cd.is_valid_sizes());
223 let _ = cd.get_partial();
224 }
225
226 #[test]
227 fn partial_extensible_data() {
228 let data = vec![48, 49, 32];
229 let ed = ExtensibleData {
230 fixed: ExtensibleDataFixed {
231 header_id: 0x0001,
232 data_size: data.len() as u16,
233 },
234 data: data.clone(),
235 };
236 assert!(ed.is_valid_sizes());
237 let _ = ed.get_partial();
238 }
239}