www.pudn.com > truecrypt-4.2-source-code.zip > Format.c


/* Legal Notice: The source code contained in this file has been derived from 
   the source code of Encryption for the Masses 2.02a, which is Copyright (c) 
   1998-99 Paul Le Roux and which is covered by the 'License Agreement for 
   Encryption for the Masses'. Modifications and additions to that source code 
   contained in this file are Copyright (c) 2004-2006 TrueCrypt Foundation and 
   Copyright (c) 2004 TrueCrypt Team, and are covered by TrueCrypt License 2.0 
   the full text of which is contained in the file License.txt included in 
   TrueCrypt binary and source code distribution archives.  */ 
 
#include "Tcdefs.h" 
 
#include "Crypto.h" 
#include "Fat.h" 
#include "Format.h" 
#include "Volumes.h" 
#include "Common.h" 
 
#ifdef _WIN32 
#include "Apidrvr.h" 
#include "Dlgcode.h" 
#include "Language.h" 
#include "Progress.h" 
#include "Resource.h" 
#include "Random.h" 
 
int 
FormatVolume (char *lpszFilename, 
	      BOOL bDevice, 
		  char *volumePath, 
	      unsigned __int64 size, 
		  unsigned __int64 hiddenVolHostSize, 
	      Password *password, 
	      int ea, 
	      int pkcs5, 
		  BOOL quickFormat, 
		  BOOL sparseFileSwitch, 
		  int fileSystem, 
		  int clusterSize, 
		  wchar_t *summaryMsg, 
	      HWND hwndDlg, 
		  BOOL hiddenVol, 
		  int *realClusterSize) 
{ 
	int nStatus; 
	PCRYPTO_INFO cryptoInfo; 
	HANDLE dev = INVALID_HANDLE_VALUE; 
	DWORD dwError, dwThen, dwNow; 
	char header[SECTOR_SIZE]; 
	unsigned __int64 num_sectors, startSector; 
	fatparams ft; 
	FILETIME ftCreationTime; 
	FILETIME ftLastWriteTime; 
	FILETIME ftLastAccessTime; 
	BOOL bTimeStampValid = FALSE; 
	 
	if (!hiddenVol) 
		size -= HEADER_SIZE;	 
 
	num_sectors = size / SECTOR_SIZE; 
	VirtualLock (header, sizeof (header)); 
 
	/* Copies any header structures into header, but does not do any 
	   disk io */ 
	nStatus = VolumeWriteHeader (header, 
				     ea, 
					 LRW, // The only supported mode of operation when creating new volumes 
				     password, 
				     pkcs5, 
					 0, 
					 0, 
				     &cryptoInfo, 
					 hiddenVol ? size : 0, 
					 FALSE); 
 
	if (nStatus != 0) 
	{ 
		burn (header, sizeof (header)); 
		VirtualUnlock (header, sizeof (header)); 
		return nStatus; 
	} 
 
	if (bDevice) 
	{ 
		dev = CreateFile (lpszFilename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 
		if (dev == INVALID_HANDLE_VALUE) 
		{ 
			// Try opening the device in shared mode 
			dev = CreateFile (lpszFilename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 
			if (dev != INVALID_HANDLE_VALUE) 
			{ 
				if (IDNO == MessageBoxW (hwndDlg, GetString ("DEVICE_IN_USE_FORMAT"), lpszTitle, MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2)) 
				{ 
					CloseHandle (dev); 
					dev = INVALID_HANDLE_VALUE; 
				} 
			} 
		} 
	} 
	else 
	{ 
		// We could support FILE_ATTRIBUTE_HIDDEN as an option 
		// (Now if the container has hidden or system file attribute, the OS will not allow 
		// overwritting it; so the user will have to delete it manually). 
		dev = CreateFile (lpszFilename, GENERIC_WRITE, 
			hiddenVol ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : 0, 
			NULL, hiddenVol ? OPEN_EXISTING : CREATE_ALWAYS, 0, NULL); 
 
		if (dev == INVALID_HANDLE_VALUE) 
		{ 
			handleWin32Error (hwndDlg); 
			nStatus = ERR_OS_ERROR;  
			goto error; 
		} 
 
		if (!hiddenVol) 
		{ 
			LARGE_INTEGER volumeSize; 
			volumeSize.QuadPart = size + HEADER_SIZE; 
 
			if (sparseFileSwitch && quickFormat) 
			{ 
				// Create as sparse file container 
				DWORD tmp; 
				if (!DeviceIoControl (dev, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &tmp, NULL)) 
				{ 
					handleWin32Error (hwndDlg); 
					nStatus = ERR_OS_ERROR;  
					goto error; 
				} 
			} 
 
			// Preallocate the file 
			if (!SetFilePointerEx (dev, volumeSize, NULL, FILE_BEGIN) 
				|| !SetEndOfFile (dev) 
				|| SetFilePointer (dev, 0, NULL, FILE_BEGIN) != 0) 
			{ 
				handleWin32Error (hwndDlg); 
				nStatus = ERR_OS_ERROR; 
				goto error; 
			} 
		} 
	} 
 
	if (hiddenVol && !bDevice && bPreserveTimestamp) 
	{ 
		/* Remember the container timestamp (used to reset file date and time of file-hosted 
		containers to preserve plausible deniability of hidden volume)  */ 
		if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) 
		{ 
			bTimeStampValid = FALSE; 
			MessageBoxW (hwndDlg, GetString ("GETFILETIME_FAILED_IMPLANT"), lpszTitle, MB_OK | MB_ICONEXCLAMATION); 
		} 
		else 
			bTimeStampValid = TRUE; 
	} 
 
	KillTimer (hwndDlg, 0xff); 
 
	InitProgressBar (num_sectors); 
	dwThen = GetTickCount (); 
 
 
	/* Volume header */ 
 
	// Hidden volume setup 
	if (hiddenVol) 
	{ 
		// Check hidden volume size 
		if (hiddenVolHostSize < MIN_HIDDEN_VOLUME_HOST_SIZE || hiddenVolHostSize > MAX_HIDDEN_VOLUME_HOST_SIZE) 
		{		 
			nStatus = ERR_VOL_SIZE_WRONG; 
			goto error; 
		} 
 
		// Seek to hidden volume header location 
		if (!SeekHiddenVolHeader ((HFILE) dev, hiddenVolHostSize, bDevice)) 
		{ 
			nStatus = ERR_VOL_SEEKING; 
			goto error; 
		} 
 
	} 
 
	// Write the volume header 
	if (_lwrite ((HFILE) dev, header, HEADER_SIZE) == HFILE_ERROR) 
		return ERR_OS_ERROR; 
 
 
	/* Data area */ 
 
	startSector = 1;	// Data area of normal volume starts right after volume header 
 
	if (hiddenVol) 
	{ 
		// Calculate data area position of hidden volume 
		unsigned __int64 startOffset = hiddenVolHostSize - size - HIDDEN_VOL_HEADER_OFFSET; 
		cryptoInfo->hiddenVolumeOffset = startOffset; 
 
		// Validate the offset 
		if (startOffset % SECTOR_SIZE != 0) 
		{ 
			nStatus = ERR_VOL_SIZE_WRONG;  
			goto error; 
		} 
 
		startSector = startOffset / SECTOR_SIZE;	 
		quickFormat = TRUE;		// To entirely format a hidden volume would be redundant 
	} 
 
	// Format filesystem 
 
	switch (fileSystem) 
	{ 
	case FILESYS_NONE: 
	case FILESYS_NTFS: // NTFS volume is just prepared for quick format performed by system 
		nStatus = FormatNoFs (startSector, num_sectors, dev, cryptoInfo, quickFormat); 
		break; 
 
	case FILESYS_FAT: 
		if (num_sectors > 0xFFFFffff) 
		{ 
			nStatus = ERR_VOL_SIZE_WRONG;  
			goto error; 
		} 
 
		// Calculate the fats, root dir etc 
		ft.num_sectors = (unsigned int) (num_sectors); 
		ft.cluster_size = clusterSize; 
		memcpy (ft.volume_name, "NO NAME    ", 11); 
		GetFatParams (&ft);  
		*realClusterSize = ft.cluster_size * SECTOR_SIZE; 
 
		nStatus = FormatFat (startSector, &ft, (void *) dev, cryptoInfo, quickFormat); 
		break; 
	} 
 
error: 
 
	dwNow = GetTickCount (); 
 
	burn (header, sizeof (header)); 
	VirtualUnlock (header, sizeof (header)); 
 
	crypto_close (cryptoInfo); 
 
	if (bTimeStampValid) 
	{ 
		// Restore the container timestamp (to preserve plausible deniability of the hidden volume)  
		if (SetFileTime (dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) 
			MessageBoxW (hwndDlg, GetString ("SETFILETIME_FAILED_IMPLANT"), lpszTitle, MB_OK | MB_ICONEXCLAMATION); 
	} 
 
	if (!bDevice && !hiddenVol && nStatus != 0) 
	{ 
		// Remove preallocated part before closing file handle if format failed 
		if (SetFilePointer (dev, 0, NULL, FILE_BEGIN) == 0) 
			SetEndOfFile (dev); 
	} 
 
	CloseHandle (dev); 
 
	dwError = GetLastError(); 
 
	if (nStatus != 0) 
		SetLastError(dwError); 
	else 
	{ 
		switch (fileSystem) 
		{ 
		case FILESYS_NONE: 
			swprintf (summaryMsg 
				, GetString ("FORMAT_STAT") 
				, num_sectors, num_sectors*512/1024/1024 
				, GetString ("NONE") 
				, (dwNow - dwThen)/1000); 
			break; 
 
		case FILESYS_FAT: 
			swprintf (summaryMsg  
				, GetString ("FORMAT_STAT_FAT") 
				, ft.num_sectors, ((__int64) ft.num_sectors*512)/1024/1024, ft.size_fat 
				, (int) (512*ft.fats*ft.fat_length), 
				(int) (512*ft.cluster_size), ft.cluster_count, 
				(dwNow - dwThen)/1000); 
			break; 
 
		case FILESYS_NTFS: 
			{ 
				// NTFS format is performed by system so we first need to mount the volume 
				int driveNo = GetLastAvailableDrive (); 
				MountOptions mountOptions; 
				ZeroMemory (&mountOptions, sizeof (mountOptions)); 
				 
				if (driveNo == -1) 
				{ 
					MessageBoxW (hwndDlg, GetString ("NO_FREE_DRIVES"), lpszTitle, ICON_HAND); 
					MessageBoxW (hwndDlg, GetString ("FORMAT_NTFS_STOP"), lpszTitle, ICON_HAND); 
 
					return ERR_NO_FREE_DRIVES; 
				} 
 
				mountOptions.ReadOnly = FALSE; 
				mountOptions.Removable = FALSE; 
				mountOptions.ProtectHiddenVolume = FALSE; 
				mountOptions.PreserveTimestamp = bPreserveTimestamp; 
 
				if (MountVolume (hwndDlg, driveNo, volumePath, password, FALSE, TRUE, &mountOptions, FALSE, TRUE) < 1) 
				{ 
					MessageBoxW (hwndDlg, GetString ("CANT_MOUNT_VOLUME"), lpszTitle, ICON_HAND); 
					MessageBoxW (hwndDlg, GetString ("FORMAT_NTFS_STOP"), lpszTitle, ICON_HAND); 
					return ERR_VOL_MOUNT_FAILED; 
				} 
 
				// Quick-format volume as NTFS 
				if (!FormatNtfs (driveNo, clusterSize)) 
				{ 
					MessageBoxW (hwndDlg, GetString ("FORMAT_NTFS_FAILED"), lpszTitle, MB_ICONERROR); 
				 
					if (!UnmountVolume (hwndDlg, driveNo, FALSE)) 
						MessageBoxW (hwndDlg, GetString ("CANT_DISMOUNT_VOLUME"), lpszTitle, ICON_HAND); 
 
					return ERR_VOL_FORMAT_BAD; 
				} 
 
				if (!UnmountVolume (hwndDlg, driveNo, FALSE)) 
					MessageBoxW (hwndDlg, GetString ("CANT_DISMOUNT_VOLUME"), lpszTitle, ICON_HAND); 
 
				dwNow = GetTickCount (); 
 
				swprintf (summaryMsg, 
					GetString ("FORMAT_STAT") 
					, num_sectors 
					, num_sectors*512/1024/1024 
					, L"NTFS" 
					, (dwNow - dwThen)/1000); 
 
				break; 
			} 
		} 
	} 
 
	return nStatus; 
} 
 
#endif 
 
int FormatNoFs (unsigned __int64 startSector, __int64 num_sectors, void * dev, PCRYPTO_INFO cryptoInfo, BOOL quickFormat) 
{ 
	int write_buf_cnt = 0; 
	char sector[SECTOR_SIZE], *write_buf; 
	unsigned __int64 nSecNo = startSector; 
	int retVal; 
	char temporaryKey[DISKKEY_SIZE]; 
 
#ifdef _WIN32 
	LARGE_INTEGER startOffset; 
	LARGE_INTEGER newOffset; 
 
	// Seek to start sector 
	startOffset.QuadPart = startSector * SECTOR_SIZE; 
	if (!SetFilePointerEx ((HANDLE) dev, startOffset, &newOffset, FILE_BEGIN) 
		|| newOffset.QuadPart != startOffset.QuadPart) 
	{ 
		return ERR_VOL_SEEKING; 
	} 
#endif 
 
	write_buf = (char *) TCalloc (WRITE_BUF_SIZE); 
	memset (sector, 0, sizeof (sector)); 
 
	/* Fill the rest of the data area with random data */ 
 
	if(!quickFormat) 
	{ 
		/* Generate a random temporary key set to be used for "dummy" encryption that will fill 
		the free disk space (data area) with random data.  This is necessary for plausible 
		deniability of hidden volumes (and also reduces the amount of predictable plaintext 
		within the volume). */ 
 
		RandgetBytes (temporaryKey, EAGetKeySize (cryptoInfo->ea), FALSE);	// Temporary master key 
		RandgetBytes (cryptoInfo->iv, sizeof cryptoInfo->iv, FALSE);		// Secondary key (LRW mode) 
 
		retVal = EAInit (cryptoInfo->ea, temporaryKey, cryptoInfo->ks); 
		if (retVal != 0) 
		{ 
			burn (temporaryKey, sizeof(temporaryKey)); 
			return retVal; 
		} 
		if (!EAInitMode (cryptoInfo)) 
		{ 
			burn (temporaryKey, sizeof(temporaryKey)); 
			return ERR_MODE_INIT_FAILED; 
		} 
 
		while (num_sectors--) 
		{ 
			/* Generate random plaintext. Note that reused plaintext blocks are not a concern 
			here since LRW mode is designed to hide patterns. Furthermore, patterns in plaintext 
			do occur commonly on media in the "real world", so it might actually be a fatal 
			mistake to try to avoid them completely. */ 
#if defined(RNG_POOL_SIZE) && RNG_POOL_SIZE < SECTOR_SIZE 
			RandpeekBytes (sector, RNG_POOL_SIZE); 
			RandpeekBytes (sector + RNG_POOL_SIZE, SECTOR_SIZE - RNG_POOL_SIZE); 
#else 
			if ((nSecNo & 0xff) == 0) 
				RandpeekBytes (sector, SECTOR_SIZE); 
#endif 
 
			// Encrypt the random plaintext and write it to the disk 
			if (WriteSector (dev, sector, write_buf, &write_buf_cnt, &nSecNo, 
				cryptoInfo) == FALSE) 
				goto fail; 
		} 
		if (write_buf_cnt != 0 &&  
#ifdef _WIN32 
			_lwrite ((HFILE)dev, write_buf, write_buf_cnt) == HFILE_ERROR) 
#else 
			fwrite (write_buf, 1, write_buf_cnt, (FILE *)dev) != (size_t)write_buf_cnt) 
#endif 
			goto fail; 
	} 
	else 
		nSecNo = num_sectors; 
 
	UpdateProgressBar (nSecNo); 
 
	TCfree (write_buf); 
	burn (temporaryKey, sizeof(temporaryKey)); 
	return 0; 
 
fail: 
 
	TCfree (write_buf); 
	burn (temporaryKey, sizeof(temporaryKey)); 
	return ERR_OS_ERROR; 
} 
 
#ifdef _WIN32 
 
volatile BOOLEAN FormatExResult; 
 
BOOLEAN __stdcall FormatExCallback (int command, DWORD subCommand, PVOID parameter) 
{ 
	if (command == FMIFS_DONE) 
		FormatExResult = *(BOOLEAN *) parameter; 
	return TRUE; 
} 
 
BOOL FormatNtfs (int driveNo, int clusterSize) 
{ 
	WCHAR dir[8] = { driveNo + 'A', 0 }; 
	PFORMATEX FormatEx; 
	HMODULE hModule = LoadLibrary ("fmifs.dll"); 
 
	if (hModule == NULL) 
		return FALSE; 
 
	if (!(FormatEx = (void *) GetProcAddress (GetModuleHandle("fmifs.dll"), "FormatEx"))) 
	{ 
		FreeLibrary (hModule); 
		return FALSE; 
	} 
 
	wcscat (dir, L":\\"); 
 
	FormatExResult = FALSE; 
 
	if (*(char *)dir > 'C' && *(char *)dir <= 'Z') 
		FormatEx (dir, FMIFS_HARDDISK, L"NTFS", L"", TRUE, clusterSize * 512, FormatExCallback); 
 
	FreeLibrary (hModule); 
	return FormatExResult; 
} 
 
#endif 
 
BOOL 
WriteSector (void *dev, char *sector, 
	     char *write_buf, int *write_buf_cnt, 
	     __int64 *nSecNo, PCRYPTO_INFO cryptoInfo) 
{ 
	static __int32 updateTime = 0; 
 
	EncryptSectors ((unsigned __int32 *) sector, 
		(*nSecNo)++, 1, cryptoInfo); 
 
	memcpy (write_buf + *write_buf_cnt, sector, SECTOR_SIZE); 
	(*write_buf_cnt) += SECTOR_SIZE; 
 
	if (*write_buf_cnt == WRITE_BUF_SIZE) 
	{ 
		if ( 
#ifdef _WIN32 
			_lwrite ((HFILE)dev, write_buf, WRITE_BUF_SIZE) == HFILE_ERROR) 
#else 
			fwrite (write_buf, 1, WRITE_BUF_SIZE, (FILE *)dev) != WRITE_BUF_SIZE) 
#endif 
			return FALSE; 
		else 
			*write_buf_cnt = 0; 
	} 
	 
#ifdef _WIN32 
	if (GetTickCount () - updateTime > 25) 
	{ 
		if (UpdateProgressBar (*nSecNo)) 
			return FALSE; 
 
		updateTime = GetTickCount (); 
	} 
#else 
	UpdateProgressBar (*nSecNo); 
#endif 
 
	return TRUE; 
 
}