use packtool::{Packed, View};
fn main() {
assert_eq!(Header::SIZE, 512,);
let bytes = std::fs::read("example.tar").expect("need example tar file");
let mut sub = bytes.as_slice();
while sub.len() > Header::SIZE {
let header_view = View::<'_, Header>::try_from_slice(&sub[..Header::SIZE])
.expect("should start with a header");
let header = header_view.unpack();
let file = std::str::from_utf8(&header.filename.0).expect("valid filename");
let file_size = header.file_size.to_size();
let file_padding = 512 - file_size % 512;
println!("compressed file: {} ({} bytes)", file, file_size);
sub = &sub[Header::SIZE + file_size + file_padding..];
if sub.len() == 1024 {
break;
}
}
}
#[derive(Packed)]
pub struct FileName([u8; 100]);
#[derive(Packed)]
pub struct FileMode([u8; 8]);
#[derive(Packed)]
pub struct Owner(u64);
#[derive(Packed)]
pub struct Group(u64);
#[derive(Packed)]
pub struct FileSize([u8; 12]);
#[derive(Packed)]
pub struct LastUpdate([u8; 12]);
#[derive(Packed)]
pub struct Checksum([u8; 8]);
#[derive(Packed)]
#[repr(u8)]
pub enum TypeFlag {
NormalFile = 0x30,
HardLink = 0x31,
SymbolicLink = 0x32,
CharacterSpecial = 0x33,
BlockSpecial = 0x34,
Directory = 0x35,
Fifo = 0x36,
ContiguousFile = 0x37,
GlobalExtended = 0x67,
ExtendedHeader = 0x78,
}
#[derive(Packed)]
#[packed(value = b"ustar\x00")]
pub struct UStar;
#[derive(Packed)]
#[packed(value = b"00")]
pub struct Version;
#[derive(Packed)]
pub struct OwnerUserName([u8; 32]);
#[derive(Packed)]
pub struct OwnerGroupName([u8; 32]);
#[derive(Packed)]
pub struct DeviceMajorNumber([u8; 8]);
#[derive(Packed)]
pub struct DeviceMinorNumber([u8; 8]);
#[derive(Packed)]
pub struct FileNamePrefix([u8; 155]);
#[derive(Packed)]
#[packed(value = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")]
pub struct HeaderPadding;
#[derive(Packed)]
pub struct Header {
filename: FileName,
file_mode: FileMode,
owner: Owner,
group: Group,
file_size: FileSize,
last_update: LastUpdate,
checksum: Checksum,
type_flag: TypeFlag,
linked_file: FileName,
ustar: UStar,
version: Version,
user_name: OwnerUserName,
group_name: OwnerGroupName,
device_major_number: DeviceMajorNumber,
device_minor_number: DeviceMinorNumber,
filename_prefix: FileNamePrefix,
_padding: HeaderPadding,
}
impl FileSize {
fn to_size(&self) -> usize {
let mut size = 0;
for o in self.0.iter().copied() {
match o {
b'0'..=b'8' => {
size = (size * 8) + (o - b'0') as usize;
}
0x00 | 0x20 => {}
_ => {
panic!("unknown octal value: {:?}", o)
}
}
}
size
}
}