aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/error.rs9
-rw-r--r--src/utils/read.rs4
-rw-r--r--src/zip/driver.rs20
-rw-r--r--src/zip/encryption.rs2
-rw-r--r--src/zip/error.rs6
-rw-r--r--tests/files/blank0
-rw-r--r--tests/files/empty.zipbin0 -> 22 bytes
-rw-r--r--tests/zip.rs31
8 files changed, 50 insertions, 22 deletions
diff --git a/src/error.rs b/src/error.rs
index 97a4e62..518b0e9 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -17,6 +17,15 @@ impl<E: Error> From<io::Error> for ArchiveError<E> {
17 } 17 }
18} 18}
19 19
20impl<E: Error + PartialEq> PartialEq<E> for ArchiveError<E> {
21 fn eq(&self, other: &E) -> bool {
22 match self {
23 Self::Archivator { error, .. } => error == other,
24 _ => false,
25 }
26 }
27}
28
20impl<E: Error> Display for ArchiveError<E> { 29impl<E: Error> Display for ArchiveError<E> {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self { 31 match self {
diff --git a/src/utils/read.rs b/src/utils/read.rs
index 185758a..491c89c 100644
--- a/src/utils/read.rs
+++ b/src/utils/read.rs
@@ -10,14 +10,14 @@ impl<R: Read> ReadUtils for R {
10 #[inline] 10 #[inline]
11 fn read_arr<const S: usize>(&mut self) -> Result<[u8; S], std::io::Error> { 11 fn read_arr<const S: usize>(&mut self) -> Result<[u8; S], std::io::Error> {
12 let mut arr = [0; S]; 12 let mut arr = [0; S];
13 self.read(&mut arr)?; 13 self.read_exact(&mut arr)?;
14 Ok(arr) 14 Ok(arr)
15 } 15 }
16 16
17 #[inline] 17 #[inline]
18 fn read_vec(&mut self, size: usize) -> Result<Vec<u8>, std::io::Error> { 18 fn read_vec(&mut self, size: usize) -> Result<Vec<u8>, std::io::Error> {
19 let mut vec = vec![0; size]; 19 let mut vec = vec![0; size];
20 self.read(&mut vec)?; 20 self.read_exact(&mut vec)?;
21 Ok(vec) 21 Ok(vec)
22 } 22 }
23} 23}
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
index 62da39f..631c4ed 100644
--- a/src/zip/driver.rs
+++ b/src/zip/driver.rs
@@ -68,23 +68,23 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
68 let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; 68 let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64;
69 let start = io.seek(SeekFrom::End(-limit))?; 69 let start = io.seek(SeekFrom::End(-limit))?;
70 let pos = start 70 let pos = start
71 + io.read_vec(limit as usize - 18)? 71 + io.read_vec(
72 .windows(4) 72 (limit as usize)
73 .rposition(|v| u32::from_le_bytes(v.try_into().unwrap()) == 0x06054b50) 73 .checked_sub(18)
74 .ok_or(ZipError::EOCDRNotFound)? as u64; 74 .ok_or(ZipError::EocdrNotFound)?,
75 )?
76 .windows(4)
77 .rposition(|v| u32::from_le_bytes(v.try_into().unwrap()) == 0x06054b50)
78 .ok_or(ZipError::EocdrNotFound)? as u64;
75 79
76 // Read eocdr 80 // Read eocdr
77 io.seek(SeekFrom::Start(pos + 4))?; 81 io.seek(SeekFrom::Start(pos + 4))?;
78 let buf = io.read_arr::<18>()?; 82 let buf = io.read_arr::<18>()?;
79 let eocdr: Eocdr = deserialize(&buf).unwrap(); 83 let eocdr: Eocdr = deserialize(&buf).unwrap();
80 let comment = { 84 let comment = String::from_cp437(io.read_vec(eocdr.comment_len as usize)?);
81 let mut buf: Vec<u8> = vec![0; eocdr.comment_len as usize];
82 io.read(&mut buf)?;
83 String::from_cp437(buf)
84 };
85 85
86 // Try to find eocdr64locator 86 // Try to find eocdr64locator
87 io.seek(SeekFrom::Start(pos - 20))?; 87 io.seek(SeekFrom::Start(pos.saturating_sub(20)))?;
88 let buf = io.read_arr::<20>()?; 88 let buf = io.read_arr::<20>()?;
89 let (cd_pointer, cd_size, cd_records) = 89 let (cd_pointer, cd_size, cd_records) =
90 // If locator found then read eocdr64 90 // If locator found then read eocdr64
diff --git a/src/zip/encryption.rs b/src/zip/encryption.rs
index 28a6bdb..76824a1 100644
--- a/src/zip/encryption.rs
+++ b/src/zip/encryption.rs
@@ -72,7 +72,7 @@ impl<Io: Read> WeakDecoder<Io> {
72 72
73 fn decode_byte(&mut self, byte: u8) -> u8 { 73 fn decode_byte(&mut self, byte: u8) -> u8 {
74 let key = self.key2 | 2; 74 let key = self.key2 | 2;
75 let byte = byte ^ ((key * (key ^ 1)) >> 8) as u8; 75 let byte = byte ^ ((key.wrapping_mul(key ^ 1)) >> 8) as u8;
76 self.update_keys(byte); 76 self.update_keys(byte);
77 byte 77 byte
78 } 78 }
diff --git a/src/zip/error.rs b/src/zip/error.rs
index a4b8c2b..525a67b 100644
--- a/src/zip/error.rs
+++ b/src/zip/error.rs
@@ -4,9 +4,9 @@ use std::fmt::Display;
4 4
5pub type ZipResult<T> = ArchiveResult<T, ZipError>; 5pub type ZipResult<T> = ArchiveResult<T, ZipError>;
6 6
7#[derive(Debug)] 7#[derive(Debug, PartialEq, Eq)]
8pub enum ZipError { 8pub enum ZipError {
9 EOCDRNotFound, 9 EocdrNotFound,
10 InvalidEOCDR64Signature, 10 InvalidEOCDR64Signature,
11 InvalidFileHeaderSignature, 11 InvalidFileHeaderSignature,
12 InvalidCDRSignature, 12 InvalidCDRSignature,
@@ -38,7 +38,7 @@ impl From<ZipError> for ArchiveError<ZipError> {
38impl Display for ZipError { 38impl Display for ZipError {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 match self { 40 match self {
41 Self::EOCDRNotFound => write!(f, "End of central directory record not found"), 41 Self::EocdrNotFound => write!(f, "End of central directory record not found"),
42 Self::InvalidEOCDR64Signature => { 42 Self::InvalidEOCDR64Signature => {
43 write!( 43 write!(
44 f, 44 f,
diff --git a/tests/files/blank b/tests/files/blank
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/blank
diff --git a/tests/files/empty.zip b/tests/files/empty.zip
new file mode 100644
index 0000000..15cb0ec
--- /dev/null
+++ b/tests/files/empty.zip
Binary files differ
diff --git a/tests/zip.rs b/tests/zip.rs
index d02e96e..2c8fc56 100644
--- a/tests/zip.rs
+++ b/tests/zip.rs
@@ -1,3 +1,4 @@
1use archivator::zip::ZipError;
1use archivator::{Archive, Zip}; 2use archivator::{Archive, Zip};
2use std::io::{Read, Seek, SeekFrom}; 3use std::io::{Read, Seek, SeekFrom};
3 4
@@ -15,10 +16,12 @@ fn test_zip_passwd() {
15 vec!["store", "deflate", "bzip"] 16 vec!["store", "deflate", "bzip"]
16 ); 17 );
17 18
18 assert!(archive.get_file_reader_by_name("store").is_err()); 19 assert!(archive
20 .get_file_reader_by_name("store")
21 .is_err_and(|e| e == ZipError::PasswordIsNotSpecified));
19 assert!(archive 22 assert!(archive
20 .get_file_reader_by_name_with_password("store", b"wrong_passwd") 23 .get_file_reader_by_name_with_password("store", b"wrong_passwd")
21 .is_err()); 24 .is_err_and(|e| e == ZipError::IncorrectPassword));
22 25
23 for (name, check_data) in [ 26 for (name, check_data) in [
24 ("store", "1e643774f40510e37c6f3c451d9d"), 27 ("store", "1e643774f40510e37c6f3c451d9d"),
@@ -62,22 +65,25 @@ fn test_zip() {
62 65
63 assert_eq!(f.seek(SeekFrom::Start(0)).unwrap(), 0); 66 assert_eq!(f.seek(SeekFrom::Start(0)).unwrap(), 0);
64 let mut data = vec![0; 4]; 67 let mut data = vec![0; 4];
65 f.read(&mut data).unwrap(); 68 f.read_exact(&mut data).unwrap();
66 assert_eq!(String::from_utf8(data).unwrap(), "test"); 69 assert_eq!(String::from_utf8(data).unwrap(), "test");
67 70
68 assert_eq!(f.seek(SeekFrom::Current(1)).unwrap(), 5); 71 assert_eq!(f.seek(SeekFrom::Current(1)).unwrap(), 5);
69 let mut data = vec![0; 4]; 72 let mut data = vec![0; 4];
70 f.read(&mut data).unwrap(); 73 f.read_exact(&mut data).unwrap();
71 assert_eq!(String::from_utf8(data).unwrap(), "file"); 74 assert_eq!(String::from_utf8(data).unwrap(), "file");
72 75
73 assert_eq!(f.seek(SeekFrom::End(-4)).unwrap(), 10); 76 assert_eq!(f.seek(SeekFrom::End(-4)).unwrap(), 10);
74 let mut data = vec![0; 4]; 77 let mut data = vec![0; 4];
75 f.read(&mut data).unwrap(); 78 f.read_exact(&mut data).unwrap();
76 assert_eq!(String::from_utf8(data).unwrap(), "data"); 79 assert_eq!(String::from_utf8(data).unwrap(), "data");
77 80
78 f.seek(SeekFrom::Start(7)).unwrap(); 81 f.seek(SeekFrom::Start(7)).unwrap();
79 assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 7); 82 assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 7);
80 assert!(f.seek(SeekFrom::End(-100)).is_err()); 83 assert!(f
84 .seek(SeekFrom::End(-100))
85 .is_err_and(|e| e.get_ref().unwrap().to_string()
86 == "Invalid seek to a negative or overflowing position"));
81 assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 7); 87 assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 7);
82 88
83 assert_eq!(f.seek(SeekFrom::Start(100)).unwrap(), 14); 89 assert_eq!(f.seek(SeekFrom::Start(100)).unwrap(), 14);
@@ -90,3 +96,16 @@ fn test_zip() {
90 assert!(!f.is_seekable() || f.info().name == "store") 96 assert!(!f.is_seekable() || f.info().name == "store")
91 } 97 }
92} 98}
99
100#[test]
101fn test_bad_zip() {
102 assert!(Archive::<Zip>::read_from_file("tests/files/blank")
103 .is_err_and(|e| e == ZipError::EocdrNotFound));
104
105 assert_eq!(
106 Archive::<Zip>::read_from_file("tests/files/empty.zip")
107 .unwrap()
108 .len(),
109 0
110 );
111}