You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ea.go 3.1KB

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