David K. Bainbridge | 215e024 | 2017-09-05 23:18:24 -0700 | [diff] [blame] | 1 | package winio
|
| 2 |
|
| 3 | import (
|
| 4 | "bytes"
|
| 5 | "encoding/binary"
|
| 6 | "errors"
|
| 7 | )
|
| 8 |
|
| 9 | type fileFullEaInformation struct {
|
| 10 | NextEntryOffset uint32
|
| 11 | Flags uint8
|
| 12 | NameLength uint8
|
| 13 | ValueLength uint16
|
| 14 | }
|
| 15 |
|
| 16 | var (
|
| 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.
|
| 25 | type ExtendedAttribute struct {
|
| 26 | Name string
|
| 27 | Value []byte
|
| 28 | Flags uint8
|
| 29 | }
|
| 30 |
|
| 31 | func 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.
|
| 60 | func 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 |
|
| 73 | func 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.
|
| 123 | func 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 | }
|