mirror of
https://github.com/openfaas/faas.git
synced 2025-06-28 01:33:24 +00:00
Vendoring with Glide and delete function handler
This commit is contained in:
138
gateway/vendor/github.com/Microsoft/go-winio/wim/decompress.go
generated
vendored
Normal file
138
gateway/vendor/github.com/Microsoft/go-winio/wim/decompress.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
package wim
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/Microsoft/go-winio/wim/lzx"
|
||||
)
|
||||
|
||||
const chunkSize = 32768 // Compressed resource chunk size
|
||||
|
||||
type compressedReader struct {
|
||||
r *io.SectionReader
|
||||
d io.ReadCloser
|
||||
chunks []int64
|
||||
curChunk int
|
||||
originalSize int64
|
||||
}
|
||||
|
||||
func newCompressedReader(r *io.SectionReader, originalSize int64, offset int64) (*compressedReader, error) {
|
||||
nchunks := (originalSize + chunkSize - 1) / chunkSize
|
||||
var base int64
|
||||
chunks := make([]int64, nchunks)
|
||||
if originalSize <= 0xffffffff {
|
||||
// 32-bit chunk offsets
|
||||
base = (nchunks - 1) * 4
|
||||
chunks32 := make([]uint32, nchunks-1)
|
||||
err := binary.Read(r, binary.LittleEndian, chunks32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, n := range chunks32 {
|
||||
chunks[i+1] = int64(n)
|
||||
}
|
||||
|
||||
} else {
|
||||
// 64-bit chunk offsets
|
||||
base = (nchunks - 1) * 8
|
||||
err := binary.Read(r, binary.LittleEndian, chunks[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for i, c := range chunks {
|
||||
chunks[i] = c + base
|
||||
}
|
||||
|
||||
cr := &compressedReader{
|
||||
r: r,
|
||||
chunks: chunks,
|
||||
originalSize: originalSize,
|
||||
}
|
||||
|
||||
err := cr.reset(int(offset / chunkSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
suboff := offset % chunkSize
|
||||
if suboff != 0 {
|
||||
_, err := io.CopyN(ioutil.Discard, cr.d, suboff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
func (r *compressedReader) chunkOffset(n int) int64 {
|
||||
if n == len(r.chunks) {
|
||||
return r.r.Size()
|
||||
}
|
||||
return r.chunks[n]
|
||||
}
|
||||
|
||||
func (r *compressedReader) chunkSize(n int) int {
|
||||
return int(r.chunkOffset(n+1) - r.chunkOffset(n))
|
||||
}
|
||||
|
||||
func (r *compressedReader) uncompressedSize(n int) int {
|
||||
if n < len(r.chunks)-1 {
|
||||
return chunkSize
|
||||
}
|
||||
size := int(r.originalSize % chunkSize)
|
||||
if size == 0 {
|
||||
size = chunkSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (r *compressedReader) reset(n int) error {
|
||||
if n >= len(r.chunks) {
|
||||
return io.EOF
|
||||
}
|
||||
if r.d != nil {
|
||||
r.d.Close()
|
||||
}
|
||||
r.curChunk = n
|
||||
size := r.chunkSize(n)
|
||||
uncompressedSize := r.uncompressedSize(n)
|
||||
section := io.NewSectionReader(r.r, r.chunkOffset(n), int64(size))
|
||||
if size != uncompressedSize {
|
||||
d, err := lzx.NewReader(section, uncompressedSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.d = d
|
||||
} else {
|
||||
r.d = ioutil.NopCloser(section)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *compressedReader) Read(b []byte) (int, error) {
|
||||
for {
|
||||
n, err := r.d.Read(b)
|
||||
if err != io.EOF {
|
||||
return n, err
|
||||
}
|
||||
|
||||
err = r.reset(r.curChunk + 1)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *compressedReader) Close() error {
|
||||
var err error
|
||||
if r.d != nil {
|
||||
err = r.d.Close()
|
||||
r.d = nil
|
||||
}
|
||||
return err
|
||||
}
|
606
gateway/vendor/github.com/Microsoft/go-winio/wim/lzx/lzx.go
generated
vendored
Normal file
606
gateway/vendor/github.com/Microsoft/go-winio/wim/lzx/lzx.go
generated
vendored
Normal file
@ -0,0 +1,606 @@
|
||||
// Package lzx implements a decompressor for the the WIM variant of the
|
||||
// LZX compression algorithm.
|
||||
//
|
||||
// The LZX algorithm is an earlier variant of LZX DELTA, which is documented
|
||||
// at https://msdn.microsoft.com/en-us/library/cc483133(v=exchg.80).aspx.
|
||||
package lzx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
maincodecount = 496
|
||||
maincodesplit = 256
|
||||
lencodecount = 249
|
||||
lenshift = 9
|
||||
codemask = 0x1ff
|
||||
tablebits = 9
|
||||
tablesize = 1 << tablebits
|
||||
|
||||
maxBlockSize = 32768
|
||||
windowSize = 32768
|
||||
|
||||
maxTreePathLen = 16
|
||||
|
||||
e8filesize = 12000000
|
||||
maxe8offset = 0x3fffffff
|
||||
|
||||
verbatimBlock = 1
|
||||
alignedOffsetBlock = 2
|
||||
uncompressedBlock = 3
|
||||
)
|
||||
|
||||
var footerBits = [...]byte{
|
||||
0, 0, 0, 0, 1, 1, 2, 2,
|
||||
3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10,
|
||||
11, 11, 12, 12, 13, 13, 14,
|
||||
}
|
||||
|
||||
var basePosition = [...]uint16{
|
||||
0, 1, 2, 3, 4, 6, 8, 12,
|
||||
16, 24, 32, 48, 64, 96, 128, 192,
|
||||
256, 384, 512, 768, 1024, 1536, 2048, 3072,
|
||||
4096, 6144, 8192, 12288, 16384, 24576, 32768,
|
||||
}
|
||||
|
||||
var (
|
||||
errCorrupt = errors.New("LZX data corrupt")
|
||||
)
|
||||
|
||||
// Reader is an interface used by the decompressor to access
|
||||
// the input stream. If the provided io.Reader does not implement
|
||||
// Reader, then a bufio.Reader is used.
|
||||
type Reader interface {
|
||||
io.Reader
|
||||
io.ByteReader
|
||||
}
|
||||
|
||||
type decompressor struct {
|
||||
r io.Reader
|
||||
err error
|
||||
unaligned bool
|
||||
nbits byte
|
||||
c uint32
|
||||
lru [3]uint16
|
||||
uncompressed int
|
||||
windowReader *bytes.Reader
|
||||
mainlens [maincodecount]byte
|
||||
lenlens [lencodecount]byte
|
||||
window [windowSize]byte
|
||||
b []byte
|
||||
bv int
|
||||
bo int
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func (f *decompressor) fail(err error) {
|
||||
if f.err == nil {
|
||||
f.err = err
|
||||
}
|
||||
f.bo = 0
|
||||
f.bv = 0
|
||||
}
|
||||
|
||||
func (f *decompressor) ensureAtLeast(n int) error {
|
||||
if f.bv-f.bo >= n {
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
|
||||
if f.bv != f.bo {
|
||||
copy(f.b[:f.bv-f.bo], f.b[f.bo:f.bv])
|
||||
}
|
||||
n, err := io.ReadAtLeast(f.r, f.b[f.bv-f.bo:], n)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
} else {
|
||||
f.fail(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
f.bv = f.bv - f.bo + n
|
||||
f.bo = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// feed retrieves another 16-bit word from the stream and consumes
|
||||
// it into f.c. It returns false if there are no more bytes available.
|
||||
// Otherwise, on error, it sets f.err.
|
||||
func (f *decompressor) feed() bool {
|
||||
err := f.ensureAtLeast(2)
|
||||
if err != nil {
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
return false
|
||||
}
|
||||
}
|
||||
f.c |= (uint32(f.b[f.bo+1])<<8 | uint32(f.b[f.bo])) << (16 - f.nbits)
|
||||
f.nbits += 16
|
||||
f.bo += 2
|
||||
return true
|
||||
}
|
||||
|
||||
// getBits retrieves the next n bits from the byte stream. n
|
||||
// must be <= 16. It sets f.err on error.
|
||||
func (f *decompressor) getBits(n byte) uint16 {
|
||||
if f.nbits < n {
|
||||
if !f.feed() {
|
||||
f.fail(io.ErrUnexpectedEOF)
|
||||
}
|
||||
}
|
||||
c := uint16(f.c >> (32 - n))
|
||||
f.c <<= n
|
||||
f.nbits -= n
|
||||
return c
|
||||
}
|
||||
|
||||
type huffman struct {
|
||||
extra [][]uint16
|
||||
maxbits byte
|
||||
table [tablesize]uint16
|
||||
}
|
||||
|
||||
// buildTable builds a huffman decoding table from a slice of code lengths,
|
||||
// one per code, in order. Each code length must be <= maxTreePathLen.
|
||||
// See https://en.wikipedia.org/wiki/Canonical_Huffman_code.
|
||||
func buildTable(codelens []byte) *huffman {
|
||||
// Determine the number of codes of each length, and the
|
||||
// maximum length.
|
||||
var count [maxTreePathLen + 1]uint
|
||||
var max byte
|
||||
for _, cl := range codelens {
|
||||
count[cl]++
|
||||
if max < cl {
|
||||
max = cl
|
||||
}
|
||||
}
|
||||
|
||||
if max == 0 {
|
||||
return &huffman{}
|
||||
}
|
||||
|
||||
// Determine the first code of each length.
|
||||
var first [maxTreePathLen + 1]uint
|
||||
code := uint(0)
|
||||
for i := byte(1); i <= max; i++ {
|
||||
code <<= 1
|
||||
first[i] = code
|
||||
code += count[i]
|
||||
}
|
||||
|
||||
if code != 1<<max {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build a table for code lookup. For code sizes < max,
|
||||
// put all possible suffixes for the code into the table, too.
|
||||
// For max > tablebits, split long codes into additional tables
|
||||
// of suffixes of max-tablebits length.
|
||||
h := &huffman{maxbits: max}
|
||||
if max > tablebits {
|
||||
core := first[tablebits+1] / 2 // Number of codes that fit without extra tables
|
||||
nextra := 1<<tablebits - core // Number of extra entries
|
||||
h.extra = make([][]uint16, nextra)
|
||||
for code := core; code < 1<<tablebits; code++ {
|
||||
h.table[code] = uint16(code - core)
|
||||
h.extra[code-core] = make([]uint16, 1<<(max-tablebits))
|
||||
}
|
||||
}
|
||||
|
||||
for i, cl := range codelens {
|
||||
if cl != 0 {
|
||||
code := first[cl]
|
||||
first[cl]++
|
||||
v := uint16(cl)<<lenshift | uint16(i)
|
||||
if cl <= tablebits {
|
||||
extendedCode := code << (tablebits - cl)
|
||||
for j := uint(0); j < 1<<(tablebits-cl); j++ {
|
||||
h.table[extendedCode+j] = v
|
||||
}
|
||||
} else {
|
||||
prefix := code >> (cl - tablebits)
|
||||
suffix := code & (1<<(cl-tablebits) - 1)
|
||||
extendedCode := suffix << (max - cl)
|
||||
for j := uint(0); j < 1<<(max-cl); j++ {
|
||||
h.extra[h.table[prefix]][extendedCode+j] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// getCode retrieves the next code using the provided
|
||||
// huffman tree. It sets f.err on error.
|
||||
func (f *decompressor) getCode(h *huffman) uint16 {
|
||||
if h.maxbits > 0 {
|
||||
if f.nbits < maxTreePathLen {
|
||||
f.feed()
|
||||
}
|
||||
|
||||
// For codes with length < tablebits, it doesn't matter
|
||||
// what the remainder of the bits used for table lookup
|
||||
// are, since entries with all possible suffixes were
|
||||
// added to the table.
|
||||
c := h.table[f.c>>(32-tablebits)]
|
||||
if c >= 1<<lenshift {
|
||||
// The code is already in c.
|
||||
} else {
|
||||
c = h.extra[c][f.c<<tablebits>>(32-(h.maxbits-tablebits))]
|
||||
}
|
||||
|
||||
n := byte(c >> lenshift)
|
||||
if f.nbits >= n {
|
||||
// Only consume the length of the code, not the maximum
|
||||
// code length.
|
||||
f.c <<= n
|
||||
f.nbits -= n
|
||||
return c & codemask
|
||||
}
|
||||
|
||||
f.fail(io.ErrUnexpectedEOF)
|
||||
return 0
|
||||
}
|
||||
|
||||
// This is an empty tree. It should not be used.
|
||||
f.fail(errCorrupt)
|
||||
return 0
|
||||
}
|
||||
|
||||
// readTree updates the huffman tree path lengths in lens by
|
||||
// reading and decoding lengths from the byte stream. lens
|
||||
// should be prepopulated with the previous block's tree's path
|
||||
// lengths. For the first block, lens should be zero.
|
||||
func (f *decompressor) readTree(lens []byte) error {
|
||||
// Get the pre-tree for the main tree.
|
||||
var pretreeLen [20]byte
|
||||
for i := range pretreeLen {
|
||||
pretreeLen[i] = byte(f.getBits(4))
|
||||
}
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
h := buildTable(pretreeLen[:])
|
||||
|
||||
// The lengths are encoded as a series of huffman codes
|
||||
// encoded by the pre-tree.
|
||||
for i := 0; i < len(lens); {
|
||||
c := byte(f.getCode(h))
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
switch {
|
||||
case c <= 16: // length is delta from previous length
|
||||
lens[i] = (lens[i] + 17 - c) % 17
|
||||
i++
|
||||
case c == 17: // next n + 4 lengths are zero
|
||||
zeroes := int(f.getBits(4)) + 4
|
||||
if i+zeroes > len(lens) {
|
||||
return errCorrupt
|
||||
}
|
||||
for j := 0; j < zeroes; j++ {
|
||||
lens[i+j] = 0
|
||||
}
|
||||
i += zeroes
|
||||
case c == 18: // next n + 20 lengths are zero
|
||||
zeroes := int(f.getBits(5)) + 20
|
||||
if i+zeroes > len(lens) {
|
||||
return errCorrupt
|
||||
}
|
||||
for j := 0; j < zeroes; j++ {
|
||||
lens[i+j] = 0
|
||||
}
|
||||
i += zeroes
|
||||
case c == 19: // next n + 4 lengths all have the same value
|
||||
same := int(f.getBits(1)) + 4
|
||||
if i+same > len(lens) {
|
||||
return errCorrupt
|
||||
}
|
||||
c = byte(f.getCode(h))
|
||||
if c > 16 {
|
||||
return errCorrupt
|
||||
}
|
||||
l := (lens[i] + 17 - c) % 17
|
||||
for j := 0; j < same; j++ {
|
||||
lens[i+j] = l
|
||||
}
|
||||
i += same
|
||||
default:
|
||||
return errCorrupt
|
||||
}
|
||||
}
|
||||
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *decompressor) readBlockHeader() (byte, uint16, error) {
|
||||
// If the previous block was an unaligned uncompressed block, restore
|
||||
// 2-byte alignment.
|
||||
if f.unaligned {
|
||||
err := f.ensureAtLeast(1)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
f.bo++
|
||||
f.unaligned = false
|
||||
}
|
||||
|
||||
blockType := f.getBits(3)
|
||||
full := f.getBits(1)
|
||||
var blockSize uint16
|
||||
if full != 0 {
|
||||
blockSize = maxBlockSize
|
||||
} else {
|
||||
blockSize = f.getBits(16)
|
||||
if blockSize > maxBlockSize {
|
||||
return 0, 0, errCorrupt
|
||||
}
|
||||
}
|
||||
|
||||
if f.err != nil {
|
||||
return 0, 0, f.err
|
||||
}
|
||||
|
||||
switch blockType {
|
||||
case verbatimBlock, alignedOffsetBlock:
|
||||
// The caller will read the huffman trees.
|
||||
case uncompressedBlock:
|
||||
if f.nbits > 16 {
|
||||
panic("impossible: more than one 16-bit word remains")
|
||||
}
|
||||
|
||||
// Drop the remaining bits in the current 16-bit word
|
||||
// If there are no bits left, discard a full 16-bit word.
|
||||
n := f.nbits
|
||||
if n == 0 {
|
||||
n = 16
|
||||
}
|
||||
|
||||
f.getBits(n)
|
||||
|
||||
// Read the LRU values for the next block.
|
||||
err := f.ensureAtLeast(12)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
f.lru[0] = uint16(binary.LittleEndian.Uint32(f.b[f.bo : f.bo+4]))
|
||||
f.lru[1] = uint16(binary.LittleEndian.Uint32(f.b[f.bo+4 : f.bo+8]))
|
||||
f.lru[2] = uint16(binary.LittleEndian.Uint32(f.b[f.bo+8 : f.bo+12]))
|
||||
f.bo += 12
|
||||
|
||||
default:
|
||||
return 0, 0, errCorrupt
|
||||
}
|
||||
|
||||
return byte(blockType), blockSize, nil
|
||||
}
|
||||
|
||||
// readTrees reads the two or three huffman trees for the current block.
|
||||
// readAligned specifies whether to read the aligned offset tree.
|
||||
func (f *decompressor) readTrees(readAligned bool) (main *huffman, length *huffman, aligned *huffman, err error) {
|
||||
// Aligned offset blocks start with a small aligned offset tree.
|
||||
if readAligned {
|
||||
var alignedLen [8]byte
|
||||
for i := range alignedLen {
|
||||
alignedLen[i] = byte(f.getBits(3))
|
||||
}
|
||||
aligned = buildTable(alignedLen[:])
|
||||
if aligned == nil {
|
||||
err = errors.New("corrupt")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// The main tree is encoded in two parts.
|
||||
err = f.readTree(f.mainlens[:maincodesplit])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = f.readTree(f.mainlens[maincodesplit:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
main = buildTable(f.mainlens[:])
|
||||
if main == nil {
|
||||
err = errors.New("corrupt")
|
||||
return
|
||||
}
|
||||
|
||||
// The length tree is encoding in a single part.
|
||||
err = f.readTree(f.lenlens[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
length = buildTable(f.lenlens[:])
|
||||
if length == nil {
|
||||
err = errors.New("corrupt")
|
||||
return
|
||||
}
|
||||
|
||||
err = f.err
|
||||
return
|
||||
}
|
||||
|
||||
// readCompressedBlock decodes a compressed block, writing into the window
|
||||
// starting at start and ending at end, and using the provided huffman trees.
|
||||
func (f *decompressor) readCompressedBlock(start, end uint16, hmain, hlength, haligned *huffman) (int, error) {
|
||||
i := start
|
||||
for i < end {
|
||||
main := f.getCode(hmain)
|
||||
if f.err != nil {
|
||||
break
|
||||
}
|
||||
if main < 256 {
|
||||
// Literal byte.
|
||||
f.window[i] = byte(main)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a match backward in the window. Determine
|
||||
// the offset and dlength.
|
||||
matchlen := (main - 256) % 8
|
||||
slot := (main - 256) / 8
|
||||
|
||||
// The length is either the low bits of the code,
|
||||
// or if this is 7, is encoded with the length tree.
|
||||
if matchlen == 7 {
|
||||
matchlen += f.getCode(hlength)
|
||||
}
|
||||
matchlen += 2
|
||||
|
||||
var matchoffset uint16
|
||||
if slot < 3 {
|
||||
// The offset is one of the LRU values.
|
||||
matchoffset = f.lru[slot]
|
||||
f.lru[slot] = f.lru[0]
|
||||
f.lru[0] = matchoffset
|
||||
} else {
|
||||
// The offset is encoded as a combination of the
|
||||
// slot and more bits from the bit stream.
|
||||
offsetbits := footerBits[slot]
|
||||
var verbatimbits, alignedbits uint16
|
||||
if offsetbits > 0 {
|
||||
if haligned != nil && offsetbits >= 3 {
|
||||
// This is an aligned offset block. Combine
|
||||
// the bits written verbatim with the aligned
|
||||
// offset tree code.
|
||||
verbatimbits = f.getBits(offsetbits-3) * 8
|
||||
alignedbits = f.getCode(haligned)
|
||||
} else {
|
||||
// There are no aligned offset bits to read,
|
||||
// only verbatim bits.
|
||||
verbatimbits = f.getBits(offsetbits)
|
||||
alignedbits = 0
|
||||
}
|
||||
}
|
||||
matchoffset = basePosition[slot] + verbatimbits + alignedbits - 2
|
||||
// Update the LRU cache.
|
||||
f.lru[2] = f.lru[1]
|
||||
f.lru[1] = f.lru[0]
|
||||
f.lru[0] = matchoffset
|
||||
}
|
||||
|
||||
if matchoffset <= i && matchlen <= end-i {
|
||||
copyend := i + matchlen
|
||||
for ; i < copyend; i++ {
|
||||
f.window[i] = f.window[i-matchoffset]
|
||||
}
|
||||
} else {
|
||||
f.fail(errCorrupt)
|
||||
break
|
||||
}
|
||||
}
|
||||
return int(i - start), f.err
|
||||
}
|
||||
|
||||
// readBlock decodes the current block and returns the number of uncompressed bytes.
|
||||
func (f *decompressor) readBlock(start uint16) (int, error) {
|
||||
blockType, size, err := f.readBlockHeader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if blockType == uncompressedBlock {
|
||||
if size%2 == 1 {
|
||||
// Remember to realign the byte stream at the next block.
|
||||
f.unaligned = true
|
||||
}
|
||||
copied := 0
|
||||
if f.bo < f.bv {
|
||||
copied = int(size)
|
||||
s := int(start)
|
||||
if copied > f.bv-f.bo {
|
||||
copied = f.bv - f.bo
|
||||
}
|
||||
copy(f.window[s:s+copied], f.b[f.bo:f.bo+copied])
|
||||
f.bo += copied
|
||||
}
|
||||
n, err := io.ReadFull(f.r, f.window[start+uint16(copied):start+size])
|
||||
return copied + n, err
|
||||
}
|
||||
|
||||
hmain, hlength, haligned, err := f.readTrees(blockType == alignedOffsetBlock)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return f.readCompressedBlock(start, start+size, hmain, hlength, haligned)
|
||||
}
|
||||
|
||||
// decodeE8 reverses the 0xe8 x86 instruction encoding that was performed
|
||||
// to the uncompressed data before it was compressed.
|
||||
func decodeE8(b []byte, off int64) {
|
||||
if off > maxe8offset || len(b) < 10 {
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(b)-10; i++ {
|
||||
if b[i] == 0xe8 {
|
||||
currentPtr := int32(off) + int32(i)
|
||||
abs := int32(binary.LittleEndian.Uint32(b[i+1 : i+5]))
|
||||
if abs >= -currentPtr && abs < e8filesize {
|
||||
var rel int32
|
||||
if abs >= 0 {
|
||||
rel = abs - currentPtr
|
||||
} else {
|
||||
rel = abs + e8filesize
|
||||
}
|
||||
binary.LittleEndian.PutUint32(b[i+1:i+5], uint32(rel))
|
||||
}
|
||||
i += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *decompressor) Read(b []byte) (int, error) {
|
||||
// Read and uncompress everything.
|
||||
if f.windowReader == nil {
|
||||
n := 0
|
||||
for n < f.uncompressed {
|
||||
k, err := f.readBlock(uint16(n))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n += k
|
||||
}
|
||||
decodeE8(f.window[:f.uncompressed], 0)
|
||||
f.windowReader = bytes.NewReader(f.window[:f.uncompressed])
|
||||
}
|
||||
|
||||
// Just read directly from the window.
|
||||
return f.windowReader.Read(b)
|
||||
}
|
||||
|
||||
func (f *decompressor) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewReader returns a new io.ReadCloser that decompresses a
|
||||
// WIM LZX stream until uncompressedSize bytes have been returned.
|
||||
func NewReader(r io.Reader, uncompressedSize int) (io.ReadCloser, error) {
|
||||
if uncompressedSize > windowSize {
|
||||
return nil, errors.New("uncompressed size is limited to 32KB")
|
||||
}
|
||||
f := &decompressor{
|
||||
lru: [3]uint16{1, 1, 1},
|
||||
uncompressed: uncompressedSize,
|
||||
b: make([]byte, 4096),
|
||||
r: r,
|
||||
}
|
||||
return f, nil
|
||||
}
|
51
gateway/vendor/github.com/Microsoft/go-winio/wim/validate/validate.go
generated
vendored
Normal file
51
gateway/vendor/github.com/Microsoft/go-winio/wim/validate/validate.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Microsoft/go-winio/wim"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
f, err := os.Open(flag.Arg(0))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
w, err := wim.NewReader(f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
}
|
||||
|
||||
fmt.Printf("%#v\n%#v\n", w.Image[0], w.Image[0].Windows)
|
||||
|
||||
dir, err := w.Image[0].Open()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = recur(dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func recur(d *wim.File) error {
|
||||
files, err := d.Readdir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", d.Name, err)
|
||||
}
|
||||
for _, f := range files {
|
||||
if f.IsDir() {
|
||||
err = recur(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", f.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
866
gateway/vendor/github.com/Microsoft/go-winio/wim/wim.go
generated
vendored
Normal file
866
gateway/vendor/github.com/Microsoft/go-winio/wim/wim.go
generated
vendored
Normal file
@ -0,0 +1,866 @@
|
||||
// Package wim implements a WIM file parser.
|
||||
//
|
||||
// WIM files are used to distribute Windows file system and container images.
|
||||
// They are documented at https://msdn.microsoft.com/en-us/library/windows/desktop/dd861280.aspx.
|
||||
package wim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// File attribute constants from Windows.
|
||||
const (
|
||||
FILE_ATTRIBUTE_READONLY = 0x00000001
|
||||
FILE_ATTRIBUTE_HIDDEN = 0x00000002
|
||||
FILE_ATTRIBUTE_SYSTEM = 0x00000004
|
||||
FILE_ATTRIBUTE_DIRECTORY = 0x00000010
|
||||
FILE_ATTRIBUTE_ARCHIVE = 0x00000020
|
||||
FILE_ATTRIBUTE_DEVICE = 0x00000040
|
||||
FILE_ATTRIBUTE_NORMAL = 0x00000080
|
||||
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
|
||||
FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200
|
||||
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
|
||||
FILE_ATTRIBUTE_COMPRESSED = 0x00000800
|
||||
FILE_ATTRIBUTE_OFFLINE = 0x00001000
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000
|
||||
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000
|
||||
FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x00008000
|
||||
FILE_ATTRIBUTE_VIRTUAL = 0x00010000
|
||||
FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x00020000
|
||||
FILE_ATTRIBUTE_EA = 0x00040000
|
||||
)
|
||||
|
||||
// Windows processor architectures.
|
||||
const (
|
||||
PROCESSOR_ARCHITECTURE_INTEL = 0
|
||||
PROCESSOR_ARCHITECTURE_MIPS = 1
|
||||
PROCESSOR_ARCHITECTURE_ALPHA = 2
|
||||
PROCESSOR_ARCHITECTURE_PPC = 3
|
||||
PROCESSOR_ARCHITECTURE_SHX = 4
|
||||
PROCESSOR_ARCHITECTURE_ARM = 5
|
||||
PROCESSOR_ARCHITECTURE_IA64 = 6
|
||||
PROCESSOR_ARCHITECTURE_ALPHA64 = 7
|
||||
PROCESSOR_ARCHITECTURE_MSIL = 8
|
||||
PROCESSOR_ARCHITECTURE_AMD64 = 9
|
||||
PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 = 10
|
||||
PROCESSOR_ARCHITECTURE_NEUTRAL = 11
|
||||
PROCESSOR_ARCHITECTURE_ARM64 = 12
|
||||
)
|
||||
|
||||
var wimImageTag = [...]byte{'M', 'S', 'W', 'I', 'M', 0, 0, 0}
|
||||
|
||||
type guid struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
func (g guid) String() string {
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", g.Data1, g.Data2, g.Data3, g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7])
|
||||
}
|
||||
|
||||
type resourceDescriptor struct {
|
||||
FlagsAndCompressedSize uint64
|
||||
Offset int64
|
||||
OriginalSize int64
|
||||
}
|
||||
|
||||
type resFlag byte
|
||||
|
||||
const (
|
||||
resFlagFree resFlag = 1 << iota
|
||||
resFlagMetadata
|
||||
resFlagCompressed
|
||||
resFlagSpanned
|
||||
)
|
||||
|
||||
const validate = false
|
||||
|
||||
const supportedResFlags = resFlagMetadata | resFlagCompressed
|
||||
|
||||
func (r *resourceDescriptor) Flags() resFlag {
|
||||
return resFlag(r.FlagsAndCompressedSize >> 56)
|
||||
}
|
||||
|
||||
func (r *resourceDescriptor) CompressedSize() int64 {
|
||||
return int64(r.FlagsAndCompressedSize & 0xffffffffffffff)
|
||||
}
|
||||
|
||||
func (r *resourceDescriptor) String() string {
|
||||
s := fmt.Sprintf("%d bytes at %d", r.CompressedSize(), r.Offset)
|
||||
if r.Flags()&4 != 0 {
|
||||
s += fmt.Sprintf(" (uncompresses to %d)", r.OriginalSize)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SHA1Hash contains the SHA1 hash of a file or stream.
|
||||
type SHA1Hash [20]byte
|
||||
|
||||
type streamDescriptor struct {
|
||||
resourceDescriptor
|
||||
PartNumber uint16
|
||||
RefCount uint32
|
||||
Hash SHA1Hash
|
||||
}
|
||||
|
||||
type hdrFlag uint32
|
||||
|
||||
const (
|
||||
hdrFlagReserved hdrFlag = 1 << iota
|
||||
hdrFlagCompressed
|
||||
hdrFlagReadOnly
|
||||
hdrFlagSpanned
|
||||
hdrFlagResourceOnly
|
||||
hdrFlagMetadataOnly
|
||||
hdrFlagWriteInProgress
|
||||
hdrFlagRpFix
|
||||
)
|
||||
|
||||
const (
|
||||
hdrFlagCompressReserved hdrFlag = 1 << (iota + 16)
|
||||
hdrFlagCompressXpress
|
||||
hdrFlagCompressLzx
|
||||
)
|
||||
|
||||
const supportedHdrFlags = hdrFlagRpFix | hdrFlagReadOnly | hdrFlagCompressed | hdrFlagCompressLzx
|
||||
|
||||
type wimHeader struct {
|
||||
ImageTag [8]byte
|
||||
Size uint32
|
||||
Version uint32
|
||||
Flags hdrFlag
|
||||
CompressionSize uint32
|
||||
WIMGuid guid
|
||||
PartNumber uint16
|
||||
TotalParts uint16
|
||||
ImageCount uint32
|
||||
OffsetTable resourceDescriptor
|
||||
XMLData resourceDescriptor
|
||||
BootMetadata resourceDescriptor
|
||||
BootIndex uint32
|
||||
Padding uint32
|
||||
Integrity resourceDescriptor
|
||||
Unused [60]byte
|
||||
}
|
||||
|
||||
type securityblockDisk struct {
|
||||
TotalLength uint32
|
||||
NumEntries uint32
|
||||
}
|
||||
|
||||
const securityblockDiskSize = 8
|
||||
|
||||
type direntry struct {
|
||||
Attributes uint32
|
||||
SecurityID uint32
|
||||
SubdirOffset int64
|
||||
Unused1, Unused2 int64
|
||||
CreationTime Filetime
|
||||
LastAccessTime Filetime
|
||||
LastWriteTime Filetime
|
||||
Hash SHA1Hash
|
||||
Padding uint32
|
||||
ReparseHardLink int64
|
||||
StreamCount uint16
|
||||
ShortNameLength uint16
|
||||
FileNameLength uint16
|
||||
}
|
||||
|
||||
var direntrySize = int64(binary.Size(direntry{}) + 8) // includes an 8-byte length prefix
|
||||
|
||||
type streamentry struct {
|
||||
Unused int64
|
||||
Hash SHA1Hash
|
||||
NameLength int16
|
||||
}
|
||||
|
||||
var streamentrySize = int64(binary.Size(streamentry{}) + 8) // includes an 8-byte length prefix
|
||||
|
||||
// Filetime represents a Windows time.
|
||||
type Filetime struct {
|
||||
LowDateTime uint32
|
||||
HighDateTime uint32
|
||||
}
|
||||
|
||||
// Time returns the time as time.Time.
|
||||
func (ft *Filetime) Time() time.Time {
|
||||
// 100-nanosecond intervals since January 1, 1601
|
||||
nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
|
||||
// change starting time to the Epoch (00:00:00 UTC, January 1, 1970)
|
||||
nsec -= 116444736000000000
|
||||
// convert into nanoseconds
|
||||
nsec *= 100
|
||||
return time.Unix(0, nsec)
|
||||
}
|
||||
|
||||
// UnmarshalXML unmarshals the time from a WIM XML blob.
|
||||
func (ft *Filetime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
type time struct {
|
||||
Low string `xml:"LOWPART"`
|
||||
High string `xml:"HIGHPART"`
|
||||
}
|
||||
var t time
|
||||
err := d.DecodeElement(&t, &start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
low, err := strconv.ParseUint(t.Low, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
high, err := strconv.ParseUint(t.High, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ft.LowDateTime = uint32(low)
|
||||
ft.HighDateTime = uint32(high)
|
||||
return nil
|
||||
}
|
||||
|
||||
type info struct {
|
||||
Image []ImageInfo `xml:"IMAGE"`
|
||||
}
|
||||
|
||||
// ImageInfo contains information about the image.
|
||||
type ImageInfo struct {
|
||||
Name string `xml:"NAME"`
|
||||
Index int `xml:"INDEX,attr"`
|
||||
CreationTime Filetime `xml:"CREATIONTIME"`
|
||||
ModTime Filetime `xml:"LASTMODIFICATIONTIME"`
|
||||
Windows *WindowsInfo `xml:"WINDOWS"`
|
||||
}
|
||||
|
||||
// WindowsInfo contains information about the Windows installation in the image.
|
||||
type WindowsInfo struct {
|
||||
Arch byte `xml:"ARCH"`
|
||||
ProductName string `xml:"PRODUCTNAME"`
|
||||
EditionID string `xml:"EDITIONID"`
|
||||
InstallationType string `xml:"INSTALLATIONTYPE"`
|
||||
ProductType string `xml:"PRODUCTTYPE"`
|
||||
Languages []string `xml:"LANGUAGES>LANGUAGE"`
|
||||
DefaultLanguage string `xml:"LANGUAGES>DEFAULT"`
|
||||
Version Version `xml:"VERSION"`
|
||||
SystemRoot string `xml:"SYSTEMROOT"`
|
||||
}
|
||||
|
||||
// Version represents a Windows build version.
|
||||
type Version struct {
|
||||
Major int `xml:"MAJOR"`
|
||||
Minor int `xml:"MINOR"`
|
||||
Build int `xml:"BUILD"`
|
||||
SPBuild int `xml:"SPBUILD"`
|
||||
SPLevel int `xml:"SPLEVEL"`
|
||||
}
|
||||
|
||||
// ParseError is returned when the WIM cannot be parsed.
|
||||
type ParseError struct {
|
||||
Oper string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ParseError) Error() string {
|
||||
if e.Path == "" {
|
||||
return "WIM parse error at " + e.Oper + ": " + e.Err.Error()
|
||||
}
|
||||
return fmt.Sprintf("WIM parse error: %s %s: %s", e.Oper, e.Path, e.Err.Error())
|
||||
}
|
||||
|
||||
// Reader provides functions to read a WIM file.
|
||||
type Reader struct {
|
||||
hdr wimHeader
|
||||
r io.ReaderAt
|
||||
fileData map[SHA1Hash]resourceDescriptor
|
||||
|
||||
XMLInfo string // The XML information about the WIM.
|
||||
Image []*Image // The WIM's images.
|
||||
}
|
||||
|
||||
// Image represents an image within a WIM file.
|
||||
type Image struct {
|
||||
wim *Reader
|
||||
offset resourceDescriptor
|
||||
sds [][]byte
|
||||
rootOffset int64
|
||||
r io.ReadCloser
|
||||
curOffset int64
|
||||
m sync.Mutex
|
||||
|
||||
ImageInfo
|
||||
}
|
||||
|
||||
// StreamHeader contains alternate data stream metadata.
|
||||
type StreamHeader struct {
|
||||
Name string
|
||||
Hash SHA1Hash
|
||||
Size int64
|
||||
}
|
||||
|
||||
// Stream represents an alternate data stream or reparse point data stream.
|
||||
type Stream struct {
|
||||
StreamHeader
|
||||
wim *Reader
|
||||
offset resourceDescriptor
|
||||
}
|
||||
|
||||
// FileHeader contains file metadata.
|
||||
type FileHeader struct {
|
||||
Name string
|
||||
ShortName string
|
||||
Attributes uint32
|
||||
SecurityDescriptor []byte
|
||||
CreationTime Filetime
|
||||
LastAccessTime Filetime
|
||||
LastWriteTime Filetime
|
||||
Hash SHA1Hash
|
||||
Size int64
|
||||
LinkID int64
|
||||
ReparseTag uint32
|
||||
ReparseReserved uint32
|
||||
}
|
||||
|
||||
// File represents a file or directory in a WIM image.
|
||||
type File struct {
|
||||
FileHeader
|
||||
Streams []*Stream
|
||||
offset resourceDescriptor
|
||||
img *Image
|
||||
subdirOffset int64
|
||||
}
|
||||
|
||||
// NewReader returns a Reader that can be used to read WIM file data.
|
||||
func NewReader(f io.ReaderAt) (*Reader, error) {
|
||||
r := &Reader{r: f}
|
||||
section := io.NewSectionReader(f, 0, 0xffff)
|
||||
err := binary.Read(section, binary.LittleEndian, &r.hdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.hdr.ImageTag != wimImageTag {
|
||||
return nil, &ParseError{Oper: "image tag", Err: errors.New("not a WIM file")}
|
||||
}
|
||||
|
||||
if r.hdr.Flags&^supportedHdrFlags != 0 {
|
||||
return nil, fmt.Errorf("unsupported WIM flags %x", r.hdr.Flags&^supportedHdrFlags)
|
||||
}
|
||||
|
||||
if r.hdr.CompressionSize != 0x8000 {
|
||||
return nil, fmt.Errorf("unsupported compression size %d", r.hdr.CompressionSize)
|
||||
}
|
||||
|
||||
if r.hdr.TotalParts != 1 {
|
||||
return nil, errors.New("multi-part WIM not supported")
|
||||
}
|
||||
|
||||
fileData, images, err := r.readOffsetTable(&r.hdr.OffsetTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xmlinfo, err := r.readXML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var info info
|
||||
err = xml.Unmarshal([]byte(xmlinfo), &info)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Oper: "XML info", Err: err}
|
||||
}
|
||||
|
||||
for i, img := range images {
|
||||
for _, imgInfo := range info.Image {
|
||||
if imgInfo.Index == i+1 {
|
||||
img.ImageInfo = imgInfo
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.fileData = fileData
|
||||
r.Image = images
|
||||
r.XMLInfo = xmlinfo
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Close releases resources associated with the Reader.
|
||||
func (r *Reader) Close() error {
|
||||
for _, img := range r.Image {
|
||||
img.reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reader) resourceReader(hdr *resourceDescriptor) (io.ReadCloser, error) {
|
||||
return r.resourceReaderWithOffset(hdr, 0)
|
||||
}
|
||||
|
||||
func (r *Reader) resourceReaderWithOffset(hdr *resourceDescriptor, offset int64) (io.ReadCloser, error) {
|
||||
var sr io.ReadCloser
|
||||
section := io.NewSectionReader(r.r, hdr.Offset, hdr.CompressedSize())
|
||||
if hdr.Flags()&resFlagCompressed == 0 {
|
||||
section.Seek(offset, 0)
|
||||
sr = ioutil.NopCloser(section)
|
||||
} else {
|
||||
cr, err := newCompressedReader(section, hdr.OriginalSize, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sr = cr
|
||||
}
|
||||
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readResource(hdr *resourceDescriptor) ([]byte, error) {
|
||||
rsrc, err := r.resourceReader(hdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rsrc.Close()
|
||||
return ioutil.ReadAll(rsrc)
|
||||
}
|
||||
|
||||
func (r *Reader) readXML() (string, error) {
|
||||
if r.hdr.XMLData.CompressedSize() == 0 {
|
||||
return "", nil
|
||||
}
|
||||
rsrc, err := r.resourceReader(&r.hdr.XMLData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer rsrc.Close()
|
||||
|
||||
XMLData := make([]uint16, r.hdr.XMLData.OriginalSize/2)
|
||||
err = binary.Read(rsrc, binary.LittleEndian, XMLData)
|
||||
if err != nil {
|
||||
return "", &ParseError{Oper: "XML data", Err: err}
|
||||
}
|
||||
|
||||
// The BOM will always indicate little-endian UTF-16.
|
||||
if XMLData[0] != 0xfeff {
|
||||
return "", &ParseError{Oper: "XML data", Err: errors.New("invalid BOM")}
|
||||
}
|
||||
return string(utf16.Decode(XMLData[1:])), nil
|
||||
}
|
||||
|
||||
func (r *Reader) readOffsetTable(res *resourceDescriptor) (map[SHA1Hash]resourceDescriptor, []*Image, error) {
|
||||
fileData := make(map[SHA1Hash]resourceDescriptor)
|
||||
var images []*Image
|
||||
|
||||
offsetTable, err := r.readResource(res)
|
||||
if err != nil {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: err}
|
||||
}
|
||||
|
||||
br := bytes.NewReader(offsetTable)
|
||||
for i := 0; ; i++ {
|
||||
var res streamDescriptor
|
||||
err := binary.Read(br, binary.LittleEndian, &res)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: err}
|
||||
}
|
||||
if res.Flags()&^supportedResFlags != 0 {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: errors.New("unsupported resource flag")}
|
||||
}
|
||||
|
||||
// Validation for ad-hoc testing
|
||||
if validate {
|
||||
sec, err := r.resourceReader(&res.resourceDescriptor)
|
||||
if err != nil {
|
||||
panic(fmt.Sprint(i, err))
|
||||
}
|
||||
hash := sha1.New()
|
||||
_, err = io.Copy(hash, sec)
|
||||
sec.Close()
|
||||
if err != nil {
|
||||
panic(fmt.Sprint(i, err))
|
||||
}
|
||||
var cmphash SHA1Hash
|
||||
copy(cmphash[:], hash.Sum(nil))
|
||||
if cmphash != res.Hash {
|
||||
panic(fmt.Sprint(i, "hash mismatch"))
|
||||
}
|
||||
}
|
||||
|
||||
if res.Flags()&resFlagMetadata != 0 {
|
||||
image := &Image{
|
||||
wim: r,
|
||||
offset: res.resourceDescriptor,
|
||||
}
|
||||
images = append(images, image)
|
||||
} else {
|
||||
fileData[res.Hash] = res.resourceDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
if len(images) != int(r.hdr.ImageCount) {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: errors.New("mismatched image count")}
|
||||
}
|
||||
|
||||
return fileData, images, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readSecurityDescriptors(rsrc io.Reader) (sds [][]byte, n int64, err error) {
|
||||
var secBlock securityblockDisk
|
||||
err = binary.Read(rsrc, binary.LittleEndian, &secBlock)
|
||||
if err != nil {
|
||||
err = &ParseError{Oper: "security table", Err: err}
|
||||
return
|
||||
}
|
||||
|
||||
n += securityblockDiskSize
|
||||
|
||||
secSizes := make([]int64, secBlock.NumEntries)
|
||||
err = binary.Read(rsrc, binary.LittleEndian, &secSizes)
|
||||
if err != nil {
|
||||
err = &ParseError{Oper: "security table sizes", Err: err}
|
||||
return
|
||||
}
|
||||
|
||||
n += int64(secBlock.NumEntries * 8)
|
||||
|
||||
sds = make([][]byte, secBlock.NumEntries)
|
||||
for i, size := range secSizes {
|
||||
sd := make([]byte, size&0xffffffff)
|
||||
_, err = io.ReadFull(rsrc, sd)
|
||||
if err != nil {
|
||||
err = &ParseError{Oper: "security descriptor", Err: err}
|
||||
return
|
||||
}
|
||||
n += int64(len(sd))
|
||||
sds[i] = sd
|
||||
}
|
||||
|
||||
secsize := int64((secBlock.TotalLength + 7) &^ 7)
|
||||
if n > secsize {
|
||||
err = &ParseError{Oper: "security descriptor", Err: errors.New("security descriptor table too small")}
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.CopyN(ioutil.Discard, rsrc, secsize-n)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
n = secsize
|
||||
return
|
||||
}
|
||||
|
||||
// Open parses the image and returns the root directory.
|
||||
func (img *Image) Open() (*File, error) {
|
||||
if img.sds == nil {
|
||||
rsrc, err := img.wim.resourceReaderWithOffset(&img.offset, img.rootOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sds, n, err := img.wim.readSecurityDescriptors(rsrc)
|
||||
if err != nil {
|
||||
rsrc.Close()
|
||||
return nil, err
|
||||
}
|
||||
img.sds = sds
|
||||
img.r = rsrc
|
||||
img.rootOffset = n
|
||||
img.curOffset = n
|
||||
}
|
||||
|
||||
f, err := img.readdir(img.rootOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(f) != 1 {
|
||||
return nil, &ParseError{Oper: "root directory", Err: errors.New("expected exactly 1 root directory entry")}
|
||||
}
|
||||
return f[0], err
|
||||
}
|
||||
|
||||
func (img *Image) reset() {
|
||||
if img.r != nil {
|
||||
img.r.Close()
|
||||
img.r = nil
|
||||
}
|
||||
img.curOffset = -1
|
||||
}
|
||||
|
||||
func (img *Image) readdir(offset int64) ([]*File, error) {
|
||||
img.m.Lock()
|
||||
defer img.m.Unlock()
|
||||
|
||||
if offset < img.curOffset || offset > img.curOffset+chunkSize {
|
||||
// Reset to seek backward or to seek forward very far.
|
||||
img.reset()
|
||||
}
|
||||
if img.r == nil {
|
||||
rsrc, err := img.wim.resourceReaderWithOffset(&img.offset, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.r = rsrc
|
||||
img.curOffset = offset
|
||||
}
|
||||
if offset > img.curOffset {
|
||||
_, err := io.CopyN(ioutil.Discard, img.r, offset-img.curOffset)
|
||||
if err != nil {
|
||||
img.reset()
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var entries []*File
|
||||
for {
|
||||
e, n, err := img.readNextEntry(img.r)
|
||||
img.curOffset += n
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
img.reset()
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, e)
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func (img *Image) readNextEntry(r io.Reader) (*File, int64, error) {
|
||||
var length int64
|
||||
err := binary.Read(r, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "directory length check", Err: err}
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
return nil, 8, io.EOF
|
||||
}
|
||||
|
||||
left := length
|
||||
if left < direntrySize {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Err: errors.New("size too short")}
|
||||
}
|
||||
|
||||
var dentry direntry
|
||||
err = binary.Read(r, binary.LittleEndian, &dentry)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Err: err}
|
||||
}
|
||||
|
||||
left -= direntrySize
|
||||
|
||||
namesLen := int64(dentry.FileNameLength + 2 + dentry.ShortNameLength)
|
||||
if left < namesLen {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Err: errors.New("size too short for names")}
|
||||
}
|
||||
|
||||
names := make([]uint16, namesLen/2)
|
||||
err = binary.Read(r, binary.LittleEndian, names)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "file name", Err: err}
|
||||
}
|
||||
|
||||
left -= namesLen
|
||||
|
||||
var name, shortName string
|
||||
if dentry.FileNameLength > 0 {
|
||||
name = string(utf16.Decode(names[:dentry.FileNameLength/2]))
|
||||
}
|
||||
|
||||
if dentry.ShortNameLength > 0 {
|
||||
shortName = string(utf16.Decode(names[dentry.FileNameLength/2+1:]))
|
||||
}
|
||||
|
||||
var offset resourceDescriptor
|
||||
zerohash := SHA1Hash{}
|
||||
if dentry.Hash != zerohash {
|
||||
var ok bool
|
||||
offset, ok = img.wim.fileData[dentry.Hash]
|
||||
if !ok {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: fmt.Errorf("could not find file data matching hash %#v", dentry)}
|
||||
}
|
||||
}
|
||||
|
||||
f := &File{
|
||||
FileHeader: FileHeader{
|
||||
Attributes: dentry.Attributes,
|
||||
CreationTime: dentry.CreationTime,
|
||||
LastAccessTime: dentry.LastAccessTime,
|
||||
LastWriteTime: dentry.LastWriteTime,
|
||||
Hash: dentry.Hash,
|
||||
Size: offset.OriginalSize,
|
||||
Name: name,
|
||||
ShortName: shortName,
|
||||
},
|
||||
|
||||
offset: offset,
|
||||
img: img,
|
||||
subdirOffset: dentry.SubdirOffset,
|
||||
}
|
||||
|
||||
isDir := false
|
||||
|
||||
if dentry.Attributes&FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
f.LinkID = dentry.ReparseHardLink
|
||||
if dentry.Attributes&FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
isDir = true
|
||||
}
|
||||
} else {
|
||||
f.ReparseTag = uint32(dentry.ReparseHardLink)
|
||||
f.ReparseReserved = uint32(dentry.ReparseHardLink >> 32)
|
||||
}
|
||||
|
||||
if isDir && f.subdirOffset == 0 {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: errors.New("no subdirectory data for directory")}
|
||||
} else if !isDir && f.subdirOffset != 0 {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: errors.New("unexpected subdirectory data for non-directory")}
|
||||
}
|
||||
|
||||
if dentry.SecurityID != 0xffffffff {
|
||||
f.SecurityDescriptor = img.sds[dentry.SecurityID]
|
||||
}
|
||||
|
||||
_, err = io.CopyN(ioutil.Discard, r, left)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if dentry.StreamCount > 0 {
|
||||
var streams []*Stream
|
||||
for i := uint16(0); i < dentry.StreamCount; i++ {
|
||||
s, n, err := img.readNextStream(r)
|
||||
length += n
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
// The first unnamed stream should be treated as the file stream.
|
||||
if i == 0 && s.Name == "" {
|
||||
f.Hash = s.Hash
|
||||
f.Size = s.Size
|
||||
f.offset = s.offset
|
||||
} else if s.Name != "" {
|
||||
streams = append(streams, s)
|
||||
}
|
||||
}
|
||||
f.Streams = streams
|
||||
}
|
||||
|
||||
if dentry.Attributes&FILE_ATTRIBUTE_REPARSE_POINT != 0 && f.Size == 0 {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: errors.New("reparse point is missing reparse stream")}
|
||||
}
|
||||
|
||||
return f, length, nil
|
||||
}
|
||||
|
||||
func (img *Image) readNextStream(r io.Reader) (*Stream, int64, error) {
|
||||
var length int64
|
||||
err := binary.Read(r, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, 0, &ParseError{Oper: "stream length check", Err: err}
|
||||
}
|
||||
|
||||
left := length
|
||||
if left < streamentrySize {
|
||||
return nil, 0, &ParseError{Oper: "stream entry", Err: errors.New("size too short")}
|
||||
}
|
||||
|
||||
var sentry streamentry
|
||||
err = binary.Read(r, binary.LittleEndian, &sentry)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "stream entry", Err: err}
|
||||
}
|
||||
|
||||
left -= streamentrySize
|
||||
|
||||
if left < int64(sentry.NameLength) {
|
||||
return nil, 0, &ParseError{Oper: "stream entry", Err: errors.New("size too short for name")}
|
||||
}
|
||||
|
||||
names := make([]uint16, sentry.NameLength/2)
|
||||
err = binary.Read(r, binary.LittleEndian, names)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "file name", Err: err}
|
||||
}
|
||||
|
||||
left -= int64(sentry.NameLength)
|
||||
name := string(utf16.Decode(names))
|
||||
|
||||
var offset resourceDescriptor
|
||||
if sentry.Hash != (SHA1Hash{}) {
|
||||
var ok bool
|
||||
offset, ok = img.wim.fileData[sentry.Hash]
|
||||
if !ok {
|
||||
return nil, 0, &ParseError{Oper: "stream entry", Path: name, Err: fmt.Errorf("could not find file data matching hash %v", sentry.Hash)}
|
||||
}
|
||||
}
|
||||
|
||||
s := &Stream{
|
||||
StreamHeader: StreamHeader{
|
||||
Hash: sentry.Hash,
|
||||
Size: offset.OriginalSize,
|
||||
Name: name,
|
||||
},
|
||||
wim: img.wim,
|
||||
offset: offset,
|
||||
}
|
||||
|
||||
_, err = io.CopyN(ioutil.Discard, r, left)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return s, length, nil
|
||||
}
|
||||
|
||||
// Open returns an io.ReadCloser that can be used to read the stream's contents.
|
||||
func (s *Stream) Open() (io.ReadCloser, error) {
|
||||
return s.wim.resourceReader(&s.offset)
|
||||
}
|
||||
|
||||
// Open returns an io.ReadCloser that can be used to read the file's contents.
|
||||
func (f *File) Open() (io.ReadCloser, error) {
|
||||
return f.img.wim.resourceReader(&f.offset)
|
||||
}
|
||||
|
||||
// Readdir reads the directory entries.
|
||||
func (f *File) Readdir() ([]*File, error) {
|
||||
if !f.IsDir() {
|
||||
return nil, errors.New("not a directory")
|
||||
}
|
||||
return f.img.readdir(f.subdirOffset)
|
||||
}
|
||||
|
||||
// IsDir returns whether the given file is a directory. It returns false when it
|
||||
// is a directory reparse point.
|
||||
func (f *FileHeader) IsDir() bool {
|
||||
return f.Attributes&(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_DIRECTORY
|
||||
}
|
Reference in New Issue
Block a user