use super::cli::ZE_ABORT;
use super::flavour;
use super::messages::{self as m, printf};
use super::readzip::{
self, Archive, CENHEAD, CFile, CopyFail, ENDHEAD, InArchive, ZFile, cstr, find_next_signature,
find_signature, get_extra_field, lg, llg, sh,
};
use super::sink::{FileSink, Temp, mkstemp, replace};
use super::unicode::{self, UTF8_BIT};
use super::zipfile::{self, Bf, LOCHEAD, ZEntry, ZOut};
use super::zipup::{drain_dots, write_failed, write_num_string, zerr};
use super::{Exit, Zr};
use crate::compat::host::Host;
const ZE_EOF: i32 = 2;
const ZE_FORM: i32 = 3;
const ZE_BIG: i32 = 6;
const ZE_TEMP: i32 = 10;
const ZE_CREAT: i32 = 15;
const ZE_READ: i32 = 11;
const ZE_WRITE: i32 = 14;
const ZIP_UWORD32_MAX: u64 = 0xffff_ffff;
#[derive(Default)]
struct Errno(i32);
impl Errno {
fn from(&mut self, f: &CFile) {
if f.errno != 0 {
self.0 = f.errno;
}
}
fn text(&self) -> Vec<u8> {
readzip::strerror(self.0)
}
}
#[derive(Default)]
struct Des {
good: bool,
crc: u32,
csize: u64,
usize: u64,
}
pub(crate) fn run(z: &mut Zr) -> Exit {
if z.o.show_what_doing {
z.mesg_write(m::SD_OPEN_FF.as_bytes());
z.mesg_write(m::SD_CREATING_FF.as_bytes());
}
let zipfile = z.o.zipfile.clone().unwrap_or_default();
let out_path = z.o.out_path.clone().unwrap_or_else(|| zipfile.clone());
if super::sink::refused_output_link(&z.host.path(&out_path), false) {
return z.ziperr_os(ZE_CREAT, &out_path, &super::sink::link_refusal());
}
let dir: Vec<u8> = match &z.o.tempath {
Some(t) => {
let mut d = t.clone();
if d.last() != Some(&b'/') {
d.push(b'/');
}
d
}
None => match zipfile.iter().rposition(|&b| b == b'/') {
Some(i) => zipfile[..=i].to_vec(),
None => Vec::new(),
},
};
let (tempzip, r) = mkstemp(&z.host, &dir);
let (file, _armed) = match r {
Ok(x) => x,
Err(e) => return z.ziperr_os(ZE_TEMP, &tempzip, &e),
};
let mut temp = Temp {
path: Some(z.host.path(&tempzip)),
};
let budget = match &z.host {
Host::Sandbox(s) => Some(s.budget.clone()),
Host::Real => None,
};
let mut out = ZOut::new(Box::new(FileSink::new(file, budget)), z.o.force_zip64, None);
out.unicode = z.unicode_out();
let mut tempzn: u64 = 0;
if !zipfile.is_empty() && zipfile != b"-" {
let _ = CFile::open(z, &zipfile);
}
let mut sc = Scan {
ar: Archive::default(),
zcomment: None,
zcomlen: 0,
files_total: 0,
bytes_total: 0,
};
if let Err(x) = scanzipf_fixnew(z, &mut sc, &mut out, &mut tempzn) {
return x;
}
if z.o.show_what_doing {
z.mesg_write(m::SD_WRITING_CENTRAL_FF.as_bytes());
}
let c = tempzn;
let mut k: u64 = 0;
for zf in sc.ar.zfiles.iter_mut() {
let r = zipfile::putcentral(&mut zf.e, &mut out);
drain_dots(z, &mut out);
match r {
Ok(()) => {}
Err(zipfile::PutError::Big { why, .. }) => {
z.zipwarn(why.as_bytes(), b"");
return z.ziperr(ZE_BIG, &tempzip);
}
Err(zipfile::PutError::Write(err)) => return write_failed(z, &err),
}
let e = &zf.e;
tempzn = tempzn.wrapping_add(
4 + zipfile::CENHEAD
+ e.iname.len() as u64
+ e.cextra.len() as u64
+ e.comment.len() as u64,
);
k += 1;
}
if sc.ar.zfiles.is_empty() {
z.zipwarn(m::ZIP_FILE_EMPTY.as_bytes(), b"");
}
let t = tempzn.wrapping_sub(c);
let comment = sc.zcomment.unwrap_or_default();
let r = zipfile::putend_with(&mut out, k, t, c, sc.zcomlen, &comment);
drain_dots(z, &mut out);
if let Err(err) = r {
return write_failed(z, &err);
}
if let Err(err) = out.w.flush() {
return z.ziperr_os(ZE_TEMP, &tempzip, &err);
}
drop(out);
if zipfile != b"-" {
let tmp = temp.path.take().unwrap_or_default();
if let Err((code, err)) = replace(&z.host.path(&out_path), &tmp, false) {
z.zipwarn(m::NEW_ZIP_LEFT_AS.as_bytes(), &tempzip);
return zerr(z, code, m::WAS_REPLACING.as_bytes(), Some(&err));
}
}
drop(_armed);
if z.log.is_some() {
let mut l = Vec::new();
printf(
&mut l,
m::LOG_TOTAL,
&[sc.files_total.to_string().as_bytes()],
);
l.extend_from_slice(write_num_string(sc.bytes_total).trim_start().as_bytes());
l.extend_from_slice(m::LOG_BYTES.as_bytes());
let now = super::asctime_now(z);
printf(&mut l, m::LOG_DONE, &[now.as_bytes()]);
z.log_write(&l);
}
super::write::finish(z, super::cli::ZE_OK, &mut sc.ar.zfiles)
}
struct Scan {
ar: Archive,
zcomment: Option<Vec<u8>>,
zcomlen: usize,
files_total: u64,
bytes_total: u64,
}
fn at(disk: u64, offset: u64) -> [Vec<u8>; 2] {
[
format!("{disk:2}").into_bytes(),
format!("{offset:6}").into_bytes(),
]
}
fn message(z: &mut Zr, fmt: &str, args: &[&[u8]], nl: bool) {
let mut s = Vec::new();
printf(&mut s, fmt, args);
z.zipmessage_nl(&s, nl);
}
fn scanzipf_fixnew(
z: &mut Zr,
sc: &mut Scan,
out: &mut ZOut,
tempzn: &mut u64,
) -> Result<i32, Exit> {
let mut errno = Errno::default();
let mut sigbuf = [0u8; 4];
let mut in_central_directory = false;
let mut sorted = SortState::default();
let mut r: i32 = 0;
z.total_disks = 1_000_000;
let in_path = z.o.in_path.clone().unwrap_or_default();
let zipfile = z.o.zipfile.clone().unwrap_or_default();
let plen = in_path.len();
if plen >= 4 && zipfile.len() >= plen {
let ext = &zipfile[plen - 4..];
let after = zipfile.get(plen).copied().unwrap_or(0);
if ext[0] == b'.'
&& ext[1].eq_ignore_ascii_case(&b'z')
&& ext[2].is_ascii_digit()
&& ext[3].is_ascii_digit()
&& (plen == 4 || after.is_ascii_digit())
{
z.zipwarn(m::FF_NEED_ZIP.as_bytes(), b"");
return Ok(ZE_FORM);
}
}
let mut ia = InArchive { f: None };
match CFile::open(z, &in_path) {
Err(e) => {
errno.0 = e.raw_os_error().unwrap_or(2);
z.zipwarn(m::FF_COULD_NOT_OPEN.as_bytes(), &in_path);
}
Ok(mut f) => {
let back = match f.end() {
Some(len) if len >= 0x40000 => f.seek(i128::from(len - 0x40000)),
Some(_) => {
f.errno = 22;
false
}
None => false,
};
if !back && !f.seek(0) {
errno.from(&f);
z.zipwarn(m::UNABLE_TO_SEEK.as_bytes(), &in_path);
return Ok(ZE_READ);
}
if !find_signature(&mut f, b"PK\x05\x06", &mut sigbuf) {
z.zipwarn(m::FF_MISSING_EOCDR.as_bytes(), b"");
} else {
let mut eocdr_offset = f.tell();
while find_signature(&mut f, b"PK\x05\x06", &mut sigbuf) {
eocdr_offset = f.tell();
}
if !f.seek(i128::from(eocdr_offset)) {
errno.from(&f);
z.zipwarn(m::UNABLE_TO_SEEK.as_bytes(), &in_path);
return Ok(ZE_READ);
}
let scbuf = f.fread(ENDHEAD);
if scbuf.len() < ENDHEAD {
let mut s = Vec::new();
printf(
&mut s,
m::FF_EOCDR_SHORT,
&[scbuf.len().to_string().as_bytes()],
);
z.zipwarn(&s, b"");
} else {
let eocdr_disk = u64::from(sh(&scbuf, 0));
z.total_disks = eocdr_disk + 1;
z.current_in_disk = z.total_disks - 1;
let in_cd_start_disk = u64::from(sh(&scbuf, 2));
if z.total_disks < 0x10000 && in_cd_start_disk > z.total_disks {
z.zipwarn(m::FF_EOCDR_BAD_DISKS.as_bytes(), b"");
z.total_disks = 0;
} else {
let zcomlen = usize::from(sh(&scbuf, 16));
sc.zcomlen = zcomlen;
if zcomlen > 0 {
let c = f.fread(zcomlen);
if c.len() != zcomlen {
errno.from(&f);
z.zipwarn(m::FF_COMMENT_TRUNCATED.as_bytes(), b"");
} else {
sc.zcomment = Some(c);
}
}
}
let mut s = Vec::new();
if z.total_disks != 1 {
printf(
&mut s,
m::FF_EXPECT_SPLITS,
&[z.total_disks.to_string().as_bytes()],
);
} else {
s.extend_from_slice(m::FF_EXPECT_SINGLE.as_bytes());
}
z.zipmessage(&s, b"");
if sc.zcomment.is_some() {
z.zipmessage(m::FF_FOUND_COMMENT.as_bytes(), b"");
}
}
}
if z.total_disks == 0 {
if !f.seek(0) {
errno.from(&f);
z.zipwarn(m::UNABLE_TO_SEEK.as_bytes(), &in_path);
return Ok(ZE_READ);
}
let mut issig = find_next_signature(&mut f, &mut sigbuf);
if issig {
let off = f.tell();
if off == 4 && &sigbuf == b"PK\x03\x03" {
issig = find_next_signature(&mut f, &mut sigbuf);
} else if off <= 4 && &sigbuf == b"PK\x03\x03" {
z.total_disks = 99999;
}
}
if issig && z.total_disks == 0 && f.tell() == 8 && &sigbuf == b"PK\x03\x04" {
ask_single_disk(z);
}
}
errno.from(&f);
if !z.o.noisy {
z.total_disks = 1;
}
if z.total_disks == 1_000_000 {
ask_single_disk(z);
}
if z.total_disks == 1_000_000 {
z.total_disks = 100_000;
}
ia.f = Some(f);
}
}
if z.o.noisy
&& let Some(f) = ia.f.take()
{
errno.from(&f);
}
z.zipmessage(m::FF_SCANNING.as_bytes(), b"");
z.current_in_disk = 0;
while z.current_in_disk < z.total_disks {
let disk = z.current_in_disk;
let ip = z.o.in_path.clone().unwrap_or_default();
let mut split_path = super::split::get_in_split_path(z, &ip, disk)?;
if ia.f.is_none() {
let mut skip_disk = false;
loop {
match CFile::open(z, &split_path) {
Ok(f) => {
ia.f = Some(f);
break;
}
Err(e) => errno.0 = e.raw_os_error().unwrap_or(2),
}
let result = super::split::ask_for_split_read_path(z, disk)?;
if result == ZE_ABORT {
z.zipwarn(m::COULD_NOT_FIND_SPLIT.as_bytes(), &split_path);
return Ok(ZE_ABORT);
} else if result == super::split::ANSWER_EOF {
z.zipmessage_nl(b"", true);
z.zipwarn(m::USER_ENDED_READING.as_bytes(), b"");
return Ok(ZE_EOF);
} else if result == super::split::ANSWER_SKIP {
z.zipmessage_nl(b"", true);
let mut s = Vec::new();
printf(&mut s, m::SKIPPING_DISK, &[disk.to_string().as_bytes()]);
z.zipwarn(&s, b"");
skip_disk = true;
break;
}
let ip = z.o.in_path.clone().unwrap_or_default();
split_path = super::split::get_in_split_path(z, &ip, disk)?;
}
if skip_disk {
z.current_in_disk += 1;
continue;
}
}
while let Some(f) = ia.f.as_mut() {
if !find_next_signature(f, &mut sigbuf) {
break;
}
let current_in_offset = f.tell();
let cur = z.current_in_disk;
let [d1, o1] = at(cur + 1, current_in_offset.wrapping_sub(4));
if &sigbuf == b"PK\x05\x06" {
message(z, m::FF_EOCDR_FOUND, &[&d1, &o1], true);
} else if &sigbuf == b"PK\x06\x06" {
message(z, m::FF_Z64_EOCDR_FOUND, &[&d1, &o1], true);
} else if &sigbuf == b"PK\x06\x07" {
message(z, m::FF_Z64_EOCDL_FOUND, &[&d1, &o1], true);
} else if &sigbuf == b"PK\x03\x04" {
if z.o.verbose > 0 {
message(z, m::FF_LOCAL, &[&d1, &o1], false);
}
let mut zf = ZFile::default();
r = zipcopy_ff(z, &mut ia, out, tempzn, &mut zf, &mut errno)?;
if in_central_directory {
message(z, m::FF_ENTRY_AFTER_CENTRAL, &[&d1, &o1], true);
in_central_directory = false;
}
if r == ZE_EOF {
break;
} else if r == 0 {
sc.files_total += 1;
sc.bytes_total = sc.bytes_total.wrapping_add(zf.e.siz);
sc.ar.zfiles.push(zf);
}
} else if &sigbuf == b"PK\x01\x02" {
if !in_central_directory {
z.zipmessage(m::FF_CENTRAL_FOUND.as_bytes(), b"");
resort(z, &mut sc.ar, &mut sorted);
}
if z.o.verbose > 0 {
message(z, m::FF_CEN, &[&d1, &o1], false);
}
in_central_directory = true;
let f = ia.f.as_mut().expect("open");
let scbuf = f.fread(CENHEAD);
if scbuf.len() != CENHEAD {
errno.from(f);
z.zipwarn(m::READING_CENTRAL.as_bytes(), &errno.text());
z.zipwarn(m::F_ERROR_READING_CENTRAL.as_bytes(), b"");
z.zipwarn(m::SKIPPING_ENTRY.as_bytes(), b"");
continue;
}
let nam = usize::from(sh(&scbuf, 24));
let cext = usize::from(sh(&scbuf, 26));
let com = usize::from(sh(&scbuf, 28));
if nam == 0 {
z.zipwarn(
m::ZERO_LENGTH_NAME.as_bytes(),
(sc.ar.zfiles.len() + 1).to_string().as_bytes(),
);
z.zipwarn(m::SKIPPING_ENTRY.as_bytes(), b"");
continue;
}
let iname = f.fread(nam);
let mut ok = iname.len() == nam;
let mut cextra = Vec::new();
let mut comment = Vec::new();
if ok && cext > 0 {
cextra = f.fread(cext);
ok = cextra.len() == cext;
}
if ok && com > 0 {
comment = f.fread(com);
ok = comment.len() == com;
}
if !ok {
errno.from(f);
z.zipwarn(m::ERROR_READING_ENTRY.as_bytes(), &errno.text());
z.zipwarn(m::SKIPPING_ENTRY.as_bytes(), b"");
continue;
}
let vem = sh(&scbuf, 0);
let tim = lg(&scbuf, 8);
let crc = lg(&scbuf, 12);
let att = sh(&scbuf, 32);
let atx = lg(&scbuf, 34);
let found = readzip::zsearch(&sc.ar, &iname);
match found {
Some(i) if sc.ar.zfiles[i].e.tim == tim => {
if z.o.verbose > 0 {
message(z, m::FF_CEN_UPDATING, &[cstr(&iname)], false);
}
let zcrc = sc.ar.zfiles[i].e.crc;
if zcrc != crc {
let mut s = Vec::new();
printf(
&mut s,
m::FF_CRC_MISMATCH,
&[zcrc.to_string().as_bytes(), crc.to_string().as_bytes()],
);
z.zipwarn(&s, b"");
}
let zf = &mut sc.ar.zfiles[i];
zf.e.vem = vem;
zf.e.cextra = cextra;
zf.e.comment = comment;
zf.e.att = att;
zf.e.atx = atx;
zf.dosflag = (vem & 0xff00) == 0;
if flavour::unicode_support(z.flavour)
&& z.o.unicode_mismatch != 3
&& zf.e.uname.is_none()
{
if zf.e.flg & UTF8_BIT != 0 {
zf.e.uname = Some(cstr(&zf.e.iname).to_vec());
} else {
let (cx, iname, oname, name) = (
zf.e.cextra.clone(),
zf.e.iname.clone(),
zf.e.oname.clone(),
zf.e.name.clone(),
);
let u = unicode::read_unicode_path(
z,
&cx,
&iname,
unicode::PathReader::CentralFix {
oname: &oname,
name: &name,
},
);
sc.ar.zfiles[i].e.uname = u?;
}
}
let zf = &mut sc.ar.zfiles[i];
if z.o.verbose > 0 {
let e = zf.e.clone();
readzip::zipoddities(z, &e);
}
let zf = &mut sc.ar.zfiles[i];
let current_offset = match out.w.tell_raw() {
Ok(o) => o,
Err(e) => {
z.zipwarn(m::FF_WRITING_SEEK.as_bytes(), &host_err(&e));
return Ok(ZE_WRITE);
}
};
if let Err(e) = out.w.seek_to(zf.e.off) {
z.zipwarn(m::FF_WRITING_SEEK.as_bytes(), &host_err(&e));
return Ok(ZE_WRITE);
}
let rr = zipfile::putlocal_rewrite(&mut zf.e, out);
drain_dots(z, out);
if rr.is_err() {
z.zipwarn(m::FF_ERROR_REWRITING.as_bytes(), b"");
}
if let Err(e) = out.w.seek_raw(current_offset) {
z.zipwarn(m::FF_WRITE_SEEK.as_bytes(), &host_err(&e));
return Ok(ZE_WRITE);
}
match out.w.tell_raw() {
Ok(o) if o == current_offset => {}
Ok(_) => {
z.zipwarn(m::FF_SEEK_AFTER_LOCAL.as_bytes(), &errno.text());
return Ok(ZE_WRITE);
}
Err(e) => {
z.zipwarn(m::FF_SEEK_AFTER_LOCAL.as_bytes(), &host_err(&e));
return Ok(ZE_WRITE);
}
}
if z.o.verbose > 0 {
z.zipmessage_nl(b"", true);
}
}
_ => {
message(z, m::FF_NO_LOCAL_ENTRY, &[cstr(&iname)], true);
}
}
} else if sc.ar.zfiles.is_empty() && &sigbuf == b"PK\x07\x08" {
let text = if z.total_disks == 1 {
m::FF_SPAN_NOT_EXPECTED
} else if z.total_disks > 1 {
m::FF_SPAN_EXPECTED
} else {
m::FF_SPAN_MAYBE
};
z.zipmessage(text.as_bytes(), b"");
} else {
let mut s = m::FF_UNEXPECTED_SIGNATURE.as_bytes().to_vec();
for c in sigbuf {
s.extend_from_slice(format!("{c:02x} ").as_bytes());
}
printf(
&mut s,
m::SIG_ON_DISK,
&[
cur.to_string().as_bytes(),
current_in_offset.wrapping_sub(4).to_string().as_bytes(),
],
);
z.zipwarn(&s, b"");
z.zipwarn(m::SKIPPING_SIGNATURE.as_bytes(), b"");
}
}
if let Some(f) = ia.f.take() {
errno.from(&f);
}
if r == ZE_EOF {
break;
}
z.current_in_disk += 1;
}
Ok(0)
}
#[derive(Default)]
struct SortState {
upto: usize,
has_equal: bool,
}
fn resort(z: &Zr, ar: &mut Archive, st: &mut SortState) {
let n = ar.zfiles.len();
if n == st.upto {
return;
}
if !st.has_equal {
if n - st.upto <= 64 {
let mut zsort = ar.zsort.clone();
for i in st.upto..n {
let k = cstr(&ar.zfiles[i].zname);
match zsort.binary_search_by(|&j| cstr(&ar.zfiles[j].zname).cmp(k)) {
Ok(_) => {
st.has_equal = true;
break;
}
Err(pos) => zsort.insert(pos, i),
}
}
if !st.has_equal {
ar.zsort = zsort;
st.upto = n;
return;
}
} else {
let mut order: Vec<usize> = (0..n).collect();
order.sort_by(|&a, &b| cstr(&ar.zfiles[a].zname).cmp(cstr(&ar.zfiles[b].zname)));
if order
.windows(2)
.all(|w| cstr(&ar.zfiles[w[0]].zname) != cstr(&ar.zfiles[w[1]].zname))
{
ar.zsort = order;
st.upto = n;
return;
}
st.has_equal = true;
}
}
let names: Vec<&[u8]> = ar.zfiles.iter().map(|f| cstr(&f.zname)).collect();
let mut order: Vec<usize> = (0..n).collect();
super::scan::qsort(z.flavour, &mut order, &|a, b| names[a].cmp(names[b]));
ar.zsort = order;
st.upto = n;
}
fn host_err(e: &std::io::Error) -> Vec<u8> {
crate::compat::host::strerror(e)
}
fn ask_single_disk(z: &mut Zr) {
z.stdout_write(m::FF_SINGLE_DISK_Q.as_bytes());
let p = super::split::prompt(z);
if let Some(a) = super::split::fgets_n(&p, 100)
&& matches!(a.first(), Some(b'y' | b'Y'))
{
z.total_disks = 1;
z.zipmessage(m::FF_ASSUMING_SINGLE.as_bytes(), b"");
}
}
fn zipcopy_ff(
z: &mut Zr,
ia: &mut InArchive,
out: &mut ZOut,
tempzn: &mut u64,
zf: &mut ZFile,
errno: &mut Errno,
) -> Result<i32, Exit> {
let f = ia.f.as_mut().expect("open");
let buf = f.fread(LOCHEAD as usize);
if buf.len() != LOCHEAD as usize {
errno.from(f);
let err = f.ferror();
z.zipwarn(m::READING_LOCAL_ENTRY.as_bytes(), &errno.text());
return Ok(if err { ZE_READ } else { ZE_EOF });
}
let mut lz = ZEntry {
ver: sh(&buf, 0),
lflg: sh(&buf, 2),
how: sh(&buf, 4),
tim: lg(&buf, 6),
crc: lg(&buf, 10),
siz: u64::from(lg(&buf, 14)),
len: u64::from(lg(&buf, 18)),
..ZEntry::default()
};
let nam = usize::from(sh(&buf, 22));
let ext = usize::from(sh(&buf, 24));
let os = lz.ver >> 8;
let pkver = lz.ver.wrapping_sub(os);
let insane = if os > 40 {
Some((m::FF_ILLEGAL_HOST, os))
} else if pkver > 100 {
Some((m::FF_ILLEGAL_PKVER, pkver))
} else if lz.how > 200 {
Some((m::FF_UNRECOGNIZED_METHOD, lz.how))
} else {
None
};
if let Some((fmt, v)) = insane {
let mut s = Vec::new();
printf(&mut s, fmt, &[v.to_string().as_bytes()]);
z.zipwarn(&s, b"");
z.zipwarn(m::FF_SKIPPING.as_bytes(), b"(null)");
return Ok(ZE_FORM);
}
lz.iname = f.fread(nam);
if lz.iname.len() != nam {
errno.from(f);
return Ok(if f.ferror() { ZE_READ } else { ZE_EOF });
}
lz.extra = f.fread(ext);
if lz.extra.len() != ext {
errno.from(f);
return Ok(if f.ferror() { ZE_READ } else { ZE_EOF });
}
lz.name = cstr(&lz.iname).to_vec();
out.zip64_entry = adjust_zip_local_entry(&mut lz);
zf.e.flg = lz.lflg;
zf.e.len = lz.len;
zf.e.siz = lz.siz;
zf.e.dsk = out.current_disk;
zf.e.off = out.w.offset();
let e: u64 = if zf.e.lflg & 8 != 0 {
if out.zip64_entry { 24 } else { 16 }
} else {
0
};
*tempzn = tempzn
.wrapping_add(4 + LOCHEAD + nam as u64 + ext as u64 + e)
.wrapping_add(zf.e.siz);
let oname = cstr(&lz.iname).to_vec();
zf.e.oname = oname.clone();
let mut s = Vec::new();
printf(&mut s, m::FF_COPYING, &[&oname]);
z.zipmessage_nl(&s, false);
zf.e.crc = lz.crc;
let r = zipfile::putlocal(&mut lz, out);
drain_dots(z, out);
match r {
Ok(()) => {}
Err(zipfile::PutError::Write(err)) => return Err(write_failed(z, &err)),
Err(pe) => return Err(super::zipup::put_failed(z, &lz, pe)),
}
let mut des = Des::default();
let r = if lz.lflg & 8 != 0 {
bfcopy_ff(z, ia, out, None, &mut des, errno)?
} else {
bfcopy_ff(z, ia, out, Some(lz.siz), &mut des, errno)?
};
if r == ZE_ABORT {
return Err(z.ziperr(ZE_ABORT, m::COULD_NOT_FIND_SPLIT_ABORT.as_bytes()));
}
if r == ZE_EOF || z.skip_this_disk != 0 {
z.zipwarn(m::ABORTING_ENTRY.as_bytes(), &oname);
let r = if r == 0 { ZE_FORM } else { r };
z.skip_this_disk = 0;
let at = out.current_local_offset;
if out.w.seek_raw(at).is_err() {
return Err(z.ziperr(ZE_WRITE, m::SEEK_FAILED_OUTPUT.as_bytes()));
}
out.w.set_offset(at);
*tempzn = at;
return Ok(r);
}
if zf.e.flg & 8 != 0 {
if des.good {
lz.crc = des.crc;
lz.siz = des.csize;
lz.len = des.usize;
} else {
z.zipwarn(m::FF_NO_END_OF_STREAM.as_bytes(), &oname);
z.zipwarn(m::FF_REWINDING.as_bytes(), b"");
let _ = out.w.seek_raw(out.current_local_offset);
return Ok(ZE_FORM);
}
}
if zf.e.flg & 8 != 0 {
let rr = zipfile::putextended(&lz, out);
drain_dots(z, out);
if let Err(err) = rr {
return Err(write_failed(z, &err));
}
}
zf.e.ver = lz.ver;
zf.e.how = lz.how;
zf.e.tim = lz.tim;
zf.e.crc = lz.crc;
zf.e.lflg = lz.lflg;
zf.e.flg = lz.lflg;
zf.e.len = lz.len;
zf.e.siz = lz.siz;
zf.e.extra = lz.extra.clone();
let mut cextra = vec![0u8; lz.extra.len()];
let upto = lz
.extra
.iter()
.position(|&b| b == 0)
.map_or(lz.extra.len(), |i| i + 1);
cextra[..upto].copy_from_slice(&lz.extra[..upto]);
zf.e.cextra = cextra;
zf.e.comment = Vec::new();
zf.e.att = 0;
zf.e.atx = 0;
zf.e.name = lz.name.clone();
zf.e.iname = lz.iname.clone();
zf.zname = cstr(&lz.iname).to_vec();
let mut s = Vec::new();
printf(&mut s, m::FF_BYTES, &[zf.e.siz.to_string().as_bytes()]);
z.zipmessage_nl(&s, true);
if r == ZE_READ {
z.zipwarn(m::FF_ENTRY_TRUNCATED.as_bytes(), &oname);
let mut s = Vec::new();
printf(
&mut s,
m::FF_EXPECTED_SIZE,
&[
lz.siz.to_string().as_bytes(),
out.bytes_this_entry.to_string().as_bytes(),
],
);
z.zipwarn(&s, b"");
}
Ok(r)
}
fn adjust_zip_local_entry(e: &mut ZEntry) -> bool {
let Some(at) = get_extra_field(0x0001, &e.extra) else {
return false;
};
let mut block = e.extra[(at + 4).min(e.extra.len())..].to_vec();
block.resize(block.len().max(16), 0);
let mut p = 0;
if e.len == ZIP_UWORD32_MAX {
e.len = llg(&block, p);
p += 8;
}
if e.siz == ZIP_UWORD32_MAX {
e.siz = llg(&block, p);
}
true
}
fn bfcopy_ff(
z: &mut Zr,
ia: &mut InArchive,
out: &mut ZOut,
n: Option<u64>,
des: &mut Des,
errno: &mut Errno,
) -> Result<i32, Exit> {
let cbsz = readzip::CBSZ;
let desc = n.is_none();
let n = n.unwrap_or(u64::MAX - 1);
let mut sbuf = [0u8; 4];
let mut data_start = 0u64;
if desc {
data_start = ia.f.as_mut().map_or(0, CFile::tell);
}
let copy_dots = z.o.copy_only && !z.o.display_globaldots;
if copy_dots {
z.o.dot_count = -1;
}
des.good = false;
let mut m: u64 = 0;
while desc || m < n {
let brd = if desc {
cbsz
} else {
(n - m).min(cbsz as u64) as usize
};
let Some(f) = ia.f.as_mut() else {
return Ok(ZE_READ);
};
let des_start = f.tell();
let first = if desc { 4 } else { brd };
let mut b = f.fread(first);
if b.is_empty() {
errno.from(f);
return Ok(ZE_READ);
}
let mut k = b.len();
if desc {
des.crc = 0;
des.csize = 0;
des.usize = 0;
let size = if out.zip64_entry { 24 } else { 16 };
if !b.starts_with(b"PK\x07\x08") {
let mut scanned = 0;
let mut found = None;
loop {
for (kk, &c) in b.iter().enumerate().skip(scanned) {
sbuf.copy_within(1..4, 0);
sbuf[3] = c;
if &sbuf == b"PK\x07\x08" && kk >= 3 {
found = Some(kk - 3);
break;
}
}
if found.is_some() || b.len() >= brd || f.feof() {
break;
}
scanned = b.len();
let more = scanned.max(64).min(brd - scanned);
let c = f.fread(more);
if c.is_empty() {
break;
}
b.extend_from_slice(&c);
}
k = b.len();
if let Some(kk) = found {
let to = out.w.offset().wrapping_add(kk as u64);
if !f.seek(i128::from(to)) {
return Err(z.ziperr(ZE_READ, m::SEEK_FAILED_DESCRIPTOR.as_bytes()));
}
k = kk;
}
} else {
if b.len() == 4 {
b.extend_from_slice(&f.fread(size - 4));
}
k = b.len();
des.good = false;
if k >= size {
if out.zip64_entry {
des.crc = lg(&b, 4);
des.csize = llg(&b, 8);
des.usize = llg(&b, 16);
} else {
des.crc = lg(&b, 4);
des.csize = u64::from(lg(&b, 8));
des.usize = u64::from(lg(&b, 12));
}
if des_start.wrapping_sub(data_start) != des.csize {
k = 4;
if !f.seek(i128::from(des_start) + 4) {
return Err(z.ziperr(ZE_READ, m::SEEK_FAILED_DESCRIPTOR.as_bytes()));
}
let r = out.bfwrite(&b[..k], Bf::Data);
drain_dots(z, out);
if let Err(err) = r {
return Err(write_failed(z, &err));
}
m += k as u64;
continue;
}
des.good = true;
k = size;
if !f.seek(i128::from(des_start) + size as i128) {
return Err(z.ziperr(ZE_READ, m::SEEK_FAILED_DESCRIPTOR.as_bytes()));
}
data_start = f.tell();
} else {
let rest = f.fread(brd - b.len());
b.extend_from_slice(&rest);
k = b.len();
}
}
}
if !des.good {
let r = out.bfwrite(&b[..k], Bf::Data);
drain_dots(z, out);
if let Err(err) = r {
return Err(write_failed(z, &err));
}
m += k as u64;
}
if copy_dots {
readzip::copy_dot(z, k);
}
if des.good {
break;
}
if desc {
continue;
}
if m < n && ia.f.as_ref().is_some_and(CFile::feof) {
match readzip::next_in_split(z, ia) {
Ok(true) => {}
Ok(false) => break,
Err(CopyFail::Code(c, _)) => return Ok(c),
Err(CopyFail::Exit(x)) => return Err(x),
Err(CopyFail::Write(err)) => return Err(write_failed(z, &err)),
}
}
}
Ok(0)
}