POSIX ustar tar archive header encoding and decoding.
A tar archive is a sequence of fixed-size 512-byte blocks. Each member (file, directory, link, ...) starts with one 512-byte ustar header block, optionally followed by the member's data rounded up to a multiple of 512 bytes:
offset size field ------ ---- ----------------------------------------- 0 100 name 100 8 mode (octal ASCII) 108 8 uid (octal ASCII) 116 8 gid (octal ASCII) 124 12 size (octal ASCII) 136 12 mtime (octal ASCII) 148 8 checksum (octal ASCII) 156 1 typeflag 157 100 linkname 257 6 magic ("ustar" + NUL) 263 2 version 265 32 uname 297 32 gname 329 8 devmajor 337 8 devminor 345 155 prefix 500 12 padding ------ ---- ----------------------------------------- 512 total
All multi-character fields are ASCII, left-justified and NUL-padded to
their field width. mode, uid, gid, size, mtime and checksum are
additionally numeric: an octal number rendered as ASCII digits, zero-padded
and NUL-terminated within the field. This coder refines those six fields to
and from number, and trims the NUL padding from every other string field
transparently.
devmajor and devminor only carry meaning for the character-special and
block-special typeflags; they are decoded as trimmed strings rather than
numbers since ustar leaves their content undefined for every other
typeflag, including the TAR_TYPEFLAG values this coder targets.
This package covers a single 512-byte header block only. It does not walk
a multi-member archive, does not size or read the data blocks that follow
a header, and does not compute or verify the checksum field — all of
that is left to the caller for now.
Round-trip a regular-file header
Round-trip a regular-file header
import { assertEquals } from "@std/assert"; import { TAR_BLOCK_SIZE, TAR_TYPEFLAG, USTAR_MAGIC, USTAR_VERSION, ustarHeader, } from "@binstruct/tar"; const coder = ustarHeader(); const header = { name: "hello.txt", mode: 0o644, uid: 1000, gid: 1000, size: 5, mtime: 1_700_000_000, checksum: 0, typeflag: TAR_TYPEFLAG.regularFile, linkname: "", magic: USTAR_MAGIC, version: USTAR_VERSION, uname: "user", gname: "user", devmajor: "", devminor: "", prefix: "", padding: new Uint8Array(12), }; const buffer = new Uint8Array(TAR_BLOCK_SIZE); const written = coder.encode(header, buffer); const [decoded, read] = coder.decode(buffer); assertEquals(written, TAR_BLOCK_SIZE); assertEquals(read, TAR_BLOCK_SIZE); assertEquals(decoded, header);