Ejemplo n.º 1
0
BiosResult WriteEncryptedSectors (uint16 sourceSegment, uint16 sourceOffset, byte drive, uint64 sector, uint16 sectorCount)
{
	BiosResult result;
	AcquireSectorBuffer();
	uint64 dataUnitNo;
	uint64 writeOffset;

	dataUnitNo = sector;
	writeOffset.HighPart = 0;
	writeOffset.LowPart = 0;

	if (BootCryptoInfo->hiddenVolume)
	{
		if (ReadWritePartiallyCoversEncryptedArea (sector, sectorCount))
			return BiosResultInvalidFunction;

		// Remap the request to the hidden volume
		writeOffset = HiddenVolumeStartSector;
		writeOffset -= EncryptedVirtualPartition.StartSector;
		dataUnitNo -= EncryptedVirtualPartition.StartSector;
		dataUnitNo += HiddenVolumeStartUnitNo;
	}

	while (sectorCount-- > 0)
	{
		CopyMemory (sourceSegment, sourceOffset, SectorBuffer, TC_LB_SIZE);

		if (drive == EncryptedVirtualPartition.Drive && sector >= EncryptedVirtualPartition.StartSector && sector <= EncryptedVirtualPartition.EndSector)
		{
			EncryptDataUnits (SectorBuffer, &dataUnitNo, 1, BootCryptoInfo);
		}

		result = WriteSectors (SectorBuffer, drive, sector + writeOffset, 1);

		if (result != BiosResultSuccess)
			break;

		++sector;
		++dataUnitNo;
		sourceOffset += TC_LB_SIZE;
	}

	ReleaseSectorBuffer();
	return result;
}
Ejemplo n.º 2
0
BOOL FlushFormatWriteBuffer (void *dev, char *write_buf, int *write_buf_cnt, __int64 *nSecNo, PCRYPTO_INFO cryptoInfo)
{
	UINT64_STRUCT unitNo;
	DWORD bytesWritten;

	if (*write_buf_cnt == 0)
		return TRUE;

	unitNo.Value = (*nSecNo * FormatSectorSize - *write_buf_cnt) / ENCRYPTION_DATA_UNIT_SIZE;

	EncryptDataUnits (write_buf, &unitNo, *write_buf_cnt / ENCRYPTION_DATA_UNIT_SIZE, cryptoInfo);

	if (WriteThreadRunning)
	{
		if (WaitForSingleObject (WriteBufferEmptyEvent, INFINITE) == WAIT_FAILED)
			return FALSE;
		
		if (WriteRequestResult != ERROR_SUCCESS)
		{
			SetEvent (WriteBufferEmptyEvent);
			SetLastError (WriteRequestResult);
			return FALSE;
		}

		memcpy (WriteThreadBuffer, write_buf, *write_buf_cnt);
		WriteRequestHandle = dev;
		WriteRequestSize = *write_buf_cnt;

		if (!SetEvent (WriteBufferFullEvent))
			return FALSE;
	}
	else
	{
		if (!WriteFile ((HANDLE) dev, write_buf, *write_buf_cnt, &bytesWritten, NULL))
			return FALSE;
	}

	*write_buf_cnt = 0;
	return TRUE;
}
Ejemplo n.º 3
0
static VOID MainThreadProc (PVOID threadArg)
{
	EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg;
	PLIST_ENTRY listEntry;
	EncryptedIoQueueItem *item;

	LARGE_INTEGER fragmentOffset;
	ULONG dataRemaining;
	PUCHAR activeFragmentBuffer = queue->FragmentBufferA;
	PUCHAR dataBuffer;
	EncryptedIoRequest *request;
	uint64 intersectStart;
	uint32 intersectLength;
	ULONGLONG addResult;
	HRESULT hResult;

	if (IsEncryptionThreadPoolRunning())
		KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY);

	while (!queue->ThreadExitRequested)
	{
		if (!NT_SUCCESS (KeWaitForSingleObject (&queue->MainThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL)))
			continue;

		while ((listEntry = ExInterlockedRemoveHeadList (&queue->MainThreadQueue, &queue->MainThreadQueueLock)))
		{
			PIRP irp = CONTAINING_RECORD (listEntry, IRP, Tail.Overlay.ListEntry);
			PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp);
			
			if (queue->Suspended)
				KeWaitForSingleObject (&queue->QueueResumedEvent, Executive, KernelMode, FALSE, NULL);

			item = GetPoolBuffer (queue, sizeof (EncryptedIoQueueItem));
			if (!item)
			{
				TCCompleteDiskIrp (irp, STATUS_INSUFFICIENT_RESOURCES, 0);
				DecrementOutstandingIoCount (queue);
				IoReleaseRemoveLock (&queue->RemoveLock, irp);

				continue;
			}

			item->Queue = queue;
			item->OriginalIrp = irp;
			item->Status = STATUS_SUCCESS;

			IoSetCancelRoutine (irp, NULL);
			if (irp->Cancel)
			{
				CompleteOriginalIrp (item, STATUS_CANCELLED, 0);
				continue;
			}

			switch (irpSp->MajorFunction)
			{
			case IRP_MJ_READ:
				item->Write = FALSE;
				item->OriginalOffset = irpSp->Parameters.Read.ByteOffset;
				item->OriginalLength = irpSp->Parameters.Read.Length;
				break;

			case IRP_MJ_WRITE:
				item->Write = TRUE;
				item->OriginalOffset = irpSp->Parameters.Write.ByteOffset;
				item->OriginalLength = irpSp->Parameters.Write.Length;
				break;

			default:
				CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0);
				continue;
			}

#ifdef TC_TRACE_IO_QUEUE
			item->OriginalIrpOffset = item->OriginalOffset;
#endif

			// Handle misaligned read operations to work around a bug in Windows System Assessment Tool which does not follow FILE_FLAG_NO_BUFFERING requirements when benchmarking disk devices
			if (queue->IsFilterDevice
				&& !item->Write
				&& item->OriginalLength > 0
				&& (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) == 0
				&& (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0)
			{
				byte *buffer;
				ULONG alignedLength;
				LARGE_INTEGER alignedOffset;
				hResult = ULongAdd(item->OriginalLength, ENCRYPTION_DATA_UNIT_SIZE, &alignedLength);
				if (hResult != S_OK)
				{
					CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0);
					continue;
				}

				alignedOffset.QuadPart = item->OriginalOffset.QuadPart & ~((LONGLONG) ENCRYPTION_DATA_UNIT_SIZE - 1);

				buffer = TCalloc (alignedLength);
				if (!buffer)
				{
					CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0);
					continue;
				}

				item->Status = TCReadDevice (queue->LowerDeviceObject, buffer, alignedOffset, alignedLength);

				if (NT_SUCCESS (item->Status))
				{
					UINT64_STRUCT dataUnit;

					dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority);
					if (!dataBuffer)
					{
						TCfree (buffer);
						CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0);
						continue;
					}

					if (queue->EncryptedAreaStart != -1 && queue->EncryptedAreaEnd != -1)
					{
						GetIntersection (alignedOffset.QuadPart, alignedLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength);
						if (intersectLength > 0)
						{
							dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE;
							DecryptDataUnits (buffer + (intersectStart - alignedOffset.QuadPart), &dataUnit, intersectLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo);
						}
					}

					memcpy (dataBuffer, buffer + (item->OriginalOffset.LowPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)), item->OriginalLength);
				}

				TCfree (buffer);
				CompleteOriginalIrp (item, item->Status, NT_SUCCESS (item->Status) ? item->OriginalLength : 0);
				continue;
			}

			// Validate offset and length
			if (item->OriginalLength == 0
				|| (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0
				|| (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0
				|| (	!queue->IsFilterDevice && 
						(	(S_OK != ULongLongAdd(item->OriginalOffset.QuadPart, item->OriginalLength, &addResult))
							||	(addResult > (ULONGLONG) queue->VirtualDeviceLength)
						)
					)
				)
			{
				CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0);
				continue;
			}

#ifdef TC_TRACE_IO_QUEUE
			Dump ("Q  %I64d [%I64d] %c len=%d\n", item->OriginalOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), item->Write ? 'W' : 'R', item->OriginalLength);
#endif

			if (!queue->IsFilterDevice)
			{
				// Adjust the offset for host file or device
				if (queue->CryptoInfo->hiddenVolume)
					hResult = ULongLongAdd(item->OriginalOffset.QuadPart, queue->CryptoInfo->hiddenVolumeOffset, &addResult);
				else
					hResult = ULongLongAdd(item->OriginalOffset.QuadPart, queue->CryptoInfo->volDataAreaOffset, &addResult); 

				if (hResult != S_OK)
				{
					CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0);
					continue;
				}
				else
					item->OriginalOffset.QuadPart = addResult;

				// Hidden volume protection
				if (item->Write && queue->CryptoInfo->bProtectHiddenVolume)
				{
					// If there has already been a write operation denied in order to protect the
					// hidden volume (since the volume mount time)
					if (queue->CryptoInfo->bHiddenVolProtectionAction)	
					{
						// Do not allow writing to this volume anymore. This is to fake a complete volume
						// or system failure (otherwise certain kinds of inconsistency within the file
						// system could indicate that this volume has used hidden volume protection).
						CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0);
						continue;
					}

					// Verify that no byte is going to be written to the hidden volume area
					if (RegionsOverlap ((unsigned __int64) item->OriginalOffset.QuadPart,
						(unsigned __int64) item->OriginalOffset.QuadPart + item->OriginalLength - 1,
						queue->CryptoInfo->hiddenVolumeOffset,
						(unsigned __int64) queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1))
					{
						Dump ("Hidden volume protection triggered: write %I64d-%I64d (protected %I64d-%I64d)\n", item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, queue->CryptoInfo->hiddenVolumeOffset, queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1);
						queue->CryptoInfo->bHiddenVolProtectionAction = TRUE;

						// Deny this write operation to prevent the hidden volume from being overwritten
						CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0);
						continue;
					}
				}
			}
			else if (item->Write
				&& RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET + TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE - 1))
			{
				// Prevent inappropriately designed software from damaging important data that may be out of sync with the backup on the Rescue Disk (such as the end of the encrypted area).
				Dump ("Preventing write to the system encryption key data area\n");
				CompleteOriginalIrp (item, STATUS_MEDIA_WRITE_PROTECTED, 0);
				continue;
			}
			else if (item->Write && IsHiddenSystemRunning()
				&& (RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, TC_SECTOR_SIZE_BIOS, TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS - 1)
				 || RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, GetBootDriveLength(), _I64_MAX)))
			{
				Dump ("Preventing write to boot loader or host protected area\n");
				CompleteOriginalIrp (item, STATUS_MEDIA_WRITE_PROTECTED, 0);
				continue;
			}

			dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority);

			if (dataBuffer == NULL)
			{
				CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0);
				continue;
			}

			// Divide data block to fragments to enable efficient overlapping of encryption and IO operations

			dataRemaining = item->OriginalLength;
			fragmentOffset = item->OriginalOffset;

			while (dataRemaining > 0)
			{
				BOOL isLastFragment = dataRemaining <= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE;
				
				ULONG dataFragmentLength = isLastFragment ? dataRemaining : TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE;
				activeFragmentBuffer = (activeFragmentBuffer == queue->FragmentBufferA ? queue->FragmentBufferB : queue->FragmentBufferA);

				InterlockedIncrement (&queue->IoThreadPendingRequestCount);

				// Create IO request
				request = GetPoolBuffer (queue, sizeof (EncryptedIoRequest));
				if (!request)
				{
					CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0);
					break;
				}
				request->Item = item;
				request->CompleteOriginalIrp = isLastFragment;
				request->Offset = fragmentOffset;
				request->Data = activeFragmentBuffer;
				request->OrigDataBufferFragment = dataBuffer;
				request->Length = dataFragmentLength;

				if (queue->IsFilterDevice)
				{
					if (queue->EncryptedAreaStart == -1 || queue->EncryptedAreaEnd == -1)
					{
						request->EncryptedLength = 0;
					}
					else
					{
						// Get intersection of data fragment with encrypted area
						GetIntersection (fragmentOffset.QuadPart, dataFragmentLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength);

						request->EncryptedOffset = intersectStart - fragmentOffset.QuadPart;
						request->EncryptedLength = intersectLength;
					}
				}
				else
				{
					request->EncryptedOffset = 0;
					request->EncryptedLength = dataFragmentLength;
				}

				AcquireFragmentBuffer (queue, activeFragmentBuffer);

				if (item->Write)
				{
					// Encrypt data
					memcpy (activeFragmentBuffer, dataBuffer, dataFragmentLength);

					if (request->EncryptedLength > 0)
					{
						UINT64_STRUCT dataUnit;
						ASSERT (request->EncryptedOffset + request->EncryptedLength <= request->Offset.QuadPart + request->Length);

						dataUnit.Value = (request->Offset.QuadPart + request->EncryptedOffset) / ENCRYPTION_DATA_UNIT_SIZE;

						if (queue->CryptoInfo->bPartitionInInactiveSysEncScope)
							dataUnit.Value += queue->CryptoInfo->FirstDataUnitNo.Value;
						else if (queue->RemapEncryptedArea)
							dataUnit.Value += queue->RemappedAreaDataUnitOffset;
								
						EncryptDataUnits (activeFragmentBuffer + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo);
					}
				}

				// Queue IO request
				ExInterlockedInsertTailList (&queue->IoThreadQueue, &request->ListEntry, &queue->IoThreadQueueLock);
				KeSetEvent (&queue->IoThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE);

				if (isLastFragment)
					break;

				dataRemaining -= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE;
				dataBuffer += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE;
				fragmentOffset.QuadPart += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE;
			}
		}
	}

	PsTerminateSystemThread (STATUS_SUCCESS);
}
Ejemplo n.º 4
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;
}