diff --git a/src/Core/Unix/CoreUnix.cpp b/src/Core/Unix/CoreUnix.cpp
index c41594bf..098e9cb4 100644
--- a/src/Core/Unix/CoreUnix.cpp
+++ b/src/Core/Unix/CoreUnix.cpp
@@ -350,7 +350,7 @@ namespace VeraCrypt
// The list is already filtered to VeraCrypt auxiliary mounts; in
// FUSE-T builds, the mount table device name varies by backend.
#ifdef VC_MACOSX_FUSET
- int controlFileRetries = 10; // 10 retries with 500ms sleep each, total 5 seconds
+ int controlFileRetries = volumePath.IsEmpty() ? 1 : 10; // Up to 10 attempts with 500ms sleeps for specific volume lookups
string controlFileError;
while (!mountedVol && (controlFileRetries-- > 0))
#endif
@@ -372,9 +372,12 @@ namespace VeraCrypt
{
#ifdef VC_MACOSX_FUSET
controlFileError = StringConverter::ToSingle (StringConverter::ToExceptionString (e));
- // FUSE-T's SMB backend can briefly expose the auxiliary mount
- // before the control file is readable and deserializable.
- Thread::Sleep (500);
+ 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);
+ }
#else
(void) e;
#endif
@@ -383,9 +386,12 @@ namespace VeraCrypt
catch (...)
{
controlFileError = "unknown exception";
- // FUSE-T's SMB backend can briefly expose the auxiliary mount
- // before the control file is readable and deserializable.
- Thread::Sleep (500);
+ 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);
+ }
}
#endif
}
@@ -393,12 +399,15 @@ namespace VeraCrypt
if (!mountedVol)
{
#ifdef VC_MACOSX_FUSET
- stringstream logMessage;
- logMessage << "Failed to read VeraCrypt auxiliary mount control file after retries: "
- << string (mf.MountPoint) << FuseService::GetControlPath();
- if (!controlFileError.empty())
- logMessage << ": " << controlFileError;
- SystemLog::WriteError (logMessage.str());
+ if (!volumePath.IsEmpty())
+ {
+ stringstream logMessage;
+ logMessage << "Failed to read VeraCrypt auxiliary mount control file after retries: "
+ << string (mf.MountPoint) << FuseService::GetControlPath();
+ if (!controlFileError.empty())
+ logMessage << ": " << controlFileError;
+ SystemLog::WriteError (logMessage.str());
+ }
#endif
continue; // Skip to the next mounted filesystem
}
diff --git a/src/Core/Unix/MacOSX/CoreMacOSX.cpp b/src/Core/Unix/MacOSX/CoreMacOSX.cpp
index 70484cfb..22883fda 100644
--- a/src/Core/Unix/MacOSX/CoreMacOSX.cpp
+++ b/src/Core/Unix/MacOSX/CoreMacOSX.cpp
@@ -23,9 +23,170 @@
#include "CoreMacOSX.h"
#include "Driver/Fuse/FuseService.h"
#include "Core/Unix/CoreServiceProxy.h"
+#include "Platform/FileStream.h"
+#include "Platform/MemoryStream.h"
+#include "Platform/Serializable.h"
+#include "Platform/SystemLog.h"
namespace VeraCrypt
{
+ static string DecodePlistXmlString (const string &xmlString)
+ {
+ string decoded;
+
+ for (size_t i = 0; i < xmlString.size(); ++i)
+ {
+ if (xmlString[i] != '&')
+ {
+ decoded += xmlString[i];
+ continue;
+ }
+
+ if (xmlString.compare (i, 5, "&") == 0)
+ {
+ decoded += '&';
+ i += 4;
+ }
+ else if (xmlString.compare (i, 4, "<") == 0)
+ {
+ decoded += '<';
+ i += 3;
+ }
+ else if (xmlString.compare (i, 4, ">") == 0)
+ {
+ decoded += '>';
+ i += 3;
+ }
+ else if (xmlString.compare (i, 6, """) == 0)
+ {
+ decoded += '"';
+ i += 5;
+ }
+ else if (xmlString.compare (i, 6, "'") == 0)
+ {
+ decoded += '\'';
+ i += 5;
+ }
+ else
+ decoded += xmlString[i];
+ }
+
+ return decoded;
+ }
+
+ static bool ExtractPlistString (const string &xml, const string &key, size_t start, size_t limit, string &value, size_t *endPos = nullptr)
+ {
+ string keyTag = "" + key + "";
+ size_t p = xml.find (keyTag, start);
+ if (p == string::npos || p >= limit)
+ return false;
+
+ p = xml.find ("", p + keyTag.size());
+ if (p == string::npos || p >= limit)
+ return false;
+ p += 8;
+
+ size_t e = xml.find ("", p);
+ if (e == string::npos || e > limit)
+ return false;
+
+ value = DecodePlistXmlString (xml.substr (p, e - p));
+ if (endPos)
+ *endPos = e + 9;
+
+ return true;
+ }
+
+ static string NormalizeDiskImagePath (const string &path)
+ {
+ string normalized;
+ bool previousSlash = false;
+
+ for (string::const_iterator i = path.begin(); i != path.end(); ++i)
+ {
+ if (*i == '/')
+ {
+ if (previousSlash)
+ continue;
+
+ previousSlash = true;
+ }
+ else
+ previousSlash = false;
+
+ normalized += *i;
+ }
+
+ if (normalized.find ("/private/var/") == 0)
+ normalized.erase (0, 8);
+
+ return normalized;
+ }
+
+ static DevicePath FindVirtualDeviceByImagePath (const string &imagePath)
+ {
+ list args;
+ args.push_back ("info");
+ args.push_back ("-plist");
+
+ string xml = Process::Execute ("/usr/bin/hdiutil", args);
+ string normalizedImagePath = NormalizeDiskImagePath (imagePath);
+
+ for (size_t p = 0; ; )
+ {
+ size_t imageKeyPos = xml.find ("image-path", p);
+ if (imageKeyPos == string::npos)
+ break;
+
+ string currentImagePath;
+ size_t imageValueEnd = 0;
+ if (!ExtractPlistString (xml, "image-path", imageKeyPos, string::npos, currentImagePath, &imageValueEnd))
+ {
+ p = imageKeyPos + 1;
+ continue;
+ }
+
+ size_t nextImageKeyPos = xml.find ("image-path", imageValueEnd);
+ if (NormalizeDiskImagePath (currentImagePath) == normalizedImagePath)
+ {
+ string devEntry;
+ if (ExtractPlistString (xml, "dev-entry", imageValueEnd, nextImageKeyPos, devEntry))
+ return StringConverter::Trim (devEntry);
+ }
+
+ p = imageValueEnd;
+ }
+
+ return DevicePath();
+ }
+
+ static bool AuxiliaryControlFileHasVirtualDevice (const DirectoryPath &auxMountPoint, const DevicePath &virtualDev)
+ {
+ for (int t = 0; t < 50; ++t)
+ {
+ try
+ {
+ shared_ptr controlFile (new File);
+ controlFile->Open (string (auxMountPoint) + FuseService::GetControlPath());
+
+ FileStream controlFileReader (controlFile);
+ string controlFileData = controlFileReader.ReadToEnd();
+ if (controlFileData.empty() || controlFileData.size() > 1024 * 1024)
+ throw ParameterIncorrect (SRC_POS);
+
+ shared_ptr controlFileStream (new MemoryStream (ConstBufferPtr ((const uint8 *) controlFileData.data(), controlFileData.size())));
+ shared_ptr mountedVol = Serializable::DeserializeNew (controlFileStream);
+ if (mountedVol && string (mountedVol->VirtualDevice) == string (virtualDev))
+ return true;
+ }
+ catch (...) { }
+
+ Thread::Sleep (100);
+ }
+
+ return false;
+ }
+
CoreMacOSX::CoreMacOSX ()
{
}
@@ -36,6 +197,17 @@ namespace VeraCrypt
shared_ptr CoreMacOSX::DismountVolume (shared_ptr mountedVolume, bool ignoreOpenFiles, bool syncVolumeInfo)
{
+ if (mountedVolume->VirtualDevice.IsEmpty() && !mountedVolume->AuxMountPoint.IsEmpty())
+ {
+ try
+ {
+ DevicePath recoveredVirtualDevice = FindVirtualDeviceByImagePath (string (mountedVolume->AuxMountPoint) + FuseService::GetVolumeImagePath());
+ if (!recoveredVirtualDevice.IsEmpty())
+ mountedVolume->VirtualDevice = recoveredVirtualDevice;
+ }
+ catch (...) { }
+ }
+
if (!mountedVolume->VirtualDevice.IsEmpty() && mountedVolume->VirtualDevice.IsBlockDevice())
{
list args;
@@ -223,6 +395,16 @@ namespace VeraCrypt
try
{
FuseService::SendAuxDeviceInfo (auxMountPoint, virtualDev);
+ if (!AuxiliaryControlFileHasVirtualDevice (auxMountPoint, virtualDev))
+ {
+ stringstream logMessage;
+ logMessage << "VeraCrypt auxiliary mount did not report hdiutil device after mount: "
+ << string (auxMountPoint) << FuseService::GetControlPath()
+ << ", expected " << string (virtualDev);
+ SystemLog::WriteError (logMessage.str());
+
+ throw TimeOut (SRC_POS);
+ }
}
catch (...)
{
@@ -230,7 +412,7 @@ namespace VeraCrypt
{
list args;
args.push_back ("detach");
- args.push_back (volImage);
+ args.push_back (virtualDev);
args.push_back ("-force");
Process::Execute ("/usr/bin/hdiutil", args);
diff --git a/src/Driver/Fuse/FuseService.cpp b/src/Driver/Fuse/FuseService.cpp
index a755c4d1..bc3ea62d 100644
--- a/src/Driver/Fuse/FuseService.cpp
+++ b/src/Driver/Fuse/FuseService.cpp
@@ -75,6 +75,14 @@ namespace VeraCrypt
statData->st_blocks = fuse_service_ceil_div ((uint64) statData->st_size, VC_FUSE_STAT_BLOCK_SIZE);
}
+ static shared_ptr fuse_service_get_control_info (struct fuse_file_info *fi)
+ {
+ if (fi && fi->fh)
+ return *reinterpret_cast *> (fi->fh);
+
+ return FuseService::GetVolumeInfo();
+ }
+
static int fuse_service_fill_dir_entry (void *buf, fuse_fill_dir_t filler, const char *name, mode_t mode, ino_t ino, off_t nextOff)
{
struct stat st;
@@ -200,7 +208,7 @@ namespace VeraCrypt
if (strcmp (path, FuseService::GetAuxDeviceInfoPath()) == 0)
{
- statData->st_mode = S_IFREG | 0200;
+ statData->st_mode = S_IFREG | 0600;
statData->st_nlink = 1;
statData->st_size = VC_FUSE_BLOCK_SIZE;
statData->st_ino = VC_FUSE_INODE_AUX_DEVICE_INFO;
@@ -218,7 +226,7 @@ namespace VeraCrypt
{
statData->st_mode = S_IFREG | 0600;
statData->st_nlink = 1;
- statData->st_size = FuseService::GetVolumeInfo()->Size();
+ statData->st_size = VC_FUSE_BLOCK_SIZE;
statData->st_ino = VC_FUSE_INODE_CONTROL;
fuse_service_set_stat_blocks (statData);
}
@@ -314,6 +322,7 @@ namespace VeraCrypt
if (strcmp (path, FuseService::GetControlPath()) == 0)
{
+ fi->fh = reinterpret_cast (new shared_ptr (FuseService::GetVolumeInfo()));
fi->direct_io = 1;
return 0;
}
@@ -372,7 +381,22 @@ namespace VeraCrypt
if (strcmp (path, FuseService::GetControlPath()) == 0)
{
- shared_ptr infoBuf = FuseService::GetVolumeInfo();
+ shared_ptr infoBuf = fuse_service_get_control_info (fi);
+ BufferPtr outBuf ((uint8 *)buf, size);
+
+ if (offset >= (off_t) infoBuf->Size())
+ return 0;
+
+ if (offset + size > infoBuf->Size())
+ size = infoBuf->Size () - offset;
+
+ outBuf.CopyFrom (infoBuf->GetRange (offset, size));
+ return size;
+ }
+
+ if (strcmp (path, FuseService::GetAuxDeviceInfoPath()) == 0)
+ {
+ shared_ptr infoBuf = FuseService::GetAuxDeviceInfo();
BufferPtr outBuf ((uint8 *)buf, size);
if (offset >= (off_t) infoBuf->Size())
@@ -393,6 +417,24 @@ namespace VeraCrypt
return -ENOENT;
}
+ static int fuse_service_release (const char *path, struct fuse_file_info *fi)
+ {
+ try
+ {
+ if (strcmp (path, FuseService::GetControlPath()) == 0 && fi && fi->fh)
+ {
+ delete reinterpret_cast *> (fi->fh);
+ fi->fh = 0;
+ }
+ }
+ catch (...)
+ {
+ return FuseService::ExceptionToErrorCode();
+ }
+
+ return 0;
+ }
+
static int fuse_service_readdir_impl (const char *path, void *buf, fuse_fill_dir_t filler, struct fuse_file_info *fi)
{
(void) fi;
@@ -413,7 +455,7 @@ namespace VeraCrypt
return 0;
if (fuse_service_fill_dir_entry (buf, filler, FuseService::GetControlPath() + 1, S_IFREG | 0600, VC_FUSE_INODE_CONTROL, 0) != 0)
return 0;
- if (fuse_service_fill_dir_entry (buf, filler, FuseService::GetAuxDeviceInfoPath() + 1, S_IFREG | 0200, VC_FUSE_INODE_AUX_DEVICE_INFO, 0) != 0)
+ if (fuse_service_fill_dir_entry (buf, filler, FuseService::GetAuxDeviceInfoPath() + 1, S_IFREG | 0600, VC_FUSE_INODE_AUX_DEVICE_INFO, 0) != 0)
return 0;
}
catch (...)
@@ -548,6 +590,25 @@ namespace VeraCrypt
}
}
+ shared_ptr FuseService::GetAuxDeviceInfo ()
+ {
+ shared_ptr stream (new MemoryStream);
+ Serializer sr (stream);
+
+ {
+ ScopeLock lock (OpenVolumeInfoMutex);
+
+ sr.Serialize ("VirtualDevice", string (OpenVolumeInfo.VirtualDevice));
+ sr.Serialize ("LoopDevice", string (OpenVolumeInfo.LoopDevice));
+ }
+
+ ConstBufferPtr infoBuf = dynamic_cast (*stream);
+ shared_ptr outBuf (new Buffer (infoBuf.Size()));
+ outBuf->CopyFrom (infoBuf);
+
+ return outBuf;
+ }
+
shared_ptr FuseService::GetVolumeInfo ()
{
shared_ptr stream (new MemoryStream);
@@ -666,6 +727,7 @@ namespace VeraCrypt
sr.Serialize ("VirtualDevice", string (virtualDevice));
sr.Serialize ("LoopDevice", string (loopDevice));
fuseServiceControl.Write (dynamic_cast (*stream));
+ fuseServiceControl.Close();
}
void FuseService::WriteVolumeSectors (const ConstBufferPtr &buffer, uint64 byteOffset)
@@ -728,6 +790,7 @@ namespace VeraCrypt
fuse_service_oper.opendir = fuse_service_opendir;
fuse_service_oper.read = fuse_service_read;
fuse_service_oper.readdir = fuse_service_readdir;
+ fuse_service_oper.release = fuse_service_release;
fuse_service_oper.statfs = fuse_service_statfs;
fuse_service_oper.write = fuse_service_write;
diff --git a/src/Driver/Fuse/FuseService.h b/src/Driver/Fuse/FuseService.h
index a07142cb..8e9939e2 100644
--- a/src/Driver/Fuse/FuseService.h
+++ b/src/Driver/Fuse/FuseService.h
@@ -51,6 +51,7 @@ namespace VeraCrypt
static string GetDeviceType () { return "veracrypt"; }
static gid_t GetGroupId () { return GroupId; }
static uid_t GetUserId () { return UserId; }
+ static shared_ptr GetAuxDeviceInfo ();
static shared_ptr GetVolumeInfo ();
static uint64 GetVolumeSize ();
static uint64 GetVolumeSectorSize () { return MountedVolume->GetSectorSize(); }