source: trunk/EraserDll/NTFS.cpp @ 74

Revision 74, 23.7 KB checked in by lowjoel, 7 years ago (diff)

-Bumped the release number
-Fixed the End of File error when erasing 0-byte files

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
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
39static bool
40initEntryPoints(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
62static CString
63formatNTError(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
80static bool
81wipeClusters(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
192static bool
193initAndOpenVolume(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
212E_UINT32
213wipeCompressedFile(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
275bool
276wipeMFTRecords(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
402bool
403wipeNTFSFileEntries(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
582bool
583findAlternateDataStreams(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}
Note: See TracBrowser for help on using the repository browser.