diff options
| author | Tolmachev Igor <me@igorek.dev> | 2024-08-27 18:03:30 +0900 |
|---|---|---|
| committer | Tolmachev Igor <me@igorek.dev> | 2024-08-27 18:03:30 +0900 |
| commit | 27da50f9d157927ec56dae8316d0edc34eaa244d (patch) | |
| tree | 1dc63d0bcdb22086e24f4dbf1f9902f8f97cd0d9 | |
| parent | a24ae8622cc2f829a8101a7f812fc98297053cc3 (diff) | |
| download | archivator-27da50f9d157927ec56dae8316d0edc34eaa244d.tar.gz archivator-27da50f9d157927ec56dae8316d0edc34eaa244d.zip | |
Rewrite Eocdr64Locator search algorithm
| -rw-r--r-- | src/zip/driver.rs | 70 | ||||
| -rw-r--r-- | src/zip/error.rs | 8 | ||||
| -rw-r--r-- | tests/zip.rs | 81 |
3 files changed, 93 insertions, 66 deletions
diff --git a/src/zip/driver.rs b/src/zip/driver.rs index 7758479..0502c85 100644 --- a/src/zip/driver.rs +++ b/src/zip/driver.rs | |||
| @@ -79,7 +79,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 79 | // Search eocdr | 79 | // Search eocdr |
| 80 | let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; | 80 | let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; |
| 81 | let start = io.seek(SeekFrom::End(-limit))?; | 81 | let start = io.seek(SeekFrom::End(-limit))?; |
| 82 | let pos = start | 82 | let eocdr_pos = start |
| 83 | + io.read_vec( | 83 | + io.read_vec( |
| 84 | (limit as usize) | 84 | (limit as usize) |
| 85 | .checked_sub(18) | 85 | .checked_sub(18) |
| @@ -90,46 +90,50 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 90 | .ok_or(ZipError::StructNotFound("Eocdr"))? as u64; | 90 | .ok_or(ZipError::StructNotFound("Eocdr"))? as u64; |
| 91 | 91 | ||
| 92 | // Read eocdr | 92 | // Read eocdr |
| 93 | io.seek(SeekFrom::Start(pos + 4))?; | 93 | io.seek(SeekFrom::Start(eocdr_pos + 4))?; |
| 94 | let buf = io.read_arr::<18>()?; | 94 | let buf = io.read_arr::<18>()?; |
| 95 | let eocdr: Eocdr = deserialize(&buf).unwrap(); | 95 | let eocdr: Eocdr = deserialize(&buf).unwrap(); |
| 96 | let comment = String::from_cp437(io.read_vec(eocdr.comment_len as usize)?); | 96 | let comment = String::from_cp437(io.read_vec(eocdr.comment_len as usize)?); |
| 97 | 97 | ||
| 98 | let mut cd_pointer = eocdr.cd_pointer as u64; | ||
| 99 | let mut cd_size = eocdr.cd_size as u64; | ||
| 100 | let mut cd_records = eocdr.cd_records as u64; | ||
| 101 | |||
| 98 | // Try to find eocdr64locator | 102 | // Try to find eocdr64locator |
| 99 | io.seek(SeekFrom::Start(pos.saturating_sub(20)))?; | 103 | if eocdr_pos >= 20 { |
| 100 | let buf = io.read_arr::<20>()?; | 104 | io.seek(SeekFrom::Start(eocdr_pos - 20))?; |
| 101 | let (cd_pointer, cd_size, cd_records) = if buf[..4] == EOCDR64_LOCATOR_SIGNATURE { | 105 | let buf = io.read_arr::<20>()?; |
| 102 | // Locator found | ||
| 103 | let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap(); | ||
| 104 | |||
| 105 | io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; | ||
| 106 | if io.read_arr()? != EOCDR64_SIGNATURE { | ||
| 107 | return Err(ZipError::InvalidSignature("Eocdr64")); | ||
| 108 | } | ||
| 109 | 106 | ||
| 110 | let eocdr64: Eocdr64 = deserialize(&io.read_arr::<52>()?).unwrap(); | 107 | if buf[..4] == EOCDR64_LOCATOR_SIGNATURE { |
| 111 | if eocdr64.cd_pointer + eocdr64.cd_size > eocdr64locator.eocdr64_pointer { | 108 | let locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap(); |
| 112 | return Err(ZipError::Overlapping( | 109 | io.seek(SeekFrom::Start(locator.eocdr64_pointer))?; |
| 113 | "Central directory records", | ||
| 114 | "Zip64 end of central directory record", | ||
| 115 | )); | ||
| 116 | } | ||
| 117 | 110 | ||
| 118 | (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records) | 111 | if io.read_arr()? != EOCDR64_SIGNATURE { |
| 119 | } else { | 112 | return Err(ZipError::InvalidSignature("Eocdr64")); |
| 120 | if (eocdr.cd_pointer + eocdr.cd_size) as u64 > pos { | 113 | } |
| 121 | return Err(ZipError::Overlapping( | 114 | |
| 122 | "Central directory records", | 115 | if locator.eocdr64_pointer + 76 > eocdr_pos { |
| 123 | "End of central directory record", | 116 | return Err(ZipError::Overlapping("Eocdr64", "Eocdr64Locator")); |
| 124 | )); | 117 | } |
| 125 | } | ||
| 126 | 118 | ||
| 127 | ( | 119 | let eocdr64: Eocdr64 = deserialize(&io.read_arr::<54>()?).unwrap(); |
| 128 | eocdr.cd_pointer as u64, | 120 | if locator.eocdr64_pointer + eocdr64.eocdr64_size + 32 > eocdr_pos { |
| 129 | eocdr.cd_size as u64, | 121 | return Err(ZipError::Overlapping("Eocdr64", "Eocdr64Locator")); |
| 130 | eocdr.cd_records as u64, | 122 | } |
| 131 | ) | 123 | |
| 132 | }; | 124 | cd_pointer = eocdr64.cd_pointer; |
| 125 | cd_size = eocdr64.cd_size; | ||
| 126 | cd_records = eocdr64.cd_records; | ||
| 127 | |||
| 128 | if cd_pointer + cd_size > locator.eocdr64_pointer { | ||
| 129 | return Err(ZipError::Overlapping("Cdr", "Eocdr64")); | ||
| 130 | } | ||
| 131 | } else if cd_pointer + cd_size > eocdr_pos { | ||
| 132 | return Err(ZipError::Overlapping("Cdr", "Eocdr")); | ||
| 133 | } | ||
| 134 | } else if cd_pointer + cd_size > eocdr_pos { | ||
| 135 | return Err(ZipError::Overlapping("Cdr", "Eocdr")); | ||
| 136 | } | ||
| 133 | 137 | ||
| 134 | // Read cd records | 138 | // Read cd records |
| 135 | let mut indexes = Map::with_capacity(cd_records as usize); | 139 | let mut indexes = Map::with_capacity(cd_records as usize); |
diff --git a/src/zip/error.rs b/src/zip/error.rs index fbc2ba1..40d45fe 100644 --- a/src/zip/error.rs +++ b/src/zip/error.rs | |||
| @@ -31,10 +31,10 @@ impl From<IoError> for ZipError { | |||
| 31 | impl PartialEq for ZipError { | 31 | impl PartialEq for ZipError { |
| 32 | fn eq(&self, other: &Self) -> bool { | 32 | fn eq(&self, other: &Self) -> bool { |
| 33 | match (self, other) { | 33 | match (self, other) { |
| 34 | (Self::Io(l0), Self::Io(r0)) => l0.kind() == r0.kind(), | 34 | (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(), |
| 35 | (Self::StructNotFound(l0), Self::StructNotFound(r0)) => l0 == r0, | 35 | (Self::StructNotFound(l), Self::StructNotFound(r)) => l == r, |
| 36 | (Self::InvalidSignature(l0), Self::InvalidSignature(r0)) => l0 == r0, | 36 | (Self::InvalidSignature(l), Self::InvalidSignature(r)) => l == r, |
| 37 | (Self::InvalidField(l0), Self::InvalidField(r0)) => l0 == r0, | 37 | (Self::InvalidField(l), Self::InvalidField(r)) => l == r, |
| 38 | (Self::Overlapping(l0, l1), Self::Overlapping(r0, r1)) => l0 == r0 && l1 == r1, | 38 | (Self::Overlapping(l0, l1), Self::Overlapping(r0, r1)) => l0 == r0 && l1 == r1, |
| 39 | _ => core::mem::discriminant(self) == core::mem::discriminant(other), | 39 | _ => core::mem::discriminant(self) == core::mem::discriminant(other), |
| 40 | } | 40 | } |
diff --git a/tests/zip.rs b/tests/zip.rs index 1422bb3..e00789d 100644 --- a/tests/zip.rs +++ b/tests/zip.rs | |||
| @@ -90,11 +90,17 @@ fn test_zip_weak() { | |||
| 90 | } | 90 | } |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | const EMPTY: Cursor<&[u8]> = Cursor::new(b"PK\x05\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); | ||
| 94 | |||
| 95 | #[test] | 93 | #[test] |
| 96 | fn test_zip() { | 94 | fn test_zip() { |
| 97 | assert_eq!(Archive::<Zip<_>>::read(EMPTY).unwrap().len(), 0); | 95 | assert_eq!( |
| 96 | Archive::<Zip<_>>::read(Cursor::new(&[ | ||
| 97 | 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, // | ||
| 98 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr | ||
| 99 | ])) | ||
| 100 | .unwrap() | ||
| 101 | .len(), | ||
| 102 | 0 | ||
| 103 | ); | ||
| 98 | 104 | ||
| 99 | let mut archive = Archive::<Zip>::read_from_file("tests/files/archive.zip").unwrap(); | 105 | let mut archive = Archive::<Zip>::read_from_file("tests/files/archive.zip").unwrap(); |
| 100 | 106 | ||
| @@ -158,32 +164,49 @@ fn test_zip() { | |||
| 158 | } | 164 | } |
| 159 | } | 165 | } |
| 160 | 166 | ||
| 161 | const NOT_FOUND: Cursor<&[u8]> = Cursor::new(b""); | ||
| 162 | const INVALID: Cursor<&[u8]> = Cursor::new( | ||
| 163 | b"PK\x06\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PK\x05\x06\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0", | ||
| 164 | ); | ||
| 165 | const OVERLAP: Cursor<&[u8]> = Cursor::new(b"PK\x05\x06\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0"); | ||
| 166 | const OVERLAP64: Cursor<&[u8]> = Cursor::new(b"PK\x06\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PK\x06\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PK\x05\x06\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0"); | ||
| 167 | |||
| 168 | #[test] | 167 | #[test] |
| 169 | fn test_bad_zip() { | 168 | fn test_bad_zip() { |
| 170 | assert!( | 169 | assert!(Archive::<Zip<_>>::read(Cursor::new(&[])) |
| 171 | Archive::<Zip<_>>::read(NOT_FOUND).is_err_and(|e| e == ZipError::StructNotFound("Eocdr")) | 170 | .is_err_and(|e| e == ZipError::StructNotFound("Eocdr"))); |
| 172 | ); | 171 | |
| 173 | 172 | assert!(Archive::<Zip<_>>::read(Cursor::new(&[ | |
| 174 | assert!( | 173 | // No Eocdr64 |
| 175 | Archive::<Zip<_>>::read(INVALID).is_err_and(|e| e == ZipError::InvalidSignature("Eocdr64")) | 174 | 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0, // |
| 176 | ); | 175 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64Locator |
| 177 | 176 | // | |
| 178 | assert!(Archive::<Zip<_>>::read(OVERLAP).is_err_and(|e| e | 177 | 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, // |
| 179 | == ZipError::Overlapping( | 178 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr |
| 180 | "Central directory records", | 179 | ])) |
| 181 | "End of central directory record" | 180 | .is_err_and(|e| e == ZipError::InvalidSignature("Eocdr64"))); |
| 182 | ))); | 181 | |
| 183 | 182 | assert!(Archive::<Zip<_>>::read(Cursor::new(&[ | |
| 184 | assert!(Archive::<Zip<_>>::read(OVERLAP64).is_err_and(|e| e | 183 | 0x50, 0x4b, 0x06, 0x06, // Eocdr64 |
| 185 | == ZipError::Overlapping( | 184 | // |
| 186 | "Central directory records", | 185 | 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0, // |
| 187 | "Zip64 end of central directory record" | 186 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64Locator |
| 188 | ))); | 187 | // |
| 188 | 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, // | ||
| 189 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr | ||
| 190 | ])) | ||
| 191 | .is_err_and(|e| e == ZipError::Overlapping("Eocdr64", "Eocdr64Locator"))); | ||
| 192 | |||
| 193 | assert!(Archive::<Zip<_>>::read(Cursor::new(&[ | ||
| 194 | // No records | ||
| 195 | 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, // | ||
| 196 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr | ||
| 197 | ])) | ||
| 198 | .is_err_and(|e| e == ZipError::Overlapping("Cdr", "Eocdr"))); | ||
| 199 | |||
| 200 | assert!(Archive::<Zip<_>>::read(Cursor::new(&[ | ||
| 201 | 0x50, 0x4b, 0x06, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // | ||
| 202 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // | ||
| 203 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64 | ||
| 204 | // | ||
| 205 | 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0, // | ||
| 206 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64Locator | ||
| 207 | // | ||
| 208 | 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, // | ||
| 209 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr | ||
| 210 | ])) | ||
| 211 | .is_err_and(|e| e == ZipError::Overlapping("Cdr", "Eocdr64"))); | ||
| 189 | } | 212 | } |
