diff --git a/src/Core/Unix/CoreUnix.cpp b/src/Core/Unix/CoreUnix.cpp index e327a6aa..c41594bf 100644 --- a/src/Core/Unix/CoreUnix.cpp +++ b/src/Core/Unix/CoreUnix.cpp @@ -11,14 +11,18 @@ */ #include "CoreUnix.h" +#include "Common/Tcdefs.h" #include #include #include #include +#include #include #include #include #include "Platform/FileStream.h" +#include "Platform/MemoryStream.h" +#include "Platform/SystemLog.h" #include "Driver/Fuse/FuseService.h" #include "Volume/VolumePasswordCache.h" @@ -347,6 +351,7 @@ namespace VeraCrypt // 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 + string controlFileError; while (!mountedVol && (controlFileRetries-- > 0)) #endif { @@ -355,30 +360,46 @@ namespace VeraCrypt shared_ptr controlFile (new File); controlFile->Open (string (mf.MountPoint) + FuseService::GetControlPath()); - shared_ptr controlFileStream (new FileStream (controlFile)); + 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()))); mountedVol = Serializable::DeserializeNew (controlFileStream); } catch (const std::exception& e) { #ifdef VC_MACOSX_FUSET - // if exception starts with "VeraCrypt::Serializer::ValidateName", then - // serialization is not ready yet and we need to wait before retrying - // this happens when FUSE-T is used under macOS and if it is the first time - // the volume is mounted - if (string (e.what()).find ("VeraCrypt::Serializer::ValidateName") != string::npos) - { - Thread::Sleep(500); // Wait before retrying - } - else - { - break; // Control file not found or other error - } + 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); +#else + (void) e; #endif } +#ifdef VC_MACOSX_FUSET + 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); + } +#endif } 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()); +#endif continue; // Skip to the next mounted filesystem } @@ -717,6 +738,8 @@ namespace VeraCrypt throw; } + DevicePath mountedVirtualDevice; + try { // Create a mount directory if a default path has been specified @@ -748,7 +771,7 @@ namespace VeraCrypt } catch (NotApplicable&) { - MountAuxVolumeImage (fuseMountPoint, options); + mountedVirtualDevice = MountAuxVolumeImage (fuseMountPoint, options); } } catch (...) @@ -790,17 +813,58 @@ namespace VeraCrypt throw; } +#ifdef VC_MACOSX_FUSET VolumeInfoList mountedVolumes = GetMountedVolumes (*options.Path); - if (mountedVolumes.size() != 1) + shared_ptr mountedVolume; + if (mountedVolumes.size() == 1) + { + mountedVolume = mountedVolumes.front(); + } + else if (!mountedVirtualDevice.IsEmpty()) + { + mountedVolume.reset (new VolumeInfo); + mountedVolume->Set (*volume); + mountedVolume->ProgramVersion = VERSION_NUM; + mountedVolume->SlotNumber = options.SlotNumber; + mountedVolume->AuxMountPoint = fuseMountPoint; + mountedVolume->VirtualDevice = mountedVirtualDevice; + + struct timeval tv; + gettimeofday (&tv, NULL); + mountedVolume->SerialInstanceNumber = (uint64) tv.tv_sec * 1000000ULL + tv.tv_usec; + + if (!options.NoFilesystem) + { + for (int mountPointRetries = 20; mountPointRetries > 0; --mountPointRetries) + { + try + { + mountedVolume->MountPoint = GetDeviceMountPoint (mountedVirtualDevice); + if (!mountedVolume->MountPoint.IsEmpty()) + break; + } + catch (...) { } + + Thread::Sleep (500); + } + } + } +#else + VolumeInfoList mountedVolumes = GetMountedVolumes (*options.Path); + shared_ptr mountedVolume; + if (mountedVolumes.size() == 1) + mountedVolume = mountedVolumes.front(); +#endif + if (!mountedVolume) throw ParameterIncorrect (SRC_POS); - VolumeEventArgs eventArgs (mountedVolumes.front()); + VolumeEventArgs eventArgs (mountedVolume); VolumeMountedEvent.Raise (eventArgs); - return mountedVolumes.front(); + return mountedVolume; } - void CoreUnix::MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const + DevicePath CoreUnix::MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const { DevicePath loopDev = AttachFileToLoopDevice (string (auxMountPoint) + FuseService::GetVolumeImagePath(), options.Protection == VolumeProtection::ReadOnly); @@ -825,6 +889,8 @@ namespace VeraCrypt options.Protection == VolumeProtection::ReadOnly, StringConverter::ToSingle (options.FilesystemOptions)); } + + return loopDev; } void CoreUnix::SetFileOwner (const FilesystemPath &path, const UserId &owner) const diff --git a/src/Core/Unix/CoreUnix.h b/src/Core/Unix/CoreUnix.h index bd7efe53..fefd3ac3 100644 --- a/src/Core/Unix/CoreUnix.h +++ b/src/Core/Unix/CoreUnix.h @@ -63,7 +63,7 @@ namespace VeraCrypt virtual gid_t GetRealGroupId () const; virtual string GetTempDirectory () const; virtual void MountFilesystem (const DevicePath &devicePath, const DirectoryPath &mountPoint, const string &filesystemType, bool readOnly, const string &systemMountOptions) const; - virtual void MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const; + virtual DevicePath MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const; virtual void MountVolumeNative (shared_ptr volume, MountOptions &options, const DirectoryPath &auxMountPoint) const { throw NotApplicable (SRC_POS); } private: diff --git a/src/Core/Unix/MacOSX/CoreMacOSX.cpp b/src/Core/Unix/MacOSX/CoreMacOSX.cpp index 21b70325..70484cfb 100644 --- a/src/Core/Unix/MacOSX/CoreMacOSX.cpp +++ b/src/Core/Unix/MacOSX/CoreMacOSX.cpp @@ -117,7 +117,7 @@ namespace VeraCrypt Process::Execute ("/usr/bin/open", args); } - void CoreMacOSX::MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const + DevicePath CoreMacOSX::MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const { #ifndef VC_MACOSX_FUSET // Check FUSE version @@ -238,6 +238,8 @@ namespace VeraCrypt catch (ExecutedProcessFailed&) { } throw; } + + return virtualDev; } unique_ptr Core (new CoreServiceProxy ); diff --git a/src/Core/Unix/MacOSX/CoreMacOSX.h b/src/Core/Unix/MacOSX/CoreMacOSX.h index e7dd9d74..38ee7237 100644 --- a/src/Core/Unix/MacOSX/CoreMacOSX.h +++ b/src/Core/Unix/MacOSX/CoreMacOSX.h @@ -29,7 +29,7 @@ namespace VeraCrypt virtual string GetDefaultMountPointPrefix () const { return "/Volumes/veracrypt"; } protected: - virtual void MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const; + virtual DevicePath MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const; private: CoreMacOSX (const CoreMacOSX &); diff --git a/src/Driver/Fuse/FuseService.cpp b/src/Driver/Fuse/FuseService.cpp index 61bbe2d5..a755c4d1 100644 --- a/src/Driver/Fuse/FuseService.cpp +++ b/src/Driver/Fuse/FuseService.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,20 @@ namespace VeraCrypt static const ino_t VC_FUSE_INODE_ROOT = 1; 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; + static const uint64 VC_FUSE_BLOCK_SIZE = 4096; + static const uint64 VC_FUSE_STAT_BLOCK_SIZE = 512; + + static uint64 fuse_service_ceil_div (uint64 value, uint64 divisor) + { + return (value / divisor) + ((value % divisor) ? 1 : 0); + } + + static void fuse_service_set_stat_blocks (struct stat *statData) + { + statData->st_blksize = VC_FUSE_BLOCK_SIZE; + statData->st_blocks = fuse_service_ceil_div ((uint64) statData->st_size, VC_FUSE_STAT_BLOCK_SIZE); + } 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) { @@ -69,6 +84,7 @@ namespace VeraCrypt st.st_uid = FuseService::GetUserId(); st.st_gid = FuseService::GetGroupId(); st.st_ino = ino; + fuse_service_set_stat_blocks (&st); return VC_FUSE_FILL_DIR (filler, buf, name, &st, nextOff); } @@ -169,6 +185,7 @@ namespace VeraCrypt statData->st_atime = time (NULL); statData->st_ctime = time (NULL); statData->st_mtime = time (NULL); + statData->st_blksize = VC_FUSE_BLOCK_SIZE; if (strcmp (path, "/") == 0) { @@ -181,12 +198,21 @@ namespace VeraCrypt if (!FuseService::CheckAccessRights()) return -EACCES; - if (strcmp (path, FuseService::GetVolumeImagePath()) == 0) + if (strcmp (path, FuseService::GetAuxDeviceInfoPath()) == 0) + { + statData->st_mode = S_IFREG | 0200; + statData->st_nlink = 1; + statData->st_size = VC_FUSE_BLOCK_SIZE; + statData->st_ino = VC_FUSE_INODE_AUX_DEVICE_INFO; + fuse_service_set_stat_blocks (statData); + } + else if (strcmp (path, FuseService::GetVolumeImagePath()) == 0) { statData->st_mode = S_IFREG | 0600; statData->st_nlink = 1; statData->st_size = FuseService::GetVolumeSize(); statData->st_ino = VC_FUSE_INODE_VOLUME; + fuse_service_set_stat_blocks (statData); } else if (strcmp (path, FuseService::GetControlPath()) == 0) { @@ -194,6 +220,7 @@ namespace VeraCrypt statData->st_nlink = 1; statData->st_size = FuseService::GetVolumeInfo()->Size(); statData->st_ino = VC_FUSE_INODE_CONTROL; + fuse_service_set_stat_blocks (statData); } else { @@ -222,6 +249,35 @@ namespace VeraCrypt } #endif + static int fuse_service_statfs (const char *path, struct statvfs *statData) + { + try + { + (void) path; + + uint64 blockCount = fuse_service_ceil_div (FuseService::GetVolumeSize(), VC_FUSE_BLOCK_SIZE); + if (blockCount == 0) + blockCount = 1; + + Memory::Zero (statData, sizeof (*statData)); + statData->f_bsize = VC_FUSE_BLOCK_SIZE; + statData->f_frsize = VC_FUSE_BLOCK_SIZE; + statData->f_blocks = blockCount; + statData->f_bfree = blockCount; + statData->f_bavail = blockCount; + statData->f_files = 4; + statData->f_ffree = 0; + statData->f_favail = 0; + statData->f_namemax = 255; + } + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + + return 0; + } + static int fuse_service_opendir (const char *path, struct fuse_file_info *fi) { try @@ -250,6 +306,12 @@ namespace VeraCrypt if (strcmp (path, FuseService::GetVolumeImagePath()) == 0) return 0; + if (strcmp (path, FuseService::GetAuxDeviceInfoPath()) == 0) + { + fi->direct_io = 1; + return 0; + } + if (strcmp (path, FuseService::GetControlPath()) == 0) { fi->direct_io = 1; @@ -351,6 +413,8 @@ 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) + return 0; } catch (...) { @@ -388,12 +452,12 @@ namespace VeraCrypt return size; } - if (strcmp (path, FuseService::GetControlPath()) == 0) + if (strcmp (path, FuseService::GetAuxDeviceInfoPath()) == 0) { if (FuseService::AuxDeviceInfoReceived()) return -EACCES; - FuseService::ReceiveAuxDeviceInfo (ConstBufferPtr ((const uint8 *)buf, size)); + FuseService::ReceiveAuxDeviceInfo (ConstBufferPtr ((const uint8 *) buf, size)); return size; } } @@ -594,7 +658,7 @@ namespace VeraCrypt void FuseService::SendAuxDeviceInfo (const DirectoryPath &fuseMountPoint, const DevicePath &virtualDevice, const DevicePath &loopDevice) { File fuseServiceControl; - fuseServiceControl.Open (string (fuseMountPoint) + GetControlPath(), File::OpenWrite); + fuseServiceControl.Open (string (fuseMountPoint) + GetAuxDeviceInfoPath(), File::OpenWrite); shared_ptr stream (new MemoryStream); Serializer sr (stream); @@ -664,6 +728,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.statfs = fuse_service_statfs; fuse_service_oper.write = fuse_service_write; // Create a new session diff --git a/src/Driver/Fuse/FuseService.h b/src/Driver/Fuse/FuseService.h index 35101678..a07142cb 100644 --- a/src/Driver/Fuse/FuseService.h +++ b/src/Driver/Fuse/FuseService.h @@ -45,6 +45,7 @@ namespace VeraCrypt static bool CheckAccessRights (); static void Dismount (); static int ExceptionToErrorCode (); + static const char *GetAuxDeviceInfoPath () { return "/aux-device-info"; } static const char *GetControlPath () { return "/control"; } static const char *GetVolumeImagePath (); static string GetDeviceType () { return "veracrypt"; }