wincmd/ntfs/mft/attributes_test.go

138 lines
4.5 KiB
Go
Raw Normal View History

package mft
import (
"encoding/binary"
"testing"
"time"
"unicode/utf16"
)
func TestConvertFileTime(t *testing.T) {
tests := []struct {
name string
value uint64
want time.Time
}{
{
name: "epoch",
value: 0,
want: time.Date(1601, time.January, 1, 0, 0, 0, 0, time.UTC),
},
{
name: "one second",
value: 10000000,
want: time.Date(1601, time.January, 1, 0, 0, 1, 0, time.UTC),
},
}
for _, tt := range tests {
got := ConvertFileTime(tt.value)
if !got.Equal(tt.want) {
t.Fatalf("%s: ConvertFileTime(%d) = %v, want %v", tt.name, tt.value, got, tt.want)
}
}
}
func TestFileAttributeConstants(t *testing.T) {
if FileAttributeCompressed == FileAttributeOffline {
t.Fatal("FileAttributeCompressed and FileAttributeOffline should differ")
}
}
func TestParseAttributeListParsesNamedEntry(t *testing.T) {
baseRef := FileReference{RecordNumber: 33, SequenceNumber: 2}
entries, err := ParseAttributeList(buildAttributeListEntry(AttributeTypeData, "alt", 7, baseRef, 9))
if err != nil {
t.Fatalf("ParseAttributeList returned error: %v", err)
}
if len(entries) != 1 {
t.Fatalf("len(entries) = %d, want 1", len(entries))
}
if entries[0].Name != "alt" {
t.Fatalf("entries[0].Name = %q, want %q", entries[0].Name, "alt")
}
if entries[0].BaseRecordReference != baseRef {
t.Fatalf("entries[0].BaseRecordReference = %+v, want %+v", entries[0].BaseRecordReference, baseRef)
}
if entries[0].StartingVCN != 7 {
t.Fatalf("entries[0].StartingVCN = %d, want 7", entries[0].StartingVCN)
}
}
func TestParseIndexRootParsesSingleEntry(t *testing.T) {
fileRef := FileReference{RecordNumber: 51, SequenceNumber: 4}
fileNameData := testFileNameData("hello.txt", FileReference{RecordNumber: 9, SequenceNumber: 1}.ToUint64(), FileNameNamespaceWin32)
root, err := ParseIndexRoot(buildIndexRoot(buildIndexEntry(fileRef, fileNameData, 0, 0)))
if err != nil {
t.Fatalf("ParseIndexRoot returned error: %v", err)
}
if len(root.Entries) != 1 {
t.Fatalf("len(root.Entries) = %d, want 1", len(root.Entries))
}
if root.Entries[0].FileReference != fileRef {
t.Fatalf("root.Entries[0].FileReference = %+v, want %+v", root.Entries[0].FileReference, fileRef)
}
if root.Entries[0].FileName.Name != "hello.txt" {
t.Fatalf("root.Entries[0].FileName.Name = %q, want %q", root.Entries[0].FileName.Name, "hello.txt")
}
}
func buildAttributeListEntry(attrType AttributeType, name string, startingVCN uint64, baseRef FileReference, attrID uint16) []byte {
encodedName := utf16.Encode([]rune(name))
entryLength := minAttributeListEntryLength + len(encodedName)*2
buf := make([]byte, entryLength)
binary.LittleEndian.PutUint32(buf[0x00:], uint32(attrType))
binary.LittleEndian.PutUint16(buf[0x04:], uint16(entryLength))
buf[0x06] = byte(len(encodedName))
if len(encodedName) > 0 {
buf[0x07] = 0x1A
for i, v := range encodedName {
binary.LittleEndian.PutUint16(buf[0x1A+i*2:], v)
}
}
binary.LittleEndian.PutUint64(buf[0x08:], startingVCN)
copy(buf[0x10:], encodeRawFileReference(baseRef))
binary.LittleEndian.PutUint16(buf[0x18:], attrID)
return buf
}
func buildIndexEntry(fileRef FileReference, fileNameData []byte, flags uint32, subNodeVCN uint64) []byte {
entryLength := 0x10 + len(fileNameData)
if flags&0b1 != 0 {
entryLength += 8
}
buf := make([]byte, entryLength)
copy(buf[0x00:], encodeRawFileReference(fileRef))
binary.LittleEndian.PutUint16(buf[0x08:], uint16(entryLength))
binary.LittleEndian.PutUint16(buf[0x0A:], uint16(len(fileNameData)))
binary.LittleEndian.PutUint32(buf[0x0C:], flags)
copy(buf[0x10:], fileNameData)
if flags&0b1 != 0 {
binary.LittleEndian.PutUint64(buf[entryLength-8:], subNodeVCN)
}
return buf
}
func buildIndexRoot(entry []byte) []byte {
totalSize := indexRootHeaderLength + len(entry)
buf := make([]byte, indexRootEntryOffset+len(entry))
binary.LittleEndian.PutUint32(buf[0x00:], uint32(AttributeTypeFileName))
binary.LittleEndian.PutUint32(buf[0x04:], uint32(CollationTypeFileName))
binary.LittleEndian.PutUint32(buf[0x08:], 4096)
binary.LittleEndian.PutUint32(buf[0x0C:], 1)
binary.LittleEndian.PutUint32(buf[0x10:], 0x10)
binary.LittleEndian.PutUint32(buf[0x14:], uint32(totalSize))
binary.LittleEndian.PutUint32(buf[0x18:], uint32(totalSize))
copy(buf[indexRootEntryOffset:], entry)
return buf
}
func encodeRawFileReference(ref FileReference) []byte {
buf := make([]byte, 8)
rawRecord := make([]byte, 8)
binary.LittleEndian.PutUint64(rawRecord, ref.RecordNumber)
copy(buf[:6], rawRecord[:6])
binary.LittleEndian.PutUint16(buf[6:], ref.SequenceNumber)
return buf
}