diff options
Diffstat (limited to 'src/Common/Format.c')
-rw-r--r-- | src/Common/Format.c | 291 |
1 files changed, 204 insertions, 87 deletions
diff --git a/src/Common/Format.c b/src/Common/Format.c index f691c78d..4d8dfed9 100644 --- a/src/Common/Format.c +++ b/src/Common/Format.c @@ -218,7 +218,7 @@ begin_format: bFailedRequiredDASD = TRUE; } } - else if (IsOSAtLeast (WIN_VISTA) && driveLetter == -1) + else if (driveLetter == -1) { // Windows Vista doesn't allow overwriting sectors belonging to an unformatted partition // to which no drive letter has been assigned under the system. This problem can be worked @@ -344,6 +344,32 @@ begin_format: else { /* File-hosted volume */ + BOOL speedupFileCreation = FALSE; + BOOL delayedSpeedupFileCreation = FALSE; + // speedup for file creation only makes sens when using quick format for non hidden volumes + if (!volParams->hiddenVol && !bInstantRetryOtherFilesys && volParams->quickFormat && volParams->fastCreateFile) + { + // we set required privileges to speedup file creation before we create the file so that the file handle inherits the privileges + if (!SetPrivilege(SE_MANAGE_VOLUME_NAME, TRUE)) + { + DWORD dwLastError = GetLastError(); + if (!IsAdmin () && IsUacSupported ()) + { + speedupFileCreation = TRUE; + delayedSpeedupFileCreation = TRUE; + } + else if (Silent || (MessageBoxW(hwndDlg, GetString ("ADMIN_PRIVILEGES_WARN_MANAGE_VOLUME"), lpszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) == IDNO)) + { + SetLastError(dwLastError); + nStatus = ERR_OS_ERROR; + goto error; + } + } + else + { + speedupFileCreation = TRUE; + } + } dev = CreateFile (volParams->volumePath, GENERIC_READ | GENERIC_WRITE, (volParams->hiddenVol || bInstantRetryOtherFilesys) ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : 0, @@ -373,13 +399,8 @@ begin_format: if (!volParams->hiddenVol && !bInstantRetryOtherFilesys) { LARGE_INTEGER volumeSize; - BOOL speedupFileCreation = FALSE; volumeSize.QuadPart = dataAreaSize + TC_VOLUME_HEADER_GROUP_SIZE; - // speedup for file creation only makes sens when using quick format - if (volParams->quickFormat && volParams->fastCreateFile) - speedupFileCreation = TRUE; - if (volParams->sparseFileSwitch && volParams->quickFormat) { // Create as sparse file container @@ -391,12 +412,15 @@ begin_format: } } - // Preallocate the file - if (!SetFilePointerEx (dev, volumeSize, NULL, FILE_BEGIN) - || !SetEndOfFile (dev)) + if (!delayedSpeedupFileCreation) { - nStatus = ERR_OS_ERROR; - goto error; + // Preallocate the file + if (!SetFilePointerEx (dev, volumeSize, NULL, FILE_BEGIN) + || !SetEndOfFile (dev)) + { + nStatus = ERR_OS_ERROR; + goto error; + } } if (speedupFileCreation) @@ -405,8 +429,46 @@ begin_format: // this has security issues since it will put existing disk content into file container // We use this mechanism only when switch /fastCreateFile specific and when quick format // also specified and which is documented to have security issues. - // we don't check returned status because failure is not issue for us - SetFileValidData (dev, volumeSize.QuadPart); + if (delayedSpeedupFileCreation) + { + // in case of delayed speedup we need to set the file size to a minimal value before performing the real preallocation through UAC + LARGE_INTEGER minimalSize; + DWORD dwOpStatus; + // 16K + minimalSize.QuadPart = 16 * 1024; + if (!SetFilePointerEx (dev, minimalSize, NULL, FILE_BEGIN) + || !SetEndOfFile (dev)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + FlushFileBuffers (dev); + CloseHandle (dev); + dev = INVALID_HANDLE_VALUE; + + dwOpStatus = UacFastFileCreation (volParams->hwndDlg, volParams->volumePath, volumeSize.QuadPart); + if (dwOpStatus != 0) + { + SetLastError(dwOpStatus); + nStatus = ERR_OS_ERROR; + goto error; + } + + // open again the file now that it was created + dev = CreateFile (volParams->volumePath, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + if (dev == INVALID_HANDLE_VALUE) + { + nStatus = ERR_OS_ERROR; + goto error; + } + } + else if (!SetFileValidData (dev, volumeSize.QuadPart)) + { + nStatus = ERR_OS_ERROR; + goto error; + } } if (SetFilePointer (dev, 0, NULL, FILE_BEGIN) != 0) @@ -521,7 +583,7 @@ begin_format: goto error; } - nStatus = FormatNoFs (hwndDlg, startSector, num_sectors, dev, cryptoInfo, volParams->quickFormat); + nStatus = FormatNoFs (hwndDlg, startSector, num_sectors, dev, cryptoInfo, volParams->quickFormat, volParams->bDevice); if (volParams->bDevice) StopFormatWriteThread(); @@ -554,7 +616,7 @@ begin_format: goto error; } - nStatus = FormatFat (hwndDlg, startSector, &ft, (void *) dev, cryptoInfo, volParams->quickFormat); + nStatus = FormatFat (hwndDlg, startSector, &ft, (void *) dev, cryptoInfo, volParams->quickFormat, volParams->bDevice); if (volParams->bDevice) StopFormatWriteThread(); @@ -768,7 +830,7 @@ error: mountOptions.PartitionInInactiveSysEncScope = FALSE; mountOptions.UseBackupHeader = FALSE; - if (MountVolume (volParams->hwndDlg, driveNo, volParams->volumePath, volParams->password, volParams->pkcs5, volParams->pim, FALSE, FALSE, FALSE, TRUE, &mountOptions, Silent, TRUE) < 1) + if (MountVolume (volParams->hwndDlg, driveNo, volParams->volumePath, volParams->password, volParams->pkcs5, volParams->pim, FALSE, FALSE, TRUE, &mountOptions, Silent, TRUE) < 1) { if (!Silent) { @@ -780,16 +842,24 @@ error: } retCode = ExternalFormatFs (driveNo, volParams->clusterSize, fsType); - if (retCode != TRUE) + if (retCode != 0) { + /* fallback to using FormatEx function from fmifs.dll */ if (!Silent && !IsAdmin () && IsUacSupported ()) retCode = UacFormatFs (volParams->hwndDlg, driveNo, volParams->clusterSize, fsType); else - retCode = FormatFs (driveNo, volParams->clusterSize, fsType); + retCode = FormatFs (driveNo, volParams->clusterSize, fsType, FALSE); /* no need to fallback to format.com since we have already tried it without elevation */ + + if (retCode != 0) + { + wchar_t auxLine[2048]; + StringCbPrintfW (auxLine, sizeof(auxLine), GetString ("FORMATEX_API_FAILED"), FormatExGetMessage(retCode)); + ErrorDirect(auxLine, volParams->hwndDlg); + } } - if (retCode != TRUE) + if (retCode != 0) { if (!UnmountVolumeAfterFormatExCall (volParams->hwndDlg, driveNo) && !Silent) MessageBoxW (volParams->hwndDlg, GetString ("CANT_DISMOUNT_VOLUME"), lpszTitle, ICON_HAND); @@ -830,11 +900,13 @@ fv_end: } -int FormatNoFs (HWND hwndDlg, unsigned __int64 startSector, __int64 num_sectors, void * dev, PCRYPTO_INFO cryptoInfo, BOOL quickFormat) +int FormatNoFs (HWND hwndDlg, unsigned __int64 startSector, unsigned __int64 num_sectors, void * dev, PCRYPTO_INFO cryptoInfo, BOOL quickFormat, BOOL bDevice) { int write_buf_cnt = 0; char sector[TC_MAX_VOLUME_SECTOR_SIZE], *write_buf; unsigned __int64 nSecNo = startSector; + unsigned __int64 nSkipSectors = 128 * (unsigned __int64) BYTES_PER_MB / FormatSectorSize; + DWORD bytesWritten; int retVal = 0; DWORD err; CRYPTOPP_ALIGN_DATA(16) char temporaryKey[MASTER_KEYDATA_SIZE]; @@ -910,18 +982,56 @@ int FormatNoFs (HWND hwndDlg, unsigned __int64 startSector, __int64 num_sectors, while (num_sectors--) { - if (WriteSector (dev, sector, write_buf, &write_buf_cnt, &nSecNo, + if (WriteSector (dev, sector, write_buf, &write_buf_cnt, &nSecNo, startSector, cryptoInfo) == FALSE) goto fail; } + if (UpdateProgressBar ((nSecNo - startSector) * FormatSectorSize)) + return FALSE; + if (!FlushFormatWriteBuffer (dev, write_buf, &write_buf_cnt, &nSecNo, cryptoInfo)) goto fail; } + else if (!bDevice) + { + // Quick format: write a zeroed sector every 128 MiB, leaving other sectors untouched + // This helps users visualize the progress of actual file creation while forcing Windows + // to allocate the disk space of each 128 MiB chunk immediately, otherwise, Windows + // would delay the allocation until we write the backup header at the end of the volume which + // would make the user think that the format process has stalled after progress bar reaches 100%. + while (num_sectors >= nSkipSectors) + { + // seek to next sector to be written + nSecNo += (nSkipSectors - 1); + startOffset.QuadPart = nSecNo * FormatSectorSize; + if (!MoveFilePointer ((HANDLE) dev, startOffset)) + { + goto fail; + } + + // sector array has been zeroed above + if (!WriteFile ((HANDLE) dev, sector, FormatSectorSize, &bytesWritten, NULL) + || bytesWritten != FormatSectorSize) + { + goto fail; + } + + nSecNo++; + num_sectors -= nSkipSectors; + + if (UpdateProgressBar ((nSecNo - startSector)* FormatSectorSize)) + goto fail; + } + + nSecNo += num_sectors; + } else - nSecNo = num_sectors; + { + nSecNo += num_sectors; + } - UpdateProgressBar (nSecNo * FormatSectorSize); + UpdateProgressBar ((nSecNo - startSector) * FormatSectorSize); // Restore the original secondary key (XTS mode) in case NTFS format fails and the user wants to try FAT immediately memcpy (cryptoInfo->k2, originalK2, sizeof (cryptoInfo->k2)); @@ -973,6 +1083,46 @@ fail: volatile BOOLEAN FormatExError; +volatile int FormatExErrorCommand; + +LPCWSTR FormatExGetMessage (int command) +{ + static WCHAR h_szMsg[32]; + switch (command) + { + case FMIFS_DONE: + return L"FORMAT_FINISHED"; + case FMIFS_STRUCTURE_PROGRESS: + return L"FORMAT_STRUCTURE_PROGRESS"; + case FMIFS_MEDIA_WRITE_PROTECTED: + return L"FORMAT_MEDIA_WRITE_PROTECTED"; + case FMIFS_INCOMPATIBLE_FILE_SYSTEM: + return L"FORMAT_INCOMPATIBLE_FILE_SYSTEM"; + case FMIFS_ACCESS_DENIED: + return L"FORMAT_ACCESS_DENIED"; + case FMIFS_VOLUME_IN_USE: + return L"FORMAT_VOLUME_IN_USE"; + case FMIFS_CLUSTER_SIZE_TOO_SMALL: + return L"FORMAT_CLUSTER_SIZE_TOO_SMALL"; + case FMIFS_CLUSTER_SIZE_TOO_BIG: + return L"FORMAT_CLUSTER_SIZE_TOO_BIG"; + case FMIFS_VOLUME_TOO_SMALL: + return L"FORMAT_VOLUME_TOO_SMALL"; + case FMIFS_VOLUME_TOO_BIG: + return L"FORMAT_VOLUME_TOO_BIG"; + case FMIFS_NO_MEDIA_IN_DRIVE: + return L"FORMAT_NO_MEDIA_IN_DRIVE"; + case FMIFS_DEVICE_NOT_READY: + return L"FORMAT_DEVICE_NOT_READY"; + case FMIFS_BAD_LABEL: + return L"FORMAT_BAD_LABEL"; + case FMIFS_CANT_QUICK_FORMAT: + return L"FORMAT_CANT_QUICK_FORMAT"; + default: + StringCbPrintfW (h_szMsg, sizeof(h_szMsg), L"0x%.8X", command); + return h_szMsg; + } +} BOOLEAN __stdcall FormatExCallback (int command, DWORD subCommand, PVOID parameter) { @@ -1029,10 +1179,14 @@ BOOLEAN __stdcall FormatExCallback (int command, DWORD subCommand, PVOID paramet FormatExError = TRUE; break; } + if (FormatExError) + { + FormatExErrorCommand = command; + } return (FormatExError? FALSE : TRUE); } -BOOL FormatFs (int driveNo, int clusterSize, int fsType) +int FormatFs (int driveNo, int clusterSize, int fsType, BOOL bFallBackExternal) { wchar_t dllPath[MAX_PATH] = {0}; WCHAR dir[8] = { (WCHAR) driveNo + L'A', 0 }; @@ -1078,41 +1232,45 @@ BOOL FormatFs (int driveNo, int clusterSize, int fsType) StringCchCatW (dir, ARRAYSIZE(dir), L":\\"); FormatExError = TRUE; + FormatExErrorCommand = 0; // Windows sometimes fails to format a volume (hosted on a removable medium) as NTFS. // It often helps to retry several times. for (i = 0; i < 50 && FormatExError; i++) { FormatExError = FALSE; - FormatEx (dir, FMIFS_HARDDISK, szFsFormat, szLabel, TRUE, clusterSize * FormatSectorSize, FormatExCallback); + FormatExErrorCommand = 0; + FormatEx (dir, FMIFS_REMOVAL, szFsFormat, szLabel, TRUE, clusterSize * FormatSectorSize, FormatExCallback); } // The device may be referenced for some time after FormatEx() returns Sleep (4000); FreeLibrary (hModule); - return FormatExError? FALSE : TRUE; + + if (FormatExError && bFallBackExternal) + { + return ExternalFormatFs (driveNo, clusterSize, fsType); + } + + return FormatExError? FormatExErrorCommand : 0; } -BOOL FormatNtfs (int driveNo, int clusterSize) +int FormatNtfs (int driveNo, int clusterSize, BOOL bFallBackExternal) { - return FormatFs (driveNo, clusterSize, FILESYS_NTFS); + return FormatFs (driveNo, clusterSize, FILESYS_NTFS, bFallBackExternal); } /* call Windows format.com program to perform formatting */ -BOOL ExternalFormatFs (int driveNo, int clusterSize, int fsType) +int ExternalFormatFs (int driveNo, int clusterSize, int fsType) { wchar_t exePath[MAX_PATH] = {0}; - HANDLE hChildStd_IN_Rd = NULL; - HANDLE hChildStd_IN_Wr = NULL; - HANDLE hChildStd_OUT_Rd = NULL; - HANDLE hChildStd_OUT_Wr = NULL; WCHAR szFsFormat[16]; TCHAR szCmdline[2 * MAX_PATH]; STARTUPINFO siStartInfo; PROCESS_INFORMATION piProcInfo; BOOL bSuccess = FALSE; - SECURITY_ATTRIBUTES saAttr; + int iRet = 0; switch (fsType) { @@ -1129,35 +1287,6 @@ BOOL ExternalFormatFs (int driveNo, int clusterSize, int fsType) return FALSE; } - /* Set the bInheritHandle flag so pipe handles are inherited. */ - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - /* Create a pipe for the child process's STDOUT. */ - if ( !CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0) ) - return FALSE; - - /* Ensure the read handle to the pipe for STDOUT is not inherited. */ - /* Create a pipe for the child process's STDIN. */ - if ( !SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) - || !CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0)) - { - CloseHandle (hChildStd_OUT_Rd); - CloseHandle (hChildStd_OUT_Wr); - return FALSE; - } - - /* Ensure the write handle to the pipe for STDIN is not inherited. */ - if ( !SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) - { - CloseHandle (hChildStd_OUT_Rd); - CloseHandle (hChildStd_OUT_Wr); - CloseHandle (hChildStd_IN_Rd); - CloseHandle (hChildStd_IN_Wr); - return FALSE; - } - if (GetSystemDirectory (exePath, MAX_PATH)) { StringCchCatW(exePath, ARRAYSIZE(exePath), L"\\format.com"); @@ -1165,7 +1294,7 @@ BOOL ExternalFormatFs (int driveNo, int clusterSize, int fsType) else StringCchCopyW(exePath, ARRAYSIZE(exePath), L"C:\\Windows\\System32\\format.com"); - StringCbPrintf (szCmdline, sizeof(szCmdline), L"%s %c: /FS:%s /Q /X /V:\"\"", exePath, (WCHAR) driveNo + L'A', szFsFormat); + StringCbPrintf (szCmdline, sizeof(szCmdline), L"%s %c: /FS:%s /Q /X /V:\"\" /Y", exePath, (WCHAR) driveNo + L'A', szFsFormat); if (clusterSize) { @@ -1188,15 +1317,11 @@ BOOL ExternalFormatFs (int driveNo, int clusterSize, int fsType) ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); /* Set up members of the STARTUPINFO structure. - This structure specifies the STDIN and STDOUT handles for redirection. */ ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); siStartInfo.cb = sizeof(STARTUPINFO); - siStartInfo.hStdError = hChildStd_OUT_Wr; - siStartInfo.hStdOutput = hChildStd_OUT_Wr; - siStartInfo.hStdInput = hChildStd_IN_Rd; siStartInfo.wShowWindow = SW_HIDE; - siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + siStartInfo.dwFlags |= STARTF_USESHOWWINDOW; /* Create the child process. */ bSuccess = CreateProcess(NULL, @@ -1212,11 +1337,7 @@ BOOL ExternalFormatFs (int driveNo, int clusterSize, int fsType) if (bSuccess) { - /* Unblock the format process by simulating hit on ENTER key */ - DWORD dwExitCode, dwWritten; - LPCSTR newLine = "\n"; - - WriteFile(hChildStd_IN_Wr, (LPCVOID) newLine, 1, &dwWritten, NULL); + DWORD dwExitCode; /* wait for the format process to finish */ WaitForSingleObject (piProcInfo.hProcess, INFINITE); @@ -1224,29 +1345,25 @@ BOOL ExternalFormatFs (int driveNo, int clusterSize, int fsType) /* check if it was successfull */ if (GetExitCodeProcess (piProcInfo.hProcess, &dwExitCode)) { - if (dwExitCode == 0) - bSuccess = TRUE; - else - bSuccess = FALSE; + iRet = (int) dwExitCode; /* dwExitCode will be 0 in case of success */ } else - bSuccess = FALSE; + iRet = (int) GetLastError(); CloseHandle (piProcInfo.hThread); CloseHandle (piProcInfo.hProcess); } + else + { + iRet = (int) GetLastError(); + } - CloseHandle(hChildStd_OUT_Wr); - CloseHandle(hChildStd_OUT_Rd); - CloseHandle(hChildStd_IN_Rd); - CloseHandle(hChildStd_IN_Wr); - - return bSuccess; + return iRet; } BOOL WriteSector (void *dev, char *sector, char *write_buf, int *write_buf_cnt, - __int64 *nSecNo, PCRYPTO_INFO cryptoInfo) + unsigned __int64 *nSecNo, unsigned __int64 startSector, PCRYPTO_INFO cryptoInfo) { static __int32 updateTime = 0; @@ -1260,7 +1377,7 @@ BOOL WriteSector (void *dev, char *sector, if (GetTickCount () - updateTime > 25) { - if (UpdateProgressBar (*nSecNo * FormatSectorSize)) + if (UpdateProgressBar ((*nSecNo - startSector) * FormatSectorSize)) return FALSE; updateTime = GetTickCount (); |