|
@@ -1,137 +1,137 @@
|
|
-package winio
|
|
|
|
-
|
|
|
|
-import (
|
|
|
|
- "bytes"
|
|
|
|
- "encoding/binary"
|
|
|
|
- "errors"
|
|
|
|
-)
|
|
|
|
-
|
|
|
|
-type fileFullEaInformation struct {
|
|
|
|
- NextEntryOffset uint32
|
|
|
|
- Flags uint8
|
|
|
|
- NameLength uint8
|
|
|
|
- ValueLength uint16
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-var (
|
|
|
|
- fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
|
|
|
-
|
|
|
|
- errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
|
|
|
- errEaNameTooLarge = errors.New("extended attribute name too large")
|
|
|
|
- errEaValueTooLarge = errors.New("extended attribute value too large")
|
|
|
|
-)
|
|
|
|
-
|
|
|
|
-// ExtendedAttribute represents a single Windows EA.
|
|
|
|
-type ExtendedAttribute struct {
|
|
|
|
- Name string
|
|
|
|
- Value []byte
|
|
|
|
- Flags uint8
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
|
|
|
- var info fileFullEaInformation
|
|
|
|
- err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
|
|
|
- if err != nil {
|
|
|
|
- err = errInvalidEaBuffer
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- nameOffset := fileFullEaInformationSize
|
|
|
|
- nameLen := int(info.NameLength)
|
|
|
|
- valueOffset := nameOffset + int(info.NameLength) + 1
|
|
|
|
- valueLen := int(info.ValueLength)
|
|
|
|
- nextOffset := int(info.NextEntryOffset)
|
|
|
|
- if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
|
|
|
- err = errInvalidEaBuffer
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
|
|
|
- ea.Value = b[valueOffset : valueOffset+valueLen]
|
|
|
|
- ea.Flags = info.Flags
|
|
|
|
- if info.NextEntryOffset != 0 {
|
|
|
|
- nb = b[info.NextEntryOffset:]
|
|
|
|
- }
|
|
|
|
- return
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
|
|
|
-// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
|
|
|
-func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
|
|
|
- for len(b) != 0 {
|
|
|
|
- ea, nb, err := parseEa(b)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- eas = append(eas, ea)
|
|
|
|
- b = nb
|
|
|
|
- }
|
|
|
|
- return
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
|
|
|
- if int(uint8(len(ea.Name))) != len(ea.Name) {
|
|
|
|
- return errEaNameTooLarge
|
|
|
|
- }
|
|
|
|
- if int(uint16(len(ea.Value))) != len(ea.Value) {
|
|
|
|
- return errEaValueTooLarge
|
|
|
|
- }
|
|
|
|
- entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
|
|
|
- withPadding := (entrySize + 3) &^ 3
|
|
|
|
- nextOffset := uint32(0)
|
|
|
|
- if !last {
|
|
|
|
- nextOffset = withPadding
|
|
|
|
- }
|
|
|
|
- info := fileFullEaInformation{
|
|
|
|
- NextEntryOffset: nextOffset,
|
|
|
|
- Flags: ea.Flags,
|
|
|
|
- NameLength: uint8(len(ea.Name)),
|
|
|
|
- ValueLength: uint16(len(ea.Value)),
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- err := binary.Write(buf, binary.LittleEndian, &info)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- _, err = buf.Write([]byte(ea.Name))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- err = buf.WriteByte(0)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- _, err = buf.Write(ea.Value)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
|
|
|
-// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
|
|
|
-func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
|
|
|
- var buf bytes.Buffer
|
|
|
|
- for i := range eas {
|
|
|
|
- last := false
|
|
|
|
- if i == len(eas)-1 {
|
|
|
|
- last = true
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- err := writeEa(&buf, &eas[i], last)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return buf.Bytes(), nil
|
|
|
|
-}
|
|
|
|
|
|
+package winio
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "bytes"
|
|
|
|
+ "encoding/binary"
|
|
|
|
+ "errors"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+type fileFullEaInformation struct {
|
|
|
|
+ NextEntryOffset uint32
|
|
|
|
+ Flags uint8
|
|
|
|
+ NameLength uint8
|
|
|
|
+ ValueLength uint16
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+var (
|
|
|
|
+ fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
|
|
|
+
|
|
|
|
+ errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
|
|
|
+ errEaNameTooLarge = errors.New("extended attribute name too large")
|
|
|
|
+ errEaValueTooLarge = errors.New("extended attribute value too large")
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+// ExtendedAttribute represents a single Windows EA.
|
|
|
|
+type ExtendedAttribute struct {
|
|
|
|
+ Name string
|
|
|
|
+ Value []byte
|
|
|
|
+ Flags uint8
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
|
|
|
+ var info fileFullEaInformation
|
|
|
|
+ err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
|
|
|
+ if err != nil {
|
|
|
|
+ err = errInvalidEaBuffer
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nameOffset := fileFullEaInformationSize
|
|
|
|
+ nameLen := int(info.NameLength)
|
|
|
|
+ valueOffset := nameOffset + int(info.NameLength) + 1
|
|
|
|
+ valueLen := int(info.ValueLength)
|
|
|
|
+ nextOffset := int(info.NextEntryOffset)
|
|
|
|
+ if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
|
|
|
+ err = errInvalidEaBuffer
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
|
|
|
+ ea.Value = b[valueOffset : valueOffset+valueLen]
|
|
|
|
+ ea.Flags = info.Flags
|
|
|
|
+ if info.NextEntryOffset != 0 {
|
|
|
|
+ nb = b[info.NextEntryOffset:]
|
|
|
|
+ }
|
|
|
|
+ return
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
|
|
|
+// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
|
|
|
+func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
|
|
|
+ for len(b) != 0 {
|
|
|
|
+ ea, nb, err := parseEa(b)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ eas = append(eas, ea)
|
|
|
|
+ b = nb
|
|
|
|
+ }
|
|
|
|
+ return
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
|
|
|
+ if int(uint8(len(ea.Name))) != len(ea.Name) {
|
|
|
|
+ return errEaNameTooLarge
|
|
|
|
+ }
|
|
|
|
+ if int(uint16(len(ea.Value))) != len(ea.Value) {
|
|
|
|
+ return errEaValueTooLarge
|
|
|
|
+ }
|
|
|
|
+ entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
|
|
|
+ withPadding := (entrySize + 3) &^ 3
|
|
|
|
+ nextOffset := uint32(0)
|
|
|
|
+ if !last {
|
|
|
|
+ nextOffset = withPadding
|
|
|
|
+ }
|
|
|
|
+ info := fileFullEaInformation{
|
|
|
|
+ NextEntryOffset: nextOffset,
|
|
|
|
+ Flags: ea.Flags,
|
|
|
|
+ NameLength: uint8(len(ea.Name)),
|
|
|
|
+ ValueLength: uint16(len(ea.Value)),
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err := binary.Write(buf, binary.LittleEndian, &info)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _, err = buf.Write([]byte(ea.Name))
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = buf.WriteByte(0)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _, err = buf.Write(ea.Value)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
|
|
|
+// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
|
|
|
+func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
|
|
|
+ var buf bytes.Buffer
|
|
|
|
+ for i := range eas {
|
|
|
|
+ last := false
|
|
|
|
+ if i == len(eas)-1 {
|
|
|
|
+ last = true
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err := writeEa(&buf, &eas[i], last)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return buf.Bytes(), nil
|
|
|
|
+}
|