func NewFile(buf []byte) (*File, error) {
	f := File{}
	f.DataOffset = FileHeaderMinLength
	r := bytes.NewReader(buf)
	if err := binary.Read(r, binary.LittleEndian, &f.Header.FileHeader); err != nil {
		return nil, err
	}
	f.Type = f.Header.Type.String()
	if f.Header.Size == [3]uint8{0xFF, 0xFF, 0xFF} {
		if err := binary.Read(r, binary.LittleEndian, &f.Header.ExtendedSize); err != nil {
			return nil, err
		}
		if f.Header.ExtendedSize == 0xFFFFFFFFFFFFFFFF {
			return nil, nil
		}
		f.DataOffset = FileHeaderExtMinLength
	} else {
		f.Header.ExtendedSize = Read3Size(f.Header.Size)
	}
	if buflen := len(buf); f.Header.ExtendedSize > uint64(buflen) {
		return nil, fmt.Errorf("File size too big! File with GUID: %v has length %v, but is only %v bytes big",
			f.Header.GUID, f.Header.ExtendedSize, buflen)
	}
	if ReadOnly {
		f.buf = buf[:f.Header.ExtendedSize]
	} else {
		newBuf := buf[:f.Header.ExtendedSize]
		f.buf = make([]byte, f.Header.ExtendedSize)
		copy(f.buf, newBuf)
	}
	if f.Header.Type == FVFileTypeRaw && f.Header.GUID == *NVAR {
		ns, err := NewNVarStore(f.buf[f.DataOffset:])
		if err != nil {
			log.Errorf("error parsing NVAR store in file %v: %v", f.Header.GUID, err)
		}
		f.NVarStore = ns
	}
	if _, ok := SupportedFiles[f.Header.Type]; !ok {
		return &f, nil
	}
	for i, offset := 0, f.DataOffset; offset < f.Header.ExtendedSize; i++ {
		s, err := NewSection(f.buf[offset:], i)
		if err != nil {
			return nil, fmt.Errorf("error parsing sections of file %v: %v", f.Header.GUID, err)
		}
		if s.Header.ExtendedSize == 0 {
			return nil, fmt.Errorf("invalid length of section of file %v", f.Header.GUID)
		}
		offset += uint64(s.Header.ExtendedSize)
		offset = Align4(offset)
		f.Sections = append(f.Sections, s)
	}
	return &f, nil
}
NewFile parses a sequence of bytes and returns a File
object, if a valid one is passed, or an error. If no error is returned and the File
pointer is nil, it means we've reached the volume free space at the end of the FV.
package uefi
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/linuxboot/fiano/pkg/guid"
	"github.com/linuxboot/fiano/pkg/log"
)
type FVFileType uint8
const (
	FVFileTypeAll FVFileType = iota
	FVFileTypeRaw
	FVFileTypeFreeForm
	FVFileTypeSECCore
	FVFileTypePEICore
	FVFileTypeDXECore
	FVFileTypePEIM
	FVFileTypeDriver
	FVFileTypeCombinedPEIMDriver
	FVFileTypeApplication
	FVFileTypeSMM
	FVFileTypeVolumeImage
	FVFileTypeCombinedSMMDXE
	FVFileTypeSMMCore
	FVFileTypeSMMStandalone
	FVFileTypeSMMCoreStandalone
	FVFileTypeOEMMin   FVFileType = 0xC0
	FVFileTypeOEMMax   FVFileType = 0xDF
	FVFileTypeDebugMin FVFileType = 0xE0
	FVFileTypeDebugMax FVFileType = 0xEF
	FVFileTypePad      FVFileType = 0xF0
	FVFileTypeFFSMin   FVFileType = 0xF0
	FVFileTypeFFSMax   FVFileType = 0xFF
)
var SupportedFiles = map[FVFileType]bool{
	FVFileTypeFreeForm: true,
	FVFileTypeSECCore:  true,
	FVFileTypePEICore:  true,
	FVFileTypeDXECore:  true,
	FVFileTypeDriver:             true,
	FVFileTypeCombinedPEIMDriver: true,
	FVFileTypeApplication:        true,
	FVFileTypeSMM:                true,
	FVFileTypeVolumeImage:        true,
	FVFileTypeCombinedSMMDXE:     true,
	FVFileTypeSMMCore:            true,
	FVFileTypeSMMStandalone:      true,
	FVFileTypeSMMCoreStandalone:  true,
}
var fileTypeNames = map[FVFileType]string{
	FVFileTypeRaw:                "EFI_FV_FILETYPE_RAW",
	FVFileTypeFreeForm:           "EFI_FV_FILETYPE_FREEFORM",
	FVFileTypeSECCore:            "EFI_FV_FILETYPE_SECURITY_CORE",
	FVFileTypePEICore:            "EFI_FV_FILETYPE_PEI_CORE",
	FVFileTypeDXECore:            "EFI_FV_FILETYPE_DXE_CORE",
	FVFileTypePEIM:               "EFI_FV_FILETYPE_PEIM",
	FVFileTypeDriver:             "EFI_FV_FILETYPE_DRIVER",
	FVFileTypeCombinedPEIMDriver: "EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER",
	FVFileTypeApplication:        "EFI_FV_FILETYPE_APPLICATION",
	FVFileTypeSMM:                "EFI_FV_FILETYPE_MM",
	FVFileTypeVolumeImage:        "EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE",
	FVFileTypeCombinedSMMDXE:     "EFI_FV_FILETYPE_COMBINED_MM_DXE",
	FVFileTypeSMMCore:            "EFI_FV_FILETYPE_MM_CORE",
	FVFileTypeSMMStandalone:      "EFI_FV_FILETYPE_MM_STANDALONE",
	FVFileTypeSMMCoreStandalone:  "EFI_FV_FILETYPE_MM_CORE_STANDALONE",
}
func (f FVFileType) String() string {
	switch {
	case FVFileTypeOEMMin <= f && f <= FVFileTypeOEMMax:
		return fmt.Sprintf("EFI_FV_FILETYPE_OEM (%#x)", uint8(f))
	case FVFileTypeDebugMin <= f && f <= FVFileTypeDebugMax:
		return fmt.Sprintf("EFI_FV_FILETYPE_DEBUG (%#x)", uint8(f))
	case FVFileTypeFFSMin < f && f <= FVFileTypeFFSMax:
		return fmt.Sprintf("EFI_FV_FILETYPE_FFS (%#x)", uint8(f))
	case f == FVFileTypePad:
		return "EFI_FV_FILETYPE_FFS_PAD"
	}
	if t, ok := fileTypeNames[f]; ok {
		return t
	}
	return "UNKNOWN"
}
var (
	ZeroGUID = guid.MustParse("00000000-0000-0000-0000-000000000000")
	FFGUID   = guid.MustParse("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF")
)
var fileAlignments = []uint64{
	1,
	16,
	128,
	512,
	1024,
	4 * 1024,
	32 * 1024,
	64 * 1024,
	128 * 1024,
	256 * 1024,
	512 * 1024,
	1024 * 1024,
	2 * 1024 * 1024,
	4 * 1024 * 1024,
	8 * 1024 * 1024,
	16 * 1024 * 1024,
}
const (
	FileHeaderMinLength = 0x18
	FileHeaderExtMinLength = 0x20
	EmptyBodyChecksum uint8 = 0xAA
)
type IntegrityCheck struct {
	Header uint8
	File   uint8
}
type fileAttr uint8
type FileState uint8
const (
	FileStateHeaderConstruction FileState = 0x01
	FileStateHeaderValid        FileState = 0x02
	FileStateDataValid          FileState = 0x04
	FileStateMarkeForUpdate     FileState = 0x08
	FileStateDeleted            FileState = 0x10
	FileStateHeaderInvalid      FileState = 0x20
	FileStateValid FileState = FileStateHeaderConstruction | FileStateHeaderValid | FileStateDataValid
)
type FileHeader struct {
	GUID       guid.GUID       
	Checksum   IntegrityCheck `json:"-"`
	Type       FVFileType
	Attributes fileAttr
	Size       [3]uint8 `json:"-"`
	State      FileState
}
func (a fileAttr) IsLarge() bool {
	return a&0x01 != 0
}
func (a fileAttr) GetAlignment() uint64 {
	alignVal := (a & 0x38) >> 3
	alignVal |= (a & 0x02) << 2
	return fileAlignments[alignVal]
}
func (a *fileAttr) setLarge(large bool) {
	if large {
		*a |= 0x01
	} else {
		*a &= 0xFE
	}
}
func (a fileAttr) HasChecksum() bool {
	return a&0x40 != 0
}
func (fh *FileHeader) SetState(s FileState) {
	fh.State = s ^ FileState(Attributes.ErasePolarity)
}
func (f *File) HeaderLen() uint64 {
	if f.Header.Attributes.IsLarge() {
		return FileHeaderExtMinLength
	}
	return FileHeaderMinLength
}
func (f *File) ChecksumHeader() uint8 {
	fh := f.Header
	headerSize := FileHeaderMinLength
	if fh.Attributes.IsLarge() {
		headerSize = FileHeaderExtMinLength
	}
	sum := Checksum8(f.buf[:headerSize])
	sum -= fh.Checksum.File
	sum -= uint8(fh.State)
	return sum
}
type FileHeaderExtended struct {
	FileHeader
	ExtendedSize uint64 `json:"-"`
}
type File struct {
	Header FileHeaderExtended
	Type   string
	Sections  []*Section `json:",omitempty"`
	NVarStore *NVarStore `json:",omitempty"`
	buf         []byte
	ExtractPath string
	DataOffset  uint64
}
func (f *File) Buf() []byte {
	return f.buf
}
func (f *File) SetBuf(buf []byte) {
	f.buf = buf
}
func (f *File) Apply(v Visitor) error {
	return v.Visit(f)
}
func (f *File) ApplyChildren(v Visitor) error {
	if f.NVarStore != nil {
		if err := f.NVarStore.Apply(v); err != nil {
			return err
		}
		return nil
	}
	for _, s := range f.Sections {
		if err := s.Apply(v); err != nil {
			return err
		}
	}
	return nil
}
func (f *File) SetSize(size uint64, resizeFile bool) {
	fh := &f.Header
	fh.ExtendedSize = size
	fh.Attributes.setLarge(false)
	if fh.ExtendedSize > 0xFFFFFF {
		if resizeFile {
			fh.ExtendedSize += FileHeaderExtMinLength - FileHeaderMinLength
		}
		fh.Attributes.setLarge(true)
	}
	fh.Size = Write3Size(fh.ExtendedSize)
}
func (f *File) ChecksumAndAssemble(fileData []byte) error {
	fh := &f.Header
	header := new(bytes.Buffer)
	err := binary.Write(header, binary.LittleEndian, fh)
	if err != nil {
		return fmt.Errorf("unable to construct binary header of file %v, got %v",
			fh.GUID, err)
	}
	f.buf = header.Bytes()
	fh.Checksum.Header -= f.ChecksumHeader()
	fh.Checksum.File = EmptyBodyChecksum
	if fh.Attributes.HasChecksum() {
		fh.Checksum.File = 0 - Checksum8(fileData)
	}
	header = new(bytes.Buffer)
	if fh.Attributes.IsLarge() {
		err = binary.Write(header, binary.LittleEndian, fh)
	} else {
		err = binary.Write(header, binary.LittleEndian, fh.FileHeader)
	}
	if err != nil {
		return err
	}
	f.buf = header.Bytes()
	f.buf = append(f.buf, fileData...)
	return nil
}
func CreatePadFile(size uint64) (*File, error) {
	if size < FileHeaderMinLength {
		return nil, fmt.Errorf("size too small! min size required is %#x bytes, requested %#x",
			FileHeaderMinLength, size)
	}
	f := File{}
	fh := &f.Header
	if Attributes.ErasePolarity == 0xFF {
		fh.GUID = *FFGUID
	} else if Attributes.ErasePolarity == 0 {
		fh.GUID = *ZeroGUID
	} else {
		return nil, fmt.Errorf("erase polarity not 0x00 or 0xFF, got %#x", Attributes.ErasePolarity)
	}
	fh.Attributes = 0
	f.SetSize(size, false)
	fh.Type = FVFileTypePad
	f.Type = fh.Type.String()
	var fileData []byte
	fileData = make([]byte, size-FileHeaderMinLength)
	if fh.Attributes.IsLarge() {
		fileData = make([]byte, size-FileHeaderExtMinLength)
	}
	for i, dataLen := 0, len(fileData); i < dataLen; i++ {
		fileData[i] = Attributes.ErasePolarity
	}
	fh.SetState(FileStateValid)
	if err := f.ChecksumAndAssemble(fileData); err != nil {
		return nil, err
	}
	return &f, nil
}