use super::stored_entry::StoredEntry;
fn parse_ut_extra(extra: &[u8]) -> Option<u64> {
let mut i = 0;
while i + 4 <= extra.len() {
let tag = u16::from_le_bytes(extra[i..i + 2].try_into().unwrap());
let data_size = u16::from_le_bytes(extra[i + 2..i + 4].try_into().unwrap()) as usize;
if i + 4 + data_size > extra.len() {
break;
}
if tag == 0x5455 && data_size >= 5 {
let flags = extra[i + 4];
if flags & 1 != 0 {
let secs = u32::from_le_bytes(extra[i + 5..i + 9].try_into().unwrap()) as u64;
return Some(secs);
}
}
i += 4 + data_size;
}
None
}
fn parse_ux_extra(extra: &[u8]) -> Option<(u32, u32)> {
let mut i = 0;
while i + 4 <= extra.len() {
let tag = u16::from_le_bytes(extra[i..i + 2].try_into().unwrap());
let data_size = u16::from_le_bytes(extra[i + 2..i + 4].try_into().unwrap()) as usize;
if i + 4 + data_size > extra.len() {
break;
}
if tag == 0x7875 {
let ver = extra[i + 4];
if ver == 1 && data_size >= 5 {
let uid_size = extra[i + 5] as usize;
let uid = if uid_size >= 4 && i + 9 + 4 <= extra.len() {
u32::from_le_bytes(extra[i + 6..i + 10].try_into().unwrap())
} else if uid_size > 0 && i + 6 + uid_size <= extra.len() {
let mut buf = [0u8; 4];
buf[..uid_size.min(4)].copy_from_slice(&extra[i + 6..i + 6 + uid_size.min(4)]);
u32::from_le_bytes(buf)
} else {
return None;
};
let gid_offset = i + 6 + uid_size;
let gid = if gid_offset < extra.len() {
let gid_size = extra[gid_offset] as usize;
if gid_size >= 4 && gid_offset + 1 + 4 <= extra.len() {
u32::from_le_bytes(
extra[gid_offset + 1..gid_offset + 5].try_into().unwrap(),
)
} else if gid_size > 0 && gid_offset + 1 + gid_size <= extra.len() {
let mut buf = [0u8; 4];
buf[..gid_size.min(4)].copy_from_slice(
&extra[gid_offset + 1..gid_offset + 1 + gid_size.min(4)],
);
u32::from_le_bytes(buf)
} else {
return None;
}
} else {
return None;
};
return Some((uid, gid));
}
}
i += 4 + data_size;
}
None
}
pub(crate) fn lookup_entry(buf: &[u8], index: usize) -> StoredEntry {
let sig = b"PK\x01\x02";
let pos: Vec<usize> = buf
.windows(4)
.enumerate()
.filter(|(_, w)| w == sig)
.map(|(i, _)| i)
.collect();
let pos = *pos.get(index).expect("entry not found");
let cd = &buf[pos..];
let method = u16::from_le_bytes(cd[10..12].try_into().unwrap());
let crc32 = u32::from_le_bytes(cd[16..20].try_into().unwrap());
let compressed_size = u32::from_le_bytes(cd[20..24].try_into().unwrap()) as u64;
let uncompressed_size = u32::from_le_bytes(cd[24..28].try_into().unwrap()) as u64;
let name_len = u16::from_le_bytes(cd[28..30].try_into().unwrap()) as usize;
let extra_len = u16::from_le_bytes(cd[30..32].try_into().unwrap()) as usize;
let external_file_attributes = u32::from_le_bytes(cd[38..42].try_into().unwrap());
let local_header_offset = u32::from_le_bytes(cd[42..46].try_into().unwrap()) as u64;
let name = String::from_utf8_lossy(&cd[46..46 + name_len]).to_string();
let file_type = (external_file_attributes >> 16) & 0o170000;
let is_directory = file_type == 0o040000;
let is_symlink = file_type == 0o120000;
let unix_permissions = if external_file_attributes != 0 {
Some((external_file_attributes >> 16) & 0o7777)
} else {
None
};
let is_stored = method == 0;
let extra_start = 46 + name_len;
let extra = &buf[pos + extra_start..pos + extra_start + extra_len];
let unix_mtime = parse_ut_extra(extra);
let uid_gid = parse_ux_extra(extra);
let mtime = (0, 0);
let unix_mtime = unix_mtime.unwrap_or(0);
StoredEntry {
name,
crc32,
compressed_size,
uncompressed_size,
local_header_offset,
is_directory,
is_symlink,
is_stored,
mtime,
unix_mtime,
unix_permissions,
uid_gid,
comment: None,
}
}