Exemple #1
0
/*
	ExpandVolume

	Sets the volume size in the volume header (and backup header) to a larger value,
	and resizes the filesystem within the volume (only NTFS supported)

	Parameters:

		hwndDlg : HWND
			[in] handle to progress dialog

		lpszVolume : char *
			[in] Pointer to a string that contains the path to the truecrypt volume

		pVolumePassword : Password *
			[in] Pointer to the volume password

		newHostSize : uint64
			[in] new value of the volume host size (can be zero for devices,
			     which means the volume should use all space of the host device)

		initFreeSpace : BOOL
			[in] if true, the new volume space will be initalized with random data

	Return value:

		int with Truecrypt error code (ERR_SUCCESS on success)

	Remarks: a lot of code is from TrueCrypt 'Common\Password.c' :: ChangePwd()

*/
static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePassword, int VolumePkcs5, int VolumePim, uint64 newHostSize, BOOL initFreeSpace)
{
    int nDosLinkCreated = 1, nStatus = ERR_OS_ERROR;
    wchar_t szDiskFile[TC_MAX_PATH], szCFDevice[TC_MAX_PATH];
    wchar_t szDosDevice[TC_MAX_PATH];
    char buffer[TC_VOLUME_HEADER_EFFECTIVE_SIZE];
    PCRYPTO_INFO cryptoInfo = NULL, ci = NULL;
    void *dev = INVALID_HANDLE_VALUE;
    DWORD dwError;
    BOOL bDevice;
    uint64 hostSize=0, newDataAreaSize, currentVolSize;
    DWORD HostSectorSize;
    FILETIME ftCreationTime;
    FILETIME ftLastWriteTime;
    FILETIME ftLastAccessTime;
    BOOL bTimeStampValid = FALSE;
    LARGE_INTEGER headerOffset;
    BOOL backupHeader;
    byte *wipeBuffer = NULL;
    uint32 workChunkSize = TC_VOLUME_HEADER_GROUP_SIZE;

    if (pVolumePassword->Length == 0) return -1;

    WaitCursor ();

    CreateFullVolumePath (szDiskFile, sizeof(szDiskFile), lpszVolume, &bDevice);

    if (bDevice == FALSE)
    {
        wcscpy (szCFDevice, szDiskFile);
    }
    else
    {
        nDosLinkCreated = FakeDosNameForDevice (szDiskFile, szDosDevice, sizeof(szDosDevice), szCFDevice, sizeof(szCFDevice), FALSE);

        if (nDosLinkCreated != 0) // note: nStatus == ERR_OS_ERROR
            goto error;
    }

    dev = CreateFile (szCFDevice, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

    if (dev == INVALID_HANDLE_VALUE)
        goto error;

    if (bDevice)
    {
        /* This is necessary to determine the hidden volume header offset */

        if (dev == INVALID_HANDLE_VALUE)
        {
            goto error;
        }
        else
        {
            PARTITION_INFORMATION diskInfo;
            DWORD dwResult;
            BOOL bResult;

            bResult = GetPartitionInfo (lpszVolume, &diskInfo);

            if (bResult)
            {
                hostSize = diskInfo.PartitionLength.QuadPart;
                HostSectorSize = TC_SECTOR_SIZE_FILE_HOSTED_VOLUME; //TO DO: get the real host disk sector size
            }
            else
            {
                DISK_GEOMETRY driveInfo;

                bResult = DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
                                           &driveInfo, sizeof (driveInfo), &dwResult, NULL);

                if (!bResult)
                    goto error;

                hostSize = driveInfo.Cylinders.QuadPart * driveInfo.BytesPerSector *
                           driveInfo.SectorsPerTrack * driveInfo.TracksPerCylinder;

                HostSectorSize = driveInfo.BytesPerSector;
            }

            if (hostSize == 0)
            {
                nStatus = ERR_VOL_SIZE_WRONG;
                goto error;
            }
        }
    }
    else
    {
        LARGE_INTEGER fileSize;
        if (!GetFileSizeEx (dev, &fileSize))
        {
            nStatus = ERR_OS_ERROR;
            goto error;
        }

        hostSize = fileSize.QuadPart;
        HostSectorSize = TC_SECTOR_SIZE_FILE_HOSTED_VOLUME; //TO DO: get the real host disk sector size
    }

    if (Randinit ())
    {
        if (CryptoAPILastError == ERROR_SUCCESS)
            nStatus = ERR_RAND_INIT_FAILED;
        else
            nStatus = ERR_CAPI_INIT_FAILED;
        goto error;
    }

    if (!bDevice && bPreserveTimestamp)
    {
        /* Remember the container modification/creation date and time, (used to reset file date and time of
        file-hosted volumes after password change (or attempt to), in order to preserve plausible deniability
        of hidden volumes (last password change time is stored in the volume header). */

        if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0)
        {
            bTimeStampValid = FALSE;
            MessageBoxW (hwndDlg, GetString ("GETFILETIME_FAILED_PW"), lpszTitle, MB_OK | MB_ICONEXCLAMATION);
        }
        else
            bTimeStampValid = TRUE;
    }

    // Seek the volume header
    headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET;

    if (!SetFilePointerEx ((HANDLE) dev, headerOffset, NULL, FILE_BEGIN))
    {
        nStatus = ERR_OS_ERROR;
        goto error;
    }

    /* Read in volume header */
    nStatus = _lread ((HFILE) dev, buffer, sizeof (buffer));
    if (nStatus != sizeof (buffer))
    {
        // Windows may report EOF when reading sectors from the last cluster of a device formatted as NTFS
        memset (buffer, 0, sizeof (buffer));
    }

    /* Try to decrypt the header */

    nStatus = ReadVolumeHeader (FALSE, buffer, pVolumePassword, VolumePkcs5, VolumePim, FALSE, &cryptoInfo, NULL);
    if (nStatus == ERR_CIPHER_INIT_WEAK_KEY)
        nStatus = 0;	// We can ignore this error here

    if (nStatus != 0)
    {
        cryptoInfo = NULL;
        goto error;
    }

    if (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_ENCRYPTED_SYSTEM)
    {
        nStatus = ERR_SYS_HIDVOL_HEAD_REENC_MODE_WRONG;
        goto error;
    }

    if (bDevice && newHostSize == 0)
    {
        // this means we shall take all host space as new volume size
        newHostSize = hostSize;
    }

    if ( newHostSize % cryptoInfo->SectorSize != 0  || newHostSize > TC_MAX_VOLUME_SIZE || (bDevice && newHostSize > hostSize) )
    {
        // 1. must be multiple of sector size
        // 2. truecrypt volume size limit
        // 3. for devices volume size can't be larger than host size
        cryptoInfo = NULL;
        nStatus = ERR_PARAMETER_INCORRECT;
        goto error;
    }

    newDataAreaSize = GetVolumeDataAreaSize (newHostSize, cryptoInfo->LegacyVolume);

    if (cryptoInfo->LegacyVolume)
    {
        if (bDevice)
        {
            if (initFreeSpace)
            {
                // unsupported
                cryptoInfo = NULL;
                nStatus = ERR_PARAMETER_INCORRECT;
                goto error;
            }
            else
            {
                // note: dummy value (only used for parameter checks)
                cryptoInfo->VolumeSize.Value = newDataAreaSize - TC_MINVAL_FS_EXPAND;
            }
        }
        else
        {
            cryptoInfo->VolumeSize.Value = GetVolumeDataAreaSize (hostSize, TRUE);
        }
    }

    currentVolSize = GetVolumeSizeByDataAreaSize (cryptoInfo->VolumeSize.Value, cryptoInfo->LegacyVolume);

    if ( newDataAreaSize < cryptoInfo->VolumeSize.Value + TC_MINVAL_FS_EXPAND )
    {
        // shrinking a volume or enlarging by less then TC_MINVAL_FS_EXPAND is not allowed
        cryptoInfo = NULL;
        nStatus = ERR_PARAMETER_INCORRECT;
        goto error;
    }

    InitProgressBar ( newHostSize, currentVolSize, FALSE, FALSE, FALSE, TRUE);

    if (bVolTransformThreadCancel)
    {
        SetLastError(0);
        nStatus = ERR_USER_ABORT;
        goto error;
    }

    if (!bDevice) {
        LARGE_INTEGER liNewSize;

        liNewSize.QuadPart=(LONGLONG)newHostSize;

        // Preallocate the file
        if (!SetFilePointerEx (dev, liNewSize, NULL, FILE_BEGIN)
                || !SetEndOfFile (dev)
                || SetFilePointer (dev, 0, NULL, FILE_BEGIN) != 0)
        {
            nStatus = ERR_OS_ERROR;
            goto error;
        }
    }

    if (initFreeSpace)
    {
        uint64 startSector;
        int64 num_sectors;

        // fill new space with random data
        startSector = currentVolSize/HostSectorSize ;
        num_sectors = (newHostSize/HostSectorSize) - startSector;

        if (bDevice && !StartFormatWriteThread())
        {
            nStatus = ERR_OS_ERROR;
            goto error;
        }

        DebugAddProgressDlgStatus(hwndDlg, L"Writing random data to new space ...\r\n");

        SetFormatSectorSize(HostSectorSize);
        nStatus = FormatNoFs (hwndDlg, startSector, num_sectors, dev, cryptoInfo, FALSE);

        dwError = GetLastError();
        StopFormatWriteThread();
        SetLastError (dwError);
    }
    else
    {
        UpdateProgressBar(newHostSize);
    }

    if (nStatus != ERR_SUCCESS)
    {
        dwError = GetLastError();
        DebugAddProgressDlgStatus(hwndDlg, L"Error: failed to write random data ...\r\n");
        if ( !bDevice ) {
            // restore original size of the container file
            LARGE_INTEGER liOldSize;
            liOldSize.QuadPart=(LONGLONG)hostSize;
            if (!SetFilePointerEx (dev, liOldSize, NULL, FILE_BEGIN) || !SetEndOfFile (dev))
            {
                DebugAddProgressDlgStatus(hwndDlg, L"Warning: failed to restore original size of the container file\r\n");
            }
        }
        SetLastError (dwError);
        goto error;
    }

    RandSetHashFunction (cryptoInfo->pkcs5);

    // Re-encrypt the volume header forn non-legacy volumes: backup header first
    backupHeader = TRUE;
    headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET + newHostSize - TC_VOLUME_HEADER_GROUP_SIZE;

    /* note: updating the header is not neccessary for legay volumes */
    while ( !cryptoInfo->LegacyVolume )
    {
        if (backupHeader)
            DebugAddProgressDlgStatus(hwndDlg, L"Writing re-encrypted backup header ...\r\n");
        else
            DebugAddProgressDlgStatus(hwndDlg, L"Writing re-encrypted primary header ...\r\n");

        // Prepare new volume header
        nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE,
                                              buffer,
                                              cryptoInfo->ea,
                                              cryptoInfo->mode,
                                              pVolumePassword,
                                              cryptoInfo->pkcs5,
                                              VolumePim,
                                              (char*)(cryptoInfo->master_keydata),
                                              &ci,
                                              newDataAreaSize,
                                              0, // hiddenVolumeSize
                                              cryptoInfo->EncryptedAreaStart.Value,
                                              newDataAreaSize,
                                              cryptoInfo->RequiredProgramVersion,
                                              cryptoInfo->HeaderFlags,
                                              cryptoInfo->SectorSize,
                                              TRUE ); // use slow poll

        if (ci != NULL)
            crypto_close (ci);

        if (nStatus != 0)
            goto error;

        if (!SetFilePointerEx ((HANDLE) dev, headerOffset, NULL, FILE_BEGIN))
        {
            nStatus = ERR_OS_ERROR;
            goto error;
        }

        nStatus = _lwrite ((HFILE) dev, buffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE);
        if (nStatus != TC_VOLUME_HEADER_EFFECTIVE_SIZE)
        {
            nStatus = ERR_OS_ERROR;
            goto error;
        }

        if ( ( backupHeader && !initFreeSpace )
                || ( bDevice
                     && !cryptoInfo->LegacyVolume
                     && !cryptoInfo->hiddenVolume
                     && cryptoInfo->HeaderVersion == 4	// BUG in TrueCrypt: doing this only for v4 make no sense
                     && (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) != 0
                     && (cryptoInfo->HeaderFlags & ~TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0 )
           )
        {
            //DebugAddProgressDlgStatus(hwndDlg, L"WriteRandomDataToReservedHeaderAreas() ...\r\n");
            nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, newDataAreaSize, !backupHeader, backupHeader);
            if (nStatus != ERR_SUCCESS)
                goto error;
        }

        FlushFileBuffers (dev);

        if (!backupHeader)
            break;

        backupHeader = FALSE;
        headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET; // offset for main header
    }

    /* header successfully updated */
    nStatus = ERR_SUCCESS;

    if (bVolTransformThreadCancel)
    {
        nStatus = ERR_USER_ABORT;
        goto error;
    }

    /* wipe old backup header */
    if ( !cryptoInfo->LegacyVolume )
    {
        byte wipeRandChars [TC_WIPE_RAND_CHAR_COUNT];
        byte wipeRandCharsUpdate [TC_WIPE_RAND_CHAR_COUNT];
        byte wipePass;
        UINT64_STRUCT unitNo;
        LARGE_INTEGER offset;
        WipeAlgorithmId wipeAlgorithm = TC_WIPE_35_GUTMANN;

        if (	!RandgetBytes (hwndDlg, wipeRandChars, TC_WIPE_RAND_CHAR_COUNT, TRUE)
                || !RandgetBytes (hwndDlg, wipeRandCharsUpdate, TC_WIPE_RAND_CHAR_COUNT, TRUE)
           )
        {
            nStatus = ERR_OS_ERROR;
            goto error;
        }

        DebugAddProgressDlgStatus(hwndDlg, L"Wiping old backup header ...\r\n");

        wipeBuffer = (byte *) TCalloc (workChunkSize);
        if (!wipeBuffer)
        {
            nStatus = ERR_OUTOFMEMORY;
            goto error;
        }

        offset.QuadPart = currentVolSize - TC_VOLUME_HEADER_GROUP_SIZE;
        unitNo.Value = offset.QuadPart;

        for (wipePass = 1; wipePass <= GetWipePassCount (wipeAlgorithm); ++wipePass)
        {
            if (!WipeBuffer (wipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, workChunkSize))
            {
                ULONG i;
                for (i = 0; i < workChunkSize; ++i)
                {
                    wipeBuffer[i] = wipePass;
                }

                EncryptDataUnits (wipeBuffer, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, cryptoInfo);
                memcpy (wipeRandCharsUpdate, wipeBuffer, sizeof (wipeRandCharsUpdate));
            }

            if ( !SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)
                    || _lwrite ((HFILE)dev, (LPCSTR)wipeBuffer, workChunkSize) == HFILE_ERROR
               )
            {
                // Write error
                DebugAddProgressDlgStatus(hwndDlg, L"Warning: Failed to wipe old backup header\r\n");
                MessageBoxW (hwndDlg, L"WARNING: Failed to wipe old backup header!\n\nIt may be possible to use the current volume password to decrypt the old backup header even after a future password change.\n", lpszTitle, MB_OK | MB_ICONEXCLAMATION);
                if (wipePass == 1)
                    continue; // retry once
                // non-critical error - it's better to continue
                nStatus = ERR_SUCCESS;
                goto error;
            }
            FlushFileBuffers(dev);
            // we don't check FlushFileBuffers() return code, because it fails for devices
            // (same implementation in password.c - a bug or not ???)
        }

        burn (wipeRandChars, TC_WIPE_RAND_CHAR_COUNT);
        burn (wipeRandCharsUpdate, TC_WIPE_RAND_CHAR_COUNT);
    }

error:
    dwError = GetLastError ();

    if (wipeBuffer)
    {
        burn (wipeBuffer, workChunkSize);
        TCfree (wipeBuffer);
        wipeBuffer = NULL;
    }

    burn (buffer, sizeof (buffer));

    if (cryptoInfo != NULL)
        crypto_close (cryptoInfo);

    if (bTimeStampValid)
    {
        // Restore the container timestamp (to preserve plausible deniability of possible hidden volume).
        if (SetFileTime (dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0)
            MessageBoxW (hwndDlg, GetString ("SETFILETIME_FAILED_PW"), lpszTitle, MB_OK | MB_ICONEXCLAMATION);
    }

    if (dev != INVALID_HANDLE_VALUE)
        CloseHandle ((HANDLE) dev);

    if (nDosLinkCreated == 0)
        RemoveFakeDosName (szDiskFile, szDosDevice);

    RandStop (FALSE);

    if (bVolTransformThreadCancel)
        nStatus = ERR_USER_ABORT;

    SetLastError (dwError);

    if (nStatus == ERR_SUCCESS)
    {
        nStatus = ExtendFileSystem (hwndDlg, lpszVolume, pVolumePassword, VolumePkcs5, VolumePim, newDataAreaSize);
    }

    return nStatus;
}
/*
	ExpandVolumeWizard

	Expands a trucrypt volume (wizard for user interface)

	Parameters:

		hwndDlg : HWND
			[in] handle to parent window (if any)

		szVolume : char *
			[in] Pointer to a string with the volume name (e.g. '\Device\Harddisk0\Partition1' or 'C:\topsecret.tc')

	Return value:

		none

*/
void ExpandVolumeWizard (HWND hwndDlg, char *lpszVolume)
{
	int nStatus = ERR_OS_ERROR;
	wchar_t szTmp[4096];
	Password VolumePassword;
	int VolumePkcs5 = 0;
	uint64 hostSize, volSize, hostSizeFree, maxSizeFS;
	BOOL bIsDevice, bIsLegacy;
	DWORD dwError;
	int driveNo;
	enum EV_FileSystem volFSType;
	char rootPath[] = "A:\\";

	switch (IsSystemDevicePath (lpszVolume, hwndDlg, TRUE))
	{
	case 1:
	case 2:
		MessageBoxW (hwndDlg, L"A VeraCrypt system volume can't be expanded.", lpszTitle, MB_OK|MB_ICONEXCLAMATION);
		goto ret;
	}

	EnableElevatedCursorChange (hwndDlg);
	WaitCursor();

	if (IsMountedVolume (lpszVolume))
	{
		Warning ("DISMOUNT_FIRST", hwndDlg);
		goto ret;
	}

	if (Randinit() != ERR_SUCCESS) {
		nStatus = ERR_PARAMETER_INCORRECT;
		goto error;
	}

	NormalCursor();

	// Ask the user if there is a hidden volume
	char *volTypeChoices[] = {0, "DOES_VOLUME_CONTAIN_HIDDEN", "VOLUME_CONTAINS_HIDDEN", "VOLUME_DOES_NOT_CONTAIN_HIDDEN", "IDCANCEL", 0};
	switch (AskMultiChoice ((void **) volTypeChoices, FALSE, hwndDlg))
	{
	case 1:
		MessageBoxW (hwndDlg, L"An outer volume containing a hidden volume can't be expanded, because this destroys the hidden volume.", lpszTitle, MB_OK|MB_ICONEXCLAMATION);
		goto ret;

	case 2:
		break;

	default:
		nStatus = ERR_SUCCESS;
		goto ret;
	}

	WaitCursor();

	nStatus = QueryVolumeInfo(hwndDlg,lpszVolume,&hostSizeFree,&maxSizeFS);

	if (nStatus!=ERR_SUCCESS)
	{
		nStatus = ERR_OS_ERROR;
		goto error;
	}

	NormalCursor();

	while (TRUE)
	{
		OpenVolumeContext expandVol;
		BOOL truecryptMode = FALSE;

		if (!VeraCryptExpander::ExtcvAskVolumePassword (hwndDlg, &VolumePassword, &VolumePkcs5, &truecryptMode, "ENTER_NORMAL_VOL_PASSWORD", FALSE))
		{
			goto ret;
		}

		EnableElevatedCursorChange (hwndDlg);
		WaitCursor();

		if (KeyFilesEnable && FirstKeyFile)
			KeyFilesApply (hwndDlg, &VolumePassword, FirstKeyFile);

		WaitCursor ();

		OpenVolumeThreadParam threadParam;
		threadParam.context = &expandVol;
		threadParam.volumePath = lpszVolume;
		threadParam.password = &VolumePassword;
		threadParam.pkcs5_prf = VolumePkcs5;
		threadParam.truecryptMode = FALSE;
		threadParam.write = FALSE;
		threadParam.preserveTimestamps = bPreserveTimestamp;
		threadParam.useBackupHeader = FALSE;
		threadParam.nStatus = &nStatus;

		ShowWaitDialog (hwndDlg, TRUE, OpenVolumeWaitThreadProc, &threadParam);

		NormalCursor ();

		dwError = GetLastError();

		if (nStatus == ERR_SUCCESS)
		{
			bIsDevice = expandVol.IsDevice;
			bIsLegacy = expandVol.CryptoInfo->LegacyVolume;
			hostSize = expandVol.HostSize;			
			VolumePkcs5 = expandVol.CryptoInfo->pkcs5;
			if ( bIsLegacy )
			{
				if ( bIsDevice ) 
					volSize = 0; // updated later
				else
					volSize = hostSize;
			}
			else
			{
				volSize = GetVolumeSizeByDataAreaSize (expandVol.CryptoInfo->VolumeSize.Value, bIsLegacy);
			}
			CloseVolume (&expandVol);
			break;
		}
		else if (nStatus != ERR_PASSWORD_WRONG)
		{
			SetLastError (dwError);
			goto error;
		}

		NormalCursor();

		handleError (hwndDlg, nStatus);
	}

	WaitCursor();

	// auto mount the volume to check the file system type
	nStatus=MountVolTemp(hwndDlg, lpszVolume, &driveNo, &VolumePassword, VolumePkcs5);

	if (nStatus != ERR_SUCCESS)
		goto error;

	rootPath[0] += driveNo;

	if ( !GetFileSystemType(rootPath,&volFSType) )
		volFSType = EV_FS_TYPE_RAW;

	if ( bIsLegacy && bIsDevice && volFSType == EV_FS_TYPE_NTFS )
	{
		uint64 NumberOfSectors;
		DWORD BytesPerSector;
		
		if ( !GetNtfsNumberOfSectors(rootPath, &NumberOfSectors, &BytesPerSector) )
			nStatus = ERR_OS_ERROR;

		// NTFS reported size does not include boot sector copy at volume end
		volSize = ( NumberOfSectors + 1 ) * BytesPerSector;
	}

	UnmountVolume (hwndDlg, driveNo, TRUE);

	NormalCursor();

	if (nStatus != ERR_SUCCESS)
		goto error;

	if ( bIsDevice && bIsLegacy && volFSType != EV_FS_TYPE_NTFS )
	{
		MessageBoxW (hwndDlg,
			L"Expanding a device hosted legacy volume with no NTFS file system\n"
			L"is unsupported.\n"
			L"Note that expanding the VeraCrypt volume itself is not neccessary\n"
			L"for legacy volumes.\n",
			lpszTitle, MB_OK|MB_ICONEXCLAMATION);
		goto ret;
	}

	// check if there is enough free space on host device/drive to expand the volume
	if ( (bIsDevice && hostSize < volSize + TC_MINVAL_FS_EXPAND) || (!bIsDevice && hostSizeFree < TC_MINVAL_FS_EXPAND) )
	{
		MessageBoxW (hwndDlg, L"Not enough free space to expand the volume", lpszTitle, MB_OK|MB_ICONEXCLAMATION);
		goto ret;
	}

	if (!bIsDevice && hostSize != volSize ) {
		// there is some junk data at the end of the volume
		if (MessageBoxW (hwndDlg, L"Warning: The container file is larger than the VeraCrypt volume area. The data after the VeraCrypt volume area will be overwritten.\n\nDo you want to continue?", lpszTitle, YES_NO|MB_ICONQUESTION|MB_DEFBUTTON2) == IDNO)
			goto ret;
	}

	switch (volFSType)
	{
	case EV_FS_TYPE_NTFS:
		break;
	case EV_FS_TYPE_FAT:
		if (MessageBoxW (hwndDlg,L"Warning: The VeraCrypt volume contains a FAT file system!\n\nOnly the VeraCrypt volume itself will be expanded, but not the file system.\n\nDo you want to continue?",
			lpszTitle, YES_NO|MB_ICONQUESTION|MB_DEFBUTTON2) == IDNO)
			goto ret;
		break;
	default:
		if (MessageBoxW (hwndDlg,L"Warning: The VeraCrypt volume contains an unknown or no file system!\n\nOnly the VeraCrypt volume itself will be expanded, the file system remains unchanged.\n\nDo you want to continue?",
			lpszTitle, YES_NO|MB_ICONQUESTION|MB_DEFBUTTON2) == IDNO)
			goto ret;
	}

	EXPAND_VOL_THREAD_PARAMS VolExpandParam;

	VolExpandParam.bInitFreeSpace = (bIsLegacy && bIsDevice) ? FALSE:TRUE;
	VolExpandParam.szVolumeName = lpszVolume;
	VolExpandParam.FileSystem = volFSType;
	VolExpandParam.pVolumePassword = &VolumePassword;
	VolExpandParam.VolumePkcs5 = VolumePkcs5;
	VolExpandParam.bIsDevice = bIsDevice;
	VolExpandParam.bIsLegacy = bIsLegacy;
	VolExpandParam.oldSize = bIsDevice ? volSize : hostSize;
	VolExpandParam.newSize = hostSize;
	VolExpandParam.hostSizeFree = hostSizeFree;

	while (1)
	{
		uint64 newVolumeSize;

		if (IDCANCEL == DialogBoxParamW (hInst,
			MAKEINTRESOURCEW (IDD_SIZE_DIALOG), hwndDlg,
			(DLGPROC) ExpandVolSizeDlgProc, (LPARAM) &VolExpandParam))
		{
			goto ret;
		}

		newVolumeSize = VolExpandParam.newSize;

		if ( !bIsDevice )
		{
			if ( newVolumeSize < hostSize + TC_MINVAL_FS_EXPAND)
			{
				swprintf(szTmp,L"New volume size too small, must be at least %I64u kB larger than the current size.",TC_MINVAL_FS_EXPAND/BYTES_PER_KB);
				MessageBoxW (hwndDlg, szTmp, lpszTitle, MB_OK | MB_ICONEXCLAMATION );
				continue;
			}

			if ( newVolumeSize - hostSize > hostSizeFree )
			{
				swprintf(szTmp,L"New volume size too large, not enough space on host drive.");
				MessageBoxW (hwndDlg, szTmp, lpszTitle, MB_OK | MB_ICONEXCLAMATION );
				continue;
			}

			if ( newVolumeSize>maxSizeFS )
			{
				swprintf(szTmp,L"Maximum file size of %I64u MB on host drive exceeded.",maxSizeFS/BYTES_PER_MB);
				MessageBoxW (hwndDlg, L"!\n",lpszTitle, MB_OK | MB_ICONEXCLAMATION );
				continue;
			}
		}

		if ( newVolumeSize > TC_MAX_VOLUME_SIZE )
		{
			// note: current limit TC_MAX_VOLUME_SIZE is 1 PetaByte
			swprintf(szTmp,L"Maximum VeraCrypt volume size of %I64u TB exceeded!\n",TC_MAX_VOLUME_SIZE/BYTES_PER_TB);
			MessageBoxW (hwndDlg, szTmp,lpszTitle, MB_OK | MB_ICONEXCLAMATION );
			if (bIsDevice)
				break; // TODO: ask to limit volume size to TC_MAX_VOLUME_SIZE
			continue;
		}

		break;
	}

	VolExpandParam.oldSize = volSize;

	// start progress dialog
	DialogBoxParamW (hInst,	MAKEINTRESOURCEW (IDD_EXPAND_PROGRESS_DLG), hwndDlg,
		(DLGPROC) ExpandVolProgressDlgProc, (LPARAM) &VolExpandParam );

ret:
	nStatus = ERR_SUCCESS;

error:

	if (nStatus != 0)
		handleError (hwndDlg, nStatus);

	burn (&VolumePassword, sizeof (VolumePassword));

	RestoreDefaultKeyFilesParam();
	RandStop (FALSE);
	NormalCursor();

	return;
}