blob: b37e930d6af6c9acc149e20042455458f83d6d54 [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001package 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
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
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
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
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}