diff options
Diffstat (limited to 'src/zip/driver.rs')
| -rw-r--r-- | src/zip/driver.rs | 179 |
1 files changed, 89 insertions, 90 deletions
diff --git a/src/zip/driver.rs b/src/zip/driver.rs index e7878cf..8dc902f 100644 --- a/src/zip/driver.rs +++ b/src/zip/driver.rs | |||
| @@ -1,48 +1,51 @@ | |||
| 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; | 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; |
| 2 | use crate::utils::ReadUtils; | 2 | use crate::utils::ReadUtils; |
| 3 | use crate::zip::cp437::FromCp437; | 3 | use crate::zip::cp437::FromCp437; |
| 4 | use crate::zip::structs::{deserialize, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader}; | 4 | use crate::zip::datetime::DosDateTime; |
| 5 | use crate::zip::structs::{ | ||
| 6 | deserialize, AesField, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader, CDR_SIGNATURE, | ||
| 7 | EOCDR64_LOCATOR_SIGNATURE, EOCDR64_SIGNATURE, EOCDR_SIGNATURE, | ||
| 8 | }; | ||
| 5 | use crate::zip::{ | 9 | use crate::zip::{ |
| 6 | BitFlag, CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipFileReader, | 10 | BitFlag, CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipFileReader, |
| 7 | ZipFileWriter, ZipResult, | 11 | ZipFileWriter, ZipResult, |
| 8 | }; | 12 | }; |
| 9 | use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; | 13 | use chrono::{DateTime, Local}; |
| 10 | use std::collections::HashMap as Map; | 14 | use std::collections::HashMap as Map; |
| 11 | use std::fs::File; | 15 | use std::fs::File; |
| 12 | use std::io::{BufReader, Read, Seek, SeekFrom, Write}; | 16 | use std::io::{BufReader, Read, Seek, SeekFrom, Write}; |
| 13 | 17 | ||
| 14 | fn dos_to_local(date: u16, time: u16) -> ZipResult<DateTime<Local>> { | 18 | #[inline] |
| 15 | Ok(NaiveDateTime::new( | 19 | fn split_fields(bytes: &[u8]) -> Option<Vec<(u16, &[u8])>> { |
| 16 | NaiveDate::from_ymd_opt( | 20 | let mut fields = Vec::new(); |
| 17 | (date as i32 >> 9 & 0x7F) + 1980, | 21 | |
| 18 | date as u32 >> 5 & 0xF, | 22 | let mut p = 0; |
| 19 | date as u32 & 0x1F, | 23 | while p < bytes.len() { |
| 20 | ) | 24 | let header: ExtraHeader = deserialize(bytes.get(p..p + 4)?).unwrap(); |
| 21 | .ok_or(ZipError::InvalidDate)?, | 25 | p += 4; |
| 22 | NaiveTime::from_hms_opt( | 26 | let data = bytes.get(p..p + header.size as usize)?; |
| 23 | (time as u32 >> 11) & 0x1F, | 27 | p += header.size as usize; |
| 24 | (time as u32 >> 5) & 0x3F, | 28 | |
| 25 | (time as u32 & 0x1F) * 2, | 29 | fields.push((header.id, data)); |
| 26 | ) | 30 | } |
| 27 | .ok_or(ZipError::InvalidTime)?, | 31 | |
| 28 | ) | 32 | Some(fields) |
| 29 | .and_local_timezone(Local) | ||
| 30 | .unwrap()) | ||
| 31 | } | 33 | } |
| 32 | 34 | ||
| 33 | fn ntfs_to_local(time: u64) -> ZipResult<DateTime<Local>> { | 35 | #[inline] |
| 34 | Ok(DateTime::from_timestamp( | 36 | fn ntfs_to_local(time: u64) -> Option<DateTime<Local>> { |
| 35 | (time / 10000000 - 11644473600) as i64, | 37 | Some( |
| 36 | (time % 10000000) as u32, | 38 | DateTime::from_timestamp( |
| 39 | (time / 10000000 - 11644473600) as i64, | ||
| 40 | (time % 10000000) as u32, | ||
| 41 | )? | ||
| 42 | .with_timezone(&Local), | ||
| 37 | ) | 43 | ) |
| 38 | .ok_or(ZipError::InvalidTime)? | ||
| 39 | .with_timezone(&Local)) | ||
| 40 | } | 44 | } |
| 41 | 45 | ||
| 42 | fn timestamp_to_local(time: i32) -> ZipResult<DateTime<Local>> { | 46 | #[inline] |
| 43 | Ok(DateTime::from_timestamp(time as i64, 0) | 47 | fn timestamp_to_local(time: i32) -> Option<DateTime<Local>> { |
| 44 | .ok_or(ZipError::InvalidTime)? | 48 | Some(DateTime::from_timestamp(time as i64, 0)?.with_timezone(&Local)) |
| 45 | .with_timezone(&Local)) | ||
| 46 | } | 49 | } |
| 47 | 50 | ||
| 48 | pub struct Zip<Io = File> { | 51 | pub struct Zip<Io = File> { |
| @@ -74,7 +77,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 74 | .ok_or(ZipError::EocdrNotFound)?, | 77 | .ok_or(ZipError::EocdrNotFound)?, |
| 75 | )? | 78 | )? |
| 76 | .windows(4) | 79 | .windows(4) |
| 77 | .rposition(|v| u32::from_le_bytes(v.try_into().unwrap()) == 0x06054b50) | 80 | .rposition(|v| v == EOCDR_SIGNATURE) |
| 78 | .ok_or(ZipError::EocdrNotFound)? as u64; | 81 | .ok_or(ZipError::EocdrNotFound)? as u64; |
| 79 | 82 | ||
| 80 | // Read eocdr | 83 | // Read eocdr |
| @@ -88,13 +91,13 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 88 | let buf = io.read_arr::<20>()?; | 91 | let buf = io.read_arr::<20>()?; |
| 89 | let (cd_pointer, cd_size, cd_records) = | 92 | let (cd_pointer, cd_size, cd_records) = |
| 90 | // If locator found then read eocdr64 | 93 | // If locator found then read eocdr64 |
| 91 | if u32::from_le_bytes(buf[0..4].try_into().unwrap()) == 0x07064b50 { | 94 | if buf[0..4] == EOCDR64_LOCATOR_SIGNATURE { |
| 92 | let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap(); | 95 | let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap(); |
| 93 | 96 | ||
| 94 | io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; | 97 | io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; |
| 95 | let buf = io.read_arr::<56>()?; | 98 | let buf = io.read_arr::<56>()?; |
| 96 | if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 { | 99 | if buf[0..4] != EOCDR64_SIGNATURE { |
| 97 | return Err(ZipError::InvalidEOCDR64Signature.into()); | 100 | return Err(ZipError::InvalidEOCDR64Signature); |
| 98 | } | 101 | } |
| 99 | let eocdr64: Eocdr64 = deserialize(&buf[4..]).unwrap(); | 102 | let eocdr64: Eocdr64 = deserialize(&buf[4..]).unwrap(); |
| 100 | 103 | ||
| @@ -116,8 +119,8 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 116 | for i in 0..cd_records as usize { | 119 | for i in 0..cd_records as usize { |
| 117 | let buf = buf_reader.read_arr::<46>()?; | 120 | let buf = buf_reader.read_arr::<46>()?; |
| 118 | 121 | ||
| 119 | if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x02014b50 { | 122 | if buf[..4] != CDR_SIGNATURE { |
| 120 | return Err(ZipError::InvalidCDRSignature.into()); | 123 | return Err(ZipError::InvalidCDRSignature); |
| 121 | } | 124 | } |
| 122 | let cdr: Cdr = deserialize(&buf[4..46]).unwrap(); | 125 | let cdr: Cdr = deserialize(&buf[4..46]).unwrap(); |
| 123 | let bit_flag = BitFlag::new(cdr.bit_flag); | 126 | let bit_flag = BitFlag::new(cdr.bit_flag); |
| @@ -138,91 +141,87 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 138 | String::from_cp437(comment) | 141 | String::from_cp437(comment) |
| 139 | }; | 142 | }; |
| 140 | 143 | ||
| 144 | let mut compression_method = cdr.compression_method; | ||
| 145 | let mut encryption_method = if !bit_flag.is_encrypted() { | ||
| 146 | EncryptionMethod::None | ||
| 147 | } else if !bit_flag.is_strong_encryption() { | ||
| 148 | EncryptionMethod::Weak | ||
| 149 | } else { | ||
| 150 | EncryptionMethod::Unsupported | ||
| 151 | }; | ||
| 152 | |||
| 141 | let mut compressed_size = cdr.compressed_size as u64; | 153 | let mut compressed_size = cdr.compressed_size as u64; |
| 142 | let mut size = cdr.size as u64; | 154 | let mut size = cdr.size as u64; |
| 143 | let mut header_pointer = cdr.header_pointer as u64; | 155 | let mut header_pointer = cdr.header_pointer as u64; |
| 144 | 156 | ||
| 145 | let mut mtime = dos_to_local(cdr.dos_date, cdr.dos_time)?; | 157 | let mut mtime = DateTime::from_dos_date_time(cdr.dos_date, cdr.dos_time, Local)?; |
| 146 | let mut atime = None; | 158 | let mut atime = None; |
| 147 | let mut ctime = None; | 159 | let mut ctime = None; |
| 148 | 160 | ||
| 149 | // Parse extensible data fields | 161 | // Parse extensible data fields |
| 150 | let mut ep: usize = 0; | 162 | for (id, mut data) in split_fields(&extra_fields).ok_or(ZipError::InvalidExtraFields)? { |
| 151 | while ep < cdr.extra_field_len as usize { | 163 | match id { |
| 152 | let header: ExtraHeader = deserialize(&extra_fields[ep..ep + 4]).unwrap(); | ||
| 153 | ep += 4; | ||
| 154 | |||
| 155 | match header.id { | ||
| 156 | // Zip64 | 164 | // Zip64 |
| 157 | 0x0001 => { | 165 | 0x0001 => { |
| 158 | if size == 0xFFFFFFFF { | 166 | if size == 0xFFFFFFFF { |
| 159 | compressed_size = | 167 | size = u64::from_le_bytes(data.read_arr()?); |
| 160 | u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap()); | ||
| 161 | ep += 8; | ||
| 162 | } | 168 | } |
| 163 | if compressed_size == 0xFFFFFFFF { | 169 | if compressed_size == 0xFFFFFFFF { |
| 164 | size = u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap()); | 170 | compressed_size = u64::from_le_bytes(data.read_arr()?); |
| 165 | ep += 8; | ||
| 166 | } | 171 | } |
| 167 | if header_pointer == 0xFFFFFFFF { | 172 | if header_pointer == 0xFFFFFFFF { |
| 168 | header_pointer = | 173 | header_pointer = u64::from_le_bytes(data.read_arr()?); |
| 169 | u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap()); | ||
| 170 | ep += 8; | ||
| 171 | } | ||
| 172 | if cdr.disk == 0xFFFF { | ||
| 173 | ep += 4 | ||
| 174 | } | 174 | } |
| 175 | } | 175 | } |
| 176 | // NTFS | 176 | // NTFS |
| 177 | 0x000a => { | 177 | 0x000a => { |
| 178 | let mut tp = ep + 4; | 178 | for (id, mut data) in |
| 179 | ep += header.size as usize; | 179 | split_fields(&data).ok_or(ZipError::InvalidExtraFields)? |
| 180 | 180 | { | |
| 181 | while tp < ep { | 181 | match id { |
| 182 | let header: ExtraHeader = | ||
| 183 | deserialize(&extra_fields[tp..tp + 4]).unwrap(); | ||
| 184 | tp += 4; | ||
| 185 | |||
| 186 | match header.id { | ||
| 187 | 0x0001 => { | 182 | 0x0001 => { |
| 188 | mtime = ntfs_to_local(u64::from_le_bytes( | 183 | mtime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?)) |
| 189 | extra_fields[tp..tp + 8].try_into().unwrap(), | 184 | .unwrap_or(mtime); |
| 190 | ))?; | 185 | atime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?)); |
| 191 | tp += 8; | 186 | ctime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?)); |
| 192 | atime = Some(ntfs_to_local(u64::from_le_bytes( | ||
| 193 | extra_fields[tp..tp + 8].try_into().unwrap(), | ||
| 194 | ))?); | ||
| 195 | tp += 8; | ||
| 196 | ctime = Some(ntfs_to_local(u64::from_le_bytes( | ||
| 197 | extra_fields[tp..tp + 8].try_into().unwrap(), | ||
| 198 | ))?); | ||
| 199 | tp += 8; | ||
| 200 | } | ||
| 201 | _ => { | ||
| 202 | tp += header.size as usize; | ||
| 203 | } | 187 | } |
| 188 | _ => {} | ||
| 204 | } | 189 | } |
| 205 | } | 190 | } |
| 206 | } | 191 | } |
| 207 | // Unix | 192 | // Unix |
| 208 | 0x000d => { | 193 | 0x000d => { |
| 209 | atime = Some(timestamp_to_local(i32::from_le_bytes( | 194 | atime = timestamp_to_local(i32::from_le_bytes(data.read_arr()?)); |
| 210 | extra_fields[ep..ep + 4].try_into().unwrap(), | 195 | mtime = timestamp_to_local(i32::from_le_bytes(data.read_arr()?)) |
| 211 | ))?); | 196 | .unwrap_or(mtime); |
| 212 | mtime = timestamp_to_local(i32::from_le_bytes( | 197 | } |
| 213 | extra_fields[ep + 4..ep + 8].try_into().unwrap(), | 198 | // AES |
| 214 | ))?; | 199 | 0x9901 => { |
| 215 | ep += header.size as usize | 200 | let aes: AesField = deserialize(&data.read_arr::<7>()?).unwrap(); |
| 201 | if aes.id != 0x4541 { | ||
| 202 | return Err(ZipError::InvalidExtraFields); | ||
| 203 | } | ||
| 204 | encryption_method = match aes.strength { | ||
| 205 | 0x01 => EncryptionMethod::Aes128, | ||
| 206 | 0x02 => EncryptionMethod::Aes192, | ||
| 207 | 0x03 => EncryptionMethod::Aes256, | ||
| 208 | _ => EncryptionMethod::Unsupported, | ||
| 209 | }; | ||
| 210 | compression_method = aes.compression_method; | ||
| 216 | } | 211 | } |
| 217 | // Skip unrecognized header | 212 | // Skip unrecognized header |
| 218 | _ => ep += header.size as usize, | 213 | _ => {} |
| 219 | } | 214 | } |
| 220 | } | 215 | } |
| 221 | 216 | ||
| 217 | if compression_method == 99 { | ||
| 218 | return Err(ZipError::AesExtraFieldNotFound); | ||
| 219 | } | ||
| 220 | |||
| 222 | indexes.insert(name.clone(), i); | 221 | indexes.insert(name.clone(), i); |
| 223 | files.push(ZipFileInfo::new( | 222 | files.push(ZipFileInfo::new( |
| 224 | CompressionMethod::from_struct_id(cdr.compression_method)?, | 223 | CompressionMethod::from_struct_id(compression_method), |
| 225 | EncryptionMethod::from_bif_flag(bit_flag, cdr.crc, cdr.dos_time), | 224 | encryption_method, |
| 226 | bit_flag, | 225 | bit_flag, |
| 227 | mtime, | 226 | mtime, |
| 228 | atime, | 227 | atime, |
| @@ -248,15 +247,15 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 248 | &self.files | 247 | &self.files |
| 249 | } | 248 | } |
| 250 | 249 | ||
| 251 | fn get_file_index(&self, name: &str) -> crate::ArchiveResult<usize, Self::Error> { | 250 | fn get_file_index(&self, name: &str) -> ZipResult<usize> { |
| 252 | self.indexes | 251 | self.indexes |
| 253 | .get(name) | 252 | .get(name) |
| 254 | .ok_or(ZipError::FileNotFound.into()) | 253 | .ok_or(ZipError::FileNotFound) |
| 255 | .copied() | 254 | .copied() |
| 256 | } | 255 | } |
| 257 | 256 | ||
| 258 | fn get_file_info(&self, index: usize) -> ZipResult<&Self::FileInfo> { | 257 | fn get_file_info(&self, index: usize) -> ZipResult<&Self::FileInfo> { |
| 259 | self.files.get(index).ok_or(ZipError::FileNotFound.into()) | 258 | self.files.get(index).ok_or(ZipError::FileNotFound) |
| 260 | } | 259 | } |
| 261 | 260 | ||
| 262 | #[inline] | 261 | #[inline] |
