Debkit.Tar (Debkit v0.2.0)

Copy Markdown View Source

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

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

dir(name, mode \\ 493)

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>

file(name, contents, mode \\ 420)

Builds a regular-file Debkit.Tar.Entry.

Examples

iex> Debkit.Tar.file("./control", "Package: hello\n")
#Debkit.Tar.Entry<file "./control" 0o644 15B>

read(binary)

@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>

read!(binary)

@spec read!(binary()) :: [Debkit.Tar.Entry.t()]

Like read/1 but returns the entries directly, raising Debkit.Error on failure.

write(entries)

@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

write!(entries)

@spec write!([Debkit.Tar.Entry.t()]) :: binary()

Like write/1 but returns the bytes directly, raising Debkit.Error on failure.