| 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 | ntc.RtlNtStatusToDosError(status) != ERROR_HANDLE_EOF) { |
|---|
| 182 | context->m_saError.Add(formatNTError(ntc, status)); |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | // restore the file handle |
|---|
| 186 | context->m_hFile = hFile; |
|---|
| 187 | |
|---|
| 188 | // if we made through with no errors we've overwritten all the file's clusters. |
|---|
| 189 | return NT_SUCCESS(status) || ntc.RtlNtStatusToDosError(status) == ERROR_HANDLE_EOF; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | static bool |
|---|
| 193 | initAndOpenVolume(NTFSContext& ntc, TCHAR cDrive) |
|---|
| 194 | { |
|---|
| 195 | TCHAR szVolumeName[] = "\\\\.\\ :"; |
|---|
| 196 | |
|---|
| 197 | if (initEntryPoints(ntc)) { |
|---|
| 198 | // open the volume for direct access |
|---|
| 199 | szVolumeName[4] = cDrive; |
|---|
| 200 | ntc.m_hVolume = CreateFile(szVolumeName, GENERIC_READ | GENERIC_WRITE, |
|---|
| 201 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
|---|
| 202 | OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0); |
|---|
| 203 | |
|---|
| 204 | return (ntc.m_hVolume != INVALID_HANDLE_VALUE); |
|---|
| 205 | } |
|---|
| 206 | return false; |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | // exported functions |
|---|
| 210 | // |
|---|
| 211 | |
|---|
| 212 | E_UINT32 |
|---|
| 213 | wipeCompressedFile(CEraserContext *context) |
|---|
| 214 | { |
|---|
| 215 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 216 | return WCF_NOTCOMPRESSED; |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | NTFSContext ntc; |
|---|
| 220 | E_UINT32 uResult = WCF_FAILURE; |
|---|
| 221 | bool bCompressed = false; |
|---|
| 222 | |
|---|
| 223 | if (initAndOpenVolume(ntc, context->m_strData[0])) { |
|---|
| 224 | // open the file exclusively |
|---|
| 225 | context->m_hFile = CreateFile((LPCTSTR)context->m_strData, GENERIC_READ, |
|---|
| 226 | (context->m_uTestMode) ? |
|---|
| 227 | FILE_SHARE_READ | FILE_SHARE_WRITE : 0, |
|---|
| 228 | NULL, OPEN_EXISTING, |
|---|
| 229 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); |
|---|
| 230 | |
|---|
| 231 | if (context->m_hFile != INVALID_HANDLE_VALUE) { |
|---|
| 232 | try { |
|---|
| 233 | // scan the location of the file |
|---|
| 234 | if (wipeClusters(ntc, context, bCompressed)) { |
|---|
| 235 | // done with the file handle |
|---|
| 236 | CloseHandle(context->m_hFile); |
|---|
| 237 | context->m_hFile = INVALID_HANDLE_VALUE; |
|---|
| 238 | |
|---|
| 239 | if (!bCompressed) { |
|---|
| 240 | // if the file wasn't really compressed, erase normally |
|---|
| 241 | uResult = WCF_NOTCOMPRESSED; |
|---|
| 242 | } else { |
|---|
| 243 | if (eraserOK(eraserRemoveFile((LPVOID)(LPCTSTR)context->m_strData, |
|---|
| 244 | (E_UINT16)context->m_strData.GetLength()))) { |
|---|
| 245 | uResult = WCF_SUCCESS; |
|---|
| 246 | } |
|---|
| 247 | } |
|---|
| 248 | } else { |
|---|
| 249 | // close the handle |
|---|
| 250 | CloseHandle(context->m_hFile); |
|---|
| 251 | context->m_hFile = INVALID_HANDLE_VALUE; |
|---|
| 252 | } |
|---|
| 253 | } catch (CException *e) { |
|---|
| 254 | handleException(e, context); |
|---|
| 255 | uResult = WCF_FAILURE; |
|---|
| 256 | |
|---|
| 257 | if (context->m_hFile != INVALID_HANDLE_VALUE) { |
|---|
| 258 | CloseHandle(context->m_hFile); |
|---|
| 259 | context->m_hFile = INVALID_HANDLE_VALUE; |
|---|
| 260 | } |
|---|
| 261 | } |
|---|
| 262 | } |
|---|
| 263 | } else { |
|---|
| 264 | // the user does not have privileges for low-level access |
|---|
| 265 | uResult = WCF_NOACCESS; |
|---|
| 266 | } |
|---|
| 267 | |
|---|
| 268 | return uResult; |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | |
|---|
| 272 | #define mftFastWriteTest(x) \ |
|---|
| 273 | WriteFile((x)->m_hFile, uTestBuffer, (x)->m_uiFileSize.LowPart, &uTemp, NULL) |
|---|
| 274 | |
|---|
| 275 | bool |
|---|
| 276 | wipeMFTRecords(CEraserContext *context) |
|---|
| 277 | { |
|---|
| 278 | // On NTFS file system small files can be resident on the MFT record so we |
|---|
| 279 | // will need to overwrite empty records by creating as many of the largest |
|---|
| 280 | // sized files as possible (if there is space in the MFT, we'll be able to |
|---|
| 281 | // create non-zero sized files, where the data is resident in the MFT record) |
|---|
| 282 | |
|---|
| 283 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 284 | return false; |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | E_UINT64 uFreeSpace = 0; |
|---|
| 288 | eraserGetFreeDiskSpace((LPVOID)context->m_piCurrent.m_szDrive, |
|---|
| 289 | (E_UINT16)lstrlen(context->m_piCurrent.m_szDrive), |
|---|
| 290 | &uFreeSpace); |
|---|
| 291 | |
|---|
| 292 | if (uFreeSpace == 0) { |
|---|
| 293 | const E_UINT16 maxMFTRecordSize = 4096; |
|---|
| 294 | |
|---|
| 295 | TCHAR szFileName[uShortFileNameLength + 1]; |
|---|
| 296 | E_UINT16 uCounter = 1; |
|---|
| 297 | E_UINT32 uTestBuffer[maxMFTRecordSize]; |
|---|
| 298 | E_UINT32 uTemp; |
|---|
| 299 | E_UINT64 ulPrevSize = maxMFTRecordSize; |
|---|
| 300 | CString strTemp; |
|---|
| 301 | CStringArray saList; |
|---|
| 302 | bool bCreatedFile; |
|---|
| 303 | |
|---|
| 304 | // fill test buffer with random data |
|---|
| 305 | isaacFill((E_PUINT8)uTestBuffer, maxMFTRecordSize); |
|---|
| 306 | |
|---|
| 307 | // do something with the progress bar to entertain the user |
|---|
| 308 | eraserDispMFT(context); |
|---|
| 309 | eraserBeginNotify(context); |
|---|
| 310 | |
|---|
| 311 | context->m_uClusterSpace = 0; |
|---|
| 312 | |
|---|
| 313 | do { |
|---|
| 314 | createRandomShortFileName(szFileName, uCounter++); |
|---|
| 315 | strTemp.Format("Eraser%s%s", context->m_piCurrent.m_szDrive, szFileName); |
|---|
| 316 | |
|---|
| 317 | context->m_hFile = CreateFile((LPCTSTR)strTemp, |
|---|
| 318 | GENERIC_WRITE, |
|---|
| 319 | (context->m_uTestMode) ? |
|---|
| 320 | FILE_SHARE_READ | FILE_SHARE_WRITE : 0, |
|---|
| 321 | NULL, |
|---|
| 322 | CREATE_NEW, |
|---|
| 323 | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_WRITE_THROUGH, |
|---|
| 324 | NULL); |
|---|
| 325 | |
|---|
| 326 | if (context->m_hFile == INVALID_HANDLE_VALUE) { |
|---|
| 327 | break; |
|---|
| 328 | } |
|---|
| 329 | |
|---|
| 330 | saList.Add(strTemp); |
|---|
| 331 | |
|---|
| 332 | try { |
|---|
| 333 | context->m_uiFileStart.QuadPart = 0; |
|---|
| 334 | context->m_uiFileSize.QuadPart = ulPrevSize; |
|---|
| 335 | bCreatedFile = false; |
|---|
| 336 | |
|---|
| 337 | while (context->m_uiFileSize.QuadPart) { |
|---|
| 338 | if (eraserInternalTerminated(context)) { |
|---|
| 339 | bCreatedFile = false; |
|---|
| 340 | break; |
|---|
| 341 | } |
|---|
| 342 | |
|---|
| 343 | // trying simple write first is much faster than calling the wipe function |
|---|
| 344 | if (!mftFastWriteTest(context) || !context->m_lpmMethod->m_pwfFunction(context)) { |
|---|
| 345 | eraserProgressSetMessage(context, ERASER_MESSAGE_MFT); |
|---|
| 346 | context->m_uiFileSize.QuadPart--; |
|---|
| 347 | } else { |
|---|
| 348 | strTemp.Format(ERASER_MESSAGE_MFT_WAIT, uCounter); |
|---|
| 349 | eraserProgressSetMessage(context, strTemp); |
|---|
| 350 | eraserUpdateNotify(context); |
|---|
| 351 | |
|---|
| 352 | ulPrevSize = context->m_uiFileSize.QuadPart; |
|---|
| 353 | bCreatedFile = true; |
|---|
| 354 | break; |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | eraserSafeAssign(context, context->m_uProgressPercent, |
|---|
| 358 | (E_UINT8)(((maxMFTRecordSize - context->m_uiFileSize.QuadPart) * 100) / maxMFTRecordSize)); |
|---|
| 359 | setTotalProgress(context); |
|---|
| 360 | eraserUpdateNotify(context); |
|---|
| 361 | } |
|---|
| 362 | } catch (CException *e) { |
|---|
| 363 | handleException(e, context); |
|---|
| 364 | bCreatedFile = false; |
|---|
| 365 | } |
|---|
| 366 | |
|---|
| 367 | resetDate(context->m_hFile); |
|---|
| 368 | CloseHandle(context->m_hFile); |
|---|
| 369 | |
|---|
| 370 | } while (bCreatedFile); |
|---|
| 371 | |
|---|
| 372 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 373 | setTotalProgress(context); |
|---|
| 374 | |
|---|
| 375 | eraserProgressSetMessage(context, ERASER_MESSAGE_REMOVING); |
|---|
| 376 | eraserUpdateNotify(context); |
|---|
| 377 | |
|---|
| 378 | E_INT32 iSize = saList.GetSize(); |
|---|
| 379 | for (E_INT32 i = 0; i < iSize; i++) { |
|---|
| 380 | eraserSafeAssign(context, context->m_uProgressPercent, (E_UINT8)((i * 100) / iSize)); |
|---|
| 381 | eraserUpdateNotify(context); |
|---|
| 382 | |
|---|
| 383 | // file names are already random, no need to use slower eraserRemoveFile |
|---|
| 384 | DeleteFile((LPCTSTR)saList[i]); |
|---|
| 385 | } |
|---|
| 386 | |
|---|
| 387 | // add entropy to the pool, number of files created |
|---|
| 388 | randomAddEntropy((E_PUINT8)&iSize, sizeof(E_INT32)); |
|---|
| 389 | |
|---|
| 390 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 391 | eraserUpdateNotify(context); |
|---|
| 392 | |
|---|
| 393 | // clean up |
|---|
| 394 | ZeroMemory(uTestBuffer, maxMFTRecordSize); |
|---|
| 395 | |
|---|
| 396 | return true; |
|---|
| 397 | } |
|---|
| 398 | |
|---|
| 399 | return false; |
|---|
| 400 | } |
|---|
| 401 | |
|---|
| 402 | bool |
|---|
| 403 | wipeNTFSFileEntries(CEraserContext *context) |
|---|
| 404 | { |
|---|
| 405 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 406 | return false; |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | NTFSContext ntc; |
|---|
| 410 | bool bResult = false; |
|---|
| 411 | |
|---|
| 412 | if (initAndOpenVolume(ntc, context->m_strData[0])) { |
|---|
| 413 | IO_STATUS_BLOCK ioStatus; |
|---|
| 414 | NTSTATUS status; |
|---|
| 415 | NTFS_VOLUME_DATA_BUFFER nvd; |
|---|
| 416 | |
|---|
| 417 | // find out MFT size |
|---|
| 418 | status = ntc.NtFsControlFile(ntc.m_hVolume, NULL, NULL, 0, &ioStatus, |
|---|
| 419 | FSCTL_GET_VOLUME_INFORMATION, |
|---|
| 420 | NULL, 0, &nvd, |
|---|
| 421 | sizeof(NTFS_VOLUME_DATA_BUFFER)); |
|---|
| 422 | |
|---|
| 423 | if (status == STATUS_SUCCESS) { |
|---|
| 424 | const E_UINT32 uMaxFilesPerFolder = 3000; |
|---|
| 425 | const E_UINT32 uMFTPollInterval = 20; |
|---|
| 426 | const E_UINT32 uFileNameLength = _MAX_FNAME - 14 /*strFolder.GetLength() + 1*/ - 8 - 1; |
|---|
| 427 | |
|---|
| 428 | HANDLE hFile, hFind; |
|---|
| 429 | WIN32_FIND_DATA wfdData; |
|---|
| 430 | E_UINT32 uSpeed, uTickCount; |
|---|
| 431 | E_UINT32 i, j; |
|---|
| 432 | E_UINT32 uFiles = 0, uFolders = 0; |
|---|
| 433 | E_UINT32 uEstimate; |
|---|
| 434 | LARGE_INTEGER uOriginalMFTSize = nvd.MftValidDataLength; |
|---|
| 435 | CString strPath, strFolder; |
|---|
| 436 | CStringArray saFolders; |
|---|
| 437 | TCHAR szPrefix[uFileNameLength + 1]; |
|---|
| 438 | |
|---|
| 439 | try { |
|---|
| 440 | // prefix each name with a couple of zeros |
|---|
| 441 | for (i = 0; i < uFileNameLength; i++) { |
|---|
| 442 | szPrefix[i] = '0'; |
|---|
| 443 | } |
|---|
| 444 | szPrefix[uFileNameLength] = 0; |
|---|
| 445 | |
|---|
| 446 | // approximate the number of files we need to create (at least 1) |
|---|
| 447 | uEstimate = max(1, (E_UINT32)(nvd.MftValidDataLength.QuadPart / nvd.BytesPerFileRecordSegment)); |
|---|
| 448 | |
|---|
| 449 | if (uEstimate > context->m_uProgressFiles) { |
|---|
| 450 | uEstimate -= context->m_uProgressFiles; |
|---|
| 451 | } |
|---|
| 452 | |
|---|
| 453 | // this may take a while, so we'll try to estimate the time |
|---|
| 454 | context->m_uProgressFlags |= eraserDispTime; |
|---|
| 455 | context->m_uProgressStartTime = GetTickCount(); |
|---|
| 456 | |
|---|
| 457 | eraserBeginNotify(context); |
|---|
| 458 | |
|---|
| 459 | do { |
|---|
| 460 | if (uFiles % uMaxFilesPerFolder == 0) { |
|---|
| 461 | strFolder.Format("%c:\\%s%04X", context->m_strData[0], |
|---|
| 462 | ERASER_TEMP_DIRECTORY_NTFS_ENTRIES, uFolders++); |
|---|
| 463 | |
|---|
| 464 | // remove possibly existing folder |
|---|
| 465 | eraserRemoveFolder((LPVOID)(LPCTSTR)strFolder, (E_UINT16)strFolder.GetLength(), |
|---|
| 466 | ERASER_REMOVE_RECURSIVELY); |
|---|
| 467 | |
|---|
| 468 | // create new directory |
|---|
| 469 | if (CreateDirectory((LPCTSTR)strFolder, NULL)) { |
|---|
| 470 | saFolders.Add(strFolder + "\\"); |
|---|
| 471 | } else { |
|---|
| 472 | eraserAddError(context, IDS_ERROR_TEMPFILE); |
|---|
| 473 | break; |
|---|
| 474 | } |
|---|
| 475 | } |
|---|
| 476 | |
|---|
| 477 | strPath.Format("%s\\%s%08X", strFolder, szPrefix, uFiles); |
|---|
| 478 | |
|---|
| 479 | hFile = CreateFile((LPCTSTR)strPath, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0); |
|---|
| 480 | |
|---|
| 481 | if (hFile != INVALID_HANDLE_VALUE) { |
|---|
| 482 | uFiles++; |
|---|
| 483 | CloseHandle(hFile); |
|---|
| 484 | } else { |
|---|
| 485 | eraserAddError(context, IDS_ERROR_TEMPFILE); |
|---|
| 486 | break; |
|---|
| 487 | } |
|---|
| 488 | |
|---|
| 489 | if (uFiles % uMFTPollInterval == 0 || eraserInternalTerminated(context)) { |
|---|
| 490 | status = ntc.NtFsControlFile(ntc.m_hVolume, NULL, NULL, 0, &ioStatus, |
|---|
| 491 | FSCTL_GET_VOLUME_INFORMATION, |
|---|
| 492 | NULL, 0, &nvd, |
|---|
| 493 | sizeof(NTFS_VOLUME_DATA_BUFFER)); |
|---|
| 494 | |
|---|
| 495 | if (eraserInternalTerminated(context)) { |
|---|
| 496 | break; |
|---|
| 497 | } else { |
|---|
| 498 | uTickCount = GetTickCount(); |
|---|
| 499 | if (uTickCount > context->m_uProgressStartTime) { |
|---|
| 500 | uSpeed = (uFiles * 1000) / (uTickCount - context->m_uProgressStartTime); |
|---|
| 501 | |
|---|
| 502 | if (uSpeed > 0) { |
|---|
| 503 | context->m_uProgressTimeLeft = ((uEstimate - uFiles) / uSpeed); |
|---|
| 504 | } else { |
|---|
| 505 | context->m_uProgressTimeLeft = 0; |
|---|
| 506 | } |
|---|
| 507 | } |
|---|
| 508 | |
|---|
| 509 | eraserSafeAssign(context, context->m_uProgressPercent, |
|---|
| 510 | (E_UINT8)min(100, (uFiles * 100) / uEstimate)); |
|---|
| 511 | setTotalProgress(context); |
|---|
| 512 | eraserUpdateNotify(context); |
|---|
| 513 | } |
|---|
| 514 | } |
|---|
| 515 | } while (status == STATUS_SUCCESS && nvd.MftValidDataLength.QuadPart <= uOriginalMFTSize.QuadPart); |
|---|
| 516 | |
|---|
| 517 | |
|---|
| 518 | // if we managed to increase MFT size, slack space was filled |
|---|
| 519 | if (nvd.MftValidDataLength.QuadPart > uOriginalMFTSize.QuadPart) { |
|---|
| 520 | bResult = true; |
|---|
| 521 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 522 | setTotalProgress(context); |
|---|
| 523 | |
|---|
| 524 | // add entropy to the pool, number of files created |
|---|
| 525 | randomAddEntropy((E_PUINT8)&uFiles, sizeof(E_UINT32)); |
|---|
| 526 | } |
|---|
| 527 | |
|---|
| 528 | if (!eraserInternalTerminated(context)) { |
|---|
| 529 | // show progress bar while removing files - may take a while |
|---|
| 530 | eraserProgressSetMessage(context, ERASER_MESSAGE_REMOVING); |
|---|
| 531 | eraserBeginNotify(context); |
|---|
| 532 | } |
|---|
| 533 | |
|---|
| 534 | // remove temporary files |
|---|
| 535 | for (i = 0, j = 0; i < uFolders; i++) { |
|---|
| 536 | strFolder = saFolders[i]; |
|---|
| 537 | hFind = FindFirstFile((LPCTSTR)(strFolder + "*"), &wfdData); |
|---|
| 538 | |
|---|
| 539 | if (hFind != INVALID_HANDLE_VALUE) { |
|---|
| 540 | do { |
|---|
| 541 | if (!bitSet(wfdData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) { |
|---|
| 542 | // eraserRemoveFile is way too slow |
|---|
| 543 | DeleteFile((LPCTSTR)(strFolder + wfdData.cFileName)); |
|---|
| 544 | |
|---|
| 545 | if (!eraserInternalTerminated(context)) { |
|---|
| 546 | eraserSafeAssign(context, context->m_uProgressPercent, (E_UINT8)((++j * 100) / uFiles)); |
|---|
| 547 | eraserUpdateNotify(context); |
|---|
| 548 | } |
|---|
| 549 | } |
|---|
| 550 | } |
|---|
| 551 | while (FindNextFile(hFind, &wfdData)); |
|---|
| 552 | |
|---|
| 553 | VERIFY(FindClose(hFind)); |
|---|
| 554 | } |
|---|
| 555 | |
|---|
| 556 | // remove the folder |
|---|
| 557 | if (eraserError(eraserRemoveFolder((LPVOID)(LPCTSTR)strFolder, |
|---|
| 558 | (E_UINT16)strFolder.GetLength(), ERASER_REMOVE_RECURSIVELY))) { |
|---|
| 559 | context->m_saFailed.Add(strFolder); |
|---|
| 560 | } |
|---|
| 561 | } |
|---|
| 562 | |
|---|
| 563 | if (!eraserInternalTerminated(context)) { |
|---|
| 564 | // and we're done |
|---|
| 565 | eraserSafeAssign(context, context->m_uProgressPercent, 100); |
|---|
| 566 | eraserUpdateNotify(context); |
|---|
| 567 | } |
|---|
| 568 | } catch (CException *e) { |
|---|
| 569 | handleException(e, context); |
|---|
| 570 | bResult = false; |
|---|
| 571 | } |
|---|
| 572 | } |
|---|
| 573 | } |
|---|
| 574 | |
|---|
| 575 | if (!bResult) { |
|---|
| 576 | eraserAddError1(context, IDS_ERROR_DIRENTRIES, (LPCTSTR)context->m_strData); |
|---|
| 577 | } |
|---|
| 578 | |
|---|
| 579 | return bResult; |
|---|
| 580 | } |
|---|
| 581 | |
|---|
| 582 | bool |
|---|
| 583 | findAlternateDataStreams(CEraserContext *context, LPCTSTR szFile, DataStreamArray& streams) |
|---|
| 584 | { |
|---|
| 585 | if (!isFileSystemNTFS(context->m_piCurrent)) { |
|---|
| 586 | return false; |
|---|
| 587 | } |
|---|
| 588 | |
|---|
| 589 | NTFSContext ntc; |
|---|
| 590 | if (initEntryPoints(ntc)) { |
|---|
| 591 | bool bResult = false; |
|---|
| 592 | HANDLE hFile; |
|---|
| 593 | PFILE_STREAM_INFORMATION psi = 0; |
|---|
| 594 | NTSTATUS status = STATUS_INVALID_PARAMETER; |
|---|
| 595 | WCHAR wszStreamName[MAX_PATH]; |
|---|
| 596 | IO_STATUS_BLOCK ioStatus; |
|---|
| 597 | DataStream ads; |
|---|
| 598 | |
|---|
| 599 | hFile = CreateFile(szFile, |
|---|
| 600 | GENERIC_READ, |
|---|
| 601 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
|---|
| 602 | NULL, |
|---|
| 603 | OPEN_EXISTING, |
|---|
| 604 | 0, 0); |
|---|
| 605 | |
|---|
| 606 | if (hFile != INVALID_HANDLE_VALUE) { |
|---|
| 607 | // use write buffer, should be large enough |
|---|
| 608 | status = ntc.NtQueryInformationFile(hFile, &ioStatus, |
|---|
| 609 | (PFILE_STREAM_INFORMATION)context->m_puBuffer, |
|---|
| 610 | ERASER_DISK_BUFFER_SIZE, |
|---|
| 611 | FileStreamInformation); |
|---|
| 612 | |
|---|
| 613 | if (NT_SUCCESS(status)) { |
|---|
| 614 | try { |
|---|
| 615 | psi = (PFILE_STREAM_INFORMATION)context->m_puBuffer; |
|---|
| 616 | |
|---|
| 617 | do { |
|---|
| 618 | memcpy(wszStreamName, psi->Name, psi->NameLength); |
|---|
| 619 | wszStreamName[psi->NameLength / sizeof(WCHAR)] = 0; |
|---|
| 620 | |
|---|
| 621 | if (_wcsicmp(wszStreamName, L"::$DATA")) { |
|---|
| 622 | // name of the alternate data stream |
|---|
| 623 | unicodeToCString(wszStreamName, ads.m_strName); |
|---|
| 624 | ads.m_strName = szFile + ads.m_strName; |
|---|
| 625 | |
|---|
| 626 | ads.m_uSize = psi->Size.QuadPart; |
|---|
| 627 | streams.Add(ads); |
|---|
| 628 | } |
|---|
| 629 | |
|---|
| 630 | if (psi->NextEntry) { |
|---|
| 631 | psi = (PFILE_STREAM_INFORMATION)((E_PUINT8)psi + psi->NextEntry); |
|---|
| 632 | } else { |
|---|
| 633 | psi = 0; |
|---|
| 634 | } |
|---|
| 635 | } while (psi); |
|---|
| 636 | |
|---|
| 637 | bResult = true; |
|---|
| 638 | } catch (...) { |
|---|
| 639 | ASSERT(0); |
|---|
| 640 | bResult = false; |
|---|
| 641 | } |
|---|
| 642 | } |
|---|
| 643 | |
|---|
| 644 | CloseHandle(hFile); |
|---|
| 645 | return bResult; |
|---|
| 646 | } |
|---|
| 647 | } |
|---|
| 648 | return false; |
|---|
| 649 | } |
|---|