use std::{ ffi::CString, fs::DirBuilder, io::{self, Read, Result, Write}, os::unix::ffi::OsStrExt, path::Path, }; use walkdir::WalkDir; use super::unix; pub fn pack, E: AsRef, I: Iterator>( archive: W, source: P, entries: I, ) -> Result { let _chdir = unix::chdir(source)?; let mut ar = tar::Builder::new(archive); ar.mode(tar::HeaderMode::Deterministic); ar.follow_symlinks(false); for entry in entries { for dir_entry in WalkDir::new(entry).sort_by_file_name() { ar.append_path(dir_entry?.path())?; } } ar.into_inner() } pub fn unpack>(archive: R, dest: P) -> Result<()> { let dest_path = dest.as_ref(); DirBuilder::new().recursive(true).create(dest_path)?; let mut ar = tar::Archive::new(archive); ar.set_preserve_permissions(true); ar.set_preserve_mtime(true); ar.set_unpack_xattrs(true); for entry_r in ar.entries()? { let mut entry = entry_r?; if entry.unpack_in(dest_path)? { let header = entry.header(); let uid = header.uid()? as libc::uid_t; let gid = header.gid()? as libc::gid_t; let path = CString::new(dest_path.join(entry.path()?).as_os_str().as_bytes()) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; if unsafe { libc::lchown(path.as_ptr(), uid, gid) } < 0 { return Err(io::Error::last_os_error()); } } } Ok(()) }