| 1 | // NTFS.cpp |
|---|
| 2 | // |
|---|
| 3 | // Eraser. Secure data removal. For Windows. |
|---|
| 4 | // Copyright © 1997-2001 Sami Tolvanen (sami@tolvanen.com). |
|---|
| 5 | // |
|---|
| 6 | // SDelete - Secure Delete |
|---|
| 7 | // Copyright (C) 1999 Mark Russinovich |
|---|
| 8 | // Systems Internals - http://www.sysinternals.com |
|---|
| 9 | // |
|---|
| 10 | // This program implements a secure delete function for |
|---|
| 11 | // Windows NT/2K. It even works on WinNT compressed, encrypted |
|---|
| 12 | // and sparse files. |
|---|
| 13 | // |
|---|
| 14 | // This program is copyrighted. You may not use the source, or |
|---|
| 15 | // derived version of the source, in a secure delete application. |
|---|
| 16 | // You may use the source or techniques herein in applications |
|---|
| 17 | // with a purpose other than secure delete. |
|---|
| 18 | |
|---|
| 19 | #include "stdafx.h" |
|---|
| 20 | #include "resource.h" |
|---|
| 21 | #include "EraserDll.h" |
|---|
| 22 | #include "Common.h" |
|---|
| 23 | #include "File.h" |
|---|
| 24 | #include "NTFS.h" |
|---|
| 25 | #include "winioctl.h" |
|---|
| 26 | // Invalid longlong number |
|---|
| 27 | |
|---|
| 28 | #define LLINVALID ((E_UINT64) -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 | |
|---|
| 39 | static bool |
|---|
| 40 | initEntryPoints(NTFSContext& ntc) |
|---|
| 41 | { |
|---|
| 42 | // load the NTDLL entry point we need |
|---|
| 43 | ntc.m_hNTDLL = AfxLoadLibrary(ERASER_MODULENAME_NTDLL); |
|---|
| 44 | |
|---|
| 45 | if (ntc.m_hNTDLL != NULL) { |
|---|
| 46 | ntc.NtFsControlFile = |
|---|
| 47 | (NTFSCONTROLFILE) GetProcAddress(ntc.m_hNTDLL, ERASER_FUNCTIONNAME_NTFSCONTROLFILE); |
|---|
| 48 | ntc.NtQueryInformationFile = |
|---|
| 49 | (NTQUERYINFORMATIONFILE) GetProcAddress(ntc.m_hNTDLL, ERASER_FUNCTIONNAME_NTQUERYINFORMATIONFILE); |
|---|
| 50 | ntc.RtlNtStatusToDosError = |
|---|
| 51 | (RTLNTSTATUSTODOSERROR) GetProcAddress(ntc.m_hNTDLL, ERASER_FUNCTIONNAME_RTLNTSTATUSTODOSERROR); |
|---|
| 52 | |
|---|
| 53 | if (ntc.NtFsControlFile == NULL || ntc.RtlNtStatusToDosError == NULL) { |
|---|
| 54 | AfxFreeLibrary(ntc.m_hNTDLL); |
|---|
| 55 | ntc.m_hNTDLL = NULL; |
|---|
| 56 | } |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | return (ntc.m_hNTDLL != NULL); |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | static CString |
|---|
| 63 | formatNTError(NTFSContext& ntc, NTSTATUS dwStatus) |
|---|
| 64 | { |
|---|
| 65 | CString strMessage; |
|---|
| 66 | LPTSTR szMessage; |
|---|
| 67 | |
|---|
| 68 | FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
|---|
| 69 | NULL, |
|---|
| 70 | ntc.RtlNtStatusToDosError(dwStatus), |
|---|
| 71 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|---|
| 72 | (LPTSTR)&szMessage, 0, NULL); |
|---|
| 73 | |
|---|
| 74 | strMessage = szMessage; |
|---|
| 75 | LocalFree(szMessage); |
|---|
| 76 | |
|---|
| 77 | return strMessage; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | static bool |
|---|
| 81 | wipeClusters(NTFSContext& ntc, CEraserContext *context, bool& bCompressed) |
|---|
| 82 | { |
|---|
| 83 | if (context->m_piCurrent.m_uCluster == 0) { |
|---|
| 84 | return false; |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | NTSTATUS status = STATUS_INVALID_PARAMETER; |
|---|
| 88 | E_INT32 i; |
|---|
| 89 | IO_STATUS_BLOCK ioStatus; |
|---|
| 90 | E_UINT64 startVcn; |
|---|
| 91 | PGET_RETRIEVAL_DESCRIPTOR fileMappings; |
|---|
| 92 | E_UINT64 fileMap[FILEMAPSIZE]; |
|---|
| 93 | HANDLE hFile; |
|---|
| 94 | |
|---|
| 95 | // set the handle passed to the wipe function |
|---|
| 96 | hFile = context->m_hFile; |
|---|
| 97 | context->m_hFile = ntc.m_hVolume; |
|---|
| 98 | |
|---|
| 99 | // assume file is in an MFT record. |
|---|
| 100 | bCompressed = false; |
|---|
| 101 | |
|---|
| 102 | startVcn = 0; |
|---|
| 103 | fileMappings = (PGET_RETRIEVAL_DESCRIPTOR) fileMap; |
|---|
| 104 | |
|---|
| 105 | status = ntc.NtFsControlFile(hFile, NULL, NULL, 0, &ioStatus, |
|---|
| 106 | FSCTL_GET_RETRIEVAL_POINTERS, |
|---|
| 107 | &startVcn, sizeof(startVcn), |
|---|
| 108 | fileMappings, |
|---|
| 109 | FILEMAPSIZE * sizeof(ULONGLONG)); |
|---|
| 110 | |
|---|
| 111 | while (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW || |
|---|
| 112 | status == STATUS_PENDING) { |
|---|
| 113 | // if the operation is pending, wait for it to finish |
|---|
| 114 | if (status == STATUS_PENDING) { |
|---|
| 115 | WaitForSingleObject(hFile, INFINITE); |
|---|
| 116 | |
|---|
| 117 | // get the status from the status block |
|---|
| 118 | if (ioStatus.Status != STATUS_SUCCESS && |
|---|
| 119 | ioStatus.Status != STATUS_BUFFER_OVERFLOW) { |
|---|
| 120 | context->m_hFile = hFile; |
|---|
| 121 | return false; |
|---|
| 122 | } |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | // progress information |
|---|
| 126 | context->m_uProgressSize = 0; |
|---|
| 127 | |
|---|
| 128 | startVcn = fileMappings->StartVcn; |
|---|
| 129 | |
|---|
| 130 | for (i = 0; i < (E_UINT64) fileMappings->NumberOfPairs; i++) { |
|---|
| 131 | if (fileMappings->Pair[i].Lcn != LLINVALID) { |
|---|
| 132 | context->m_uProgressSize += (fileMappings->Pair[i].Vcn - startVcn) * |
|---|
| 133 | (E_UINT64)context->m_piCurrent.m_uCluster; |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | startVcn = fileMappings->Pair[i].Vcn; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | eraserProgressStartEstimate(context, context->m_uProgressSize); |
|---|
| 140 | |
|---|
| 141 | // loop through the buffer of number/cluster pairs, printing them out. |
|---|
| 142 | startVcn = fileMappings->StartVcn; |
|---|
| 143 | |
|---|
| 144 | for (i = 0; i < (E_UINT64)fileMappings->NumberOfPairs; i++) { |
|---|
| 145 | // On NT 4.0, a compressed virtual run (0-filled) is |
|---|
| 146 | // identified with a cluster offset of -1 |
|---|
| 147 | |
|---|
| 148 | if (fileMappings->Pair[i].Lcn != LLINVALID) { |
|---|
| 149 | // its compressed and outside the zone |
|---|
| 150 | bCompressed = true; |
|---|
| 151 | |
|---|
| 152 | // Overwrite the clusters if we were able to open the volume |
|---|
| 153 | // for write access. |
|---|
| 154 | context->m_uiFileStart.QuadPart = fileMappings->Pair[i].Lcn * context->m_piCurrent.m_uCluster; |
|---|
| 155 | context->m_uiFileSize.QuadPart = (fileMappings->Pair[i].Vcn - startVcn) * |
|---|
| 156 | (E_UINT64)context->m_piCurrent.m_uCluster; |
|---|
| 157 | |
|---|
| 158 | if (!context->m_lpmMethod->m_pwfFunction(context)) { |
|---|
| 159 | context->m_hFile = hFile; |
|---|
| 160 | return false; |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | startVcn = fileMappings->Pair[i].Vcn; |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | // if the buffer wasn't overflowed, then we're done |
|---|
| 169 | if (NT_SUCCESS(status)) { |
|---|
| 170 | break; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | status = ntc.NtFsControlFile(hFile, NULL, NULL, 0, &ioStatus, |
|---|
| 174 | FSCTL_GET_RETRIEVAL_POINTERS, |
|---|
| 175 | &startVcn, sizeof(startVcn), |
|---|
| 176 | fileMappings, |
|---|
| 177 | FILEMAPSIZE * sizeof(E_UINT64)); |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | if (status != STATUS_SUCCESS && status != STATUS_INVALID_PARAMETER) { |
|---|
| 181 | context->m_saError.Add(formatNTError(ntc, status)); |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | // restore the file handle |
|---|
| 185 | context->m_hFile = hFile; |
|---|
| 186 | |
|---|
| 187 | // if we made through with no errors we've overwritten all the file's clusters. |
|---|
| 188 | return NT_SUCCESS(status); |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | static bool |
|---|
| 192 | initAndOpenVolume(NTFSContext& ntc, TCHAR cDrive) |
|---|
| 193 | { |
|---|
| 194 | TCHAR szVolumeName[] = "\\\\.\\ :"; |
|---|
| 195 | |
|---|
| 196 | if (initEntryPoints(ntc)) { |
|---|
| 197 | // open the volume for direct access |
|---|
| 198 | szVolumeName[4] = cDrive; |
|---|
| 199 | ntc.m_hVolume = CreateFile(szVolumeName, GENERIC_READ | GENERIC_WRITE, |
|---|
| 200 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
|---|
| 201 | OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0); |
|---|
| 202 | |
|---|
| 203 | return (ntc.m_hVolume != INVALID_HANDLE_VALUE); |
|---|
| 204 | } |
|---|
| 205 | return false; |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | // exported functions |
|---|
| 209 | // |
|---|
| 210 | |
|---|
| 211 | E_UINT32 |
|---|
| 212 | wipeCompressedFile(CEraserContext *context) |
|---|
| 213 | { |
|---|
| 214 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 215 | return WCF_NOTCOMPRESSED; |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | NTFSContext ntc; |
|---|
| 219 | E_UINT32 uResult = WCF_FAILURE; |
|---|
| 220 | bool bCompressed = false; |
|---|
| 221 | |
|---|
| 222 | if (initAndOpenVolume(ntc, context->m_strData[0])) { |
|---|
| 223 | // open the file exclusively |
|---|
| 224 | context->m_hFile = CreateFile((LPCTSTR)context->m_strData, GENERIC_READ, |
|---|
| 225 | (context->m_uTestMode) ? |
|---|
| 226 | FILE_SHARE_READ | FILE_SHARE_WRITE : 0, |
|---|
| 227 | NULL, OPEN_EXISTING, 0, NULL); |
|---|
| 228 | |
|---|
| 229 | if (context->m_hFile != INVALID_HANDLE_VALUE) { |
|---|
| 230 | try { |
|---|
| 231 | // scan the location of the file |
|---|
| 232 | if (wipeClusters(ntc, context, bCompressed)) { |
|---|
| 233 | // done with the file handle |
|---|
| 234 | CloseHandle(context->m_hFile); |
|---|
| 235 | context->m_hFile = INVALID_HANDLE_VALUE; |
|---|
| 236 | |
|---|
| 237 | if (!bCompressed) { |
|---|
| 238 | // if the file wasn't really compressed, erase normally |
|---|
| 239 | uResult = WCF_NOTCOMPRESSED; |
|---|
| 240 | } else { |
|---|
| 241 | if (eraserOK(eraserRemoveFile((LPVOID)(LPCTSTR)context->m_strData, |
|---|
| 242 | (E_UINT16)context->m_strData.GetLength()))) { |
|---|
| 243 | uResult = WCF_SUCCESS; |
|---|
| 244 | } |
|---|
| 245 | } |
|---|
| 246 | } else { |
|---|
| 247 | // close the handle |
|---|
| 248 | CloseHandle(context->m_hFile); |
|---|
| 249 | context->m_hFile = INVALID_HANDLE_VALUE; |
|---|
| 250 | } |
|---|
| 251 | } catch (CException *e) { |
|---|
| 252 | handleException(e, context); |
|---|
| 253 | uResult = WCF_FAILURE; |
|---|
| 254 | |
|---|
| 255 | if (context->m_hFile != INVALID_HANDLE_VALUE) { |
|---|
| 256 | CloseHandle(context->m_hFile); |
|---|
| 257 | context->m_hFile = INVALID_HANDLE_VALUE; |
|---|
| 258 | } |
|---|
| 259 | } |
|---|
| 260 | } |
|---|
| 261 | } else { |
|---|
| 262 | // the user does not have privileges for low-level access |
|---|
| 263 | uResult = WCF_NOACCESS; |
|---|
| 264 | } |
|---|
| 265 | |
|---|
| 266 | return uResult; |
|---|
| 267 | } |
|---|
| 268 | |
|---|
| 269 | |
|---|
| 270 | #define mftFastWriteTest(x) \ |
|---|
| 271 | WriteFile((x)->m_hFile, uTestBuffer, (x)->m_uiFileSize.LowPart, &uTemp, NULL) |
|---|
| 272 | |
|---|
| 273 | bool |
|---|
| 274 | wipeMFTRecords(CEraserContext *context) |
|---|
| 275 | { |
|---|
| 276 | // On NTFS file system small files can be resident on the MFT record so we |
|---|
| 277 | // will need to overwrite empty records by creating as many of the largest |
|---|
| 278 | // sized files as possible (if there is space in the MFT, we'll be able to |
|---|
| 279 | // create non-zero sized files, where the data is resident in the MFT record) |
|---|
| 280 | |
|---|
| 281 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 282 | return false; |
|---|
| 283 | } |
|---|
| 284 | |
|---|
| 285 | E_UINT64 uFreeSpace = 0; |
|---|
| 286 | eraserGetFreeDiskSpace((LPVOID)context->m_piCurrent.m_szDrive, |
|---|
| 287 | (E_UINT16)lstrlen(context->m_piCurrent.m_szDrive), |
|---|
| 288 | &uFreeSpace); |
|---|
| 289 | |
|---|
| 290 | if (uFreeSpace == 0) { |
|---|
| 291 | const E_UINT16 maxMFTRecordSize = 4096; |
|---|
| 292 | |
|---|
| 293 | TCHAR szFileName[uShortFileNameLength + 1]; |
|---|
| 294 | E_UINT16 uCounter = 1; |
|---|
| 295 | E_UINT32 uTestBuffer[maxMFTRecordSize]; |
|---|
| 296 | E_UINT32 uTemp; |
|---|
| 297 | E_UINT64 ulPrevSize = maxMFTRecordSize; |
|---|
| 298 | CString strTemp; |
|---|
| 299 | CStringArray saList; |
|---|
| 300 | bool bCreatedFile; |
|---|
| 301 | |
|---|
| 302 | // fill test buffer with random data |
|---|
| 303 | isaacFill((E_PUINT8)uTestBuffer, maxMFTRecordSize); |
|---|
| 304 | |
|---|
| 305 | // do something with the progress bar to entertain the user |
|---|
| 306 | eraserDispMFT(context); |
|---|
| 307 | eraserBeginNotify(context); |
|---|
| 308 | |
|---|
| 309 | context->m_uClusterSpace = 0; |
|---|
| 310 | |
|---|
| 311 | do { |
|---|
| 312 | createRandomShortFileName(szFileName, uCounter++); |
|---|
| 313 | strTemp.Format("Eraser%s%s", context->m_piCurrent.m_szDrive, szFileName); |
|---|
| 314 | |
|---|
| 315 | context->m_hFile = CreateFile((LPCTSTR)strTemp, |
|---|
| 316 | GENERIC_WRITE, |
|---|
| 317 | (context->m_uTestMode) ? |
|---|
| 318 | FILE_SHARE_READ | FILE_SHARE_WRITE : 0, |
|---|
| 319 | NULL, |
|---|
| 320 | CREATE_NEW, |
|---|
| 321 | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_WRITE_THROUGH, |
|---|
| 322 | NULL); |
|---|
| 323 | |
|---|
| 324 | if (context->m_hFile == INVALID_HANDLE_VALUE) { |
|---|
| 325 | break; |
|---|
| 326 | } |
|---|
| 327 | |
|---|
| 328 | saList.Add(strTemp); |
|---|
| 329 | |
|---|
| 330 | try { |
|---|
| 331 | context->m_uiFileStart.QuadPart = 0; |
|---|
| 332 | context->m_uiFileSize.QuadPart = ulPrevSize; |
|---|
| 333 | bCreatedFile = false; |
|---|
| 334 | |
|---|
| 335 | while (context->m_uiFileSize.QuadPart) { |
|---|
| 336 | if (eraserInternalTerminated(context)) { |
|---|
| 337 | bCreatedFile = false; |
|---|
| 338 | break; |
|---|
| 339 | } |
|---|
| 340 | |
|---|
| 341 | // trying simple write first is much faster than calling the wipe function |
|---|
| 342 | if (!mftFastWriteTest(context) || !context->m_lpmMethod->m_pwfFunction(context)) { |
|---|
| 343 | eraserProgressSetMessage(context, ERASER_MESSAGE_MFT); |
|---|
| 344 | context->m_uiFileSize.QuadPart--; |
|---|
| 345 | } else { |
|---|
| 346 | strTemp.Format(ERASER_MESSAGE_MFT_WAIT, uCounter); |
|---|
| 347 | eraserProgressSetMessage(context, strTemp); |
|---|
| 348 | eraserUpdateNotify(context); |
|---|
| 349 | |
|---|
| 350 | ulPrevSize = context->m_uiFileSize.QuadPart; |
|---|
| 351 | bCreatedFile = true; |
|---|
| 352 | break; |
|---|
| 353 | } |
|---|
| 354 | |
|---|
| 355 | eraserSafeAssign(context, context->m_uProgressPercent, |
|---|
| 356 | (E_UINT8)(((maxMFTRecordSize - context->m_uiFileSize.QuadPart) * 100) / maxMFTRecordSize)); |
|---|
| 357 | setTotalProgress(context); |
|---|
| 358 | eraserUpdateNotify(context); |
|---|
| 359 | } |
|---|
| 360 | } catch (CException *e) { |
|---|
| 361 | handleException(e, context); |
|---|
| 362 | bCreatedFile = false; |
|---|
| 363 | } |
|---|
| 364 | |
|---|
| 365 | resetDate(context->m_hFile); |
|---|
| 366 | CloseHandle(context->m_hFile); |
|---|
| 367 | |
|---|
| 368 | } while (bCreatedFile); |
|---|
| 369 | |
|---|
| 370 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 371 | setTotalProgress(context); |
|---|
| 372 | |
|---|
| 373 | eraserProgressSetMessage(context, ERASER_MESSAGE_REMOVING); |
|---|
| 374 | eraserUpdateNotify(context); |
|---|
| 375 | |
|---|
| 376 | E_INT32 iSize = saList.GetSize(); |
|---|
| 377 | for (E_INT32 i = 0; i < iSize; i++) { |
|---|
| 378 | eraserSafeAssign(context, context->m_uProgressPercent, (E_UINT8)((i * 100) / iSize)); |
|---|
| 379 | eraserUpdateNotify(context); |
|---|
| 380 | |
|---|
| 381 | // file names are already random, no need to use slower eraserRemoveFile |
|---|
| 382 | DeleteFile((LPCTSTR)saList[i]); |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | // add entropy to the pool, number of files created |
|---|
| 386 | randomAddEntropy((E_PUINT8)&iSize, sizeof(E_INT32)); |
|---|
| 387 | |
|---|
| 388 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 389 | eraserUpdateNotify(context); |
|---|
| 390 | |
|---|
| 391 | // clean up |
|---|
| 392 | ZeroMemory(uTestBuffer, maxMFTRecordSize); |
|---|
| 393 | |
|---|
| 394 | return true; |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | return false; |
|---|
| 398 | } |
|---|
| 399 | |
|---|
| 400 | bool |
|---|
| 401 | wipeNTFSFileEntries(CEraserContext *context) |
|---|
| 402 | { |
|---|
| 403 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 404 | return false; |
|---|
| 405 | } |
|---|
| 406 | |
|---|
| 407 | NTFSContext ntc; |
|---|
| 408 | bool bResult = false; |
|---|
| 409 | |
|---|
| 410 | if (initAndOpenVolume(ntc, context->m_strData[0])) { |
|---|
| 411 | IO_STATUS_BLOCK ioStatus; |
|---|
| 412 | NTSTATUS status; |
|---|
| 413 | NTFS_VOLUME_DATA_BUFFER nvd; |
|---|
| 414 | |
|---|
| 415 | // find out MFT size |
|---|
| 416 | status = ntc.NtFsControlFile(ntc.m_hVolume, NULL, NULL, 0, &ioStatus, |
|---|
| 417 | FSCTL_GET_VOLUME_INFORMATION, |
|---|
| 418 | NULL, 0, &nvd, |
|---|
| 419 | sizeof(NTFS_VOLUME_DATA_BUFFER)); |
|---|
| 420 | |
|---|
| 421 | if (status == STATUS_SUCCESS) { |
|---|
| 422 | const E_UINT32 uMaxFilesPerFolder = 3000; |
|---|
| 423 | const E_UINT32 uMFTPollInterval = 20; |
|---|
| 424 | const E_UINT32 uFileNameLength = _MAX_FNAME - 14 /*strFolder.GetLength() + 1*/ - 8 - 1; |
|---|
| 425 | |
|---|
| 426 | HANDLE hFile, hFind; |
|---|
| 427 | WIN32_FIND_DATA wfdData; |
|---|
| 428 | E_UINT32 uSpeed, uTickCount; |
|---|
| 429 | E_UINT32 i, j; |
|---|
| 430 | E_UINT32 uFiles = 0, uFolders = 0; |
|---|
| 431 | E_UINT32 uEstimate; |
|---|
| 432 | LARGE_INTEGER uOriginalMFTSize = nvd.MftValidDataLength; |
|---|
| 433 | CString strPath, strFolder; |
|---|
| 434 | CStringArray saFolders; |
|---|
| 435 | TCHAR szPrefix[uFileNameLength + 1]; |
|---|
| 436 | |
|---|
| 437 | try { |
|---|
| 438 | // prefix each name with a couple of zeros |
|---|
| 439 | for (i = 0; i < uFileNameLength; i++) { |
|---|
| 440 | szPrefix[i] = '0'; |
|---|
| 441 | } |
|---|
| 442 | szPrefix[uFileNameLength] = 0; |
|---|
| 443 | |
|---|
| 444 | // approximate the number of files we need to create (at least 1) |
|---|
| 445 | uEstimate = max(1, (E_UINT32)(nvd.MftValidDataLength.QuadPart / nvd.BytesPerFileRecordSegment)); |
|---|
| 446 | |
|---|
| 447 | if (uEstimate > context->m_uProgressFiles) { |
|---|
| 448 | uEstimate -= context->m_uProgressFiles; |
|---|
| 449 | } |
|---|
| 450 | |
|---|
| 451 | // this may take a while, so we'll try to estimate the time |
|---|
| 452 | context->m_uProgressFlags |= eraserDispTime; |
|---|
| 453 | context->m_uProgressStartTime = GetTickCount(); |
|---|
| 454 | |
|---|
| 455 | eraserBeginNotify(context); |
|---|
| 456 | |
|---|
| 457 | do { |
|---|
| 458 | if (uFiles % uMaxFilesPerFolder == 0) { |
|---|
| 459 | strFolder.Format("%c:\\%s%04X", context->m_strData[0], |
|---|
| 460 | ERASER_TEMP_DIRECTORY_NTFS_ENTRIES, uFolders++); |
|---|
| 461 | |
|---|
| 462 | // remove possibly existing folder |
|---|
| 463 | eraserRemoveFolder((LPVOID)(LPCTSTR)strFolder, (E_UINT16)strFolder.GetLength(), |
|---|
| 464 | ERASER_REMOVE_RECURSIVELY); |
|---|
| 465 | |
|---|
| 466 | // create new directory |
|---|
| 467 | if (CreateDirectory((LPCTSTR)strFolder, NULL)) { |
|---|
| 468 | saFolders.Add(strFolder + "\\"); |
|---|
| 469 | } else { |
|---|
| 470 | eraserAddError(context, IDS_ERROR_TEMPFILE); |
|---|
| 471 | break; |
|---|
| 472 | } |
|---|
| 473 | } |
|---|
| 474 | |
|---|
| 475 | strPath.Format("%s\\%s%08X", strFolder, szPrefix, uFiles); |
|---|
| 476 | |
|---|
| 477 | hFile = CreateFile((LPCTSTR)strPath, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0); |
|---|
| 478 | |
|---|
| 479 | if (hFile != INVALID_HANDLE_VALUE) { |
|---|
| 480 | uFiles++; |
|---|
| 481 | CloseHandle(hFile); |
|---|
| 482 | } else { |
|---|
| 483 | eraserAddError(context, IDS_ERROR_TEMPFILE); |
|---|
| 484 | break; |
|---|
| 485 | } |
|---|
| 486 | |
|---|
| 487 | if (uFiles % uMFTPollInterval == 0 || eraserInternalTerminated(context)) { |
|---|
| 488 | status = ntc.NtFsControlFile(ntc.m_hVolume, NULL, NULL, 0, &ioStatus, |
|---|
| 489 | FSCTL_GET_VOLUME_INFORMATION, |
|---|
| 490 | NULL, 0, &nvd, |
|---|
| 491 | sizeof(NTFS_VOLUME_DATA_BUFFER)); |
|---|
| 492 | |
|---|
| 493 | if (eraserInternalTerminated(context)) { |
|---|
| 494 | break; |
|---|
| 495 | } else { |
|---|
| 496 | uTickCount = GetTickCount(); |
|---|
| 497 | if (uTickCount > context->m_uProgressStartTime) { |
|---|
| 498 | uSpeed = (uFiles * 1000) / (uTickCount - context->m_uProgressStartTime); |
|---|
| 499 | |
|---|
| 500 | if (uSpeed > 0) { |
|---|
| 501 | context->m_uProgressTimeLeft = ((uEstimate - uFiles) / uSpeed); |
|---|
| 502 | } else { |
|---|
| 503 | context->m_uProgressTimeLeft = 0; |
|---|
| 504 | } |
|---|
| 505 | } |
|---|
| 506 | |
|---|
| 507 | eraserSafeAssign(context, context->m_uProgressPercent, |
|---|
| 508 | (E_UINT8)min(100, (uFiles * 100) / uEstimate)); |
|---|
| 509 | setTotalProgress(context); |
|---|
| 510 | eraserUpdateNotify(context); |
|---|
| 511 | } |
|---|
| 512 | } |
|---|
| 513 | } while (status == STATUS_SUCCESS && nvd.MftValidDataLength.QuadPart <= uOriginalMFTSize.QuadPart); |
|---|
| 514 | |
|---|
| 515 | |
|---|
| 516 | // if we managed to increase MFT size, slack space was filled |
|---|
| 517 | if (nvd.MftValidDataLength.QuadPart > uOriginalMFTSize.QuadPart) { |
|---|
| 518 | bResult = true; |
|---|
| 519 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 520 | setTotalProgress(context); |
|---|
| 521 | |
|---|
| 522 | // add entropy to the pool, number of files created |
|---|
| 523 | randomAddEntropy((E_PUINT8)&uFiles, sizeof(E_UINT32)); |
|---|
| 524 | } |
|---|
| 525 | |
|---|
| 526 | if (!eraserInternalTerminated(context)) { |
|---|
| 527 | // show progress bar while removing files - may take a while |
|---|
| 528 | eraserProgressSetMessage(context, ERASER_MESSAGE_REMOVING); |
|---|
| 529 | eraserBeginNotify(context); |
|---|
| 530 | } |
|---|
| 531 | |
|---|
| 532 | // remove temporary files |
|---|
| 533 | for (i = 0, j = 0; i < uFolders; i++) { |
|---|
| 534 | strFolder = saFolders[i]; |
|---|
| 535 | hFind = FindFirstFile((LPCTSTR)(strFolder + "*"), &wfdData); |
|---|
| 536 | |
|---|
| 537 | if (hFind != INVALID_HANDLE_VALUE) { |
|---|
| 538 | do { |
|---|
| 539 | if (!bitSet(wfdData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) { |
|---|
| 540 | // eraserRemoveFile is way too slow |
|---|
| 541 | DeleteFile((LPCTSTR)(strFolder + wfdData.cFileName)); |
|---|
| 542 | |
|---|
| 543 | if (!eraserInternalTerminated(context)) { |
|---|
| 544 | eraserSafeAssign(context, context->m_uProgressPercent, (E_UINT8)((++j * 100) / uFiles)); |
|---|
| 545 | eraserUpdateNotify(context); |
|---|
| 546 | } |
|---|
| 547 | } |
|---|
| 548 | } |
|---|
| 549 | while (FindNextFile(hFind, &wfdData)); |
|---|
| 550 | |
|---|
| 551 | VERIFY(FindClose(hFind)); |
|---|
| 552 | } |
|---|
| 553 | |
|---|
| 554 | // remove the folder |
|---|
| 555 | if (eraserError(eraserRemoveFolder((LPVOID)(LPCTSTR)strFolder, |
|---|
| 556 | (E_UINT16)strFolder.GetLength(), ERASER_REMOVE_RECURSIVELY))) { |
|---|
| 557 | context->m_saFailed.Add(strFolder); |
|---|
| 558 | } |
|---|
| 559 | } |
|---|
| 560 | |
|---|
| 561 | if (!eraserInternalTerminated(context)) { |
|---|
| 562 | // and we're done |
|---|
| 563 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 564 | eraserUpdateNotify(context); |
|---|
| 565 | } |
|---|
| 566 | } catch (CException *e) { |
|---|
| 567 | handleException(e, context); |
|---|
| 568 | bResult = false; |
|---|
| 569 | } |
|---|
| 570 | } |
|---|
| 571 | } |
|---|
| 572 | |
|---|
| 573 | if (!bResult) { |
|---|
| 574 | eraserAddError1(context, IDS_ERROR_DIRENTRIES, (LPCTSTR)context->m_strData); |
|---|
| 575 | } |
|---|
| 576 | |
|---|
| 577 | return bResult; |
|---|
| 578 | } |
|---|
| 579 | |
|---|
| 580 | bool |
|---|
| 581 | findAlternateDataStreams(CEraserContext *context, LPCTSTR szFile, DataStreamArray& streams) |
|---|
| 582 | { |
|---|
| 583 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 584 | return false; |
|---|
| 585 | } |
|---|
| 586 | |
|---|
| 587 | NTFSContext ntc; |
|---|
| 588 | if (initEntryPoints(ntc)) { |
|---|
| 589 | bool bResult = false; |
|---|
| 590 | HANDLE hFile; |
|---|
| 591 | PFILE_STREAM_INFORMATION psi = 0; |
|---|
| 592 | NTSTATUS status = STATUS_INVALID_PARAMETER; |
|---|
| 593 | WCHAR wszStreamName[MAX_PATH]; |
|---|
| 594 | IO_STATUS_BLOCK ioStatus; |
|---|
| 595 | DataStream ads; |
|---|
| 596 | |
|---|
| 597 | hFile = CreateFile(szFile, |
|---|
| 598 | GENERIC_READ, |
|---|
| 599 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
|---|
| 600 | NULL, |
|---|
| 601 | OPEN_EXISTING, |
|---|
| 602 | 0, 0); |
|---|
| 603 | |
|---|
| 604 | if (hFile != INVALID_HANDLE_VALUE) { |
|---|
| 605 | // use write buffer, should be large enough |
|---|
| 606 | status = ntc.NtQueryInformationFile(hFile, &ioStatus, |
|---|
| 607 | (PFILE_STREAM_INFORMATION)context->m_puBuffer, |
|---|
| 608 | ERASER_DISK_BUFFER_SIZE, |
|---|
| 609 | FileStreamInformation); |
|---|
| 610 | |
|---|
| 611 | if (NT_SUCCESS(status)) { |
|---|
| 612 | try { |
|---|
| 613 | psi = (PFILE_STREAM_INFORMATION)context->m_puBuffer; |
|---|
| 614 | |
|---|
| 615 | do { |
|---|
| 616 | memcpy(wszStreamName, psi->Name, psi->NameLength); |
|---|
| 617 | wszStreamName[psi->NameLength / sizeof(WCHAR)] = 0; |
|---|
| 618 | |
|---|
| 619 | if (wcsicmp(wszStreamName, L"::$DATA")) { |
|---|
| 620 | // name of the alternate data stream |
|---|
| 621 | unicodeToCString(wszStreamName, ads.m_strName); |
|---|
| 622 | ads.m_strName = szFile + ads.m_strName; |
|---|
| 623 | |
|---|
| 624 | ads.m_uSize = psi->Size.QuadPart; |
|---|
| 625 | streams.Add(ads); |
|---|
| 626 | } |
|---|
| 627 | |
|---|
| 628 | if (psi->NextEntry) { |
|---|
| 629 | psi = (PFILE_STREAM_INFORMATION)((E_PUINT8)psi + psi->NextEntry); |
|---|
| 630 | } else { |
|---|
| 631 | psi = 0; |
|---|
| 632 | } |
|---|
| 633 | } while (psi); |
|---|
| 634 | |
|---|
| 635 | bResult = true; |
|---|
| 636 | } catch (...) { |
|---|
| 637 | ASSERT(0); |
|---|
| 638 | bResult = false; |
|---|
| 639 | } |
|---|
| 640 | } |
|---|
| 641 | |
|---|
| 642 | CloseHandle(hFile); |
|---|
| 643 | return bResult; |
|---|
| 644 | } |
|---|
| 645 | } |
|---|
| 646 | return false; |
|---|
| 647 | } |
|---|