From 83cb2471bb4c3f2c6211d3dccea369675c08ed8d Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Mon, 28 Sep 2026 11:07:54 +0200 Subject: [PATCH] Unix: avoid false unmount success reports on retries Skip success reporting after a failed first-pass unmount is queued for retry. Cover successful, failed, and unconfirmed retries. --- Tests/test_fuset_cleanup.py | 12 +++++++++++- src/Main/UserInterface.cpp | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Tests/test_fuset_cleanup.py b/Tests/test_fuset_cleanup.py index 2e52bfe6..c6abcda7 100644 --- a/Tests/test_fuset_cleanup.py +++ b/Tests/test_fuset_cleanup.py @@ -137,6 +137,7 @@ static struct { std::wstring operator[] (const char *key) const { return StringC enum Outcome { Unmounted, Busy, Unconfirmed, Failed }; static std::map > Plan; static bool ForceAnswer; +static std::wstring Info; class UserInterface { public: struct BusyScope { BusyScope (const UserInterface *) {} }; @@ -152,7 +153,7 @@ public: } bool AskYesNo (const std::wstring &, bool, bool) const { return ForceAnswer; } void ShowWarning (const std::wstring &) const {} - void ShowInfo (const std::wstring &) const {} + void ShowInfo (const std::wstring &text) const { Info = text; } static wxString ExceptionToMessage (const exception &e) { return StringConverter::ToExceptionString (e); } struct { bool Verbose; } Preferences = { false }; }; @@ -173,6 +174,7 @@ static VolumeInfoList Volumes () { // Returns the details reported for unconfirmed cleanups, or the name of the other outcome. static std::wstring Run (std::deque a, std::deque b, bool interactive, bool force = false) { UserInterface ui; Plan.clear(); Plan[L"A"] = a; Plan[L"B"] = b; ForceAnswer = force; + ui.Preferences.Verbose = true; Info.clear(); try { ui.DismountVolumes (Volumes(), false, interactive); return L""; } catch (DismountServiceCleanupFailed &e) { return e.GetSubject(); } catch (UserAbort &) { return L""; } @@ -194,7 +196,15 @@ int Run () { Require (Run ({Unmounted}, {Busy, Busy}, false) == L"", "busy volume error changed without unconfirmed cleanup"); Require (Run ({Unmounted}, {Busy}, true, false) == L"", "cancellation changed without unconfirmed cleanup"); Require (Run ({Unmounted}, {Busy, Unmounted}, true, true) == L"", "forced second pass failed"); + for (bool interactive : {false, true}) { + Require (Run ({Failed, Unmounted}, {Unmounted}, interactive) == L"", "generic failure was not retried"); + Require (Info == L"LINUX_VOL_UNMOUNTED B\nLINUX_VOL_UNMOUNTED A", "retried volume reported unmounted more than once"); + } + Require (Run ({Failed, Failed}, {Unmounted}, false) == L"" && Info.empty(), "failed retry reported success"); + r = Run ({Failed, Unconfirmed}, {Unmounted}, false); + Require (Has (r, L"service of A") && Info == L"LINUX_VOL_UNMOUNTED B", "unconfirmed retry reported success"); std::cout << "PASS: multi-volume unmount reports every unconfirmed cleanup with other failures and cancellation\n"; + std::cout << "PASS: verbose unmount reporting follows successful attempts, including second-pass retries\n"; return 0; } } diff --git a/src/Main/UserInterface.cpp b/src/Main/UserInterface.cpp index 98cb9ff0..a51d7d6c 100644 --- a/src/Main/UserInterface.cpp +++ b/src/Main/UserInterface.cpp @@ -302,7 +302,10 @@ namespace VeraCrypt catch (...) { if (twoPassMode && firstPass) + { volumesLeft.push_back (volume); + continue; + } else throw; }