Commit d467c5e7 authored by Michael Cohen's avatar Michael Cohen
Browse files

Export a model struct with summary information about MFT entry.

parent 1fe4c7fe
Pipeline #68820735 passed with stages
in 8 minutes and 15 seconds
package main
import (
"encoding/json"
"fmt"
kingpin "gopkg.in/alecthomas/kingpin.v2"
......@@ -11,6 +12,9 @@ var (
stat_command = app.Command(
"stat", "inspect the boot record.")
stat_command_detailed = stat_command.Flag(
"verbose", "Show verbose information").Bool()
stat_command_file_arg = stat_command.Arg(
"file", "The image file to inspect",
).Required().File()
......@@ -38,7 +42,23 @@ func doSTAT() {
}
kingpin.FatalIfError(err, "Can not open path")
fmt.Println(mft_entry.DebugString())
if *stat_command_detailed {
fmt.Println(mft_entry.DebugString())
path, err := ntfs.GetFullPath(mft_entry)
fmt.Printf("FullPath: %s\n", path)
if err != nil {
fmt.Printf("FullPath error: %s\n", err)
}
} else {
stat, err := ntfs.ModelMFTEntry(mft_entry)
kingpin.FatalIfError(err, "Can not open path")
serialized, err := json.MarshalIndent(stat, " ", " ")
kingpin.FatalIfError(err, "Marshal")
fmt.Println(string(serialized))
}
}
func init() {
......
package ntfs
import "time"
// This file defines a model for MFT entry.
type TimeStamps struct {
CreateTime time.Time
FileModifiedTime time.Time
MFTModifiedTime time.Time
AccessedTime time.Time
}
type FilenameInfo struct {
Times TimeStamps
Type string
Name string
}
type NTFSFileInformation struct {
FullPath string
MFTID int64
Size int64
Allocated bool
IsDir bool
SI_Times *TimeStamps
Filenames []*FilenameInfo
}
func ModelMFTEntry(mft_entry *MFT_ENTRY) (*NTFSFileInformation, error) {
full_path, _ := GetFullPath(mft_entry)
result := &NTFSFileInformation{
FullPath: full_path,
MFTID: mft_entry.Get("record_number").AsInteger(),
Allocated: mft_entry.Get("flags").Get("ALLOCATED").AsInteger() != 0,
IsDir: mft_entry.Get("flags").Get("DIRECTORY").AsInteger() != 0,
}
si, err := mft_entry.StandardInformation()
if err == nil {
result.SI_Times = &TimeStamps{
CreateTime: time.Unix(si.Get("create_time").
AsInteger(), 0),
FileModifiedTime: time.Unix(si.Get("file_altered_time").
AsInteger(), 0),
MFTModifiedTime: time.Unix(si.Get("mft_altered_time").
AsInteger(), 0),
AccessedTime: time.Unix(si.Get("file_accessed_time").
AsInteger(), 0),
}
}
for _, filename := range mft_entry.FileName() {
result.Filenames = append(result.Filenames, &FilenameInfo{
Times: TimeStamps{
CreateTime: time.Unix(
filename.Get("created").AsInteger(), 0),
FileModifiedTime: time.Unix(
filename.Get("file_modified").AsInteger(), 0),
MFTModifiedTime: time.Unix(
filename.Get("mft_modified").AsInteger(), 0),
AccessedTime: time.Unix(
filename.Get("file_accessed").AsInteger(), 0),
},
Type: filename.Get("name_type").AsString(),
Name: filename.Name(),
})
}
for _, attr := range mft_entry.Attributes() {
// $DATA attribute = 128.
if attr.Get("type").AsInteger() == 128 {
if attr.IsResident() {
result.Size = attr.Get("content_size").AsInteger()
} else {
result.Size = attr.Get("actual_size").AsInteger()
}
break
}
}
return result, nil
}
......@@ -2,6 +2,9 @@ package ntfs
import (
"encoding/binary"
"errors"
"fmt"
"path"
"unicode/utf16"
)
......@@ -18,3 +21,47 @@ func UTF16ToString(bytes []byte) string {
return string(utf16.Decode(u16s))
}
func get_longest_name(file_names []*FILE_NAME) string {
result := ""
for _, fn := range file_names {
name := fn.Name()
if len(result) < len(name) {
result = name
}
}
return result
}
// Traverse the mft entry and attempt to find its owner until the
// root. We return the full path of the MFT entry.
func GetFullPath(mft_entry *MFT_ENTRY) (string, error) {
result := []string{}
seen := make(map[int64]bool)
var err error
for {
id := mft_entry.Get("record_number").AsInteger()
seen[id] = true
file_names := mft_entry.FileName()
if len(file_names) == 0 {
return path.Join(result...), errors.New(fmt.Sprintf(
"Entry %v has no filename", id))
}
result = append([]string{get_longest_name(file_names)}, result...)
mft_entry, err = mft_entry.MFTEntry(
file_names[0].Get("mftReference").AsInteger())
if err != nil {
return path.Join(result...), errors.New(fmt.Sprintf(
"Entry %v has invalid parent", id))
}
_, pres := seen[mft_entry.Get("record_number").AsInteger()]
if pres || len(result) > 20 {
break
}
}
return path.Join(result...), nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment