From 03f2eeef0966fe1e7f7e6bfbb7acb46f77417bf3 Mon Sep 17 00:00:00 2001 From: Grigoryev Ilya Alekseevich Date: Wed, 1 Apr 2026 00:50:06 +0500 Subject: [PATCH] add validation --- README.md | 2 +- internal/cmd/cmd.go | 98 ++++++++++++++++++++++++++++++ internal/cmd/render.go | 19 +++++- internal/processing/mft/extract.go | 0 internal/processing/mft/parse.go | 1 + internal/processing/mft/show.go | 0 6 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 internal/processing/mft/extract.go create mode 100644 internal/processing/mft/parse.go create mode 100644 internal/processing/mft/show.go diff --git a/README.md b/README.md index 7797135..ffe2b17 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Поддерживаемые форматы файлов В качестве входных файлов утилита поддерживает: - `$MFT` - системный файл в файловой системе NTFS, в котором хранится информация о содержимом тома. -- `$J` (USN Journal) -системный файл в файловой системе NTFS, который хранит журнал изменений на томе. +- `$J` (USN Journal) - системный файл в файловой системе NTFS, который хранит журнал изменений на томе. - `$Boot` - системный файл в файловой системе NTFS, содержащий загрузочный сектор и код запуска. - `$SDS` - системный файл в файловой системе NTFS, содержит список дескрипторов безопасности для всех файлов и каталогов на томе. - `$I30` - атрибут индекса размещения. Связан с каталогами и содержит информацию о файлах и подкаталогах, содержащихся в каталоге. diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index bc8ef95..a010798 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -1,7 +1,9 @@ package cmd import ( + "errors" "fmt" + "reflect" "strings" ) @@ -111,3 +113,99 @@ func ValidateLeaf(cmd any) error { return nil } } + +// Валидация путей. +// Принимает на вход объект и при наличии метода Enabled производит валидацию +func ValidatePath(cmd any) error { + switch v := cmd.(type) { + case interface{ Enabled() error }: + return v.Enabled() + default: + return nil + } +} + +// Проверяет, что у команды с output-блоками включён хотя бы один вывод. +func ValidateOutput(cmd any) error { + if cmd == nil { + return nil + } + + v := reflect.ValueOf(cmd) + if v.Kind() == reflect.Pointer { + if v.IsNil() { + return nil + } + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return nil + } + + var hasOutputBlocks bool + var hasEnabledOutput bool + + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := v.Type().Field(i) + + switch fieldType.Type { + case reflect.TypeOf(CSVOutput{}): + hasOutputBlocks = true + if enabled, ok := callEnabled(field); ok && enabled { + hasEnabledOutput = true + } + + case reflect.TypeOf(JSONOutput{}): + hasOutputBlocks = true + if enabled, ok := callEnabled(field); ok && enabled { + hasEnabledOutput = true + } + + case reflect.TypeOf(BodyOutput{}): + hasOutputBlocks = true + if enabled, ok := callEnabled(field); ok && enabled { + hasEnabledOutput = true + } + } + } + + if hasOutputBlocks && !hasEnabledOutput { + return errors.New("you must specify at least one output path: --csv, --json or --body") + } + + return nil +} + +// Вызов Enabled у поля +// Возвращает результат Enabled() и флаг успешности поиска метода +func callEnabled(v reflect.Value) (bool, bool) { + if !v.IsValid() { + return false, false + } + + // Пробуем метод у самого значения + if method := v.MethodByName("Enabled"); method.IsValid() { + if method.Type().NumIn() == 0 && + method.Type().NumOut() == 1 && + method.Type().Out(0).Kind() == reflect.Bool { + out := method.Call(nil) + return out[0].Bool(), true + } + } + + // Пробуем метод у указателя + if v.CanAddr() { + if method := v.Addr().MethodByName("Enabled"); method.IsValid() { + if method.Type().NumIn() == 0 && + method.Type().NumOut() == 1 && + method.Type().Out(0).Kind() == reflect.Bool { + out := method.Call(nil) + return out[0].Bool(), true + } + } + } + + return false, false +} diff --git a/internal/cmd/render.go b/internal/cmd/render.go index 3e30645..0a1b126 100644 --- a/internal/cmd/render.go +++ b/internal/cmd/render.go @@ -20,7 +20,6 @@ func NewCLI() *CLI { &c.cmd, kong.Name("mftecmd"), kong.Description("Utility for processing $MFT, $J, $LogFile, $Boot, $SDS, $I30"), - kong.UsageOnError(), ) return c @@ -28,8 +27,24 @@ func NewCLI() *CLI { // Метод запуска CLI func (c *CLI) Run() *CMD { + // Парсинг флагов и аргументов ctx, err := c.parser.Parse(os.Args[1:]) - c.parser.FatalIfErrorf(err) + if err != nil { + c.parser.FatalIfErrorf(err) + } + + // Валидируем команды + err = ValidateLeaf(c) + if err != nil { + c.parser.FatalIfErrorf(err) + } + + // Валидируем путь вывода + err = ValidateOutput(c) + if err != nil { + c.parser.FatalIfErrorf(err) + } + _ = ctx return &c.cmd diff --git a/internal/processing/mft/extract.go b/internal/processing/mft/extract.go new file mode 100644 index 0000000..e69de29 diff --git a/internal/processing/mft/parse.go b/internal/processing/mft/parse.go new file mode 100644 index 0000000..6b937d7 --- /dev/null +++ b/internal/processing/mft/parse.go @@ -0,0 +1 @@ +package mft diff --git a/internal/processing/mft/show.go b/internal/processing/mft/show.go new file mode 100644 index 0000000..e69de29