Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Core/CoreBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ namespace VeraCrypt
virtual int GetOSMinorVersion () const = 0;
virtual shared_ptr <VolumeInfo> GetMountedVolume (const VolumePath &volumePath) const;
virtual shared_ptr <VolumeInfo> GetMountedVolume (VolumeSlotNumber slot) const;
virtual VolumeInfoList GetMountedVolumes (const VolumePath &volumePath = VolumePath()) const = 0;
virtual VolumeInfoList GetMountedVolumes (const VolumePath &volumePath = VolumePath(), bool retryControlFileAccess = false) const = 0;
virtual bool HasAdminPrivileges () const = 0;
virtual void Init () { }
virtual bool IsDeviceChangeInProgress () const { return DeviceChangeInProgress; }
Expand Down
192 changes: 146 additions & 46 deletions src/Core/Unix/CoreUnix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

#include "CoreUnix.h"
#include "Common/Tcdefs.h"
#ifdef VC_MACOSX_FUSET
#include <chrono>
#endif
#include <errno.h>
#include <iostream>
#include <signal.h>
Expand Down Expand Up @@ -521,76 +524,173 @@ namespace VeraCrypt
return mountedFilesystems.front()->MountPoint;
}

VolumeInfoList CoreUnix::GetMountedVolumes (const VolumePath &volumePath) const
static shared_ptr <VolumeInfo> ReadMountedVolumeControlFile (const MountedFilesystem &mountedFileSystem, string *controlFileError)
{
VolumeInfoList volumes;
if (controlFileError)
controlFileError->clear();

foreach_ref (const MountedFilesystem &mf, GetMountedFilesystems ())
try
{
if (string (mf.MountPoint).find (GetFuseMountDirPrefix()) == string::npos)
continue;
shared_ptr <File> controlFile (new File);
controlFile->Open (string (mountedFileSystem.MountPoint) + FuseService::GetControlPath());

shared_ptr <VolumeInfo> mountedVol;
// Introduce a retry mechanism with a timeout for control file access.
// The list is already filtered to VeraCrypt auxiliary mounts; in
// FUSE-T builds, the mount table device name varies by backend.
FileStream controlFileReader (controlFile);
string controlFileData = controlFileReader.ReadToEnd();
if (controlFileData.empty() || controlFileData.size() > 1024 * 1024)
throw ParameterIncorrect (SRC_POS);

shared_ptr <Stream> controlFileStream (new MemoryStream (ConstBufferPtr ((const uint8 *) controlFileData.data(), controlFileData.size())));
return Serializable::DeserializeNew <VolumeInfo> (controlFileStream);
}
catch (const std::exception &e)
{
#ifdef VC_MACOSX_FUSET
int controlFileRetries = volumePath.IsEmpty() ? 1 : 10; // Up to 10 attempts with 500ms sleeps for specific volume lookups
string controlFileError;
while (!mountedVol && (controlFileRetries-- > 0))
if (controlFileError)
*controlFileError = StringConverter::ToSingle (StringConverter::ToExceptionString (e));
#else
(void) e;
#endif
}
#ifdef VC_MACOSX_FUSET
catch (...)
{
if (controlFileError)
*controlFileError = "unknown exception";
}
#endif

return shared_ptr <VolumeInfo>();
}

VolumeInfoList CoreUnix::GetMountedVolumes (const VolumePath &volumePath, bool retryControlFileAccess) const
{
VolumeInfoList volumes;

struct MountedVolumeCandidate
{
MountedVolumeCandidate (shared_ptr <MountedFilesystem> mountedFileSystem)
: FileSystem (mountedFileSystem), ControlFileAttempts (0) { }

shared_ptr <MountedFilesystem> FileSystem;
shared_ptr <VolumeInfo> Volume;
string ControlFileError;
int ControlFileAttempts;
};

vector <MountedVolumeCandidate> candidates;
MountedFilesystemList mountedFileSystems = GetMountedFilesystems ();
for (MountedFilesystemList::const_iterator i = mountedFileSystems.begin(); i != mountedFileSystems.end(); ++i)
{
if (string ((*i)->MountPoint).find (GetFuseMountDirPrefix()) != string::npos)
candidates.push_back (MountedVolumeCandidate (*i));
}

#ifdef VC_MACOSX_FUSET
if (!volumePath.IsEmpty())
{
// Preserve the existing per-candidate retry behavior for specific
// volume lookups while a FUSE-T auxiliary mount becomes readable.
for (size_t candidateIndex = 0; candidateIndex < candidates.size(); ++candidateIndex)
{
try
MountedVolumeCandidate &candidate = candidates[candidateIndex];
for (int attempt = 0; attempt < 10 && !candidate.Volume; ++attempt)
{
shared_ptr <File> controlFile (new File);
controlFile->Open (string (mf.MountPoint) + FuseService::GetControlPath());
++candidate.ControlFileAttempts;
candidate.Volume = ReadMountedVolumeControlFile (*candidate.FileSystem, &candidate.ControlFileError);
if (!candidate.Volume && attempt + 1 < 10)
Thread::Sleep (500);
}

FileStream controlFileReader (controlFile);
string controlFileData = controlFileReader.ReadToEnd();
if (controlFileData.empty() || controlFileData.size() > 1024 * 1024)
throw ParameterIncorrect (SRC_POS);
if (candidate.Volume && wstring (candidate.Volume->Path).compare (volumePath) == 0)
break;
}
}
else
{
const bool useSecurityRetryDeadline = retryControlFileAccess;
const std::chrono::steady_clock::time_point retryDeadline =
std::chrono::steady_clock::now() + std::chrono::seconds (2);

// Every candidate receives one initial attempt. Security-event callers
// then retry unresolved control files in shared, rotating rounds; no
// retry read or sleep begins after the monotonic deadline.
for (size_t candidateIndex = 0; candidateIndex < candidates.size(); ++candidateIndex)
{
MountedVolumeCandidate &candidate = candidates[candidateIndex];
++candidate.ControlFileAttempts;
candidate.Volume = ReadMountedVolumeControlFile (*candidate.FileSystem, &candidate.ControlFileError);
}

shared_ptr <Stream> controlFileStream (new MemoryStream (ConstBufferPtr ((const uint8 *) controlFileData.data(), controlFileData.size())));
mountedVol = Serializable::DeserializeNew <VolumeInfo> (controlFileStream);
}
catch (const std::exception& e)
size_t retryStartIndex = 0;
while (useSecurityRetryDeadline && !candidates.empty())
{
bool retryableCandidateFound = false;
for (size_t candidateIndex = 0; candidateIndex < candidates.size(); ++candidateIndex)
{
#ifdef VC_MACOSX_FUSET
controlFileError = StringConverter::ToSingle (StringConverter::ToExceptionString (e));
if (controlFileRetries > 0)
if (!candidates[candidateIndex].Volume && candidates[candidateIndex].ControlFileAttempts < 10)
{
// FUSE-T's SMB backend can briefly expose the auxiliary mount
// before the control file is readable and deserializable.
Thread::Sleep (500);
retryableCandidateFound = true;
break;
}
#else
(void) e;
#endif
}
#ifdef VC_MACOSX_FUSET
catch (...)

if (!retryableCandidateFound)
break;

std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
if (now >= retryDeadline)
break;

std::chrono::milliseconds remaining =
std::chrono::duration_cast <std::chrono::milliseconds> (retryDeadline - now);
uint32 retryDelay = remaining.count() > 200 ? 200 : static_cast <uint32> (remaining.count());
if (retryDelay > 0)
Thread::Sleep (retryDelay);

for (size_t offset = 0; offset < candidates.size(); ++offset)
{
controlFileError = "unknown exception";
if (controlFileRetries > 0)
{
// FUSE-T's SMB backend can briefly expose the auxiliary mount
// before the control file is readable and deserializable.
Thread::Sleep (500);
}
if (std::chrono::steady_clock::now() >= retryDeadline)
break;

size_t candidateIndex = (retryStartIndex + offset) % candidates.size();
MountedVolumeCandidate &candidate = candidates[candidateIndex];
if (candidate.Volume || candidate.ControlFileAttempts >= 10)
continue;

++candidate.ControlFileAttempts;
candidate.Volume = ReadMountedVolumeControlFile (*candidate.FileSystem, &candidate.ControlFileError);
}
#endif

retryStartIndex = (retryStartIndex + 1) % candidates.size();
}
}
#else
(void) retryControlFileAccess;
for (size_t candidateIndex = 0; candidateIndex < candidates.size(); ++candidateIndex)
{
MountedVolumeCandidate &candidate = candidates[candidateIndex];
++candidate.ControlFileAttempts;
candidate.Volume = ReadMountedVolumeControlFile (*candidate.FileSystem, nullptr);
}
#endif

for (size_t candidateIndex = 0; candidateIndex < candidates.size(); ++candidateIndex)
{
MountedVolumeCandidate &candidate = candidates[candidateIndex];
const MountedFilesystem &mf = *candidate.FileSystem;
shared_ptr <VolumeInfo> mountedVol = candidate.Volume;

if (!mountedVol)
{
#ifdef VC_MACOSX_FUSET
if (!volumePath.IsEmpty())
if (candidate.ControlFileAttempts > 0 && (retryControlFileAccess || !volumePath.IsEmpty()))
{
stringstream logMessage;
logMessage << "Failed to read VeraCrypt auxiliary mount control file after retries: "
logMessage << "Failed to read VeraCrypt auxiliary mount control file after "
<< candidate.ControlFileAttempts << " attempt(s): "
<< string (mf.MountPoint) << FuseService::GetControlPath();
if (!controlFileError.empty())
logMessage << ": " << controlFileError;
if (!candidate.ControlFileError.empty())
logMessage << ": " << candidate.ControlFileError;
SystemLog::WriteError (logMessage.str());
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Unix/CoreUnix.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ namespace VeraCrypt
virtual uint64 GetDeviceSize (const DevicePath &devicePath) const;
virtual int GetOSMajorVersion () const { throw NotApplicable (SRC_POS); }
virtual int GetOSMinorVersion () const { throw NotApplicable (SRC_POS); }
virtual VolumeInfoList GetMountedVolumes (const VolumePath &volumePath = VolumePath()) const;
virtual VolumeInfoList GetMountedVolumes (const VolumePath &volumePath = VolumePath(), bool retryControlFileAccess = false) const;
virtual bool IsDevicePresent (const DevicePath &device) const { throw NotApplicable (SRC_POS); }
virtual bool IsInPortableMode () const { return false; }
virtual bool IsMountPointAvailable (const DirectoryPath &mountPoint) const;
Expand Down
2 changes: 1 addition & 1 deletion src/Main/Forms/Forms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1832,7 +1832,7 @@ PreferencesDialogBase::PreferencesDialogBase( wxWindow* parent, wxWindowID id, c
DismountOnScreenSaverCheckBox = new wxCheckBox( sbSizer13->GetStaticBox(), wxID_ANY, _("IDC_PREF_UNMOUNT_SCREENSAVER"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizer13->Add( DismountOnScreenSaverCheckBox, 0, wxALL, 5 );

DismountOnPowerSavingCheckBox = new wxCheckBox( sbSizer13->GetStaticBox(), wxID_ANY, _("LINUX_ENTERING_POVERSAWING"), wxDefaultPosition, wxDefaultSize, 0 );
DismountOnPowerSavingCheckBox = new wxCheckBox( sbSizer13->GetStaticBox(), wxID_ANY, _("LINUX_ENTERING_POWERSAVING"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizer13->Add( DismountOnPowerSavingCheckBox, 0, wxALL, 5 );


Expand Down
5 changes: 5 additions & 0 deletions src/Main/Forms/MainFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include "Main/Resources.h"
#include "Main/Application.h"
#include "Main/GraphicUserInterface.h"
#ifdef TC_MACOSX
#include "Main/MacOSXSleepLock.h"
#endif
#include "Main/VolumeHistory.h"
#include "Main/Xml.h"
#include "MainFrame.h"
Expand Down Expand Up @@ -1435,6 +1438,8 @@ namespace VeraCrypt
previousState = running;
}
}
#elif defined (TC_MACOSX)
ReconcileMacOSXScreenLockState ();
#endif
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/Main/Forms/PreferencesDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,16 @@ namespace VeraCrypt
CloseExplorerWindowsOnDismountCheckBox->Show (false);
#endif

#ifndef wxHAS_POWER_EVENTS
// On macOS, wxHAS_POWER_EVENTS is undefined but sleep is handled by a
// native observer (see MacOSXSleepLock), so keep the checkbox visible.
#if !defined(wxHAS_POWER_EVENTS) && !defined(TC_MACOSX)
DismountOnPowerSavingCheckBox->Show (false);
#endif

#ifdef TC_MACOSX
DismountOnScreenSaverCheckBox->Show (false);
// On macOS this checkbox drives dismount on screen lock (see
// MacOSXSleepLock), so relabel it accordingly.
DismountOnScreenSaverCheckBox->SetLabel (LangString["IDC_PREF_UNMOUNT_SESSION_LOCKED"]);
DismountOnLogOffCheckBox->SetLabel (LangString["LINUX_VC_QUITS"]);
Comment on lines +298 to 308

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please align the visible preference text with the macOS behavior. If this means screen lock, please use the existing IDC_PREF_UNMOUNT_SESSION_LOCKED label on macOS, or also handle the actual screen saver notification. Also, making the power saving checkbox visible exposes the existing LINUX_ENTERING_POVERSAWING typo, which should be fixed or avoided.

OpenExplorerWindowAfterMountCheckBox->SetLabel (LangString["LINUX_OPEN_FINDER"]);

Expand Down
2 changes: 1 addition & 1 deletion src/Main/Forms/TrueCrypt.fbp
Original file line number Diff line number Diff line change
Expand Up @@ -11134,7 +11134,7 @@
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">LINUX_ENTERING_POVERSAWING</property>
<property name="label">LINUX_ENTERING_POWERSAVING</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
Expand Down
Loading
Loading