use std::borrow::Cow;
use std::io;
use std::io::prelude::*;
#[cfg(unix)]
use std::os::unix::prelude::*;
use std::path::Path;
use std::str;
use tar::{EntryType, Header};
pub(super) fn calculate_header_size(mut header: tar::Header, path: &Path) -> io::Result<usize> {
if let Err(e) = header.set_path(path) {
let data = path2bytes(path)?;
let max = header.as_old().name.len();
if data.len() < max {
return Err(e);
}
let header2 = prepare_header(data.len() as u64, b'L');
let mut data2 = data.chain(io::repeat(0).take(1));
let mut dst = vec![];
append(&mut dst, &header2, &mut data2)?;
let truncated = match str::from_utf8(&data[..max]) {
Ok(s) => s,
Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
};
header.set_path(truncated)?;
Ok(dst.len() + header.as_bytes().len())
} else {
Ok(header.as_bytes().len())
}
}
#[cfg(any(windows, target_arch = "wasm32"))]
pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
p.as_os_str()
.to_str()
.map(|s| s.as_bytes())
.ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
.map(|bytes| {
if bytes.contains(&b'\\') {
let mut bytes = bytes.to_owned();
for b in &mut bytes {
if *b == b'\\' {
*b = b'/';
}
}
Cow::Owned(bytes)
} else {
Cow::Borrowed(bytes)
}
})
}
#[cfg(unix)]
pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
Ok(p.as_os_str().as_bytes()).map(Cow::Borrowed)
}
#[cfg(target_arch = "wasm32")]
fn invalid_utf8<T>(_: T) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
}
fn append(mut dst: &mut dyn Write, header: &Header, mut data: &mut dyn Read) -> io::Result<()> {
dst.write_all(header.as_bytes())?;
let len = io::copy(&mut data, &mut dst)?;
let buf = [0; 512];
let remaining = 512 - (len % 512);
if remaining < 512 {
dst.write_all(&buf[..remaining as usize])?;
}
Ok(())
}
fn prepare_header(size: u64, entry_type: u8) -> Header {
let mut header = Header::new_gnu();
let name = b"././@LongLink";
header.as_gnu_mut().unwrap().name[..name.len()].clone_from_slice(&name[..]);
header.set_mode(0o644);
header.set_uid(0);
header.set_gid(0);
header.set_mtime(0);
header.set_size(size + 1);
header.set_entry_type(EntryType::new(entry_type));
header.set_cksum();
header
}
#[cfg(any(windows, target_arch = "wasm32"))]
fn other(msg: &str) -> io::Error {
io::Error::new(io::ErrorKind::Other, msg)
}