diff --git a/ntfs/cmd/example/main.go b/ntfs/cmd/example/main.go index 2c82a21..493edb4 100644 --- a/ntfs/cmd/example/main.go +++ b/ntfs/cmd/example/main.go @@ -108,7 +108,7 @@ func main() { if v.Format == "NTFS" && v.Driver == `C:\` { starlog.Infoln("开始获取NTFS USN日志,磁盘:", v.Driver) - fileLists, err := wincmd.ListUsnFileFn(v.Driver, func(name string, typed uint8) bool { + fileLists, err := wincmd.ListUsnFileFn(v.Driver, func(name string, typed bool) bool { return true if ok, _ := regexp.MatchString(`\.exe$`, name); ok { return true diff --git a/mft.go b/ntfs/mft/mftoper.go similarity index 55% rename from mft.go rename to ntfs/mft/mftoper.go index 94f50d8..2ea2727 100644 --- a/mft.go +++ b/ntfs/mft/mftoper.go @@ -1,9 +1,7 @@ -package wincmd +package mft import ( - "b612.me/win32api" "b612.me/wincmd/ntfs/binutil" - "b612.me/wincmd/ntfs/mft" "b612.me/wincmd/ntfs/utf16" "encoding/binary" "errors" @@ -11,6 +9,7 @@ import ( "os" "reflect" "runtime" + "strings" "time" "unsafe" ) @@ -24,11 +23,16 @@ type MFTFile struct { IsDir bool Node uint64 } +type FileEntry struct { + Name string + Parent uint64 +} func GetFileListsByMftFn(driver string, fn func(string, bool) bool) ([]MFTFile, error) { var result []MFTFile - fileMap := make(map[win32api.DWORDLONG]FileEntry) - f, size, err := mft.GetMFTFile(driver) + extendMftRecord := make(map[uint64][]Attribute) + fileMap := make(map[uint64]FileEntry) + f, size, err := GetMFTFile(driver) if err != nil { return []MFTFile{}, err } @@ -59,20 +63,41 @@ func GetFileListsByMftFn(driver string, fn func(string, bool) bool) ([]MFTFile, } alreadyGot += int64(got) for j := int64(0); j < 1024*maxRecordSize; j += 1024 { - record, err := mft.ParseRecord(buf[j : j+1024]) + record, err := ParseRecord(buf[j : j+1024]) if err != nil { continue } - if record.Flags&mft.RecordFlagInUse == 1 && record.Flags&mft.RecordFlagIsIndex == 0 { + if record.BaseRecordReference.ToUint64() != 0 { + val := extendMftRecord[record.BaseRecordReference.ToUint64()] + for _, v := range record.Attributes { + if v.Type == AttributeTypeData && v.ActualSize != 0 { + val = append(val, v) + } + } + if len(val) != 0 { + extendMftRecord[record.BaseRecordReference.ToUint64()] = val + } + } + if record.Flags&RecordFlagInUse == 1 && record.Flags&RecordFlagIsIndex == 0 { var file MFTFile - file.IsDir = record.Flags&mft.RecordFlagIsDirectory != 0 + file.IsDir = record.Flags&RecordFlagIsDirectory != 0 file.Node = record.FileReference.ToUint64() parent := uint64(0) for _, v := range record.Attributes { - if v.Type == mft.AttributeTypeFileName { + if v.Type == AttributeTypeData { + file.Size = v.ActualSize + file.Aszie = v.AllocatedSize + } + if v.Type == AttributeTypeStandardInformation { + if len(v.Data) >= 48 { + r := binutil.NewLittleEndianReader(v.Data) + file.ModTime = ConvertFileTime(r.Uint64(0x08)) + } + } + if v.Type == AttributeTypeFileName { name := utf16.DecodeString(v.Data[66:], binary.LittleEndian) if len(file.Name) < len(name) && len(name) > 0 { - if len(name) > 2 && name[len(name)-2] == '~' { + if len(file.Name) > 0 && !strings.Contains(file.Name, "~") { continue } file.Name = name @@ -81,28 +106,17 @@ func GetFileListsByMftFn(driver string, fn func(string, bool) bool) ([]MFTFile, parent = binutil.NewLittleEndianReader(v.Data[:8]).Uint64(0) } } - if v.Type == mft.AttributeTypeData { - file.Size = v.ActualSize - file.Aszie = v.AllocatedSize - } - if v.Type == mft.AttributeTypeStandardInformation { - if len(v.Data) < 48 { - continue - } - r := binutil.NewLittleEndianReader(v.Data) - file.ModTime = mft.ConvertFileTime(r.Uint64(0x08)) - } } + if file.Name != "" { canAdd := fn(file.Name, file.IsDir) if canAdd { result = append(result, file) } if canAdd || file.IsDir { - fileMap[win32api.DWORDLONG(file.Node)] = FileEntry{ + fileMap[uint64(file.Node)] = FileEntry{ Name: file.Name, - Parent: win32api.DWORDLONG(parent), - Type: 0, + Parent: uint64(parent), } } } @@ -112,7 +126,18 @@ func GetFileListsByMftFn(driver string, fn func(string, bool) bool) ([]MFTFile, (*reflect.SliceHeader)(unsafe.Pointer(&result)).Cap = len(result) for k, v := range result { - result[k].Path = GetFullUsnPath(driver, fileMap, win32api.DWORDLONG(v.Node)) + if attrs, ok := extendMftRecord[v.Node]; ok { + if v.Aszie == 0 { + for _, v := range attrs { + if v.Type == AttributeTypeData && v.ActualSize != 0 { + result[k].Size = v.ActualSize + result[k].Aszie = v.AllocatedSize + } + } + } + delete(extendMftRecord, v.Node) + } + result[k].Path = GetFullUsnPath(driver, fileMap, uint64(v.Node)) } fileMap = nil runtime.GC() @@ -125,7 +150,8 @@ func GetFileListsByMft(driver string) ([]MFTFile, error) { func GetFileListsFromMftFileFn(filepath string, fn func(string, bool) bool) ([]MFTFile, error) { var result []MFTFile - fileMap := make(map[win32api.DWORDLONG]FileEntry) + extendMftRecord := make(map[uint64][]Attribute) + fileMap := make(map[uint64]FileEntry) f, err := os.Open(filepath) if err != nil { return []MFTFile{}, err @@ -162,20 +188,41 @@ func GetFileListsFromMftFileFn(filepath string, fn func(string, bool) bool) ([]M } alreadyGot += int64(got) for j := int64(0); j < 1024*maxRecordSize; j += 1024 { - record, err := mft.ParseRecord(buf[j : j+1024]) + record, err := ParseRecord(buf[j : j+1024]) if err != nil { continue } - if record.Flags&mft.RecordFlagInUse == 1 && record.Flags&mft.RecordFlagIsIndex == 0 { + if record.BaseRecordReference.ToUint64() != 0 { + val := extendMftRecord[record.BaseRecordReference.ToUint64()] + for _, v := range record.Attributes { + if v.Type == AttributeTypeData && v.ActualSize != 0 { + val = append(val, v) + } + } + if len(val) != 0 { + extendMftRecord[record.BaseRecordReference.ToUint64()] = val + } + } + if record.Flags&RecordFlagInUse == 1 && record.Flags&RecordFlagIsIndex == 0 { var file MFTFile - file.IsDir = record.Flags&mft.RecordFlagIsDirectory != 0 + file.IsDir = record.Flags&RecordFlagIsDirectory != 0 file.Node = record.FileReference.ToUint64() parent := uint64(0) for _, v := range record.Attributes { - if v.Type == mft.AttributeTypeFileName { + if v.Type == AttributeTypeData { + file.Size = v.ActualSize + file.Aszie = v.AllocatedSize + } + if v.Type == AttributeTypeStandardInformation { + if len(v.Data) >= 48 { + r := binutil.NewLittleEndianReader(v.Data) + file.ModTime = ConvertFileTime(r.Uint64(0x08)) + } + } + if v.Type == AttributeTypeFileName { name := utf16.DecodeString(v.Data[66:], binary.LittleEndian) if len(file.Name) < len(name) && len(name) > 0 { - if len(name) > 2 && name[len(name)-2] == '~' { + if len(file.Name) > 0 && !strings.Contains(file.Name, "~") { continue } file.Name = name @@ -184,17 +231,6 @@ func GetFileListsFromMftFileFn(filepath string, fn func(string, bool) bool) ([]M parent = binutil.NewLittleEndianReader(v.Data[:8]).Uint64(0) } } - if v.Type == mft.AttributeTypeData { - file.Size = v.ActualSize - file.Aszie = v.AllocatedSize - } - if v.Type == mft.AttributeTypeStandardInformation { - if len(v.Data) < 48 { - continue - } - r := binutil.NewLittleEndianReader(v.Data) - file.ModTime = mft.ConvertFileTime(r.Uint64(0x08)) - } } if file.Name != "" { canAdd := fn(file.Name, file.IsDir) @@ -202,10 +238,9 @@ func GetFileListsFromMftFileFn(filepath string, fn func(string, bool) bool) ([]M result = append(result, file) } if canAdd || file.IsDir { - fileMap[win32api.DWORDLONG(file.Node)] = FileEntry{ + fileMap[uint64(file.Node)] = FileEntry{ Name: file.Name, - Parent: win32api.DWORDLONG(parent), - Type: 0, + Parent: uint64(parent), } } } @@ -215,7 +250,18 @@ func GetFileListsFromMftFileFn(filepath string, fn func(string, bool) bool) ([]M (*reflect.SliceHeader)(unsafe.Pointer(&result)).Cap = len(result) for k, v := range result { - result[k].Path = GetFullUsnPath(" ", fileMap, win32api.DWORDLONG(v.Node)) + if attrs, ok := extendMftRecord[v.Node]; ok { + if v.Aszie == 0 { + for _, v := range attrs { + if v.Type == AttributeTypeData && v.ActualSize != 0 { + result[k].Size = v.ActualSize + result[k].Aszie = v.AllocatedSize + } + } + } + delete(extendMftRecord, v.Node) + } + result[k].Path = GetFullUsnPath(" ", fileMap, uint64(v.Node)) } fileMap = nil runtime.GC() @@ -225,3 +271,21 @@ func GetFileListsFromMftFileFn(filepath string, fn func(string, bool) bool) ([]M func GetFileListsFromMftFile(filepath string) ([]MFTFile, error) { return GetFileListsFromMftFileFn(filepath, func(string, bool) bool { return true }) } + +func GetFullUsnPath(diskName string, fileMap map[uint64]FileEntry, id uint64) (name string) { + for id != 0 { + fe := fileMap[id] + if id == fe.Parent { + name = "\\" + name + break + } + if name == "" { + name = fe.Name + } else { + name = fe.Name + "\\" + name + } + id = fe.Parent + } + name = diskName[:len(diskName)-1] + name + return +} diff --git a/ntfs/mft/output.go b/ntfs/mft/output.go index c5b27c6..4ec2f9f 100644 --- a/ntfs/mft/output.go +++ b/ntfs/mft/output.go @@ -5,7 +5,6 @@ import ( "b612.me/wincmd/ntfs/fragment" "bytes" "fmt" - "github.com/t9t/gomft/mft" "io" "os" "runtime" @@ -13,7 +12,6 @@ import ( const supportedOemId = "NTFS " - const isWin = runtime.GOOS == "windows" func GetMFTFileBytes(volume string) ([]byte, error) { @@ -85,12 +83,12 @@ func GetMFTFile(volume string) (io.Reader, int64, error) { return nil, 0, fmt.Errorf("Unable to read $MFT record: %v\n", err) } - record, err := mft.ParseRecord(mftData) + record, err := ParseRecord(mftData) if err != nil { return nil, 0, fmt.Errorf("Unable to parse $MFT record: %v\n", err) } - dataAttributes := record.FindAttributes(mft.AttributeTypeData) + dataAttributes := record.FindAttributes(AttributeTypeData) if len(dataAttributes) == 0 { return nil, 0, fmt.Errorf("No $DATA attribute found in $MFT record\n") } diff --git a/filestats_windows.go b/ntfs/usn/filestats_windows.go similarity index 99% rename from filestats_windows.go rename to ntfs/usn/filestats_windows.go index 36e1208..113df0c 100644 --- a/filestats_windows.go +++ b/ntfs/usn/filestats_windows.go @@ -1,4 +1,4 @@ -package wincmd +package usn import ( "b612.me/win32api" diff --git a/osio_test.go b/ntfs/usn/osio_test.go similarity index 91% rename from osio_test.go rename to ntfs/usn/osio_test.go index 647c562..f40b01b 100644 --- a/osio_test.go +++ b/ntfs/usn/osio_test.go @@ -1,4 +1,4 @@ -package wincmd +package usn import ( "fmt" diff --git a/usn.go b/ntfs/usn/usn.go similarity index 99% rename from usn.go rename to ntfs/usn/usn.go index 2408ca9..e93fe6a 100644 --- a/usn.go +++ b/ntfs/usn/usn.go @@ -1,4 +1,4 @@ -package wincmd +package usn import ( "b612.me/stario"