📄 inplace.c
字号:
/*
Copyright (c) 2008 TrueCrypt Foundation. All rights reserved.
Governed by the TrueCrypt License 2.6 the full text of which is contained
in the file License.txt included in TrueCrypt binary and source code
distribution packages.
*/
/* In this file, _WIN32_WINNT is defined as 0x0600 to make file shrink available (Vista
or later). _WIN32_WINNT cannot be defined as 0x0600 for the entire user-space projects
because it breaks the main font app when the app is running on XP (likely an MS bug).
IMPORTANT: Due to this issue, functions in this file must not directly interact with GUI. */
#define TC_LOCAL_WIN32_WINNT_OVERRIDE 1
#if (_WIN32_WINNT < 0x0600)
# undef _WIN32_WINNT
# define _WIN32_WINNT 0x0600
#endif
#include <stdlib.h>
#include <string.h>
#include <string>
#include "Tcdefs.h"
#include "Platform/Finally.h"
#include "Common.h"
#include "Crc.h"
#include "Dlgcode.h"
#include "Language.h"
#include "Tcformat.h"
#include "Volumes.h"
#include "InPlace.h"
using namespace std;
using namespace TrueCrypt;
#define TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE (2048 * BYTES_PER_KB)
#define TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE (2 * TC_MAX_VOLUME_SECTOR_SIZE)
#define TC_NTFS_CONCEAL_CONSTANT 0xFF
#define TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL (64 * BYTES_PER_MB)
#define TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE (TC_TOTAL_VOLUME_HEADERS_SIZE + TC_MIN_NTFS_FS_SIZE * 2)
// If the returned value is greater than 0, it is the desired volume size in NTFS sectors (not in bytes)
// after shrinking has been performed. If there's any error, returns -1.
static __int64 NewFileSysSizeAfterShrink (HANDLE dev, const char *devicePath, int64 *totalClusterCount, DWORD *bytesPerCluster, BOOL silent)
{
NTFS_VOLUME_DATA_BUFFER ntfsVolData;
DWORD nBytesReturned;
__int64 fileSysSize, desiredNbrSectors;
// Filesystem size and sector size
if (!DeviceIoControl (dev,
FSCTL_GET_NTFS_VOLUME_DATA,
NULL,
0,
(LPVOID) &ntfsVolData,
sizeof (ntfsVolData),
&nBytesReturned,
NULL))
{
if (!silent)
handleWin32Error (MainDlg);
return -1;
}
fileSysSize = ntfsVolData.NumberSectors.QuadPart * ntfsVolData.BytesPerSector;
desiredNbrSectors = (fileSysSize - TC_TOTAL_VOLUME_HEADERS_SIZE) / ntfsVolData.BytesPerSector;
if (desiredNbrSectors <= 0)
return -1;
if (totalClusterCount)
*totalClusterCount = ntfsVolData.TotalClusters.QuadPart;
if (bytesPerCluster)
*bytesPerCluster = ntfsVolData.BytesPerCluster;
return desiredNbrSectors;
}
BOOL CheckRequirementsForNonSysInPlaceEnc (const char *devicePath, BOOL silent)
{
NTFS_VOLUME_DATA_BUFFER ntfsVolData;
DWORD nBytesReturned;
HANDLE dev;
char szFileSysName [256];
WCHAR devPath [MAX_PATH];
char dosDev [TC_MAX_PATH] = {0};
char devName [MAX_PATH] = {0};
int driveLetterNo = -1;
char szRootPath[4] = {0, ':', '\\', 0};
__int64 deviceSize;
int partitionNumber = -1, driveNumber = -1;
/* ---------- Checks that do not require admin rights ----------- */
/* Operating system */
if (CurrentOSMajor < 6)
{
if (!silent)
ShowInPlaceEncErrMsgWAltSteps ("OS_NOT_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE);
return FALSE;
}
/* Volume type (must be a partition or a dynamic volume) */
if (sscanf (devicePath, "\\Device\\HarddiskVolume%d", &partitionNumber) != 1
&& sscanf (devicePath, "\\Device\\Harddisk%d\\Partition%d", &partitionNumber, &driveNumber) != 2)
{
if (!silent)
Error ("INPLACE_ENC_INVALID_PATH");
return FALSE;
}
if (driveNumber == 0)
{
if (!silent)
Warning ("RAW_DEV_NOT_SUPPORTED_FOR_INPLACE_ENC");
return FALSE;
}
/* Admin rights */
if (!IsAdmin())
{
// We rely on the wizard process to call us only when the whole wizard process has been elevated (so UAC
// status can be ignored). In case the IsAdmin() detection somehow fails, we allow the user to continue.
if (!silent)
Warning ("ADMIN_PRIVILEGES_WARN_DEVICES");
}
/* ---------- Checks that may require admin rights ----------- */
/* Access to the partition */
strcpy ((char *) devPath, devicePath);
ToUNICODE ((char *) devPath);
driveLetterNo = GetDiskDeviceDriveLetter (devPath);
if (driveLetterNo >= 0)
szRootPath[0] = driveLetterNo + 'A';
if (FakeDosNameForDevice (devicePath, dosDev, devName, FALSE) != 0)
{
if (!silent)
{
handleWin32Error (MainDlg);
Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL");
}
return FALSE;
}
dev = OpenPartitionVolume (devName,
FALSE, // Do not require exclusive access
TRUE, // Require shared access (must be TRUE; otherwise, volume properties will not be possible to obtain)
FALSE, // Do not ask the user to confirm shared access (if exclusive fails)
FALSE, // Do not append alternative instructions how to encrypt the data (to applicable error messages)
silent); // Silent mode
if (dev == INVALID_HANDLE_VALUE)
return FALSE;
/* File system type */
GetVolumeInformation (szRootPath, NULL, 0, NULL, NULL, NULL, szFileSysName, sizeof(szFileSysName));
if (strncmp (szFileSysName, "NTFS", 4))
{
// The previous filesystem type detection method failed (or it's not NTFS) -- try an alternative method
if (!DeviceIoControl (dev,
FSCTL_GET_NTFS_VOLUME_DATA,
NULL,
0,
(LPVOID) &ntfsVolData,
sizeof (ntfsVolData),
&nBytesReturned,
NULL))
{
if (!silent)
{
// The filesystem is not NTFS or the filesystem type could not be determined (or the NTFS filesystem
// is dismounted).
if (IsDeviceMounted (devName))
ShowInPlaceEncErrMsgWAltSteps ("ONLY_NTFS_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE);
else
Warning ("ONLY_MOUNTED_VOL_SUPPORTED_FOR_NONSYS_INPLACE_ENC");
}
CloseHandle (dev);
return FALSE;
}
}
/* Attempt to determine whether the filesystem can be safely shrunk */
if (NewFileSysSizeAfterShrink (dev, devicePath, NULL, NULL, silent) == -1)
{
// Cannot determine whether shrinking is required
if (!silent)
ShowInPlaceEncErrMsgWAltSteps ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
CloseHandle (dev);
return FALSE;
}
/* Partition size */
deviceSize = GetDeviceSize (devicePath);
if (deviceSize < 0)
{
// Cannot determine the size of the partition
if (!silent)
Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL");
CloseHandle (dev);
return FALSE;
}
if (deviceSize < TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE)
{
// The partition is too small
if (!silent)
{
ShowInPlaceEncErrMsgWAltSteps ("PARTITION_TOO_SMALL_FOR_NONSYS_INPLACE_ENC", FALSE);
}
CloseHandle (dev);
return FALSE;
}
/* Free space on the filesystem */
if (!DeviceIoControl (dev,
FSCTL_GET_NTFS_VOLUME_DATA,
NULL,
0,
(LPVOID) &ntfsVolData,
sizeof (ntfsVolData),
&nBytesReturned,
NULL))
{
if (!silent)
ShowInPlaceEncErrMsgWAltSteps ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", TRUE);
CloseHandle (dev);
return FALSE;
}
if (ntfsVolData.FreeClusters.QuadPart * ntfsVolData.BytesPerCluster < TC_TOTAL_VOLUME_HEADERS_SIZE)
{
if (!silent)
ShowInPlaceEncErrMsgWAltSteps ("NOT_ENOUGH_FREE_FILESYS_SPACE_FOR_SHRINK", TRUE);
CloseHandle (dev);
return FALSE;
}
/* Filesystem sector size */
if (ntfsVolData.BytesPerSector > TC_MAX_VOLUME_SECTOR_SIZE
|| ntfsVolData.BytesPerSector % ENCRYPTION_DATA_UNIT_SIZE != 0)
{
if (!silent)
ShowInPlaceEncErrMsgWAltSteps ("ERR_UNSUPPORTED_SECTOR_SIZE_GENERIC", TRUE);
CloseHandle (dev);
return FALSE;
}
CloseHandle (dev);
return TRUE;
}
int EncryptPartitionInPlaceBegin (volatile FORMAT_VOL_PARAMETERS *volParams, volatile HANDLE *outHandle, WipeAlgorithmId wipeAlgorithm)
{
SHRINK_VOLUME_INFORMATION shrinkVolInfo;
signed __int64 sizeToShrinkTo;
int nStatus = ERR_SUCCESS;
PCRYPTO_INFO cryptoInfo = NULL;
PCRYPTO_INFO cryptoInfo2 = NULL;
HANDLE dev = INVALID_HANDLE_VALUE;
DWORD dwError;
char *header;
char dosDev[TC_MAX_PATH] = {0};
char devName[MAX_PATH] = {0};
int driveLetter = -1;
WCHAR deviceName[MAX_PATH];
uint64 dataAreaSize;
__int64 deviceSize;
LARGE_INTEGER offset;
DWORD dwResult;
SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PREPARING);
if (!CheckRequirementsForNonSysInPlaceEnc (volParams->volumePath, FALSE))
return ERR_DONT_REPORT;
header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE);
if (!header)
return ERR_OUTOFMEMORY;
VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
deviceSize = GetDeviceSize (volParams->volumePath);
if (deviceSize < 0)
{
// Cannot determine the size of the partition
nStatus = ERR_PARAMETER_INCORRECT;
goto closing_seq;
}
if (deviceSize < TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE)
{
ShowInPlaceEncErrMsgWAltSteps ("PARTITION_TOO_SMALL_FOR_NONSYS_INPLACE_ENC", TRUE);
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
dataAreaSize = GetVolumeDataAreaSize (volParams->hiddenVol, deviceSize);
strcpy ((char *)deviceName, volParams->volumePath);
ToUNICODE ((char *)deviceName);
driveLetter = GetDiskDeviceDriveLetter (deviceName);
if (FakeDosNameForDevice (volParams->volumePath, dosDev, devName, FALSE) != 0)
{
nStatus = ERR_OS_ERROR;
goto closing_seq;
}
if (IsDeviceMounted (devName))
{
dev = OpenPartitionVolume (devName,
FALSE, // Do not require exclusive access (must be FALSE; otherwise, it will not be possible to dismount the volume or obtain its properties and FSCTL_ALLOW_EXTENDED_DASD_IO will fail too)
TRUE, // Require shared access (must be TRUE; otherwise, it will not be possible to dismount the volume or obtain its properties and FSCTL_ALLOW_EXTENDED_DASD_IO will fail too)
FALSE, // Do not ask the user to confirm shared access (if exclusive fails)
FALSE, // Do not append alternative instructions how to encrypt the data (to applicable error messages)
FALSE); // Non-silent mode
if (dev == INVALID_HANDLE_VALUE)
{
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
}
else
{
// The volume is not mounted so we can't work with the filesystem.
Error ("ONLY_MOUNTED_VOL_SUPPORTED_FOR_NONSYS_INPLACE_ENC");
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
/* Gain "raw" access to the partition (the NTFS driver guards hidden sectors). */
if (!DeviceIoControl (dev,
FSCTL_ALLOW_EXTENDED_DASD_IO,
NULL,
0,
NULL,
0,
&dwResult,
NULL))
{
handleWin32Error (MainDlg);
ShowInPlaceEncErrMsgWAltSteps ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
/* Shrink the filesystem */
int64 totalClusterCount;
DWORD bytesPerCluster;
sizeToShrinkTo = NewFileSysSizeAfterShrink (dev, volParams->volumePath, &totalClusterCount, &bytesPerCluster, FALSE);
if (sizeToShrinkTo == -1)
{
ShowInPlaceEncErrMsgWAltSteps ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_RESIZING);
memset (&shrinkVolInfo, 0, sizeof (shrinkVolInfo));
shrinkVolInfo.ShrinkRequestType = ShrinkPrepare;
shrinkVolInfo.NewNumberOfSectors = sizeToShrinkTo;
if (!DeviceIoControl (dev,
FSCTL_SHRINK_VOLUME,
(LPVOID) &shrinkVolInfo,
sizeof (shrinkVolInfo),
NULL,
0,
&dwResult,
NULL))
{
handleWin32Error (MainDlg);
ShowInPlaceEncErrMsgWAltSteps ("CANNOT_RESIZE_FILESYS", TRUE);
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
BOOL clustersMovedBeforeVolumeEnd = FALSE;
while (true)
{
shrinkVolInfo.ShrinkRequestType = ShrinkCommit;
shrinkVolInfo.NewNumberOfSectors = 0;
if (!DeviceIoControl (dev, FSCTL_SHRINK_VOLUME, &shrinkVolInfo, sizeof (shrinkVolInfo), NULL, 0, &dwResult, NULL))
{
// If there are any occupied clusters beyond the new desired end of the volume, the call fails with
// ERROR_ACCESS_DENIED (STATUS_ALREADY_COMMITTED).
if (GetLastError () == ERROR_ACCESS_DENIED)
{
if (!clustersMovedBeforeVolumeEnd)
{
if (MoveClustersBeforeThreshold (dev, deviceName, totalClusterCount - (bytesPerCluster > TC_TOTAL_VOLUME_HEADERS_SIZE ? 1 : TC_TOTAL_VOLUME_HEADERS_SIZE / bytesPerCluster)))
{
clustersMovedBeforeVolumeEnd = TRUE;
continue;
}
handleWin32Error (MainDlg);
}
}
else
handleWin32Error (MainDlg);
ShowInPlaceEncErrMsgWAltSteps ("CANNOT_RESIZE_FILESYS", TRUE);
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
break;
}
SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PREPARING);
/* Gain exclusive access to the volume */
nStatus = DismountFileSystem (dev,
driveLetter,
TRUE,
TRUE,
FALSE);
if (nStatus != ERR_SUCCESS)
{
nStatus = ERR_DONT_REPORT;
goto closing_seq;
}
/* Create header backup on the partition. Until the volume is fully encrypted, the backup header will provide
us with the master key, encrypted range, and other data for pause/resume operations. We cannot create the
primary header until the entire partition is encrypted (because we encrypt backwards and the primary header
area is occuppied by data until the very end of the process). */
// Prepare the backup header
nStatus = CreateVolumeHeaderInMemory (FALSE,
header,
volParams->ea,
FIRST_MODE_OF_OPERATION_ID,
volParams->password,
volParams->pkcs5,
NULL,
&cryptoInfo,
dataAreaSize,
0,
TC_VOLUME_DATA_OFFSET + dataAreaSize, // Start of the encrypted area = the first byte of the backup heeader (encrypting from the end)
0, // No data is encrypted yet
0,
volParams->headerFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC,
FALSE);
if (nStatus != 0)
goto closing_seq;
offset.QuadPart = TC_VOLUME_DATA_OFFSET + dataAreaSize;
if (!SetFilePointerEx (dev, offset, NULL, FILE_BEGIN))
{
nStatus = ERR_OS_ERROR;
goto closing_seq;
}
// Write the backup header to the partition
if (_lwrite ((HFILE) dev, header, TC_VOLUME_HEADER_EFFECTIVE_SIZE) == HFILE_ERROR)
{
nStatus = ERR_OS_ERROR;
goto closing_seq;
}
// Fill the reserved sectors of the backup header area with random data
nStatus = WriteRandomDataToReservedHeaderAreas (dev, cryptoInfo, dataAreaSize, FALSE, TRUE);
if (nStatus != ERR_SUCCESS)
goto closing_seq;
/* Now we will try to decrypt the backup header to verify it has been correctly written. */
nStatus = OpenBackupHeader (dev, volParams->volumePath, volParams->password, &cryptoInfo2, NULL, deviceSize);
if (nStatus != ERR_SUCCESS
|| cryptoInfo->EncryptedAreaStart.Value != cryptoInfo2->EncryptedAreaStart.Value
|| cryptoInfo2->EncryptedAreaStart.Value == 0)
{
if (nStatus == ERR_SUCCESS)
nStatus = ERR_PARAMETER_INCORRECT;
goto closing_seq;
}
// The backup header is valid so we know we should be able to safely resume in-place encryption
// of this partition even if the system/app crashes.
/* Conceal the NTFS filesystem (by performing an easy-to-undo modification). This will prevent Windows
and apps from interfering with the volume until it has been fully encrypted. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -