Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

572 lines
14 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. wipedisk.cpp
  5. Abstract:
  6. Utility program to zero out the partition-tables and first/last
  7. few sectors of disks
  8. Author:
  9. Guhan Suriyanarayanan (guhans) 30-Sep-2000
  10. Environment:
  11. User-mode only.
  12. Revision History:
  13. 30-Sep-2000 guhans
  14. Initial creation
  15. --*/
  16. #include <nt.h>
  17. #include <ntrtl.h>
  18. #include <nturtl.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <windows.h>
  22. #include <ntdddisk.h>
  23. BOOL g_bPrompt = TRUE;
  24. //
  25. // write in 64K chunks.
  26. //
  27. #define BUFFER_SIZE_BYTES (64 * 1024)
  28. BOOL
  29. pSetSignature(
  30. IN CONST ULONG ulDiskNumber,
  31. IN CONST DWORD dwNewSignature
  32. )
  33. {
  34. DWORD dwStatus = ERROR_SUCCESS,
  35. dwBytesReturned = 0,
  36. dwBufferSize = 0;
  37. PDRIVE_LAYOUT_INFORMATION_EX driveLayoutEx;
  38. int i = 0, loopTimes = 0;
  39. BOOL bResult = FALSE;
  40. HANDLE hDisk = NULL,
  41. hHeap = NULL;
  42. WCHAR szFriendlyName[100]; // For display "Disk 2"
  43. WCHAR szDiskPath[100]; // For CreateFile "\\.\PhysicalDrive2"
  44. wsprintf(szFriendlyName, L"Disk %lu", ulDiskNumber);
  45. wsprintf(szDiskPath, L"\\\\.\\PhysicalDrive%lu", ulDiskNumber);
  46. hHeap = GetProcessHeap();
  47. hDisk = CreateFile(
  48. szDiskPath, // lpFileName
  49. GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
  50. FILE_SHARE_READ, // dwShareMode
  51. NULL, // lpSecurityAttributes
  52. OPEN_EXISTING, // dwCreationFlags
  53. FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, // dwFlagsAndAttributes
  54. NULL // hTemplateFile
  55. );
  56. if ((INVALID_HANDLE_VALUE == hDisk) || (NULL == hDisk)) {
  57. //
  58. // Couldn't open a handle.
  59. //
  60. wprintf(L"Unable to open a handle to %ws (%lu)\n", szDiskPath, GetLastError());
  61. return FALSE;
  62. }
  63. dwBufferSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
  64. (sizeof(PARTITION_INFORMATION_EX) * 3);
  65. driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc(
  66. hHeap,
  67. HEAP_ZERO_MEMORY,
  68. dwBufferSize
  69. );
  70. if (!driveLayoutEx) {
  71. wprintf(L"Could not allocate memory\n");
  72. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  73. goto EXIT;
  74. }
  75. bResult = FALSE;
  76. while (!bResult) {
  77. bResult = DeviceIoControl(
  78. hDisk,
  79. IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
  80. NULL,
  81. 0L,
  82. driveLayoutEx,
  83. dwBufferSize,
  84. &dwBytesReturned,
  85. NULL
  86. );
  87. if (!bResult) {
  88. dwStatus = GetLastError();
  89. HeapFree(hHeap, 0L, driveLayoutEx);
  90. driveLayoutEx = NULL;
  91. //
  92. // If the buffer is of insufficient size, resize the buffer.
  93. // Note that get-drive-layout-ex could return error-insufficient-
  94. // buffer (instead of? in addition to? error-more-data)
  95. //
  96. if ((ERROR_MORE_DATA == dwStatus) ||
  97. (ERROR_INSUFFICIENT_BUFFER == dwStatus)
  98. ) {
  99. dwStatus = ERROR_SUCCESS;
  100. dwBufferSize += sizeof(PARTITION_INFORMATION_EX) * 4;
  101. driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc(
  102. hHeap,
  103. HEAP_ZERO_MEMORY,
  104. dwBufferSize
  105. );
  106. if (!driveLayoutEx) {
  107. wprintf(L"Could not allocate memory\n");
  108. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  109. goto EXIT;
  110. }
  111. }
  112. else {
  113. //
  114. // some other error occurred, EXIT, and go to the next drive.
  115. //
  116. wprintf(L"Could not get the drive layout for %ws (%lu)\n", szDiskPath, dwStatus);
  117. goto EXIT;
  118. }
  119. }
  120. }
  121. //
  122. // Now modify the signature, and set the layout again
  123. //
  124. driveLayoutEx->Mbr.Signature = dwNewSignature;
  125. bResult = DeviceIoControl(
  126. hDisk,
  127. IOCTL_DISK_SET_DRIVE_LAYOUT_EX,
  128. driveLayoutEx,
  129. dwBufferSize,
  130. NULL,
  131. 0L,
  132. &dwBytesReturned,
  133. NULL
  134. );
  135. if (!bResult) {
  136. //
  137. // SET_DRIVE_LAYOUT failed
  138. //
  139. dwStatus = GetLastError();
  140. wprintf(L"Could not SET the drive layout for %ws (%lu)\n", szDiskPath, dwStatus);
  141. goto EXIT;
  142. }
  143. EXIT:
  144. if (driveLayoutEx) {
  145. HeapFree(hHeap, 0L, driveLayoutEx);
  146. driveLayoutEx = NULL;
  147. }
  148. if ((hDisk) && (INVALID_HANDLE_VALUE != hDisk)) {
  149. CloseHandle(hDisk);
  150. hDisk = NULL;
  151. }
  152. SetLastError(dwStatus);
  153. return bResult;
  154. }
  155. BOOL
  156. pConfirmWipe(
  157. IN CONST PCWSTR szDiskDisplayName
  158. )
  159. {
  160. WCHAR szInput[10];
  161. wprintf(L"\nWARNING. This will delete all partitions from %ws\n", szDiskDisplayName);
  162. wprintf(L"Are you sure you want to continue [y/n]? ");
  163. wscanf(L"%ws", szInput);
  164. wprintf(L"\n");
  165. return ((L'Y' == szInput[0]) || (L'y' == szInput[0]));
  166. }
  167. BOOL
  168. pWipeDisk(
  169. IN CONST ULONG ulDiskNumber,
  170. IN CONST ULONG ulFirstMB,
  171. IN CONST ULONG ulLastMB
  172. )
  173. /*++
  174. Routine Description:
  175. Deletes the drive layout for disk, and writes 0's at the start and end of the disk.
  176. Arguments:
  177. ulDiskNumber - DiskNumber to wipe
  178. ulFirstMB - Number of MB to erase at the beginning of the disk
  179. ulLastMB - Number of MB to erase at the end of the disk
  180. Return Values:
  181. If the function succeeds, the return value is a nonzero value.
  182. If the function fails, the return value is zero. To get extended error
  183. information, call GetLastError().
  184. --*/
  185. {
  186. DWORD dwStatus = ERROR_SUCCESS,
  187. dwBytes = 0;
  188. BYTE Buffer[BUFFER_SIZE_BYTES];
  189. int i = 0, loopTimes = 0;
  190. BOOL bResult = FALSE;
  191. HANDLE hDisk = NULL;
  192. WCHAR szFriendlyName[100]; // For display "Disk 2"
  193. WCHAR szDiskPath[100]; // For CreateFile "\\.\PhysicalDrive2"
  194. PARTITION_INFORMATION_EX ptnInfo;
  195. wsprintf(szFriendlyName, L"Disk %lu", ulDiskNumber);
  196. wsprintf(szDiskPath, L"\\\\.\\PhysicalDrive%lu", ulDiskNumber);
  197. ZeroMemory(Buffer, BUFFER_SIZE_BYTES);
  198. if (g_bPrompt && !pConfirmWipe(szFriendlyName)) {
  199. //
  200. // User didn't want to continue
  201. //
  202. SetLastError(ERROR_CANCELLED);
  203. return FALSE;
  204. }
  205. //
  206. // Set the signature to something random. We need to do this before
  207. // deleting the layout for the boot disk.
  208. //
  209. pSetSignature(ulDiskNumber, 0);
  210. hDisk = CreateFile(
  211. szDiskPath, // lpFileName
  212. GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
  213. FILE_SHARE_READ, // dwShareMode
  214. NULL, // lpSecurityAttributes
  215. OPEN_EXISTING, // dwCreationFlags
  216. FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, // dwFlagsAndAttributes
  217. NULL // hTemplateFile
  218. );
  219. if ((INVALID_HANDLE_VALUE == hDisk) || (NULL == hDisk)) {
  220. //
  221. // Couldn't open a handle.
  222. //
  223. wprintf(L"Unable to open a handle to %ws (%lu)\n", szDiskPath, GetLastError());
  224. return FALSE;
  225. }
  226. //
  227. // Zero out the first two sectors of each partition?
  228. //
  229. //
  230. // Delete the drive layout
  231. //
  232. wprintf(L"Deleting partitions on %ws ...\n", szFriendlyName);
  233. bResult = DeviceIoControl(
  234. hDisk,
  235. IOCTL_DISK_DELETE_DRIVE_LAYOUT,
  236. NULL,
  237. 0L,
  238. NULL,
  239. 0L,
  240. &dwBytes,
  241. NULL
  242. );
  243. bResult = TRUE;
  244. if (!bResult) {
  245. wprintf(L"Unable to delete partitions on %ws (%lu)\n", szDiskPath, GetLastError());
  246. CloseHandle(hDisk);
  247. return FALSE;
  248. }
  249. //
  250. // Erase MB at the start of the disk
  251. //
  252. if (ulFirstMB > 0) {
  253. wprintf(L"Erasing first %lu MB on %ws ...\n", ulFirstMB, szFriendlyName);
  254. //
  255. // Write 0's to disk in BUFFER_SIZE chunks
  256. //
  257. loopTimes = (ulFirstMB * 1024 * 1024 / BUFFER_SIZE_BYTES);
  258. for (i = 0; i < loopTimes; i++) {
  259. bResult = WriteFile(
  260. hDisk,
  261. &Buffer,
  262. BUFFER_SIZE_BYTES,
  263. &dwBytes,
  264. NULL
  265. );
  266. if (!bResult) {
  267. wprintf(L"Error while writing to %ws (%d, %lu)\n", szDiskPath, i, GetLastError());
  268. break;
  269. }
  270. }
  271. if (!bResult) {
  272. CloseHandle(hDisk);
  273. return FALSE;
  274. }
  275. }
  276. //
  277. // Erase MB at the end of the disk
  278. //
  279. if (ulLastMB > 0) {
  280. wprintf(L"Erasing last %lu MB on %ws ...\n", ulLastMB, szFriendlyName);
  281. //
  282. // Find the end of the disk
  283. //
  284. bResult = DeviceIoControl(
  285. hDisk,
  286. IOCTL_DISK_GET_PARTITION_INFO_EX,
  287. NULL,
  288. 0,
  289. &ptnInfo,
  290. sizeof(PARTITION_INFORMATION_EX),
  291. &dwBytes,
  292. NULL
  293. );
  294. if (!bResult) {
  295. wprintf(L"Could not find size of disk %ws (%lu)\n", szDiskPath, GetLastError());
  296. CloseHandle(hDisk);
  297. return FALSE;
  298. }
  299. //
  300. // Find offset we'd like to start zero-ing out from
  301. // (end of disk - bytes to zero out)
  302. //
  303. ptnInfo.PartitionLength.QuadPart -= (ulLastMB * 1024 * 1024);
  304. dwBytes = SetFilePointer(hDisk, (ptnInfo.PartitionLength.LowPart), &(ptnInfo.PartitionLength.HighPart), FILE_BEGIN);
  305. if ((INVALID_SET_FILE_POINTER == dwBytes) && (NO_ERROR != GetLastError())) {
  306. wprintf(L"Could not move to end of disk for %ws (%lu)\n", szDiskPath, GetLastError());
  307. }
  308. //
  309. // Write 0's to disk in BUFFER_SIZE chunks
  310. //
  311. loopTimes = (ulLastMB * 1024 * 1024 / BUFFER_SIZE_BYTES);
  312. for (i = 0; i < loopTimes; i++) {
  313. bResult = WriteFile(
  314. hDisk,
  315. &Buffer,
  316. BUFFER_SIZE_BYTES,
  317. &dwBytes,
  318. NULL
  319. );
  320. if (!bResult) {
  321. wprintf(L"Error while writing to %ws (%d, %lu)\n", szDiskPath, i, GetLastError());
  322. break;
  323. }
  324. }
  325. if (!bResult) {
  326. CloseHandle(hDisk);
  327. return FALSE;
  328. }
  329. }
  330. CloseHandle(hDisk);
  331. return TRUE;
  332. }
  333. VOID
  334. pPrintUsage(
  335. IN CONST PCWSTR szArgV0
  336. ) {
  337. wprintf(L"usage: %ws [/f] disk-number [mb-at-start [mb-at-end]]\n"
  338. L" %ws /s new-signature disk-number\n"
  339. L"\n"
  340. L" /f: Suppress the prompt to confirm action\n"
  341. L"\n"
  342. L" disk-number: The NT disk-number of the disk that\n"
  343. L" the operation is to be performed\n"
  344. L"\n"
  345. L" mb-at-start: The number of MB to zero-out at\n"
  346. L" the start of the disk. Default is 1\n"
  347. L"\n"
  348. L" mb-at-end: The number of MB to zero-out at\n"
  349. L" the end of the disk. Default is 4\n"
  350. L"\n",
  351. L" /s: Set the signature of disk\n"
  352. L"\n"
  353. L" new-signature: Value to set the disk signature to.\n"
  354. L" Specify 0 to use a randomly generated\n"
  355. L" value\n"
  356. L"\n",
  357. szArgV0,
  358. szArgV0
  359. );
  360. }
  361. int __cdecl
  362. wmain(
  363. int argc,
  364. WCHAR *argv[],
  365. WCHAR *envp[]
  366. )
  367. /*++
  368. Routine Description:
  369. Entry point to wipedisk.exe.
  370. Arguments:
  371. argc - Number of command-line parameters used to invoke the app
  372. argv - The command-line parameters as an array of strings.
  373. argv[1] (required) is expected to be the disk number
  374. argv[2] (optional) is the initial MB to erase, default 1
  375. argv[3] (optional) is the last MB to erase, default 4
  376. envp - The process environment block, not currently used
  377. Return Values:
  378. If the function succeeds, the exit code is zero.
  379. If the function fails, the exit code is a win-32 error code.
  380. --*/
  381. {
  382. BOOL bResult = TRUE,
  383. bSetSignature = FALSE;
  384. ULONG ulDiskNumber = 0,
  385. ulInitialMB = 1,
  386. ulFinalMB = 4;
  387. DWORD dwNewSignature = 0L;
  388. int shift = 0;
  389. SetLastError(ERROR_CAN_NOT_COMPLETE); // for unexpected failures
  390. if (argc >= 2) {
  391. if ((L'-' == argv[1][0]) || (L'/' == argv[1][0])) {
  392. //
  393. // Parse the options
  394. //
  395. switch (argv[1][1]) {
  396. case L'f':
  397. case L'F': {
  398. g_bPrompt = FALSE;
  399. shift = 1; // account for /f
  400. break;
  401. }
  402. case L's':
  403. case L'S': {
  404. bSetSignature = TRUE;
  405. shift = 2; // account for /s <New Signature>
  406. break;
  407. }
  408. }
  409. }
  410. }
  411. if (argc >= shift + 2) {
  412. swscanf(argv[shift + 1], L"%lu", &ulDiskNumber);
  413. }
  414. else {
  415. //
  416. // We need at least one argument--the disk number
  417. //
  418. pPrintUsage(argv[0]);
  419. return ERROR_INVALID_PARAMETER;
  420. }
  421. if (bSetSignature) {
  422. //
  423. // Set the signature of the disk to a new value
  424. //
  425. swscanf(argv[shift], L"%lu", &dwNewSignature);
  426. bResult = pSetSignature(ulDiskNumber, dwNewSignature);
  427. }
  428. else {
  429. //
  430. // Wipe the disk. Get the amount to zero-out at the start
  431. // and end of disk
  432. //
  433. if (argc >= shift + 3) {
  434. swscanf(argv[shift + 2], L"%lu", &ulInitialMB);
  435. if (argc >= shift + 4) {
  436. swscanf(argv[shift + 3], L"%lu", &ulFinalMB);
  437. }
  438. }
  439. bResult = pWipeDisk(ulDiskNumber, ulInitialMB, ulFinalMB);
  440. }
  441. if (bResult) {
  442. wprintf(L"Done.\n");
  443. }
  444. return (bResult ? 0 : GetLastError());
  445. }