From bf3bd32ae1ccd33703bb76417dd42547a2ad8268 Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Thu, 4 Jul 2024 23:11:37 +1000 Subject: [PATCH] Suppress EOF errors when seeking empty result sets. (#3593) --- api/tables/table.go | 4 ++-- .../definitions/Server/Utils/CreateMSI.yaml | 7 +++++++ .../definitions/Windows/Memory/Acquisition.yaml | 9 +++++++-- result_sets/api.go | 1 + services/hunt_dispatcher/flows.go | 9 +++++++++ services/hunt_dispatcher/storage.go | 6 ++++++ services/launcher/storage.go | 16 ++++++++++++---- services/vfs_service/directory.go | 5 +++++ vql/server/flows/results.go | 5 +++++ 9 files changed, 54 insertions(+), 8 deletions(-) diff --git a/api/tables/table.go b/api/tables/table.go index ada5bf52d14..99f487fd1e0 100644 --- a/api/tables/table.go +++ b/api/tables/table.go @@ -147,7 +147,7 @@ func getTable( // Seek to the row we need. err = rs_reader.SeekToRow(int64(in.StartRow)) - if err == io.EOF { + if errors.Is(err, io.EOF) { return result, nil } @@ -207,7 +207,7 @@ func getStackTable( // Seek to the row we need. err = rs_reader.SeekToRow(int64(in.StartRow)) - if err == io.EOF { + if errors.Is(err, io.EOF) { return result, nil } diff --git a/artifacts/definitions/Server/Utils/CreateMSI.yaml b/artifacts/definitions/Server/Utils/CreateMSI.yaml index 411f3e372cf..5b25915e45f 100644 --- a/artifacts/definitions/Server/Utils/CreateMSI.yaml +++ b/artifacts/definitions/Server/Utils/CreateMSI.yaml @@ -2,6 +2,13 @@ name: Server.Utils.CreateMSI description: | Build an MSI ready for deployment in the current org. + This artifact depends on the following tools: + + * + * + + You can replace those with suitable MSI builds. + type: SERVER parameters: diff --git a/artifacts/definitions/Windows/Memory/Acquisition.yaml b/artifacts/definitions/Windows/Memory/Acquisition.yaml index 06d31ffe1b3..40707f8c78c 100755 --- a/artifacts/definitions/Windows/Memory/Acquisition.yaml +++ b/artifacts/definitions/Windows/Memory/Acquisition.yaml @@ -12,13 +12,18 @@ tools: github_asset_regex: winpmem_mini_x64.+exe serve_locally: true -precondition: SELECT OS From info() where OS = 'windows' AND Architecture = "amd64" +precondition: | + SELECT OS FROM info() + WHERE OS = 'windows' + AND Architecture = "amd64" sources: - query: | + LET Tempfile <= tempfile(extension=".raw") + SELECT * FROM foreach( row={ - SELECT OSPath, tempfile(extension=".raw", remove_last=TRUE) AS Tempfile + SELECT OSPath FROM Artifact.Generic.Utils.FetchBinary(ToolName="WinPmem64") }, query={ diff --git a/result_sets/api.go b/result_sets/api.go index 70b96a8d1fb..010194e63ad 100644 --- a/result_sets/api.go +++ b/result_sets/api.go @@ -45,6 +45,7 @@ type TimedResultSetWriter interface { } type ResultSetReader interface { + // Returns EOF if the row does not exist in the result set. SeekToRow(start int64) error Rows(ctx context.Context) <-chan *ordereddict.Dict diff --git a/services/hunt_dispatcher/flows.go b/services/hunt_dispatcher/flows.go index 549a9b5f63b..30ccfa7cfca 100644 --- a/services/hunt_dispatcher/flows.go +++ b/services/hunt_dispatcher/flows.go @@ -2,6 +2,8 @@ package hunt_dispatcher import ( "context" + "errors" + "io" "time" api_proto "www.velocidex.com/golang/velociraptor/api/proto" @@ -133,6 +135,13 @@ func (self *HuntDispatcher) GetFlows( // Seek to the row we need. err = rs_reader.SeekToRow(int64(start)) + if errors.Is(err, io.EOF) { + close(output_chan) + rs_reader.Close() + + return output_chan, 0, nil + } + if err != nil { close(output_chan) rs_reader.Close() diff --git a/services/hunt_dispatcher/storage.go b/services/hunt_dispatcher/storage.go index 99e91becb7e..85677a00912 100644 --- a/services/hunt_dispatcher/storage.go +++ b/services/hunt_dispatcher/storage.go @@ -2,7 +2,9 @@ package hunt_dispatcher import ( "context" + "errors" "fmt" + "io" "os" "sync" "sync/atomic" @@ -249,6 +251,10 @@ func (self *HuntStorageManagerImpl) ListHunts( defer rs_reader.Close() err = rs_reader.SeekToRow(offset) + if errors.Is(err, io.EOF) { + return nil, 0, nil + } + if err != nil { return nil, 0, err } diff --git a/services/launcher/storage.go b/services/launcher/storage.go index 588f34f18b9..573665981ec 100644 --- a/services/launcher/storage.go +++ b/services/launcher/storage.go @@ -3,6 +3,7 @@ package launcher import ( "context" "fmt" + "io" "os" "github.com/Velocidex/ordereddict" @@ -101,30 +102,37 @@ func (self *FlowStorageManager) ListFlows( // Try to rebuild the index err = self.buildFlowIndexFromLegacy(ctx, config_obj, client_id) if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("buildFlowIndexFromLegacy %w", err) } rs_reader, err = result_sets.NewResultSetReaderWithOptions( ctx, config_obj, file_store_factory, client_path_manager.FlowIndex(), options) + if err != nil { + return nil, 0, fmt.Errorf("NewResultSetReaderWithOptions %w", err) + } } if err != nil { return nil, 0, err } + result := []*services.FlowSummary{} err = rs_reader.SeekToRow(offset) + if errors.Is(err, io.EOF) { + return result, 0, nil + } + if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("SeekToRow %v %w", offset, err) } // Highly optimized reader for speed. json_chan, err := rs_reader.JSON(ctx) if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("JSON %w", err) } - result := []*services.FlowSummary{} for serialized := range json_chan { summary := &services.FlowSummary{} err = json.Unmarshal(serialized, summary) diff --git a/services/vfs_service/directory.go b/services/vfs_service/directory.go index 2f515f6faab..1951e7a4149 100644 --- a/services/vfs_service/directory.go +++ b/services/vfs_service/directory.go @@ -3,6 +3,7 @@ package vfs_service import ( "context" "errors" + "io" "github.com/Velocidex/ordereddict" "google.golang.org/protobuf/proto" @@ -102,6 +103,10 @@ func renderDBVFS( defer reader.Close() err = reader.SeekToRow(int64(result.StartIdx)) + if errors.Is(err, io.EOF) { + return nil, nil + } + if err != nil { return nil, err } diff --git a/vql/server/flows/results.go b/vql/server/flows/results.go index c093872f701..3e1b3c43454 100644 --- a/vql/server/flows/results.go +++ b/vql/server/flows/results.go @@ -21,6 +21,7 @@ import ( "context" "errors" "fmt" + "io" "github.com/Velocidex/ordereddict" "www.velocidex.com/golang/velociraptor/acls" @@ -181,6 +182,10 @@ func (self SourcePlugin) Call( if arg.StartRow > 0 { err = result_set_reader.SeekToRow(arg.StartRow) + if errors.Is(err, io.EOF) { + return + } + if err != nil { scope.Log("source: %v", err) return