WIM: Parse XML data

This parses the XML data so that callers do not have to.
This commit is contained in:
John Starks
2016-04-06 17:09:08 -07:00
parent 9ef8a67999
commit a7636ef88a
2 changed files with 123 additions and 14 deletions
+3
View File
@@ -20,6 +20,9 @@ func main() {
panic(err) panic(err)
} }
fmt.Printf("%#v\n%#v\n", w.Image[0], w.Image[0].Windows)
dir, err := w.Image[0].Open() dir, err := w.Image[0].Open()
if err != nil { if err != nil {
panic(err) panic(err)
+120 -14
View File
@@ -9,10 +9,12 @@ import (
"bytes" "bytes"
"crypto/sha1" "crypto/sha1"
"encoding/binary" "encoding/binary"
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"strconv"
"time" "time"
"unicode/utf16" "unicode/utf16"
) )
@@ -39,6 +41,23 @@ const (
FILE_ATTRIBUTE_EA = 0x00040000 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} var wimImageTag = [...]byte{'M', 'S', 'W', 'I', 'M', 0, 0, 0}
type guid struct { type guid struct {
@@ -150,9 +169,9 @@ type direntry struct {
SecurityID uint32 SecurityID uint32
SubdirOffset int64 SubdirOffset int64
Unused1, Unused2 int64 Unused1, Unused2 int64
CreationTime filetime CreationTime Filetime
LastAccessTime filetime LastAccessTime Filetime
LastWriteTime filetime LastWriteTime Filetime
Hash SHA1Hash Hash SHA1Hash
Padding uint32 Padding uint32
ReparseHardLink int64 ReparseHardLink int64
@@ -172,12 +191,14 @@ type streamentry struct {
const streamentrySize = 38 const streamentrySize = 38
type filetime struct { // Filetime represents a Windows time.
type Filetime struct {
LowDateTime uint32 LowDateTime uint32
HighDateTime uint32 HighDateTime uint32
} }
func (ft *filetime) Time() time.Time { // Time returns the time as time.Time.
func (ft *Filetime) Time() time.Time {
// 100-nanosecond intervals since January 1, 1601 // 100-nanosecond intervals since January 1, 1601
nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
// change starting time to the Epoch (00:00:00 UTC, January 1, 1970) // change starting time to the Epoch (00:00:00 UTC, January 1, 1970)
@@ -187,6 +208,67 @@ func (ft *filetime) Time() time.Time {
return time.Unix(0, nsec) 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. // ParseError is returned when the WIM cannot be parsed.
type ParseError struct { type ParseError struct {
Oper string Oper string
@@ -207,7 +289,8 @@ type Reader struct {
r io.ReaderAt r io.ReaderAt
fileData map[SHA1Hash]resourceDescriptor fileData map[SHA1Hash]resourceDescriptor
Image []*Image // The WIM's images. XMLInfo string // The XML information about the WIM.
Image []*Image // The WIM's images.
} }
// Image represents an image within a WIM file. // Image represents an image within a WIM file.
@@ -216,6 +299,8 @@ type Image struct {
offset resourceDescriptor offset resourceDescriptor
sds [][]byte sds [][]byte
rootOffset int64 rootOffset int64
ImageInfo
} }
// StreamHeader contains alternate data stream metadata. // StreamHeader contains alternate data stream metadata.
@@ -238,9 +323,9 @@ type FileHeader struct {
ShortName string ShortName string
Attributes uint32 Attributes uint32
SecurityDescriptor []byte SecurityDescriptor []byte
CreationTime time.Time CreationTime Filetime
LastAccessTime time.Time LastAccessTime Filetime
LastWriteTime time.Time LastWriteTime Filetime
Hash SHA1Hash Hash SHA1Hash
Size int64 Size int64
LinkID int64 LinkID int64
@@ -286,8 +371,30 @@ func NewReader(f io.ReaderAt) (*Reader, error) {
if err != nil { if err != nil {
return nil, err 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.fileData = fileData
r.Image = images r.Image = images
r.XMLInfo = xmlinfo
return r, nil return r, nil
} }
@@ -321,8 +428,7 @@ func (r *Reader) readResource(hdr *resourceDescriptor) ([]byte, error) {
return ioutil.ReadAll(rsrc) return ioutil.ReadAll(rsrc)
} }
// ReadXML reads the XML metadata from a WIM. func (r *Reader) readXML() (string, error) {
func (r *Reader) ReadXML() (string, error) {
if r.hdr.XMLData.CompressedSize() == 0 { if r.hdr.XMLData.CompressedSize() == 0 {
return "", nil return "", nil
} }
@@ -552,9 +658,9 @@ func (img *Image) readNextEntry(r *bufio.Reader) (*File, error) {
f := &File{ f := &File{
FileHeader: FileHeader{ FileHeader: FileHeader{
Attributes: dentry.Attributes, Attributes: dentry.Attributes,
CreationTime: dentry.CreationTime.Time(), CreationTime: dentry.CreationTime,
LastAccessTime: dentry.LastAccessTime.Time(), LastAccessTime: dentry.LastAccessTime,
LastWriteTime: dentry.LastWriteTime.Time(), LastWriteTime: dentry.LastWriteTime,
Hash: dentry.Hash, Hash: dentry.Hash,
Size: offset.OriginalSize, Size: offset.OriginalSize,
Name: name, Name: name,