Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
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.
137 lines
3.1 KiB
137 lines
3.1 KiB
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 |
|
}
|
|
|