cabinet

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: May 5, 2026 License: MIT Imports: 18 Imported by: 0

README

go-cabinet: Pure Go Microsoft Cabinet File Reader & Writer

Go Reference Go Report Card Codecov

A pure Go library for reading, writing, and extracting Microsoft Cabinet (.cab) files - commonly used in Windows Update packages, driver distributions, and software installers.

See the Microsoft Cabinet File Format specification for details.

Installation

go get github.com/abemedia/go-cabinet

Usage

Reading / Extracting a Cabinet File
r, err := cabinet.OpenReader("archive.cab")
if err != nil {
  return err
}
defer r.Close()

for _, f := range r.Files {
    rc, err := f.Open()
    if err != nil {
      return err
    }

    out, err := os.Create(f.Name)
    if err != nil {
      return err
    }

    if _, err := io.Copy(out, rc); err != nil {
      return err
    }

    if err := out.Close(); err != nil {
      return err
    }

    if err := rc.Close(); err != nil {
      return err
    }
}

The Reader also implements fs.FS, so it works with fs.WalkDir, fs.ReadFile, and other standard library functions:

fs.WalkDir(r, ".", func(path string, d fs.DirEntry, err error) error {
    fmt.Println(path)
    return err
})
Writing a Cabinet File
f, err := os.Create("archive.cab")
if err != nil {
  return err
}
defer f.Close()

w := cabinet.NewWriter(f)

wr, err := w.Create("hello.txt")
if err != nil {
  return err
}

if _, err = wr.Write([]byte("hello world")); err != nil {
  return err
}

// Make sure to check the error on Close.
if err := w.Close(); err != nil {
  return err
}

If you want to pack any fs.FS in one go, use AddFS:

if err := w.AddFS(os.DirFS("./my-app-files")); err != nil {
  return err
}

You can also add any local file or directory using AddPath:

if err := w.AddPath("hello.txt", "./hello.txt"); err != nil {
  return err
}
if err := w.AddPath("", "./my-app-files"); err != nil {
  return err
}

See the package documentation for further examples.

Supported Compression Algorithms

  • None (e.g. uncompressed)
  • MS-Zip (default)

The LZX and Quantum compression methods are not yet implemented, however custom compressors/decompressors can be registered via RegisterCompressor / RegisterDecompressor.

Limitations

  • Single-cabinet only - multi-cabinet spanning (split archives across multiple .cab files) is not supported.
  • Reserved fields - the reader skips reserved header/folder/data fields and the writer does not produce them (post-processing tools such as SignTool add these).

Documentation

Overview

Package cabinet reads and writes Microsoft Cabinet (.cab) files.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrFormat is returned when the data is not a valid Cabinet file.
	ErrFormat = errors.New("cabinet: not a valid cabinet file")

	// ErrAlreadyOpen is returned by [File.Open] when another reader for the same folder is still open.
	ErrAlreadyOpen = errors.New("cabinet: folder already has an open reader")

	// ErrChecksum is returned when a data block fails its checksum.
	ErrChecksum = errors.New("cabinet: checksum error")
)
View Source
var ErrAlgorithm = errors.New("cabinet: unsupported compression algorithm")

ErrAlgorithm is returned when a folder uses a compression method that has no registered compressor or decompressor.

Functions

func RegisterCompressor

func RegisterCompressor(method Compression, comp Compressor)

RegisterCompressor registers a custom compressor for the given method. The built-in methods None and MSZip are registered by default.

func RegisterDecompressor

func RegisterDecompressor(method Compression, dcomp Decompressor)

RegisterDecompressor registers a custom decompressor for the given method. The built-in methods None and MSZip are registered by default.

Types

type Compression

type Compression uint16

Compression identifies the compression algorithm used in a Cabinet folder.

const (
	None    Compression = 0 // no compression
	MSZip   Compression = 1 // MS-ZIP compression
	Quantum Compression = 2 // Quantum compression; requires a third-party decompressor
	LZX     Compression = 3 // LZX compression; requires a third-party decompressor
)

Compression methods supported by the Cabinet format.

func (Compression) String

func (c Compression) String() string

String returns the human-readable name of the compression method.

type Compressor

type Compressor func(w io.Writer) (io.WriteCloser, error)

A Compressor returns a new compressing writer, writing to w. The WriteCloser's Close method must be used to flush pending data to w. If the returned writer implements `Flush() error`, it will be called after each block of input to ensure the compressed output for that block is complete before a data block boundary is written.

type Decompressor

type Decompressor func(r io.Reader) io.ReadCloser

A Decompressor returns a new decompressing reader, reading from r. The io.ReadCloser's Close method must be used to release associated resources.

type File

type File struct {
	FileHeader
	// contains filtered or unexported fields
}

A File is a single file in a Cabinet archive. The file information is in the embedded FileHeader. The file content can be accessed by calling File.Open.

func (*File) FolderIndex

func (f *File) FolderIndex() uint16

FolderIndex returns the index of the folder containing the file.

func (*File) OffsetInFolder

func (f *File) OffsetInFolder() uint32

OffsetInFolder returns the uncompressed byte offset of the file within its folder.

func (*File) Open

func (f *File) Open() (io.ReadCloser, error)

Open returns an io.ReadCloser that provides access to the file's contents. Only one file within the same folder may be open at a time; opening a second returns ErrAlreadyOpen.

func (*File) Size

func (f *File) Size() uint32

Size returns the uncompressed size in bytes.

type FileHeader

type FileHeader struct {
	// Name is the name of the file.
	Name string

	// Modified is the modification time of the file.
	Modified time.Time

	// ReadOnly indicates the file is read-only.
	ReadOnly bool

	// Hidden indicates the file is hidden.
	Hidden bool

	// System indicates the file is a system file.
	System bool

	// Archive indicates the file has the archive attribute set.
	Archive bool

	// Exec indicates the file should be run after extraction.
	Exec bool

	// NonUTF8 indicates that Name is not encoded in UTF-8.
	NonUTF8 bool
}

FileHeader describes a file within a Cabinet archive.

type ReadCloser

type ReadCloser struct {
	*Reader
	// contains filtered or unexported fields
}

A ReadCloser is a Reader that must be closed when no longer needed.

func OpenReader

func OpenReader(name string) (*ReadCloser, error)

OpenReader opens the named CAB file.

func (*ReadCloser) Close

func (rc *ReadCloser) Close() error

Close closes the Cabinet file, rendering it unusable for I/O.

type Reader

type Reader struct {
	// Files is the list of files in the archive.
	Files []*File

	// SkipChecksum disables data block checksum verification.
	SkipChecksum bool
	// contains filtered or unexported fields
}

A Reader serves content from a Cabinet archive.

Example
// Open a cabinet archive for reading.
r, err := cabinet.OpenReader("testdata/example.cab")
if err != nil {
	log.Fatal(err)
}
defer r.Close()

// Iterate through the files in the archive,
// printing some of their contents.
for _, f := range r.Files {
	fmt.Printf("Contents of %s:\n", f.Name)
	rc, err := f.Open()
	if err != nil {
		log.Fatal(err)
	}
	_, err = io.Copy(os.Stdout, rc)
	if err != nil {
		log.Fatal(err)
	}
	rc.Close()
	fmt.Println()
}
Output:
Contents of README.md:
This is an example cabinet file.

func NewReader

func NewReader(r io.ReaderAt) (*Reader, error)

NewReader creates a new Reader reading from r.

func (*Reader) Open

func (rd *Reader) Open(name string) (fs.File, error)

Open opens the named file in the archive, using the semantics of fs.FS.Open: paths are always slash-separated, with no leading slash or dot-dot elements.

For best performance, open files in the order they appear in [Reader.Files], as reading out of order may require restarting decompression from the beginning of the folder.

func (*Reader) RegisterDecompressor

func (rd *Reader) RegisterDecompressor(method Compression, dcomp Decompressor)

RegisterDecompressor registers or overrides a decompressor for the given method. If a decompressor for a given method is not found, Reader will default to looking up the decompressor at the package level.

type Writer

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

Writer implements a Cabinet file writer.

Nothing is written to the underlying io.WriteSeeker until Writer.Close is called.

Example
// Create a file to write our archive to.
f, err := os.CreateTemp("", "example-*.cab")
if err != nil {
	log.Fatal(err)
}
defer f.Close()

// Create a new cabinet archive.
w := cabinet.NewWriter(f)

// Add some files to the archive.
files := []struct {
	Name, Body string
}{
	{"readme.txt", "This archive contains some text files."},
	{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
	{"todo.txt", "Get animal handling licence.\nWrite more examples."},
}
for _, file := range files {
	f, err := w.Create(file.Name)
	if err != nil {
		log.Fatal(err)
	}
	_, err = f.Write([]byte(file.Body))
	if err != nil {
		log.Fatal(err)
	}
}

// Make sure to check the error on Close.
if err := w.Close(); err != nil {
	log.Fatal(err)
}

func NewWriter

func NewWriter(w io.WriteSeeker) *Writer

NewWriter returns a new Writer writing to w.

func (*Writer) AddFS

func (w *Writer) AddFS(fsys fs.FS) error

AddFS adds the files from `fsys` to the archive, walking the fs.FS directory tree and maintaining the directory structure.

func (*Writer) AddPath

func (w *Writer) AddPath(name, path string) error

AddPath adds a file or directory tree rooted at `path` to the archive. For directories, all files are added recursively with `name` as the path prefix.

func (*Writer) Close

func (w *Writer) Close() error

Close finalizes and writes the archive. It does not close the underlying writer.

func (*Writer) Create

func (w *Writer) Create(name string) (io.Writer, error)

Create adds a file to the archive with the given name and returns an io.Writer to which the file contents should be written. The file's contents must be written before the next call to Writer.Create, Writer.CreateHeader, or Writer.Close.

func (*Writer) CreateHeader

func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error)

CreateHeader adds a file to the archive using the provided FileHeader for the file metadata.

This returns an io.Writer to which the file contents should be written. The file's contents must be written before the next call to Writer.Create, Writer.CreateHeader, or Writer.Close.

func (*Writer) FlushFolder

func (w *Writer) FlushFolder()

FlushFolder starts a new folder. It is a no-op if the current folder is empty.

func (*Writer) RegisterCompressor

func (w *Writer) RegisterCompressor(method Compression, comp Compressor)

RegisterCompressor registers or overrides a compressor for the given method. If a compressor for a given method is not found, Writer will default to looking up the compressor at the package level.

Example
// Override the default MS-ZIP compressor with a higher compression level.

// Create a file to write our archive to.
f, err := os.CreateTemp("", "example-*.cab")
if err != nil {
	log.Fatal(err)
}
defer f.Close()

// Create a new cabinet archive.
w := cabinet.NewWriter(f)

// Register a custom MS-ZIP compressor.
w.RegisterCompressor(cabinet.MSZip, func(out io.Writer) (io.WriteCloser, error) {
	return mszip.NewWriter(out, mszip.BestCompression)
})

// Proceed to add files to w.

func (*Writer) SetCompression

func (w *Writer) SetCompression(c Compression)

SetCompression sets the compression method for subsequently added files. If the current folder already contains files, it starts a new folder.

Directories

Path Synopsis
Package mszip implements the MS-ZIP compression format as defined by [MS-MCI]: Microsoft ZIP (MSZIP) Compression and Decompression Data Structure https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-mci/27f0a9bf-9567-4e40-ad66-6ae9ab9d2786
Package mszip implements the MS-ZIP compression format as defined by [MS-MCI]: Microsoft ZIP (MSZIP) Compression and Decompression Data Structure https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-mci/27f0a9bf-9567-4e40-ad66-6ae9ab9d2786

Jump to

Keyboard shortcuts

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