From 73a6e77823a4c3f390b1f6c0e30021c66f474bda Mon Sep 17 00:00:00 2001 From: fabienfl Date: Wed, 17 May 2023 10:35:30 +0200 Subject: [PATCH 01/14] OrcLib: FileFind: fix missleading log --- src/OrcLib/FileFind.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrcLib/FileFind.cpp b/src/OrcLib/FileFind.cpp index 9067c1f3..a2f40192 100644 --- a/src/OrcLib/FileFind.cpp +++ b/src/OrcLib/FileFind.cpp @@ -4406,7 +4406,7 @@ HRESULT FileFind::Find( }; } - if (FAILED(hr = walk.Walk(cbs))) + if (FAILED(hr = walk.Walk(cbs)) && hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) { Log::Debug(L"Failed to walk volume '{}' [{}]", aLoc->GetLocation(), SystemError(hr)); } From f08b53c43a0777bc242e675ed503bb0288616550 Mon Sep 17 00:00:00 2001 From: fabienfl Date: Wed, 17 May 2023 12:29:29 +0200 Subject: [PATCH 02/14] OrcLib: FileFind: add overload FileFind::Find expecting Location --- src/OrcLib/FileFind.cpp | 192 +++++++++++++++++++++++----------------- src/OrcLib/FileFind.h | 11 ++- 2 files changed, 121 insertions(+), 82 deletions(-) diff --git a/src/OrcLib/FileFind.cpp b/src/OrcLib/FileFind.cpp index a2f40192..20f60b35 100644 --- a/src/OrcLib/FileFind.cpp +++ b/src/OrcLib/FileFind.cpp @@ -4292,7 +4292,7 @@ HRESULT FileFind::ExcludeMatch(const std::shared_ptr& aMatch) HRESULT FileFind::Find( const LocationSet& locations, - FileFind::FoundMatchCallback aCallback, + FileFind::FoundMatchCallback foundMatchCallback, bool bParseI30Data, ResurrectRecordsMode resurrectRecordsMode) { @@ -4318,112 +4318,142 @@ HRESULT FileFind::Find( bool hasSomeFailure = false; - for (const auto& aLoc : locs) + for (const auto& location : locs) + { + hr = Find(location, foundMatchCallback, bParseI30Data, resurrectRecordsMode); + if (FAILED(hr)) + { + Log::Error(L"Failed FileFind::Find on '{}'", location->GetLocation()); + hasSomeFailure = true; + continue; + } + } + + if (hasSomeFailure) + { + return E_FAIL; + } + + return S_OK; +} + +HRESULT FileFind::Find( + const std::shared_ptr& location, + FileFind::FoundMatchCallback aCallback, + bool bParseI30Data, + ResurrectRecordsMode resurrectRecordsMode) +{ + HRESULT hr = E_FAIL; + + if (m_ExactNameTerms.empty() && m_ExactPathTerms.empty() && m_Terms.empty() && m_SizeTerms.empty() + && m_I30ExactNameTerms.empty() && m_I30ExactPathTerms.empty() && m_I30Terms.empty()) + return S_OK; + + m_NeededHash = GetNeededHashAlgorithms(); + + hr = InitializeYara(); + if (FAILED(hr)) { - HRESULT hr = E_FAIL; - MFTWalker walk; + return hr; + } + + MFTWalker walk; - m_FullNameBuilder = walk.GetFullNameBuilder(); - m_InLocationBuilder = walk.GetInLocationBuilder(); + m_FullNameBuilder = walk.GetFullNameBuilder(); + m_InLocationBuilder = walk.GetInLocationBuilder(); - m_pVolReader = aLoc->GetReader(); + m_pVolReader = location->GetReader(); - if (FAILED(hr = walk.Initialize(aLoc, resurrectRecordsMode))) + if (FAILED(hr = walk.Initialize(location, resurrectRecordsMode))) + { + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_SYSTEM_LIMITATION)) { - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_SYSTEM_LIMITATION)) - { - Log::Debug(L"File system not eligible for volume '{}' [{}]", aLoc->GetLocation(), SystemError(hr)); - } - else - { - Log::Critical(L"Failed to init walk for volume '{}' [{}]", aLoc->GetLocation(), SystemError(hr)); - hasSomeFailure = true; - } + Log::Debug(L"File system not eligible for volume '{}' [{}]", location->GetLocation(), SystemError(hr)); } else { - MFTWalker::Callbacks cbs; - auto pCB = aCallback; + Log::Critical(L"Failed to init walk for volume '{}' [{}]", location->GetLocation(), SystemError(hr)); + return hr; + } + } + else + { + MFTWalker::Callbacks cbs; + auto pCB = aCallback; - bool bStop = false; + bool bStop = false; - cbs.ElementCallback = - [this, aCallback, &bStop, &hr](const std::shared_ptr& volreader, MFTRecord* pElt) { - DBG_UNREFERENCED_PARAMETER(volreader); - try + cbs.ElementCallback = + [this, aCallback, &bStop, &hr](const std::shared_ptr& volreader, MFTRecord* pElt) { + DBG_UNREFERENCED_PARAMETER(volreader); + try + { + if (pElt) { - if (pElt) + if (FAILED(hr = FindMatch(pElt, bStop, aCallback))) { - if (FAILED(hr = FindMatch(pElt, bStop, aCallback))) - { - Log::Error(L"FindMatch failed"); - pElt->CleanCachedData(); - return; - } + Log::Error(L"FindMatch failed"); pElt->CleanCachedData(); + return; } + pElt->CleanCachedData(); } - catch (WCHAR* e) - { - Log::Error(L"Could not parse record: '{}'", e); - } - return; - }; - - cbs.ProgressCallback = [&bStop](ULONG ulProgress) -> HRESULT { - if (bStop) + } + catch (WCHAR* e) { - return HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES); + Log::Error(L"Could not parse record: '{}'", e); } - return S_OK; + return; }; - if (bParseI30Data && (!m_I30ExactNameTerms.empty() || !m_I30ExactPathTerms.empty() || !m_I30Terms.empty())) + cbs.ProgressCallback = [&bStop](ULONG ulProgress) -> HRESULT { + if (bStop) { - cbs.I30Callback = [this, aCallback, &bStop, &hr]( - const std::shared_ptr& volreader, - MFTRecord* pElt, - const PINDEX_ENTRY pEntry, - const PFILE_NAME pFileName, - bool bCarvedEntry) { - DBG_UNREFERENCED_PARAMETER(volreader); - DBG_UNREFERENCED_PARAMETER(bCarvedEntry); - DBG_UNREFERENCED_PARAMETER(pEntry); - DBG_UNREFERENCED_PARAMETER(pElt); - try - { - if (FAILED(hr = FindI30Match(pFileName, bStop, aCallback))) - { - Log::Error(L"FindI30Match failed"); - return; - } - } - catch (WCHAR* e) + return HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES); + } + return S_OK; + }; + + if (bParseI30Data && (!m_I30ExactNameTerms.empty() || !m_I30ExactPathTerms.empty() || !m_I30Terms.empty())) + { + cbs.I30Callback = [this, aCallback, &bStop, &hr]( + const std::shared_ptr& volreader, + MFTRecord* pElt, + const PINDEX_ENTRY pEntry, + const PFILE_NAME pFileName, + bool bCarvedEntry) { + DBG_UNREFERENCED_PARAMETER(volreader); + DBG_UNREFERENCED_PARAMETER(bCarvedEntry); + DBG_UNREFERENCED_PARAMETER(pEntry); + DBG_UNREFERENCED_PARAMETER(pElt); + try + { + if (FAILED(hr = FindI30Match(pFileName, bStop, aCallback))) { - Log::Error(L"Could not parse record: '{}'", e); + Log::Error(L"FindI30Match failed"); + return; } - return; - }; - } - - if (FAILED(hr = walk.Walk(cbs)) && hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) - { - Log::Debug(L"Failed to walk volume '{}' [{}]", aLoc->GetLocation(), SystemError(hr)); - } - else - { - Log::Debug("Done"); - walk.Statistics(L"Done"); - } + } + catch (WCHAR* e) + { + Log::Error(L"Could not parse record: '{}'", e); + } + return; + }; } - } - if (hasSomeFailure) - { - return E_FAIL; + if (FAILED(hr = walk.Walk(cbs)) && hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) + { + Log::Debug(L"Failed to walk volume '{}' [{}]", location->GetLocation(), SystemError(hr)); + } + else + { + Log::Debug("Done"); + walk.Statistics(L"Done"); + } } - return S_OK; + return hr; } void FileFind::PrintSpecs() const diff --git a/src/OrcLib/FileFind.h b/src/OrcLib/FileFind.h index 168c2340..bbcfd6e6 100644 --- a/src/OrcLib/FileFind.h +++ b/src/OrcLib/FileFind.h @@ -512,7 +512,16 @@ class FileFind bool bParseI30Data, ResurrectRecordsMode resurrectRecordsMode); - const std::vector>& Matches() const { return m_Matches; } + HRESULT Find( + const std::shared_ptr& location, + FileFind::FoundMatchCallback aCallback, + bool bParseI30Data, + ResurrectRecordsMode resurrectRecordsMode); + + const std::vector>& Matches() const + { + return m_Matches; + } void PrintSpecs() const; From 1007c2ee6468e5c246f5eddec7b0262d952a640b Mon Sep 17 00:00:00 2001 From: fabienfl Date: Wed, 17 May 2023 12:30:19 +0200 Subject: [PATCH 03/14] OrcCommand: GetThis: fix resurrectRecord option --- src/OrcCommand/Command/GetThis/GetThis_Run.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrcCommand/Command/GetThis/GetThis_Run.cpp b/src/OrcCommand/Command/GetThis/GetThis_Run.cpp index 2e60c35f..1c51f6af 100644 --- a/src/OrcCommand/Command/GetThis/GetThis_Run.cpp +++ b/src/OrcCommand/Command/GetThis/GetThis_Run.cpp @@ -1431,7 +1431,7 @@ HRESULT Main::FindMatchingSamples() config.Locations, std::bind(&Main::OnMatchingSample, this, std::placeholders::_1, std::placeholders::_2), false, - ResurrectRecordsMode::kNo); + config.resurrectRecordsMode); if (FAILED(hr)) { From 3f1dd5781e65485e17c5c488137b6dfd9d74c04e Mon Sep 17 00:00:00 2001 From: fabienfl Date: Wed, 17 May 2023 12:31:02 +0200 Subject: [PATCH 04/14] OrcCommand: Log: UtilitiesLoggerConfiguration: fix log file option --- src/OrcCommand/Log/UtilitiesLoggerConfiguration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrcCommand/Log/UtilitiesLoggerConfiguration.cpp b/src/OrcCommand/Log/UtilitiesLoggerConfiguration.cpp index 987648ca..394b9fca 100644 --- a/src/OrcCommand/Log/UtilitiesLoggerConfiguration.cpp +++ b/src/OrcCommand/Log/UtilitiesLoggerConfiguration.cpp @@ -835,7 +835,7 @@ void UtilitiesLoggerConfiguration::Parse(const ConfigItem& item, UtilitiesLogger configuration.file.level = ::ParseLogLevel(item[CONFIGITEM_LOG_LOGFILE_NODE]); configuration.file.backtraceTrigger = ::ParseBacktraceLevel(item[CONFIGITEM_LOG_LOGFILE_NODE]); - if (item[CONFIGITEM_LOG_LOGFILE_OUTPUT]) + if (item[CONFIGITEM_LOG_LOGFILE_NODE][CONFIGITEM_LOG_LOGFILE_OUTPUT]) { const auto& outputItem = item[CONFIGITEM_LOG_LOGFILE_NODE][CONFIGITEM_LOG_LOGFILE_OUTPUT]; From ddb14c03c83ad72c004617da36b8a4d73488dca5 Mon Sep 17 00:00:00 2001 From: fabienfl Date: Wed, 17 May 2023 12:32:35 +0200 Subject: [PATCH 05/14] OrcLib: USNJournalWalkerOffline: do not 'resurrect' records --- src/OrcLib/USNJournalWalkerOffline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrcLib/USNJournalWalkerOffline.cpp b/src/OrcLib/USNJournalWalkerOffline.cpp index 7747d92a..5fed6e8d 100644 --- a/src/OrcLib/USNJournalWalkerOffline.cpp +++ b/src/OrcLib/USNJournalWalkerOffline.cpp @@ -110,7 +110,7 @@ HRESULT USNJournalWalkerOffline::EnumJournal(const IUSNJournalWalker::Callbacks& MFTWalker walk; - if (FAILED(hr = walk.Initialize(locations.begin()->second, ResurrectRecordsMode::kYes))) + if (FAILED(hr = walk.Initialize(locations.begin()->second, ResurrectRecordsMode::kNo))) { Log::Error(L"Failed during MFT walk initialisation [{}]", SystemError(hr)); return hr; From 4a8f093596d4e2b8f3a377d9dd8eb70c17c059ab Mon Sep 17 00:00:00 2001 From: fabienfl Date: Wed, 17 May 2023 14:43:11 +0200 Subject: [PATCH 06/14] OrcCommand: USNJournal: fix location resolution USNInfo was not working when some shadow copy were available because when resolving locations all versions of an USN journal were fetched erasing the previous found until the older one (which could be empty). --- .../Command/USNInfo/USNInfo_Run.cpp | 114 ++++++++++-------- src/OrcLib/USNJournalWalkerOffline.cpp | 14 +-- src/OrcLib/USNJournalWalkerOffline.h | 3 +- 3 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp b/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp index 09bb9b4b..68223bf3 100644 --- a/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp +++ b/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp @@ -152,15 +152,24 @@ HRESULT Main::Run() return hr; } - const auto& unique_locs = config.locs.GetAltitudeLocations(); + const auto& locs = config.locs.GetAltitudeLocations(); std::vector> locations; + std::vector> allLocations; - // keep only the locations we're parsing - std::copy_if( - std::cbegin(unique_locs), - std::cend(unique_locs), - std::back_inserter(locations), - [](const std::shared_ptr& item) -> bool { return item->GetParse(); }); + std::copy_if(begin(locs), end(locs), back_inserter(locations), [](const std::shared_ptr& loc) { + if (loc == nullptr) + return false; + + return (loc->GetParse() && loc->IsNTFS()); + }); + + if (locations.empty()) + { + Log::Critical( + L"No NTFS volumes configured for parsing. Use \"*\" to parse all mounted volumes or list the volumes you " + L"want parsed"); + return E_INVALIDARG; + } if (config.output.Type == OutputSpec::Kind::Archive) { @@ -172,8 +181,9 @@ HRESULT Main::Run() } } - BOOST_SCOPE_EXIT(&config, &m_outputs) { m_outputs.CloseAll(config.output); } - BOOST_SCOPE_EXIT_END; + Guard::Scope closeOnExit([&]() { + m_outputs.CloseAll(config.output); + }); hr = m_outputs.GetWriters(config.output, L"USNInfo", locations); if (FAILED(hr)) @@ -182,57 +192,59 @@ HRESULT Main::Run() return hr; } - hr = m_outputs.ForEachOutput( - config.output, [this](const MultipleOutput::OutputPair& dir) -> HRESULT { - m_console.Print(L"Parsing volume '{}'", dir.first.m_pLoc->GetLocation()); - USNJournalWalkerOffline walker; + auto outputIt = std::begin(m_outputs.Outputs()); + for (const auto& loc : locations) + { + Guard::Scope onExit([this, &outputIt]() { + m_outputs.CloseOne(config.output, *outputIt); + outputIt++; + }); - HRESULT hr = walker.Initialize(dir.first.m_pLoc); - if (FAILED(hr)) - { - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_SYSTEM_LIMITATION)) - { - Log::Warn(L"File system not eligible for volume '{}'", dir.first.m_pLoc->GetLocation()); - return S_OK; - } - - Log::Critical( - L"Failed to init walk for volume '{}' [{}]", dir.first.m_pLoc->GetLocation(), SystemError(hr)); - return hr; - } + m_console.Print(L"Parsing: {} [{}]", loc->GetLocation(), boost::join(loc->GetPaths(), L", ")); + USNJournalWalkerOffline walker; - if (!walker.GetUsnJournal()) + HRESULT hr = walker.Initialize(loc); + if (FAILED(hr)) + { + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_SYSTEM_LIMITATION)) { - Log::Warn(L"Did not find a USN journal on following volume '{}'", dir.first.m_pLoc->GetLocation()); - return S_OK; + Log::Warn(L"File system not eligible for volume '{}'", loc->GetLocation()); + continue; } - IUSNJournalWalker::Callbacks callbacks; - callbacks.RecordCallback = - [](const std::shared_ptr& volreader, WCHAR* szFullName, USN_RECORD* pElt) {}; + Log::Critical(L"Failed to init walk for volume '{}' [{}]", loc->GetLocation(), SystemError(hr)); + continue; + } - hr = walker.EnumJournal(callbacks); - if (FAILED(hr)) - { - Log::Error(L"Failed to enum MFT records '{}' [{}]", dir.first.m_pLoc->GetLocation(), SystemError(hr)); - return S_OK; - } + if (!walker.GetUsnJournal()) + { + Log::Warn(L"Did not find a USN journal on following volume '{}'", loc->GetLocation()); + continue; + } - callbacks.RecordCallback = - [this, dir](const std::shared_ptr& volreader, WCHAR* szFullName, USN_RECORD* pElt) { - USNRecordInformation(*dir.second, volreader, szFullName, pElt); - }; + IUSNJournalWalker::Callbacks callbacks; + callbacks.RecordCallback = + [](const std::shared_ptr& volreader, WCHAR* szFullName, USN_RECORD* pElt) {}; - hr = walker.ReadJournal(callbacks); - if (FAILED(hr)) - { - Log::Error(L"Failed to walk volume '{}' [{}]", dir.first.m_pLoc->GetLocation(), SystemError(hr)); - return S_OK; - } + hr = walker.EnumJournal(callbacks); + if (FAILED(hr)) + { + Log::Error(L"Failed to enum MFT records '{}' [{}]", loc->GetLocation(), SystemError(hr)); + continue; + } - Log::Info(L"Done"); - return S_OK; - }); + callbacks.RecordCallback = + [this, &outputIt](const std::shared_ptr& volreader, WCHAR* szFullName, USN_RECORD* pElt) { + USNRecordInformation(*outputIt->second, volreader, szFullName, pElt); + }; + + hr = walker.ReadJournal(callbacks); + if (FAILED(hr)) + { + Log::Error(L"Failed to walk volume '{}' [{}]", loc->GetLocation(), SystemError(hr)); + continue; + } + } if (FAILED(hr)) { diff --git a/src/OrcLib/USNJournalWalkerOffline.cpp b/src/OrcLib/USNJournalWalkerOffline.cpp index 5fed6e8d..86805272 100644 --- a/src/OrcLib/USNJournalWalkerOffline.cpp +++ b/src/OrcLib/USNJournalWalkerOffline.cpp @@ -24,7 +24,7 @@ static const auto ROOT_USN = 0x0005000000000005LL; DWORD USNJournalWalkerOffline::m_BufferSize = 0x10000; USNJournalWalkerOffline::USNJournalWalkerOffline() - : m_Locations() + : m_location() { m_dwlRootUSN = ROOT_USN; m_cchMaxComponentLength = 255; @@ -61,13 +61,11 @@ HRESULT USNJournalWalkerOffline::Initialize(const std::shared_ptr& loc fileFind.AddTerm(fs); - std::shared_ptr added; - m_Locations.AddLocation(loc, added, true); - m_Locations.Consolidate(true, FSVBR::FSType::NTFS); + m_location = loc; if (FAILED( hr = fileFind.Find( - m_Locations, + m_location, [this, hr](const std::shared_ptr& aFileMatch, bool& bStop) { Log::Info( L"Found USN journal {}: {}", @@ -103,14 +101,12 @@ HRESULT USNJournalWalkerOffline::EnumJournal(const IUSNJournalWalker::Callbacks& { HRESULT hr = E_FAIL; - const LocationSet::Locations& locations(m_Locations.GetLocations()); - - if (locations.size() == 0) + if (m_location == nullptr) return hr; MFTWalker walk; - if (FAILED(hr = walk.Initialize(locations.begin()->second, ResurrectRecordsMode::kNo))) + if (FAILED(hr = walk.Initialize(m_location, ResurrectRecordsMode::kNo))) { Log::Error(L"Failed during MFT walk initialisation [{}]", SystemError(hr)); return hr; diff --git a/src/OrcLib/USNJournalWalkerOffline.h b/src/OrcLib/USNJournalWalkerOffline.h index 563e9522..7c49d0d6 100644 --- a/src/OrcLib/USNJournalWalkerOffline.h +++ b/src/OrcLib/USNJournalWalkerOffline.h @@ -52,8 +52,7 @@ class USNJournalWalkerOffline static void SetBufferSize(DWORD size); private: - LocationSet m_Locations; - + std::shared_ptr m_location; std::shared_ptr m_USNJournal; static DWORD m_BufferSize; From abf39516d7dedc9ae78ddfcb13c89599daf3cfa9 Mon Sep 17 00:00:00 2001 From: fabienfl Date: Fri, 26 May 2023 15:03:45 +0200 Subject: [PATCH 07/14] OrcLib: LocationSet: accept wildcard as exclude path --- src/OrcLib/LocationSet.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/OrcLib/LocationSet.cpp b/src/OrcLib/LocationSet.cpp index 5b8748dc..992b5f82 100644 --- a/src/OrcLib/LocationSet.cpp +++ b/src/OrcLib/LocationSet.cpp @@ -223,9 +223,11 @@ void GetExcludedVolumeLocations( const LocationSet::PathExcludes& excludedPaths, std::vector& excludedLocations) { + const bool excludeAll = excludedPaths.find(L"*") != std::cend(excludedPaths); + for (const auto& path : volume.Paths) { - if (excludedPaths.find(path) != std::cend(excludedPaths)) + if (excludeAll || excludedPaths.find(path) != std::cend(excludedPaths)) { std::copy( std::cbegin(volume.Locations), std::cend(volume.Locations), std::back_inserter(excludedLocations)); @@ -236,7 +238,7 @@ void GetExcludedVolumeLocations( for (const auto& location : volume.Locations) { - if (excludedPaths.find(location->GetLocation()) != std::cend(excludedPaths)) + if (excludeAll || excludedPaths.find(location->GetLocation()) != std::cend(excludedPaths)) { std::copy( std::cbegin(volume.Locations), std::cend(volume.Locations), std::back_inserter(excludedLocations)); From 1469f75b00e19ff2e8ab5ae6dac8a26ce9ad026a Mon Sep 17 00:00:00 2001 From: fabienfl Date: Fri, 26 May 2023 15:05:15 +0200 Subject: [PATCH 08/14] OrcLib: Location: use parent volume identifier for snapshot's This will make the output file name for shadow copies more readable. --- src/OrcLib/Location.cpp | 23 ++++++++++++++++++----- src/OrcLib/VolumeShadowCopies.h | 1 + 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/OrcLib/Location.cpp b/src/OrcLib/Location.cpp index 03ec5dd0..fa3f7b7e 100644 --- a/src/OrcLib/Location.cpp +++ b/src/OrcLib/Location.cpp @@ -394,6 +394,11 @@ void Location::EnumerateShadowCopies( return; } + for (auto& shadow : shadows) + { + shadow.parentIdentifier = GetIdentifier(); + } + // Most recent snapshot first std::sort(std::begin(shadows), std::end(shadows), [](const auto& lhs, const auto& rhs) { return lhs.CreationTime > rhs.CreationTime; @@ -715,13 +720,21 @@ void Location::MakeIdentifier() } break; case Type::Snapshot: { - wregex r(REGEX_SNAPSHOT, regex_constants::icase); - wsmatch s; - - if (regex_match(m_Location, s, r)) + if (m_Shadow) { + m_Identifier = fmt::format(L"{}_{}", m_Shadow->parentIdentifier, m_Shadow->guid); + } + else + { + Log::Error(L"Location::MakeIdentifier: unexpected code path"); + wregex r(REGEX_SNAPSHOT, regex_constants::icase); + wsmatch s; - m_Identifier = L"Snapshot_" + s[REGEX_SNAPSHOT_NUM].str(); + if (regex_match(m_Location, s, r)) + { + + m_Identifier = L"Snapshot_" + s[REGEX_SNAPSHOT_NUM].str(); + } } } break; diff --git a/src/OrcLib/VolumeShadowCopies.h b/src/OrcLib/VolumeShadowCopies.h index 579e9e84..3bd82341 100644 --- a/src/OrcLib/VolumeShadowCopies.h +++ b/src/OrcLib/VolumeShadowCopies.h @@ -36,6 +36,7 @@ class VolumeShadowCopies VSS_TIMESTAMP CreationTime; GUID guid; std::shared_ptr parentVolume; + std::wstring parentIdentifier; Shadow( LPCWSTR szVolume, From 8ec02045edd710153ceaff221abb1a1ac5bb752c Mon Sep 17 00:00:00 2001 From: fabienfl Date: Fri, 26 May 2023 15:26:55 +0200 Subject: [PATCH 09/14] OrcLib: Location: Shadow: fix missing volume name initialization --- src/OrcLib/Location.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/OrcLib/Location.cpp b/src/OrcLib/Location.cpp index fa3f7b7e..f8ee0352 100644 --- a/src/OrcLib/Location.cpp +++ b/src/OrcLib/Location.cpp @@ -397,6 +397,10 @@ void Location::EnumerateShadowCopies( for (auto& shadow : shadows) { shadow.parentIdentifier = GetIdentifier(); + if (shadow.parentVolume->ShortVolumeName()) + { + shadow.VolumeName = shadow.parentVolume->ShortVolumeName(); + } } // Most recent snapshot first From ad59c0f520edcf2ccfb04095df59b36602ca83f7 Mon Sep 17 00:00:00 2001 From: fabienfl <55688171+fabienfl-orc@users.noreply.github.com> Date: Wed, 31 May 2023 15:39:59 +0200 Subject: [PATCH 10/14] OrcLib: OrcLimits: fix limit handling when set to UINT_MAX --- .../Command/GetSamples/GetSamples_Config.cpp | 2 +- .../Command/GetSamples/GetSamples_Run.cpp | 12 ++++----- .../Command/GetThis/GetThis_Config.cpp | 2 +- .../Command/GetThis/GetThis_Run.cpp | 24 +++++++++--------- src/OrcCommand/UtilitiesMain.h | 25 +++++++++++++++++++ src/OrcLib/OrcLimits.h | 6 ++--- 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/OrcCommand/Command/GetSamples/GetSamples_Config.cpp b/src/OrcCommand/Command/GetSamples/GetSamples_Config.cpp index 0c2bc481..b8a8c7af 100644 --- a/src/OrcCommand/Command/GetSamples/GetSamples_Config.cpp +++ b/src/OrcCommand/Command/GetSamples/GetSamples_Config.cpp @@ -304,7 +304,7 @@ HRESULT Main::CheckConfiguration() // TODO: make a function to use also in GetThis_config.cpp if (!config.limits.bIgnoreLimits - && (config.limits.dwlMaxTotalBytes == INFINITE && config.limits.dwMaxSampleCount == INFINITE)) + && (!config.limits.dwlMaxTotalBytes.has_value() && !config.limits.dwMaxSampleCount.has_value())) { Log::Critical( "No global (at samples level, MaxTotalBytes or MaxSampleCount) has been set: set limits in configuration " diff --git a/src/OrcCommand/Command/GetSamples/GetSamples_Run.cpp b/src/OrcCommand/Command/GetSamples/GetSamples_Run.cpp index b03df6ee..e92baa0c 100644 --- a/src/OrcCommand/Command/GetSamples/GetSamples_Run.cpp +++ b/src/OrcCommand/Command/GetSamples/GetSamples_Run.cpp @@ -283,24 +283,24 @@ HRESULT Main::WriteGetThisConfig( getthisconfig[GETTHIS_SAMPLES].Status = ConfigItem::PRESENT; - if (config.limits.dwlMaxTotalBytes != INFINITE) + if (config.limits.dwlMaxTotalBytes.has_value()) { getthisconfig[GETTHIS_SAMPLES].SubItems[CONFIG_MAXTOTALBYTES].strData = - std::to_wstring(config.limits.dwlMaxTotalBytes); + std::to_wstring(config.limits.dwlMaxTotalBytes.value()); getthisconfig[GETTHIS_SAMPLES].SubItems[CONFIG_MAXTOTALBYTES].Status = ConfigItem::PRESENT; } - if (config.limits.dwlMaxBytesPerSample != INFINITE) + if (config.limits.dwlMaxBytesPerSample.has_value()) { getthisconfig[GETTHIS_SAMPLES].SubItems[CONFIG_MAXBYTESPERSAMPLE].strData = - std::to_wstring(config.limits.dwlMaxBytesPerSample); + std::to_wstring(config.limits.dwlMaxBytesPerSample.value()); getthisconfig[GETTHIS_SAMPLES].SubItems[CONFIG_MAXBYTESPERSAMPLE].Status = ConfigItem::PRESENT; } - if (config.limits.dwMaxSampleCount != INFINITE) + if (config.limits.dwMaxSampleCount.has_value()) { getthisconfig[GETTHIS_SAMPLES].SubItems[CONFIG_MAXSAMPLECOUNT].strData = - std::to_wstring(config.limits.dwMaxSampleCount); + std::to_wstring(config.limits.dwMaxSampleCount.value()); getthisconfig[GETTHIS_SAMPLES].SubItems[CONFIG_MAXSAMPLECOUNT].Status = ConfigItem::PRESENT; } diff --git a/src/OrcCommand/Command/GetThis/GetThis_Config.cpp b/src/OrcCommand/Command/GetThis/GetThis_Config.cpp index f508826b..bb7c25fa 100644 --- a/src/OrcCommand/Command/GetThis/GetThis_Config.cpp +++ b/src/OrcCommand/Command/GetThis/GetThis_Config.cpp @@ -562,7 +562,7 @@ HRESULT Main::CheckConfiguration() // TODO: make a function to use also in GetSamples_config.cpp if (!config.limits.bIgnoreLimits - && (config.limits.dwlMaxTotalBytes == INFINITE && config.limits.dwMaxSampleCount == INFINITE)) + && (!config.limits.dwlMaxTotalBytes.has_value() && !config.limits.dwMaxSampleCount.has_value())) { Log::Critical( L"No global (at samples level, MaxTotalBytes or MaxSampleCount) has been set: set limits in configuration " diff --git a/src/OrcCommand/Command/GetThis/GetThis_Run.cpp b/src/OrcCommand/Command/GetThis/GetThis_Run.cpp index 1c51f6af..f94b2dad 100644 --- a/src/OrcCommand/Command/GetThis/GetThis_Run.cpp +++ b/src/OrcCommand/Command/GetThis/GetThis_Run.cpp @@ -593,49 +593,49 @@ LimitStatus SampleLimitStatus(const Limits& globalLimits, const Limits& localLim return LimitStatus::NoLimits; } - if (globalLimits.dwMaxSampleCount != INFINITE) + if (globalLimits.dwMaxSampleCount.has_value()) { - if (globalLimits.dwAccumulatedSampleCount >= globalLimits.dwMaxSampleCount) + if (globalLimits.dwAccumulatedSampleCount >= globalLimits.dwMaxSampleCount.value()) { return GlobalSampleCountLimitReached; } } - if (localLimits.dwMaxSampleCount != INFINITE) + if (localLimits.dwMaxSampleCount.has_value()) { - if (localLimits.dwAccumulatedSampleCount >= localLimits.dwMaxSampleCount) + if (localLimits.dwAccumulatedSampleCount >= localLimits.dwMaxSampleCount.value()) { return LocalSampleCountLimitReached; } } - if (globalLimits.dwlMaxBytesPerSample != INFINITE) + if (globalLimits.dwlMaxBytesPerSample.has_value()) { - if (dataSize > globalLimits.dwlMaxBytesPerSample) + if (dataSize > globalLimits.dwlMaxBytesPerSample.value()) { return GlobalMaxBytesPerSample; } } - if (globalLimits.dwlMaxTotalBytes != INFINITE) + if (globalLimits.dwlMaxTotalBytes.has_value()) { - if (dataSize + globalLimits.dwlAccumulatedBytesTotal > globalLimits.dwlMaxTotalBytes) + if (dataSize + globalLimits.dwlAccumulatedBytesTotal > globalLimits.dwlMaxTotalBytes.value()) { return GlobalMaxTotalBytes; } } - if (localLimits.dwlMaxBytesPerSample != INFINITE) + if (localLimits.dwlMaxBytesPerSample.has_value()) { - if (dataSize > localLimits.dwlMaxBytesPerSample) + if (dataSize > localLimits.dwlMaxBytesPerSample.value()) { return LocalMaxBytesPerSample; } } - if (localLimits.dwlMaxTotalBytes != INFINITE) + if (localLimits.dwlMaxTotalBytes.has_value()) { - if (dataSize + localLimits.dwlAccumulatedBytesTotal > localLimits.dwlMaxTotalBytes) + if (dataSize + localLimits.dwlAccumulatedBytesTotal > localLimits.dwlMaxTotalBytes.value()) { return LocalMaxTotalBytes; } diff --git a/src/OrcCommand/UtilitiesMain.h b/src/OrcCommand/UtilitiesMain.h index a16839c2..61c8c482 100644 --- a/src/OrcCommand/UtilitiesMain.h +++ b/src/OrcCommand/UtilitiesMain.h @@ -41,6 +41,7 @@ #include "Log/LogTerminationHandler.h" #include "Utils/StdStream/StandardOutput.h" #include "Text/Guid.h" +#include "Limit.h" #include "VolumeReader.h" #include "Utils/EnumFlags.h" @@ -557,6 +558,18 @@ class UtilitiesMain return false; } + template + static bool ParameterOption(LPCWSTR szArg, LPCWSTR szOption, std::optional>& parameter) + { + OptionType result; + if (ParameterOption(szArg, szOption, result)) + { + parameter.emplace(std::move(result)); + return true; + } + return false; + } + static bool OptionalParameterOption( LPCWSTR szArg, LPCWSTR szOption, @@ -624,6 +637,18 @@ class UtilitiesMain return false; } + template + static bool FileSizeOption(LPCWSTR szArg, LPCWSTR szOption, std::optional& parameter) + { + OptionType result(0); + if (FileSizeOption(szArg, szOption, result)) + { + parameter.emplace(std::move(result)); + return true; + } + return false; + } + static bool FileSizeOption(LPCWSTR szArg, LPCWSTR szOption, DWORDLONG& dwlFileSize); static bool AltitudeOption(LPCWSTR szArg, LPCWSTR szOption, LocationSet::Altitude& altitude); diff --git a/src/OrcLib/OrcLimits.h b/src/OrcLib/OrcLimits.h index 175e1e11..51fc2809 100644 --- a/src/OrcLib/OrcLimits.h +++ b/src/OrcLib/OrcLimits.h @@ -23,14 +23,14 @@ class Limits bool bIgnoreLimits = false; - Limit> dwlMaxTotalBytes = INFINITE; + std::optional>> dwlMaxTotalBytes; DWORDLONG dwlAccumulatedBytesTotal = 0LL; bool bMaxTotalBytesReached = false; - Limit> dwlMaxBytesPerSample = INFINITE; + std::optional>> dwlMaxBytesPerSample; bool bMaxBytesPerSampleReached = false; - Limit dwMaxSampleCount = INFINITE; + std::optional> dwMaxSampleCount; DWORD dwAccumulatedSampleCount = 0LL; bool bMaxSampleCountReached = false; }; From 4fcc7295c7860b59c0d1b1bc285e65924315d7e6 Mon Sep 17 00:00:00 2001 From: fabienfl <55688171+fabienfl-orc@users.noreply.github.com> Date: Wed, 31 May 2023 17:21:58 +0200 Subject: [PATCH 11/14] OrcLib: Configuration: make xml 'Location' optional --- src/OrcLib/Configuration/ConfigFile_Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrcLib/Configuration/ConfigFile_Common.cpp b/src/OrcLib/Configuration/ConfigFile_Common.cpp index 703ac916..ca444cbc 100644 --- a/src/OrcLib/Configuration/ConfigFile_Common.cpp +++ b/src/OrcLib/Configuration/ConfigFile_Common.cpp @@ -399,7 +399,7 @@ HRESULT Orc::Config::Common::location(ConfigItem& parent, DWORD dwIndex, ConfigI // LOCATION item HRESULT Orc::Config::Common::location(ConfigItem& parent, DWORD dwIndex) { - return location(parent, dwIndex, ConfigItem::MANDATORY); + return location(parent, dwIndex, ConfigItem::OPTION); } HRESULT Orc::Config::Common::optional_location(ConfigItem& parent, DWORD dwIndex) From db8da0dbb5d879b2dc28164a6ae47875b7a4a69c Mon Sep 17 00:00:00 2001 From: fabienfl <55688171+fabienfl-orc@users.noreply.github.com> Date: Wed, 31 May 2023 17:24:51 +0200 Subject: [PATCH 12/14] OrcLib: LocationSet: cli overrides any previously set parsed location --- src/OrcLib/LocationSet.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/OrcLib/LocationSet.cpp b/src/OrcLib/LocationSet.cpp index 992b5f82..8f3ee8ff 100644 --- a/src/OrcLib/LocationSet.cpp +++ b/src/OrcLib/LocationSet.cpp @@ -1101,6 +1101,27 @@ HRESULT LocationSet::AddLocationsFromArgcArgv(int argc, LPCWSTR argv[]) if (FAILED(hr = EnumerateLocations())) return hr; + bool hasLocation = false; + for (int i = 1; i < argc; i++) + { + if (argv[i][0] != L'/' && argv[i][0] != L'+' && argv[i][0] != L'-') + { + hasLocation = true; + break; + } + } + + if (!hasLocation) + { + return S_OK; + } + + Log::Debug("Disable location set previously as a cli option overrides the value"); + for (auto& location : m_Locations) + { + location.second->SetParse(false); + } + for (int i = 1; i < argc; i++) { if (argv[i][0] != L'/' && argv[i][0] != L'+' && argv[i][0] != L'-') From e179c9225b694b69caf4e9e1509333c79674c7b5 Mon Sep 17 00:00:00 2001 From: fabienfl <55688171+fabienfl-orc@users.noreply.github.com> Date: Wed, 31 May 2023 17:26:01 +0200 Subject: [PATCH 13/14] OrcCommand: add log message on missing 'Location' resolution --- src/OrcCommand/Command/FastFind/FastFind.h | 1 + src/OrcCommand/Command/FatInfo/FatInfo.h | 1 + .../Command/FatInfo/FatInfo_Config.cpp | 18 ++++- src/OrcCommand/Command/GetThis/GetThis.h | 2 + .../Command/GetThis/GetThis_Config.cpp | 29 ++++++-- src/OrcCommand/Command/NTFSInfo/NTFSInfo.h | 1 + .../Command/NTFSInfo/NTFSInfo_Config.cpp | 13 +++- .../Command/NTFSInfo/NTFSInfo_Run.cpp | 8 ++ src/OrcCommand/Command/USNInfo/USNInfo.h | 1 + .../Command/USNInfo/USNInfo_Config.cpp | 9 +++ src/OrcLib/LocationSet.cpp | 73 ++++++++++++++----- src/OrcLib/LocationSet.h | 3 + src/OrcLib/USNJournalWalkerOffline.cpp | 2 +- 13 files changed, 130 insertions(+), 31 deletions(-) diff --git a/src/OrcCommand/Command/FastFind/FastFind.h b/src/OrcCommand/Command/FastFind/FastFind.h index f2888831..f62f18e0 100644 --- a/src/OrcCommand/Command/FastFind/FastFind.h +++ b/src/OrcCommand/Command/FastFind/FastFind.h @@ -260,6 +260,7 @@ class ORCUTILS_API Main : public UtilitiesMain std::wstring YaraSource; std::unique_ptr Yara; + std::vector inputFilesystemLocations; Configuration() : FileSystem() diff --git a/src/OrcCommand/Command/FatInfo/FatInfo.h b/src/OrcCommand/Command/FatInfo/FatInfo.h index 89373235..81f8b21f 100644 --- a/src/OrcCommand/Command/FatInfo/FatInfo.h +++ b/src/OrcCommand/Command/FatInfo/FatInfo.h @@ -59,6 +59,7 @@ class ORCUTILS_API Main : public UtilitiesMain Intentions ColumnIntentions; Intentions DefaultIntentions; std::vector Filters; + std::vector InputLocations; }; static LPCWSTR ToolName() { return L"FatInfo"; } diff --git a/src/OrcCommand/Command/FatInfo/FatInfo_Config.cpp b/src/OrcCommand/Command/FatInfo/FatInfo_Config.cpp index 4e4cb4f4..1beba522 100644 --- a/src/OrcCommand/Command/FatInfo/FatInfo_Config.cpp +++ b/src/OrcCommand/Command/FatInfo/FatInfo_Config.cpp @@ -116,10 +116,15 @@ HRESULT Main::GetConfigurationFromConfig(const ConfigItem& configitem) m_Config.bPopSystemObjects = false; m_Config.locs.SetPopulateSystemObjects((bool)m_Config.bPopSystemObjects); - if (FAILED(hr = m_Config.locs.AddLocationsFromConfigItem(configitem[FATINFO_LOCATIONS]))) + if (configitem[FATINFO_LOCATIONS]) { - Log::Error("Failed to get locations definition from config [{}]", SystemError(hr)); - return hr; + if (FAILED(hr = m_Config.locs.AddLocationsFromConfigItem(configitem[FATINFO_LOCATIONS]))) + { + Log::Error("Failed to get locations definition from config [{}]", SystemError(hr)); + return hr; + } + + LocationSet::ParseLocationsFromConfigItem(configitem[FATINFO_LOCATIONS], m_Config.InputLocations); } if (configitem[FATINFO_LOGGING]) @@ -235,6 +240,8 @@ HRESULT Main::GetConfigurationFromArgcArgv(int argc, LPCWSTR argv[]) m_Config.bPopSystemObjects = false; m_Config.locs.SetPopulateSystemObjects((bool)m_Config.bPopSystemObjects); + LocationSet::ParseLocationsFromArgcArgv(argc, argv, m_Config.InputLocations); + if (FAILED(hr = m_Config.locs.AddLocationsFromArgcArgv(argc, argv))) return hr; @@ -261,6 +268,11 @@ HRESULT Main::CheckConfiguration() SystemDetails::SetOrcComputerName(m_utilitiesConfig.strComputerName); } + if (m_Config.InputLocations.empty()) + { + Log::Critical("Missing location parameter"); + } + m_Config.locs.Consolidate(false, FSVBR::FSType::FAT); if (m_Config.output.Type == OutputSpec::Kind::None) diff --git a/src/OrcCommand/Command/GetThis/GetThis.h b/src/OrcCommand/Command/GetThis/GetThis.h index 128badd0..ed42d589 100644 --- a/src/OrcCommand/Command/GetThis/GetThis.h +++ b/src/OrcCommand/Command/GetThis/GetThis.h @@ -153,6 +153,8 @@ class ORCUTILS_API Main : public UtilitiesMain ContentSpec GetContentSpecFromString(const std::wstring& str); + std::vector inputLocations; + private: static std::wregex g_ContentRegEx; }; diff --git a/src/OrcCommand/Command/GetThis/GetThis_Config.cpp b/src/OrcCommand/Command/GetThis/GetThis_Config.cpp index bb7c25fa..fa6f29e0 100644 --- a/src/OrcCommand/Command/GetThis/GetThis_Config.cpp +++ b/src/OrcCommand/Command/GetThis/GetThis_Config.cpp @@ -168,16 +168,26 @@ HRESULT Main::GetConfigurationFromConfig(const ConfigItem& configitem) config.bAddShadows = bAddShadows; } - if (FAILED(hr = config.Locations.AddLocationsFromConfigItem(configitem[GETTHIS_LOCATION]))) + if (configitem[GETTHIS_LOCATION]) { - Log::Error(L"Syntax error in specific locations parsing in config file"); - return hr; + if (FAILED(hr = config.Locations.AddLocationsFromConfigItem(configitem[GETTHIS_LOCATION]))) + { + Log::Error(L"Syntax error in specific locations parsing in config file"); + return hr; + } + + LocationSet::ParseLocationsFromConfigItem(configitem[GETTHIS_LOCATION], config.inputLocations); } - if (FAILED(hr = config.Locations.AddKnownLocations(configitem[GETTHIS_KNOWNLOCATIONS]))) + if (configitem[GETTHIS_KNOWNLOCATIONS]) { - Log::Error(L"Syntax error in known locations parsing in config file"); - return hr; + if (FAILED(hr = config.Locations.AddKnownLocations(configitem[GETTHIS_KNOWNLOCATIONS]))) + { + Log::Error(L"Syntax error in known locations parsing in config file"); + return hr; + } + + LocationSet::ParseLocationsFromConfigItem(configitem[GETTHIS_KNOWNLOCATIONS], config.inputLocations); } if (configitem[GETTHIS_SAMPLES][CONFIG_MAXBYTESPERSAMPLE]) @@ -477,6 +487,8 @@ HRESULT Main::GetConfigurationFromArgcArgv(int argc, LPCWSTR argv[]) } } + LocationSet::ParseLocationsFromArgcArgv(argc, argv, config.inputLocations); + if (FAILED(hr = config.Locations.AddLocationsFromArgcArgv(argc, argv))) { Log::Error("Error in specific locations parsing"); @@ -506,6 +518,11 @@ HRESULT Main::CheckConfiguration() config.bAddShadows = false; } + if (config.inputLocations.empty()) + { + Log::Critical("Missing location parameter"); + } + config.Locations.Consolidate( (bool)config.bAddShadows, config.m_shadows.value_or(LocationSet::ShadowFilters()), diff --git a/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h b/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h index 91b5b400..2d9dd401 100644 --- a/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h +++ b/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h @@ -100,6 +100,7 @@ class ORCUTILS_API Main : public UtilitiesMain Intentions ColumnIntentions; Intentions DefaultIntentions; std::vector Filters; + std::vector InputLocations; }; private: diff --git a/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Config.cpp b/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Config.cpp index 364d4762..981b5004 100644 --- a/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Config.cpp +++ b/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Config.cpp @@ -205,7 +205,9 @@ HRESULT Main::GetConfigurationFromConfig(const ConfigItem& configitem) if (!mode) { Log::Error( - L"Failed to parse 'Resurrect' attribute (value: {}) [{}]", configitem[NTFSINFO_RESURRECT].c_str(), mode.error()); + L"Failed to parse 'Resurrect' attribute (value: {}) [{}]", + configitem[NTFSINFO_RESURRECT].c_str(), + mode.error()); } else { @@ -253,6 +255,8 @@ HRESULT Main::GetConfigurationFromConfig(const ConfigItem& configitem) return hr; } + LocationSet::ParseLocationsFromConfigItem(configitem[NTFSINFO_LOCATIONS], config.InputLocations); + if (FAILED(hr = GetColumnsAndFiltersFromConfig(configitem))) { Log::Error(L"Failed to get column definition from config [{}]", SystemError(hr)); @@ -381,6 +385,8 @@ HRESULT Main::GetConfigurationFromArgcArgv(int argc, LPCWSTR argv[]) } // argc/argv parameters only + LocationSet::ParseLocationsFromArgcArgv(argc, argv, config.InputLocations); + if (boost::logic::indeterminate(config.bAddShadows)) config.bAddShadows = false; if (boost::logic::indeterminate(config.bPopSystemObjects)) @@ -429,6 +435,11 @@ HRESULT Main::CheckConfiguration() config.bAddShadows = false; } + if (config.InputLocations.empty()) + { + Log::Critical("Missing location parameter"); + } + config.locs.Consolidate( static_cast(config.bAddShadows), config.m_shadows.value_or(LocationSet::ShadowFilters()), diff --git a/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp b/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp index d7164c63..5265fabc 100644 --- a/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp +++ b/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp @@ -646,6 +646,14 @@ HRESULT Main::RunThroughMFT() if (locations.empty()) { + if (config.m_excludes.has_value() && config.m_excludes->find(L"*") != std::cend(*config.m_excludes)) + { + // TODO: BEWARE: this is not complete, it should handle cases where specific drive is targetted and excluded + // and ShadowFilters. + Log::Info(L"No volume found"); + return S_OK; + } + Log::Critical( L"No NTFS volumes configured for parsing. Use \"*\" to parse all mounted volumes or list the volumes you " L"want parsed"); diff --git a/src/OrcCommand/Command/USNInfo/USNInfo.h b/src/OrcCommand/Command/USNInfo/USNInfo.h index cf718d7c..fe5fa1ef 100644 --- a/src/OrcCommand/Command/USNInfo/USNInfo.h +++ b/src/OrcCommand/Command/USNInfo/USNInfo.h @@ -50,6 +50,7 @@ class ORCUTILS_API Main : public UtilitiesMain std::optional m_shadows; std::optional m_shadowsParser; std::optional m_excludes; + std::vector m_inputLocations; }; private: diff --git a/src/OrcCommand/Command/USNInfo/USNInfo_Config.cpp b/src/OrcCommand/Command/USNInfo/USNInfo_Config.cpp index ab924ac1..863c84f6 100644 --- a/src/OrcCommand/Command/USNInfo/USNInfo_Config.cpp +++ b/src/OrcCommand/Command/USNInfo/USNInfo_Config.cpp @@ -45,6 +45,8 @@ HRESULT Main::GetConfigurationFromConfig(const ConfigItem& configitem) return hr; } + LocationSet::ParseLocationsFromConfigItem(configitem[USNINFO_LOCATIONS], config.m_inputLocations); + boost::logic::tribool bAddShadows; for (auto& item : configitem[USNINFO_LOCATIONS].NodeList) { @@ -141,6 +143,8 @@ HRESULT Main::GetConfigurationFromArgcArgv(int argc, LPCWSTR argv[]) } } + LocationSet::ParseLocationsFromArgcArgv(argc, argv, config.m_inputLocations); + if (FAILED(hr = config.locs.AddLocationsFromArgcArgv(argc, argv))) return hr; @@ -165,6 +169,11 @@ HRESULT Main::CheckConfiguration() config.bAddShadows = false; } + if (config.m_inputLocations.empty()) + { + Log::Critical("Missing location parameter"); + } + config.locs.Consolidate( static_cast(config.bAddShadows), config.m_shadows.value_or(LocationSet::ShadowFilters()), diff --git a/src/OrcLib/LocationSet.cpp b/src/OrcLib/LocationSet.cpp index 8f3ee8ff..b865a493 100644 --- a/src/OrcLib/LocationSet.cpp +++ b/src/OrcLib/LocationSet.cpp @@ -1094,24 +1094,60 @@ HRESULT LocationSet::AddLocationsFromConfigItem(const ConfigItem& config) return hr; } -HRESULT LocationSet::AddLocationsFromArgcArgv(int argc, LPCWSTR argv[]) +HRESULT LocationSet::ParseLocationsFromConfigItem(const ConfigItem& config, std::vector& locations) { - HRESULT hr = E_FAIL; + if (!config) + { + return S_OK; + } - if (FAILED(hr = EnumerateLocations())) - return hr; + if (config.Type != ConfigItem::NODELIST) + { + return E_INVALIDARG; + } - bool hasLocation = false; + if (config.strName != L"location") + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + for (auto& item : config.NodeList) + { + locations.emplace_back(item.c_str()); + } + + return S_OK; +} + +void LocationSet::ParseLocationsFromArgcArgv(int argc, LPCWSTR argv[], std::vector& locations) +{ for (int i = 1; i < argc; i++) { if (argv[i][0] != L'/' && argv[i][0] != L'+' && argv[i][0] != L'-') { - hasLocation = true; - break; + if (argv[i][0] == L'*') + { + Log::Debug(L"Enable all locations"); + } + + locations.emplace_back(argv[i]); } } +} + +HRESULT LocationSet::AddLocationsFromArgcArgv(int argc, LPCWSTR argv[]) +{ + HRESULT hr = E_FAIL; + + if (FAILED(hr = EnumerateLocations())) + { + return hr; + } - if (!hasLocation) + std::vector locationsFromCli; + ParseLocationsFromArgcArgv(argc, argv, locationsFromCli); + if (locationsFromCli.empty()) { return S_OK; } @@ -1122,21 +1158,18 @@ HRESULT LocationSet::AddLocationsFromArgcArgv(int argc, LPCWSTR argv[]) location.second->SetParse(false); } - for (int i = 1; i < argc; i++) + for (const auto& location : locationsFromCli) { - if (argv[i][0] != L'/' && argv[i][0] != L'+' && argv[i][0] != L'-') + if (location == L"*") { - if (argv[i][0] == L'*') - { - if (FAILED(hr = ParseAllVolumes())) - return hr; - return S_OK; - } - else + return ParseAllVolumes(); + } + else + { + std::vector> addedLocs; + if (FAILED(hr = AddLocations(location.c_str(), addedLocs))) { - std::vector> addedLocs; - if (FAILED(hr = AddLocations(argv[i], addedLocs))) - return hr; + return hr; } } } diff --git a/src/OrcLib/LocationSet.h b/src/OrcLib/LocationSet.h index dafb8db1..26831e83 100644 --- a/src/OrcLib/LocationSet.h +++ b/src/OrcLib/LocationSet.h @@ -165,6 +165,9 @@ class LocationSet HRESULT AddLocations(const WCHAR* szLocation, std::vector>& addedLocs, bool bToParse = true); + static HRESULT ParseLocationsFromConfigItem(const ConfigItem& config, std::vector& locations); + static void ParseLocationsFromArgcArgv(int argc, LPCWSTR argv[], std::vector& locations); + HRESULT AddLocationsFromConfigItem(const ConfigItem& config); HRESULT AddLocationsFromArgcArgv(int argc, LPCWSTR argv[]); HRESULT AddKnownLocations(const ConfigItem& item); diff --git a/src/OrcLib/USNJournalWalkerOffline.cpp b/src/OrcLib/USNJournalWalkerOffline.cpp index 86805272..66899f54 100644 --- a/src/OrcLib/USNJournalWalkerOffline.cpp +++ b/src/OrcLib/USNJournalWalkerOffline.cpp @@ -86,7 +86,7 @@ HRESULT USNJournalWalkerOffline::Initialize(const std::shared_ptr& loc false, ResurrectRecordsMode::kNo))) { - Log::Error("Failed to parse location while searching for USN journal"); + Log::Error("Failed to parse location while searching for USN journal [{}]", SystemError(hr)); } if (FAILED(hr = m_RecordStore.InitializeStore(USN_MAX_NUMBER, m_dwRecordMaxSize))) From 5d0905fb36e0284321249ae8bea5289a1dd92169 Mon Sep 17 00:00:00 2001 From: fabienfl Date: Mon, 19 Jun 2023 10:54:55 +0200 Subject: [PATCH 14/14] changelog: update to 10.2.1 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ed2bba6..d063442d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # ChangeLog +## [10.2.1] - 2023-06-20 +### Changed +- Configuration: accept wildcard as exclusion path +- Output filename for shadow copies will be also based on volume identifier +- Limits accept UINT_MAX instead of handling it as "no limits" + +### Fixed +- Location is now overridable with cli like others options +- GetThis: fix option 'ResurrectRecord' always set to 'no' +- USNInfo: fix option 'ResurrectRecord' always set to 'yes' +- USNInfo: fix location resolution on some situations with vss +- Fix log file option + + ## [10.2.0] - 2023-04-20 ### Added - Volume Shadow Copy: add fallback mode when 'vss' service is stopped using directly 'volsnap.sys'