silk.compress.tar.
Read and write tar archives held in memory.
When to use#
Use a TarReader to unpack a .tar file, or a .tar.gz file after you decode it with
silk.compress.inflate. Open the archive with TarReader.open and call TarReader.next
until it returns none. Use a TarWriter to make one: add members with TarWriter.file and
TarWriter.directory, then take the bytes with TarWriter.finish, and compress them with
silk.compress.deflate for a .tar.gz. This module does not compress or decompress.
Details#
The reader understands ustar, GNU, and pax archives: the ustar prefix field, GNU long names,
pax path records, and base-256 sizes. It does not allocate, and every member it returns
borrows the archive. Each header's checksum is checked. A member's path is checked before the
member is returned: an absolute path, a backslash, a NUL, or a .. segment refuses the archive, so a
caller that joins a path to a directory and creates no links never writes outside it. Empty and
. segments are allowed, because tar -C dir . writes paths such as ./include/x.h.
The writer writes ustar headers with fixed metadata: modification time 0, owner and group 0
with no names, and mode 644, or 755 for an executable file or a directory. A path longer than
100 bytes goes in the ustar prefix field when it splits at a /, else in a pax path
record. The same members in the same order give the same bytes on every host, so a checksum of
the archive names its content.
Errors#
TarErrorfromTarReader.nextwhen a header is corrupt, the archive ends inside a member, or a path is unsafe.TarErrorfromTarWriter.fileandTarWriter.directorywhen a path is one the reader would refuse: empty, absolute, ending in/, or with a backslash, a NUL or a..segment.
Examples#
import silk.compress.tar {TarError, TarKind, TarMember, TarReader}
import silk.core.option {Option}
import silk.core.result {Result}
// Counts the regular files of `archive`, or answers -1 when it is not a valid tar archive.
fn files(archive: &[u8]) -> i32 {
let mut reader = TarReader.open(archive)
let mut count = 0
while true {
match TarReader.next(&mut reader) {
Result<Option<TarMember>, TarError>.Success {value} => match move value {
Option<TarMember>.Some {value: member} => { if member.kind == TarKind.File { count = count + 1 } }
Option<TarMember>.None => { return count }
}
Result<Option<TarMember>, TarError>.Failure {..} => { return -1 }
}
}
return count
}
pub fn main() -> i32 {
let empty: [u8; 1024] = [0; 1024]
return files(&empty)
}Write an archive and read it back#
import silk.collections.bytes {Bytes}
import silk.compress.tar {TarError, TarMember, TarReader, TarWriter}
import silk.core.effect {Effect}
import silk.core.option {Option}
import silk.core.result {Result}
import silk.mem.allocator {Allocator, OutOfMemoryError}
effect fn archive() -> Bytes ! TarError | OutOfMemoryError ? &mut Allocator {
let mut writer = TarWriter.make()
run TarWriter.directory(&mut writer, b"app/bin")
run TarWriter.file(&mut writer, b"app/bin/run", b"#!/bin/sh\n", true)
run TarWriter.file(&mut writer, b"app/README", b"hello\n", false)
return run TarWriter.finish(move writer)
}
pub effect fn main() -> i32 ! TarError | OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let bytes = run archive() |> Effect.provideMut<Allocator>(&mut allocator)
let mut reader = TarReader.open(Bytes.asSlice(&bytes))
let mut members = 0
while true {
match TarReader.next(&mut reader) {
Result<Option<TarMember>, TarError>.Success {value} => match move value {
Option<TarMember>.Some {..} => { members = members + 1 }
Option<TarMember>.None => { break }
}
Result<Option<TarMember>, TarError>.Failure {..} => { return 1 }
}
}
return if members == 3 { 0 } else { 2 }
}Import as TarReader with import silk.compress.tar { TarReader }.
Public declarations: 5.
TarError#
pub enum TarErrorThe reason a tar archive was refused.
Truncated#
TruncatedThe archive ends inside a header or inside a member's content.
BadHeader#
BadHeaderA header's checksum does not match its bytes, or a number field holds something other than an octal or base-256 number.
UnsafePath#
UnsafePathA member path is absolute, holds a backslash or a NUL, or has a .. segment.
Implementation TarError#
impl TarErrorAssociated function describe#
pub fn describe(error: TarError) -> string<'static>Returns a short sentence that describes error, such as "the tar archive is truncated".
Implementation Debug for TarError#
impl Debug for TarErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as Truncated.
TarKind#
pub enum TarKindWhat a tar member is.
File#
FileA regular file. Its bytes are the member's content.
Directory#
DirectoryA directory. Its name has no trailing /, and it has no content.
SymbolicLink#
SymbolicLinkA symbolic link to link, which the reader does not check. A target longer than the
100-byte header field comes back cut to that field.
HardLink#
HardLinkA hard link to the earlier member link, which the reader does not check. A target longer
than the 100-byte header field comes back cut to that field.
Other#
OtherA device, a FIFO, or a type this module does not know.
TarMember#
pub struct TarMember<'a>One member of a tar archive: its checked path, its kind, and its content.
Details#
The member's path is prefix/name, name alone when prefix is empty, or prefix alone when
name is empty, as ustar writes a long directory path. A GNU long name or a
pax path record comes back as name with an empty prefix. The slices borrow the archive
given to TarReader.open.
Field prefix#
pub prefix: &'a [u8]The ustar prefix field, or empty.
Field name#
pub name: &'a [u8]The rest of the path, without the trailing / a directory may carry.
Field kind#
pub kind: TarKindWhether the member is a file, a directory, a link, or something else.
Field executable#
pub executable: boolWhether the member is a file whose mode has an execute permission bit.
Field link#
pub link: &'a [u8]The target of a symbolic or hard link from the header's 100-byte link field, or empty. GNU
long link names and pax linkpath records are not read.
Field content#
pub content: &'a [u8]The bytes of a regular file. It is empty for a directory or a link.
TarReader#
pub struct TarReader<'a>A position in a tar archive held in memory.
When to use#
Make one with TarReader.open and call TarReader.next until it returns none.
Details#
The reader does not allocate and borrows the archive. It stops at the first all-zero block or at the end of the bytes. After a failure, discard the reader.
Implementation TarReader<'a>#
impl<'a> TarReader<'a>Associated function open#
pub fn open(archive: &'a [u8]) -> TarReader<'a>Returns a reader at the start of archive.
Method next#
pub fn next(self: &mut TarReader<'a>) -> Result<Option<TarMember<'a>>, TarError>Returns the next member in archive order, or none at the end-of-archive blocks or the end of the bytes. GNU long-name and pax headers are read as part of the member they name.
Errors#
TarErrorwithTruncatedwhen the archive ends inside a header or a member,BadHeaderwhen a checksum or number is invalid, andUnsafePathwhen the member's path is absolute, holds a backslash or a NUL, or has a..segment.
TarWriter#
pub struct TarWriterA tar archive being written in memory, one member at a time.
When to use#
Make one with TarWriter.make, add members with TarWriter.file and
TarWriter.directory in the order the archive should list them, and take the archive with
TarWriter.finish.
Details#
The writer writes ustar headers with fixed metadata: modification time zero, owner and group
zero with empty names, and mode 644 for a file, 755 for an executable file or a directory. The
same members in the same order therefore always give the same bytes, on any host. A path
longer than the 100-byte name field is split into the ustar prefix and name at a / when it
fits, and is otherwise written in a pax path record. A file of 8 GiB or more records its size
in base-256. TarReader reads every archive the writer makes.
Implementation TarWriter#
impl TarWriterAssociated function make#
pub fn make() -> TarWriterReturns a writer with no members.
Method file#
pub effect fn file(self: &mut Self, path: &[u8], content: &[u8], executable: bool) -> ()
! TarError | OutOfMemoryError
? &mut AllocatorAdds the regular file path holding content, with mode 755 when executable and 644
otherwise.
Errors#
TarErrorwithUnsafePathwhenpathis empty, absolute, ends in/, or holds a backslash, a NUL, or a..segment. Nothing is added.OutOfMemoryErrorwhen the archive cannot grow.
Method directory#
pub effect fn directory(self: &mut Self, path: &[u8]) -> ()
! TarError | OutOfMemoryError
? &mut AllocatorAdds the directory path, with mode 755. Write it without a trailing /.
Errors#
TarErrorwithUnsafePathwhenpathis empty, absolute, ends in/, or holds a backslash, a NUL, or a..segment. Nothing is added.OutOfMemoryErrorwhen the archive cannot grow.
Method finish#
pub effect fn finish(self: TarWriter) -> Bytes
! OutOfMemoryError
? &mut AllocatorEnds the archive with its two zero blocks and returns its bytes.
Errors#
OutOfMemoryErrorwhen the archive cannot grow.
Category: Data formats.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.