use super::fileio::{self, PInfo, readbuf, seek_zipf};
use super::matching::{iswild, matches};
use super::messages as m;
use super::{
IZ_DIR, PK_BADERR, PK_COOL, PK_EOF, PK_ERR, PK_FIND, PK_NOZIP, PK_OK, PK_WARN, Uz, flavour,
list, unix,
};
use crate::compat::host;
pub(crate) const CENTRAL_HDR_SIG: [u8; 4] = *b"PK\x01\x02";
pub(crate) const LOCAL_HDR_SIG: [u8; 4] = *b"PK\x03\x04";
pub(crate) const END_CENTRAL_SIG: [u8; 4] = *b"PK\x05\x06";
pub(crate) const END_CENTLOC64_SIG: [u8; 4] = *b"PK\x06\x07";
pub(crate) const END_CENTRAL64_SIG: [u8; 4] = *b"PK\x06\x06";
const LREC_SIZE: usize = 26;
const CREC_SIZE: usize = 42;
const ECREC_SIZE: usize = 18;
const ECLOC64_SIZE: usize = 16;
const ECREC64_SIZE: usize = 52;
const ECREC64_LENGTH: usize = 4;
const NUM_HOSTS: u8 = 31;
const VMS_: u8 = 2;
const VM_CMS_: u8 = 4;
const ATARI_: u8 = 5;
const CPM_: u8 = 9;
const TOPS20_: u8 = 10;
const MVS_: u8 = 15;
const TANDEM_: u8 = 17;
const EB_HEADSIZE: usize = 4;
const EF_PKSZ64: u16 = 0x0001;
const EF_PKUNIX: u16 = 0x000d;
const EF_IZUNIX: u16 = 0x5855;
const EF_IZUNIX2: u16 = 0x7855;
const EF_IZUNIX3: u16 = 0x7875;
const EF_TIME: u16 = 0x5455;
const EB_UX_MINLEN: usize = 8;
const EB_UX_FULLSIZE: usize = 12;
const EB_UX_UID: usize = 8;
const EB_UX_GID: usize = 10;
const EB_UX2_MINLEN: usize = 4;
const EB_UX3_MINLEN: usize = 7;
const EB_UX_ATIME: usize = 0;
const EB_UX_MTIME: usize = 4;
const EB_UT_MINLEN: usize = 1;
const EB_UT_FLAGS: usize = 0;
const EB_UT_TIME1: usize = 1;
pub(crate) const EB_UT_FL_MTIME: u32 = 1 << 0;
pub(crate) const EB_UT_FL_ATIME: u32 = 1 << 1;
pub(crate) const EB_UT_FL_CTIME: u32 = 1 << 2;
const MASK_ZUCN16: u64 = 0xffff;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct Ecrec {
pub number_this_disk: u64,
pub num_disk_start_cdir: u64,
pub num_entries_centrl_dir_ths_disk: u64,
pub total_entries_central_dir: u64,
pub size_central_directory: u64,
pub offset_start_central_directory: u64,
pub zipfile_comment_length: u16,
pub have_ecr64: bool,
pub is_zip64_archive: bool,
pub ec_start: u64,
pub ec_end: u64,
pub ec64_start: u64,
pub ec64_end: u64,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct Lrec {
pub version_needed_to_extract: [u8; 2],
pub general_purpose_bit_flag: u16,
pub compression_method: u16,
pub last_mod_dos_datetime: u32,
pub crc32: u32,
pub csize: u64,
pub ucsize: u64,
pub filename_length: u16,
pub extra_field_length: u16,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct CentralEntry {
pub index: u64,
pub version_made_by: [u8; 2],
pub version_needed_to_extract: [u8; 2],
pub general_purpose_bit_flag: u16,
pub compression_method: u16,
pub last_mod_dos_datetime: u32,
pub crc32: u32,
pub csize: u64,
pub ucsize: u64,
pub filename_length: u16,
pub extra_field_length: u16,
pub file_comment_length: u16,
pub disk_number_start: u64,
pub internal_file_attributes: u16,
pub external_file_attributes: u64,
pub relative_offset_local_header: u64,
pub pinfo: PInfo,
pub filename: Vec<u8>,
pub extra: Option<Vec<u8>>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct IzTimes {
pub atime: i64,
pub mtime: i64,
pub ctime: i64,
}
pub(crate) fn process_zipfiles(uz: &mut Uz, parsed: &super::cli::Parsed) -> i32 {
uz.wildzipfn = super::cli::bytes(&parsed.archive);
uz.pfnames = parsed
.include
.iter()
.map(|a| super::cli::bytes(a))
.collect();
uz.pxnames = parsed
.exclude
.iter()
.map(|a| super::cli::bytes(a))
.collect();
uz.overwrite_mode = if uz.o.overwrite_none {
super::extract::OVERWRT_NEVER
} else if uz.o.overwrite_all != 0 {
super::extract::OVERWRT_ALWAYS
} else {
super::extract::OVERWRT_QUERY
};
let mut error = 0;
let mut error_in_archive = 0;
let mut lastzipfn: Option<Vec<u8>> = None;
let (mut num_win, mut num_lose, mut num_warn) = (0i32, 0i32, 0i32);
let (mut num_miss_dirs, mut num_miss_files) = (0i32, 0i32);
let wild = uz.wildzipfn.clone();
let mut w = unix::Wild::new();
while let Some(zipfn) = w.do_wild(uz, &wild) {
uz.zipfn = zipfn.clone();
lastzipfn = Some(zipfn);
if uz.o.qflag == 0
&& error != PK_NOZIP
&& error != IZ_DIR
&& (!uz.o.t_flag || uz.o.zipinfo_mode)
&& (num_win + num_lose + num_warn + num_miss_files) > 0
{
uz.info(0, b"\n");
}
error = do_seekable(uz, false);
if let Some(code) = uz.abort {
return code;
}
if error == PK_WARN {
num_warn += 1;
} else if error == IZ_DIR {
num_miss_dirs += 1;
} else if error == PK_NOZIP {
num_miss_files += 1;
} else if error != PK_OK {
num_lose += 1;
} else {
num_win += 1;
}
if error != IZ_DIR && error > error_in_archive {
error_in_archive = error;
}
}
if (num_win + num_warn + num_lose) == 0
&& (num_miss_dirs + num_miss_files) == 1
&& let Some(mut last) = lastzipfn
{
let p = last.len();
last.extend_from_slice(m::ZSUFX.as_bytes());
uz.zipfn = last;
num_miss_dirs = 0;
num_miss_files = 0;
error_in_archive = PK_COOL;
error = do_seekable(uz, false);
if error == PK_NOZIP || error == IZ_DIR {
if error == IZ_DIR {
num_miss_dirs += 1;
}
uz.zipfn.truncate(p);
uz.zipfn.extend_from_slice(m::ALT_ZSUFX.as_bytes());
error = do_seekable(uz, true);
}
match error {
PK_WARN => num_warn += 1,
IZ_DIR => {
num_miss_dirs += 1;
error = PK_NOZIP;
}
PK_NOZIP => {}
_ => {
if error != 0 {
num_lose += 1;
} else {
num_win += 1;
}
}
}
if error > error_in_archive {
error_in_archive = error;
}
}
let o = &uz.o;
if iswild(&uz.wildzipfn) && o.qflag < 3 && !(o.t_flag && !o.zipinfo_mode && o.qflag > 1) {
if (num_miss_files + num_lose + num_warn > 0 || num_win != 1)
&& !(o.t_flag && !o.zipinfo_mode && o.qflag != 0)
&& !(o.tflag && o.qflag > 1)
{
uz.info(0x401, b"\n");
}
if num_win > 1 || (num_win == 1 && num_miss_dirs + num_miss_files + num_lose + num_warn > 0)
{
let n = num_win.to_string();
let s: &[u8] = if num_win == 1 { b" was" } else { b"s were" };
uz.info_fmt(0x401, m::FILES_PROCESS_OK, &[n.as_bytes(), s]);
}
if num_warn > 0 {
let n = num_warn.to_string();
let s: &[u8] = if num_warn == 1 { b"" } else { b"s" };
uz.info_fmt(0x401, m::ARCHIVE_WARNING, &[n.as_bytes(), s]);
}
if num_lose > 0 {
let n = num_lose.to_string();
let s: &[u8] = if num_lose == 1 { b"" } else { b"s" };
uz.info_fmt(0x401, m::ARCHIVE_FATAL_ERROR, &[n.as_bytes(), s]);
}
if num_miss_files > 0 {
let n = num_miss_files.to_string();
let s: &[u8] = if num_miss_files == 1 { b"" } else { b"s" };
uz.info_fmt(0x401, m::FILE_HAD_NO_ZIPFILE_DIR, &[n.as_bytes(), s]);
}
if num_miss_dirs == 1 {
uz.info(0x401, m::ZIPFILE_WAS_DIR.as_bytes());
} else if num_miss_dirs > 0 {
let n = num_miss_dirs.to_string();
uz.info_fmt(0x401, m::MANY_ZIPFILES_WERE_DIR, &[n.as_bytes()]);
}
if num_win + num_lose + num_warn == 0 {
uz.info(0x401, m::NO_ZIPFILE_FOUND.as_bytes());
}
}
error_in_archive
}
fn who(uz: &Uz) -> &'static [u8] {
if uz.o.zipinfo_mode {
m::ZIPNFO.as_bytes()
} else {
m::UNZIP.as_bytes()
}
}
fn close_infile(uz: &mut Uz) {
uz.zin.fd = None;
}
fn open_input_file(uz: &mut Uz) -> i32 {
let path = uz.host.path(&uz.zipfn);
match std::fs::File::open(&path) {
Ok(f) => {
uz.qf = super::unix::attrs::archive_quarantine(&uz.host, &f);
uz.zin.fd = Some(uz.host.track(&path, f));
0
}
Err(e) => {
let zipfn = uz.zipfn.clone();
uz.info_fmt(
0x401,
m::CANNOT_OPEN_ZIPFILE,
&[&zipfn, &host::strerror(&e)],
);
1
}
}
}
fn do_seekable(uz: &mut Uz, lastchance: bool) -> i32 {
let mut too_weird_to_continue = false;
let mut uxstamp = 0i64;
let mut nmember = 0u64;
let path = uz.host.path(&uz.zipfn);
let stat = std::fs::metadata(&path);
let is_dir = stat.as_ref().is_ok_and(|md| md.is_dir());
let Some(meta) = stat.ok().filter(|md| !md.is_dir()) else {
if lastchance && uz.o.qflag < 3 {
let (wild, zipfn) = (uz.wildzipfn.clone(), uz.zipfn.clone());
if uz.no_ecrec {
let gap: &[u8] = if uz.o.zipinfo_mode { b" " } else { b"" };
uz.info_fmt(
1,
m::CANNOT_FIND_ZIPFILE_DIR_MSG,
&[who(uz), &wild, gap, &wild, &zipfn],
);
} else {
uz.info_fmt(
1,
m::CANNOT_FIND_EITHER_ZIPFILE,
&[who(uz), &wild, &wild, &zipfn],
);
}
}
return if is_dir { IZ_DIR } else { PK_NOZIP };
};
uz.zin.ziplen = meta.len() as i64;
#[cfg(unix)]
let maybe_exe = {
use std::os::unix::fs::PermissionsExt;
meta.permissions().mode() & 0o100 != 0
};
#[cfg(not(unix))]
let maybe_exe = false;
if open_input_file(uz) != 0 {
return PK_NOZIP;
}
uz.zin.cur_zipfile_bufstart = 0;
uz.zin.inptr = 0;
if (!uz.o.zipinfo_mode && uz.o.qflag == 0 && !uz.o.t_flag) || (uz.o.zipinfo_mode && uz.o.hflag)
{
let zipfn = uz.zipfn.clone();
uz.info_fmt(0, m::LOG_INITLINE, &[&zipfn]);
}
let searchlen = if uz.o.zipinfo_mode {
uz.zin.ziplen
} else {
uz.zin.ziplen.min(66000)
};
let mut error_in_archive = end::find_ecrec(uz, searchlen);
if uz.abort.is_some() {
close_infile(uz);
return PK_COOL;
}
if error_in_archive > PK_WARN {
close_infile(uz);
if maybe_exe {
let zipfn = uz.zipfn.clone();
uz.info_fmt(0x401, m::MAYBE_EXE, &[&zipfn]);
}
if lastchance {
return error_in_archive;
}
uz.no_ecrec = true; return PK_NOZIP; }
if uz.o.zflag > 0 && !uz.o.zipinfo_mode {
close_infile(uz);
return error_in_archive;
}
let ecrec = uz.ecrec;
let multi = !uz.o.zipinfo_mode && ecrec.number_this_disk != 0;
if uz.o.zipinfo_mode && ecrec.number_this_disk != ecrec.num_disk_start_cdir {
let zipfn = uz.zipfn.clone();
let (a, b) = (
ecrec.number_this_disk.to_string(),
ecrec.num_disk_start_cdir.to_string(),
);
if ecrec.number_this_disk > ecrec.num_disk_start_cdir {
uz.info_fmt(
0x401,
m::CENT_DIR_NOT_IN_ZIP_MSG,
&[&zipfn, a.as_bytes(), b.as_bytes()],
);
error_in_archive = PK_FIND;
too_weird_to_continue = true;
} else {
uz.info_fmt(
0x401,
m::END_CENT_DIR_BOGUS,
&[&zipfn, a.as_bytes(), b.as_bytes()],
);
error_in_archive = PK_WARN;
}
}
if !too_weird_to_continue {
if multi {
let zipfn = uz.zipfn.clone();
uz.info_fmt(0x401, m::MAYBE_PAK_BUG, &[&zipfn]);
error_in_archive = PK_WARN;
}
uz.zin.extra_bytes = uz.real_ecrec_offset.wrapping_sub(uz.expect_ecrec_offset);
if uz.zin.extra_bytes < 0 {
let zipfn = uz.zipfn.clone();
let n = uz.zin.extra_bytes.wrapping_neg().to_string();
uz.info_fmt(0x401, m::MISSING_BYTES, &[&zipfn, n.as_bytes()]);
error_in_archive = PK_ERR;
} else if uz.zin.extra_bytes > 0 {
if uz.ecrec.offset_start_central_directory == 0 && uz.ecrec.size_central_directory != 0
{
let zipfn = uz.zipfn.clone();
uz.info_fmt(0x401, m::NULL_CENT_DIR_OFFSET, &[&zipfn]);
uz.ecrec.offset_start_central_directory = uz.zin.extra_bytes as u64;
uz.zin.extra_bytes = 0;
error_in_archive = PK_ERR;
} else {
let zipfn = uz.zipfn.clone();
let n = uz.zin.extra_bytes.to_string();
let s: &[u8] = if uz.zin.extra_bytes == 1 { b"" } else { b"s" };
uz.info_fmt(0x401, m::EXTRA_BYTES_AT_START, &[&zipfn, n.as_bytes(), s]);
error_in_archive = PK_WARN;
}
}
if uz.expect_ecrec_offset == 0 && uz.ecrec.size_central_directory == 0 {
if uz.o.zipinfo_mode {
let pre: &[u8] = if uz.o.lflag > 9 { b"\n " } else { b"" };
uz.info_fmt(0, m::EMPTY_ZIPFILE_ZI, &[pre]);
} else {
let zipfn = uz.zipfn.clone();
uz.info_fmt(0x401, m::ZIPFILE_EMPTY, &[&zipfn]);
}
close_infile(uz);
return if error_in_archive > PK_WARN {
error_in_archive
} else {
PK_WARN
};
}
let start = uz.ecrec.offset_start_central_directory as i64;
let mut error = seek_zipf(uz, start);
if error == PK_BADERR {
close_infile(uz);
return PK_BADERR;
}
if error != PK_OK || read_sig(uz) == 0 || uz.sig != CENTRAL_HDR_SIG {
let tmp = uz.zin.extra_bytes;
uz.zin.extra_bytes = 0;
error = seek_zipf(uz, start);
if error != PK_OK || read_sig(uz) == 0 || uz.sig != CENTRAL_HDR_SIG {
if error != PK_BADERR {
let zipfn = uz.zipfn.clone();
uz.info_fmt(
0x401,
m::CENT_DIR_START_NOT_FOUND,
&[&zipfn, m::REPORT_MSG.as_bytes()],
);
}
close_infile(uz);
return if error != PK_OK { error } else { PK_BADERR };
}
let zipfn = uz.zipfn.clone();
let n = tmp.wrapping_neg().to_string();
uz.info_fmt(0x401, m::CENT_DIR_TOO_LONG, &[&zipfn, n.as_bytes()]);
error_in_archive = PK_ERR;
}
let error = seek_zipf(uz, start);
if error != PK_OK {
close_infile(uz);
return error;
}
let error = if uz.o.zipinfo_mode {
super::zipinfo::zipinfo(uz) } else if uz.o.t_flag {
list::get_time_stamp(uz, &mut uxstamp, &mut nmember)
} else if uz.o.vflag != 0 && !uz.o.tflag && !uz.o.cflag {
list::list_files(uz) } else {
super::extract::extract_or_test_files(uz) };
if uz.abort.is_some() {
return error;
}
if error > error_in_archive {
error_in_archive = error;
}
}
close_infile(uz);
if uz.o.t_flag && !uz.o.zipinfo_mode && nmember > 0 {
let zipfn = uz.zipfn.clone();
if unix::stamp_file(uz, &zipfn, uxstamp).is_err() {
if uz.o.qflag < 3 {
uz.info_fmt(0x201, m::ZIP_TIME_STAMP_FAILED, &[&zipfn]);
}
if error_in_archive < PK_WARN {
error_in_archive = PK_WARN;
}
} else if uz.o.qflag == 0 {
uz.info_fmt(0, m::ZIP_TIME_STAMP_SUCCESS, &[&zipfn]);
}
}
error_in_archive
}
pub(crate) fn read_sig(uz: &mut Uz) -> usize {
let mut sig = uz.sig;
let n = readbuf(uz, &mut sig);
uz.sig = sig;
n
}
pub(crate) fn process_cdir_file_hdr(uz: &mut Uz) -> Result<CentralEntry, i32> {
let mut e = get_cdir_ent(uz)?;
e.pinfo.hostver = e.version_made_by[0];
e.pinfo.hostnum = e.version_made_by[1].min(NUM_HOSTS);
e.pinfo.lcflag = false;
if uz.o.l_flag == 1 {
e.pinfo.lcflag = matches!(
e.pinfo.hostnum,
fileio::FS_FAT_ | CPM_ | VM_CMS_ | MVS_ | TANDEM_ | TOPS20_ | VMS_
);
} else if uz.o.l_flag > 1 {
e.pinfo.lcflag = true;
}
if e.internal_file_attributes & 0x0004 != 0 {
e.external_file_attributes &= 0xff;
}
if e.external_file_attributes & 0x08 != 0
&& matches!(
e.pinfo.hostnum,
fileio::FS_FAT_ | fileio::FS_HPFS_ | fileio::FS_NTFS_ | ATARI_
)
{
e.pinfo.vollabel = true;
e.pinfo.lcflag = false; } else {
e.pinfo.vollabel = false;
}
e.pinfo.has_ux_att = e.external_file_attributes & 0xffff_0000 != 0;
e.pinfo.gpf_is_utf8 = e.general_purpose_bit_flag & (1 << 11) == 1 << 11;
Ok(e)
}
fn get_cdir_ent(uz: &mut Uz) -> Result<CentralEntry, i32> {
let mut b = [0u8; CREC_SIZE];
if readbuf(uz, &mut b) == 0 {
return Err(PK_EOF);
}
let word = |at: usize| fileio::makeword(&b[at..]);
let long = |at: usize| fileio::makelong(&b[at..]);
Ok(CentralEntry {
version_made_by: [b[0], b[1]],
version_needed_to_extract: [b[2], b[3]],
general_purpose_bit_flag: word(4),
compression_method: word(6),
last_mod_dos_datetime: long(8),
crc32: long(12),
csize: u64::from(long(16)),
ucsize: u64::from(long(20)),
filename_length: word(24),
extra_field_length: word(26),
file_comment_length: word(28),
disk_number_start: u64::from(word(30)),
internal_file_attributes: word(32),
external_file_attributes: u64::from(long(34)), relative_offset_local_header: u64::from(long(38)),
..CentralEntry::default()
})
}
pub(crate) fn process_local_file_hdr(uz: &mut Uz, e: &CentralEntry) -> i32 {
let mut b = [0u8; LREC_SIZE];
if readbuf(uz, &mut b) == 0 {
return PK_EOF;
}
let word = |at: usize| fileio::makeword(&b[at..]);
let long = |at: usize| fileio::makelong(&b[at..]);
uz.lrec = Lrec {
version_needed_to_extract: [b[0], b[1]],
general_purpose_bit_flag: word(2),
compression_method: word(4),
last_mod_dos_datetime: long(6),
crc32: long(10),
csize: u64::from(long(14)),
ucsize: u64::from(long(18)),
filename_length: word(22),
extra_field_length: word(24),
};
if uz.lrec.general_purpose_bit_flag & 8 != 0 {
uz.lrec.crc32 = e.crc32;
uz.lrec.csize = e.csize;
uz.lrec.ucsize = e.ucsize;
}
uz.csize = uz.lrec.csize as i64;
PK_COOL
}
pub(crate) fn get_zip64_data(uz: &mut Uz, e: &mut CentralEntry, ef: &[u8]) -> i32 {
const Z64FLGS: u64 = 0xffff;
const Z64FLGL: u64 = 0xffff_ffff;
let f = uz.flavour;
uz.zip64 = false;
let mut ef = ef;
while ef.len() >= EB_HEADSIZE {
let eb_id = fileio::makeword(ef);
let eb_len = usize::from(fileio::makeword(&ef[2..]));
if eb_len > ef.len() - EB_HEADSIZE {
break;
}
if eb_id == EF_PKSZ64 {
let mut offset = EB_HEADSIZE;
let ef_len = ef.len();
if e.ucsize == Z64FLGL || uz.lrec.ucsize == Z64FLGL {
if offset + 8 > ef_len {
return PK_ERR;
}
let v = fileio::makeint64(f, &ef[offset..]);
e.ucsize = v;
uz.lrec.ucsize = v;
offset += 8;
}
if e.csize == Z64FLGL || uz.lrec.csize == Z64FLGL {
if offset + 8 > ef_len {
return PK_ERR;
}
let v = fileio::makeint64(f, &ef[offset..]);
e.csize = v;
uz.lrec.csize = v;
uz.csize = v as i64;
offset += 8;
}
if e.relative_offset_local_header == Z64FLGL {
if offset + 8 > ef_len {
return PK_ERR;
}
e.relative_offset_local_header = fileio::makeint64(f, &ef[offset..]);
offset += 8;
}
if e.disk_number_start == Z64FLGS {
if offset + 4 > ef_len {
return PK_ERR;
}
e.disk_number_start = u64::from(fileio::makelong(&ef[offset..]));
}
}
ef = &ef[eb_len + EB_HEADSIZE..];
}
PK_COOL
}
pub(crate) fn read_extra_field(uz: &mut Uz, e: &mut CentralEntry) -> i32 {
let length = usize::from(e.extra_field_length);
if length == 0 {
return PK_COOL;
}
let mut field = vec![0u8; length];
let bytes_read = readbuf(uz, &mut field);
if bytes_read == 0 {
e.extra = Some(field);
return PK_EOF;
}
if bytes_read != length {
e.extra = Some(field);
return PK_ERR;
}
let mut error = PK_OK;
if get_zip64_data(uz, e, &field) != PK_COOL {
uz.info_fmt(
0x401,
m::EXTRA_FIELD_CORRUPT,
&[m::EF_PKSZ64_HEX.as_bytes()],
);
error = PK_WARN;
}
if flavour::unicode_support(uz.flavour) {
let pinfo = e.pinfo;
let r = fileio::unicode_name(uz, &field, &pinfo);
if r != PK_COOL {
error = r;
}
}
e.extra = Some(field);
error
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum WalkEnd {
List,
TimeStamp,
}
pub(crate) fn walk_central(
uz: &mut Uz,
each: &mut dyn FnMut(&mut Uz, &CentralEntry) -> i32,
) -> i32 {
walk(uz, WalkEnd::List, each)
}
pub(crate) fn walk(
uz: &mut Uz,
end: WalkEnd,
each: &mut dyn FnMut(&mut Uz, &CentralEntry) -> i32,
) -> i32 {
let mut error_in_archive = PK_COOL;
let mut j: u64 = 1;
loop {
if read_sig(uz) == 0 {
return PK_EOF;
}
if uz.sig != CENTRAL_HDR_SIG {
let done = match end {
WalkEnd::List => {
let mask = if uz.ecrec.have_ecr64 {
u64::MAX
} else {
MASK_ZUCN16
};
((j - 1) & mask) == uz.ecrec.total_entries_central_dir
}
WalkEnd::TimeStamp => {
((j - 1) as u32 & 0xffff) == uz.ecrec.total_entries_central_dir as u32
}
};
if done {
break;
}
let n = j.to_string();
uz.info_fmt(0x401, m::CENT_SIG_MSG, &[n.as_bytes()]);
uz.info(0x401, m::REPORT_MSG.as_bytes());
return PK_BADERR; }
let mut e = match process_cdir_file_hdr(uz) {
Ok(e) => e,
Err(code) => return code, };
e.index = j;
let pinfo = e.pinfo;
let error = fileio::read_filename(uz, usize::from(e.filename_length), &pinfo, false);
if error != PK_COOL {
error_in_archive = error;
if error > PK_WARN {
return error;
}
}
e.filename = fileio::c_str(&uz.filename).to_vec();
let error = read_extra_field(uz, &mut e);
if error != 0 {
error_in_archive = error;
if error > PK_WARN {
return error;
}
}
if uz.flavour == super::Flavour::Ubuntu {
e.filename = fileio::c_str(&uz.filename).to_vec();
}
let error = each(uz, &e);
if error != 0 {
error_in_archive = error; if error > PK_WARN {
return error;
}
}
j = j.wrapping_add(1);
}
error_in_archive
}
pub(crate) fn selected(
uz: &Uz,
name: &[u8],
mut fn_matched: Option<&mut [bool]>,
mut xn_matched: Option<&mut [bool]>,
) -> bool {
if let Some(m) = fn_matched.as_deref() {
assert_eq!(m.len(), uz.pfnames.len(), "one fn_matched flag per pattern");
}
if let Some(m) = xn_matched.as_deref() {
assert_eq!(m.len(), uz.pxnames.len(), "one xn_matched flag per pattern");
}
if uz.process_all_files {
return true;
}
let ic = uz.o.c_flag;
let stop = flavour::wild_stop_at_dir(uz.flavour) && uz.o.w_flag;
let mut do_this_file = if uz.pfnames.is_empty() {
true
} else {
let mut hit = false;
for (i, p) in uz.pfnames.iter().enumerate() {
if matches(p, name, ic, stop) {
hit = true;
if let Some(m) = fn_matched.as_deref_mut() {
m[i] = true;
}
break; }
}
hit
};
if do_this_file {
for (i, p) in uz.pxnames.iter().enumerate() {
if matches(p, name, ic, stop) {
do_this_file = false; if let Some(m) = xn_matched.as_deref_mut() {
m[i] = true;
}
break;
}
}
}
do_this_file
}
pub(crate) fn ef_scan_for_izux(
f: flavour::Flavour,
ef: &[u8],
ef_is_c: bool,
dos_mdatetime: u32,
z_utim: Option<&mut IzTimes>,
) -> u32 {
ef_scan_for_izux_owner(f, ef, ef_is_c, dos_mdatetime, z_utim, None)
}
pub(crate) const EB_UX2_VALID: u32 = 1 << 8;
fn read_ux3_value(f: flavour::Flavour, b: &[u8], size: usize) -> Option<u64> {
match size {
2 => Some(u64::from(fileio::makeword(b))),
4 => Some(u64::from(fileio::makelong(b))),
8 => Some(fileio::makeint64(f, b)),
_ => Some(0),
}
}
pub(crate) fn ef_scan_for_izux_owner(
f: flavour::Flavour,
ef: &[u8],
ef_is_c: bool,
dos_mdatetime: u32,
z_utim: Option<&mut IzTimes>,
mut owner: Option<&mut [u64; 2]>,
) -> u32 {
let keeps_times = flavour::izunix_keeps_times(f);
let mut flags: u32 = 0;
let mut have_new_type_eb = 0;
let mut ut_zip_unzip_compatible = false;
let Some(z_utim) = z_utim else {
return 0;
};
if ef.is_empty() {
return 0;
}
let mut ef = ef;
while ef.len() >= EB_HEADSIZE {
let eb_id = fileio::makeword(ef);
let eb_len = usize::from(fileio::makeword(&ef[2..]));
if eb_len > ef.len() - EB_HEADSIZE {
break;
}
let data = &ef[EB_HEADSIZE..EB_HEADSIZE + eb_len];
let long_at = |at: usize| i64::from(fileio::makelong(&data[at..]));
match eb_id {
EF_TIME => 'ut: {
flags &= !0x0ff; have_new_type_eb = 1;
if eb_len >= EB_UT_MINLEN {
let mut eb_idx = EB_UT_TIME1;
flags |= u32::from(data[EB_UT_FLAGS]) & 0x0ff;
if flags & EB_UT_FL_MTIME != 0 {
if eb_idx + 4 <= eb_len {
let i_time = long_at(eb_idx);
eb_idx += 4;
if i_time & 0x8000_0000 != 0 {
ut_zip_unzip_compatible =
dos_mdatetime >= fileio::DOSTIME_2038_01_18;
if !ut_zip_unzip_compatible {
flags &= !0x0ff; break 'ut; }
} else {
ut_zip_unzip_compatible = false;
}
z_utim.mtime = i_time;
} else {
flags &= !EB_UT_FL_MTIME;
}
}
if ef_is_c {
break 'ut; }
if flags & EB_UT_FL_ATIME != 0 {
if eb_idx + 4 <= eb_len {
let i_time = long_at(eb_idx);
eb_idx += 4;
if i_time & 0x8000_0000 != 0 && !ut_zip_unzip_compatible {
flags &= !EB_UT_FL_ATIME;
} else {
z_utim.atime = i_time;
}
} else {
flags &= !EB_UT_FL_ATIME;
}
}
if flags & EB_UT_FL_CTIME != 0 {
if eb_idx + 4 <= eb_len {
let i_time = long_at(eb_idx);
if i_time & 0x8000_0000 != 0 && !ut_zip_unzip_compatible {
flags &= !EB_UT_FL_CTIME;
} else {
z_utim.ctime = i_time;
}
} else {
flags &= !EB_UT_FL_CTIME;
}
}
}
}
EF_IZUNIX2 => {
if keeps_times {
if have_new_type_eb == 0 {
have_new_type_eb = 1;
}
if have_new_type_eb <= 1 {
flags &= 0x0ff;
}
} else if have_new_type_eb == 0 {
flags &= !0x0ff; have_new_type_eb = 1;
}
if have_new_type_eb <= 1
&& eb_len == EB_UX2_MINLEN
&& let Some(ug) = owner.as_mut()
{
ug[0] = u64::from(fileio::makeword(data));
ug[1] = u64::from(fileio::makeword(&data[2..]));
flags |= EB_UX2_VALID; }
}
EF_IZUNIX3 => {
have_new_type_eb = 2;
if keeps_times {
flags &= 0x0ff;
}
if eb_len >= EB_UX3_MINLEN
&& data[0] == 1
&& let Some(ug) = owner.as_mut()
{
let uid_size = usize::from(data[1]);
if !keeps_times {
flags &= !0x0ff; }
let gid_at = uid_size + 2;
if let Some(&gid_size) = data.get(gid_at) {
let gid_size = usize::from(gid_size);
let uid = data.get(2..2 + uid_size);
let gid = data.get(gid_at + 1..gid_at + 1 + gid_size);
if let (Some(u), Some(g)) = (uid, gid)
&& let (Some(u), Some(g)) = (
read_ux3_value(f, &pad8(u), uid_size),
read_ux3_value(f, &pad8(g), gid_size),
)
{
ug[0] = u;
ug[1] = g;
flags |= EB_UX2_VALID; }
}
}
}
EF_IZUNIX | EF_PKUNIX if eb_len >= EB_UX_MINLEN && have_new_type_eb == 0 => {
{
flags |= EB_UT_FL_MTIME | EB_UT_FL_ATIME;
let i_time = long_at(EB_UX_MTIME);
if i_time & 0x8000_0000 != 0 {
ut_zip_unzip_compatible = dos_mdatetime >= fileio::DOSTIME_2038_01_18;
if !ut_zip_unzip_compatible {
flags &= !0x0ff; }
} else {
ut_zip_unzip_compatible = false;
}
z_utim.mtime = i_time;
let i_time = long_at(EB_UX_ATIME);
if i_time & 0x8000_0000 != 0 && !ut_zip_unzip_compatible && flags & 0x0ff != 0 {
flags &= !EB_UT_FL_ATIME;
} else {
z_utim.atime = i_time;
}
if eb_len >= EB_UX_FULLSIZE
&& let Some(ug) = owner.as_mut()
{
ug[0] = u64::from(fileio::makeword(&data[EB_UX_UID..]));
ug[1] = u64::from(fileio::makeword(&data[EB_UX_GID..]));
flags |= EB_UX2_VALID;
}
}
}
_ => {}
}
ef = &ef[eb_len + EB_HEADSIZE..];
}
flags
}
fn pad8(b: &[u8]) -> Vec<u8> {
let mut v = b.to_vec();
if v.len() < 8 {
v.resize(8, 0);
}
v
}
mod end;
pub(crate) mod unicode;
#[cfg(test)]
mod tests;