diff --git a/src/Core/Unix/MacOSX/CoreMacOSX.cpp b/src/Core/Unix/MacOSX/CoreMacOSX.cpp index aeff76ec3d..2ddcf5e9a2 100644 --- a/src/Core/Unix/MacOSX/CoreMacOSX.cpp +++ b/src/Core/Unix/MacOSX/CoreMacOSX.cpp @@ -317,12 +317,25 @@ namespace VeraCrypt mountedVolume = ml.front(); } +#ifdef VC_MACOSX_FUSET + pid_t fuseServiceProcessId = FuseService::RequestDismount (mountedVolume->AuxMountPoint, + mountedVolume->SerialInstanceNumber, mountedVolume->SlotNumber); +#endif + list args; args.push_back ("--"); args.push_back (mountedVolume->AuxMountPoint); for (int t = 0; true; t++) { +#ifdef VC_MACOSX_FUSET + try + { + if (GetMountedFilesystems (DevicePath(), mountedVolume->AuxMountPoint).empty()) + break; + } + catch (...) { } +#endif try { Process::Execute ("/sbin/umount", args); @@ -336,6 +349,10 @@ namespace VeraCrypt } } +#ifdef VC_MACOSX_FUSET + FuseService::WaitForDismount (fuseServiceProcessId, mountedVolume->AuxMountPoint, mountedVolume->SlotNumber); +#endif + try { mountedVolume->AuxMountPoint.Delete(); diff --git a/src/Driver/Fuse/FuseService.cpp b/src/Driver/Fuse/FuseService.cpp index d1cc507be6..581b6ae9cd 100644 --- a/src/Driver/Fuse/FuseService.cpp +++ b/src/Driver/Fuse/FuseService.cpp @@ -34,8 +34,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -46,6 +48,13 @@ #include #include "FuseService.h" +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) +#include +#undef fuse_unmount +#ifdef ERR_SUCCESS +#undef ERR_SUCCESS +#endif +#endif #include "Platform/FileStream.h" #include "Platform/MemoryStream.h" #include "Platform/Serializable.h" @@ -62,6 +71,16 @@ namespace VeraCrypt static const ino_t VC_FUSE_INODE_VOLUME = 2; static const ino_t VC_FUSE_INODE_CONTROL = 3; static const ino_t VC_FUSE_INODE_AUX_DEVICE_INFO = 4; +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + static const ino_t VC_FUSE_INODE_SHUTDOWN = 5; + static atomic FuseServiceShutdownRequested (false); + + struct FuseServiceShutdownContext + { + explicit FuseServiceShutdownContext (struct fuse *fuseHandle) : FuseHandle (fuseHandle) { } + struct fuse *FuseHandle; + }; +#endif static const uint64 VC_FUSE_BLOCK_SIZE = 4096; static const uint64 VC_FUSE_METADATA_SIZE = 64 * 1024; static const uint64 VC_FUSE_STAT_BLOCK_SIZE = 512; @@ -85,6 +104,51 @@ namespace VeraCrypt return FuseService::GetVolumeInfo(); } +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + static string fuse_service_get_shutdown_identity () + { + stringstream identity; + identity << getpid() << " " << FuseService::GetSerialInstanceNumber() << " " << FuseService::GetSlotNumber() << "\n"; + return identity.str(); + } + + static bool fuse_service_parse_shutdown_identity (const string &identity, pid_t &processId, uint64 &serialInstanceNumber, VolumeSlotNumber &slotNumber) + { + long long parsedProcessId; + uint64 parsedSerialInstanceNumber; + VolumeSlotNumber parsedSlotNumber; + stringstream parser (identity); + + if (!(parser >> parsedProcessId >> parsedSerialInstanceNumber >> parsedSlotNumber)) + return false; + + parser >> ws; + if (!parser.eof() || parsedProcessId <= 1 || static_cast (parsedProcessId) != parsedProcessId) + return false; + + processId = static_cast (parsedProcessId); + serialInstanceNumber = parsedSerialInstanceNumber; + slotNumber = parsedSlotNumber; + return true; + } + + static TC_THREAD_PROC fuse_service_shutdown (void *contextArg) + { + unique_ptr context (static_cast (contextArg)); + + // Let the write reply reach the caller before closing FUSE-T's channel. + Thread::Sleep (100); + fuse_exit (context->FuseHandle); + + struct fuse_session *session = fuse_get_session (context->FuseHandle); + struct fuse_chan *channel = session ? fuse_session_next_chan (session, NULL) : NULL; + if (channel) + fuse_unmount (NULL, channel); + + return 0; + } +#endif + 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; @@ -234,6 +298,16 @@ namespace VeraCrypt statData->st_ino = VC_FUSE_INODE_CONTROL; fuse_service_set_stat_blocks (statData); } +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + else if (strcmp (path, FuseService::GetShutdownPath()) == 0) + { + statData->st_mode = S_IFREG | 0600; + statData->st_nlink = 1; + statData->st_size = fuse_service_get_shutdown_identity().size(); + statData->st_ino = VC_FUSE_INODE_SHUTDOWN; + fuse_service_set_stat_blocks (statData); + } +#endif else { return -ENOENT; @@ -330,6 +404,14 @@ namespace VeraCrypt fi->direct_io = 1; return 0; } + +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + if (strcmp (path, FuseService::GetShutdownPath()) == 0) + { + fi->direct_io = 1; + return 0; + } +#endif } catch (...) { @@ -412,6 +494,24 @@ namespace VeraCrypt outBuf.CopyFrom (infoBuf->GetRange (offset, size)); return size; } + +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + if (strcmp (path, FuseService::GetShutdownPath()) == 0) + { + string identity = fuse_service_get_shutdown_identity(); + if (offset < 0) + return -EINVAL; + + if (offset >= (off_t) identity.size()) + return 0; + + if (offset + size > identity.size()) + size = identity.size() - offset; + + memcpy (buf, identity.data() + offset, size); + return size; + } +#endif } catch (...) { @@ -461,6 +561,10 @@ namespace VeraCrypt return 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; +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + if (fuse_service_fill_dir_entry (buf, filler, FuseService::GetShutdownPath() + 1, S_IFREG | 0600, VC_FUSE_INODE_SHUTDOWN, 0) != 0) + return 0; +#endif } catch (...) { @@ -506,6 +610,42 @@ namespace VeraCrypt FuseService::ReceiveAuxDeviceInfo (ConstBufferPtr ((const uint8 *) buf, size)); return size; } + +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + if (strcmp (path, FuseService::GetShutdownPath()) == 0) + { + pid_t processId; + uint64 serialInstanceNumber; + VolumeSlotNumber slotNumber; + struct fuse_context *context = fuse_get_context(); + + if (offset != 0 || !context || !context->fuse + || !fuse_service_parse_shutdown_identity (string (buf, size), processId, serialInstanceNumber, slotNumber) + || processId != getpid() + || serialInstanceNumber != FuseService::GetSerialInstanceNumber() + || slotNumber != FuseService::GetSlotNumber()) + return -EINVAL; + + bool expected = false; + if (FuseServiceShutdownRequested.compare_exchange_strong (expected, true)) + { + try + { + unique_ptr shutdownContext (new FuseServiceShutdownContext (context->fuse)); + Thread shutdownThread; + shutdownThread.Start (fuse_service_shutdown, shutdownContext.get()); + shutdownContext.release(); + shutdownThread.Detach(); + } + catch (...) + { + FuseServiceShutdownRequested = false; + throw; + } + } + return size; + } +#endif } #ifdef TC_FREEBSD // FreeBSD apparently retries failed write operations forever, which may lead to a system crash. @@ -742,6 +882,64 @@ namespace VeraCrypt fuseServiceControl.Close(); } +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + pid_t FuseService::RequestDismount (const DirectoryPath &fuseMountPoint, uint64 serialInstanceNumber, VolumeSlotNumber slotNumber) + { + shared_ptr shutdownFile (new File); + shutdownFile->Open (string (fuseMountPoint) + GetShutdownPath()); + + FileStream shutdownReader (shutdownFile); + string identity = shutdownReader.ReadToEnd(); + shutdownFile->Close(); + if (identity.empty() || identity.size() > 256) + throw ParameterIncorrect (SRC_POS); + + pid_t processId; + uint64 serviceSerialInstanceNumber; + VolumeSlotNumber serviceSlotNumber; + if (!fuse_service_parse_shutdown_identity (identity, processId, serviceSerialInstanceNumber, serviceSlotNumber) + || serviceSerialInstanceNumber != serialInstanceNumber || serviceSlotNumber != slotNumber) + { + stringstream logMessage; + logMessage << "Refusing to shut down mismatched VeraCrypt FUSE service: slot=" << slotNumber + << ", auxiliary mount=" << string (fuseMountPoint); + SystemLog::WriteError (logMessage.str()); + throw ParameterIncorrect (SRC_POS); + } + + shutdownFile->Open (string (fuseMountPoint) + GetShutdownPath(), File::OpenWrite); + shutdownFile->Write (ConstBufferPtr (reinterpret_cast (identity.data()), identity.size())); + shutdownFile->Close(); + return processId; + } + + void FuseService::WaitForDismount (pid_t processId, const DirectoryPath &fuseMountPoint, VolumeSlotNumber slotNumber, int timeOut) + { + for (int timeTaken = 0; ; timeTaken += 100) + { + if (kill (processId, 0) == -1) + { + if (errno == ESRCH) + return; + + if (errno != EPERM) + throw SystemException (SRC_POS); + } + + if (timeTaken >= timeOut) + { + stringstream logMessage; + logMessage << "VeraCrypt FUSE service did not terminate after shutdown request: pid=" << processId + << ", slot=" << slotNumber << ", auxiliary mount=" << string (fuseMountPoint); + SystemLog::WriteError (logMessage.str()); + throw TimeOut (SRC_POS); + } + + Thread::Sleep (100); + } + } +#endif + void FuseService::WriteVolumeSectors (const ConstBufferPtr &buffer, uint64 byteOffset) { if (!MountedVolume) diff --git a/src/Driver/Fuse/FuseService.h b/src/Driver/Fuse/FuseService.h index 72e6ed7d23..4686c1b94e 100644 --- a/src/Driver/Fuse/FuseService.h +++ b/src/Driver/Fuse/FuseService.h @@ -48,9 +48,14 @@ namespace VeraCrypt static const char *GetAuxDeviceInfoPath () { return "/aux-device-info"; } static const char *GetControlPath () { return "/control"; } static const char *GetVolumeImagePath (); +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + static const char *GetShutdownPath () { return "/shutdown"; } +#endif static string GetDeviceType () { return "veracrypt"; } static gid_t GetGroupId () { return GroupId; } static uid_t GetUserId () { return UserId; } + static uint64 GetSerialInstanceNumber () { return OpenVolumeInfo.SerialInstanceNumber; } + static VolumeSlotNumber GetSlotNumber () { return SlotNumber; } static shared_ptr GetAuxDeviceInfo (); static shared_ptr GetVolumeInfo (); static uint64 GetVolumeSize (); @@ -59,6 +64,10 @@ namespace VeraCrypt static void ReadVolumeSectors (const BufferPtr &buffer, uint64 byteOffset); static void ReceiveAuxDeviceInfo (const ConstBufferPtr &buffer); static void SendAuxDeviceInfo (const DirectoryPath &fuseMountPoint, const DevicePath &virtualDevice, const DevicePath &loopDevice = DevicePath()); +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + static pid_t RequestDismount (const DirectoryPath &fuseMountPoint, uint64 serialInstanceNumber, VolumeSlotNumber slotNumber); + static void WaitForDismount (pid_t processId, const DirectoryPath &fuseMountPoint, VolumeSlotNumber slotNumber, int timeOut = 10000); +#endif static void WriteVolumeSectors (const ConstBufferPtr &buffer, uint64 byteOffset); protected: