From fc37cc4a02ed13d1a73b941a9f80975600fd1b99 Mon Sep 17 00:00:00 2001 From: David Foerster Date: Tue, 10 May 2016 20:20:14 +0200 Subject: Normalize all line terminators --- src/Format/InPlace.c | 4586 +++++++++++++++++++++++++------------------------- 1 file changed, 2293 insertions(+), 2293 deletions(-) (limited to 'src/Format/InPlace.c') diff --git a/src/Format/InPlace.c b/src/Format/InPlace.c index 720b9466..ff7fed1b 100644 --- a/src/Format/InPlace.c +++ b/src/Format/InPlace.c @@ -1,2293 +1,2293 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - - -/* In this file, _WIN32_WINNT is defined as 0x0600 to make filesystem 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 -#include -#include -#include - -#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" - -#include - -using namespace std; -using namespace VeraCrypt; - -#if TC_VOLUME_DATA_OFFSET != 131072 -# error TC_VOLUME_DATA_OFFSET != 131072 -#endif - -#if TC_VOLUME_HEADER_EFFECTIVE_SIZE != 512 -# error TC_VOLUME_HEADER_EFFECTIVE_SIZE != 512 -#endif - -#if TC_TOTAL_VOLUME_HEADERS_SIZE != 262144 -# error TC_TOTAL_VOLUME_HEADERS_SIZE != 262144 -#endif - -#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 wchar_t *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, SRC_POS); - - return -1; - } - - if ( (ntfsVolData.NumberSectors.QuadPart <= 0) - || (ntfsVolData.NumberSectors.QuadPart > (INT64_MAX / (__int64) ntfsVolData.BytesPerSector)) // overflow test - ) - { - SetLastError (ERROR_INTERNAL_ERROR); - if (!silent) - handleWin32Error (MainDlg, SRC_POS); - - 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 (HWND hwndDlg, const wchar_t *devicePath, BOOL silent) -{ - NTFS_VOLUME_DATA_BUFFER ntfsVolData; - DWORD nBytesReturned; - HANDLE dev; - WCHAR szFileSysName [256]; - WCHAR devPath [MAX_PATH]; - WCHAR dosDev [TC_MAX_PATH] = {0}; - WCHAR devName [MAX_PATH] = {0}; - int driveLetterNo = -1; - WCHAR szRootPath[4] = {0, L':', L'\\', 0}; - __int64 deviceSize; - int partitionNumber = -1, driveNumber = -1; - - - /* ---------- Checks that do not require admin rights ----------- */ - - - /* Operating system */ - - if (CurrentOSMajor < 6) - { - if (!silent) - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "OS_NOT_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE); - - return FALSE; - } - - - /* Volume type (must be a partition or a dynamic volume) */ - - if (swscanf (devicePath, L"\\Device\\HarddiskVolume%d", &partitionNumber) != 1 - && swscanf (devicePath, L"\\Device\\Harddisk%d\\Partition%d", &driveNumber, &partitionNumber) != 2) - { - if (!silent) - Error ("INPLACE_ENC_INVALID_PATH", hwndDlg); - - return FALSE; - } - - if (partitionNumber == 0) - { - if (!silent) - Warning ("RAW_DEV_NOT_SUPPORTED_FOR_INPLACE_ENC", hwndDlg); - - 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", hwndDlg); - } - - - /* ---------- Checks that may require admin rights ----------- */ - - - /* Access to the partition */ - - StringCbCopyW (devPath, sizeof(devPath), devicePath); - - driveLetterNo = GetDiskDeviceDriveLetter (devPath); - - if (driveLetterNo >= 0) - szRootPath[0] = (wchar_t) driveLetterNo + L'A'; - - if (FakeDosNameForDevice (devicePath, dosDev, sizeof(dosDev), devName, sizeof(devName),FALSE) != 0) - { - if (!silent) - { - handleWin32Error (hwndDlg, SRC_POS); - Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg); - } - return FALSE; - } - - dev = OpenPartitionVolume (hwndDlg, 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, ARRAYSIZE (szFileSysName)); - - if (wcsncmp (szFileSysName, L"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 (hwndDlg, "ONLY_NTFS_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE); - else - Warning ("ONLY_MOUNTED_VOL_SUPPORTED_FOR_NONSYS_INPLACE_ENC", hwndDlg); - } - - 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 (hwndDlg, "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", hwndDlg); - - CloseHandle (dev); - return FALSE; - } - - if (deviceSize < TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE) - { - // The partition is too small - if (!silent) - { - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, "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 (hwndDlg, "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 (hwndDlg, "SECTOR_SIZE_UNSUPPORTED", TRUE); - - CloseHandle (dev); - return FALSE; - } - - - CloseHandle (dev); - return TRUE; -} - -BOOL CheckRequirementsForNonSysInPlaceDec (HWND hwndDlg, const wchar_t *devicePath, BOOL silent) -{ - int partitionNumber = -1, driveNumber = -1; - - /* ---------- Checks that do not require admin rights ----------- */ - - /* Volume type (must be a partition or a dynamic volume) */ - if ((swscanf (devicePath, L"\\Device\\HarddiskVolume%d", &partitionNumber) != 1 - && swscanf (devicePath, L"\\Device\\Harddisk%d\\Partition%d", &driveNumber, &partitionNumber) != 2) - || partitionNumber == 0) - { - if (!silent) - Error ("INPLACE_ENC_INVALID_PATH", hwndDlg); - - 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", hwndDlg); - } - - - /* ---------- Checks that may require admin rights ----------- */ - - // [Currently none] - - 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; - WCHAR dosDev[TC_MAX_PATH] = {0}; - WCHAR devName[MAX_PATH] = {0}; - int driveLetter = -1; - WCHAR deviceName[MAX_PATH]; - uint64 dataAreaSize; - __int64 deviceSize; - LARGE_INTEGER offset; - DWORD dwResult; - HWND hwndDlg = volParams->hwndDlg; - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PREPARING); - - - if (!CheckRequirementsForNonSysInPlaceEnc (hwndDlg, 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 (hwndDlg, "PARTITION_TOO_SMALL_FOR_NONSYS_INPLACE_ENC", TRUE); - nStatus = ERR_DONT_REPORT; - goto closing_seq; - } - - dataAreaSize = GetVolumeDataAreaSize (volParams->hiddenVol, deviceSize); - - StringCchCopyW (deviceName, ARRAYSIZE(deviceName), volParams->volumePath); - - driveLetter = GetDiskDeviceDriveLetter (deviceName); - - - if (FakeDosNameForDevice (volParams->volumePath, dosDev, sizeof(dosDev),devName, sizeof(devName),FALSE) != 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (IsDeviceMounted (devName)) - { - dev = OpenPartitionVolume (hwndDlg, 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", hwndDlg); - 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, SRC_POS); - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, "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 (hwndDlg, SRC_POS); - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, SRC_POS); - } - } - else - handleWin32Error (hwndDlg, SRC_POS); - - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, 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 - for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++) - { - nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, - header, - volParams->ea, - FIRST_MODE_OF_OPERATION_ID, - volParams->password, - volParams->pkcs5, - volParams->pim, - wipePass == 0 ? NULL : (char *) cryptoInfo->master_keydata, - &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, - volParams->sectorSize, - wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1)); - - 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 (!WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header)) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - // Fill the reserved sectors of the backup header area with random data - nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, 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, volParams->pkcs5, volParams->pim, &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. */ - - nStatus = ConcealNTFS (dev); - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - - - // /* If a drive letter is assigned to the device, remove it (so that users do not try to open it, which - //would cause Windows to ask them if they want to format the volume and other dangerous things). */ - - //if (driveLetter >= 0) - //{ - // char rootPath[] = { driveLetter + 'A', ':', '\\', 0 }; - - // // Try to remove the assigned drive letter - // if (DeleteVolumeMountPoint (rootPath)) - // driveLetter = -1; - //} - - - - /* Update config files and app data */ - - // In the config file, increase the number of partitions where in-place encryption is in progress - - SaveNonSysInPlaceEncSettings (1, wipeAlgorithm, FALSE); - - - // Add the wizard to the system startup sequence if appropriate - - if (!IsNonInstallMode ()) - ManageStartupSeqWiz (FALSE, L"/prinplace"); - - - nStatus = ERR_SUCCESS; - - -closing_seq: - - dwError = GetLastError(); - - if (cryptoInfo != NULL) - { - crypto_close (cryptoInfo); - cryptoInfo = NULL; - } - - if (cryptoInfo2 != NULL) - { - crypto_close (cryptoInfo2); - cryptoInfo2 = NULL; - } - - burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - TCfree (header); - - if (dosDev[0]) - RemoveFakeDosName (volParams->volumePath, dosDev); - - *outHandle = dev; - - if (nStatus != ERR_SUCCESS) - SetLastError (dwError); - - return nStatus; -} - - -int EncryptPartitionInPlaceResume (HANDLE dev, - volatile FORMAT_VOL_PARAMETERS *volParams, - WipeAlgorithmId wipeAlgorithm, - volatile BOOL *bTryToCorrectReadErrors) -{ - PCRYPTO_INFO masterCryptoInfo = NULL, headerCryptoInfo = NULL, tmpCryptoInfo = NULL; - UINT64_STRUCT unitNo; - char *buf = NULL, *header = NULL; - byte *wipeBuffer = NULL; - byte wipeRandChars [TC_WIPE_RAND_CHAR_COUNT]; - byte wipeRandCharsUpdate [TC_WIPE_RAND_CHAR_COUNT]; - WCHAR dosDev[TC_MAX_PATH] = {0}; - WCHAR devName[MAX_PATH] = {0}; - WCHAR deviceName[MAX_PATH]; - int nStatus = ERR_SUCCESS; - __int64 deviceSize; - uint64 remainingBytes, lastHeaderUpdateDistance = 0, zeroedSectorCount = 0; - uint32 workChunkSize; - DWORD dwError, dwResult; - BOOL bPause = FALSE, bEncryptedAreaSizeChanged = FALSE; - LARGE_INTEGER offset; - int sectorSize; - int i; - DWORD n; - WCHAR *devicePath = volParams->volumePath; - Password *password = volParams->password; - int pkcs5_prf = volParams->pkcs5; - int pim = volParams->pim; - DISK_GEOMETRY driveGeometry; - HWND hwndDlg = volParams->hwndDlg; - - - bInPlaceEncNonSysResumed = TRUE; - - buf = (char *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); - if (!buf) - { - nStatus = ERR_OUTOFMEMORY; - goto closing_seq; - } - - header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE); - if (!header) - { - nStatus = ERR_OUTOFMEMORY; - goto closing_seq; - } - - VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - - if (wipeAlgorithm != TC_WIPE_NONE) - { - wipeBuffer = (byte *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); - if (!wipeBuffer) - { - nStatus = ERR_OUTOFMEMORY; - goto closing_seq; - } - } - - headerCryptoInfo = crypto_open(); - - if (headerCryptoInfo == NULL) - { - nStatus = ERR_OUTOFMEMORY; - goto closing_seq; - } - - deviceSize = GetDeviceSize (devicePath); - if (deviceSize < 0) - { - // Cannot determine the size of the partition - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (dev == INVALID_HANDLE_VALUE) - { - StringCchCopyW (deviceName, ARRAYSIZE(deviceName), devicePath); - - if (FakeDosNameForDevice (deviceName, dosDev, sizeof(dosDev),devName, sizeof(devName),FALSE) != 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - dev = OpenPartitionVolume (hwndDlg, devName, - FALSE, // Do not require exclusive access - FALSE, // Do not require shared access - TRUE, // 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; - } - } - - // This should never be needed, but is still performed for extra safety (without checking the result) - DeviceIoControl (dev, - FSCTL_ALLOW_EXTENDED_DASD_IO, - NULL, - 0, - NULL, - 0, - &dwResult, - NULL); - - - if (!DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveGeometry, sizeof (driveGeometry), &dwResult, NULL)) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - sectorSize = driveGeometry.BytesPerSector; - - - nStatus = OpenBackupHeader (dev, devicePath, password, pkcs5_prf, pim, &masterCryptoInfo, headerCryptoInfo, deviceSize); - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - - - remainingBytes = masterCryptoInfo->VolumeSize.Value - masterCryptoInfo->EncryptedAreaLength.Value; - - lastHeaderUpdateDistance = 0; - - - ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_ENCRYPTING); - - bFirstNonSysInPlaceEncResumeDone = TRUE; - - - /* The in-place encryption core */ - - while (remainingBytes > 0) - { - workChunkSize = (uint32) min (remainingBytes, TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); - - if (workChunkSize % ENCRYPTION_DATA_UNIT_SIZE != 0) - { - nStatus = ERR_PARAMETER_INCORRECT; - goto closing_seq; - } - - unitNo.Value = (remainingBytes - workChunkSize + TC_VOLUME_DATA_OFFSET) / ENCRYPTION_DATA_UNIT_SIZE; - - - // Read the plaintext into RAM - -inplace_enc_read: - - offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize - TC_VOLUME_DATA_OFFSET; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (ReadFile (dev, buf, workChunkSize, &n, NULL) == 0) - { - // Read error - - DWORD dwTmpErr = GetLastError (); - - if (IsDiskReadError (dwTmpErr) && !bVolTransformThreadCancel) - { - // Physical defect or data corruption - - if (!*bTryToCorrectReadErrors) - { - *bTryToCorrectReadErrors = (AskWarnYesNo ("ENABLE_BAD_SECTOR_ZEROING", hwndDlg) == IDYES); - } - - if (*bTryToCorrectReadErrors) - { - // Try to correct the read errors physically - - offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize - TC_VOLUME_DATA_OFFSET; - - nStatus = ZeroUnreadableSectors (dev, offset, workChunkSize, sectorSize, &zeroedSectorCount); - - if (nStatus != ERR_SUCCESS) - { - // Due to write errors, we can't correct the read errors - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - goto inplace_enc_read; - } - } - - SetLastError (dwTmpErr); // Preserve the original error code - - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (remainingBytes - workChunkSize < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE) - { - // We reached the inital portion of the filesystem, which we had concealed (in order to prevent - // Windows from interfering with the volume). Now we need to undo that modification. - - for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE - (remainingBytes - workChunkSize); i++) - buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; - } - - - // Encrypt the plaintext in RAM - - EncryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); - - - // If enabled, wipe the area to which we will write the ciphertext - - if (wipeAlgorithm != TC_WIPE_NONE) - { - byte wipePass; - int wipePassCount = GetWipePassCount (wipeAlgorithm); - - if (wipePassCount <= 0) - { - SetLastError (ERROR_INVALID_PARAMETER); - nStatus = ERR_PARAMETER_INCORRECT; - goto closing_seq; - } - - offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize; - - for (wipePass = 1; wipePass <= wipePassCount; ++wipePass) - { - if (!WipeBuffer (wipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, workChunkSize)) - { - ULONG i; - for (i = 0; i < workChunkSize; ++i) - { - wipeBuffer[i] = buf[i] + wipePass; - } - - EncryptDataUnits (wipeBuffer, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); - memcpy (wipeRandCharsUpdate, wipeBuffer, sizeof (wipeRandCharsUpdate)); - } - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 - || WriteFile (dev, wipeBuffer, workChunkSize, &n, NULL) == 0) - { - // Write error - dwError = GetLastError(); - - // Undo failed write operation - if (workChunkSize > TC_VOLUME_DATA_OFFSET && SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) - { - DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); - WriteFile (dev, buf + TC_VOLUME_DATA_OFFSET, workChunkSize - TC_VOLUME_DATA_OFFSET, &n, NULL); - } - - SetLastError (dwError); - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - } - - memcpy (wipeRandChars, wipeRandCharsUpdate, sizeof (wipeRandCharsUpdate)); - } - - - // Write the ciphertext - - offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (WriteFile (dev, buf, workChunkSize, &n, NULL) == 0) - { - // Write error - dwError = GetLastError(); - - // Undo failed write operation - if (workChunkSize > TC_VOLUME_DATA_OFFSET && SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) - { - DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); - WriteFile (dev, buf + TC_VOLUME_DATA_OFFSET, workChunkSize - TC_VOLUME_DATA_OFFSET, &n, NULL); - } - - SetLastError (dwError); - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - - masterCryptoInfo->EncryptedAreaStart.Value -= workChunkSize; - masterCryptoInfo->EncryptedAreaLength.Value += workChunkSize; - - remainingBytes -= workChunkSize; - lastHeaderUpdateDistance += workChunkSize; - - bEncryptedAreaSizeChanged = TRUE; - - if (lastHeaderUpdateDistance >= TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL) - { - nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - lastHeaderUpdateDistance = 0; - } - - ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); - - if (bVolTransformThreadCancel) - { - bPause = TRUE; - break; - } - } - - nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); - - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - - if (!bPause) - { - /* The data area has been fully encrypted; create and write the primary volume header */ - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINALIZING); - - for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++) - { - nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, - header, - headerCryptoInfo->ea, - headerCryptoInfo->mode, - password, - masterCryptoInfo->pkcs5, - pim, - (char *) masterCryptoInfo->master_keydata, - &tmpCryptoInfo, - masterCryptoInfo->VolumeSize.Value, - 0, - masterCryptoInfo->EncryptedAreaStart.Value, - masterCryptoInfo->EncryptedAreaLength.Value, - masterCryptoInfo->RequiredProgramVersion, - masterCryptoInfo->HeaderFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC, - masterCryptoInfo->SectorSize, - wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1)); - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - - offset.QuadPart = TC_VOLUME_HEADER_OFFSET; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 - || !WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header)) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - // Fill the reserved sectors of the header area with random data - nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, headerCryptoInfo, masterCryptoInfo->VolumeSize.Value, TRUE, FALSE); - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - } - - // Update the configuration files - - SaveNonSysInPlaceEncSettings (-1, wipeAlgorithm, FALSE); - - - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINISHED); - - nStatus = ERR_SUCCESS; - } - else - { - // The process has been paused by the user or aborted by the wizard (e.g. on app exit) - - nStatus = ERR_USER_ABORT; - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PAUSED); - } - - -closing_seq: - - dwError = GetLastError(); - - if (bEncryptedAreaSizeChanged - && dev != INVALID_HANDLE_VALUE - && masterCryptoInfo != NULL - && headerCryptoInfo != NULL - && deviceSize > 0) - { - // Execution of the core loop may have been interrupted due to an error or user action without updating the header - FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); - } - - if (masterCryptoInfo != NULL) - { - crypto_close (masterCryptoInfo); - masterCryptoInfo = NULL; - } - - if (headerCryptoInfo != NULL) - { - crypto_close (headerCryptoInfo); - headerCryptoInfo = NULL; - } - - if (tmpCryptoInfo != NULL) - { - crypto_close (tmpCryptoInfo); - tmpCryptoInfo = NULL; - } - - if (dosDev[0]) - RemoveFakeDosName (devicePath, dosDev); - - if (dev != INVALID_HANDLE_VALUE) - { - CloseHandle (dev); - dev = INVALID_HANDLE_VALUE; - } - - if (buf != NULL) - TCfree (buf); - - if (header != NULL) - { - burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - TCfree (header); - } - - if (wipeBuffer != NULL) - TCfree (wipeBuffer); - - if (zeroedSectorCount > 0) - { - wchar_t msg[30000] = {0}; - wchar_t sizeStr[500] = {0}; - - GetSizeString (zeroedSectorCount * sectorSize, sizeStr, sizeof(sizeStr)); - - StringCbPrintfW (msg, sizeof(msg), - GetString ("ZEROED_BAD_SECTOR_COUNT"), - zeroedSectorCount, - sizeStr); - - WarningDirect (msg, hwndDlg); - } - - if (nStatus != ERR_SUCCESS && nStatus != ERR_USER_ABORT) - SetLastError (dwError); - - return nStatus; -} - -int DecryptPartitionInPlace (volatile FORMAT_VOL_PARAMETERS *volParams, volatile BOOL *DiscardUnreadableEncryptedSectors) -{ - HANDLE dev = INVALID_HANDLE_VALUE; - PCRYPTO_INFO masterCryptoInfo = NULL, headerCryptoInfo = NULL; - UINT64_STRUCT unitNo; - char *buf = NULL; - byte *tmpSectorBuf = NULL; - WCHAR dosDev[TC_MAX_PATH] = {0}; - WCHAR devName[MAX_PATH] = {0}; - WCHAR deviceName[MAX_PATH]; - int nStatus = ERR_SUCCESS; - __int64 deviceSize; - uint64 remainingBytes, workChunkStartByteOffset, lastHeaderUpdateDistance = 0, skippedBadSectorCount = 0; - uint32 workChunkSize; - DWORD dwError, dwResult; - BOOL bPause = FALSE, bEncryptedAreaSizeChanged = FALSE; - LARGE_INTEGER offset; - int sectorSize; - int i; - DWORD n; - WCHAR *devicePath = volParams->volumePath; - Password *password = volParams->password; - HWND hwndDlg = volParams->hwndDlg; - int pkcs5_prf = volParams->pkcs5; - int pim = volParams->pim; - DISK_GEOMETRY driveGeometry; - - - buf = (char *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); - if (!buf) - { - nStatus = ERR_OUTOFMEMORY; - goto closing_seq; - } - - headerCryptoInfo = crypto_open(); - - if (headerCryptoInfo == NULL) - { - nStatus = ERR_OUTOFMEMORY; - goto closing_seq; - } - - deviceSize = GetDeviceSize (devicePath); - if (deviceSize < 0) - { - // Cannot determine the size of the partition - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - - // The wizard should have dismounted the TC volume if it was mounted, but for extra safety we will check this again. - if (IsMountedVolume (devicePath)) - { - int driveLetter = GetMountedVolumeDriveNo (devicePath); - - if (driveLetter == -1 - || !UnmountVolume (hwndDlg, driveLetter, TRUE)) - { - handleWin32Error (hwndDlg, SRC_POS); - AbortProcess ("CANT_DISMOUNT_VOLUME"); - } - } - - - StringCchCopyW (deviceName, ARRAYSIZE(deviceName), devicePath); - - if (FakeDosNameForDevice (deviceName, dosDev, sizeof(dosDev), devName, sizeof(devName), FALSE) != 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - dev = OpenPartitionVolume (hwndDlg, devName, - TRUE, // Require exclusive access - FALSE, // Do not require shared access - TRUE, // 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; - } - - - - // This should never be needed, but is still performed for extra safety (without checking the result) - DeviceIoControl (dev, - FSCTL_ALLOW_EXTENDED_DASD_IO, - NULL, - 0, - NULL, - 0, - &dwResult, - NULL); - - - if (!DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveGeometry, sizeof (driveGeometry), &dwResult, NULL)) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if ( (driveGeometry.BytesPerSector == 0) - || (driveGeometry.BytesPerSector > TC_MAX_VOLUME_SECTOR_SIZE) - || (driveGeometry.BytesPerSector % ENCRYPTION_DATA_UNIT_SIZE != 0) - ) - { - Error ("SECTOR_SIZE_UNSUPPORTED", hwndDlg); - nStatus = ERR_DONT_REPORT; - goto closing_seq; - } - - sectorSize = driveGeometry.BytesPerSector; - - - tmpSectorBuf = (byte *) TCalloc (sectorSize); - if (!tmpSectorBuf) - { - nStatus = ERR_OUTOFMEMORY; - goto closing_seq; - } - - - nStatus = OpenBackupHeader (dev, devicePath, password, pkcs5_prf, pim, &masterCryptoInfo, headerCryptoInfo, deviceSize); - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - - if (masterCryptoInfo->LegacyVolume) - { - Error ("NONSYS_INPLACE_DECRYPTION_BAD_VOL_FORMAT", hwndDlg); - nStatus = ERR_DONT_REPORT; - goto closing_seq; - } - - if (masterCryptoInfo->hiddenVolume) - { - Error ("NONSYS_INPLACE_DECRYPTION_CANT_DECRYPT_HID_VOL", hwndDlg); - nStatus = ERR_DONT_REPORT; - goto closing_seq; - } - - if (!bInPlaceEncNonSysResumed - && masterCryptoInfo->VolumeSize.Value == masterCryptoInfo->EncryptedAreaLength.Value) - { - /* Decryption started (not resumed) */ - - if ((masterCryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0) - { - // The volume has not been encrypted in-place so it may contain a hidden volume. - // Ask the user to confirm it does not. - - char *tmpStr[] = {0, - "CONFIRM_VOL_CONTAINS_NO_HIDDEN_VOL", - "VOL_CONTAINS_NO_HIDDEN_VOL", - "VOL_CONTAINS_A_HIDDEN_VOL", - 0}; - - switch (AskMultiChoice ((void **) tmpStr, FALSE, hwndDlg)) - { - case 1: - // NOP - break; - case 2: - default: - // Cancel - nStatus = ERR_DONT_REPORT; - goto closing_seq; - } - } - - // Update config files and app data - - // In the config file, increase the number of partitions where in-place decryption is in progress - SaveNonSysInPlaceEncSettings (1, TC_WIPE_NONE, TRUE); - - // Add the wizard to the system startup sequence if appropriate - if (!IsNonInstallMode ()) - ManageStartupSeqWiz (FALSE, L"/prinplace"); - } - - - - bInPlaceEncNonSysResumed = TRUE; - bFirstNonSysInPlaceEncResumeDone = TRUE; - - - remainingBytes = masterCryptoInfo->EncryptedAreaLength.Value; - - lastHeaderUpdateDistance = 0; - - - ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_DECRYPTING); - - - - /* The in-place decryption core */ - - while (remainingBytes > 0) - { - workChunkSize = (uint32) min (remainingBytes, TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); - - if (workChunkSize % ENCRYPTION_DATA_UNIT_SIZE != 0) - { - nStatus = ERR_PARAMETER_INCORRECT; - goto closing_seq; - } - - workChunkStartByteOffset = masterCryptoInfo->EncryptedAreaStart.Value; - - unitNo.Value = workChunkStartByteOffset / ENCRYPTION_DATA_UNIT_SIZE; - - - // Read the ciphertext into RAM - - offset.QuadPart = workChunkStartByteOffset; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (ReadFile (dev, buf, workChunkSize, &n, NULL) == 0) - { - // Read error - - DWORD dwTmpErr = GetLastError (); - - if (IsDiskReadError (dwTmpErr) && !bVolTransformThreadCancel) - { - // Physical defect or data corruption - - if (!*DiscardUnreadableEncryptedSectors) - { - *DiscardUnreadableEncryptedSectors = (AskWarnYesNo ("DISCARD_UNREADABLE_ENCRYPTED_SECTORS", hwndDlg) == IDYES); - } - - if (*DiscardUnreadableEncryptedSectors) - { - // Read the work chunk again, but this time each sector individually and skiping each bad sector - - LARGE_INTEGER tmpSectorOffset; - uint64 tmpSectorCount; - uint64 tmpBufOffset = 0; - DWORD tmpNbrReadBytes = 0; - - tmpSectorOffset.QuadPart = offset.QuadPart; - - for (tmpSectorCount = workChunkSize / sectorSize; tmpSectorCount > 0; --tmpSectorCount) - { - if (SetFilePointerEx (dev, tmpSectorOffset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (ReadFile (dev, tmpSectorBuf, sectorSize, &tmpNbrReadBytes, NULL) == 0 - || tmpNbrReadBytes != (DWORD) sectorSize) - { - // Read error - - // Clear the buffer so the content of each unreadable sector is replaced with decrypted all-zero blocks (producing pseudorandom data) - memset (tmpSectorBuf, 0, sectorSize); - - skippedBadSectorCount++; - } - - memcpy (buf + tmpBufOffset, tmpSectorBuf, sectorSize); - - tmpSectorOffset.QuadPart += sectorSize; - tmpBufOffset += sectorSize; - } - } - else - { - SetLastError (dwTmpErr); // Preserve the original error code - - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - } - else - { - SetLastError (dwTmpErr); // Preserve the original error code - - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - } - - // Decrypt the ciphertext in RAM - - DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); - - - - // Conceal initial portion of the filesystem - - if (workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE) - { - // We are decrypting the initial TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE bytes of the filesystem. We will - // conceal this portion to prevent Windows and applications from interfering with the volume. - - for (i = 0; i < min (TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET + workChunkSize); i++) - buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; - } - - - // Write the plaintext - - offset.QuadPart = workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (WriteFile (dev, buf, workChunkSize, &n, NULL) == 0) - { - // Write error - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - - masterCryptoInfo->EncryptedAreaStart.Value += workChunkSize; - masterCryptoInfo->EncryptedAreaLength.Value -= workChunkSize; - - remainingBytes -= workChunkSize; - lastHeaderUpdateDistance += workChunkSize; - - bEncryptedAreaSizeChanged = TRUE; - - if (lastHeaderUpdateDistance >= TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL) - { - nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); - - if (nStatus != ERR_SUCCESS) - { - // Possible write error - goto closing_seq; - } - - lastHeaderUpdateDistance = 0; - } - - ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); - - if (bVolTransformThreadCancel) - { - bPause = TRUE; - break; - } - } - - nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); - - - if (nStatus != ERR_SUCCESS) - { - // Possible write error - goto closing_seq; - } - - - if (!bPause) - { - /* Volume has been fully decrypted. */ - - - // Prevent attempts to update volume header during the closing sequence - bEncryptedAreaSizeChanged = FALSE; - - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINALIZING); - - - - /* Undo concealing of the filesystem */ - - nStatus = ConcealNTFS (dev); - - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - - - /* Ovewrite the backup header and the remaining ciphertext with all-zero blocks (the primary header was overwritten with the decrypted data). */ - - memset (tmpSectorBuf, 0, sectorSize); - - for (offset.QuadPart = masterCryptoInfo->VolumeSize.Value; - offset.QuadPart <= deviceSize - sectorSize; - offset.QuadPart += sectorSize) - { - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (WriteFile (dev, tmpSectorBuf, sectorSize, &n, NULL) == 0) - { - // Write error - dwError = GetLastError(); - - SetLastError (dwError); - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - } - - - - /* Update the configuration files */ - - SaveNonSysInPlaceEncSettings (-1, TC_WIPE_NONE, TRUE); - - - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINISHED); - - nStatus = ERR_SUCCESS; - - } - else - { - // The process has been paused by the user or aborted by the wizard (e.g. on app exit) - - nStatus = ERR_USER_ABORT; - - SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PAUSED); - } - - if (dev != INVALID_HANDLE_VALUE) - { - CloseHandle (dev); - dev = INVALID_HANDLE_VALUE; - } - - -closing_seq: - - dwError = GetLastError(); - - if (bEncryptedAreaSizeChanged - && dev != INVALID_HANDLE_VALUE - && masterCryptoInfo != NULL - && headerCryptoInfo != NULL - && deviceSize > 0) - { - // Execution of the core loop may have been interrupted due to an error or user action without updating the header - FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); - } - - if (dev != INVALID_HANDLE_VALUE) - { - CloseHandle (dev); - dev = INVALID_HANDLE_VALUE; - } - - if (masterCryptoInfo != NULL) - { - crypto_close (masterCryptoInfo); - masterCryptoInfo = NULL; - } - - if (headerCryptoInfo != NULL) - { - crypto_close (headerCryptoInfo); - headerCryptoInfo = NULL; - } - - if (dosDev[0]) - RemoveFakeDosName (devicePath, dosDev); - - if (buf != NULL) - { - TCfree (buf); - buf = NULL; - } - - if (tmpSectorBuf != NULL) - { - TCfree (tmpSectorBuf); - tmpSectorBuf = NULL; - } - - if (skippedBadSectorCount > 0) - { - wchar_t msg[30000] = {0}; - wchar_t sizeStr[500] = {0}; - - GetSizeString (skippedBadSectorCount * sectorSize, sizeStr, sizeof(sizeStr)); - - StringCbPrintfW (msg, sizeof(msg), - GetString ("SKIPPED_BAD_SECTOR_COUNT"), - skippedBadSectorCount, - sizeStr); - - WarningDirect (msg, hwndDlg); - } - - if (nStatus != ERR_SUCCESS && nStatus != ERR_USER_ABORT) - SetLastError (dwError); - - return nStatus; -} - -int FastVolumeHeaderUpdate (HANDLE dev, CRYPTO_INFO *headerCryptoInfo, CRYPTO_INFO *masterCryptoInfo, __int64 deviceSize) -{ - LARGE_INTEGER offset; - DWORD n; - int nStatus = ERR_SUCCESS; - byte *header; - DWORD dwError; - uint32 headerCrc32; - byte *fieldPos; - - header = (byte *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE); - - if (!header) - return ERR_OUTOFMEMORY; - - VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - - - fieldPos = (byte *) header + TC_HEADER_OFFSET_ENCRYPTED_AREA_START; - - offset.QuadPart = deviceSize - TC_VOLUME_HEADER_GROUP_SIZE; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 - || !ReadEffectiveVolumeHeader (TRUE, dev, header, &n) || n < TC_VOLUME_HEADER_EFFECTIVE_SIZE) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - - DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo); - - if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x56455241) - { - nStatus = ERR_PARAMETER_INCORRECT; - goto closing_seq; - } - - mputInt64 (fieldPos, (masterCryptoInfo->EncryptedAreaStart.Value)); - mputInt64 (fieldPos, (masterCryptoInfo->EncryptedAreaLength.Value)); - - // We need to ensure the TC_HEADER_FLAG_NONSYS_INPLACE_ENC flag bit is set, because if volumes created by TC-format - // were decrypted in place, it would be possible to mount them partially encrypted and it wouldn't be possible - // to resume interrupted decryption after the wizard exits. - masterCryptoInfo->HeaderFlags |= TC_HEADER_FLAG_NONSYS_INPLACE_ENC; - fieldPos = (byte *) header + TC_HEADER_OFFSET_FLAGS; - mputLong (fieldPos, (masterCryptoInfo->HeaderFlags)); - - - headerCrc32 = GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC); - fieldPos = (byte *) header + TC_HEADER_OFFSET_HEADER_CRC; - mputLong (fieldPos, headerCrc32); - - EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo); - - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 - || !WriteEffectiveVolumeHeader (TRUE, dev, header)) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - -closing_seq: - - dwError = GetLastError(); - - burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - TCfree (header); - - if (nStatus != ERR_SUCCESS) - SetLastError (dwError); - - return nStatus; -} - - -static HANDLE OpenPartitionVolume (HWND hwndDlg, const wchar_t *devName, - BOOL bExclusiveRequired, - BOOL bSharedRequired, - BOOL bSharedRequiresConfirmation, - BOOL bShowAlternativeSteps, - BOOL bSilent) -{ - HANDLE dev = INVALID_HANDLE_VALUE; - int retryCount = 0; - - if (bExclusiveRequired) - bSharedRequired = FALSE; - - if (bExclusiveRequired || !bSharedRequired) - { - // Exclusive access - // Note that when exclusive access is denied, it is worth retrying (usually succeeds after a few tries). - while (dev == INVALID_HANDLE_VALUE && retryCount++ < EXCL_ACCESS_MAX_AUTO_RETRIES) - { - dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); - - if (retryCount > 1) - Sleep (EXCL_ACCESS_AUTO_RETRY_DELAY); - } - } - - if (dev == INVALID_HANDLE_VALUE) - { - if (bExclusiveRequired) - { - if (!bSilent) - { - handleWin32Error (hwndDlg, SRC_POS); - - if (bShowAlternativeSteps) - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE); - else - Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg); - } - return INVALID_HANDLE_VALUE; - } - - // Shared mode - dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); - if (dev != INVALID_HANDLE_VALUE) - { - if (bSharedRequiresConfirmation - && !bSilent - && AskWarnNoYes ("DEVICE_IN_USE_INPLACE_ENC", hwndDlg) == IDNO) - { - CloseHandle (dev); - return INVALID_HANDLE_VALUE; - } - } - else - { - if (!bSilent) - { - handleWin32Error (MainDlg, SRC_POS); - - if (bShowAlternativeSteps) - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE); - else - Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg); - } - return INVALID_HANDLE_VALUE; - } - } - - return dev; -} - - -static int DismountFileSystem (HWND hwndDlg, HANDLE dev, - int driveLetter, - BOOL bForcedAllowed, - BOOL bForcedRequiresConfirmation, - BOOL bSilent) -{ - int attempt; - BOOL bResult; - DWORD dwResult; - - CloseVolumeExplorerWindows (MainDlg, driveLetter); - - attempt = UNMOUNT_MAX_AUTO_RETRIES * 10; - - while (!(bResult = DeviceIoControl (dev, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwResult, NULL)) - && attempt > 0) - { - Sleep (UNMOUNT_AUTO_RETRY_DELAY); - attempt--; - } - - if (!bResult) - { - if (!bForcedAllowed) - { - if (!bSilent) - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_LOCK_OR_DISMOUNT_FILESYS", TRUE); - - return ERR_DONT_REPORT; - } - - if (bForcedRequiresConfirmation - && !bSilent - && AskWarnYesNo ("VOL_LOCK_FAILED_OFFER_FORCED_DISMOUNT", hwndDlg) == IDNO) - { - return ERR_DONT_REPORT; - } - } - - // Dismount the volume - - attempt = UNMOUNT_MAX_AUTO_RETRIES * 10; - - while (!(bResult = DeviceIoControl (dev, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwResult, NULL)) - && attempt > 0) - { - Sleep (UNMOUNT_AUTO_RETRY_DELAY); - attempt--; - } - - if (!bResult) - { - if (!bSilent) - ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_LOCK_OR_DISMOUNT_FILESYS", TRUE); - - return ERR_DONT_REPORT; - } - - return ERR_SUCCESS; -} - - -// Easy-to-undo modification applied to conceal the NTFS filesystem (to prevent Windows and apps from -// interfering with it until the volume has been fully encrypted). Note that this function will precisely -// undo any modifications it made to the filesystem automatically if an error occurs when writing (including -// physical drive defects). -static int ConcealNTFS (HANDLE dev) -{ - char buf [TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE]; - DWORD nbrBytesProcessed, nbrBytesProcessed2; - int i; - LARGE_INTEGER offset; - DWORD dwError; - - offset.QuadPart = 0; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) - return ERR_OS_ERROR; - - if (ReadFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed, NULL) == 0) - return ERR_OS_ERROR; - - for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE; i++) - buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; - - offset.QuadPart = 0; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) - return ERR_OS_ERROR; - - if (WriteFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed, NULL) == 0) - { - // One or more of the sectors is/are probably damaged and cause write errors. - // We must undo the modifications we made. - - dwError = GetLastError(); - - for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE; i++) - buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; - - offset.QuadPart = 0; - - do - { - Sleep (1); - } - while (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 - || WriteFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed2, NULL) == 0); - - SetLastError (dwError); - - return ERR_OS_ERROR; - } - - return ERR_SUCCESS; -} - - -void ShowInPlaceEncErrMsgWAltSteps (HWND hwndDlg, char *iniStrId, BOOL bErr) -{ - wchar_t msg[30000]; - - StringCbCopyW (msg, sizeof(msg), GetString (iniStrId)); - - StringCbCatW (msg, sizeof(msg), L"\n\n\n"); - StringCbCatW (msg, sizeof(msg), GetString ("INPLACE_ENC_ALTERNATIVE_STEPS")); - - if (bErr) - ErrorDirect (msg, hwndDlg); - else - WarningDirect (msg, hwndDlg); -} - - -static void ExportProgressStats (__int64 bytesDone, __int64 totalSize) -{ - NonSysInplaceEncBytesDone = bytesDone; - NonSysInplaceEncTotalSize = totalSize; -} - - -void SetNonSysInplaceEncUIStatus (int nonSysInplaceEncStatus) -{ - NonSysInplaceEncStatus = nonSysInplaceEncStatus; -} - - -BOOL SaveNonSysInPlaceEncSettings (int delta, WipeAlgorithmId newWipeAlgorithm, BOOL bDecrypt) -{ - int count; - char str[32]; - WipeAlgorithmId savedWipeAlgorithm = TC_WIPE_NONE; - - if (delta == 0) - return TRUE; - - count = LoadNonSysInPlaceEncSettings (&savedWipeAlgorithm) + delta; - - if (count < 1) - { - RemoveNonSysInPlaceEncNotifications(); - return TRUE; - } - else if (!bDecrypt) - { - if (newWipeAlgorithm != TC_WIPE_NONE) - { - StringCbPrintfA (str, sizeof(str), "%d", (int) newWipeAlgorithm); - - SaveBufferToFile (str, GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE), (DWORD) strlen(str), FALSE, FALSE); - } - else if (FileExists (GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE))) - { - _wremove (GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE)); - } - } - - StringCbPrintfA (str, sizeof(str), "%d", count); - - return SaveBufferToFile (str, GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC), (DWORD) strlen(str), FALSE, FALSE); -} - - -// Repairs damaged sectors (i.e. those with read errors) by zeroing them. -// Note that this operating fails if there are any write errors. -int ZeroUnreadableSectors (HANDLE dev, LARGE_INTEGER startOffset, int64 size, int sectorSize, uint64 *zeroedSectorCount) -{ - int nStatus; - DWORD n; - int64 sectorCount; - LARGE_INTEGER workOffset; - byte *sectorBuffer = NULL; - DWORD dwError; - - workOffset.QuadPart = startOffset.QuadPart; - - sectorBuffer = (byte *) TCalloc (sectorSize); - - if (!sectorBuffer) - return ERR_OUTOFMEMORY; - - if (SetFilePointerEx (dev, startOffset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - - for (sectorCount = size / sectorSize; sectorCount > 0; --sectorCount) - { - if (ReadFile (dev, sectorBuffer, sectorSize, &n, NULL) == 0) - { - memset (sectorBuffer, 0, sectorSize); - - if (SetFilePointerEx (dev, workOffset, NULL, FILE_BEGIN) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - if (WriteFile (dev, sectorBuffer, sectorSize, &n, NULL) == 0) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - ++(*zeroedSectorCount); - } - - workOffset.QuadPart += n; - } - - nStatus = ERR_SUCCESS; - -closing_seq: - - dwError = GetLastError(); - - if (sectorBuffer != NULL) - TCfree (sectorBuffer); - - if (nStatus != ERR_SUCCESS) - SetLastError (dwError); - - return nStatus; -} - - -static int OpenBackupHeader (HANDLE dev, const wchar_t *devicePath, Password *password, int pkcs5, int pim, PCRYPTO_INFO *retMasterCryptoInfo, CRYPTO_INFO *headerCryptoInfo, __int64 deviceSize) -{ - LARGE_INTEGER offset; - DWORD n; - int nStatus = ERR_SUCCESS; - char *header; - DWORD dwError; - - header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE); - if (!header) - return ERR_OUTOFMEMORY; - - VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - - - - offset.QuadPart = deviceSize - TC_VOLUME_HEADER_GROUP_SIZE; - - if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 - || !ReadEffectiveVolumeHeader (TRUE, dev, (byte *) header, &n) || n < TC_VOLUME_HEADER_EFFECTIVE_SIZE) - { - nStatus = ERR_OS_ERROR; - goto closing_seq; - } - - - nStatus = ReadVolumeHeader (FALSE, header, password, pkcs5, pim, FALSE, retMasterCryptoInfo, headerCryptoInfo); - if (nStatus != ERR_SUCCESS) - goto closing_seq; - - -closing_seq: - - dwError = GetLastError(); - - burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - TCfree (header); - - dwError = GetLastError(); - - if (nStatus != ERR_SUCCESS) - SetLastError (dwError); - - return nStatus; -} - - -static BOOL GetFreeClusterBeforeThreshold (HANDLE volumeHandle, int64 *freeCluster, int64 clusterThreshold) -{ - const int bitmapSize = 65536; - byte bitmapBuffer[bitmapSize + sizeof (VOLUME_BITMAP_BUFFER)]; - VOLUME_BITMAP_BUFFER *bitmap = (VOLUME_BITMAP_BUFFER *) bitmapBuffer; - STARTING_LCN_INPUT_BUFFER startLcn; - startLcn.StartingLcn.QuadPart = 0; - - DWORD bytesReturned; - while (DeviceIoControl (volumeHandle, FSCTL_GET_VOLUME_BITMAP, &startLcn, sizeof (startLcn), &bitmapBuffer, sizeof (bitmapBuffer), &bytesReturned, NULL) - || GetLastError() == ERROR_MORE_DATA) - { - for (int64 bitmapIndex = 0; bitmapIndex < min (bitmapSize, (bitmap->BitmapSize.QuadPart / 8)); ++bitmapIndex) - { - if (bitmap->StartingLcn.QuadPart + bitmapIndex * 8 >= clusterThreshold) - goto err; - - if (bitmap->Buffer[bitmapIndex] != 0xff) - { - for (int bit = 0; bit < 8; ++bit) - { - if ((bitmap->Buffer[bitmapIndex] & (1 << bit)) == 0) - { - *freeCluster = bitmap->StartingLcn.QuadPart + bitmapIndex * 8 + bit; - - if (*freeCluster >= clusterThreshold) - goto err; - - return TRUE; - } - } - } - } - - startLcn.StartingLcn.QuadPart += min (bitmapSize * 8, bitmap->BitmapSize.QuadPart); - } - -err: - SetLastError (ERROR_DISK_FULL); - return FALSE; -} - - -static BOOL MoveClustersBeforeThresholdInDir (HANDLE volumeHandle, const wstring &directory, int64 clusterThreshold) -{ - WIN32_FIND_DATAW findData; - - HANDLE findHandle = FindFirstFileW (((directory.size() <= 3 ? L"" : L"\\\\?\\") + directory + L"\\*").c_str(), &findData); - if (findHandle == INVALID_HANDLE_VALUE) - return TRUE; // Error ignored - - finally_do_arg (HANDLE, findHandle, { FindClose (finally_arg); }); - - // Find all files and directories - do - { - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - wstring subDir = findData.cFileName; - - if (subDir == L"." || subDir == L"..") - continue; - - if (!MoveClustersBeforeThresholdInDir (volumeHandle, directory + L"\\" + subDir, clusterThreshold)) - return FALSE; - } - - DWORD access = FILE_READ_ATTRIBUTES; - - if (findData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) - access = FILE_READ_DATA; - - HANDLE fsObject = CreateFileW ((directory + L"\\" + findData.cFileName).c_str(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (fsObject == INVALID_HANDLE_VALUE) - continue; - - finally_do_arg (HANDLE, fsObject, { CloseHandle (finally_arg); }); - - STARTING_VCN_INPUT_BUFFER startVcn; - startVcn.StartingVcn.QuadPart = 0; - RETRIEVAL_POINTERS_BUFFER retPointers; - DWORD bytesReturned; - - // Find clusters allocated beyond the threshold - while (DeviceIoControl (fsObject, FSCTL_GET_RETRIEVAL_POINTERS, &startVcn, sizeof (startVcn), &retPointers, sizeof (retPointers), &bytesReturned, NULL) - || GetLastError() == ERROR_MORE_DATA) - { - if (retPointers.ExtentCount == 0) - break; - - if (retPointers.Extents[0].Lcn.QuadPart != -1) - { - int64 extentStartCluster = retPointers.Extents[0].Lcn.QuadPart; - int64 extentLen = retPointers.Extents[0].NextVcn.QuadPart - retPointers.StartingVcn.QuadPart; - int64 extentEndCluster = extentStartCluster + extentLen - 1; - - if (extentEndCluster >= clusterThreshold) - { - // Move clusters before the threshold - for (int64 movedCluster = max (extentStartCluster, clusterThreshold); movedCluster <= extentEndCluster; ++movedCluster) - { - for (int retry = 0; ; ++retry) - { - MOVE_FILE_DATA moveData; - - if (GetFreeClusterBeforeThreshold (volumeHandle, &moveData.StartingLcn.QuadPart, clusterThreshold)) - { - moveData.FileHandle = fsObject; - moveData.StartingVcn.QuadPart = movedCluster - extentStartCluster + retPointers.StartingVcn.QuadPart; - moveData.ClusterCount = 1; - - if (DeviceIoControl (volumeHandle, FSCTL_MOVE_FILE, &moveData, sizeof (moveData), NULL, 0, &bytesReturned, NULL)) - break; - } - - if (retry > 600) - return FALSE; - - // There are possible race conditions as we work on a live filesystem - Sleep (100); - } - } - } - } - - startVcn.StartingVcn = retPointers.Extents[0].NextVcn; - } - - } while (FindNextFileW (findHandle, &findData)); - - return TRUE; -} - - -BOOL MoveClustersBeforeThreshold (HANDLE volumeHandle, PWSTR volumeDevicePath, int64 clusterThreshold) -{ - int drive = GetDiskDeviceDriveLetter (volumeDevicePath); - if (drive == -1) - { - SetLastError (ERROR_INVALID_PARAMETER); - return FALSE; - } - - wstring volumeRoot = L"X:"; - volumeRoot[0] = L'A' + (wchar_t) drive; - - return MoveClustersBeforeThresholdInDir (volumeHandle, volumeRoot, clusterThreshold); -} +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + + +/* In this file, _WIN32_WINNT is defined as 0x0600 to make filesystem 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 +#include +#include +#include + +#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" + +#include + +using namespace std; +using namespace VeraCrypt; + +#if TC_VOLUME_DATA_OFFSET != 131072 +# error TC_VOLUME_DATA_OFFSET != 131072 +#endif + +#if TC_VOLUME_HEADER_EFFECTIVE_SIZE != 512 +# error TC_VOLUME_HEADER_EFFECTIVE_SIZE != 512 +#endif + +#if TC_TOTAL_VOLUME_HEADERS_SIZE != 262144 +# error TC_TOTAL_VOLUME_HEADERS_SIZE != 262144 +#endif + +#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 wchar_t *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, SRC_POS); + + return -1; + } + + if ( (ntfsVolData.NumberSectors.QuadPart <= 0) + || (ntfsVolData.NumberSectors.QuadPart > (INT64_MAX / (__int64) ntfsVolData.BytesPerSector)) // overflow test + ) + { + SetLastError (ERROR_INTERNAL_ERROR); + if (!silent) + handleWin32Error (MainDlg, SRC_POS); + + 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 (HWND hwndDlg, const wchar_t *devicePath, BOOL silent) +{ + NTFS_VOLUME_DATA_BUFFER ntfsVolData; + DWORD nBytesReturned; + HANDLE dev; + WCHAR szFileSysName [256]; + WCHAR devPath [MAX_PATH]; + WCHAR dosDev [TC_MAX_PATH] = {0}; + WCHAR devName [MAX_PATH] = {0}; + int driveLetterNo = -1; + WCHAR szRootPath[4] = {0, L':', L'\\', 0}; + __int64 deviceSize; + int partitionNumber = -1, driveNumber = -1; + + + /* ---------- Checks that do not require admin rights ----------- */ + + + /* Operating system */ + + if (CurrentOSMajor < 6) + { + if (!silent) + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "OS_NOT_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE); + + return FALSE; + } + + + /* Volume type (must be a partition or a dynamic volume) */ + + if (swscanf (devicePath, L"\\Device\\HarddiskVolume%d", &partitionNumber) != 1 + && swscanf (devicePath, L"\\Device\\Harddisk%d\\Partition%d", &driveNumber, &partitionNumber) != 2) + { + if (!silent) + Error ("INPLACE_ENC_INVALID_PATH", hwndDlg); + + return FALSE; + } + + if (partitionNumber == 0) + { + if (!silent) + Warning ("RAW_DEV_NOT_SUPPORTED_FOR_INPLACE_ENC", hwndDlg); + + 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", hwndDlg); + } + + + /* ---------- Checks that may require admin rights ----------- */ + + + /* Access to the partition */ + + StringCbCopyW (devPath, sizeof(devPath), devicePath); + + driveLetterNo = GetDiskDeviceDriveLetter (devPath); + + if (driveLetterNo >= 0) + szRootPath[0] = (wchar_t) driveLetterNo + L'A'; + + if (FakeDosNameForDevice (devicePath, dosDev, sizeof(dosDev), devName, sizeof(devName),FALSE) != 0) + { + if (!silent) + { + handleWin32Error (hwndDlg, SRC_POS); + Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg); + } + return FALSE; + } + + dev = OpenPartitionVolume (hwndDlg, 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, ARRAYSIZE (szFileSysName)); + + if (wcsncmp (szFileSysName, L"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 (hwndDlg, "ONLY_NTFS_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE); + else + Warning ("ONLY_MOUNTED_VOL_SUPPORTED_FOR_NONSYS_INPLACE_ENC", hwndDlg); + } + + 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 (hwndDlg, "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", hwndDlg); + + CloseHandle (dev); + return FALSE; + } + + if (deviceSize < TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE) + { + // The partition is too small + if (!silent) + { + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, "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 (hwndDlg, "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 (hwndDlg, "SECTOR_SIZE_UNSUPPORTED", TRUE); + + CloseHandle (dev); + return FALSE; + } + + + CloseHandle (dev); + return TRUE; +} + +BOOL CheckRequirementsForNonSysInPlaceDec (HWND hwndDlg, const wchar_t *devicePath, BOOL silent) +{ + int partitionNumber = -1, driveNumber = -1; + + /* ---------- Checks that do not require admin rights ----------- */ + + /* Volume type (must be a partition or a dynamic volume) */ + if ((swscanf (devicePath, L"\\Device\\HarddiskVolume%d", &partitionNumber) != 1 + && swscanf (devicePath, L"\\Device\\Harddisk%d\\Partition%d", &driveNumber, &partitionNumber) != 2) + || partitionNumber == 0) + { + if (!silent) + Error ("INPLACE_ENC_INVALID_PATH", hwndDlg); + + 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", hwndDlg); + } + + + /* ---------- Checks that may require admin rights ----------- */ + + // [Currently none] + + 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; + WCHAR dosDev[TC_MAX_PATH] = {0}; + WCHAR devName[MAX_PATH] = {0}; + int driveLetter = -1; + WCHAR deviceName[MAX_PATH]; + uint64 dataAreaSize; + __int64 deviceSize; + LARGE_INTEGER offset; + DWORD dwResult; + HWND hwndDlg = volParams->hwndDlg; + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PREPARING); + + + if (!CheckRequirementsForNonSysInPlaceEnc (hwndDlg, 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 (hwndDlg, "PARTITION_TOO_SMALL_FOR_NONSYS_INPLACE_ENC", TRUE); + nStatus = ERR_DONT_REPORT; + goto closing_seq; + } + + dataAreaSize = GetVolumeDataAreaSize (volParams->hiddenVol, deviceSize); + + StringCchCopyW (deviceName, ARRAYSIZE(deviceName), volParams->volumePath); + + driveLetter = GetDiskDeviceDriveLetter (deviceName); + + + if (FakeDosNameForDevice (volParams->volumePath, dosDev, sizeof(dosDev),devName, sizeof(devName),FALSE) != 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (IsDeviceMounted (devName)) + { + dev = OpenPartitionVolume (hwndDlg, 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", hwndDlg); + 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, SRC_POS); + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, "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 (hwndDlg, SRC_POS); + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, SRC_POS); + } + } + else + handleWin32Error (hwndDlg, SRC_POS); + + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "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 (hwndDlg, 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 + for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++) + { + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + header, + volParams->ea, + FIRST_MODE_OF_OPERATION_ID, + volParams->password, + volParams->pkcs5, + volParams->pim, + wipePass == 0 ? NULL : (char *) cryptoInfo->master_keydata, + &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, + volParams->sectorSize, + wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1)); + + 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 (!WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + // Fill the reserved sectors of the backup header area with random data + nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, 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, volParams->pkcs5, volParams->pim, &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. */ + + nStatus = ConcealNTFS (dev); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + + + // /* If a drive letter is assigned to the device, remove it (so that users do not try to open it, which + //would cause Windows to ask them if they want to format the volume and other dangerous things). */ + + //if (driveLetter >= 0) + //{ + // char rootPath[] = { driveLetter + 'A', ':', '\\', 0 }; + + // // Try to remove the assigned drive letter + // if (DeleteVolumeMountPoint (rootPath)) + // driveLetter = -1; + //} + + + + /* Update config files and app data */ + + // In the config file, increase the number of partitions where in-place encryption is in progress + + SaveNonSysInPlaceEncSettings (1, wipeAlgorithm, FALSE); + + + // Add the wizard to the system startup sequence if appropriate + + if (!IsNonInstallMode ()) + ManageStartupSeqWiz (FALSE, L"/prinplace"); + + + nStatus = ERR_SUCCESS; + + +closing_seq: + + dwError = GetLastError(); + + if (cryptoInfo != NULL) + { + crypto_close (cryptoInfo); + cryptoInfo = NULL; + } + + if (cryptoInfo2 != NULL) + { + crypto_close (cryptoInfo2); + cryptoInfo2 = NULL; + } + + burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + TCfree (header); + + if (dosDev[0]) + RemoveFakeDosName (volParams->volumePath, dosDev); + + *outHandle = dev; + + if (nStatus != ERR_SUCCESS) + SetLastError (dwError); + + return nStatus; +} + + +int EncryptPartitionInPlaceResume (HANDLE dev, + volatile FORMAT_VOL_PARAMETERS *volParams, + WipeAlgorithmId wipeAlgorithm, + volatile BOOL *bTryToCorrectReadErrors) +{ + PCRYPTO_INFO masterCryptoInfo = NULL, headerCryptoInfo = NULL, tmpCryptoInfo = NULL; + UINT64_STRUCT unitNo; + char *buf = NULL, *header = NULL; + byte *wipeBuffer = NULL; + byte wipeRandChars [TC_WIPE_RAND_CHAR_COUNT]; + byte wipeRandCharsUpdate [TC_WIPE_RAND_CHAR_COUNT]; + WCHAR dosDev[TC_MAX_PATH] = {0}; + WCHAR devName[MAX_PATH] = {0}; + WCHAR deviceName[MAX_PATH]; + int nStatus = ERR_SUCCESS; + __int64 deviceSize; + uint64 remainingBytes, lastHeaderUpdateDistance = 0, zeroedSectorCount = 0; + uint32 workChunkSize; + DWORD dwError, dwResult; + BOOL bPause = FALSE, bEncryptedAreaSizeChanged = FALSE; + LARGE_INTEGER offset; + int sectorSize; + int i; + DWORD n; + WCHAR *devicePath = volParams->volumePath; + Password *password = volParams->password; + int pkcs5_prf = volParams->pkcs5; + int pim = volParams->pim; + DISK_GEOMETRY driveGeometry; + HWND hwndDlg = volParams->hwndDlg; + + + bInPlaceEncNonSysResumed = TRUE; + + buf = (char *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); + if (!buf) + { + nStatus = ERR_OUTOFMEMORY; + goto closing_seq; + } + + header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE); + if (!header) + { + nStatus = ERR_OUTOFMEMORY; + goto closing_seq; + } + + VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + + if (wipeAlgorithm != TC_WIPE_NONE) + { + wipeBuffer = (byte *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); + if (!wipeBuffer) + { + nStatus = ERR_OUTOFMEMORY; + goto closing_seq; + } + } + + headerCryptoInfo = crypto_open(); + + if (headerCryptoInfo == NULL) + { + nStatus = ERR_OUTOFMEMORY; + goto closing_seq; + } + + deviceSize = GetDeviceSize (devicePath); + if (deviceSize < 0) + { + // Cannot determine the size of the partition + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (dev == INVALID_HANDLE_VALUE) + { + StringCchCopyW (deviceName, ARRAYSIZE(deviceName), devicePath); + + if (FakeDosNameForDevice (deviceName, dosDev, sizeof(dosDev),devName, sizeof(devName),FALSE) != 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + dev = OpenPartitionVolume (hwndDlg, devName, + FALSE, // Do not require exclusive access + FALSE, // Do not require shared access + TRUE, // 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; + } + } + + // This should never be needed, but is still performed for extra safety (without checking the result) + DeviceIoControl (dev, + FSCTL_ALLOW_EXTENDED_DASD_IO, + NULL, + 0, + NULL, + 0, + &dwResult, + NULL); + + + if (!DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveGeometry, sizeof (driveGeometry), &dwResult, NULL)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + sectorSize = driveGeometry.BytesPerSector; + + + nStatus = OpenBackupHeader (dev, devicePath, password, pkcs5_prf, pim, &masterCryptoInfo, headerCryptoInfo, deviceSize); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + + + remainingBytes = masterCryptoInfo->VolumeSize.Value - masterCryptoInfo->EncryptedAreaLength.Value; + + lastHeaderUpdateDistance = 0; + + + ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_ENCRYPTING); + + bFirstNonSysInPlaceEncResumeDone = TRUE; + + + /* The in-place encryption core */ + + while (remainingBytes > 0) + { + workChunkSize = (uint32) min (remainingBytes, TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); + + if (workChunkSize % ENCRYPTION_DATA_UNIT_SIZE != 0) + { + nStatus = ERR_PARAMETER_INCORRECT; + goto closing_seq; + } + + unitNo.Value = (remainingBytes - workChunkSize + TC_VOLUME_DATA_OFFSET) / ENCRYPTION_DATA_UNIT_SIZE; + + + // Read the plaintext into RAM + +inplace_enc_read: + + offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize - TC_VOLUME_DATA_OFFSET; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (ReadFile (dev, buf, workChunkSize, &n, NULL) == 0) + { + // Read error + + DWORD dwTmpErr = GetLastError (); + + if (IsDiskReadError (dwTmpErr) && !bVolTransformThreadCancel) + { + // Physical defect or data corruption + + if (!*bTryToCorrectReadErrors) + { + *bTryToCorrectReadErrors = (AskWarnYesNo ("ENABLE_BAD_SECTOR_ZEROING", hwndDlg) == IDYES); + } + + if (*bTryToCorrectReadErrors) + { + // Try to correct the read errors physically + + offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize - TC_VOLUME_DATA_OFFSET; + + nStatus = ZeroUnreadableSectors (dev, offset, workChunkSize, sectorSize, &zeroedSectorCount); + + if (nStatus != ERR_SUCCESS) + { + // Due to write errors, we can't correct the read errors + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + goto inplace_enc_read; + } + } + + SetLastError (dwTmpErr); // Preserve the original error code + + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (remainingBytes - workChunkSize < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE) + { + // We reached the inital portion of the filesystem, which we had concealed (in order to prevent + // Windows from interfering with the volume). Now we need to undo that modification. + + for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE - (remainingBytes - workChunkSize); i++) + buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; + } + + + // Encrypt the plaintext in RAM + + EncryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); + + + // If enabled, wipe the area to which we will write the ciphertext + + if (wipeAlgorithm != TC_WIPE_NONE) + { + byte wipePass; + int wipePassCount = GetWipePassCount (wipeAlgorithm); + + if (wipePassCount <= 0) + { + SetLastError (ERROR_INVALID_PARAMETER); + nStatus = ERR_PARAMETER_INCORRECT; + goto closing_seq; + } + + offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize; + + for (wipePass = 1; wipePass <= wipePassCount; ++wipePass) + { + if (!WipeBuffer (wipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, workChunkSize)) + { + ULONG i; + for (i = 0; i < workChunkSize; ++i) + { + wipeBuffer[i] = buf[i] + wipePass; + } + + EncryptDataUnits (wipeBuffer, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); + memcpy (wipeRandCharsUpdate, wipeBuffer, sizeof (wipeRandCharsUpdate)); + } + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 + || WriteFile (dev, wipeBuffer, workChunkSize, &n, NULL) == 0) + { + // Write error + dwError = GetLastError(); + + // Undo failed write operation + if (workChunkSize > TC_VOLUME_DATA_OFFSET && SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) + { + DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); + WriteFile (dev, buf + TC_VOLUME_DATA_OFFSET, workChunkSize - TC_VOLUME_DATA_OFFSET, &n, NULL); + } + + SetLastError (dwError); + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + } + + memcpy (wipeRandChars, wipeRandCharsUpdate, sizeof (wipeRandCharsUpdate)); + } + + + // Write the ciphertext + + offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (WriteFile (dev, buf, workChunkSize, &n, NULL) == 0) + { + // Write error + dwError = GetLastError(); + + // Undo failed write operation + if (workChunkSize > TC_VOLUME_DATA_OFFSET && SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) + { + DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); + WriteFile (dev, buf + TC_VOLUME_DATA_OFFSET, workChunkSize - TC_VOLUME_DATA_OFFSET, &n, NULL); + } + + SetLastError (dwError); + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + + masterCryptoInfo->EncryptedAreaStart.Value -= workChunkSize; + masterCryptoInfo->EncryptedAreaLength.Value += workChunkSize; + + remainingBytes -= workChunkSize; + lastHeaderUpdateDistance += workChunkSize; + + bEncryptedAreaSizeChanged = TRUE; + + if (lastHeaderUpdateDistance >= TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL) + { + nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + lastHeaderUpdateDistance = 0; + } + + ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); + + if (bVolTransformThreadCancel) + { + bPause = TRUE; + break; + } + } + + nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); + + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + + if (!bPause) + { + /* The data area has been fully encrypted; create and write the primary volume header */ + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINALIZING); + + for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++) + { + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + header, + headerCryptoInfo->ea, + headerCryptoInfo->mode, + password, + masterCryptoInfo->pkcs5, + pim, + (char *) masterCryptoInfo->master_keydata, + &tmpCryptoInfo, + masterCryptoInfo->VolumeSize.Value, + 0, + masterCryptoInfo->EncryptedAreaStart.Value, + masterCryptoInfo->EncryptedAreaLength.Value, + masterCryptoInfo->RequiredProgramVersion, + masterCryptoInfo->HeaderFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC, + masterCryptoInfo->SectorSize, + wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1)); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + + offset.QuadPart = TC_VOLUME_HEADER_OFFSET; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 + || !WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + // Fill the reserved sectors of the header area with random data + nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, headerCryptoInfo, masterCryptoInfo->VolumeSize.Value, TRUE, FALSE); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + } + + // Update the configuration files + + SaveNonSysInPlaceEncSettings (-1, wipeAlgorithm, FALSE); + + + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINISHED); + + nStatus = ERR_SUCCESS; + } + else + { + // The process has been paused by the user or aborted by the wizard (e.g. on app exit) + + nStatus = ERR_USER_ABORT; + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PAUSED); + } + + +closing_seq: + + dwError = GetLastError(); + + if (bEncryptedAreaSizeChanged + && dev != INVALID_HANDLE_VALUE + && masterCryptoInfo != NULL + && headerCryptoInfo != NULL + && deviceSize > 0) + { + // Execution of the core loop may have been interrupted due to an error or user action without updating the header + FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); + } + + if (masterCryptoInfo != NULL) + { + crypto_close (masterCryptoInfo); + masterCryptoInfo = NULL; + } + + if (headerCryptoInfo != NULL) + { + crypto_close (headerCryptoInfo); + headerCryptoInfo = NULL; + } + + if (tmpCryptoInfo != NULL) + { + crypto_close (tmpCryptoInfo); + tmpCryptoInfo = NULL; + } + + if (dosDev[0]) + RemoveFakeDosName (devicePath, dosDev); + + if (dev != INVALID_HANDLE_VALUE) + { + CloseHandle (dev); + dev = INVALID_HANDLE_VALUE; + } + + if (buf != NULL) + TCfree (buf); + + if (header != NULL) + { + burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + TCfree (header); + } + + if (wipeBuffer != NULL) + TCfree (wipeBuffer); + + if (zeroedSectorCount > 0) + { + wchar_t msg[30000] = {0}; + wchar_t sizeStr[500] = {0}; + + GetSizeString (zeroedSectorCount * sectorSize, sizeStr, sizeof(sizeStr)); + + StringCbPrintfW (msg, sizeof(msg), + GetString ("ZEROED_BAD_SECTOR_COUNT"), + zeroedSectorCount, + sizeStr); + + WarningDirect (msg, hwndDlg); + } + + if (nStatus != ERR_SUCCESS && nStatus != ERR_USER_ABORT) + SetLastError (dwError); + + return nStatus; +} + +int DecryptPartitionInPlace (volatile FORMAT_VOL_PARAMETERS *volParams, volatile BOOL *DiscardUnreadableEncryptedSectors) +{ + HANDLE dev = INVALID_HANDLE_VALUE; + PCRYPTO_INFO masterCryptoInfo = NULL, headerCryptoInfo = NULL; + UINT64_STRUCT unitNo; + char *buf = NULL; + byte *tmpSectorBuf = NULL; + WCHAR dosDev[TC_MAX_PATH] = {0}; + WCHAR devName[MAX_PATH] = {0}; + WCHAR deviceName[MAX_PATH]; + int nStatus = ERR_SUCCESS; + __int64 deviceSize; + uint64 remainingBytes, workChunkStartByteOffset, lastHeaderUpdateDistance = 0, skippedBadSectorCount = 0; + uint32 workChunkSize; + DWORD dwError, dwResult; + BOOL bPause = FALSE, bEncryptedAreaSizeChanged = FALSE; + LARGE_INTEGER offset; + int sectorSize; + int i; + DWORD n; + WCHAR *devicePath = volParams->volumePath; + Password *password = volParams->password; + HWND hwndDlg = volParams->hwndDlg; + int pkcs5_prf = volParams->pkcs5; + int pim = volParams->pim; + DISK_GEOMETRY driveGeometry; + + + buf = (char *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); + if (!buf) + { + nStatus = ERR_OUTOFMEMORY; + goto closing_seq; + } + + headerCryptoInfo = crypto_open(); + + if (headerCryptoInfo == NULL) + { + nStatus = ERR_OUTOFMEMORY; + goto closing_seq; + } + + deviceSize = GetDeviceSize (devicePath); + if (deviceSize < 0) + { + // Cannot determine the size of the partition + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + + // The wizard should have dismounted the TC volume if it was mounted, but for extra safety we will check this again. + if (IsMountedVolume (devicePath)) + { + int driveLetter = GetMountedVolumeDriveNo (devicePath); + + if (driveLetter == -1 + || !UnmountVolume (hwndDlg, driveLetter, TRUE)) + { + handleWin32Error (hwndDlg, SRC_POS); + AbortProcess ("CANT_DISMOUNT_VOLUME"); + } + } + + + StringCchCopyW (deviceName, ARRAYSIZE(deviceName), devicePath); + + if (FakeDosNameForDevice (deviceName, dosDev, sizeof(dosDev), devName, sizeof(devName), FALSE) != 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + dev = OpenPartitionVolume (hwndDlg, devName, + TRUE, // Require exclusive access + FALSE, // Do not require shared access + TRUE, // 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; + } + + + + // This should never be needed, but is still performed for extra safety (without checking the result) + DeviceIoControl (dev, + FSCTL_ALLOW_EXTENDED_DASD_IO, + NULL, + 0, + NULL, + 0, + &dwResult, + NULL); + + + if (!DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveGeometry, sizeof (driveGeometry), &dwResult, NULL)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if ( (driveGeometry.BytesPerSector == 0) + || (driveGeometry.BytesPerSector > TC_MAX_VOLUME_SECTOR_SIZE) + || (driveGeometry.BytesPerSector % ENCRYPTION_DATA_UNIT_SIZE != 0) + ) + { + Error ("SECTOR_SIZE_UNSUPPORTED", hwndDlg); + nStatus = ERR_DONT_REPORT; + goto closing_seq; + } + + sectorSize = driveGeometry.BytesPerSector; + + + tmpSectorBuf = (byte *) TCalloc (sectorSize); + if (!tmpSectorBuf) + { + nStatus = ERR_OUTOFMEMORY; + goto closing_seq; + } + + + nStatus = OpenBackupHeader (dev, devicePath, password, pkcs5_prf, pim, &masterCryptoInfo, headerCryptoInfo, deviceSize); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + + if (masterCryptoInfo->LegacyVolume) + { + Error ("NONSYS_INPLACE_DECRYPTION_BAD_VOL_FORMAT", hwndDlg); + nStatus = ERR_DONT_REPORT; + goto closing_seq; + } + + if (masterCryptoInfo->hiddenVolume) + { + Error ("NONSYS_INPLACE_DECRYPTION_CANT_DECRYPT_HID_VOL", hwndDlg); + nStatus = ERR_DONT_REPORT; + goto closing_seq; + } + + if (!bInPlaceEncNonSysResumed + && masterCryptoInfo->VolumeSize.Value == masterCryptoInfo->EncryptedAreaLength.Value) + { + /* Decryption started (not resumed) */ + + if ((masterCryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0) + { + // The volume has not been encrypted in-place so it may contain a hidden volume. + // Ask the user to confirm it does not. + + char *tmpStr[] = {0, + "CONFIRM_VOL_CONTAINS_NO_HIDDEN_VOL", + "VOL_CONTAINS_NO_HIDDEN_VOL", + "VOL_CONTAINS_A_HIDDEN_VOL", + 0}; + + switch (AskMultiChoice ((void **) tmpStr, FALSE, hwndDlg)) + { + case 1: + // NOP + break; + case 2: + default: + // Cancel + nStatus = ERR_DONT_REPORT; + goto closing_seq; + } + } + + // Update config files and app data + + // In the config file, increase the number of partitions where in-place decryption is in progress + SaveNonSysInPlaceEncSettings (1, TC_WIPE_NONE, TRUE); + + // Add the wizard to the system startup sequence if appropriate + if (!IsNonInstallMode ()) + ManageStartupSeqWiz (FALSE, L"/prinplace"); + } + + + + bInPlaceEncNonSysResumed = TRUE; + bFirstNonSysInPlaceEncResumeDone = TRUE; + + + remainingBytes = masterCryptoInfo->EncryptedAreaLength.Value; + + lastHeaderUpdateDistance = 0; + + + ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_DECRYPTING); + + + + /* The in-place decryption core */ + + while (remainingBytes > 0) + { + workChunkSize = (uint32) min (remainingBytes, TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE); + + if (workChunkSize % ENCRYPTION_DATA_UNIT_SIZE != 0) + { + nStatus = ERR_PARAMETER_INCORRECT; + goto closing_seq; + } + + workChunkStartByteOffset = masterCryptoInfo->EncryptedAreaStart.Value; + + unitNo.Value = workChunkStartByteOffset / ENCRYPTION_DATA_UNIT_SIZE; + + + // Read the ciphertext into RAM + + offset.QuadPart = workChunkStartByteOffset; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (ReadFile (dev, buf, workChunkSize, &n, NULL) == 0) + { + // Read error + + DWORD dwTmpErr = GetLastError (); + + if (IsDiskReadError (dwTmpErr) && !bVolTransformThreadCancel) + { + // Physical defect or data corruption + + if (!*DiscardUnreadableEncryptedSectors) + { + *DiscardUnreadableEncryptedSectors = (AskWarnYesNo ("DISCARD_UNREADABLE_ENCRYPTED_SECTORS", hwndDlg) == IDYES); + } + + if (*DiscardUnreadableEncryptedSectors) + { + // Read the work chunk again, but this time each sector individually and skiping each bad sector + + LARGE_INTEGER tmpSectorOffset; + uint64 tmpSectorCount; + uint64 tmpBufOffset = 0; + DWORD tmpNbrReadBytes = 0; + + tmpSectorOffset.QuadPart = offset.QuadPart; + + for (tmpSectorCount = workChunkSize / sectorSize; tmpSectorCount > 0; --tmpSectorCount) + { + if (SetFilePointerEx (dev, tmpSectorOffset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (ReadFile (dev, tmpSectorBuf, sectorSize, &tmpNbrReadBytes, NULL) == 0 + || tmpNbrReadBytes != (DWORD) sectorSize) + { + // Read error + + // Clear the buffer so the content of each unreadable sector is replaced with decrypted all-zero blocks (producing pseudorandom data) + memset (tmpSectorBuf, 0, sectorSize); + + skippedBadSectorCount++; + } + + memcpy (buf + tmpBufOffset, tmpSectorBuf, sectorSize); + + tmpSectorOffset.QuadPart += sectorSize; + tmpBufOffset += sectorSize; + } + } + else + { + SetLastError (dwTmpErr); // Preserve the original error code + + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + } + else + { + SetLastError (dwTmpErr); // Preserve the original error code + + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + } + + // Decrypt the ciphertext in RAM + + DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo); + + + + // Conceal initial portion of the filesystem + + if (workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE) + { + // We are decrypting the initial TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE bytes of the filesystem. We will + // conceal this portion to prevent Windows and applications from interfering with the volume. + + for (i = 0; i < min (TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET + workChunkSize); i++) + buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; + } + + + // Write the plaintext + + offset.QuadPart = workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (WriteFile (dev, buf, workChunkSize, &n, NULL) == 0) + { + // Write error + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + + masterCryptoInfo->EncryptedAreaStart.Value += workChunkSize; + masterCryptoInfo->EncryptedAreaLength.Value -= workChunkSize; + + remainingBytes -= workChunkSize; + lastHeaderUpdateDistance += workChunkSize; + + bEncryptedAreaSizeChanged = TRUE; + + if (lastHeaderUpdateDistance >= TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL) + { + nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); + + if (nStatus != ERR_SUCCESS) + { + // Possible write error + goto closing_seq; + } + + lastHeaderUpdateDistance = 0; + } + + ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value); + + if (bVolTransformThreadCancel) + { + bPause = TRUE; + break; + } + } + + nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); + + + if (nStatus != ERR_SUCCESS) + { + // Possible write error + goto closing_seq; + } + + + if (!bPause) + { + /* Volume has been fully decrypted. */ + + + // Prevent attempts to update volume header during the closing sequence + bEncryptedAreaSizeChanged = FALSE; + + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINALIZING); + + + + /* Undo concealing of the filesystem */ + + nStatus = ConcealNTFS (dev); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + + + /* Ovewrite the backup header and the remaining ciphertext with all-zero blocks (the primary header was overwritten with the decrypted data). */ + + memset (tmpSectorBuf, 0, sectorSize); + + for (offset.QuadPart = masterCryptoInfo->VolumeSize.Value; + offset.QuadPart <= deviceSize - sectorSize; + offset.QuadPart += sectorSize) + { + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (WriteFile (dev, tmpSectorBuf, sectorSize, &n, NULL) == 0) + { + // Write error + dwError = GetLastError(); + + SetLastError (dwError); + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + } + + + + /* Update the configuration files */ + + SaveNonSysInPlaceEncSettings (-1, TC_WIPE_NONE, TRUE); + + + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINISHED); + + nStatus = ERR_SUCCESS; + + } + else + { + // The process has been paused by the user or aborted by the wizard (e.g. on app exit) + + nStatus = ERR_USER_ABORT; + + SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PAUSED); + } + + if (dev != INVALID_HANDLE_VALUE) + { + CloseHandle (dev); + dev = INVALID_HANDLE_VALUE; + } + + +closing_seq: + + dwError = GetLastError(); + + if (bEncryptedAreaSizeChanged + && dev != INVALID_HANDLE_VALUE + && masterCryptoInfo != NULL + && headerCryptoInfo != NULL + && deviceSize > 0) + { + // Execution of the core loop may have been interrupted due to an error or user action without updating the header + FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize); + } + + if (dev != INVALID_HANDLE_VALUE) + { + CloseHandle (dev); + dev = INVALID_HANDLE_VALUE; + } + + if (masterCryptoInfo != NULL) + { + crypto_close (masterCryptoInfo); + masterCryptoInfo = NULL; + } + + if (headerCryptoInfo != NULL) + { + crypto_close (headerCryptoInfo); + headerCryptoInfo = NULL; + } + + if (dosDev[0]) + RemoveFakeDosName (devicePath, dosDev); + + if (buf != NULL) + { + TCfree (buf); + buf = NULL; + } + + if (tmpSectorBuf != NULL) + { + TCfree (tmpSectorBuf); + tmpSectorBuf = NULL; + } + + if (skippedBadSectorCount > 0) + { + wchar_t msg[30000] = {0}; + wchar_t sizeStr[500] = {0}; + + GetSizeString (skippedBadSectorCount * sectorSize, sizeStr, sizeof(sizeStr)); + + StringCbPrintfW (msg, sizeof(msg), + GetString ("SKIPPED_BAD_SECTOR_COUNT"), + skippedBadSectorCount, + sizeStr); + + WarningDirect (msg, hwndDlg); + } + + if (nStatus != ERR_SUCCESS && nStatus != ERR_USER_ABORT) + SetLastError (dwError); + + return nStatus; +} + +int FastVolumeHeaderUpdate (HANDLE dev, CRYPTO_INFO *headerCryptoInfo, CRYPTO_INFO *masterCryptoInfo, __int64 deviceSize) +{ + LARGE_INTEGER offset; + DWORD n; + int nStatus = ERR_SUCCESS; + byte *header; + DWORD dwError; + uint32 headerCrc32; + byte *fieldPos; + + header = (byte *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE); + + if (!header) + return ERR_OUTOFMEMORY; + + VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + + + fieldPos = (byte *) header + TC_HEADER_OFFSET_ENCRYPTED_AREA_START; + + offset.QuadPart = deviceSize - TC_VOLUME_HEADER_GROUP_SIZE; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 + || !ReadEffectiveVolumeHeader (TRUE, dev, header, &n) || n < TC_VOLUME_HEADER_EFFECTIVE_SIZE) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + + DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo); + + if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x56455241) + { + nStatus = ERR_PARAMETER_INCORRECT; + goto closing_seq; + } + + mputInt64 (fieldPos, (masterCryptoInfo->EncryptedAreaStart.Value)); + mputInt64 (fieldPos, (masterCryptoInfo->EncryptedAreaLength.Value)); + + // We need to ensure the TC_HEADER_FLAG_NONSYS_INPLACE_ENC flag bit is set, because if volumes created by TC-format + // were decrypted in place, it would be possible to mount them partially encrypted and it wouldn't be possible + // to resume interrupted decryption after the wizard exits. + masterCryptoInfo->HeaderFlags |= TC_HEADER_FLAG_NONSYS_INPLACE_ENC; + fieldPos = (byte *) header + TC_HEADER_OFFSET_FLAGS; + mputLong (fieldPos, (masterCryptoInfo->HeaderFlags)); + + + headerCrc32 = GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC); + fieldPos = (byte *) header + TC_HEADER_OFFSET_HEADER_CRC; + mputLong (fieldPos, headerCrc32); + + EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo); + + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 + || !WriteEffectiveVolumeHeader (TRUE, dev, header)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + +closing_seq: + + dwError = GetLastError(); + + burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + TCfree (header); + + if (nStatus != ERR_SUCCESS) + SetLastError (dwError); + + return nStatus; +} + + +static HANDLE OpenPartitionVolume (HWND hwndDlg, const wchar_t *devName, + BOOL bExclusiveRequired, + BOOL bSharedRequired, + BOOL bSharedRequiresConfirmation, + BOOL bShowAlternativeSteps, + BOOL bSilent) +{ + HANDLE dev = INVALID_HANDLE_VALUE; + int retryCount = 0; + + if (bExclusiveRequired) + bSharedRequired = FALSE; + + if (bExclusiveRequired || !bSharedRequired) + { + // Exclusive access + // Note that when exclusive access is denied, it is worth retrying (usually succeeds after a few tries). + while (dev == INVALID_HANDLE_VALUE && retryCount++ < EXCL_ACCESS_MAX_AUTO_RETRIES) + { + dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); + + if (retryCount > 1) + Sleep (EXCL_ACCESS_AUTO_RETRY_DELAY); + } + } + + if (dev == INVALID_HANDLE_VALUE) + { + if (bExclusiveRequired) + { + if (!bSilent) + { + handleWin32Error (hwndDlg, SRC_POS); + + if (bShowAlternativeSteps) + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE); + else + Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg); + } + return INVALID_HANDLE_VALUE; + } + + // Shared mode + dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); + if (dev != INVALID_HANDLE_VALUE) + { + if (bSharedRequiresConfirmation + && !bSilent + && AskWarnNoYes ("DEVICE_IN_USE_INPLACE_ENC", hwndDlg) == IDNO) + { + CloseHandle (dev); + return INVALID_HANDLE_VALUE; + } + } + else + { + if (!bSilent) + { + handleWin32Error (MainDlg, SRC_POS); + + if (bShowAlternativeSteps) + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE); + else + Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg); + } + return INVALID_HANDLE_VALUE; + } + } + + return dev; +} + + +static int DismountFileSystem (HWND hwndDlg, HANDLE dev, + int driveLetter, + BOOL bForcedAllowed, + BOOL bForcedRequiresConfirmation, + BOOL bSilent) +{ + int attempt; + BOOL bResult; + DWORD dwResult; + + CloseVolumeExplorerWindows (MainDlg, driveLetter); + + attempt = UNMOUNT_MAX_AUTO_RETRIES * 10; + + while (!(bResult = DeviceIoControl (dev, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwResult, NULL)) + && attempt > 0) + { + Sleep (UNMOUNT_AUTO_RETRY_DELAY); + attempt--; + } + + if (!bResult) + { + if (!bForcedAllowed) + { + if (!bSilent) + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_LOCK_OR_DISMOUNT_FILESYS", TRUE); + + return ERR_DONT_REPORT; + } + + if (bForcedRequiresConfirmation + && !bSilent + && AskWarnYesNo ("VOL_LOCK_FAILED_OFFER_FORCED_DISMOUNT", hwndDlg) == IDNO) + { + return ERR_DONT_REPORT; + } + } + + // Dismount the volume + + attempt = UNMOUNT_MAX_AUTO_RETRIES * 10; + + while (!(bResult = DeviceIoControl (dev, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwResult, NULL)) + && attempt > 0) + { + Sleep (UNMOUNT_AUTO_RETRY_DELAY); + attempt--; + } + + if (!bResult) + { + if (!bSilent) + ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_LOCK_OR_DISMOUNT_FILESYS", TRUE); + + return ERR_DONT_REPORT; + } + + return ERR_SUCCESS; +} + + +// Easy-to-undo modification applied to conceal the NTFS filesystem (to prevent Windows and apps from +// interfering with it until the volume has been fully encrypted). Note that this function will precisely +// undo any modifications it made to the filesystem automatically if an error occurs when writing (including +// physical drive defects). +static int ConcealNTFS (HANDLE dev) +{ + char buf [TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE]; + DWORD nbrBytesProcessed, nbrBytesProcessed2; + int i; + LARGE_INTEGER offset; + DWORD dwError; + + offset.QuadPart = 0; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) + return ERR_OS_ERROR; + + if (ReadFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed, NULL) == 0) + return ERR_OS_ERROR; + + for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE; i++) + buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; + + offset.QuadPart = 0; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0) + return ERR_OS_ERROR; + + if (WriteFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed, NULL) == 0) + { + // One or more of the sectors is/are probably damaged and cause write errors. + // We must undo the modifications we made. + + dwError = GetLastError(); + + for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE; i++) + buf[i] ^= TC_NTFS_CONCEAL_CONSTANT; + + offset.QuadPart = 0; + + do + { + Sleep (1); + } + while (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 + || WriteFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed2, NULL) == 0); + + SetLastError (dwError); + + return ERR_OS_ERROR; + } + + return ERR_SUCCESS; +} + + +void ShowInPlaceEncErrMsgWAltSteps (HWND hwndDlg, char *iniStrId, BOOL bErr) +{ + wchar_t msg[30000]; + + StringCbCopyW (msg, sizeof(msg), GetString (iniStrId)); + + StringCbCatW (msg, sizeof(msg), L"\n\n\n"); + StringCbCatW (msg, sizeof(msg), GetString ("INPLACE_ENC_ALTERNATIVE_STEPS")); + + if (bErr) + ErrorDirect (msg, hwndDlg); + else + WarningDirect (msg, hwndDlg); +} + + +static void ExportProgressStats (__int64 bytesDone, __int64 totalSize) +{ + NonSysInplaceEncBytesDone = bytesDone; + NonSysInplaceEncTotalSize = totalSize; +} + + +void SetNonSysInplaceEncUIStatus (int nonSysInplaceEncStatus) +{ + NonSysInplaceEncStatus = nonSysInplaceEncStatus; +} + + +BOOL SaveNonSysInPlaceEncSettings (int delta, WipeAlgorithmId newWipeAlgorithm, BOOL bDecrypt) +{ + int count; + char str[32]; + WipeAlgorithmId savedWipeAlgorithm = TC_WIPE_NONE; + + if (delta == 0) + return TRUE; + + count = LoadNonSysInPlaceEncSettings (&savedWipeAlgorithm) + delta; + + if (count < 1) + { + RemoveNonSysInPlaceEncNotifications(); + return TRUE; + } + else if (!bDecrypt) + { + if (newWipeAlgorithm != TC_WIPE_NONE) + { + StringCbPrintfA (str, sizeof(str), "%d", (int) newWipeAlgorithm); + + SaveBufferToFile (str, GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE), (DWORD) strlen(str), FALSE, FALSE); + } + else if (FileExists (GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE))) + { + _wremove (GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE)); + } + } + + StringCbPrintfA (str, sizeof(str), "%d", count); + + return SaveBufferToFile (str, GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC), (DWORD) strlen(str), FALSE, FALSE); +} + + +// Repairs damaged sectors (i.e. those with read errors) by zeroing them. +// Note that this operating fails if there are any write errors. +int ZeroUnreadableSectors (HANDLE dev, LARGE_INTEGER startOffset, int64 size, int sectorSize, uint64 *zeroedSectorCount) +{ + int nStatus; + DWORD n; + int64 sectorCount; + LARGE_INTEGER workOffset; + byte *sectorBuffer = NULL; + DWORD dwError; + + workOffset.QuadPart = startOffset.QuadPart; + + sectorBuffer = (byte *) TCalloc (sectorSize); + + if (!sectorBuffer) + return ERR_OUTOFMEMORY; + + if (SetFilePointerEx (dev, startOffset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + + for (sectorCount = size / sectorSize; sectorCount > 0; --sectorCount) + { + if (ReadFile (dev, sectorBuffer, sectorSize, &n, NULL) == 0) + { + memset (sectorBuffer, 0, sectorSize); + + if (SetFilePointerEx (dev, workOffset, NULL, FILE_BEGIN) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + if (WriteFile (dev, sectorBuffer, sectorSize, &n, NULL) == 0) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + ++(*zeroedSectorCount); + } + + workOffset.QuadPart += n; + } + + nStatus = ERR_SUCCESS; + +closing_seq: + + dwError = GetLastError(); + + if (sectorBuffer != NULL) + TCfree (sectorBuffer); + + if (nStatus != ERR_SUCCESS) + SetLastError (dwError); + + return nStatus; +} + + +static int OpenBackupHeader (HANDLE dev, const wchar_t *devicePath, Password *password, int pkcs5, int pim, PCRYPTO_INFO *retMasterCryptoInfo, CRYPTO_INFO *headerCryptoInfo, __int64 deviceSize) +{ + LARGE_INTEGER offset; + DWORD n; + int nStatus = ERR_SUCCESS; + char *header; + DWORD dwError; + + header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE); + if (!header) + return ERR_OUTOFMEMORY; + + VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + + + + offset.QuadPart = deviceSize - TC_VOLUME_HEADER_GROUP_SIZE; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 + || !ReadEffectiveVolumeHeader (TRUE, dev, (byte *) header, &n) || n < TC_VOLUME_HEADER_EFFECTIVE_SIZE) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + + nStatus = ReadVolumeHeader (FALSE, header, password, pkcs5, pim, FALSE, retMasterCryptoInfo, headerCryptoInfo); + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + +closing_seq: + + dwError = GetLastError(); + + burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + TCfree (header); + + dwError = GetLastError(); + + if (nStatus != ERR_SUCCESS) + SetLastError (dwError); + + return nStatus; +} + + +static BOOL GetFreeClusterBeforeThreshold (HANDLE volumeHandle, int64 *freeCluster, int64 clusterThreshold) +{ + const int bitmapSize = 65536; + byte bitmapBuffer[bitmapSize + sizeof (VOLUME_BITMAP_BUFFER)]; + VOLUME_BITMAP_BUFFER *bitmap = (VOLUME_BITMAP_BUFFER *) bitmapBuffer; + STARTING_LCN_INPUT_BUFFER startLcn; + startLcn.StartingLcn.QuadPart = 0; + + DWORD bytesReturned; + while (DeviceIoControl (volumeHandle, FSCTL_GET_VOLUME_BITMAP, &startLcn, sizeof (startLcn), &bitmapBuffer, sizeof (bitmapBuffer), &bytesReturned, NULL) + || GetLastError() == ERROR_MORE_DATA) + { + for (int64 bitmapIndex = 0; bitmapIndex < min (bitmapSize, (bitmap->BitmapSize.QuadPart / 8)); ++bitmapIndex) + { + if (bitmap->StartingLcn.QuadPart + bitmapIndex * 8 >= clusterThreshold) + goto err; + + if (bitmap->Buffer[bitmapIndex] != 0xff) + { + for (int bit = 0; bit < 8; ++bit) + { + if ((bitmap->Buffer[bitmapIndex] & (1 << bit)) == 0) + { + *freeCluster = bitmap->StartingLcn.QuadPart + bitmapIndex * 8 + bit; + + if (*freeCluster >= clusterThreshold) + goto err; + + return TRUE; + } + } + } + } + + startLcn.StartingLcn.QuadPart += min (bitmapSize * 8, bitmap->BitmapSize.QuadPart); + } + +err: + SetLastError (ERROR_DISK_FULL); + return FALSE; +} + + +static BOOL MoveClustersBeforeThresholdInDir (HANDLE volumeHandle, const wstring &directory, int64 clusterThreshold) +{ + WIN32_FIND_DATAW findData; + + HANDLE findHandle = FindFirstFileW (((directory.size() <= 3 ? L"" : L"\\\\?\\") + directory + L"\\*").c_str(), &findData); + if (findHandle == INVALID_HANDLE_VALUE) + return TRUE; // Error ignored + + finally_do_arg (HANDLE, findHandle, { FindClose (finally_arg); }); + + // Find all files and directories + do + { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + wstring subDir = findData.cFileName; + + if (subDir == L"." || subDir == L"..") + continue; + + if (!MoveClustersBeforeThresholdInDir (volumeHandle, directory + L"\\" + subDir, clusterThreshold)) + return FALSE; + } + + DWORD access = FILE_READ_ATTRIBUTES; + + if (findData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) + access = FILE_READ_DATA; + + HANDLE fsObject = CreateFileW ((directory + L"\\" + findData.cFileName).c_str(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (fsObject == INVALID_HANDLE_VALUE) + continue; + + finally_do_arg (HANDLE, fsObject, { CloseHandle (finally_arg); }); + + STARTING_VCN_INPUT_BUFFER startVcn; + startVcn.StartingVcn.QuadPart = 0; + RETRIEVAL_POINTERS_BUFFER retPointers; + DWORD bytesReturned; + + // Find clusters allocated beyond the threshold + while (DeviceIoControl (fsObject, FSCTL_GET_RETRIEVAL_POINTERS, &startVcn, sizeof (startVcn), &retPointers, sizeof (retPointers), &bytesReturned, NULL) + || GetLastError() == ERROR_MORE_DATA) + { + if (retPointers.ExtentCount == 0) + break; + + if (retPointers.Extents[0].Lcn.QuadPart != -1) + { + int64 extentStartCluster = retPointers.Extents[0].Lcn.QuadPart; + int64 extentLen = retPointers.Extents[0].NextVcn.QuadPart - retPointers.StartingVcn.QuadPart; + int64 extentEndCluster = extentStartCluster + extentLen - 1; + + if (extentEndCluster >= clusterThreshold) + { + // Move clusters before the threshold + for (int64 movedCluster = max (extentStartCluster, clusterThreshold); movedCluster <= extentEndCluster; ++movedCluster) + { + for (int retry = 0; ; ++retry) + { + MOVE_FILE_DATA moveData; + + if (GetFreeClusterBeforeThreshold (volumeHandle, &moveData.StartingLcn.QuadPart, clusterThreshold)) + { + moveData.FileHandle = fsObject; + moveData.StartingVcn.QuadPart = movedCluster - extentStartCluster + retPointers.StartingVcn.QuadPart; + moveData.ClusterCount = 1; + + if (DeviceIoControl (volumeHandle, FSCTL_MOVE_FILE, &moveData, sizeof (moveData), NULL, 0, &bytesReturned, NULL)) + break; + } + + if (retry > 600) + return FALSE; + + // There are possible race conditions as we work on a live filesystem + Sleep (100); + } + } + } + } + + startVcn.StartingVcn = retPointers.Extents[0].NextVcn; + } + + } while (FindNextFileW (findHandle, &findData)); + + return TRUE; +} + + +BOOL MoveClustersBeforeThreshold (HANDLE volumeHandle, PWSTR volumeDevicePath, int64 clusterThreshold) +{ + int drive = GetDiskDeviceDriveLetter (volumeDevicePath); + if (drive == -1) + { + SetLastError (ERROR_INVALID_PARAMETER); + return FALSE; + } + + wstring volumeRoot = L"X:"; + volumeRoot[0] = L'A' + (wchar_t) drive; + + return MoveClustersBeforeThresholdInDir (volumeHandle, volumeRoot, clusterThreshold); +} -- cgit v1.2.3