diff options
| -rw-r--r-- | src/error.rs | 9 | ||||
| -rw-r--r-- | src/utils/read.rs | 4 | ||||
| -rw-r--r-- | src/zip/driver.rs | 20 | ||||
| -rw-r--r-- | src/zip/encryption.rs | 2 | ||||
| -rw-r--r-- | src/zip/error.rs | 6 | ||||
| -rw-r--r-- | tests/files/blank | 0 | ||||
| -rw-r--r-- | tests/files/empty.zip | bin | 0 -> 22 bytes | |||
| -rw-r--r-- | tests/zip.rs | 31 |
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 | ||
| 20 | impl<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 | |||
| 20 | impl<E: Error> Display for ArchiveError<E> { | 29 | impl<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 | ||
| 5 | pub type ZipResult<T> = ArchiveResult<T, ZipError>; | 5 | pub type ZipResult<T> = ArchiveResult<T, ZipError>; |
| 6 | 6 | ||
| 7 | #[derive(Debug)] | 7 | #[derive(Debug, PartialEq, Eq)] |
| 8 | pub enum ZipError { | 8 | pub 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> { | |||
| 38 | impl Display for ZipError { | 38 | impl 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 @@ | |||
| 1 | use archivator::zip::ZipError; | ||
| 1 | use archivator::{Archive, Zip}; | 2 | use archivator::{Archive, Zip}; |
| 2 | use std::io::{Read, Seek, SeekFrom}; | 3 | use 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] | ||
| 101 | fn 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 | } | ||
