1use crate::result::invalid;
5use crate::result::{ZipError, ZipResult};
6use crate::unstable::LittleEndianReadExt;
7use core::mem;
8use std::io::Read;
9
10#[rustfmt::skip]
12#[repr(u8)]
13#[derive(Debug, Copy, Clone, PartialEq, Eq)]
14enum ExtendedTimestampFlags {
15 Modified = 0b0000_0001_u8,
17 Accessed = 0b0000_0010_u8,
19 Created = 0b0000_0100_u8,
21 }
23
24impl ExtendedTimestampFlags {
25 #[inline(always)]
26 fn matching(flags: u8, matching_flag: Self) -> bool {
27 flags & u8::from(matching_flag) != 0
28 }
29}
30
31impl From<ExtendedTimestampFlags> for u8 {
32 fn from(extended_timestamp: ExtendedTimestampFlags) -> u8 {
33 extended_timestamp as u8
34 }
35}
36
37#[derive(Debug, Clone, PartialEq)]
39pub struct ExtendedTimestamp {
40 modified: Option<u32>,
41 accessed: Option<u32>,
42 created: Option<u32>,
43}
44
45impl ExtendedTimestamp {
46 const MAX_LENGTH: u16 = (mem::size_of::<u8>()
47 + mem::size_of::<u32>()
48 + mem::size_of::<u32>()
49 + mem::size_of::<u32>()) as u16;
50 pub fn try_from_reader<R: Read>(
60 reader: &mut R,
61 len: u16,
62 is_local_header: bool,
63 ) -> ZipResult<Self> {
64 if len == 0 {
65 return Err(invalid!("Extended timestamp field is empty"));
66 }
67 let mut flags = [0u8];
68 reader.read_exact(&mut flags)?;
69 let bytes_to_read = (len as usize)
70 .checked_sub(flags.len())
71 .ok_or_else(|| invalid!("Extended timestamp field too short for flags"))?;
72
73 if is_local_header {
74 let flags = flags[0];
75 return Self::parse_local_header(reader, len, flags, bytes_to_read);
76 }
77 Self::parse_central_header(reader, bytes_to_read)
79 }
80
81 fn parse_local_header<R: Read>(
83 reader: &mut R,
84 len: u16,
85 flags: u8,
86 mut bytes_to_read: usize,
87 ) -> ZipResult<Self> {
88 if len != 5 && u32::from(len) != 1 + 4 * flags.count_ones() {
98 return Err(ZipError::Io(std::io::Error::other(format!(
99 "flags and len don't match in extended timestamp field len={len} flags={flags:08b}"
100 ))));
101 }
102
103 let modified =
104 if (ExtendedTimestampFlags::matching(flags, ExtendedTimestampFlags::Modified)
105 && bytes_to_read >= mem::size_of::<u32>())
106 || len == Self::MAX_LENGTH
107 {
108 bytes_to_read = bytes_to_read.checked_sub(mem::size_of::<u32>()).ok_or_else(||
109 invalid!(
110 "Extended timestamp field too short for mod_time len={} flags={flags:08b}",
111 len
112 ),
113 )?;
114 Some(reader.read_u32_le()?)
115 } else {
116 None
117 };
118
119 let accessed =
120 if (ExtendedTimestampFlags::matching(flags, ExtendedTimestampFlags::Accessed)
121 && bytes_to_read >= mem::size_of::<u32>())
122 || len == Self::MAX_LENGTH
123 {
124 bytes_to_read = bytes_to_read.checked_sub(mem::size_of::<u32>()).ok_or_else(||
125 invalid!(
126 "Extended timestamp field too short for ac_time len={} flags={flags:08b}",
127 len
128 ),
129 )?;
130 Some(reader.read_u32_le()?)
131 } else {
132 None
133 };
134
135 let created =
136 if (ExtendedTimestampFlags::matching(flags, ExtendedTimestampFlags::Created)
137 && bytes_to_read >= mem::size_of::<u32>())
138 || len == Self::MAX_LENGTH
139 {
140 bytes_to_read = bytes_to_read
141 .checked_sub(mem::size_of::<u32>())
142 .ok_or_else(|| invalid!(
143 "Extended timestamp field too short for cr_time len={} flags={flags:08b}",
144 len
145 ))?;
146 Some(reader.read_u32_le()?)
147 } else {
148 None
149 };
150
151 if bytes_to_read > 0 {
152 let n = std::io::copy(&mut reader.take(bytes_to_read as u64), &mut std::io::sink())?;
154
155 if n != bytes_to_read as u64 {
156 return Err(invalid!(
157 "unexpected EOF in local header of Extended Timestamp extra field"
158 ));
159 }
160 }
161
162 Ok(Self {
163 modified,
164 accessed,
165 created,
166 })
167 }
168
169 fn parse_central_header<R: Read>(reader: &mut R, mut bytes_to_read: usize) -> ZipResult<Self> {
171 let modified = if bytes_to_read >= mem::size_of::<u32>() {
172 bytes_to_read -= mem::size_of::<u32>();
173 Some(reader.read_u32_le()?)
174 } else {
175 None
176 };
177 if bytes_to_read > 0 {
178 let n = std::io::copy(&mut reader.take(bytes_to_read as u64), &mut std::io::sink())?;
180
181 if n != bytes_to_read as u64 {
182 return Err(invalid!(
183 "unexpected EOF in central header of Extended Timestamp extra field"
184 ));
185 }
186 }
187 Ok(Self {
188 modified,
189 accessed: None,
190 created: None,
191 })
192 }
193
194 #[must_use]
196 pub fn mod_time(&self) -> Option<u32> {
197 self.modified
198 }
199
200 #[must_use]
202 pub fn ac_time(&self) -> Option<u32> {
203 self.accessed
204 }
205
206 #[must_use]
208 pub fn cr_time(&self) -> Option<u32> {
209 self.created
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::ExtendedTimestamp;
216
217 use std::io::Cursor;
218
219 #[test]
221 pub fn test_bad_extended_timestamp() {
222 use crate::ZipArchive;
223
224 assert!(
225 ZipArchive::new(Cursor::new(include_bytes!(
226 "../../tests/data/extended_timestamp_bad.zip"
227 )))
228 .is_err()
229 );
230 }
231
232 #[test]
235 fn test_extended_timestamp_overflow() {
236 let tests_args = [
237 (vec![0b0000_0001_u8, 0x00, 0x00, 0x00], 0), (vec![0b0000_0001_u8, 0x00, 0x00, 0x00], 2), (vec![0b0000_0010_u8, 0x00, 0x00, 0x00], 3), (vec![0b0000_0100_u8, 0x00, 0x00, 0x00], 4), (
243 vec![
244 0b0000_0011_u8,
245 0x00,
246 0x00,
247 0x00,
248 0x00,
249 0x00,
250 0x00,
251 0x00,
252 0x00,
253 ],
254 8,
255 ),
256 (vec![0b0000_0011_u8, 0x00, 0x00, 0x00, 0x00], 8),
258 (vec![0b0000_0000_u8], 5), ];
260 for (data, len) in tests_args {
261 let mut cursor = Cursor::new(data);
262 let result = ExtendedTimestamp::try_from_reader(&mut cursor, len, true);
263 assert!(result.is_err());
264 }
265 }
266
267 #[test]
268 fn check_extended_timestamp_value() {
269 let mut cursor = Cursor::new(&[0b0000_0001_u8, 0x00, 0x00, 0x00, 0x01]);
270 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 5, true).unwrap();
271 assert_eq!(result.mod_time(), Some(1 << 24));
272 assert_eq!(result.ac_time(), None);
273 assert_eq!(result.cr_time(), None);
274
275 let mut cursor = Cursor::new(&[0b0000_0010_u8, 0x00, 0x00, 0x00, 0x02]);
276 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 5, true).unwrap();
277 assert_eq!(result.mod_time(), None);
278 assert_eq!(result.ac_time(), Some(2 << 24));
279 assert_eq!(result.cr_time(), None);
280
281 let mut cursor = Cursor::new(&[0b0000_0100_u8, 0x00, 0x00, 0x00, 0x03]);
282 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 5, true).unwrap();
283 assert_eq!(result.mod_time(), None);
284 assert_eq!(result.ac_time(), None);
285 assert_eq!(result.cr_time(), Some(3 << 24));
286
287 let mut cursor = Cursor::new(&[
288 0b0000_0011_u8,
289 0x00,
290 0x00,
291 0x00,
292 0x01,
293 0x00,
294 0x00,
295 0x00,
296 0x02,
297 ]);
298 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 9, true).unwrap();
299 assert_eq!(result.mod_time(), Some(1 << 24));
300 assert_eq!(result.ac_time(), Some(2 << 24));
301 assert_eq!(result.cr_time(), None);
302
303 let mut cursor = Cursor::new(&[
304 0b0000_0111_u8,
305 0x00,
306 0x00,
307 0x00,
308 0x01,
309 0x00,
310 0x00,
311 0x00,
312 0x02,
313 0x00,
314 0x00,
315 0x00,
316 0x03,
317 ]);
318 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 13, true).unwrap();
319 assert_eq!(result.mod_time(), Some(1 << 24));
320 assert_eq!(result.ac_time(), Some(2 << 24));
321 assert_eq!(result.cr_time(), Some(3 << 24));
322 }
323
324 #[test]
325 fn test_extended_timestamp() {
326 let mut cursor = Cursor::new(&[0b0000_0111_u8, 0x00, 0x00, 0x00, 0x01]);
328 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 5, true).unwrap();
329 assert_eq!(result.mod_time(), Some(1 << 24));
330 assert_eq!(result.ac_time(), None);
331 assert_eq!(result.cr_time(), None);
332
333 let mut cursor = Cursor::new(&[
335 0b0000_0111_u8,
336 0x00,
337 0x00,
338 0x00,
339 0x01,
340 0x00,
341 0x00,
342 0x00,
343 0x02,
344 0x00,
345 0x00,
346 0x00,
347 0x03,
348 ]);
349 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 13, true).unwrap();
350 assert_eq!(result.mod_time(), Some(1 << 24));
351 assert_eq!(result.ac_time(), Some(2 << 24));
352 assert_eq!(result.cr_time(), Some(3 << 24));
353 }
354
355 #[test]
356 fn test_extended_timestamp_in_central_header() {
357 let mut cursor = Cursor::new(&[0b0000_0111_u8]);
358 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 1, false).unwrap();
359 assert_eq!(result.mod_time(), None);
360 assert_eq!(result.ac_time(), None);
361 assert_eq!(result.cr_time(), None);
362
363 let mut cursor = Cursor::new(&[0b0000_0100_u8, 0x00, 0xF1, 0x53, 0x65]);
364 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 5, false).unwrap();
365 assert_eq!(result.mod_time(), Some(1700000000));
366 assert_eq!(result.ac_time(), None);
367 assert_eq!(result.cr_time(), None);
368
369 let data = [
371 0b0000_0011_u8,
372 0x00,
373 0xF1,
374 0x53,
375 0x65,
376 0x01,
377 0xF1,
378 0x53,
379 0x65,
380 ];
381 let mut cursor = Cursor::new(&data);
382 let result = ExtendedTimestamp::try_from_reader(&mut cursor, 9, false).unwrap();
383 assert_eq!(result.mod_time(), Some(1700000000));
384 assert_eq!(cursor.position(), 9);
385 }
386}