VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Core/Unix/CoreUnix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Core/Unix/CoreUnix.cpp')
-rw-r--r--src/Core/Unix/CoreUnix.cpp618
1 files changed, 618 insertions, 0 deletions
diff --git a/src/Core/Unix/CoreUnix.cpp b/src/Core/Unix/CoreUnix.cpp
new file mode 100644
index 0000000..89f34e2
--- /dev/null
+++ b/src/Core/Unix/CoreUnix.cpp
@@ -0,0 +1,618 @@
+/*
+ Copyright (c) 2008-2010 TrueCrypt Developers Association. All rights reserved.
+
+ Governed by the TrueCrypt License 3.0 the full text of which is contained in
+ the file License.txt included in TrueCrypt binary and source code distribution
+ packages.
+*/
+
+#include "CoreUnix.h"
+#include <errno.h>
+#include <iostream>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "Platform/FileStream.h"
+#include "Driver/Fuse/FuseService.h"
+#include "Volume/VolumePasswordCache.h"
+
+namespace TrueCrypt
+{
+ CoreUnix::CoreUnix ()
+ {
+ signal (SIGPIPE, SIG_IGN);
+
+ char *loc = setlocale (LC_ALL, "");
+ if (!loc || string (loc) == "C")
+ setlocale (LC_ALL, "en_US.UTF-8");
+ }
+
+ CoreUnix::~CoreUnix ()
+ {
+ }
+
+ void CoreUnix::CheckFilesystem (shared_ptr <VolumeInfo> mountedVolume, bool repair) const
+ {
+ if (!mountedVolume->MountPoint.IsEmpty())
+ DismountFilesystem (mountedVolume->MountPoint, false);
+
+ list <string> args;
+
+ args.push_back ("-T");
+ args.push_back ("fsck");
+
+ args.push_back ("-e");
+
+ string xargs = "fsck ";
+
+#ifdef TC_LINUX
+ if (!repair)
+ xargs += "-n ";
+ else
+ xargs += "-r ";
+#endif
+
+ xargs += string (mountedVolume->VirtualDevice) + "; echo '[Done]'; read W";
+ args.push_back (xargs);
+
+ try
+ {
+ Process::Execute ("xterm", args, 1000);
+ } catch (TimeOut&) { }
+ }
+
+ void CoreUnix::DismountFilesystem (const DirectoryPath &mountPoint, bool force) const
+ {
+ list <string> args;
+
+#ifdef TC_MACOSX
+ if (force)
+ args.push_back ("-f");
+#endif
+ args.push_back ("--");
+ args.push_back (mountPoint);
+
+ Process::Execute ("umount", args);
+ }
+
+ shared_ptr <VolumeInfo> CoreUnix::DismountVolume (shared_ptr <VolumeInfo> mountedVolume, bool ignoreOpenFiles, bool syncVolumeInfo)
+ {
+ if (!mountedVolume->MountPoint.IsEmpty())
+ {
+ DismountFilesystem (mountedVolume->MountPoint, ignoreOpenFiles);
+
+ // Delete mount directory if a default path has been used
+ if (string (mountedVolume->MountPoint).find (GetDefaultMountPointPrefix()) == 0)
+ mountedVolume->MountPoint.Delete();
+ }
+
+ try
+ {
+ DismountNativeVolume (mountedVolume);
+ }
+ catch (NotApplicable &) { }
+
+ if (!mountedVolume->LoopDevice.IsEmpty())
+ {
+ try
+ {
+ DetachLoopDevice (mountedVolume->LoopDevice);
+ }
+ catch (ExecutedProcessFailed&) { }
+ }
+
+ if (syncVolumeInfo || mountedVolume->Protection == VolumeProtection::HiddenVolumeReadOnly)
+ {
+ sync();
+ VolumeInfoList ml = GetMountedVolumes (mountedVolume->Path);
+
+ if (ml.size() > 0)
+ mountedVolume = ml.front();
+ }
+
+ list <string> args;
+ args.push_back ("--");
+ args.push_back (mountedVolume->AuxMountPoint);
+
+ for (int t = 0; true; t++)
+ {
+ try
+ {
+ Process::Execute ("umount", args);
+ break;
+ }
+ catch (ExecutedProcessFailed&)
+ {
+ if (t > 10)
+ throw;
+ Thread::Sleep (200);
+ }
+ }
+
+ try
+ {
+ mountedVolume->AuxMountPoint.Delete();
+ }
+ catch (...) { }
+
+ VolumeEventArgs eventArgs (mountedVolume);
+ VolumeDismountedEvent.Raise (eventArgs);
+
+ return mountedVolume;
+ }
+
+ bool CoreUnix::FilesystemSupportsLargeFiles (const FilePath &filePath) const
+ {
+ string path = filePath;
+ size_t pos;
+
+ while ((pos = path.find_last_of ('/')) != string::npos)
+ {
+ path = path.substr (0, pos);
+
+ if (path.empty())
+ break;
+
+ try
+ {
+ MountedFilesystemList filesystems = GetMountedFilesystems (DevicePath(), path);
+ if (!filesystems.empty())
+ {
+ const MountedFilesystem &fs = *filesystems.front();
+
+ if (fs.Type == "fat"
+ || fs.Type == "fat32"
+ || fs.Type == "vfat"
+ || fs.Type == "fatfs"
+ || fs.Type == "msdos"
+ || fs.Type == "msdosfs"
+ || fs.Type == "umsdos"
+ || fs.Type == "dos"
+ || fs.Type == "dosfs"
+ || fs.Type == "pcfs"
+ )
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ catch (...) { }
+ }
+
+ return true; // Prevent errors if the filesystem cannot be identified
+ }
+
+ bool CoreUnix::FilesystemSupportsUnixPermissions (const DevicePath &devicePath) const
+ {
+ File device;
+ device.Open (devicePath);
+
+ Buffer bootSector (device.GetDeviceSectorSize());
+ device.SeekAt (0);
+ device.ReadCompleteBuffer (bootSector);
+
+ byte *b = bootSector.Ptr();
+
+ return memcmp (b + 3, "NTFS", 4) != 0
+ && memcmp (b + 54, "FAT", 3) != 0
+ && memcmp (b + 82, "FAT32", 5) != 0
+ && memcmp (b + 3, "EXFAT", 5) != 0;
+ }
+
+ string CoreUnix::GetDefaultMountPointPrefix () const
+ {
+ const char *envPrefix = getenv ("TRUECRYPT_MOUNT_PREFIX");
+ if (envPrefix && !string (envPrefix).empty())
+ return envPrefix;
+
+ if (FilesystemPath ("/media").IsDirectory())
+ return "/media/truecrypt";
+
+ if (FilesystemPath ("/mnt").IsDirectory())
+ return "/mnt/truecrypt";
+
+ return GetTempDirectory() + "/truecrypt_mnt";
+ }
+
+ uint32 CoreUnix::GetDeviceSectorSize (const DevicePath &devicePath) const
+ {
+ File dev;
+ dev.Open (devicePath);
+ return dev.GetDeviceSectorSize();
+ }
+
+ uint64 CoreUnix::GetDeviceSize (const DevicePath &devicePath) const
+ {
+ File dev;
+ dev.Open (devicePath);
+ return dev.Length();
+ }
+
+ DirectoryPath CoreUnix::GetDeviceMountPoint (const DevicePath &devicePath) const
+ {
+ DevicePath devPath = devicePath;
+#ifdef TC_MACOSX
+ if (string (devPath).find ("/dev/rdisk") != string::npos)
+ devPath = string ("/dev/") + string (devicePath).substr (6);
+#endif
+ MountedFilesystemList mountedFilesystems = GetMountedFilesystems (devPath);
+
+ if (mountedFilesystems.size() < 1)
+ return DirectoryPath();
+
+ return mountedFilesystems.front()->MountPoint;
+ }
+
+ VolumeInfoList CoreUnix::GetMountedVolumes (const VolumePath &volumePath) const
+ {
+ VolumeInfoList volumes;
+
+ foreach_ref (const MountedFilesystem &mf, GetMountedFilesystems ())
+ {
+ if (string (mf.MountPoint).find (GetFuseMountDirPrefix()) == string::npos)
+ continue;
+
+ shared_ptr <VolumeInfo> mountedVol;
+ try
+ {
+ shared_ptr <File> controlFile (new File);
+ controlFile->Open (string (mf.MountPoint) + FuseService::GetControlPath());
+
+ shared_ptr <Stream> controlFileStream (new FileStream (controlFile));
+ mountedVol = Serializable::DeserializeNew <VolumeInfo> (controlFileStream);
+ }
+ catch (...)
+ {
+ continue;
+ }
+
+ if (!volumePath.IsEmpty() && wstring (mountedVol->Path).compare (volumePath) != 0)
+ continue;
+
+ mountedVol->AuxMountPoint = mf.MountPoint;
+
+ if (!mountedVol->VirtualDevice.IsEmpty())
+ {
+ MountedFilesystemList mpl = GetMountedFilesystems (mountedVol->VirtualDevice);
+
+ if (mpl.size() > 0)
+ mountedVol->MountPoint = mpl.front()->MountPoint;
+ }
+
+ volumes.push_back (mountedVol);
+
+ if (!volumePath.IsEmpty())
+ break;
+ }
+
+ return volumes;
+ }
+
+ gid_t CoreUnix::GetRealGroupId () const
+ {
+ const char *env = getenv ("SUDO_GID");
+ if (env)
+ {
+ try
+ {
+ string s (env);
+ return static_cast <gid_t> (StringConverter::ToUInt64 (s));
+ }
+ catch (...) { }
+ }
+
+ return getgid();
+ }
+
+ uid_t CoreUnix::GetRealUserId () const
+ {
+ const char *env = getenv ("SUDO_UID");
+ if (env)
+ {
+ try
+ {
+ string s (env);
+ return static_cast <uid_t> (StringConverter::ToUInt64 (s));
+ }
+ catch (...) { }
+ }
+
+ return getuid();
+ }
+
+ string CoreUnix::GetTempDirectory () const
+ {
+ char *envDir = getenv ("TMPDIR");
+ return envDir ? envDir : "/tmp";
+ }
+
+ bool CoreUnix::IsMountPointAvailable (const DirectoryPath &mountPoint) const
+ {
+ return GetMountedFilesystems (DevicePath(), mountPoint).size() == 0;
+ }
+
+ void CoreUnix::MountFilesystem (const DevicePath &devicePath, const DirectoryPath &mountPoint, const string &filesystemType, bool readOnly, const string &systemMountOptions) const
+ {
+ if (GetMountedFilesystems (DevicePath(), mountPoint).size() > 0)
+ throw MountPointUnavailable (SRC_POS);
+
+ list <string> args;
+ string options;
+
+ if (!filesystemType.empty())
+ {
+#ifdef TC_SOLARIS
+ args.push_back ("-F");
+#else
+ args.push_back ("-t");
+#endif
+ args.push_back (filesystemType);
+ }
+
+ if (readOnly)
+ options = "-oro";
+
+ if (!systemMountOptions.empty())
+ {
+ if (options.empty())
+ options = "-o";
+ else
+ options += ",";
+
+ options += systemMountOptions;
+ }
+
+ if (!options.empty())
+ args.push_back (options);
+
+ args.push_back ("--");
+ args.push_back (devicePath);
+ args.push_back (mountPoint);
+
+ Process::Execute ("mount", args);
+ }
+
+ VolumeSlotNumber CoreUnix::MountPointToSlotNumber (const DirectoryPath &mountPoint) const
+ {
+ string mountPointStr (mountPoint);
+ if (mountPointStr.find (GetDefaultMountPointPrefix()) == 0)
+ {
+ try
+ {
+ return StringConverter::ToUInt32 (StringConverter::GetTrailingNumber (mountPointStr));
+ }
+ catch (...) { }
+ }
+ return GetFirstFreeSlotNumber();
+ }
+
+ shared_ptr <VolumeInfo> CoreUnix::MountVolume (MountOptions &options)
+ {
+ CoalesceSlotNumberAndMountPoint (options);
+
+ if (IsVolumeMounted (*options.Path))
+ throw VolumeAlreadyMounted (SRC_POS);
+
+ Cipher::EnableHwSupport (!options.NoHardwareCrypto);
+
+ shared_ptr <Volume> volume;
+
+ while (true)
+ {
+ try
+ {
+ volume = OpenVolume (
+ options.Path,
+ options.PreserveTimestamps,
+ options.Password,
+ options.Keyfiles,
+ options.Protection,
+ options.ProtectionPassword,
+ options.ProtectionKeyfiles,
+ options.SharedAccessAllowed,
+ VolumeType::Unknown,
+ options.UseBackupHeaders,
+ options.PartitionInSystemEncryptionScope
+ );
+
+ options.Password.reset();
+ }
+ catch (SystemException &e)
+ {
+ if (options.Protection != VolumeProtection::ReadOnly
+ && (e.GetErrorCode() == EROFS || e.GetErrorCode() == EACCES || e.GetErrorCode() == EPERM))
+ {
+ // Read-only filesystem
+ options.Protection = VolumeProtection::ReadOnly;
+ continue;
+ }
+
+ throw;
+ }
+
+ break;
+ }
+
+ if (options.Path->IsDevice())
+ {
+ if (volume->GetFile()->GetDeviceSectorSize() != volume->GetSectorSize())
+ throw ParameterIncorrect (SRC_POS);
+
+#if defined (TC_LINUX)
+ if (volume->GetSectorSize() != TC_SECTOR_SIZE_LEGACY)
+ {
+ if (options.Protection == VolumeProtection::HiddenVolumeReadOnly)
+ throw UnsupportedSectorSizeHiddenVolumeProtection();
+
+ if (options.NoKernelCrypto)
+ throw UnsupportedSectorSizeNoKernelCrypto();
+ }
+#endif
+ }
+
+ // Find a free mount point for FUSE service
+ MountedFilesystemList mountedFilesystems = GetMountedFilesystems ();
+ string fuseMountPoint;
+ for (int i = 1; true; i++)
+ {
+ stringstream path;
+ path << GetTempDirectory() << "/" << GetFuseMountDirPrefix() << i;
+ FilesystemPath fsPath (path.str());
+
+ bool inUse = false;
+
+ foreach_ref (const MountedFilesystem &mf, mountedFilesystems)
+ {
+ if (mf.MountPoint == path.str())
+ {
+ inUse = true;
+ break;
+ }
+ }
+
+ if (!inUse)
+ {
+ try
+ {
+ if (fsPath.IsDirectory())
+ fsPath.Delete();
+
+ throw_sys_sub_if (mkdir (path.str().c_str(), S_IRUSR | S_IXUSR) == -1, path.str());
+
+ fuseMountPoint = fsPath;
+ break;
+ }
+ catch (...)
+ {
+ if (i > 255)
+ throw TemporaryDirectoryFailure (SRC_POS, StringConverter::ToWide (path.str()));
+ }
+ }
+ }
+
+ try
+ {
+ FuseService::Mount (volume, options.SlotNumber, fuseMountPoint);
+ }
+ catch (...)
+ {
+ try
+ {
+ DirectoryPath (fuseMountPoint).Delete();
+ }
+ catch (...) { }
+ throw;
+ }
+
+ try
+ {
+ // Create a mount directory if a default path has been specified
+ bool mountDirCreated = false;
+ string mountPoint;
+ if (!options.NoFilesystem && options.MountPoint)
+ {
+ mountPoint = *options.MountPoint;
+
+#ifndef TC_MACOSX
+ if (mountPoint.find (GetDefaultMountPointPrefix()) == 0 && !options.MountPoint->IsDirectory())
+ {
+ Directory::Create (*options.MountPoint);
+ try
+ {
+ throw_sys_sub_if (chown (mountPoint.c_str(), GetRealUserId(), GetRealGroupId()) == -1, mountPoint);
+ } catch (ParameterIncorrect&) { }
+
+ mountDirCreated = true;
+ }
+#endif
+ }
+
+ try
+ {
+ try
+ {
+ MountVolumeNative (volume, options, fuseMountPoint);
+ }
+ catch (NotApplicable&)
+ {
+ MountAuxVolumeImage (fuseMountPoint, options);
+ }
+ }
+ catch (...)
+ {
+ if (mountDirCreated)
+ remove (mountPoint.c_str());
+ throw;
+ }
+ }
+ catch (...)
+ {
+ try
+ {
+ VolumeInfoList mountedVolumes = GetMountedVolumes (*options.Path);
+ if (mountedVolumes.size() > 0)
+ {
+ shared_ptr <VolumeInfo> mountedVolume (mountedVolumes.front());
+ DismountVolume (mountedVolume);
+ }
+ }
+ catch (...) { }
+ throw;
+ }
+
+ VolumeInfoList mountedVolumes = GetMountedVolumes (*options.Path);
+ if (mountedVolumes.size() != 1)
+ throw ParameterIncorrect (SRC_POS);
+
+ VolumeEventArgs eventArgs (mountedVolumes.front());
+ VolumeMountedEvent.Raise (eventArgs);
+
+ return mountedVolumes.front();
+ }
+
+ void CoreUnix::MountAuxVolumeImage (const DirectoryPath &auxMountPoint, const MountOptions &options) const
+ {
+ DevicePath loopDev = AttachFileToLoopDevice (string (auxMountPoint) + FuseService::GetVolumeImagePath(), options.Protection == VolumeProtection::ReadOnly);
+
+ try
+ {
+ FuseService::SendAuxDeviceInfo (auxMountPoint, loopDev, loopDev);
+ }
+ catch (...)
+ {
+ try
+ {
+ DetachLoopDevice (loopDev);
+ }
+ catch (...) { }
+ throw;
+ }
+
+ if (!options.NoFilesystem && options.MountPoint && !options.MountPoint->IsEmpty())
+ {
+ MountFilesystem (loopDev, *options.MountPoint,
+ StringConverter::ToSingle (options.FilesystemType),
+ options.Protection == VolumeProtection::ReadOnly,
+ StringConverter::ToSingle (options.FilesystemOptions));
+ }
+ }
+
+ void CoreUnix::SetFileOwner (const FilesystemPath &path, const UserId &owner) const
+ {
+ throw_sys_if (chown (string (path).c_str(), owner.SystemId, (gid_t) -1) == -1);
+ }
+
+ DirectoryPath CoreUnix::SlotNumberToMountPoint (VolumeSlotNumber slotNumber) const
+ {
+ if (slotNumber < GetFirstSlotNumber() || slotNumber > GetLastSlotNumber())
+ throw ParameterIncorrect (SRC_POS);
+
+ stringstream s;
+ s << GetDefaultMountPointPrefix() << slotNumber;
+ return s.str();
+ }
+}