main
Raw Download raw file
  1package winio
  2
  3import (
  4	"bytes"
  5	"encoding/binary"
  6	"errors"
  7)
  8
  9type fileFullEaInformation struct {
 10	NextEntryOffset uint32
 11	Flags           uint8
 12	NameLength      uint8
 13	ValueLength     uint16
 14}
 15
 16var (
 17	fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
 18
 19	errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
 20	errEaNameTooLarge  = errors.New("extended attribute name too large")
 21	errEaValueTooLarge = errors.New("extended attribute value too large")
 22)
 23
 24// ExtendedAttribute represents a single Windows EA.
 25type ExtendedAttribute struct {
 26	Name  string
 27	Value []byte
 28	Flags uint8
 29}
 30
 31func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
 32	var info fileFullEaInformation
 33	err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
 34	if err != nil {
 35		err = errInvalidEaBuffer
 36		return ea, nb, err
 37	}
 38
 39	nameOffset := fileFullEaInformationSize
 40	nameLen := int(info.NameLength)
 41	valueOffset := nameOffset + int(info.NameLength) + 1
 42	valueLen := int(info.ValueLength)
 43	nextOffset := int(info.NextEntryOffset)
 44	if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
 45		err = errInvalidEaBuffer
 46		return ea, nb, err
 47	}
 48
 49	ea.Name = string(b[nameOffset : nameOffset+nameLen])
 50	ea.Value = b[valueOffset : valueOffset+valueLen]
 51	ea.Flags = info.Flags
 52	if info.NextEntryOffset != 0 {
 53		nb = b[info.NextEntryOffset:]
 54	}
 55	return ea, nb, err
 56}
 57
 58// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
 59// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
 60func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
 61	for len(b) != 0 {
 62		ea, nb, err := parseEa(b)
 63		if err != nil {
 64			return nil, err
 65		}
 66
 67		eas = append(eas, ea)
 68		b = nb
 69	}
 70	return eas, err
 71}
 72
 73func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
 74	if int(uint8(len(ea.Name))) != len(ea.Name) {
 75		return errEaNameTooLarge
 76	}
 77	if int(uint16(len(ea.Value))) != len(ea.Value) {
 78		return errEaValueTooLarge
 79	}
 80	entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
 81	withPadding := (entrySize + 3) &^ 3
 82	nextOffset := uint32(0)
 83	if !last {
 84		nextOffset = withPadding
 85	}
 86	info := fileFullEaInformation{
 87		NextEntryOffset: nextOffset,
 88		Flags:           ea.Flags,
 89		NameLength:      uint8(len(ea.Name)),
 90		ValueLength:     uint16(len(ea.Value)),
 91	}
 92
 93	err := binary.Write(buf, binary.LittleEndian, &info)
 94	if err != nil {
 95		return err
 96	}
 97
 98	_, err = buf.Write([]byte(ea.Name))
 99	if err != nil {
100		return err
101	}
102
103	err = buf.WriteByte(0)
104	if err != nil {
105		return err
106	}
107
108	_, err = buf.Write(ea.Value)
109	if err != nil {
110		return err
111	}
112
113	_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
114	if err != nil {
115		return err
116	}
117
118	return nil
119}
120
121// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
122// buffer for use with BackupWrite, ZwSetEaFile, etc.
123func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
124	var buf bytes.Buffer
125	for i := range eas {
126		last := false
127		if i == len(eas)-1 {
128			last = true
129		}
130
131		err := writeEa(&buf, &eas[i], last)
132		if err != nil {
133			return nil, err
134		}
135	}
136	return buf.Bytes(), nil
137}