/*++ Copyright (c) 2000 Microsoft Corporation Module Name: wipedisk.cpp Abstract: Utility program to zero out the partition-tables and first/last few sectors of disks Author: Guhan Suriyanarayanan (guhans) 30-Sep-2000 Environment: User-mode only. Revision History: 30-Sep-2000 guhans Initial creation --*/ #include #include #include #include #include #include #include BOOL g_bPrompt = TRUE; // // write in 64K chunks. // #define BUFFER_SIZE_BYTES (64 * 1024) BOOL pSetSignature( IN CONST ULONG ulDiskNumber, IN CONST DWORD dwNewSignature ) { DWORD dwStatus = ERROR_SUCCESS, dwBytesReturned = 0, dwBufferSize = 0; PDRIVE_LAYOUT_INFORMATION_EX driveLayoutEx; int i = 0, loopTimes = 0; BOOL bResult = FALSE; HANDLE hDisk = NULL, hHeap = NULL; WCHAR szFriendlyName[100]; // For display "Disk 2" WCHAR szDiskPath[100]; // For CreateFile "\\.\PhysicalDrive2" wsprintf(szFriendlyName, L"Disk %lu", ulDiskNumber); wsprintf(szDiskPath, L"\\\\.\\PhysicalDrive%lu", ulDiskNumber); hHeap = GetProcessHeap(); hDisk = CreateFile( szDiskPath, // lpFileName GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess FILE_SHARE_READ, // dwShareMode NULL, // lpSecurityAttributes OPEN_EXISTING, // dwCreationFlags FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, // dwFlagsAndAttributes NULL // hTemplateFile ); if ((INVALID_HANDLE_VALUE == hDisk) || (NULL == hDisk)) { // // Couldn't open a handle. // wprintf(L"Unable to open a handle to %ws (%lu)\n", szDiskPath, GetLastError()); return FALSE; } dwBufferSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + (sizeof(PARTITION_INFORMATION_EX) * 3); driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc( hHeap, HEAP_ZERO_MEMORY, dwBufferSize ); if (!driveLayoutEx) { wprintf(L"Could not allocate memory\n"); dwStatus = ERROR_NOT_ENOUGH_MEMORY; goto EXIT; } bResult = FALSE; while (!bResult) { bResult = DeviceIoControl( hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0L, driveLayoutEx, dwBufferSize, &dwBytesReturned, NULL ); if (!bResult) { dwStatus = GetLastError(); HeapFree(hHeap, 0L, driveLayoutEx); driveLayoutEx = NULL; // // If the buffer is of insufficient size, resize the buffer. // Note that get-drive-layout-ex could return error-insufficient- // buffer (instead of? in addition to? error-more-data) // if ((ERROR_MORE_DATA == dwStatus) || (ERROR_INSUFFICIENT_BUFFER == dwStatus) ) { dwStatus = ERROR_SUCCESS; dwBufferSize += sizeof(PARTITION_INFORMATION_EX) * 4; driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc( hHeap, HEAP_ZERO_MEMORY, dwBufferSize ); if (!driveLayoutEx) { wprintf(L"Could not allocate memory\n"); dwStatus = ERROR_NOT_ENOUGH_MEMORY; goto EXIT; } } else { // // some other error occurred, EXIT, and go to the next drive. // wprintf(L"Could not get the drive layout for %ws (%lu)\n", szDiskPath, dwStatus); goto EXIT; } } } // // Now modify the signature, and set the layout again // driveLayoutEx->Mbr.Signature = dwNewSignature; bResult = DeviceIoControl( hDisk, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, driveLayoutEx, dwBufferSize, NULL, 0L, &dwBytesReturned, NULL ); if (!bResult) { // // SET_DRIVE_LAYOUT failed // dwStatus = GetLastError(); wprintf(L"Could not SET the drive layout for %ws (%lu)\n", szDiskPath, dwStatus); goto EXIT; } EXIT: if (driveLayoutEx) { HeapFree(hHeap, 0L, driveLayoutEx); driveLayoutEx = NULL; } if ((hDisk) && (INVALID_HANDLE_VALUE != hDisk)) { CloseHandle(hDisk); hDisk = NULL; } SetLastError(dwStatus); return bResult; } BOOL pConfirmWipe( IN CONST PCWSTR szDiskDisplayName ) { WCHAR szInput[10]; wprintf(L"\nWARNING. This will delete all partitions from %ws\n", szDiskDisplayName); wprintf(L"Are you sure you want to continue [y/n]? "); wscanf(L"%ws", szInput); wprintf(L"\n"); return ((L'Y' == szInput[0]) || (L'y' == szInput[0])); } BOOL pWipeDisk( IN CONST ULONG ulDiskNumber, IN CONST ULONG ulFirstMB, IN CONST ULONG ulLastMB ) /*++ Routine Description: Deletes the drive layout for disk, and writes 0's at the start and end of the disk. Arguments: ulDiskNumber - DiskNumber to wipe ulFirstMB - Number of MB to erase at the beginning of the disk ulLastMB - Number of MB to erase at the end of the disk Return Values: If the function succeeds, the return value is a nonzero value. If the function fails, the return value is zero. To get extended error information, call GetLastError(). --*/ { DWORD dwStatus = ERROR_SUCCESS, dwBytes = 0; BYTE Buffer[BUFFER_SIZE_BYTES]; int i = 0, loopTimes = 0; BOOL bResult = FALSE; HANDLE hDisk = NULL; WCHAR szFriendlyName[100]; // For display "Disk 2" WCHAR szDiskPath[100]; // For CreateFile "\\.\PhysicalDrive2" PARTITION_INFORMATION_EX ptnInfo; wsprintf(szFriendlyName, L"Disk %lu", ulDiskNumber); wsprintf(szDiskPath, L"\\\\.\\PhysicalDrive%lu", ulDiskNumber); ZeroMemory(Buffer, BUFFER_SIZE_BYTES); if (g_bPrompt && !pConfirmWipe(szFriendlyName)) { // // User didn't want to continue // SetLastError(ERROR_CANCELLED); return FALSE; } // // Set the signature to something random. We need to do this before // deleting the layout for the boot disk. // pSetSignature(ulDiskNumber, 0); hDisk = CreateFile( szDiskPath, // lpFileName GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess FILE_SHARE_READ, // dwShareMode NULL, // lpSecurityAttributes OPEN_EXISTING, // dwCreationFlags FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, // dwFlagsAndAttributes NULL // hTemplateFile ); if ((INVALID_HANDLE_VALUE == hDisk) || (NULL == hDisk)) { // // Couldn't open a handle. // wprintf(L"Unable to open a handle to %ws (%lu)\n", szDiskPath, GetLastError()); return FALSE; } // // Zero out the first two sectors of each partition? // // // Delete the drive layout // wprintf(L"Deleting partitions on %ws ...\n", szFriendlyName); bResult = DeviceIoControl( hDisk, IOCTL_DISK_DELETE_DRIVE_LAYOUT, NULL, 0L, NULL, 0L, &dwBytes, NULL ); bResult = TRUE; if (!bResult) { wprintf(L"Unable to delete partitions on %ws (%lu)\n", szDiskPath, GetLastError()); CloseHandle(hDisk); return FALSE; } // // Erase MB at the start of the disk // if (ulFirstMB > 0) { wprintf(L"Erasing first %lu MB on %ws ...\n", ulFirstMB, szFriendlyName); // // Write 0's to disk in BUFFER_SIZE chunks // loopTimes = (ulFirstMB * 1024 * 1024 / BUFFER_SIZE_BYTES); for (i = 0; i < loopTimes; i++) { bResult = WriteFile( hDisk, &Buffer, BUFFER_SIZE_BYTES, &dwBytes, NULL ); if (!bResult) { wprintf(L"Error while writing to %ws (%d, %lu)\n", szDiskPath, i, GetLastError()); break; } } if (!bResult) { CloseHandle(hDisk); return FALSE; } } // // Erase MB at the end of the disk // if (ulLastMB > 0) { wprintf(L"Erasing last %lu MB on %ws ...\n", ulLastMB, szFriendlyName); // // Find the end of the disk // bResult = DeviceIoControl( hDisk, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &ptnInfo, sizeof(PARTITION_INFORMATION_EX), &dwBytes, NULL ); if (!bResult) { wprintf(L"Could not find size of disk %ws (%lu)\n", szDiskPath, GetLastError()); CloseHandle(hDisk); return FALSE; } // // Find offset we'd like to start zero-ing out from // (end of disk - bytes to zero out) // ptnInfo.PartitionLength.QuadPart -= (ulLastMB * 1024 * 1024); dwBytes = SetFilePointer(hDisk, (ptnInfo.PartitionLength.LowPart), &(ptnInfo.PartitionLength.HighPart), FILE_BEGIN); if ((INVALID_SET_FILE_POINTER == dwBytes) && (NO_ERROR != GetLastError())) { wprintf(L"Could not move to end of disk for %ws (%lu)\n", szDiskPath, GetLastError()); } // // Write 0's to disk in BUFFER_SIZE chunks // loopTimes = (ulLastMB * 1024 * 1024 / BUFFER_SIZE_BYTES); for (i = 0; i < loopTimes; i++) { bResult = WriteFile( hDisk, &Buffer, BUFFER_SIZE_BYTES, &dwBytes, NULL ); if (!bResult) { wprintf(L"Error while writing to %ws (%d, %lu)\n", szDiskPath, i, GetLastError()); break; } } if (!bResult) { CloseHandle(hDisk); return FALSE; } } CloseHandle(hDisk); return TRUE; } VOID pPrintUsage( IN CONST PCWSTR szArgV0 ) { wprintf(L"usage: %ws [/f] disk-number [mb-at-start [mb-at-end]]\n" L" %ws /s new-signature disk-number\n" L"\n" L" /f: Suppress the prompt to confirm action\n" L"\n" L" disk-number: The NT disk-number of the disk that\n" L" the operation is to be performed\n" L"\n" L" mb-at-start: The number of MB to zero-out at\n" L" the start of the disk. Default is 1\n" L"\n" L" mb-at-end: The number of MB to zero-out at\n" L" the end of the disk. Default is 4\n" L"\n", L" /s: Set the signature of disk\n" L"\n" L" new-signature: Value to set the disk signature to.\n" L" Specify 0 to use a randomly generated\n" L" value\n" L"\n", szArgV0, szArgV0 ); } int __cdecl wmain( int argc, WCHAR *argv[], WCHAR *envp[] ) /*++ Routine Description: Entry point to wipedisk.exe. Arguments: argc - Number of command-line parameters used to invoke the app argv - The command-line parameters as an array of strings. argv[1] (required) is expected to be the disk number argv[2] (optional) is the initial MB to erase, default 1 argv[3] (optional) is the last MB to erase, default 4 envp - The process environment block, not currently used Return Values: If the function succeeds, the exit code is zero. If the function fails, the exit code is a win-32 error code. --*/ { BOOL bResult = TRUE, bSetSignature = FALSE; ULONG ulDiskNumber = 0, ulInitialMB = 1, ulFinalMB = 4; DWORD dwNewSignature = 0L; int shift = 0; SetLastError(ERROR_CAN_NOT_COMPLETE); // for unexpected failures if (argc >= 2) { if ((L'-' == argv[1][0]) || (L'/' == argv[1][0])) { // // Parse the options // switch (argv[1][1]) { case L'f': case L'F': { g_bPrompt = FALSE; shift = 1; // account for /f break; } case L's': case L'S': { bSetSignature = TRUE; shift = 2; // account for /s break; } } } } if (argc >= shift + 2) { swscanf(argv[shift + 1], L"%lu", &ulDiskNumber); } else { // // We need at least one argument--the disk number // pPrintUsage(argv[0]); return ERROR_INVALID_PARAMETER; } if (bSetSignature) { // // Set the signature of the disk to a new value // swscanf(argv[shift], L"%lu", &dwNewSignature); bResult = pSetSignature(ulDiskNumber, dwNewSignature); } else { // // Wipe the disk. Get the amount to zero-out at the start // and end of disk // if (argc >= shift + 3) { swscanf(argv[shift + 2], L"%lu", &ulInitialMB); if (argc >= shift + 4) { swscanf(argv[shift + 3], L"%lu", &ulFinalMB); } } bResult = pWipeDisk(ulDiskNumber, ulInitialMB, ulFinalMB); } if (bResult) { wprintf(L"Done.\n"); } return (bResult ? 0 : GetLastError()); }