Help us shape the future of Go! Take the 2026 Go Developer Survey

archive

package
v0.2.47 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package archive reads the headers of RAR (4.x and 5.x) and 7z archives and maps each inner file onto byte ranges (extents) of the archive volumes, so that store-mode members can be streamed (with native decryption when needed) out of the volumes without extracting anything.

Every volume is accessed through an io.ReaderAt whose reads may be expensive (each one can trigger a network fetch), so the parsers read only header bytes, in small exact-size chunks, and never touch file data.

Index

Constants

View Source
const SignatureLen = 8

SignatureLen is the number of leading bytes Detect needs to recognise every supported format.

Variables

View Source
var (
	ErrNotArchive       = errors.New("archive: not a recognised archive")
	ErrCompressed       = errors.New("archive: compressed (not store mode)")
	ErrEncrypted        = errors.New("archive: encrypted")
	ErrPasswordRequired = fmt.Errorf("archive: password required: %w", ErrEncrypted)
	ErrBadPassword      = fmt.Errorf("archive: incorrect password: %w", ErrEncrypted)
	ErrSolid            = errors.New("archive: solid archive")
	ErrUnsupported      = errors.New("archive: unsupported archive feature")
	ErrCorrupt          = errors.New("archive: corrupt or truncated header")
	ErrMissingVolume    = errors.New("archive: missing volume")
)

Sentinel errors. Errors returned by this package wrap one of these (via fmt.Errorf with %w) so callers can test them with errors.Is.

Functions

This section is empty.

Types

type Encryption added in v0.2.4

type Encryption struct {
	Key    []byte `json:"-"`
	IV     [aes.BlockSize]byte
	Offset int64
	Size   int64
}

Encryption describes the CBC-encrypted logical stream formed by a member's Extents. Size excludes block padding; Offset locates this member within that plaintext stream (a 7z folder can contain several members). Keys are ephemeral parser results and must not be persisted or included in diagnostic JSON. CBC provides confidentiality, not authentication: archive password checks and checksums must be validated separately by the format reader.

type Extent

type Extent struct {
	Volume int   // index into the volumes slice passed in
	Offset int64 // byte offset inside that volume
	Size   int64
}

Extent is a contiguous byte range inside one volume.

func SplitExtents

func SplitExtents(vols []Volume, exts []Extent) ([]Extent, error)

SplitExtents maps extents expressed as offsets in the concatenated stream (Volume must be 0, as returned by List7z over a MultiReader) onto extents of the individual volumes passed to NewMultiReader. Volumes with Size <= 0 keep their index but never receive extents.

type Format

type Format int

Format identifies an archive container format.

const (
	FormatUnknown Format = iota
	FormatRAR4
	FormatRAR5
	Format7z
)

Known formats.

func Detect

func Detect(head []byte) Format

Detect sniffs the signature at the start of head. It needs at most SignatureLen bytes.

func (Format) String

func (f Format) String() string

String returns "unknown", "rar4", "rar5" or "7z".

type Member

type Member struct {
	Name       string
	Size       int64 // unpacked size
	Stored     bool
	Encrypted  bool
	Solid      bool
	Dir        bool
	Method     string      // human-readable, e.g. "store", "rar5:m3", "lzma", "copy", "aes"
	Extents    []Extent    // in order; ciphertext ranges when Encryption is non-nil
	Encryption *Encryption // ephemeral decryption metadata; nil for plain/unsupported encryption
}

Member is one inner file mapped onto volume byte ranges.

func ComposeRAR

func ComposeRAR(vols []*RARVolume) ([]Member, error)

ComposeRAR joins per-volume listings (vols in volume order) into members. A file that starts in volume i and continues (SplitAfter) is followed into volume i+1, whose first file header must be the continuation (SplitBefore, same name); otherwise the result wraps ErrMissingVolume (the set ends early or a volume number is skipped) or ErrCorrupt.

Extents of stored members cover exactly the member bytes; extents of other members cover their packed (compressed and/or encrypted) data.

func List7z

func List7z(r io.ReaderAt, size int64) ([]Member, error)

List7z parses a 7z archive. r is the full archive (for a multi-volume .7z.001/.002… set pass the concatenation, e.g. a MultiReader; SplitExtents maps the result back onto the individual volumes). Extents use Volume 0 and offsets in r. Only stored (Copy-only) members have extents.

func List7zWithPassword added in v0.2.4

func List7zWithPassword(ctx context.Context, r io.ReaderAt, size int64, password string) ([]Member, error)

List7zWithPassword lists an archive and prepares seekable decryption for stored encrypted members. Keys live only with the returned member plans.

func ListRAR

func ListRAR(vols []Volume) ([]Member, error)

ListRAR reads every volume (in order) with ReadRARVolume and composes the members. Extent volume indexes refer to vols.

func (*Member) Streamable

func (m *Member) Streamable() error

Streamable returns nil if the member can be read directly from its extents, else an error wrapping ErrEncrypted, ErrSolid or ErrCompressed (checked in that order), or ErrUnsupported for a directory.

type MemberReader

type MemberReader struct {
	// contains filtered or unexported fields
}

MemberReader is an io.ReaderAt over a stored member's bytes.

func NewMemberReader

func NewMemberReader(m Member, vols []Volume) (*MemberReader, error)

NewMemberReader returns a reader over the bytes of a stored member. It fails with the Streamable error if the member cannot be read directly, and with ErrMissingVolume or ErrCorrupt if the extents do not fit vols.

func (*MemberReader) ReadAt

func (r *MemberReader) ReadAt(p []byte, off int64) (int, error)

ReadAt implements io.ReaderAt: it returns io.EOF when fewer than len(p) bytes are available at off.

func (*MemberReader) Size

func (r *MemberReader) Size() int64

Size returns the member size.

type MultiReader

type MultiReader struct {
	// contains filtered or unexported fields
}

MultiReader is an io.ReaderAt over the concatenation of volumes, e.g. a split .7z.001/.7z.002… set.

func NewMultiReader

func NewMultiReader(vols []Volume) *MultiReader

NewMultiReader returns a reader over the concatenation of vols in order. Volumes with Size <= 0 contribute no bytes.

func (*MultiReader) ReadAt

func (m *MultiReader) ReadAt(p []byte, off int64) (int, error)

ReadAt implements io.ReaderAt.

func (*MultiReader) Size

func (m *MultiReader) Size() int64

Size returns the total size of all volumes.

type RARFile

type RARFile struct {
	Name         string      // path inside the archive, '/'-separated
	HeaderOffset int64       // offset of the block header in this volume
	DataOffset   int64       // offset of this file's data in this volume
	PackedSize   int64       // bytes of this file's data stored in this volume
	UnpackedSize int64       // total unpacked size of the whole file (-1 if unknown)
	Method       int         // raw method: RAR4 0x30..0x35, RAR5 0..5
	Stored       bool        // RAR4 method 0x30 / RAR5 method 0
	Encryption   *Encryption `json:"-"`
	Encrypted    bool        // RAR4 LHD_PASSWORD, RAR5 file encryption extra record
	Solid        bool        // RAR4 LHD_SOLID / RAR5 compression-info solid bit
	Dir          bool
	SplitBefore  bool // continued from previous volume
	SplitAfter   bool // continues in next volume
	CRC32        uint32
	HasCRC       bool
}

RARFile is one file header found in one RAR volume.

func (*RARFile) MethodName added in v0.2.7

func (f *RARFile) MethodName(format Format) string

MethodName returns the human-readable compression method of a RAR file header.

type RAROptions

type RAROptions struct {
	// MaxFiles stops parsing after this many file headers (0 = no limit).
	MaxFiles      int
	Password      string `json:"-"`
	Context       context.Context
	PasswordCache *RARPasswordCache
}

RAROptions tunes ReadRARVolume.

type RARPasswordCache added in v0.2.4

type RARPasswordCache struct {
	// contains filtered or unexported fields
}

RARPasswordCache shares at most 16 derived keys across volume parses. Its zero value is usable; discard it with the archive plan.

type RARVolume

type RARVolume struct {
	Format       Format // FormatRAR4 or FormatRAR5
	MultiVolume  bool   // archive is part of a volume set
	First        bool   // first volume (also true for a single-volume archive)
	Number       int    // 0-based volume number when known, else -1
	Solid        bool   // archive-level solid flag
	NewNumbering bool   // RAR4 MHD_NEWNUMBERING (name.partNN.rar)
	Files        []RARFile
	DataStart    int64 // offset of the first byte after the main archive header
	EndFound     bool  // end-of-archive block was read
	MoreVolumes  bool  // end block says another volume follows (only meaningful if EndFound)
}

RARVolume is the parsed header listing of one volume.

func ReadRARVolume

func ReadRARVolume(r io.ReaderAt, size int64, opts *RAROptions) (*RARVolume, error)

ReadRARVolume parses the block headers of one RAR4 or RAR5 volume without reading file data. It stops at the end-of-archive block, at size, after a file header with SplitAfter set (nothing streamable follows it in this volume, so the end block is not read), or after opts.MaxFiles file headers. opts may be nil.

Encrypted headers require opts.Password; derived keys remain ephemeral. Unknown block types are skipped. Header CRCs are verified (ErrCorrupt on mismatch).

type Volume

type Volume struct {
	R    io.ReaderAt
	Size int64
}

Volume is one archive volume.

Directories

Path Synopsis
Package archivetest builds small RAR 4.x / RAR 5.0 archives (and Copy-mode 7z archives) for tests.
Package archivetest builds small RAR 4.x / RAR 5.0 archives (and Copy-mode 7z archives) for tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL