SDEL.C
上传用户:lw2005ff
上传日期:2007-01-07
资源大小:35k
文件大小:31k
源码类别:

系统编程

开发平台:

Visual C++

  1. //--------------------------------------------------------------------
  2. //
  3. // SDelete - Secure Delete
  4. // Copyright (C) 1999 Mark Russinovich
  5. // Systems Internals - http://www.sysinternals.com
  6. //
  7. // This program implements a secure delete function for 
  8. // Windows NT/2K. It even works on WinNT compressed, encrypted 
  9. // and sparse files.
  10. //
  11. // This program is copyrighted. You may not use the source, or 
  12. // derived version of the source, in a secure delete application.
  13. // You may use the source or techniques herein in applications
  14. // with a purpose other than secure delete.
  15. //
  16. //--------------------------------------------------------------------
  17. #include <windows.h>
  18. #include <tchar.h>
  19. #include <stdio.h>
  20. #include <time.h>
  21. #include "defrag.h"
  22. //--------------------------------------------------------------------
  23. //                         D E F I N E S
  24. //--------------------------------------------------------------------
  25. //
  26. // Invalid longlong number
  27. //
  28. #define LLINVALID ((ULONGLONG) -1)
  29. //
  30. // Size of the buffer we read file mapping information into.
  31. // The buffer is big enough to hold the 16 bytes that 
  32. // come back at the head of the buffer (the number of entries 
  33. // and the starting virtual cluster), as well as 512 pairs
  34. // of [virtual cluster, logical cluster] pairs.
  35. //
  36. #define FILEMAPSIZE (16384+2)
  37. //--------------------------------------------------------------------
  38. //                        G L O B A L S
  39. //--------------------------------------------------------------------
  40. //
  41. // Global variables
  42. //
  43. BOOLEAN Silent = FALSE;
  44. BOOLEAN Recurse = FALSE;
  45. BOOLEAN ZapFreeSpace = FALSE;
  46. BOOLEAN CleanCompressedFiles = FALSE;
  47. DWORD NumPasses = 1;
  48. DWORD FilesFound = 0;
  49. BOOL (__stdcall *pGetDiskFreeSpaceEx)(
  50.   LPCTSTR lpDirectoryName,                 // pointer to the directory name
  51.   PULARGE_INTEGER lpFreeBytesAvailableToCaller, // receives the number of bytes on
  52. // disk available to the caller
  53.   PULARGE_INTEGER lpTotalNumberOfBytes,    // receives the number of bytes on disk
  54.   PULARGE_INTEGER lpTotalNumberOfFreeBytes // receives the free bytes on disk
  55. );
  56.  
  57. //----------------------------------------------------------------------
  58. //
  59. // PrintNtError
  60. //
  61. // Formats an error message for the last native error.
  62. //
  63. //----------------------------------------------------------------------
  64. void PrintNtError( NTSTATUS status )
  65. {
  66. TCHAR *errMsg;
  67. FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  68. NULL, RtlNtStatusToDosError( status ), 
  69. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
  70. (LPTSTR) &errMsg, 0, NULL );
  71. _tprintf(_T("%sn"), errMsg );
  72. LocalFree( errMsg );
  73. }
  74. //--------------------------------------------------------------------
  75. //
  76. // PrintWin32Error
  77. // 
  78. // Translates a Win32 error into a text equivalent
  79. //
  80. //--------------------------------------------------------------------
  81. void PrintWin32Error( DWORD ErrorCode )
  82. {
  83. LPVOID lpMsgBuf;
  84. FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  85. NULL, ErrorCode, 
  86. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  87. (LPTSTR) &lpMsgBuf, 0, NULL );
  88. _tprintf(_T("%sn"), lpMsgBuf );
  89. LocalFree( lpMsgBuf );
  90. }
  91. //--------------------------------------------------------------------
  92. //
  93. // OverwriteFileName
  94. //
  95. // Securely deletes a file's original name by renaming it several
  96. // times. This works by changing each non-'.' character in the file's
  97. // name to successive alphabetic characters, thus overwriting the
  98. // name 26 times.
  99. //
  100. //--------------------------------------------------------------------
  101. VOID OverwriteFileName( PTCHAR FileName, PTCHAR LastFileName )
  102. {
  103. TCHAR newName[MAX_PATH];
  104. PTCHAR lastSlash;
  105. DWORD i, j, index;
  106. _tcscpy( LastFileName, FileName );
  107. lastSlash = _tcsrchr( LastFileName, _T('\'));
  108. index = (lastSlash - LastFileName)/sizeof(TCHAR);
  109. //
  110. // Loop through each letter in the English alphabet
  111. //
  112. _tcscpy( newName, FileName );
  113. for( i = 0; i < 26; i++ ) {
  114. //
  115. // Replace each non-'.' character with the same letter
  116. //
  117. for( j = index+1 ; j < _tcsclen( FileName ); j++ ) {
  118. if( FileName[j] != _T('.')) newName[j] = (TCHAR) i + _T('A');
  119. }
  120. //
  121. // Got a new name so rename
  122. //
  123. if( !MoveFile( LastFileName, newName )) {
  124. //
  125. // Bail on error
  126. //
  127. return;
  128. }
  129. _tcscpy( LastFileName, newName );
  130. }
  131. }
  132. //--------------------------------------------------------------------
  133. //
  134. // SecureOverwrite
  135. //
  136. // This function implements a secure santize of rigid (removable 
  137. // and fixed) disk media as per the Department of Defense clearing 
  138. // and sanitizing standard: DOD 5220.22-M
  139. // 
  140. // The standard states that hard disk media is sanatized by 
  141. // overwriting with a character, then the character's complement,
  142. // and then a random character. Note that the standard specicically
  143. // states that this method is not suitable for TOP SECRET information.
  144. // TOP SECRET data sanatizing is only achievable by a Type 1 or 2 
  145. // degauss of the disk, or by disintegrating, incinerating, 
  146. // pulverizing, shreding, or melting the disk.
  147. //
  148. //--------------------------------------------------------------------
  149. BOOLEAN SecureOverwrite( HANDLE FileHandle, DWORD Length )
  150. {
  151. #define CLEANBUFSIZE 65536
  152. static PBYTE cleanBuffer[3];
  153. static BOOLEAN buffersAlloced = FALSE;
  154. DWORD i, j, passes;
  155. DWORD bytesWritten, bytesToWrite, totalWritten;
  156. LONG seekLength;
  157. BOOLEAN status;
  158. //
  159. // Allocate our cleaning buffers if necessary (we just let program
  160. // exit free the buffers).
  161. //
  162. if( !buffersAlloced ) {
  163. //
  164. // Seed the random number generator
  165. //
  166. srand( (unsigned)time( NULL ) );
  167. for( i = 0; i < 3; i++ ) {
  168. cleanBuffer[i] = VirtualAlloc( NULL, CLEANBUFSIZE, MEM_COMMIT, PAGE_READWRITE );
  169. if( !cleanBuffer[i] ) {
  170. for( j = 0; j < i; j++ ) {
  171. VirtualFree( cleanBuffer[j], 0, MEM_RELEASE );
  172. }
  173. return FALSE;
  174. }
  175. //
  176. // Fill each buffer with a different signature
  177. //
  178. switch( i ) {
  179. case 0:
  180. // do nothing - buffer is zero-filled by Windows
  181. break;
  182. case 1:
  183. // fill with complement of 0 - 0xFF
  184. memset( cleanBuffer[i], 0xFF, CLEANBUFSIZE );
  185. break;
  186. case 2:
  187. // fill with a random value
  188. for( j = 0; j < CLEANBUFSIZE; j++ ) cleanBuffer[i][j] = (BYTE) rand();
  189. break;
  190. }
  191. }
  192. buffersAlloced = TRUE;
  193. }
  194. //
  195. // Do the overwrite
  196. //
  197. seekLength = (LONG) Length;
  198. for( passes = 0; passes < NumPasses; passes++ ) {
  199. if( passes != 0 ) {
  200. SetFilePointer( FileHandle, -seekLength, NULL, FILE_CURRENT );
  201. }
  202. for( i = 0; i < 3; i++ ) {
  203. //
  204. // Move back to the start of where we're overwriting
  205. //
  206. if( i != 0 ) {
  207. SetFilePointer( FileHandle, -seekLength, NULL, FILE_CURRENT );
  208. }
  209. //
  210. // Loop and overwrite
  211. //
  212. bytesToWrite = Length;
  213. totalWritten = 0;
  214. while( totalWritten < Length ) {
  215. bytesToWrite = Length - totalWritten;
  216. if( bytesToWrite > CLEANBUFSIZE ) bytesToWrite = CLEANBUFSIZE;
  217. status = WriteFile( FileHandle, cleanBuffer[i], bytesToWrite, &bytesWritten, NULL );
  218. if( !status ) return FALSE;
  219. //
  220. // Note: no need to flush since the file is opened with write-through or 
  221. // no cache buffering
  222. //
  223. totalWritten += bytesWritten;
  224. }
  225. }
  226. }
  227. return TRUE;
  228. }
  229. //--------------------------------------------------------------------
  230. //
  231. // SecureDelete
  232. //
  233. // Performs a secure delete on the specified file.
  234. //
  235. //--------------------------------------------------------------------
  236. VOID SecureDelete( PTCHAR FileName, DWORD FileLengthHi,
  237. DWORD FileLengthLo ) 
  238. {
  239. HANDLE hFile;
  240. ULONGLONG bytesToWrite, bytesWritten;
  241. ULARGE_INTEGER fileLength;
  242. TCHAR   lastFileName[MAX_PATH];
  243. //
  244. // First, open the file in overwrite mode
  245. //
  246. hFile = CreateFile( FileName, GENERIC_WRITE, 
  247. FILE_SHARE_READ|FILE_SHARE_WRITE,
  248. NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL );
  249. if( hFile == INVALID_HANDLE_VALUE ) {
  250. _tprintf( _T("nError opening %s for delete: "), FileName );
  251. PrintWin32Error( GetLastError());
  252. return;
  253. }
  254. //
  255. // If the file has a non-zero length, fill it with 0's first in order
  256. // to preserve is cluster allocation.
  257. //
  258. if( FileLengthLo || FileLengthHi ) {
  259. //
  260. // Seek to the last byte of the file
  261. //
  262. FileLengthLo--;
  263. if( FileLengthLo == (DWORD) -1 && FileLengthHi ) FileLengthHi--;
  264. SetFilePointer( hFile, FileLengthLo, &FileLengthHi, FILE_BEGIN );
  265. //
  266. // Write one zero byte, which causes the file system to fill the entire
  267. // file's on-disk contents with 0.
  268. //
  269. if( !SecureOverwrite( hFile, 1 )) {
  270. _tprintf( _T("nError overwriting %s: "), FileName );
  271. PrintWin32Error( GetLastError() );
  272. CloseHandle( hFile );
  273. return;
  274. }
  275. //
  276. // Now go back to the start of the file and overwrite the rest of the
  277. // file.
  278. //
  279. SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
  280. fileLength.LowPart = FileLengthLo;
  281. fileLength.HighPart = FileLengthHi;
  282. bytesWritten = 0;
  283. while( bytesWritten < fileLength.QuadPart ) {
  284. bytesToWrite = min( fileLength.QuadPart - bytesWritten, 65536 );
  285. if( !SecureOverwrite( hFile, (DWORD) bytesToWrite )) {
  286. _tprintf( _T("nError overwriting %s: "), FileName );
  287. PrintWin32Error( GetLastError() );
  288. CloseHandle( hFile );
  289. return;
  290. }
  291. bytesWritten += bytesToWrite;
  292. }
  293. }
  294. //
  295. // Done!
  296. //
  297. CloseHandle( hFile );
  298. //
  299. // Rename the file a few times
  300. //
  301. OverwriteFileName( FileName, lastFileName );
  302. //
  303. // Now we can delete the file
  304. //
  305. if( !DeleteFile( lastFileName ) ) {
  306. _tprintf( _T("nError deleting %s: "), FileName );
  307. PrintWin32Error( GetLastError() );
  308. //
  309. // Rename back to original name so as not to confuse the user
  310. //
  311. if( !MoveFile( lastFileName, FileName )) {
  312. _tprintf( _T("nError renaming file back to original name. File is left as %sn"),
  313. lastFileName );
  314. }
  315. return;
  316. }
  317. if( !Silent ) _tprintf( _T("deleted.n"));
  318. }
  319. //--------------------------------------------------------------------
  320. //
  321. // ScanFile
  322. //
  323. // This is only invoked for compressed, encrypted or sparse files, 
  324. // which exists only on NTFS drives (WinNT/2K). Thus, we can use
  325. // the defrag API to zap the clusters belonging to the file 
  326. // Determines if the the file is non-resident (outside the MFT), and 
  327. // if so and we were able to open the volume for write access, we zap 
  328. // the clusters.
  329. //
  330. //--------------------------------------------------------------------
  331. BOOLEAN ScanFile( HANDLE VolumeHandle,
  332.   DWORD ClusterSize,
  333.   HANDLE FileHandle, 
  334.   PBOOLEAN ReallyCompressed,
  335.   PBOOLEAN ZappedFile )
  336. {
  337. DWORD status;
  338. int i;
  339. IO_STATUS_BLOCK ioStatus;
  340. ULONGLONG startVcn, prevVcn;
  341. LARGE_INTEGER clusterOffset;
  342. ULONGLONG endOfPrevRun;
  343. PGET_RETRIEVAL_DESCRIPTOR fileMappings;
  344. ULONGLONG fileMap[ FILEMAPSIZE ];
  345. int lines = 0;
  346. //
  347. // Assume file is in an MFT record.
  348. //
  349. *ReallyCompressed = FALSE;
  350. *ZappedFile = FALSE;
  351. startVcn = 0;
  352. endOfPrevRun = LLINVALID;
  353. fileMappings = (PGET_RETRIEVAL_DESCRIPTOR) fileMap;
  354. while( !(status = NtFsControlFile( FileHandle, NULL, NULL, 0, &ioStatus,
  355. FSCTL_GET_RETRIEVAL_POINTERS,
  356. &startVcn, sizeof( startVcn ),
  357. fileMappings, FILEMAPSIZE * sizeof(ULONGLONG) ) ) ||
  358.  status == STATUS_BUFFER_OVERFLOW ||
  359.  status == STATUS_PENDING ) {
  360. // 
  361. // If the operation is pending, wait for it to finish
  362. //
  363. if( status == STATUS_PENDING ) {
  364. WaitForSingleObject( FileHandle, INFINITE ); 
  365. //
  366. // Get the status from the status block
  367. //
  368. if( ioStatus.Status != STATUS_SUCCESS && 
  369. ioStatus.Status != STATUS_BUFFER_OVERFLOW ) {
  370. return ioStatus.Status == STATUS_SUCCESS;
  371. }
  372. }
  373. //
  374. // Loop through the buffer of number/cluster pairs, printing them
  375. // out.
  376. //
  377. startVcn = fileMappings->StartVcn;
  378. prevVcn  = fileMappings->StartVcn;
  379. for( i = 0; i < (ULONGLONG) fileMappings->NumberOfPairs; i++ ) {  
  380. //
  381. // On NT 4.0, a compressed virtual run (0-filled) is 
  382. // identified with a cluster offset of -1
  383. //
  384. if( fileMappings->Pair[i].Lcn != LLINVALID ) {
  385. //
  386. // Its compressed and outside the zone
  387. //
  388. *ReallyCompressed = TRUE;
  389. //
  390. // Overwrite the clusters if we were able to open the volume
  391. // for write access.
  392. //
  393. if( VolumeHandle != INVALID_HANDLE_VALUE ) {
  394. clusterOffset.QuadPart = fileMappings->Pair[i].Lcn * ClusterSize;
  395. SetFilePointer( VolumeHandle, clusterOffset.LowPart,
  396. &clusterOffset.HighPart, FILE_BEGIN );
  397. if( !SecureOverwrite( VolumeHandle,
  398. ClusterSize * (DWORD) (fileMappings->Pair[i].Vcn - startVcn) )) {
  399. //
  400. // Couldn't zap the clusters, so we'll have to clean the free space
  401. //
  402. return TRUE;
  403. }
  404. } else {
  405. return TRUE;
  406. }
  407. }
  408. startVcn = fileMappings->Pair[i].Vcn;
  409. }
  410. //
  411. // If the buffer wasn't overflowed, then we're done
  412. //
  413. if( !status ) break;
  414. }
  415. //
  416. // Return now if there were any errors
  417. //
  418. if( status && status != STATUS_INVALID_PARAMETER && !Silent ) {
  419. printf("Scanning file: ");
  420. PrintNtError( status );
  421. }
  422. // 
  423. // If we made through with no errors we've overwritten all the file's
  424. // clusters.
  425. //
  426. if( status == STATUS_SUCCESS ) *ZappedFile = TRUE;
  427. return status == STATUS_SUCCESS;
  428. }
  429. //--------------------------------------------------------------------
  430. //
  431. // SecureDeleteCompressed
  432. //
  433. // More complicated than a regular file - we actually try to use
  434. // direct disk access to overwrite the clusters that are used by a 
  435. // compressed file. The function returns FALSE if the file is
  436. // not really compressed (it is stored as resident data in the MFT).
  437. //
  438. //--------------------------------------------------------------------
  439. BOOLEAN SecureDeleteCompressed( PTCHAR FileName ) 
  440. {
  441. HANDLE hFile;
  442. BOOLEAN reallyCompressed = FALSE;
  443. BOOLEAN zappedFile = FALSE;
  444. TCHAR lastFileName[MAX_PATH];
  445. static TCHAR volumeName[] = _T("\\.\A:");
  446. static TCHAR volumeRoot[] = _T("A:\");
  447. static HANDLE hVolume = INVALID_HANDLE_VALUE;
  448. static DWORD clusterSize;
  449. DWORD sectorsPerCluster, bytesPerSector, freeClusters, totalClusters;
  450. //
  451. // If we haven't opened the volume, attempt it now
  452. //
  453. if( hVolume == INVALID_HANDLE_VALUE ) {
  454. volumeName[4] = FileName[0];
  455. hVolume = CreateFile( volumeName, GENERIC_READ|GENERIC_WRITE,
  456. FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  457. 0, 0 );
  458. volumeRoot[0] = FileName[0];
  459. GetDiskFreeSpace( volumeRoot, &sectorsPerCluster, &bytesPerSector,
  460. &freeClusters, &totalClusters );
  461. clusterSize = bytesPerSector * sectorsPerCluster;
  462. }
  463. //
  464. // Open the file exclusively
  465. //
  466. hFile = CreateFile( FileName, GENERIC_READ, 
  467. 0,NULL, OPEN_EXISTING, 0, NULL );
  468. if( hFile == INVALID_HANDLE_VALUE ) {
  469. _tprintf( _T("nError opening %s for compressed file scan: "), FileName );
  470. PrintWin32Error( GetLastError());
  471. return TRUE;
  472. }
  473. //
  474. // Scan the location of the file
  475. //
  476. if( !ScanFile( hVolume, clusterSize, hFile, 
  477. &reallyCompressed, &zappedFile )) {
  478. CloseHandle( hFile );
  479. return TRUE;
  480. }
  481. // 
  482. // Done with the file handle
  483. //
  484. CloseHandle( hFile );
  485. //
  486. // If the file is really compressed (it is non-resident),
  487. // we can delete it now.
  488. //
  489. if( reallyCompressed ) {
  490. //
  491. // Rename the file a few times
  492. //
  493. CloseHandle( hFile );
  494. OverwriteFileName( FileName, lastFileName );
  495. if( !DeleteFile( lastFileName )) {
  496. //
  497. // Rename back to the original name on error so as
  498. // not to confuse the user
  499. //
  500. _tprintf( _T("nError deleting %s: "), FileName );
  501. PrintWin32Error( GetLastError() );
  502. if( !MoveFile( lastFileName, FileName )) {
  503. _tprintf( _T("nError renaming file back to original name. File is left as %sn"),
  504. lastFileName );
  505. }
  506. return TRUE;
  507. }
  508. //
  509. // If we couldn't directly overwrite the file's clusters, we'll
  510. // have to clean free space to overwrite them indirectly
  511. //
  512. if( !zappedFile ) CleanCompressedFiles = TRUE;
  513. if( !Silent ) _tprintf( _T("deleted.n"));
  514. }
  515. //
  516. // Return TRUE if the file had clusters outside the MFT
  517. //
  518. return reallyCompressed;
  519. }
  520. //--------------------------------------------------------------------
  521. //
  522. // ProcessFile
  523. //
  524. // Performs a secure delete on the specified file.
  525. //
  526. //--------------------------------------------------------------------
  527. VOID ProcessFile( PWIN32_FIND_DATA FindData, TCHAR *FileName )
  528. {
  529. //
  530. // Directories are deleted by ProcessDirectory
  531. //
  532. if( FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) return;
  533. //
  534. // Got a matching file
  535. //
  536. FilesFound++;
  537. //
  538. // Print which file we're working on
  539. //
  540. if( !Silent ) {
  541. _tprintf( _T("%s..."), FileName );
  542. fflush( stdout );
  543. }
  544. //
  545. // If the file is compressed, we have to go a different path
  546. //
  547. if( FindData->dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED ||
  548. FindData->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED  ||
  549. FindData->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE ) {
  550. //
  551. // We need to determine where the compressed file is located 
  552. // physically on disk.
  553. //
  554. if( SecureDeleteCompressed( FileName )) return;
  555. // 
  556. // Regular path, non-compressed/encrypted/sparse file or one of those
  557. // types of files with their data resident in an MFT record: perform a 
  558. // simple secure delete
  559. //
  560. SecureDelete( FileName, FindData->nFileSizeHigh,
  561. FindData->nFileSizeLow );
  562. }
  563. //--------------------------------------------------------------------
  564. //
  565. // ProcessDirectory
  566. // 
  567. // Recursive routine that passes files to the delete function.
  568. //
  569. //--------------------------------------------------------------------
  570. void ProcessDirectory( TCHAR *PathName, TCHAR *SearchPattern )
  571. {
  572. TCHAR subName[MAX_PATH], fileSearchName[MAX_PATH], searchName[MAX_PATH];
  573. HANDLE dirHandle, patternHandle;
  574. static BOOLEAN firstCall = TRUE;
  575. static BOOLEAN  deleteDirectories = FALSE;
  576. WIN32_FIND_DATA foundFile;
  577. //
  578. // Scan the files and/or directories if this is a directory
  579. //
  580. if( firstCall ) {
  581. if( _tcsrchr( PathName, '*' ) ) {
  582.             if( _tcsrchr( PathName, '\' ) ) {
  583.                 _stprintf( SearchPattern, _tcsrchr( PathName, '\' )+1 );
  584.                 _tcscpy( searchName, PathName );
  585.                 _tcscpy( _tcsrchr( searchName, '\')+1, _T("*.*") );
  586. if( !_tcscmp( SearchPattern, _T("*.*")) ||
  587. !_tcscmp( SearchPattern, _T("*"))) {
  588. deleteDirectories = TRUE;
  589. }
  590.             } else {
  591.                 
  592.                 _stprintf( SearchPattern, PathName );
  593.                 _tcscpy( searchName, PathName );
  594.             }
  595.             _stprintf( fileSearchName, _T("%s"), PathName );
  596. } else {
  597. _stprintf( SearchPattern, _T("*.*") );
  598. _stprintf( searchName, _T("%s"), PathName );
  599.             _stprintf( fileSearchName, _T("%s"), PathName );
  600. deleteDirectories = TRUE;
  601. }
  602. } else {
  603. _stprintf( searchName, _T("%s\*.*"), PathName );
  604. _stprintf( fileSearchName, _T("%s\%s"), PathName, SearchPattern );
  605. }
  606. //
  607. // Process all the files, according to the search pattern
  608. //
  609. if( (patternHandle = FindFirstFile( fileSearchName, &foundFile )) != 
  610. INVALID_HANDLE_VALUE  ) {
  611. do {
  612. if( _tcscmp( foundFile.cFileName, _T(".") ) &&
  613. _tcscmp( foundFile.cFileName, _T("..") )) {
  614. _tcscpy( subName, searchName );
  615. if( _tcsrchr( subName, '\' ) ) 
  616. _tcscpy( _tcsrchr( subName, '\')+1, foundFile.cFileName );
  617. else
  618. _tcscpy( subName, foundFile.cFileName );
  619. //
  620. // Do this file/directory
  621. //
  622. ProcessFile( &foundFile, subName );
  623. }
  624. } while( FindNextFile( patternHandle, &foundFile ));
  625. FindClose( patternHandle );
  626. }
  627. //
  628. // Now recurse if we're supposed to
  629. //
  630. if( Recurse ) {
  631.         if( firstCall && !_tcsrchr( searchName, L'\') ) {
  632.             if( _tcsrchr( searchName, L'*' )) {
  633.                 if( (dirHandle = FindFirstFile( _T("*.*"), &foundFile )) == 
  634.                     INVALID_HANDLE_VALUE  ) {
  635.                     //
  636.                     // Nothing to process
  637.                     //
  638.                     return;
  639.                 }
  640.             } else {
  641.                 if( (dirHandle = FindFirstFile( searchName, &foundFile )) == 
  642.                     INVALID_HANDLE_VALUE  ) {
  643.                     //
  644.                     // Nothing to process
  645.                     //
  646.                     return;
  647.                 }
  648.             }
  649.         } else {
  650.             if( (dirHandle = FindFirstFile( searchName, &foundFile )) == 
  651.                 INVALID_HANDLE_VALUE  ) {
  652.                 //
  653.                 // Nothing to process
  654.                 //
  655.                 return;
  656.             }
  657.         }
  658.         firstCall = FALSE;
  659. do {
  660. if( (foundFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  661. _tcscmp( foundFile.cFileName, _T(".") ) &&
  662. _tcscmp( foundFile.cFileName, _T("..") )) {
  663. _tcscpy( subName, searchName );
  664. if( _tcsrchr( subName, '\' ) ) 
  665. _tcscpy( _tcsrchr( subName, '\')+1, foundFile.cFileName );
  666. else
  667. _tcscpy( subName, foundFile.cFileName );
  668. //
  669. // Go into this directory
  670. //
  671. ProcessDirectory( subName, SearchPattern );
  672. //
  673. // Delete the directory if we're supposed to
  674. //
  675. if( deleteDirectories ) {
  676. if( !RemoveDirectory( subName )) {
  677. _tprintf( _T("nError deleting %s: "), subName );
  678. PrintWin32Error( GetLastError() );
  679. }
  680. }
  681. }
  682. } while( FindNextFile( dirHandle, &foundFile ));
  683. FindClose( dirHandle );
  684. }
  685. }
  686. //--------------------------------------------------------------------
  687. //
  688. // CleanFreeSpace
  689. //
  690. // This function overwrites all free-space data areas on a disk, 
  691. // including free space in the MFT. Note that it does not overwrite 
  692. // freespace filename information.
  693. //
  694. //--------------------------------------------------------------------
  695. BOOLEAN CleanFreeSpace( PTCHAR DrivePath )
  696. {
  697. TCHAR tempFileName[MAX_PATH];
  698. ULARGE_INTEGER bytesAvail, totalBytes, freeBytes;
  699. DWORD sectorsPerCluster, bytesPerSector, totalClusters, freeClusters;
  700. ULONGLONG tempSize = 0;
  701. TCHAR progress[] = _T("|/-\|/-\");
  702. HANDLE hTempFile;
  703. BOOLEAN createdFile;
  704. DWORD percent, cleanSize, mftFilesCreated;
  705. DWORD prevSize, prevPercent = 0;
  706. if( DrivePath[1] != ':' ) {
  707. _tprintf( _T("Cannot clean free space for UNC drivenn"));
  708. return FALSE;
  709. }
  710. //
  711. // Drive letter - move to one past slash
  712. //
  713. DrivePath[3] = 0;
  714. if( CleanCompressedFiles ) {
  715. _tprintf(_T("Cleaning free space to securely delete compressed files: 0%%"));
  716. } else {
  717. _tprintf(_T("Cleaning free space on %s: 0%%"), DrivePath );
  718. }
  719. fflush( stdout );
  720. if( !GetDiskFreeSpace( DrivePath, &sectorsPerCluster, &bytesPerSector,
  721. &freeClusters, &totalClusters )) {
  722. _tprintf( _T("Could not determine disk cluster size: "));
  723. PrintWin32Error( GetLastError());
  724. return FALSE;
  725. }
  726. #if UNICODE
  727. if( !(pGetDiskFreeSpaceEx = (PVOID) GetProcAddress( GetModuleHandle( _T("kernel32.dll") ),
  728. "GetFreeDiskSpaceExW" ))) {
  729. #else
  730. if( !(pGetDiskFreeSpaceEx = (PVOID) GetProcAddress( GetModuleHandle( _T("kernel32.dll") ),
  731. "GetDiskFreeSpaceExA" ))) {
  732. #endif
  733. bytesAvail.QuadPart = sectorsPerCluster * freeClusters * bytesPerSector;
  734. } else {
  735. if( !pGetDiskFreeSpaceEx( DrivePath, &bytesAvail, &totalBytes, &freeBytes )) {
  736. _tprintf( _T("Could not determine amount of free space: "));
  737. PrintWin32Error( GetLastError());
  738. return FALSE;
  739. }
  740. }
  741. //
  742. // If the user doesn't have access to all the free space, we can't perform a full clean
  743. //
  744. if( bytesAvail.QuadPart != freeBytes.QuadPart ) {
  745. _tprintf(_T("Your disk quota prevents you from cleaning free space on this drive.nn"));
  746. return FALSE;
  747. }
  748. _stprintf( tempFileName, _T("%sSDELTEMP"), DrivePath );
  749. hTempFile = CreateFile( tempFileName, GENERIC_WRITE, 
  750. 0, NULL, CREATE_NEW, 
  751. FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN|
  752. FILE_FLAG_DELETE_ON_CLOSE|FILE_ATTRIBUTE_HIDDEN, NULL );
  753. if( hTempFile == INVALID_HANDLE_VALUE ) {
  754. _tprintf( _T("Could not create free-space cleanup file: "));
  755. PrintWin32Error( GetLastError());
  756. return FALSE;
  757. }
  758. //
  759. // Allcate a buffer that is a cluster in size
  760. //
  761. cleanSize = sectorsPerCluster * bytesPerSector * 128;
  762. //
  763. // Grow the file by cluster size chunks until we fail
  764. //
  765. while( cleanSize > bytesPerSector * sectorsPerCluster ) {
  766. if( SecureOverwrite( hTempFile, cleanSize )) {
  767. tempSize += cleanSize;
  768. percent = (DWORD) ((tempSize * 100)/ freeBytes.QuadPart );
  769. if( percent != prevPercent ) {
  770. if( CleanCompressedFiles ) {
  771. _tprintf(_T("rCleaning free space to securely delete compressed files: %d%%"),
  772. percent );
  773. } else {
  774. _tprintf(_T("rCleaning free space on %s: %d%%"), DrivePath, percent );
  775. }
  776. prevPercent = percent;
  777. }
  778. } else {
  779. cleanSize -= bytesPerSector * sectorsPerCluster;
  780. }
  781. }
  782. //
  783. // There's less than a full cluster (outside the MFT if this is NTFS) free. 
  784. // Let's allocate another file to take care of it.
  785. //
  786. _stprintf( tempFileName, _T("%sSDELTEMP1"), DrivePath );
  787. hTempFile = CreateFile( tempFileName, GENERIC_WRITE, 
  788. 0, NULL, CREATE_NEW, 
  789. FILE_FLAG_SEQUENTIAL_SCAN|FILE_FLAG_DELETE_ON_CLOSE|
  790. FILE_ATTRIBUTE_HIDDEN|FILE_FLAG_WRITE_THROUGH, NULL );
  791. if( hTempFile != INVALID_HANDLE_VALUE ) {
  792. while( cleanSize ) {
  793. if( !SecureOverwrite( hTempFile, cleanSize )) {
  794. cleanSize--;
  795. }
  796. }
  797. }
  798. //
  799. // If we're just zapping free space, and this is NTFS, we have to take care of
  800. // deleted files within the MFT. We do this by creating as many of the largest sized
  801. // files we can (if there is space in the MFT, we'll be able to create non-zero sized
  802. // files, where the data is resident in the MFT record).
  803. //
  804. if( ZapFreeSpace ) {
  805. mftFilesCreated = 0;
  806. prevSize = 4096; // max MFT record size
  807. while( 1 ) {
  808. _stprintf( tempFileName, _T("%sSDELMFT%06d"), DrivePath, mftFilesCreated++ );
  809. hTempFile = CreateFile( tempFileName, GENERIC_WRITE, 
  810. 0, NULL, CREATE_NEW, 
  811. FILE_FLAG_SEQUENTIAL_SCAN|FILE_FLAG_DELETE_ON_CLOSE|
  812. FILE_ATTRIBUTE_HIDDEN, NULL );
  813. if( hTempFile == INVALID_HANDLE_VALUE ) {
  814. break;
  815. }
  816. //
  817. // Mft record can be up to 4K in size
  818. //
  819. cleanSize = prevSize;
  820. createdFile = FALSE;
  821. while( cleanSize ) {
  822. if( !SecureOverwrite( hTempFile, cleanSize )) {
  823. cleanSize--;
  824. } else {
  825. prevSize = cleanSize;
  826. createdFile = TRUE;
  827. }
  828. }
  829. //
  830. // If the only file we could create is length 0, then this is FAT
  831. //
  832. if( !createdFile ) break;
  833. if( mftFilesCreated == 1 ) {
  834. _tprintf( _T("r                                                           "));
  835. _tprintf( _T("rCleaning MFT...%c"),
  836. progress[ mftFilesCreated % 8 ]);
  837. // Don't close the file, since we want it to keep the space until we're
  838. // done.
  839. }
  840. }
  841. //
  842. // Done. No need to close our handles, since they are all delete-on-close.
  843. //
  844. _tprintf(_T("rFree space cleaned on %s                                       n"),
  845. DrivePath );
  846. return TRUE;
  847. }
  848. //--------------------------------------------------------------------
  849. //
  850. // LocateNativeEntryPoints
  851. //
  852. //--------------------------------------------------------------------
  853. VOID LocateNativeEntryPoints()
  854. {
  855. //
  856. // If we're on Win9x, just return
  857. //
  858. if( GetVersion() >= 0x80000000) return;
  859.     //
  860.     // Load the NTDLL entry point we need
  861.     //
  862. if( !(NtFsControlFile = (void *) GetProcAddress( GetModuleHandle(_T("ntdll.dll")),
  863. "NtFsControlFile" )) ) {
  864. _tprintf(_T("nCould not find NtFsControlFile entry point in NTDLL.DLLn"));
  865. exit(1);
  866. }
  867. if( !(RtlNtStatusToDosError = (void *) GetProcAddress( GetModuleHandle(_T("ntdll.dll")),
  868. "RtlNtStatusToDosError" )) ) {
  869. _tprintf(_T("nCould not find RtlNtStatusToDosError entry point in NTDLL.DLLn"));
  870. exit(1);
  871. }
  872. }
  873. //--------------------------------------------------------------------
  874. //
  875. // Usage
  876. //
  877. // Tell user how to use the program.
  878. //
  879. //--------------------------------------------------------------------
  880. int Usage( TCHAR *ProgramName ) 
  881. {
  882.     _tprintf(_T("usage: %s [-p passes] [-s] [-q] <file or directory>n"), ProgramName );
  883. _tprintf(_T("       %s [-p passes] -z [drive letter]n"), ProgramName );
  884. _tprintf(_T("   -p passes  Specifies number of overwrite passes (default is 1)n"));
  885.     _tprintf(_T("   -s         Recurse subdirectoriesn"));
  886.     _tprintf(_T("   -q         Don't print errors (Quiet)n"));
  887. _tprintf(_T("   -z         Clean free spacenn"));
  888.     return -1;
  889. }
  890. //--------------------------------------------------------------------
  891. //
  892. // Main
  893. //
  894. // Runs the show. 
  895. //
  896. //--------------------------------------------------------------------
  897. int _tmain( int argc, TCHAR *argv[] ) 
  898. {
  899.     TCHAR       searchPattern[MAX_PATH];
  900. TCHAR searchPath[MAX_PATH];
  901. PTCHAR filePart;
  902. BOOL foundFileArg = FALSE;
  903. int i;
  904.     //
  905.     // Print banner and perform parameter check
  906.     //
  907.     _tprintf(_T("nSDelete - Secure Delete v1.1n") );
  908.     _tprintf(_T("Copyright (C) 1999 Mark Russinovichn"));
  909.     _tprintf(_T("Systems Internals - http://www.sysinternals.comnn"));
  910.     if( argc < 2 ) {
  911.         return Usage( argv[0] );
  912.     }
  913.     for( i = 1; i < argc; i++ ) {
  914.     if( !_tcsicmp( argv[i], _T("/s") ) ||
  915. !_tcsicmp( argv[i], _T("-s") )) {
  916. Recurse = TRUE;
  917. } else if( !_tcsicmp( argv[i], _T("/q") ) ||
  918.    !_tcsicmp( argv[i], _T("-q") )) {
  919. Silent = TRUE;
  920. } else if( !_tcsicmp( argv[i], _T("/z") ) ||
  921.    !_tcsicmp( argv[i], _T("-z") )) {
  922. ZapFreeSpace = TRUE;
  923. } else if( !_tcsicmp( argv[i], _T("/p") ) ||
  924.    !_tcsicmp( argv[i], _T("-p") )) {
  925. NumPasses = atoi( argv[i+1] );
  926. if( !NumPasses ) return Usage( argv[0] );
  927. i++;
  928. } else if( !_tcsicmp( argv[i], _T("/?") ) ||
  929.    !_tcsicmp( argv[i], _T("-?") )) {
  930.    
  931. return Usage( argv[0] );
  932. } else {
  933. if( foundFileArg ) return Usage( argv[0] );
  934. foundFileArg = TRUE;
  935. }
  936.     }
  937. //
  938. // Have to have file if not zapping free space
  939. //
  940. if( !ZapFreeSpace && !foundFileArg ) {
  941. return Usage( argv[0] );
  942. }
  943. //
  944. // Locate Native entry points we need
  945. //
  946. LocateNativeEntryPoints();
  947. if( foundFileArg ) {
  948. //
  949. // Get canonical path name
  950. //
  951. GetFullPathName( argv[argc-1], MAX_PATH, searchPath, &filePart );
  952. }
  953. printf("SDelete is set for %d pass%s.n", NumPasses,
  954. NumPasses > 1 ? "es" : "");
  955. //
  956. // Do the deleting
  957. //
  958. if( !ZapFreeSpace ) {
  959. //
  960. // Now go and process directories
  961. //
  962. ProcessDirectory( searchPath, searchPattern );
  963. if( !FilesFound ) _tprintf(_T("No files found that match %s.n"), argv[argc-1] );
  964. } else if( !foundFileArg ) {
  965. GetCurrentDirectory( MAX_PATH, searchPath );
  966. //
  967. // If we encountered a compressed file along the way, and we couldn't directly
  968. // zap its on-disk clusters then we have to clean all the free space.
  969. //
  970. if( CleanCompressedFiles || ZapFreeSpace ) {
  971. CleanFreeSpace( searchPath );
  972. }
  973. _tprintf(_T("n"));
  974.     return 0;
  975. }