mod gnu;
mod ustar;
mod pax;
pub mod header;
pub mod label;
pub mod recovery;
use std::{io, path, fs};
use std::io::{Seek};
use crate::fs::{ArchivalSink};
pub fn canonicalized_tar_path(dirpath: &path::Path, filetype: header::TarFileType) -> String {
let mut relapath_encoded : String = String::with_capacity(255);
let mut first = true;
for component in dirpath.components() {
if let path::Component::RootDir = component {
} else {
if !first {
relapath_encoded.push('/');
} else {
first = false;
}
match component {
path::Component::Prefix(prefix) => {
match prefix.kind() {
path::Prefix::Verbatim(rootpath) => {
relapath_encoded.extend(rootpath.to_string_lossy().into_owned().chars());
},
path::Prefix::VerbatimUNC(server, share) => {
relapath_encoded.extend(server.to_string_lossy().into_owned().chars());
relapath_encoded.push('/');
relapath_encoded.extend(share.to_string_lossy().into_owned().chars());
},
path::Prefix::VerbatimDisk(letter) => {
relapath_encoded.push((letter as char).to_ascii_lowercase());
},
path::Prefix::DeviceNS(devicename) => {
relapath_encoded.extend(devicename.to_string_lossy().into_owned().chars());
},
path::Prefix::UNC(server, share) => {
relapath_encoded.extend(server.to_string_lossy().into_owned().chars());
relapath_encoded.push('/');
relapath_encoded.extend(share.to_string_lossy().into_owned().chars());
},
path::Prefix::Disk(letter) => {
relapath_encoded.push((letter as char).to_ascii_lowercase());
},
}
},
path::Component::Normal(name) => {
relapath_encoded.extend(name.to_string_lossy().into_owned().chars());
},
path::Component::CurDir => relapath_encoded.extend(".".chars()),
path::Component::ParentDir => relapath_encoded.extend("..".chars()),
_ => {}
}
}
}
if let header::TarFileType::Directory = filetype {
relapath_encoded.push('/');
}
relapath_encoded
}
pub fn serialize<I>(traversal: &header::HeaderGenResult, tarball: &mut ArchivalSink<I>) -> io::Result<u64> {
let mut tarball_size : u64 = 0;
tarball_size += traversal.encoded_header.len() as u64;
tarball.write_all(&traversal.encoded_header)?;
if let header::TarFileType::FileStream = traversal.tar_header.file_type {
let mut stream_needed = true;
let mut stream_start = 0;
if let Some(ref readahead) = traversal.file_prefix {
tarball_size += readahead.len() as u64;
tarball.write_all(&readahead)?;
stream_start = readahead.len() as u64;
if readahead.len() as u64 >= traversal.tar_header.file_size {
stream_needed = false;
}
}
if stream_needed {
let mut source_file = fs::File::open(traversal.canonical_path.as_ref())?;
source_file.seek(io::SeekFrom::Start(stream_start))?;
tarball_size += io::copy(&mut source_file, tarball)?;
}
let expected_size = traversal.encoded_header.len() as u64 + traversal.tar_header.file_size;
if tarball_size != expected_size {
return Err(io::Error::new(io::ErrorKind::InvalidData, format!("File {:?} was shorter than indicated in traversal by {} bytes, archive may be damaged.", traversal.original_path, (expected_size - tarball_size))));
}
}
let padding_needed = tarball_size % 512;
if padding_needed != 0 {
tarball_size += padding_needed;
tarball.write_all(&vec![0; (512 - padding_needed) as usize])?;
}
Ok(tarball_size)
}