Read and write tar archives — the middle layer of a .deb.
Inside a .deb, both control.tar.* and data.tar.* are tar archives (once
decompressed). Entries are Debkit.Tar.Entry structs — the same struct flows
through read/1 and write/1, so an archive round-trips. Build entries with
the file/3 and dir/2 constructors:
tar = Debkit.Tar.write!([
Debkit.Tar.dir("./usr/", 0o755),
Debkit.Tar.dir("./usr/bin/"),
Debkit.Tar.file("./usr/bin/hello", "#!/bin/sh\n", 0o755)
])Directory entries (ustar typeflag 5) are what a .deb's data.tar
conventionally lists for each parent directory; emit them explicitly, in order,
before the files they contain.
Files and directories only
read/1 returns regular-file and directory entries. Symlinks, hardlinks, and
device nodes are skipped — .deb control/data tars don't use them.
Determinism
write/1 emits ustar entries with mtime 0, uid 0, and gid 0, and stores
names verbatim, so equal input yields equal output — the basis for reproducible
packages.
Summary
Functions
Builds a directory Debkit.Tar.Entry.
Builds a regular-file Debkit.Tar.Entry.
Reads a tar archive into Debkit.Tar.Entry structs, in archive order.
Like read/1 but returns the entries directly, raising Debkit.Error on failure.
Writes Debkit.Tar.Entry structs to a deterministic ustar archive, in order.
Like write/1 but returns the bytes directly, raising Debkit.Error on failure.
Functions
@spec dir(String.t(), non_neg_integer()) :: Debkit.Tar.Entry.t()
Builds a directory Debkit.Tar.Entry.
Keep the trailing / in name — it's stored verbatim.
Examples
iex> Debkit.Tar.dir("./usr/bin/")
#Debkit.Tar.Entry<dir "./usr/bin/" 0o755>
@spec file(String.t(), binary(), non_neg_integer()) :: Debkit.Tar.Entry.t()
Builds a regular-file Debkit.Tar.Entry.
Examples
iex> Debkit.Tar.file("./control", "Package: hello\n")
#Debkit.Tar.Entry<file "./control" 0o644 15B>
@spec read(binary()) :: {:ok, [Debkit.Tar.Entry.t()]} | {:error, Debkit.error()}
Reads a tar archive into Debkit.Tar.Entry structs, in archive order.
Returns regular-file and directory entries; other entry types are skipped.
Returns {:error, :corrupt} if binary is not a well-formed tar archive.
Examples
iex> {:ok, [entry]} = Debkit.Tar.read(Debkit.Tar.write!([Debkit.Tar.file("./x", "hi")]))
iex> entry
#Debkit.Tar.Entry<file "./x" 0o644 2B>
@spec read!(binary()) :: [Debkit.Tar.Entry.t()]
Like read/1 but returns the entries directly, raising Debkit.Error on failure.
@spec write([Debkit.Tar.Entry.t()]) :: {:ok, binary()} | {:error, Debkit.error()}
Writes Debkit.Tar.Entry structs to a deterministic ustar archive, in order.
An entry's mode of nil is resolved to the default for its type (0o644 for
a file, 0o755 for a directory). Use file/3 and dir/2 to build entries
conveniently.
Examples
iex> {:ok, tar} = Debkit.Tar.write([Debkit.Tar.file("./control", "Package: hello\n")])
iex> is_binary(tar)
true
@spec write!([Debkit.Tar.Entry.t()]) :: binary()
Like write/1 but returns the bytes directly, raising Debkit.Error on failure.