"Fossies" - the Fresh Open Source Software Archive

Member "src/Format/InPlace.c" (10 Oct 2018, 61671 Bytes) of package /windows/misc/VeraCrypt_1.23-Hotfix-2_Source.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "InPlace.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.21_Source_vs_1.22_Source.

    1 /*
    2  Derived from source code of TrueCrypt 7.1a, which is
    3  Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed
    4  by the TrueCrypt License 3.0.
    5 
    6  Modifications and additions to the original source code (contained in this file)
    7  and all other portions of this file are Copyright (c) 2013-2017 IDRIX
    8  and are governed by the Apache License 2.0 the full text of which is
    9  contained in the file License.txt included in VeraCrypt binary and source
   10  code distribution packages.
   11 */
   12 
   13 
   14 /* In this file, _WIN32_WINNT is defined as 0x0600 to make filesystem shrink available (Vista
   15 or later). _WIN32_WINNT cannot be defined as 0x0600 for the entire user-space projects
   16 because it breaks the main font app when the app is running on XP (likely an MS bug).
   17 IMPORTANT: Due to this issue, functions in this file must not directly interact with GUI. */
   18 #define TC_LOCAL_WIN32_WINNT_OVERRIDE    1
   19 #if (_WIN32_WINNT < 0x0600)
   20 #   undef _WIN32_WINNT
   21 #   define _WIN32_WINNT 0x0600
   22 #endif
   23 
   24 
   25 #include <stdlib.h>
   26 #include <string.h>
   27 #include <string>
   28 #include <intsafe.h>
   29 
   30 #include "Tcdefs.h"
   31 #include "Platform/Finally.h"
   32 
   33 #include "Common.h"
   34 #include "Crc.h"
   35 #include "Dlgcode.h"
   36 #include "Language.h"
   37 #include "Tcformat.h"
   38 #include "Volumes.h"
   39 
   40 #include "InPlace.h"
   41 
   42 #include <Strsafe.h>
   43 
   44 using namespace std;
   45 using namespace VeraCrypt;
   46 
   47 #if TC_VOLUME_DATA_OFFSET != 131072
   48 #   error TC_VOLUME_DATA_OFFSET != 131072
   49 #endif
   50 
   51 #if TC_VOLUME_HEADER_EFFECTIVE_SIZE != 512
   52 #   error TC_VOLUME_HEADER_EFFECTIVE_SIZE != 512
   53 #endif
   54 
   55 #if TC_TOTAL_VOLUME_HEADERS_SIZE != 262144
   56 #   error TC_TOTAL_VOLUME_HEADERS_SIZE != 262144
   57 #endif
   58 
   59 #define TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE   (2048 * BYTES_PER_KB)
   60 #define TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE        (2 * TC_MAX_VOLUME_SECTOR_SIZE)
   61 #define TC_NTFS_CONCEAL_CONSTANT    0xFF
   62 #define TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL    (64 * BYTES_PER_MB)
   63 #define TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE          (TC_TOTAL_VOLUME_HEADERS_SIZE + TC_MIN_NTFS_FS_SIZE * 2)
   64 
   65 
   66 // If the returned value is greater than 0, it is the desired volume size in NTFS sectors (not in bytes)
   67 // after shrinking has been performed. If there's any error, returns -1.
   68 static __int64 NewFileSysSizeAfterShrink (HANDLE dev, const wchar_t *devicePath, int64 *totalClusterCount, DWORD *bytesPerCluster, BOOL silent)
   69 {
   70     NTFS_VOLUME_DATA_BUFFER ntfsVolData;
   71     DWORD nBytesReturned;
   72     __int64 fileSysSize, desiredNbrSectors;
   73 
   74     // Filesystem size and sector size
   75 
   76     if (!DeviceIoControl (dev,
   77         FSCTL_GET_NTFS_VOLUME_DATA,
   78         NULL,
   79         0,
   80         (LPVOID) &ntfsVolData,
   81         sizeof (ntfsVolData),
   82         &nBytesReturned,
   83         NULL))
   84     {
   85         if (!silent)
   86             handleWin32Error (MainDlg, SRC_POS);
   87 
   88         return -1;
   89     }
   90 
   91     if (    (ntfsVolData.NumberSectors.QuadPart <= 0)
   92         ||  (ntfsVolData.NumberSectors.QuadPart > (INT64_MAX / (__int64) ntfsVolData.BytesPerSector)) // overflow test
   93         )
   94     {
   95         SetLastError (ERROR_INTERNAL_ERROR);
   96         if (!silent)
   97             handleWin32Error (MainDlg, SRC_POS);
   98 
   99         return -1;
  100     }
  101 
  102     fileSysSize = ntfsVolData.NumberSectors.QuadPart * ntfsVolData.BytesPerSector;
  103 
  104     desiredNbrSectors = (fileSysSize - TC_TOTAL_VOLUME_HEADERS_SIZE) / ntfsVolData.BytesPerSector;
  105 
  106     if (desiredNbrSectors <= 0)
  107         return -1;
  108 
  109     if (totalClusterCount)
  110         *totalClusterCount = ntfsVolData.TotalClusters.QuadPart;
  111     if (bytesPerCluster)
  112         *bytesPerCluster = ntfsVolData.BytesPerCluster;
  113 
  114     return desiredNbrSectors;
  115 }
  116 
  117 
  118 BOOL CheckRequirementsForNonSysInPlaceEnc (HWND hwndDlg, const wchar_t *devicePath, BOOL silent)
  119 {
  120     NTFS_VOLUME_DATA_BUFFER ntfsVolData;
  121     DWORD nBytesReturned;
  122     HANDLE dev;
  123     WCHAR szFileSysName [256];
  124     WCHAR devPath [MAX_PATH];
  125     WCHAR dosDev [TC_MAX_PATH] = {0};
  126     WCHAR devName [MAX_PATH] = {0};
  127     int driveLetterNo = -1;
  128     WCHAR szRootPath[4] = {0, L':', L'\\', 0};
  129     __int64 deviceSize;
  130     int partitionNumber = -1, driveNumber = -1;
  131 
  132 
  133     /* ---------- Checks that do not require admin rights ----------- */
  134 
  135 
  136     /* Operating system */
  137 
  138     if (CurrentOSMajor < 6)
  139     {
  140         if (!silent)
  141             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "OS_NOT_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE);
  142 
  143         return FALSE;
  144     }
  145 
  146 
  147     /* Volume type (must be a partition or a dynamic volume) */
  148 
  149     if (swscanf (devicePath, L"\\Device\\HarddiskVolume%d", &partitionNumber) != 1
  150         && swscanf (devicePath, L"\\Device\\Harddisk%d\\Partition%d", &driveNumber, &partitionNumber) != 2)
  151     {
  152         if (!silent)
  153             Error ("INPLACE_ENC_INVALID_PATH", hwndDlg);
  154 
  155         return FALSE;
  156     }
  157 
  158     if (partitionNumber == 0)
  159     {
  160         if (!silent)
  161             Warning ("RAW_DEV_NOT_SUPPORTED_FOR_INPLACE_ENC", hwndDlg);
  162 
  163         return FALSE;
  164     }
  165 
  166 
  167     /* Admin rights */
  168 
  169     if (!IsAdmin())
  170     {
  171         // We rely on the wizard process to call us only when the whole wizard process has been elevated (so UAC
  172         // status can be ignored). In case the IsAdmin() detection somehow fails, we allow the user to continue.
  173 
  174         if (!silent)
  175             Warning ("ADMIN_PRIVILEGES_WARN_DEVICES", hwndDlg);
  176     }
  177 
  178 
  179     /* ---------- Checks that may require admin rights ----------- */
  180 
  181 
  182     /* Access to the partition */
  183 
  184     StringCbCopyW (devPath, sizeof(devPath), devicePath);
  185 
  186     driveLetterNo = GetDiskDeviceDriveLetter (devPath);
  187 
  188     if (driveLetterNo >= 0)
  189         szRootPath[0] = (wchar_t) driveLetterNo + L'A';
  190 
  191     if (FakeDosNameForDevice (devicePath, dosDev, sizeof(dosDev), devName, sizeof(devName),FALSE) != 0)
  192     {
  193         if (!silent)
  194         {
  195             handleWin32Error (hwndDlg, SRC_POS);
  196             Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg);
  197         }
  198         return FALSE;
  199     }
  200 
  201     dev = OpenPartitionVolume (hwndDlg, devName,
  202         FALSE,  // Do not require exclusive access
  203         TRUE,   // Require shared access (must be TRUE; otherwise, volume properties will not be possible to obtain)
  204         FALSE,  // Do not ask the user to confirm shared access (if exclusive fails)
  205         FALSE,  // Do not append alternative instructions how to encrypt the data (to applicable error messages)
  206         silent);    // Silent mode
  207 
  208     if (dev == INVALID_HANDLE_VALUE)
  209         return FALSE;
  210 
  211 
  212     /* File system type */
  213 
  214     GetVolumeInformation (szRootPath, NULL, 0, NULL, NULL, NULL, szFileSysName, ARRAYSIZE (szFileSysName));
  215 
  216     if (wcsncmp (szFileSysName, L"NTFS", 4))
  217     {
  218         // The previous filesystem type detection method failed (or it's not NTFS) -- try an alternative method
  219 
  220         if (!DeviceIoControl (dev,
  221             FSCTL_GET_NTFS_VOLUME_DATA,
  222             NULL,
  223             0,
  224             (LPVOID) &ntfsVolData,
  225             sizeof (ntfsVolData),
  226             &nBytesReturned,
  227             NULL))
  228         {
  229             if (!silent)
  230             {
  231                 // The filesystem is not NTFS or the filesystem type could not be determined (or the NTFS filesystem
  232                 // is dismounted).
  233 
  234                 if (IsDeviceMounted (devName))
  235                     ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "ONLY_NTFS_SUPPORTED_FOR_NONSYS_INPLACE_ENC", FALSE);
  236                 else
  237                     Warning ("ONLY_MOUNTED_VOL_SUPPORTED_FOR_NONSYS_INPLACE_ENC", hwndDlg);
  238             }
  239 
  240             CloseHandle (dev);
  241             return FALSE;
  242         }
  243     }
  244 
  245 
  246     /* Attempt to determine whether the filesystem can be safely shrunk */
  247 
  248     if (NewFileSysSizeAfterShrink (dev, devicePath, NULL, NULL, silent) == -1)
  249     {
  250         // Cannot determine whether shrinking is required
  251         if (!silent)
  252             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
  253 
  254         CloseHandle (dev);
  255         return FALSE;
  256     }
  257 
  258 
  259     /* Partition size */
  260 
  261     deviceSize = GetDeviceSize (devicePath);
  262     if (deviceSize < 0)
  263     {
  264         // Cannot determine the size of the partition
  265         if (!silent)
  266             Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg);
  267 
  268         CloseHandle (dev);
  269         return FALSE;
  270     }
  271 
  272     if (deviceSize < TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE)
  273     {
  274         // The partition is too small
  275         if (!silent)
  276         {
  277             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "PARTITION_TOO_SMALL_FOR_NONSYS_INPLACE_ENC", FALSE);
  278         }
  279 
  280         CloseHandle (dev);
  281         return FALSE;
  282     }
  283 
  284 
  285     /* Free space on the filesystem */
  286 
  287     if (!DeviceIoControl (dev,
  288         FSCTL_GET_NTFS_VOLUME_DATA,
  289         NULL,
  290         0,
  291         (LPVOID) &ntfsVolData,
  292         sizeof (ntfsVolData),
  293         &nBytesReturned,
  294         NULL))
  295     {
  296         if (!silent)
  297             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", TRUE);
  298 
  299         CloseHandle (dev);
  300         return FALSE;
  301     }
  302 
  303     if (ntfsVolData.FreeClusters.QuadPart * ntfsVolData.BytesPerCluster < TC_TOTAL_VOLUME_HEADERS_SIZE)
  304     {
  305         if (!silent)
  306             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "NOT_ENOUGH_FREE_FILESYS_SPACE_FOR_SHRINK", TRUE);
  307 
  308         CloseHandle (dev);
  309         return FALSE;
  310     }
  311 
  312 
  313     /* Filesystem sector size */
  314 
  315     if (ntfsVolData.BytesPerSector > TC_MAX_VOLUME_SECTOR_SIZE
  316         || ntfsVolData.BytesPerSector % ENCRYPTION_DATA_UNIT_SIZE != 0)
  317     {
  318         if (!silent)
  319             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "SECTOR_SIZE_UNSUPPORTED", TRUE);
  320 
  321         CloseHandle (dev);
  322         return FALSE;
  323     }
  324 
  325 
  326     CloseHandle (dev);
  327     return TRUE;
  328 }
  329 
  330 BOOL CheckRequirementsForNonSysInPlaceDec (HWND hwndDlg, const wchar_t *devicePath, BOOL silent)
  331 {
  332     int partitionNumber = -1, driveNumber = -1;
  333 
  334     /* ---------- Checks that do not require admin rights ----------- */
  335 
  336     /* Volume type (must be a partition or a dynamic volume) */
  337     if ((swscanf (devicePath, L"\\Device\\HarddiskVolume%d", &partitionNumber) != 1
  338         && swscanf (devicePath, L"\\Device\\Harddisk%d\\Partition%d", &driveNumber, &partitionNumber) != 2)
  339         || partitionNumber == 0)
  340     {
  341         if (!silent)
  342             Error ("INPLACE_ENC_INVALID_PATH", hwndDlg);
  343 
  344         return FALSE;
  345     }
  346 
  347 
  348     /* Admin rights */
  349     if (!IsAdmin())
  350     {
  351         // We rely on the wizard process to call us only when the whole wizard process has been elevated (so UAC
  352         // status can be ignored). In case the IsAdmin() detection somehow fails, we allow the user to continue.
  353 
  354         if (!silent)
  355             Warning ("ADMIN_PRIVILEGES_WARN_DEVICES", hwndDlg);
  356     }
  357 
  358 
  359     /* ---------- Checks that may require admin rights ----------- */
  360 
  361     // [Currently none]
  362 
  363     return TRUE;
  364 }
  365 
  366 
  367 int EncryptPartitionInPlaceBegin (volatile FORMAT_VOL_PARAMETERS *volParams, volatile HANDLE *outHandle, WipeAlgorithmId wipeAlgorithm)
  368 {
  369     SHRINK_VOLUME_INFORMATION shrinkVolInfo;
  370     signed __int64 sizeToShrinkTo;
  371     int nStatus = ERR_SUCCESS;
  372     PCRYPTO_INFO cryptoInfo = NULL;
  373     PCRYPTO_INFO cryptoInfo2 = NULL;
  374     HANDLE dev = INVALID_HANDLE_VALUE;
  375     DWORD dwError;
  376     char *header;
  377     WCHAR dosDev[TC_MAX_PATH] = {0};
  378     WCHAR devName[MAX_PATH] = {0};
  379     int driveLetter = -1;
  380     WCHAR deviceName[MAX_PATH];
  381     uint64 dataAreaSize;
  382     __int64 deviceSize;
  383     LARGE_INTEGER offset;
  384     DWORD dwResult;
  385     HWND hwndDlg = volParams->hwndDlg;
  386 
  387     SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PREPARING);
  388 
  389 
  390     if (!CheckRequirementsForNonSysInPlaceEnc (hwndDlg, volParams->volumePath, FALSE))
  391         return ERR_DONT_REPORT;
  392 
  393 
  394     header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE);
  395     if (!header)
  396         return ERR_OUTOFMEMORY;
  397 
  398     VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
  399 
  400     deviceSize = GetDeviceSize (volParams->volumePath);
  401     if (deviceSize < 0)
  402     {
  403         // Cannot determine the size of the partition
  404         nStatus = ERR_PARAMETER_INCORRECT;
  405         goto closing_seq;
  406     }
  407 
  408     if (deviceSize < TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE)
  409     {
  410         ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "PARTITION_TOO_SMALL_FOR_NONSYS_INPLACE_ENC", TRUE);
  411         nStatus = ERR_DONT_REPORT;
  412         goto closing_seq;
  413     }
  414 
  415     dataAreaSize = GetVolumeDataAreaSize (volParams->hiddenVol, deviceSize);
  416 
  417     StringCchCopyW (deviceName, ARRAYSIZE(deviceName), volParams->volumePath);
  418 
  419     driveLetter = GetDiskDeviceDriveLetter (deviceName);
  420 
  421 
  422     if (FakeDosNameForDevice (volParams->volumePath, dosDev, sizeof(dosDev),devName, sizeof(devName),FALSE) != 0)
  423     {
  424         nStatus = ERR_OS_ERROR;
  425         goto closing_seq;
  426     }
  427 
  428     if (IsDeviceMounted (devName))
  429     {
  430         dev = OpenPartitionVolume (hwndDlg, devName,
  431             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)
  432             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)
  433             FALSE,  // Do not ask the user to confirm shared access (if exclusive fails)
  434             FALSE,  // Do not append alternative instructions how to encrypt the data (to applicable error messages)
  435             FALSE); // Non-silent mode
  436 
  437         if (dev == INVALID_HANDLE_VALUE)
  438         {
  439             nStatus = ERR_DONT_REPORT;
  440             goto closing_seq;
  441         }
  442     }
  443     else
  444     {
  445         // The volume is not mounted so we can't work with the filesystem.
  446         Error ("ONLY_MOUNTED_VOL_SUPPORTED_FOR_NONSYS_INPLACE_ENC", hwndDlg);
  447         nStatus = ERR_DONT_REPORT;
  448         goto closing_seq;
  449     }
  450 
  451 
  452     /* Gain "raw" access to the partition (the NTFS driver guards hidden sectors). */
  453 
  454     if (!DeviceIoControl (dev,
  455         FSCTL_ALLOW_EXTENDED_DASD_IO,
  456         NULL,
  457         0,
  458         NULL,
  459         0,
  460         &dwResult,
  461         NULL))
  462     {
  463         handleWin32Error (MainDlg, SRC_POS);
  464         ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
  465         nStatus = ERR_DONT_REPORT;
  466         goto closing_seq;
  467     }
  468 
  469 
  470 
  471     /* Shrink the filesystem */
  472 
  473     int64 totalClusterCount;
  474     DWORD bytesPerCluster;
  475 
  476     sizeToShrinkTo = NewFileSysSizeAfterShrink (dev, volParams->volumePath, &totalClusterCount, &bytesPerCluster, FALSE);
  477 
  478     if (sizeToShrinkTo == -1)
  479     {
  480         ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
  481         nStatus = ERR_DONT_REPORT;
  482         goto closing_seq;
  483     }
  484 
  485     SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_RESIZING);
  486 
  487     memset (&shrinkVolInfo, 0, sizeof (shrinkVolInfo));
  488 
  489     shrinkVolInfo.ShrinkRequestType = ShrinkPrepare;
  490     shrinkVolInfo.NewNumberOfSectors = sizeToShrinkTo;
  491 
  492     if (!DeviceIoControl (dev,
  493         FSCTL_SHRINK_VOLUME,
  494         (LPVOID) &shrinkVolInfo,
  495         sizeof (shrinkVolInfo),
  496         NULL,
  497         0,
  498         &dwResult,
  499         NULL))
  500     {
  501         handleWin32Error (hwndDlg, SRC_POS);
  502         ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "CANNOT_RESIZE_FILESYS", TRUE);
  503         nStatus = ERR_DONT_REPORT;
  504         goto closing_seq;
  505     }
  506 
  507     BOOL clustersMovedBeforeVolumeEnd = FALSE;
  508 
  509     while (true)
  510     {
  511         shrinkVolInfo.ShrinkRequestType = ShrinkCommit;
  512         shrinkVolInfo.NewNumberOfSectors = 0;
  513 
  514         if (!DeviceIoControl (dev, FSCTL_SHRINK_VOLUME, &shrinkVolInfo, sizeof (shrinkVolInfo), NULL, 0, &dwResult, NULL))
  515         {
  516             // If there are any occupied clusters beyond the new desired end of the volume, the call fails with
  517             // ERROR_ACCESS_DENIED (STATUS_ALREADY_COMMITTED).
  518             if (GetLastError () == ERROR_ACCESS_DENIED)
  519             {
  520                 if (!clustersMovedBeforeVolumeEnd)
  521                 {
  522                     if (MoveClustersBeforeThreshold (dev, deviceName, totalClusterCount - (bytesPerCluster > TC_TOTAL_VOLUME_HEADERS_SIZE ? 1 : TC_TOTAL_VOLUME_HEADERS_SIZE / bytesPerCluster)))
  523                     {
  524                         clustersMovedBeforeVolumeEnd = TRUE;
  525                         continue;
  526                     }
  527 
  528                     handleWin32Error (hwndDlg, SRC_POS);
  529                 }
  530             }
  531             else
  532                 handleWin32Error (hwndDlg, SRC_POS);
  533 
  534             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "CANNOT_RESIZE_FILESYS", TRUE);
  535             nStatus = ERR_DONT_REPORT;
  536             goto closing_seq;
  537         }
  538 
  539         break;
  540     }
  541 
  542     SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PREPARING);
  543 
  544 
  545     /* Gain exclusive access to the volume */
  546 
  547     nStatus = DismountFileSystem (hwndDlg, dev,
  548         driveLetter,
  549         TRUE,
  550         TRUE,
  551         FALSE);
  552 
  553     if (nStatus != ERR_SUCCESS)
  554     {
  555         nStatus = ERR_DONT_REPORT;
  556         goto closing_seq;
  557     }
  558 
  559 
  560 
  561     /* Create header backup on the partition. Until the volume is fully encrypted, the backup header will provide
  562     us with the master key, encrypted range, and other data for pause/resume operations. We cannot create the
  563     primary header until the entire partition is encrypted (because we encrypt backwards and the primary header
  564     area is occuppied by data until the very end of the process). */
  565 
  566     // Prepare the backup header
  567     for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++)
  568     {
  569         PCRYPTO_INFO dummyInfo = NULL;
  570 
  571         nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE,
  572             header,
  573             volParams->ea,
  574             FIRST_MODE_OF_OPERATION_ID,
  575             volParams->password,
  576             volParams->pkcs5,
  577             volParams->pim,
  578             wipePass == 0 ? NULL : (char *) cryptoInfo->master_keydata,
  579             &cryptoInfo,
  580             dataAreaSize,
  581             0,
  582             TC_VOLUME_DATA_OFFSET + dataAreaSize,   // Start of the encrypted area = the first byte of the backup heeader (encrypting from the end)
  583             0,  // No data is encrypted yet
  584             0,
  585             volParams->headerFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC,
  586             volParams->sectorSize,
  587             wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1));
  588 
  589         if (nStatus != 0)
  590             goto closing_seq;
  591 
  592         offset.QuadPart = TC_VOLUME_DATA_OFFSET + dataAreaSize;
  593 
  594         if (!SetFilePointerEx (dev, offset, NULL, FILE_BEGIN))
  595         {
  596             nStatus = ERR_OS_ERROR;
  597             goto closing_seq;
  598         }
  599 
  600         // Write the backup header to the partition
  601         if (!WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header))
  602         {
  603             nStatus = ERR_OS_ERROR;
  604             goto closing_seq;
  605         }
  606 
  607         // Fill the reserved sectors of the backup header area with random data
  608         nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, dataAreaSize, FALSE, TRUE);
  609 
  610         if (nStatus != ERR_SUCCESS)
  611             goto closing_seq;
  612 
  613         // write fake hidden volume header to protect against attacks that use statistical entropy
  614         // analysis to detect presence of hidden volumes
  615         nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE,
  616             header,
  617             volParams->ea,
  618             FIRST_MODE_OF_OPERATION_ID,
  619             NULL,
  620             0,
  621             0,
  622             NULL,
  623             &dummyInfo,
  624             dataAreaSize,
  625             dataAreaSize,
  626             TC_VOLUME_DATA_OFFSET + dataAreaSize,   // Start of the encrypted area = the first byte of the backup heeader (encrypting from the end)
  627             dataAreaSize,   // No data is encrypted yet
  628             0,
  629             volParams->headerFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC,
  630             volParams->sectorSize,
  631             wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1));
  632 
  633         if (nStatus != ERR_SUCCESS)
  634             goto closing_seq;
  635 
  636         crypto_close (dummyInfo);
  637 
  638         offset.QuadPart += TC_HIDDEN_VOLUME_HEADER_OFFSET;
  639 
  640         if (!SetFilePointerEx (dev, offset, NULL, FILE_BEGIN))
  641         {
  642             nStatus = ERR_OS_ERROR;
  643             goto closing_seq;
  644         }
  645 
  646         // Write the fake hidden backup header to the partition
  647         if (!WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header))
  648         {
  649             nStatus = ERR_OS_ERROR;
  650             goto closing_seq;
  651         }
  652 
  653     }
  654 
  655 
  656     /* Now we will try to decrypt the backup header to verify it has been correctly written. */
  657 
  658     nStatus = OpenBackupHeader (dev, volParams->volumePath, volParams->password, volParams->pkcs5, volParams->pim, &cryptoInfo2, NULL, deviceSize);
  659 
  660     if (nStatus != ERR_SUCCESS
  661         || cryptoInfo->EncryptedAreaStart.Value != cryptoInfo2->EncryptedAreaStart.Value
  662         || cryptoInfo2->EncryptedAreaStart.Value == 0)
  663     {
  664         if (nStatus == ERR_SUCCESS)
  665             nStatus = ERR_PARAMETER_INCORRECT;
  666 
  667         goto closing_seq;
  668     }
  669 
  670     // The backup header is valid so we know we should be able to safely resume in-place encryption
  671     // of this partition even if the system/app crashes.
  672 
  673 
  674 
  675     /* Conceal the NTFS filesystem (by performing an easy-to-undo modification). This will prevent Windows
  676     and apps from interfering with the volume until it has been fully encrypted. */
  677 
  678     nStatus = ConcealNTFS (dev);
  679 
  680     if (nStatus != ERR_SUCCESS)
  681         goto closing_seq;
  682 
  683 
  684 
  685     // /* If a drive letter is assigned to the device, remove it (so that users do not try to open it, which
  686     //would cause Windows to ask them if they want to format the volume and other dangerous things). */
  687 
  688     //if (driveLetter >= 0)
  689     //{
  690     //  char rootPath[] = { driveLetter + 'A', ':', '\\', 0 };
  691 
  692     //  // Try to remove the assigned drive letter
  693     //  if (DeleteVolumeMountPoint (rootPath))
  694     //      driveLetter = -1;
  695     //}
  696 
  697 
  698 
  699     /* Update config files and app data */
  700 
  701     // In the config file, increase the number of partitions where in-place encryption is in progress
  702 
  703     SaveNonSysInPlaceEncSettings (1, wipeAlgorithm, FALSE);
  704 
  705 
  706     // Add the wizard to the system startup sequence if appropriate
  707 
  708     if (!IsNonInstallMode ())
  709         ManageStartupSeqWiz (FALSE, L"/prinplace");
  710 
  711 
  712     nStatus = ERR_SUCCESS;
  713 
  714 
  715 closing_seq:
  716 
  717     dwError = GetLastError();
  718 
  719     if (cryptoInfo != NULL)
  720     {
  721         crypto_close (cryptoInfo);
  722         cryptoInfo = NULL;
  723     }
  724 
  725     if (cryptoInfo2 != NULL)
  726     {
  727         crypto_close (cryptoInfo2);
  728         cryptoInfo2 = NULL;
  729     }
  730 
  731     burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
  732     VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
  733     TCfree (header);
  734 
  735     if (dosDev[0])
  736         RemoveFakeDosName (volParams->volumePath, dosDev);
  737 
  738     *outHandle = dev;
  739 
  740     if (nStatus != ERR_SUCCESS)
  741         SetLastError (dwError);
  742 
  743     return nStatus;
  744 }
  745 
  746 
  747 int EncryptPartitionInPlaceResume (HANDLE dev,
  748                                    volatile FORMAT_VOL_PARAMETERS *volParams,
  749                                    WipeAlgorithmId wipeAlgorithm,
  750                                    volatile BOOL *bTryToCorrectReadErrors)
  751 {
  752     PCRYPTO_INFO masterCryptoInfo = NULL, headerCryptoInfo = NULL, tmpCryptoInfo = NULL;
  753     UINT64_STRUCT unitNo;
  754     char *buf = NULL, *header = NULL;
  755     byte *wipeBuffer = NULL;
  756     byte wipeRandChars [TC_WIPE_RAND_CHAR_COUNT];
  757     byte wipeRandCharsUpdate [TC_WIPE_RAND_CHAR_COUNT];
  758     WCHAR dosDev[TC_MAX_PATH] = {0};
  759     WCHAR devName[MAX_PATH] = {0};
  760     WCHAR deviceName[MAX_PATH];
  761     int nStatus = ERR_SUCCESS;
  762     __int64 deviceSize;
  763     uint64 remainingBytes, lastHeaderUpdateDistance = 0, zeroedSectorCount = 0;
  764     uint32 workChunkSize;
  765     DWORD dwError, dwResult;
  766     BOOL bPause = FALSE, bEncryptedAreaSizeChanged = FALSE;
  767     LARGE_INTEGER offset;
  768     int sectorSize;
  769     int i;
  770     DWORD n;
  771     WCHAR *devicePath = volParams->volumePath;
  772     Password *password = volParams->password;
  773     int pkcs5_prf = volParams->pkcs5;
  774     int pim = volParams->pim;
  775     DISK_GEOMETRY driveGeometry;
  776     HWND hwndDlg = volParams->hwndDlg;
  777 
  778 
  779     bInPlaceEncNonSysResumed = TRUE;
  780 
  781     buf = (char *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE);
  782     if (!buf)
  783     {
  784         nStatus = ERR_OUTOFMEMORY;
  785         goto closing_seq;
  786     }
  787 
  788     header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE);
  789     if (!header)
  790     {
  791         nStatus = ERR_OUTOFMEMORY;
  792         goto closing_seq;
  793     }
  794 
  795     VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
  796 
  797     if (wipeAlgorithm != TC_WIPE_NONE)
  798     {
  799         wipeBuffer = (byte *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE);
  800         if (!wipeBuffer)
  801         {
  802             nStatus = ERR_OUTOFMEMORY;
  803             goto closing_seq;
  804         }
  805     }
  806 
  807     headerCryptoInfo = crypto_open();
  808 
  809     if (headerCryptoInfo == NULL)
  810     {
  811         nStatus = ERR_OUTOFMEMORY;
  812         goto closing_seq;
  813     }
  814 
  815     deviceSize = GetDeviceSize (devicePath);
  816     if (deviceSize < 0)
  817     {
  818         // Cannot determine the size of the partition
  819         nStatus = ERR_OS_ERROR;
  820         goto closing_seq;
  821     }
  822 
  823     if (dev == INVALID_HANDLE_VALUE)
  824     {
  825         StringCchCopyW (deviceName, ARRAYSIZE(deviceName), devicePath);
  826 
  827         if (FakeDosNameForDevice (deviceName, dosDev, sizeof(dosDev),devName, sizeof(devName),FALSE) != 0)
  828         {
  829             nStatus = ERR_OS_ERROR;
  830             goto closing_seq;
  831         }
  832 
  833         dev = OpenPartitionVolume (hwndDlg, devName,
  834             FALSE,  // Do not require exclusive access
  835             FALSE,  // Do not require shared access
  836             TRUE,   // Ask the user to confirm shared access (if exclusive fails)
  837             FALSE,  // Do not append alternative instructions how to encrypt the data (to applicable error messages)
  838             FALSE); // Non-silent mode
  839 
  840         if (dev == INVALID_HANDLE_VALUE)
  841         {
  842             nStatus = ERR_DONT_REPORT;
  843             goto closing_seq;
  844         }
  845     }
  846 
  847     // This should never be needed, but is still performed for extra safety (without checking the result)
  848     DeviceIoControl (dev,
  849         FSCTL_ALLOW_EXTENDED_DASD_IO,
  850         NULL,
  851         0,
  852         NULL,
  853         0,
  854         &dwResult,
  855         NULL);
  856 
  857 
  858     if (!DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveGeometry, sizeof (driveGeometry), &dwResult, NULL))
  859     {
  860         nStatus = ERR_OS_ERROR;
  861         goto closing_seq;
  862     }
  863 
  864     sectorSize = driveGeometry.BytesPerSector;
  865 
  866 
  867     nStatus = OpenBackupHeader (dev, devicePath, password, pkcs5_prf, pim, &masterCryptoInfo, headerCryptoInfo, deviceSize);
  868 
  869     if (nStatus != ERR_SUCCESS)
  870         goto closing_seq;
  871 
  872 
  873 
  874     remainingBytes = masterCryptoInfo->VolumeSize.Value - masterCryptoInfo->EncryptedAreaLength.Value;
  875 
  876     lastHeaderUpdateDistance = 0;
  877 
  878 
  879     ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value);
  880 
  881     SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_ENCRYPTING);
  882 
  883     bFirstNonSysInPlaceEncResumeDone = TRUE;
  884 
  885 
  886     /* The in-place encryption core */
  887 
  888     while (remainingBytes > 0)
  889     {
  890         workChunkSize = (uint32) min (remainingBytes, TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE);
  891 
  892         if (workChunkSize % ENCRYPTION_DATA_UNIT_SIZE != 0)
  893         {
  894             nStatus = ERR_PARAMETER_INCORRECT;
  895             goto closing_seq;
  896         }
  897 
  898         unitNo.Value = (remainingBytes - workChunkSize + TC_VOLUME_DATA_OFFSET) / ENCRYPTION_DATA_UNIT_SIZE;
  899 
  900 
  901         // Read the plaintext into RAM
  902 
  903 inplace_enc_read:
  904 
  905         offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize - TC_VOLUME_DATA_OFFSET;
  906 
  907         if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0)
  908         {
  909             nStatus = ERR_OS_ERROR;
  910             goto closing_seq;
  911         }
  912 
  913         if (ReadFile (dev, buf, workChunkSize, &n, NULL) == 0)
  914         {
  915             // Read error
  916 
  917             DWORD dwTmpErr = GetLastError ();
  918 
  919             if (IsDiskReadError (dwTmpErr) && !bVolTransformThreadCancel)
  920             {
  921                 // Physical defect or data corruption
  922 
  923                 if (!*bTryToCorrectReadErrors)
  924                 {
  925                     *bTryToCorrectReadErrors = (AskWarnYesNo ("ENABLE_BAD_SECTOR_ZEROING", hwndDlg) == IDYES);
  926                 }
  927 
  928                 if (*bTryToCorrectReadErrors)
  929                 {
  930                     // Try to correct the read errors physically
  931 
  932                     offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize - TC_VOLUME_DATA_OFFSET;
  933 
  934                     nStatus = ZeroUnreadableSectors (dev, offset, workChunkSize, sectorSize, &zeroedSectorCount);
  935 
  936                     if (nStatus != ERR_SUCCESS)
  937                     {
  938                         // Due to write errors, we can't correct the read errors
  939                         nStatus = ERR_OS_ERROR;
  940                         goto closing_seq;
  941                     }
  942 
  943                     goto inplace_enc_read;
  944                 }
  945             }
  946 
  947             SetLastError (dwTmpErr);        // Preserve the original error code
  948 
  949             nStatus = ERR_OS_ERROR;
  950             goto closing_seq;
  951         }
  952 
  953         if (remainingBytes - workChunkSize < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE)
  954         {
  955             // We reached the inital portion of the filesystem, which we had concealed (in order to prevent
  956             // Windows from interfering with the volume). Now we need to undo that modification.
  957 
  958             for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE - (remainingBytes - workChunkSize); i++)
  959                 buf[i] ^= TC_NTFS_CONCEAL_CONSTANT;
  960         }
  961 
  962 
  963         // Encrypt the plaintext in RAM
  964 
  965         EncryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo);
  966 
  967 
  968         // If enabled, wipe the area to which we will write the ciphertext
  969 
  970         if (wipeAlgorithm != TC_WIPE_NONE)
  971         {
  972             byte wipePass;
  973             int wipePassCount = GetWipePassCount (wipeAlgorithm);
  974 
  975             if (wipePassCount <= 0)
  976             {
  977                 SetLastError (ERROR_INVALID_PARAMETER);
  978                 nStatus = ERR_PARAMETER_INCORRECT;
  979                 goto closing_seq;
  980             }
  981 
  982             offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize;
  983 
  984             for (wipePass = 1; wipePass <= wipePassCount; ++wipePass)
  985             {
  986                 if (!WipeBuffer (wipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, workChunkSize))
  987                 {
  988                     ULONG i;
  989                     for (i = 0; i < workChunkSize; ++i)
  990                     {
  991                         wipeBuffer[i] = buf[i] + wipePass;
  992                     }
  993 
  994                     EncryptDataUnits (wipeBuffer, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo);
  995                     memcpy (wipeRandCharsUpdate, wipeBuffer, sizeof (wipeRandCharsUpdate));
  996                 }
  997 
  998                 if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0
  999                     || WriteFile (dev, wipeBuffer, workChunkSize, &n, NULL) == 0)
 1000                 {
 1001                     // Write error
 1002                     dwError = GetLastError();
 1003 
 1004                     // Undo failed write operation
 1005                     if (workChunkSize > TC_VOLUME_DATA_OFFSET && SetFilePointerEx (dev, offset, NULL, FILE_BEGIN))
 1006                     {
 1007                         DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo);
 1008                         WriteFile (dev, buf + TC_VOLUME_DATA_OFFSET, workChunkSize - TC_VOLUME_DATA_OFFSET, &n, NULL);
 1009                     }
 1010 
 1011                     SetLastError (dwError);
 1012                     nStatus = ERR_OS_ERROR;
 1013                     goto closing_seq;
 1014                 }
 1015             }
 1016 
 1017             memcpy (wipeRandChars, wipeRandCharsUpdate, sizeof (wipeRandCharsUpdate));
 1018         }
 1019 
 1020 
 1021         // Write the ciphertext
 1022 
 1023         offset.QuadPart = masterCryptoInfo->EncryptedAreaStart.Value - workChunkSize;
 1024 
 1025         if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0)
 1026         {
 1027             nStatus = ERR_OS_ERROR;
 1028             goto closing_seq;
 1029         }
 1030 
 1031         if (WriteFile (dev, buf, workChunkSize, &n, NULL) == 0)
 1032         {
 1033             // Write error
 1034             dwError = GetLastError();
 1035 
 1036             // Undo failed write operation
 1037             if (workChunkSize > TC_VOLUME_DATA_OFFSET && SetFilePointerEx (dev, offset, NULL, FILE_BEGIN))
 1038             {
 1039                 DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo);
 1040                 WriteFile (dev, buf + TC_VOLUME_DATA_OFFSET, workChunkSize - TC_VOLUME_DATA_OFFSET, &n, NULL);
 1041             }
 1042 
 1043             SetLastError (dwError);
 1044             nStatus = ERR_OS_ERROR;
 1045             goto closing_seq;
 1046         }
 1047 
 1048 
 1049         masterCryptoInfo->EncryptedAreaStart.Value -= workChunkSize;
 1050         masterCryptoInfo->EncryptedAreaLength.Value += workChunkSize;
 1051 
 1052         remainingBytes -= workChunkSize;
 1053         lastHeaderUpdateDistance += workChunkSize;
 1054 
 1055         bEncryptedAreaSizeChanged = TRUE;
 1056 
 1057         if (lastHeaderUpdateDistance >= TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL)
 1058         {
 1059             nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize);
 1060 
 1061             if (nStatus != ERR_SUCCESS)
 1062                 goto closing_seq;
 1063 
 1064             lastHeaderUpdateDistance = 0;
 1065         }
 1066 
 1067         ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value);
 1068 
 1069         if (bVolTransformThreadCancel)
 1070         {
 1071             bPause = TRUE;
 1072             break;
 1073         }
 1074     }
 1075 
 1076     nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize);
 1077 
 1078 
 1079     if (nStatus != ERR_SUCCESS)
 1080         goto closing_seq;
 1081 
 1082 
 1083     if (!bPause)
 1084     {
 1085         /* The data area has been fully encrypted; create and write the primary volume header */
 1086 
 1087         SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINALIZING);
 1088 
 1089         for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++)
 1090         {
 1091             PCRYPTO_INFO dummyInfo = NULL;
 1092 
 1093             nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE,
 1094                 header,
 1095                 headerCryptoInfo->ea,
 1096                 headerCryptoInfo->mode,
 1097                 password,
 1098                 masterCryptoInfo->pkcs5,
 1099                 pim,
 1100                 (char *) masterCryptoInfo->master_keydata,
 1101                 &tmpCryptoInfo,
 1102                 masterCryptoInfo->VolumeSize.Value,
 1103                 0,
 1104                 masterCryptoInfo->EncryptedAreaStart.Value,
 1105                 masterCryptoInfo->EncryptedAreaLength.Value,
 1106                 masterCryptoInfo->RequiredProgramVersion,
 1107                 masterCryptoInfo->HeaderFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC,
 1108                 masterCryptoInfo->SectorSize,
 1109                 wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1));
 1110 
 1111             if (nStatus != ERR_SUCCESS)
 1112                 goto closing_seq;
 1113 
 1114 
 1115             offset.QuadPart = TC_VOLUME_HEADER_OFFSET;
 1116 
 1117             if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0
 1118                 || !WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header))
 1119             {
 1120                 nStatus = ERR_OS_ERROR;
 1121                 goto closing_seq;
 1122             }
 1123 
 1124             // Fill the reserved sectors of the header area with random data
 1125             nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, headerCryptoInfo, masterCryptoInfo->VolumeSize.Value, TRUE, FALSE);
 1126 
 1127             if (nStatus != ERR_SUCCESS)
 1128                 goto closing_seq;
 1129 
 1130             // write fake hidden volume header to protect against attacks that use statistical entropy
 1131             // analysis to detect presence of hidden volumes
 1132             nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE,
 1133                 header,
 1134                 headerCryptoInfo->ea,
 1135                 headerCryptoInfo->mode,
 1136                 NULL,
 1137                 0,
 1138                 0,
 1139                 NULL,
 1140                 &dummyInfo,
 1141                 masterCryptoInfo->VolumeSize.Value,
 1142                 masterCryptoInfo->VolumeSize.Value,
 1143                 masterCryptoInfo->EncryptedAreaStart.Value,
 1144                 masterCryptoInfo->EncryptedAreaLength.Value,
 1145                 masterCryptoInfo->RequiredProgramVersion,
 1146                 masterCryptoInfo->HeaderFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC,
 1147                 masterCryptoInfo->SectorSize,
 1148                 wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1));
 1149 
 1150             if (nStatus != ERR_SUCCESS)
 1151                 goto closing_seq;
 1152 
 1153             crypto_close (dummyInfo);
 1154 
 1155             offset.QuadPart += TC_HIDDEN_VOLUME_HEADER_OFFSET;
 1156 
 1157             if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0
 1158                 || !WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header))
 1159             {
 1160                 nStatus = ERR_OS_ERROR;
 1161                 goto closing_seq;
 1162             }
 1163         }
 1164 
 1165         // Update the configuration files
 1166 
 1167         SaveNonSysInPlaceEncSettings (-1, wipeAlgorithm, FALSE);
 1168 
 1169 
 1170 
 1171         SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINISHED);
 1172 
 1173         nStatus = ERR_SUCCESS;
 1174     }
 1175     else
 1176     {
 1177         // The process has been paused by the user or aborted by the wizard (e.g. on app exit)
 1178 
 1179         nStatus = ERR_USER_ABORT;
 1180 
 1181         SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PAUSED);
 1182     }
 1183 
 1184 
 1185 closing_seq:
 1186 
 1187     dwError = GetLastError();
 1188 
 1189     if (bEncryptedAreaSizeChanged
 1190         && dev != INVALID_HANDLE_VALUE
 1191         && masterCryptoInfo != NULL
 1192         && headerCryptoInfo != NULL
 1193         && deviceSize > 0)
 1194     {
 1195         // Execution of the core loop may have been interrupted due to an error or user action without updating the header
 1196         FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize);
 1197     }
 1198 
 1199     if (masterCryptoInfo != NULL)
 1200     {
 1201         crypto_close (masterCryptoInfo);
 1202         masterCryptoInfo = NULL;
 1203     }
 1204 
 1205     if (headerCryptoInfo != NULL)
 1206     {
 1207         crypto_close (headerCryptoInfo);
 1208         headerCryptoInfo = NULL;
 1209     }
 1210 
 1211     if (tmpCryptoInfo != NULL)
 1212     {
 1213         crypto_close (tmpCryptoInfo);
 1214         tmpCryptoInfo = NULL;
 1215     }
 1216 
 1217     if (dosDev[0])
 1218         RemoveFakeDosName (devicePath, dosDev);
 1219 
 1220     if (dev != INVALID_HANDLE_VALUE)
 1221     {
 1222         CloseHandle (dev);
 1223         dev = INVALID_HANDLE_VALUE;
 1224     }
 1225 
 1226     if (buf != NULL)
 1227         TCfree (buf);
 1228 
 1229     if (header != NULL)
 1230     {
 1231         burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 1232         VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 1233         TCfree (header);
 1234     }
 1235 
 1236     if (wipeBuffer != NULL)
 1237         TCfree (wipeBuffer);
 1238 
 1239     if (zeroedSectorCount > 0)
 1240     {
 1241         wchar_t msg[30000] = {0};
 1242         wchar_t sizeStr[500] = {0};
 1243 
 1244         GetSizeString (zeroedSectorCount * sectorSize, sizeStr, sizeof(sizeStr));
 1245 
 1246         StringCbPrintfW (msg, sizeof(msg),
 1247             GetString ("ZEROED_BAD_SECTOR_COUNT"),
 1248             zeroedSectorCount,
 1249             sizeStr);
 1250 
 1251         WarningDirect (msg, hwndDlg);
 1252     }
 1253 
 1254     if (nStatus != ERR_SUCCESS && nStatus != ERR_USER_ABORT)
 1255         SetLastError (dwError);
 1256 
 1257     return nStatus;
 1258 }
 1259 
 1260 int DecryptPartitionInPlace (volatile FORMAT_VOL_PARAMETERS *volParams, volatile BOOL *DiscardUnreadableEncryptedSectors)
 1261 {
 1262     HANDLE dev = INVALID_HANDLE_VALUE;
 1263     PCRYPTO_INFO masterCryptoInfo = NULL, headerCryptoInfo = NULL;
 1264     UINT64_STRUCT unitNo;
 1265     char *buf = NULL;
 1266     byte *tmpSectorBuf = NULL;
 1267     WCHAR dosDev[TC_MAX_PATH] = {0};
 1268     WCHAR devName[MAX_PATH] = {0};
 1269     WCHAR deviceName[MAX_PATH];
 1270     int nStatus = ERR_SUCCESS;
 1271     __int64 deviceSize;
 1272     uint64 remainingBytes, workChunkStartByteOffset, lastHeaderUpdateDistance = 0, skippedBadSectorCount = 0;
 1273     uint32 workChunkSize;
 1274     DWORD dwError, dwResult;
 1275     BOOL bPause = FALSE, bEncryptedAreaSizeChanged = FALSE;
 1276     LARGE_INTEGER offset;
 1277     int sectorSize;
 1278     int i;
 1279     DWORD n;
 1280     WCHAR *devicePath = volParams->volumePath;
 1281     Password *password = volParams->password;
 1282     HWND hwndDlg = volParams->hwndDlg;
 1283     int pkcs5_prf = volParams->pkcs5;
 1284     int pim = volParams->pim;
 1285     DISK_GEOMETRY driveGeometry;
 1286 
 1287 
 1288     buf = (char *) TCalloc (TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE);
 1289     if (!buf)
 1290     {
 1291         nStatus = ERR_OUTOFMEMORY;
 1292         goto closing_seq;
 1293     }
 1294 
 1295     headerCryptoInfo = crypto_open();
 1296 
 1297     if (headerCryptoInfo == NULL)
 1298     {
 1299         nStatus = ERR_OUTOFMEMORY;
 1300         goto closing_seq;
 1301     }
 1302 
 1303     deviceSize = GetDeviceSize (devicePath);
 1304     if (deviceSize < 0)
 1305     {
 1306         // Cannot determine the size of the partition
 1307         nStatus = ERR_OS_ERROR;
 1308         goto closing_seq;
 1309     }
 1310 
 1311 
 1312     // The wizard should have dismounted the TC volume if it was mounted, but for extra safety we will check this again.
 1313     if (IsMountedVolume (devicePath))
 1314     {
 1315         int driveLetter = GetMountedVolumeDriveNo (devicePath);
 1316 
 1317         if (driveLetter == -1
 1318             || !UnmountVolume (hwndDlg, driveLetter, TRUE))
 1319         {
 1320             handleWin32Error (hwndDlg, SRC_POS);
 1321             AbortProcess ("CANT_DISMOUNT_VOLUME");
 1322         }
 1323     }
 1324 
 1325 
 1326     StringCchCopyW (deviceName, ARRAYSIZE(deviceName), devicePath);
 1327 
 1328     if (FakeDosNameForDevice (deviceName, dosDev, sizeof(dosDev), devName, sizeof(devName), FALSE) != 0)
 1329     {
 1330         nStatus = ERR_OS_ERROR;
 1331         goto closing_seq;
 1332     }
 1333 
 1334     dev = OpenPartitionVolume (hwndDlg, devName,
 1335         TRUE,   // Require exclusive access
 1336         FALSE,  // Do not require shared access
 1337         TRUE,   // Ask the user to confirm shared access (if exclusive fails)
 1338         FALSE,  // Do not append alternative instructions how to encrypt the data (to applicable error messages)
 1339         FALSE); // Non-silent mode
 1340 
 1341     if (dev == INVALID_HANDLE_VALUE)
 1342     {
 1343         nStatus = ERR_DONT_REPORT;
 1344         goto closing_seq;
 1345     }
 1346 
 1347 
 1348 
 1349     // This should never be needed, but is still performed for extra safety (without checking the result)
 1350     DeviceIoControl (dev,
 1351         FSCTL_ALLOW_EXTENDED_DASD_IO,
 1352         NULL,
 1353         0,
 1354         NULL,
 1355         0,
 1356         &dwResult,
 1357         NULL);
 1358 
 1359 
 1360     if (!DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveGeometry, sizeof (driveGeometry), &dwResult, NULL))
 1361     {
 1362         nStatus = ERR_OS_ERROR;
 1363         goto closing_seq;
 1364     }
 1365 
 1366     if (    (driveGeometry.BytesPerSector == 0)
 1367         ||  (driveGeometry.BytesPerSector > TC_MAX_VOLUME_SECTOR_SIZE)
 1368         || (driveGeometry.BytesPerSector % ENCRYPTION_DATA_UNIT_SIZE != 0)
 1369         )
 1370     {
 1371         Error ("SECTOR_SIZE_UNSUPPORTED", hwndDlg);
 1372         nStatus = ERR_DONT_REPORT;
 1373         goto closing_seq;
 1374     }
 1375 
 1376     sectorSize = driveGeometry.BytesPerSector;
 1377 
 1378 
 1379     tmpSectorBuf = (byte *) TCalloc (sectorSize);
 1380     if (!tmpSectorBuf)
 1381     {
 1382         nStatus = ERR_OUTOFMEMORY;
 1383         goto closing_seq;
 1384     }
 1385 
 1386 
 1387     nStatus = OpenBackupHeader (dev, devicePath, password, pkcs5_prf, pim, &masterCryptoInfo, headerCryptoInfo, deviceSize);
 1388 
 1389     if (nStatus != ERR_SUCCESS)
 1390         goto closing_seq;
 1391 
 1392 
 1393     if (masterCryptoInfo->LegacyVolume)
 1394     {
 1395         Error ("NONSYS_INPLACE_DECRYPTION_BAD_VOL_FORMAT", hwndDlg);
 1396         nStatus = ERR_DONT_REPORT;
 1397         goto closing_seq;
 1398     }
 1399 
 1400     if (masterCryptoInfo->hiddenVolume)
 1401     {
 1402         Error ("NONSYS_INPLACE_DECRYPTION_CANT_DECRYPT_HID_VOL", hwndDlg);
 1403         nStatus = ERR_DONT_REPORT;
 1404         goto closing_seq;
 1405     }
 1406 
 1407     if (!bInPlaceEncNonSysResumed
 1408         && masterCryptoInfo->VolumeSize.Value == masterCryptoInfo->EncryptedAreaLength.Value)
 1409     {
 1410         /* Decryption started (not resumed) */
 1411 
 1412         if ((masterCryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0)
 1413         {
 1414             // The volume has not been encrypted in-place so it may contain a hidden volume.
 1415             // Ask the user to confirm it does not.
 1416 
 1417             char *tmpStr[] = {0,
 1418                 "CONFIRM_VOL_CONTAINS_NO_HIDDEN_VOL",
 1419                 "VOL_CONTAINS_NO_HIDDEN_VOL",
 1420                 "VOL_CONTAINS_A_HIDDEN_VOL",
 1421                 0};
 1422 
 1423             switch (AskMultiChoice ((void **) tmpStr, FALSE, hwndDlg))
 1424             {
 1425             case 1:
 1426                 // NOP
 1427                 break;
 1428             case 2:
 1429             default:
 1430                 // Cancel
 1431                 nStatus = ERR_DONT_REPORT;
 1432                 goto closing_seq;
 1433             }
 1434         }
 1435 
 1436         // Update config files and app data
 1437 
 1438         // In the config file, increase the number of partitions where in-place decryption is in progress
 1439         SaveNonSysInPlaceEncSettings (1, TC_WIPE_NONE, TRUE);
 1440 
 1441         // Add the wizard to the system startup sequence if appropriate
 1442         if (!IsNonInstallMode ())
 1443             ManageStartupSeqWiz (FALSE, L"/prinplace");
 1444     }
 1445 
 1446 
 1447 
 1448     bInPlaceEncNonSysResumed = TRUE;
 1449     bFirstNonSysInPlaceEncResumeDone = TRUE;
 1450 
 1451 
 1452     remainingBytes = masterCryptoInfo->EncryptedAreaLength.Value;
 1453 
 1454     lastHeaderUpdateDistance = 0;
 1455 
 1456 
 1457     ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value);
 1458 
 1459     SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_DECRYPTING);
 1460 
 1461 
 1462 
 1463     /* The in-place decryption core */
 1464 
 1465     while (remainingBytes > 0)
 1466     {
 1467         workChunkSize = (uint32) min (remainingBytes, TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE);
 1468 
 1469         if (workChunkSize % ENCRYPTION_DATA_UNIT_SIZE != 0)
 1470         {
 1471             nStatus = ERR_PARAMETER_INCORRECT;
 1472             goto closing_seq;
 1473         }
 1474 
 1475         workChunkStartByteOffset = masterCryptoInfo->EncryptedAreaStart.Value;
 1476 
 1477         unitNo.Value = workChunkStartByteOffset / ENCRYPTION_DATA_UNIT_SIZE;
 1478 
 1479 
 1480         // Read the ciphertext into RAM
 1481 
 1482         offset.QuadPart = workChunkStartByteOffset;
 1483 
 1484         if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0)
 1485         {
 1486             nStatus = ERR_OS_ERROR;
 1487             goto closing_seq;
 1488         }
 1489 
 1490         if (ReadFile (dev, buf, workChunkSize, &n, NULL) == 0)
 1491         {
 1492             // Read error
 1493 
 1494             DWORD dwTmpErr = GetLastError ();
 1495 
 1496             if (IsDiskReadError (dwTmpErr) && !bVolTransformThreadCancel)
 1497             {
 1498                 // Physical defect or data corruption
 1499 
 1500                 if (!*DiscardUnreadableEncryptedSectors)
 1501                 {
 1502                     *DiscardUnreadableEncryptedSectors = (AskWarnYesNo ("DISCARD_UNREADABLE_ENCRYPTED_SECTORS", hwndDlg) == IDYES);
 1503                 }
 1504 
 1505                 if (*DiscardUnreadableEncryptedSectors)
 1506                 {
 1507                     // Read the work chunk again, but this time each sector individually and skiping each bad sector
 1508 
 1509                     LARGE_INTEGER tmpSectorOffset;
 1510                     uint64 tmpSectorCount;
 1511                     uint64 tmpBufOffset = 0;
 1512                     DWORD tmpNbrReadBytes = 0;
 1513 
 1514                     tmpSectorOffset.QuadPart = offset.QuadPart;
 1515 
 1516                     for (tmpSectorCount = workChunkSize / sectorSize; tmpSectorCount > 0; --tmpSectorCount)
 1517                     {
 1518                         if (SetFilePointerEx (dev, tmpSectorOffset, NULL, FILE_BEGIN) == 0)
 1519                         {
 1520                             nStatus = ERR_OS_ERROR;
 1521                             goto closing_seq;
 1522                         }
 1523 
 1524                         if (ReadFile (dev, tmpSectorBuf, sectorSize, &tmpNbrReadBytes, NULL) == 0
 1525                             || tmpNbrReadBytes != (DWORD) sectorSize)
 1526                         {
 1527                             // Read error
 1528 
 1529                             // Clear the buffer so the content of each unreadable sector is replaced with decrypted all-zero blocks (producing pseudorandom data)
 1530                             memset (tmpSectorBuf, 0, sectorSize);
 1531 
 1532                             skippedBadSectorCount++;
 1533                         }
 1534 
 1535                         memcpy (buf + tmpBufOffset, tmpSectorBuf, sectorSize);
 1536 
 1537                         tmpSectorOffset.QuadPart += sectorSize;
 1538                         tmpBufOffset += sectorSize;
 1539                     }
 1540                 }
 1541                 else
 1542                 {
 1543                     SetLastError (dwTmpErr);        // Preserve the original error code
 1544 
 1545                     nStatus = ERR_OS_ERROR;
 1546                     goto closing_seq;
 1547                 }
 1548             }
 1549             else
 1550             {
 1551                 SetLastError (dwTmpErr);        // Preserve the original error code
 1552 
 1553                 nStatus = ERR_OS_ERROR;
 1554                 goto closing_seq;
 1555             }
 1556         }
 1557 
 1558         // Decrypt the ciphertext in RAM
 1559 
 1560         DecryptDataUnits ((byte *) buf, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, masterCryptoInfo);
 1561 
 1562 
 1563 
 1564         // Conceal initial portion of the filesystem
 1565 
 1566         if (workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE)
 1567         {
 1568             // We are decrypting the initial TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE bytes of the filesystem. We will
 1569             // conceal this portion to prevent Windows and applications from interfering with the volume.
 1570 
 1571             for (i = 0; i < min (TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET + workChunkSize); i++)
 1572                 buf[i] ^= TC_NTFS_CONCEAL_CONSTANT;
 1573         }
 1574 
 1575 
 1576         // Write the plaintext
 1577 
 1578         offset.QuadPart = workChunkStartByteOffset - TC_VOLUME_DATA_OFFSET;
 1579 
 1580         if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0)
 1581         {
 1582             nStatus = ERR_OS_ERROR;
 1583             goto closing_seq;
 1584         }
 1585 
 1586         if (WriteFile (dev, buf, workChunkSize, &n, NULL) == 0)
 1587         {
 1588             // Write error
 1589             nStatus = ERR_OS_ERROR;
 1590             goto closing_seq;
 1591         }
 1592 
 1593 
 1594         masterCryptoInfo->EncryptedAreaStart.Value += workChunkSize;
 1595         masterCryptoInfo->EncryptedAreaLength.Value -= workChunkSize;
 1596 
 1597         remainingBytes -= workChunkSize;
 1598         lastHeaderUpdateDistance += workChunkSize;
 1599 
 1600         bEncryptedAreaSizeChanged = TRUE;
 1601 
 1602         if (lastHeaderUpdateDistance >= TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL)
 1603         {
 1604             nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize);
 1605 
 1606             if (nStatus != ERR_SUCCESS)
 1607             {
 1608                 // Possible write error
 1609                 goto closing_seq;
 1610             }
 1611 
 1612             lastHeaderUpdateDistance = 0;
 1613         }
 1614 
 1615         ExportProgressStats (masterCryptoInfo->EncryptedAreaLength.Value, masterCryptoInfo->VolumeSize.Value);
 1616 
 1617         if (bVolTransformThreadCancel)
 1618         {
 1619             bPause = TRUE;
 1620             break;
 1621         }
 1622     }
 1623 
 1624     nStatus = FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize);
 1625 
 1626 
 1627     if (nStatus != ERR_SUCCESS)
 1628     {
 1629         // Possible write error
 1630         goto closing_seq;
 1631     }
 1632 
 1633 
 1634     if (!bPause)
 1635     {
 1636         /* Volume has been fully decrypted. */
 1637 
 1638 
 1639         // Prevent attempts to update volume header during the closing sequence
 1640         bEncryptedAreaSizeChanged = FALSE;
 1641 
 1642 
 1643         SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINALIZING);
 1644 
 1645 
 1646 
 1647         /* Undo concealing of the filesystem */
 1648 
 1649         nStatus = ConcealNTFS (dev);
 1650 
 1651         if (nStatus != ERR_SUCCESS)
 1652             goto closing_seq;
 1653 
 1654 
 1655 
 1656         /* Ovewrite the backup header and the remaining ciphertext with all-zero blocks (the primary header was overwritten with the decrypted data). */
 1657 
 1658         memset (tmpSectorBuf, 0, sectorSize);
 1659 
 1660         for (offset.QuadPart = masterCryptoInfo->VolumeSize.Value;
 1661             offset.QuadPart <= deviceSize - sectorSize;
 1662             offset.QuadPart += sectorSize)
 1663         {
 1664             if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0)
 1665             {
 1666                 nStatus = ERR_OS_ERROR;
 1667                 goto closing_seq;
 1668             }
 1669 
 1670             if (WriteFile (dev, tmpSectorBuf, sectorSize, &n, NULL) == 0)
 1671             {
 1672                 // Write error
 1673                 dwError = GetLastError();
 1674 
 1675                 SetLastError (dwError);
 1676                 nStatus = ERR_OS_ERROR;
 1677                 goto closing_seq;
 1678             }
 1679         }
 1680 
 1681 
 1682 
 1683         /* Update the configuration files */
 1684 
 1685         SaveNonSysInPlaceEncSettings (-1, TC_WIPE_NONE, TRUE);
 1686 
 1687 
 1688 
 1689         SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_FINISHED);
 1690 
 1691         nStatus = ERR_SUCCESS;
 1692 
 1693     }
 1694     else
 1695     {
 1696         // The process has been paused by the user or aborted by the wizard (e.g. on app exit)
 1697 
 1698         nStatus = ERR_USER_ABORT;
 1699 
 1700         SetNonSysInplaceEncUIStatus (NONSYS_INPLACE_ENC_STATUS_PAUSED);
 1701     }
 1702 
 1703     if (dev != INVALID_HANDLE_VALUE)
 1704     {
 1705         CloseHandle (dev);
 1706         dev = INVALID_HANDLE_VALUE;
 1707     }
 1708 
 1709 
 1710 closing_seq:
 1711 
 1712     dwError = GetLastError();
 1713 
 1714     if (bEncryptedAreaSizeChanged
 1715         && dev != INVALID_HANDLE_VALUE
 1716         && masterCryptoInfo != NULL
 1717         && headerCryptoInfo != NULL
 1718         && deviceSize > 0)
 1719     {
 1720         // Execution of the core loop may have been interrupted due to an error or user action without updating the header
 1721         FastVolumeHeaderUpdate (dev, headerCryptoInfo, masterCryptoInfo, deviceSize);
 1722     }
 1723 
 1724     if (dev != INVALID_HANDLE_VALUE)
 1725     {
 1726         CloseHandle (dev);
 1727         dev = INVALID_HANDLE_VALUE;
 1728     }
 1729 
 1730     if (masterCryptoInfo != NULL)
 1731     {
 1732         crypto_close (masterCryptoInfo);
 1733         masterCryptoInfo = NULL;
 1734     }
 1735 
 1736     if (headerCryptoInfo != NULL)
 1737     {
 1738         crypto_close (headerCryptoInfo);
 1739         headerCryptoInfo = NULL;
 1740     }
 1741 
 1742     if (dosDev[0])
 1743         RemoveFakeDosName (devicePath, dosDev);
 1744 
 1745     if (buf != NULL)
 1746     {
 1747         TCfree (buf);
 1748         buf = NULL;
 1749     }
 1750 
 1751     if (tmpSectorBuf != NULL)
 1752     {
 1753         TCfree (tmpSectorBuf);
 1754         tmpSectorBuf = NULL;
 1755     }
 1756 
 1757     if (skippedBadSectorCount > 0)
 1758     {
 1759         wchar_t msg[30000] = {0};
 1760         wchar_t sizeStr[500] = {0};
 1761 
 1762         GetSizeString (skippedBadSectorCount * sectorSize, sizeStr, sizeof(sizeStr));
 1763 
 1764         StringCbPrintfW (msg, sizeof(msg),
 1765             GetString ("SKIPPED_BAD_SECTOR_COUNT"),
 1766             skippedBadSectorCount,
 1767             sizeStr);
 1768 
 1769         WarningDirect (msg, hwndDlg);
 1770     }
 1771 
 1772     if (nStatus != ERR_SUCCESS && nStatus != ERR_USER_ABORT)
 1773         SetLastError (dwError);
 1774 
 1775     return nStatus;
 1776 }
 1777 
 1778 int FastVolumeHeaderUpdate (HANDLE dev, CRYPTO_INFO *headerCryptoInfo, CRYPTO_INFO *masterCryptoInfo, __int64 deviceSize)
 1779 {
 1780     LARGE_INTEGER offset;
 1781     DWORD n;
 1782     int nStatus = ERR_SUCCESS;
 1783     byte *header;
 1784     DWORD dwError;
 1785     uint32 headerCrc32;
 1786     byte *fieldPos;
 1787 
 1788     header = (byte *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 1789 
 1790     if (!header)
 1791         return ERR_OUTOFMEMORY;
 1792 
 1793     VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 1794 
 1795 
 1796     fieldPos = (byte *) header + TC_HEADER_OFFSET_ENCRYPTED_AREA_START;
 1797 
 1798     offset.QuadPart = deviceSize - TC_VOLUME_HEADER_GROUP_SIZE;
 1799 
 1800     if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0
 1801         || !ReadEffectiveVolumeHeader (TRUE, dev, header, &n) || n < TC_VOLUME_HEADER_EFFECTIVE_SIZE)
 1802     {
 1803         nStatus = ERR_OS_ERROR;
 1804         goto closing_seq;
 1805     }
 1806 
 1807 
 1808     DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo);
 1809 
 1810     if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x56455241)
 1811     {
 1812         nStatus = ERR_PARAMETER_INCORRECT;
 1813         goto closing_seq;
 1814     }
 1815 
 1816     mputInt64 (fieldPos, (masterCryptoInfo->EncryptedAreaStart.Value));
 1817     mputInt64 (fieldPos, (masterCryptoInfo->EncryptedAreaLength.Value));
 1818 
 1819     // We need to ensure the TC_HEADER_FLAG_NONSYS_INPLACE_ENC flag bit is set, because if volumes created by TC-format
 1820     // were decrypted in place, it would be possible to mount them partially encrypted and it wouldn't be possible
 1821     // to resume interrupted decryption after the wizard exits.
 1822     masterCryptoInfo->HeaderFlags |= TC_HEADER_FLAG_NONSYS_INPLACE_ENC;
 1823     fieldPos = (byte *) header + TC_HEADER_OFFSET_FLAGS;
 1824     mputLong (fieldPos, (masterCryptoInfo->HeaderFlags));
 1825 
 1826 
 1827     headerCrc32 = GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC);
 1828     fieldPos = (byte *) header + TC_HEADER_OFFSET_HEADER_CRC;
 1829     mputLong (fieldPos, headerCrc32);
 1830 
 1831     EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo);
 1832 
 1833 
 1834     if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0
 1835         || !WriteEffectiveVolumeHeader (TRUE, dev, header))
 1836     {
 1837         nStatus = ERR_OS_ERROR;
 1838         goto closing_seq;
 1839     }
 1840 
 1841 
 1842 closing_seq:
 1843 
 1844     dwError = GetLastError();
 1845 
 1846     burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 1847     VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 1848     TCfree (header);
 1849 
 1850     if (nStatus != ERR_SUCCESS)
 1851         SetLastError (dwError);
 1852 
 1853     return nStatus;
 1854 }
 1855 
 1856 
 1857 static HANDLE OpenPartitionVolume (HWND hwndDlg, const wchar_t *devName,
 1858                              BOOL bExclusiveRequired,
 1859                              BOOL bSharedRequired,
 1860                              BOOL bSharedRequiresConfirmation,
 1861                              BOOL bShowAlternativeSteps,
 1862                              BOOL bSilent)
 1863 {
 1864     HANDLE dev = INVALID_HANDLE_VALUE;
 1865     int retryCount = 0;
 1866 
 1867     if (bExclusiveRequired)
 1868         bSharedRequired = FALSE;
 1869 
 1870     if (bExclusiveRequired || !bSharedRequired)
 1871     {
 1872         // Exclusive access
 1873         // Note that when exclusive access is denied, it is worth retrying (usually succeeds after a few tries).
 1874         while (dev == INVALID_HANDLE_VALUE && retryCount++ < EXCL_ACCESS_MAX_AUTO_RETRIES)
 1875         {
 1876             dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
 1877 
 1878             if (retryCount > 1)
 1879                 Sleep (EXCL_ACCESS_AUTO_RETRY_DELAY);
 1880         }
 1881     }
 1882 
 1883     if (dev == INVALID_HANDLE_VALUE)
 1884     {
 1885         if (bExclusiveRequired)
 1886         {
 1887             if (!bSilent)
 1888             {
 1889                 handleWin32Error (hwndDlg, SRC_POS);
 1890 
 1891                 if (bShowAlternativeSteps)
 1892                     ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
 1893                 else
 1894                     Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg);
 1895             }
 1896             return INVALID_HANDLE_VALUE;
 1897         }
 1898 
 1899         // Shared mode
 1900         dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
 1901         if (dev != INVALID_HANDLE_VALUE)
 1902         {
 1903             if (bSharedRequiresConfirmation
 1904                 && !bSilent
 1905                 && AskWarnNoYes ("DEVICE_IN_USE_INPLACE_ENC", hwndDlg) == IDNO)
 1906             {
 1907                 CloseHandle (dev);
 1908                 return INVALID_HANDLE_VALUE;
 1909             }
 1910         }
 1911         else
 1912         {
 1913             if (!bSilent)
 1914             {
 1915                 handleWin32Error (MainDlg, SRC_POS);
 1916 
 1917                 if (bShowAlternativeSteps)
 1918                     ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL_ALT", TRUE);
 1919                 else
 1920                     Error ("INPLACE_ENC_CANT_ACCESS_OR_GET_INFO_ON_VOL", hwndDlg);
 1921             }
 1922             return INVALID_HANDLE_VALUE;
 1923         }
 1924     }
 1925 
 1926     return dev;
 1927 }
 1928 
 1929 
 1930 static int DismountFileSystem (HWND hwndDlg, HANDLE dev,
 1931                     int driveLetter,
 1932                     BOOL bForcedAllowed,
 1933                     BOOL bForcedRequiresConfirmation,
 1934                     BOOL bSilent)
 1935 {
 1936     int attempt;
 1937     BOOL bResult;
 1938     DWORD dwResult;
 1939 
 1940     CloseVolumeExplorerWindows (MainDlg, driveLetter);
 1941 
 1942     attempt = UNMOUNT_MAX_AUTO_RETRIES * 10;
 1943 
 1944     while (!(bResult = DeviceIoControl (dev, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwResult, NULL))
 1945         && attempt > 0)
 1946     {
 1947         Sleep (UNMOUNT_AUTO_RETRY_DELAY);
 1948         attempt--;
 1949     }
 1950 
 1951     if (!bResult)
 1952     {
 1953         if (!bForcedAllowed)
 1954         {
 1955             if (!bSilent)
 1956                 ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_LOCK_OR_DISMOUNT_FILESYS", TRUE);
 1957 
 1958             return ERR_DONT_REPORT;
 1959         }
 1960 
 1961         if (bForcedRequiresConfirmation
 1962             && !bSilent
 1963             && AskWarnYesNo ("VOL_LOCK_FAILED_OFFER_FORCED_DISMOUNT", hwndDlg) == IDNO)
 1964         {
 1965             return ERR_DONT_REPORT;
 1966         }
 1967     }
 1968 
 1969     // Dismount the volume
 1970 
 1971     attempt = UNMOUNT_MAX_AUTO_RETRIES * 10;
 1972 
 1973     while (!(bResult = DeviceIoControl (dev, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwResult, NULL))
 1974         && attempt > 0)
 1975     {
 1976         Sleep (UNMOUNT_AUTO_RETRY_DELAY);
 1977         attempt--;
 1978     }
 1979 
 1980     if (!bResult)
 1981     {
 1982         if (!bSilent)
 1983             ShowInPlaceEncErrMsgWAltSteps (hwndDlg, "INPLACE_ENC_CANT_LOCK_OR_DISMOUNT_FILESYS", TRUE);
 1984 
 1985         return ERR_DONT_REPORT;
 1986     }
 1987 
 1988     return ERR_SUCCESS;
 1989 }
 1990 
 1991 
 1992 // Easy-to-undo modification applied to conceal the NTFS filesystem (to prevent Windows and apps from
 1993 // interfering with it until the volume has been fully encrypted). Note that this function will precisely
 1994 // undo any modifications it made to the filesystem automatically if an error occurs when writing (including
 1995 // physical drive defects).
 1996 static int ConcealNTFS (HANDLE dev)
 1997 {
 1998     char buf [TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE];
 1999     DWORD nbrBytesProcessed, nbrBytesProcessed2;
 2000     int i;
 2001     LARGE_INTEGER offset;
 2002     DWORD dwError;
 2003 
 2004     offset.QuadPart = 0;
 2005 
 2006     if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0)
 2007         return ERR_OS_ERROR;
 2008 
 2009     if (ReadFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed, NULL) == 0)
 2010         return ERR_OS_ERROR;
 2011 
 2012     for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE; i++)
 2013         buf[i] ^= TC_NTFS_CONCEAL_CONSTANT;
 2014 
 2015     offset.QuadPart = 0;
 2016 
 2017     if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0)
 2018         return ERR_OS_ERROR;
 2019 
 2020     if (WriteFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed, NULL) == 0)
 2021     {
 2022         // One or more of the sectors is/are probably damaged and cause write errors.
 2023         // We must undo the modifications we made.
 2024 
 2025         dwError = GetLastError();
 2026 
 2027         for (i = 0; i < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE; i++)
 2028             buf[i] ^= TC_NTFS_CONCEAL_CONSTANT;
 2029 
 2030         offset.QuadPart = 0;
 2031 
 2032         do
 2033         {
 2034             Sleep (1);
 2035         }
 2036         while (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0
 2037             || WriteFile (dev, buf, TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE, &nbrBytesProcessed2, NULL) == 0);
 2038 
 2039         SetLastError (dwError);
 2040 
 2041         return ERR_OS_ERROR;
 2042     }
 2043 
 2044     return ERR_SUCCESS;
 2045 }
 2046 
 2047 
 2048 void ShowInPlaceEncErrMsgWAltSteps (HWND hwndDlg, char *iniStrId, BOOL bErr)
 2049 {
 2050     wchar_t msg[30000];
 2051 
 2052     StringCbCopyW (msg, sizeof(msg), GetString (iniStrId));
 2053 
 2054     StringCbCatW (msg, sizeof(msg), L"\n\n\n");
 2055     StringCbCatW (msg, sizeof(msg), GetString ("INPLACE_ENC_ALTERNATIVE_STEPS"));
 2056 
 2057     if (bErr)
 2058         ErrorDirect (msg, hwndDlg);
 2059     else
 2060         WarningDirect (msg, hwndDlg);
 2061 }
 2062 
 2063 
 2064 static void ExportProgressStats (__int64 bytesDone, __int64 totalSize)
 2065 {
 2066     NonSysInplaceEncBytesDone = bytesDone;
 2067     NonSysInplaceEncTotalSize = totalSize;
 2068 }
 2069 
 2070 
 2071 void SetNonSysInplaceEncUIStatus (int nonSysInplaceEncStatus)
 2072 {
 2073     NonSysInplaceEncStatus = nonSysInplaceEncStatus;
 2074 }
 2075 
 2076 
 2077 BOOL SaveNonSysInPlaceEncSettings (int delta, WipeAlgorithmId newWipeAlgorithm, BOOL bDecrypt)
 2078 {
 2079     int count;
 2080     char str[32];
 2081     WipeAlgorithmId savedWipeAlgorithm = TC_WIPE_NONE;
 2082 
 2083     if (delta == 0)
 2084         return TRUE;
 2085 
 2086     count = LoadNonSysInPlaceEncSettings (&savedWipeAlgorithm) + delta;
 2087 
 2088     if (count < 1)
 2089     {
 2090         RemoveNonSysInPlaceEncNotifications();
 2091         return TRUE;
 2092     }
 2093     else if (!bDecrypt)
 2094     {
 2095         if (newWipeAlgorithm != TC_WIPE_NONE)
 2096         {
 2097             StringCbPrintfA (str, sizeof(str), "%d", (int) newWipeAlgorithm);
 2098 
 2099             SaveBufferToFile (str, GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE), (DWORD) strlen(str), FALSE, FALSE);
 2100         }
 2101         else if (FileExists (GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE)))
 2102         {
 2103             _wremove (GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC_WIPE));
 2104         }
 2105     }
 2106 
 2107     StringCbPrintfA (str, sizeof(str), "%d", count);
 2108 
 2109     return SaveBufferToFile (str, GetConfigPath (TC_APPD_FILENAME_NONSYS_INPLACE_ENC), (DWORD) strlen(str), FALSE, FALSE);
 2110 }
 2111 
 2112 
 2113 // Repairs damaged sectors (i.e. those with read errors) by zeroing them.
 2114 // Note that this operating fails if there are any write errors.
 2115 int ZeroUnreadableSectors (HANDLE dev, LARGE_INTEGER startOffset, int64 size, int sectorSize, uint64 *zeroedSectorCount)
 2116 {
 2117     int nStatus;
 2118     DWORD n;
 2119     int64 sectorCount;
 2120     LARGE_INTEGER workOffset;
 2121     byte *sectorBuffer = NULL;
 2122     DWORD dwError;
 2123 
 2124     workOffset.QuadPart = startOffset.QuadPart;
 2125 
 2126     sectorBuffer = (byte *) TCalloc (sectorSize);
 2127 
 2128     if (!sectorBuffer)
 2129         return ERR_OUTOFMEMORY;
 2130 
 2131     if (SetFilePointerEx (dev, startOffset, NULL, FILE_BEGIN) == 0)
 2132     {
 2133         nStatus = ERR_OS_ERROR;
 2134         goto closing_seq;
 2135     }
 2136 
 2137 
 2138     for (sectorCount = size / sectorSize; sectorCount > 0; --sectorCount)
 2139     {
 2140         if (ReadFile (dev, sectorBuffer, sectorSize, &n, NULL) == 0)
 2141         {
 2142             memset (sectorBuffer, 0, sectorSize);
 2143 
 2144             if (SetFilePointerEx (dev, workOffset, NULL, FILE_BEGIN) == 0)
 2145             {
 2146                 nStatus = ERR_OS_ERROR;
 2147                 goto closing_seq;
 2148             }
 2149 
 2150             if (WriteFile (dev, sectorBuffer, sectorSize, &n, NULL) == 0)
 2151             {
 2152                 nStatus = ERR_OS_ERROR;
 2153                 goto closing_seq;
 2154             }
 2155             ++(*zeroedSectorCount);
 2156         }
 2157 
 2158         workOffset.QuadPart += n;
 2159     }
 2160 
 2161     nStatus = ERR_SUCCESS;
 2162 
 2163 closing_seq:
 2164 
 2165     dwError = GetLastError();
 2166 
 2167     if (sectorBuffer != NULL)
 2168         TCfree (sectorBuffer);
 2169 
 2170     if (nStatus != ERR_SUCCESS)
 2171         SetLastError (dwError);
 2172 
 2173     return nStatus;
 2174 }
 2175 
 2176 
 2177 static int OpenBackupHeader (HANDLE dev, const wchar_t *devicePath, Password *password, int pkcs5, int pim, PCRYPTO_INFO *retMasterCryptoInfo, CRYPTO_INFO *headerCryptoInfo, __int64 deviceSize)
 2178 {
 2179     LARGE_INTEGER offset;
 2180     DWORD n;
 2181     int nStatus = ERR_SUCCESS;
 2182     char *header;
 2183     DWORD dwError;
 2184 
 2185     header = (char *) TCalloc (TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 2186     if (!header)
 2187         return ERR_OUTOFMEMORY;
 2188 
 2189     VirtualLock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 2190 
 2191 
 2192 
 2193     offset.QuadPart = deviceSize - TC_VOLUME_HEADER_GROUP_SIZE;
 2194 
 2195     if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0
 2196         || !ReadEffectiveVolumeHeader (TRUE, dev, (byte *) header, &n) || n < TC_VOLUME_HEADER_EFFECTIVE_SIZE)
 2197     {
 2198         nStatus = ERR_OS_ERROR;
 2199         goto closing_seq;
 2200     }
 2201 
 2202 
 2203     nStatus = ReadVolumeHeader (FALSE, header, password, pkcs5, pim, FALSE, retMasterCryptoInfo, headerCryptoInfo);
 2204     if (nStatus != ERR_SUCCESS)
 2205         goto closing_seq;
 2206 
 2207 
 2208 closing_seq:
 2209 
 2210     dwError = GetLastError();
 2211 
 2212     burn (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 2213     VirtualUnlock (header, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
 2214     TCfree (header);
 2215 
 2216     dwError = GetLastError();
 2217 
 2218     if (nStatus != ERR_SUCCESS)
 2219         SetLastError (dwError);
 2220 
 2221     return nStatus;
 2222 }
 2223 
 2224 
 2225 static BOOL GetFreeClusterBeforeThreshold (HANDLE volumeHandle, int64 *freeCluster, int64 clusterThreshold)
 2226 {
 2227     const int bitmapSize = 65536;
 2228     byte bitmapBuffer[bitmapSize + sizeof (VOLUME_BITMAP_BUFFER)];
 2229     VOLUME_BITMAP_BUFFER *bitmap = (VOLUME_BITMAP_BUFFER *) bitmapBuffer;
 2230     STARTING_LCN_INPUT_BUFFER startLcn;
 2231     startLcn.StartingLcn.QuadPart = 0;
 2232 
 2233     DWORD bytesReturned;
 2234     while (DeviceIoControl (volumeHandle, FSCTL_GET_VOLUME_BITMAP, &startLcn, sizeof (startLcn), &bitmapBuffer, sizeof (bitmapBuffer), &bytesReturned, NULL)
 2235         || GetLastError() == ERROR_MORE_DATA)
 2236     {
 2237         for (int64 bitmapIndex = 0; bitmapIndex < min (bitmapSize, (bitmap->BitmapSize.QuadPart / 8)); ++bitmapIndex)
 2238         {
 2239             if (bitmap->StartingLcn.QuadPart + bitmapIndex * 8 >= clusterThreshold)
 2240                 goto err;
 2241 
 2242             if (bitmap->Buffer[bitmapIndex] != 0xff)
 2243             {
 2244                 for (int bit = 0; bit < 8; ++bit)
 2245                 {
 2246                     if ((bitmap->Buffer[bitmapIndex] & (1 << bit)) == 0)
 2247                     {
 2248                         *freeCluster = bitmap->StartingLcn.QuadPart + bitmapIndex * 8 + bit;
 2249 
 2250                         if (*freeCluster >= clusterThreshold)
 2251                             goto err;
 2252 
 2253                         return TRUE;
 2254                     }
 2255                 }
 2256             }
 2257         }
 2258 
 2259         startLcn.StartingLcn.QuadPart += min (bitmapSize * 8, bitmap->BitmapSize.QuadPart);
 2260     }
 2261 
 2262 err:
 2263     SetLastError (ERROR_DISK_FULL);
 2264     return FALSE;
 2265 }
 2266 
 2267 
 2268 static BOOL MoveClustersBeforeThresholdInDir (HANDLE volumeHandle, const wstring &directory, int64 clusterThreshold)
 2269 {
 2270     WIN32_FIND_DATAW findData;
 2271 
 2272     HANDLE findHandle = FindFirstFileW (((directory.size() <= 3 ? L"" : L"\\\\?\\") + directory + L"\\*").c_str(), &findData);
 2273     if (findHandle == INVALID_HANDLE_VALUE)
 2274         return TRUE;    // Error ignored
 2275 
 2276     finally_do_arg (HANDLE, findHandle, { FindClose (finally_arg); });
 2277 
 2278     // Find all files and directories
 2279     do
 2280     {
 2281         if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 2282         {
 2283             wstring subDir = findData.cFileName;
 2284 
 2285             if (subDir == L"." || subDir == L"..")
 2286                 continue;
 2287 
 2288             if (!MoveClustersBeforeThresholdInDir (volumeHandle, directory + L"\\" + subDir, clusterThreshold))
 2289                 return FALSE;
 2290         }
 2291 
 2292         DWORD access = FILE_READ_ATTRIBUTES;
 2293 
 2294         if (findData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
 2295             access = FILE_READ_DATA;
 2296 
 2297         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);
 2298         if (fsObject == INVALID_HANDLE_VALUE)
 2299             continue;
 2300 
 2301         finally_do_arg (HANDLE, fsObject, { CloseHandle (finally_arg); });
 2302 
 2303         STARTING_VCN_INPUT_BUFFER startVcn;
 2304         startVcn.StartingVcn.QuadPart = 0;
 2305         RETRIEVAL_POINTERS_BUFFER retPointers;
 2306         DWORD bytesReturned;
 2307 
 2308         // Find clusters allocated beyond the threshold
 2309         while (DeviceIoControl (fsObject, FSCTL_GET_RETRIEVAL_POINTERS, &startVcn, sizeof (startVcn), &retPointers, sizeof (retPointers), &bytesReturned, NULL)
 2310             || GetLastError() == ERROR_MORE_DATA)
 2311         {
 2312             if (retPointers.ExtentCount == 0)
 2313                 break;
 2314 
 2315             if (retPointers.Extents[0].Lcn.QuadPart != -1)
 2316             {
 2317                 int64 extentStartCluster = retPointers.Extents[0].Lcn.QuadPart;
 2318                 int64 extentLen = retPointers.Extents[0].NextVcn.QuadPart - retPointers.StartingVcn.QuadPart;
 2319                 int64 extentEndCluster = extentStartCluster + extentLen - 1;
 2320 
 2321                 if (extentEndCluster >= clusterThreshold)
 2322                 {
 2323                     // Move clusters before the threshold
 2324                     for (int64 movedCluster = max (extentStartCluster, clusterThreshold); movedCluster <= extentEndCluster; ++movedCluster)
 2325                     {
 2326                         for (int retry = 0; ; ++retry)
 2327                         {
 2328                             MOVE_FILE_DATA moveData;
 2329 
 2330                             if (GetFreeClusterBeforeThreshold (volumeHandle, &moveData.StartingLcn.QuadPart, clusterThreshold))
 2331                             {
 2332                                 moveData.FileHandle = fsObject;
 2333                                 moveData.StartingVcn.QuadPart = movedCluster - extentStartCluster + retPointers.StartingVcn.QuadPart;
 2334                                 moveData.ClusterCount = 1;
 2335 
 2336                                 if (DeviceIoControl (volumeHandle, FSCTL_MOVE_FILE, &moveData, sizeof (moveData), NULL, 0, &bytesReturned, NULL))
 2337                                     break;
 2338                             }
 2339 
 2340                             if (retry > 600)
 2341                                 return FALSE;
 2342 
 2343                             // There are possible race conditions as we work on a live filesystem
 2344                             Sleep (100);
 2345                         }
 2346                     }
 2347                 }
 2348             }
 2349 
 2350             startVcn.StartingVcn = retPointers.Extents[0].NextVcn;
 2351         }
 2352 
 2353     } while (FindNextFileW (findHandle, &findData));
 2354 
 2355     return TRUE;
 2356 }
 2357 
 2358 
 2359 BOOL MoveClustersBeforeThreshold (HANDLE volumeHandle, PWSTR volumeDevicePath, int64 clusterThreshold)
 2360 {
 2361     int drive = GetDiskDeviceDriveLetter (volumeDevicePath);
 2362     if (drive == -1)
 2363     {
 2364         SetLastError (ERROR_INVALID_PARAMETER);
 2365         return FALSE;
 2366     }
 2367 
 2368     wstring volumeRoot = L"X:";
 2369     volumeRoot[0] = L'A' + (wchar_t) drive;
 2370 
 2371     return MoveClustersBeforeThresholdInDir (volumeHandle, volumeRoot, clusterThreshold);
 2372 }