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

wim

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 8, 2026 License: MIT Imports: 19 Imported by: 2

README

go-wim

A native Go library for reading and writing Windows Imaging (.wim) files.

err := wim.CaptureDir(ctx, "tree", "out.wim", wim.ImageInfo{Name: "Image", Boot: true},
    wim.Options{Security: wim.UniformSecurity(sd)})

Writing walks a file tree, deduplicates streams by SHA-1, and emits the dentry tree, the security table, the blob table and the XML. Resources are stored uncompressed or LZX-compressed. The LZX codec is in lzx, works in both directions, and is usable on its own.

Reading presents an image as an fs.FS:

rd, err := wim.Open(f, size)   // or wim.OpenBytes(b)
im, err := rd.Boot()           // or rd.Image(1), rd.Images()
data, err := fs.ReadFile(im.FS(), "windows/system32/ntoskrnl.exe")

so anything already written against a filesystem — fs.WalkDir, fs.Stat, http.FS — works against a WIM unchanged. A read decodes only the chunks it touches, so listing an image costs its metadata rather than its contents, and pulling one file out of a large image costs that file. Paths are matched exactly, including case; wrap the fs.FS if you want Windows' own case-insensitive matching.

What this aims at is a correct, dependency-free implementation, not a fast one — no subprocess, no cgo, no imaging tool on the build host. It does not try to match wimlib's throughput, and Performance says plainly where that costs you.

Why

Go could read a WIM but not write one, and the LZX variant the format uses had no Go implementation in either direction. That variant is distinct from the cabinet LZX that Go and C libraries do implement: a fixed 32 KiB window, a different block-size encoding, per-chunk resets of the window, the Huffman trees and the recent-offset state, and an unconditional Intel E8 filter at a fixed magic size. A cabinet decoder misreads a WIM chunk from its first field on.

The per-chunk reset is the interesting property: it is what lets a reader decode any chunk of a resource without the ones before it, which is what makes a WIM randomly accessible.

Two things the format will let you get wrong

Both are legal per the format, pass verification under other tools, and produce an image Windows cannot use — one unreadable, one unmountable. This package refuses them instead.

Some readers take the metadata resource's compression from the header rather than from its own flag. They decode the metadata with the codec the header names, without consulting that resource's compressed bit. An image declaring LZX while storing its metadata raw is therefore well-formed and passes wimlib-imagex verify, and yet its metadata decodes to garbage in such a reader — the file list, and with it everything the image contains, becomes unreadable. That is not hypothetical: we shipped one, and it failed late and silently, with nothing in the image pointing at the cause. So a compressed image always compresses its metadata here, even when that costs bytes.

Every dentry needs a security ID. A capture from a POSIX filesystem has no NT security data to read, and the natural result is security_id = -1 throughout — which Windows rejects at mount. Options.Security therefore has no default, and a SecurityFunc returning nil for any file is an error rather than a way to skip it. What belongs in a descriptor is not this package's business; it treats them as opaque bytes and deduplicates them by content.

Compatibility

This writes the general-purpose WIM format: version 0x00010D00, non-solid, LZX-compressed or uncompressed, 32 KiB chunks — the same format install.wim and boot.wim have used across Windows releases. The header it emits is byte-for-byte identical to the one Microsoft's own imagex writes.

Deliberately not written, all optional: LZMS and solid resources (the .esd format), XPRESS, integrity tables, reparse points, hard links, short names, alternate data streams, extended attributes.

The reader accepts the same format and is refused by name outside it — XPRESS, LZMS, solid or spanned resources, and split WIMs each report what they are rather than failing obscurely. It does handle constructs the writer never emits, because real images carry them: alternate data streams are stepped over to find the next entry, a directory reparse point is listed but never followed, and a metadata resource whose reference count has been cleared is recognised as a superseded image rather than counted as a live one. That last one is not hypothetical — the Windows AIK's WinPE image declares one image and carries four metadata resources.

A damaged image stays inspectable. A file whose content the image does not actually hold is listed with everything else and fails only when opened, naming what is missing — rather than reading back as the empty file it would otherwise be indistinguishable from.

The reader is deliberately more permissive than the strictest consumers of the format. It takes each resource's storage from that resource's own flag rather than from the WIM header, which is the more literal reading of what is on disk — and which means it will read images that something else rejects, the metadata case above among them. A successful read here says the image parses, not that every other tool will accept it.

Verification status, stated precisely:

header and structures match Microsoft imagex's output measured
every resource verified and applied back by wimlib measured
parsed by go-winio's independent reader measured
a written image boots a Windows installation measured
read by DISM/WIMGAPI inferred from format identity
reader byte-identical to wimlib on a real image measured — a Windows PE image, ~8,600 files, ~770 MB
reader round-trips this package's own captures measured — both codecs
reader handles alternate streams and reparse points measured on hand-built metadata only

Booting has been exercised on one Windows release and one architecture, so read that row as "this works" rather than "this works everywhere". The last row follows from emitting the documented version with the documented codec, and from the header matching imagex's byte for byte — but it has not been exercised directly, so it is listed as an inference rather than a result.

Read the alternate-streams row precisely: none of the images tested against carries a named alternate data stream or a reparse point, so those two branches are exercised by hand-built metadata rather than by a real capture. That is weaker evidence, and it is listed as what it is.

Performance

Speed is not a goal of this package, and the numbers below are not a competitive claim. What it sets out to be is a native implementation: correct, verified against wimlib, and usable from a Go program with no subprocess, no cgo, and no imaging tool on the build host. Matching a mature C implementation with a decade of optimisation behind it is not something it attempts, and where it falls short it falls short — the measurements are published so you can tell whether it fits your use, not to suggest it competes.

So: if you are extracting or capturing whole images in bulk and throughput is what matters, use wimlib. If you want a WIM read or written from Go without depending on it, this is that, and the numbers tell you what it costs you.

The gaps are understood rather than mysterious, and are recorded here because knowing which part is slow is worth more than a single figure — see the notes under each table.

Measured against wimlib on a Windows PE image of roughly 8,600 files and about 770 MB uncompressed, on a current multi-core desktop, with everything in cache so no disk is in the loop. Figures are rounded; treat them as the shape of the difference rather than as a benchmark to reproduce. Both sides were checked to be doing the same work: the image carries no integrity table, so wimverify decompresses and hashes every resource, and user ≈ real on its runs confirms it is single-threaded.

Decoding, resource for resource (one reused buffer, no per-file allocation, no hashing):

throughput
wimlib verify (decompress + SHA-1) ~570 MB/s 1 core
go-wim, resource by resource ~70 MB/s 1 core, roughly 8x slower
go-wim, through the fs.FS ~50 MB/s adds per-file allocation and the tree walk

Encoding the same tree, LZX, 32 KiB chunks:

wimlib go-wim
1 thread ~26 s ~26 s parity
all cores ~3.4 s ~8.8 s roughly 2.5x slower
output ~166 MB ~172 MB about 4% larger

Read those three rows as three different findings. Single-threaded compression is at parity — the LZX encoder is competitive. Parallel compression is not, and the arithmetic says why: solving for the serial fraction gives wimlib a little over a second of non-parallel work against go-wim's seven, so what limits the all-cores run is the path around the codec, not the codec. Capturing hashes every file and the write pass re-reads and re-hashes it; that is serial, and it is where the difference lives.

Decoding is the weakest column. A CPU profile puts about three quarters of the time in Huffman symbol decoding: the decoder walks a canonical code one bit at a time, so a nine-bit symbol costs nine iterations where a table-driven decoder costs one lookup. That shape was inherited from a setting where a decode table would have cost heap that was not available. Building the trees is not the cost — it is under 1% of runtime — so the usual objection to table-driven decoding does not apply here.

It is worth knowing the ceiling before anyone acts on that: the remaining quarter is match copying and the Intel E8 filter, so even an infinitely fast symbol decoder would only reach about 4x, and a realistic one lands in the low hundreds of MB/s. It would close most of this gap, not all of it.

Whether any of it matters depends on what you read. Whole-image extraction: about 770 MB takes some 16 seconds against wimlib's 1.5. Inspecting an image: it does not — dumping a registry hive out of one takes tens of milliseconds and listing an image rather less, because a read decodes only the chunks it touches.

Determinism

The same tree produces the same bytes. Timestamps come from the tree or from Options.Timestamp, the GUID is derived from the image's content rather than randomised, and chunk results are placed by index rather than as workers finish — so Options.Concurrency changes only how long a capture takes.

Testing

CI runs these on every push to main and every pull request:

go build ./...
go vet ./...
go test ./...          # round trips, fs.FS conformance, rejection of damaged images
golangci-lint run      # configured by .golangci.yml

Worth running locally as well, and not run by CI:

go test -race ./...    # the parallel compression path, and the reader's shared state

Coverage leans on implementations that share no code with this one, because a round trip only proves this package agrees with itself:

  • go-winio's WIM parser reads back what the writer produces — no external tool needed, so this runs anywhere.
  • wimlib, when wimcapture/wimapply/wimlib-imagex are on PATH, verifies every resource's SHA-1 and round-trips an image back to a tree. For the reader it is the differential oracle: every file extracted from a real image is byte-compared against wimlib's extraction of the same image. Tests skip cleanly without it.
  • The reader also runs the standard library's own fstest.TestFS conformance suite, and a set of deliberately damaged images — each corrupted in exactly one way, each asserting which kind of failure is reported rather than merely that one is.

Environment-gated sweeps, for bulk validation against images that cannot be committed. Each takes one or more directories, separated as PATH is (: on Unix). These run under go test:

variable what it does
GOWIM_WIM_CORPUS=<dir>[:<dir>…] reads every WIM found there; with wimlib on PATH, byte-compares every extracted file against wimlib's extraction of the same image, and without it only checks that each image parses
GOWIM_CORPUS=<dir> decodes every resource in a corpus and checks it against its recorded hash
GOWIM_LZX_CORPUS=<dir> measures the compressor's ratio and throughput against real files
GOWIM_LZX_BASELINE=<n> with the above, prints the delta against a previous run's total

And these gate benchmarks, so they need go test -bench, not go test alone:

variable what it does
GOWIM_CAPTURE_DIR=<dir> captures a tree, for comparison against another imaging tool
GOWIM_CAPTURE_THREADS=1 with the above, captures single-threaded
GOWIM_WIM_CORPUS=<dir>[:<dir>…] also drives the decode-throughput benchmarks

Compression

Roughly 4.5% larger than wimlib on the same input (41.1% of raw against 39.3%), at about 80% of its single-threaded speed, and parallel across cores. The difference is the parse strategy — greedy with lazy matching here, near-optimal in wimlib — not the framing: the Huffman trees are 1.67% of output, and deeper match search buys 0.14%. Closing it needs a real optimal parser.

Provenance

An independent reimplementation from the documented WIM format.

  • The MIT-licensed go-winio WIM reader (Copyright (c) Microsoft Corporation) was consulted as a format reference only; no go-winio source is incorporated. It is also a test dependency, used as an independent reader.
  • The LZX decoder, and the metadata parse the reader is built on, were ported from the author's own C implementations in pe-wimmf (reader/lzx.c, reader/wimcore.c). That project is Apache-2.0 and remains so; its sole copyright holder is the author of this package, who licenses these ports under MIT here. No third-party code is carried across.
  • wimlib is used only as a black-box oracle at test time. It is GPL, and is neither linked into nor distributed with this software.
  • Field values and structure layouts were measured from real images — wimlib's captures and Microsoft imagex's — rather than assumed.

No Microsoft binaries or Windows files are included in this repository.

License

Copyright © 2026 Nikita Radchenko

Licensed under the MIT License.

The library has no runtime dependencies; go-winio (MIT) is used by the tests only.

Disclaimer

go-wim is not affiliated with, endorsed by, or derived from the code of Microsoft. Windows is a trademark of Microsoft Corporation.

Documentation

Overview

Package wim reads and writes Windows Imaging (.wim) files.

Writing captures a file tree into a WIM: walking the tree, deduplicating streams by SHA-1, and emitting the dentry tree, the security table, the blob table, and the XML — with no external imaging tool involved. Resources are stored uncompressed or LZX-compressed; the codec lives in the lzx subpackage and is usable on its own.

Reading goes the other way and presents an image as an io/fs.FS:

rd, err := wim.Open(f, size)
im, err := rd.Boot()
data, err := fs.ReadFile(im.FS(), "windows/system32/ntoskrnl.exe")

so anything already written against a filesystem works against a WIM unchanged. Reads are served from the chunks they touch rather than by expanding the image, which is what makes listing one cheap and reading one file out of a large one cheaper still.

Two properties of the format are load-bearing and easy to get wrong, and this package refuses rather than lets you discover them at boot. A loader takes the metadata resource's compression from the WIM header rather than from that resource's own flag, so an image declaring a codec must compress its metadata too — the mismatch is legal, verifies clean under other tools, and produces an image Windows will not boot. And every dentry needs a security ID: an image whose dentries carry -1, which is what a POSIX capture naturally produces, is rejected at mount, which is why Options.Security has no default.

Both of those constrain what is written. The reader is deliberately more permissive: it takes each resource's storage from that resource's own flag, so it reads images a loader would refuse. A WIM this package reads is therefore not necessarily one Windows will boot — writing it here is what makes it that.

Index

Examples

Constants

View Source
const DefaultChunkSize = 32768

DefaultChunkSize is the uncompressed size of one chunk of a compressed resource, and the value every WIM of this version in the wild uses. It is also the LZX window, which is why the codec cannot code a larger one.

Variables

View Source
var (
	// ErrNotWIM reports input that is not a WIM: too short, or without the signature.
	ErrNotWIM = errors.New("wim: not a WIM file")
	// ErrUnsupported reports a well-formed WIM in a form this package does not read — another
	// codec, a solid or split image — as opposed to a damaged one.
	ErrUnsupported = errors.New("wim: unsupported WIM")
	// ErrCorrupt reports a WIM whose structure contradicts itself. Every bound this package
	// checks reports through it, so a caller can tell a damaged image from an unreadable one.
	ErrCorrupt = errors.New("wim: corrupt WIM")
)

The three error kinds a reader distinguishes. They are kept apart because they mean different things to a caller: the input is not a WIM at all, it is a WIM this package declines to read, or it is a WIM whose contents contradict themselves. Collapsing them would make "we cannot read this" indistinguishable from "this file is damaged".

View Source
var ErrChunkSize = errors.New("wim: ChunkSize must be a power of two no larger than the LZX window")

ErrChunkSize reports a chunk size the codec cannot code against.

It is refused because the failure is otherwise silent and looks like success: the LZX window is one chunk, so a chunk larger than the window is declined by the codec and stored raw, and a resource of those can still come out a few bytes shorter than its input — earning the compressed flag while holding almost entirely uncompressed data. The image is well formed, reads back correctly, and is neither compressed nor readable by anything expecting the standard chunk size.

View Source
var ErrNoSecurity = errors.New("wim: Options.Security is required (an image with no security table is rejected by Windows)")

ErrNoSecurity reports a capture with no security descriptors. An image whose dentries carry no security ID is what a POSIX filesystem naturally yields — there is no NT security data to read — and it is what Windows rejects at mount, so it is refused at capture time rather than discovered when the image is used.

Functions

func Capture

func Capture(ctx context.Context, dst io.WriteSeeker, src fs.FS, info ImageInfo, opts Options) error

Capture writes src to dst as a single-image WIM. It is NewWriter + AddImage + Close.

Example

Capture an in-memory tree, which is all a Writer needs — any fs.FS will do.

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"testing/fstest"

	"github.com/nradchenko/go-wim"
)

// A security descriptor is opaque to this package; a real caller supplies one its target
// Windows accepts. This is a minimal self-relative descriptor so the examples run.
var descriptor = []byte{
	0x01, 0x00, 0x00, 0x80,
	0x14, 0x00, 0x00, 0x00,
	0x20, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00,
	0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00,
}

func main() {
	src := fstest.MapFS{
		"boot.ini":            &fstest.MapFile{Data: []byte("[boot loader]\n")},
		"system32/kernel.dll": &fstest.MapFile{Data: []byte("MZ...")},
	}

	out, err := os.CreateTemp("", "example-*.wim")
	if err != nil {
		log.Fatal(err)
	}
	defer os.Remove(out.Name())
	defer out.Close()

	err = wim.Capture(context.Background(), out, src,
		wim.ImageInfo{Name: "Image"},
		wim.Options{
			Security: wim.UniformSecurity(descriptor),
			// Deduplicated by content, so one descriptor for every file costs one entry.
			Compression: wim.CompressLZX,
		},
	)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("captured")
}
Output:
captured

func CaptureDir

func CaptureDir(ctx context.Context, dir, out string, info ImageInfo, opts Options) error

CaptureDir captures the directory dir into a new WIM file at out, replacing it if it exists. A failed capture leaves no partial file behind.

Example

Capture a directory into a bootable single-image WIM.

package main

import (
	"context"
	"log"

	"github.com/nradchenko/go-wim"
)

// A security descriptor is opaque to this package; a real caller supplies one its target
// Windows accepts. This is a minimal self-relative descriptor so the examples run.
var descriptor = []byte{
	0x01, 0x00, 0x00, 0x80,
	0x14, 0x00, 0x00, 0x00,
	0x20, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00,
	0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00,
}

func main() {
	err := wim.CaptureDir(context.Background(), "tree", "out.wim",
		wim.ImageInfo{
			Name: "Image",
			Boot: true,
			Windows: &wim.WindowsInfo{
				Arch:    wim.ArchIntel,
				Version: wim.Version{Major: 5, Minor: 1, Build: 2600},
			},
		},
		wim.Options{Security: wim.UniformSecurity(descriptor)},
	)
	if err != nil {
		log.Fatal(err)
	}
}

Types

type Arch

type Arch int

Arch is the processor architecture recorded in <WINDOWS><ARCH>, using Windows' own PROCESSOR_ARCHITECTURE numbering.

const (
	ArchIntel Arch = 0 // x86
	ArchAMD64 Arch = 9 // x64
)

The architectures a WIM of this vintage records.

type Compression

type Compression int

Compression selects the codec a WIM's file resources are stored with.

const (
	// CompressLZX stores resources as LZX-compressed chunks. It is the codec WIMs of this
	// version carry, and the only one this package implements; XPRESS is not written.
	CompressLZX Compression = iota
	// CompressNone stores every resource uncompressed. Useful for tests and for inspecting
	// a capture; a whole PE image stored this way is legal but large.
	CompressNone
)

type Image added in v0.2.0

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

Image is one image inside a WIM: its identity, and its file tree.

The tree is parsed on first use rather than at Open: listing a WIM's images should not cost the decode of every image's metadata, and most callers want one image out of the WIM.

func (*Image) FS added in v0.2.0

func (im *Image) FS() fs.FS

FS presents the image's file tree as a read-only fs.FS, implementing fs.ReadDirFS and fs.StatFS as well, so a caller walks, stats and reads it exactly as it would any other filesystem.

Paths are matched exactly, including case. A WIM stores names in the case the capture saw, and Windows would match them case-insensitively; that is a property of the filesystem the image came from rather than of the format, and folding it in here would make Open succeed on paths this tree does not contain. A caller wanting Windows' own matching wraps this.

FS never fails: an image whose metadata cannot be parsed reports that from the first operation on it, rather than making every caller handle an error from a method that has nothing else to go wrong.

func (*Image) Index added in v0.2.0

func (im *Image) Index() int

Index returns the image's number within the WIM, counting from 1.

func (*Image) Info added in v0.2.0

func (im *Image) Info() ImageInfo

Info returns the image's identity as the WIM's XML records it.

type ImageInfo

type ImageInfo struct {
	// Name is the image's <NAME>. It is what wimlib and DISM show as the image's identity.
	Name string
	// Description is the image's optional <DESCRIPTION>.
	Description string
	// Boot marks this image bootable: it becomes the WIM's boot index, and the header's
	// boot-metadata resource points at this image's metadata. A loader reads the image the
	// boot index names.
	Boot bool
	// Windows, when non-nil, adds the <WINDOWS> block describing the Windows installation
	// the image holds.
	Windows *WindowsInfo
}

ImageInfo describes an image being captured.

type Options

type Options struct {
	// Compression is the codec for file resources. Defaults to CompressLZX.
	Compression Compression

	// ChunkSize is the uncompressed size of one chunk of a compressed resource. Defaults to
	// DefaultChunkSize, which is what real images carry; setting anything else is for tests.
	// It must be a power of two no larger than the LZX window, which is what the codec can
	// code a chunk against (see ErrChunkSize).
	ChunkSize int

	// Security assigns a security descriptor to each captured file. It has no default: see
	// ErrNoSecurity.
	Security SecurityFunc

	// Timestamp, when non-zero, is recorded as every dentry's creation, access, and write
	// time in place of the source tree's own times. Set it for a byte-reproducible capture
	// of a tree whose timestamps vary.
	Timestamp time.Time

	// GUID is the WIM's identifier. A zero GUID is derived from the image's own content,
	// keeping a capture reproducible; wimlib randomises it.
	GUID [16]byte

	// Concurrency bounds the chunk compressions in flight. Zero means one per CPU. Chunks are
	// independent by construction — each resets the window, the trees and the recent offsets —
	// so this is the cheapest speed available, and the output is byte-identical whatever the
	// value: results are placed by chunk index, never in completion order.
	Concurrency int
}

Options configure a Writer. Security is required; the rest default to LZX, 32768-byte chunks, and timestamps taken from the captured tree.

type Reader added in v0.2.0

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

Reader reads a WIM: its images, and through them their file trees.

A Reader is safe for concurrent use, and so must be the io.ReaderAt it was opened over — a tree walk fans out across files, and each read seeks independently.

func Open added in v0.2.0

func Open(r io.ReaderAt, size int64) (*Reader, error)

Open reads the WIM held in r, whose length is size.

r must support random access and be safe for concurrent use: the format is offset-indexed — the blob table, the metadata resource and every chunk of every file are found by offset — so reading one file never costs a scan of the image, and several may be read at once.

func OpenBytes added in v0.2.0

func OpenBytes(b []byte) (*Reader, error)

OpenBytes reads a WIM already held in memory. It is the convenience form of Open for a WIM pulled out of an archive or an image, where the bytes are in hand already.

func (*Reader) Boot added in v0.2.0

func (rd *Reader) Boot() (*Image, error)

Boot returns the image the header marks bootable, or the sole image of a WIM that marks none.

A bootable image is named twice — by index, and by a copy of its metadata resource header that a loader reads directly — and the two are cross-checked here. On disagreement the image is reported as corrupt rather than one of the two being preferred silently: the header's copy is what a loader would boot and the index is what every other tool would show, so the image is two different images depending on who reads it. Only Boot fails, so an image in that state can still be listed and inspected through Image.

func (*Reader) Image added in v0.2.0

func (rd *Reader) Image(i int) (*Image, error)

Image returns image i, numbered as the WIM numbers it: the first image is 1.

func (*Reader) Images added in v0.2.0

func (rd *Reader) Images() []ImageInfo

Images returns one entry per image, in the WIM's own order: element 0 is image 1.

type SecurityFunc

type SecurityFunc func(path string, d fs.DirEntry) []byte

SecurityFunc returns the self-relative NT security descriptor to record for the captured file at path — slash-separated and relative to the capture root, with "." for the root itself. It must return one for every file: a dentry left without a security ID carries -1, which is the wimlib-on-Linux shape Windows rejects at mount, and a capture holding even a few of them verifies clean and fails at boot. Returning nil is therefore an error, not a way to skip a file.

The writer deduplicates descriptors by content, so returning the same bytes for every file costs exactly one security-table entry.

Example

Give different files different descriptors.

package main

import (
	"io/fs"

	"github.com/nradchenko/go-wim"
)

// A security descriptor is opaque to this package; a real caller supplies one its target
// Windows accepts. This is a minimal self-relative descriptor so the examples run.
var descriptor = []byte{
	0x01, 0x00, 0x00, 0x80,
	0x14, 0x00, 0x00, 0x00,
	0x20, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00,
	0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00,
}

func main() {
	security := func(_ string, d fs.DirEntry) []byte {
		if d.IsDir() {
			return directoryDescriptor
		}
		return descriptor
	}
	_ = wim.Options{Security: security}
}

var directoryDescriptor = descriptor

func UniformSecurity

func UniformSecurity(sd []byte) SecurityFunc

UniformSecurity returns a SecurityFunc that assigns sd to every captured file — the shape a PE build wants, where one system descriptor covers the whole image.

type Version

type Version struct {
	Major   int
	Minor   int
	Build   int
	SPBuild int
	SPLevel int
}

Version is a Windows build version, recorded in <WINDOWS><VERSION>, in the four parts that element carries: 5.1.2600.5512 is one such version.

type WindowsInfo

type WindowsInfo struct {
	Arch       Arch
	Version    Version
	SystemRoot string
}

WindowsInfo is an image's <WINDOWS> block. Zero fields are omitted from the XML.

type Writer

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

Writer builds a WIM into dst. Resources are written as they are captured and the header, blob table, and XML are finalised by Close, so dst must be seekable — the header carries offsets that are only known once every image has been written.

A Writer holds one blob table across every image it writes, which is the whole reason it is a type rather than a function: identical files, within an image or across images, are stored once and referenced by SHA-1.

func NewWriter

func NewWriter(dst io.WriteSeeker, opts Options) *Writer

NewWriter returns a Writer that builds a WIM into dst. Nothing is written until the first AddImage; Close must be called to produce a valid file.

func (*Writer) AddImage

func (w *Writer) AddImage(ctx context.Context, src fs.FS, info ImageInfo) error

AddImage captures the tree rooted at src as a new image described by info, appending its file resources and metadata resource to the WIM. Files are deduplicated against everything already written. Symlinks in src are an error rather than being followed, so a capture cannot silently duplicate a file or escape its root.

func (*Writer) Close

func (w *Writer) Close() error

Close writes the blob table and the XML data, rewrites the header now that their offsets are known, and releases the Writer. It does not close dst.

Directories

Path Synopsis
Package lzx implements the WIM variant of the LZX compression algorithm, in both directions: a compressor for building WIMs and a decompressor the round-trip tests check it against.
Package lzx implements the WIM variant of the LZX compression algorithm, in both directions: a compressor for building WIMs and a decompressor the round-trip tests check it against.

Jump to

Keyboard shortcuts

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