source: trunk/EraserDll/File.cpp @ 65

Revision 65, 8.8 KB checked in by lowjoel, 7 years ago (diff)

Disable file buffering on all levels when wiping data streams.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1// File.cpp
2//
3// Eraser. Secure data removal. For Windows.
4// Copyright © 1997-2001  Sami Tolvanen (sami@tolvanen.com).
5//
6// This program is free software; you can redistribute it and/or
7// modify it under the terms of the GNU General Public License
8// as published by the Free Software Foundation; either version 2
9// of the License, or (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19// 02111-1307, USA.
20
21#include "stdafx.h"
22#include "resource.h"
23#include "EraserDll.h"
24#include "Common.h"
25#include "File.h"
26#include "NTFS.h"
27#include "io.h"
28
29
30static inline void
31formatError(CString& strError)
32{
33    strError.Empty();
34    if (GetLastError() != NO_ERROR) {
35        LPVOID lpMsgBuf;
36
37        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
38                      NULL,
39                      GetLastError(),
40                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
41                      (LPTSTR) &lpMsgBuf,
42                      0,
43                      NULL);
44
45        strError = (LPCTSTR) lpMsgBuf;
46
47        // remove CRLFs
48        strError.TrimRight();
49        E_INT32 iPos = strError.Find("\r\n");
50
51        while (iPos != -1) {
52            strError = strError.Left(iPos) + " " +
53                       strError.Right(strError.GetLength() - iPos - 2);
54
55            iPos = strError.Find("\r\n");
56        }
57
58        // Free the buffer.
59        LocalFree(lpMsgBuf);
60    }
61}
62
63bool
64resetDate(HANDLE hFile)
65{
66    // changes all file dates to January 1st, 1980 0:00
67    SYSTEMTIME  stTime;
68    FILETIME    ftLocalTime;
69    FILETIME    ftTime;
70
71
72    stTime.wYear            = 1980;
73    stTime.wMonth           = 1;
74    stTime.wDayOfWeek       = 0;
75    stTime.wDay             = 1;
76    stTime.wHour            = 0;
77    stTime.wMinute          = 0;
78    stTime.wSecond          = 0;
79    stTime.wMilliseconds    = 0;
80   
81    return (SystemTimeToFileTime(&stTime, &ftLocalTime) &&
82            LocalFileTimeToFileTime(&ftLocalTime, &ftTime) &&
83            SetFileTime(hFile, &ftTime, &ftTime, &ftTime));
84}
85
86static inline bool
87wipeDataStreams(CEraserContext *context, DataStreamArray& streams)
88{
89    bool bResult = false;
90    E_INT32 lHigh = 0;
91    E_INT32 iSize = streams.GetSize();
92
93    for (E_INT32 i = 0; i < iSize; i++) {
94        context->m_hFile = CreateFile((LPCTSTR)streams[i].m_strName,
95                                      GENERIC_READ | GENERIC_WRITE,
96                                      (context->m_uTestMode) ? FILE_SHARE_READ | FILE_SHARE_WRITE : 0,
97                                      NULL,
98                                      OPEN_EXISTING,
99                                      FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
100                                      NULL);
101
102        bResult = (context->m_hFile != INVALID_HANDLE_VALUE);
103        if (!bResult)
104            context->HandleError(static_cast<LPCTSTR>(streams[i].m_strName));
105        else {
106            try {
107                // set display name
108                eraserSafeAssign(context, context->m_strData, streams[i].m_strName);
109
110                // data stream size
111                if (streams[i].m_bDefault) {
112                    // we don't have the size for the default stream, get it
113                    context->m_uiFileSize.LowPart =
114                        GetFileSize(context->m_hFile, &context->m_uiFileSize.HighPart);
115
116                    if (context->m_uiFileSize.LowPart == (E_UINT32)-1 &&
117                        GetLastError() != NO_ERROR) {
118                        // GetFileSize failed
119                        bResult = false;
120                    }
121                } else {
122                    context->m_uiFileSize.QuadPart = streams[i].m_uSize;
123                }
124
125                if (bResult) {
126                    if (context->m_uiFileSize.QuadPart > 0) {
127                        // mmm, entropy.
128                        randomAddEntropy((E_PUINT8)&context->m_uiFileSize, sizeof(ULARGE_INTEGER));
129
130                        // we will also wipe the slack space at the end of the last cluster or if
131                        // cluster size isn't available, writes must at least be sector aligned
132                        E_UINT64 uTotal = fileSizeToArea(context, context->m_uiFileSize.QuadPart);
133
134                        context->m_uClusterSpace = (E_UINT32)(uTotal - context->m_uiFileSize.QuadPart);
135                        context->m_uiFileSize.QuadPart = uTotal;
136
137                        // set progress info
138                        eraserProgressStartEstimate(context, context->m_uiFileSize.QuadPart);
139
140                        // and overwrite
141                        bResult = context->m_lpmMethod->m_pwfFunction(context);
142
143                        if (bResult) {
144                            // set stream length to zero so allocated clusters cannot be trailed
145                            lHigh = 0L;
146                            SetFilePointer(context->m_hFile, 0, &lHigh, FILE_BEGIN);
147                            SetEndOfFile(context->m_hFile);
148
149                            resetDate(context->m_hFile);
150                        }
151                    } else {
152                        // nothing to erase
153                        resetDate(context->m_hFile);
154                    }
155                }
156
157                CloseHandle(context->m_hFile);
158
159            } catch (CException *e) {
160                handleException(e, context);
161
162                bResult = false;
163                SetLastError(ERROR_GEN_FAILURE);
164                CloseHandle(context->m_hFile);
165            }
166        }
167
168        if (!bResult) {
169            // if we were terminated while erasing a stream, the stream
170            // that wasn't completed will be added to the error list
171            CString strError;
172            formatError(strError);
173
174            if (!strError.IsEmpty()) {
175                strError = " (" + strError + ")";
176            }
177            strError = streams[i].m_strName + strError;
178            context->m_saFailed.Add(strError);
179
180            if (iSize > 1) {
181                eraserAddError1(context, IDS_ERROR_ADS, streams[i].m_strName);
182            }
183
184            break;
185        }
186    }
187
188    return bResult;
189}
190
191bool
192wipeFile(CEraserContext *context)
193{
194    try {
195        E_UINT32 uAttributes;
196        DataStreamArray streams;
197        DataStream defaultStream;
198
199        // default stream
200        defaultStream.m_strName = context->m_strData;
201        defaultStream.m_bDefault = true;
202
203        // get file attributes
204        uAttributes = GetFileAttributes(defaultStream.m_strName);
205
206        // if the file does not exist or an error has occurred
207        if (uAttributes == (E_UINT32)-1) {
208            CString strError;
209            formatError(strError);
210
211            if (!strError.IsEmpty()) {
212                strError = " (" + strError + ")";
213            }
214            strError = defaultStream.m_strName + strError;
215            context->m_saFailed.Add(strError);
216            return false;
217        }
218
219        // ignore read-only
220        SetFileAttributes(defaultStream.m_strName, FILE_ATTRIBUTE_NORMAL);
221
222        if (isWindowsNT &&
223            (bitSet(uAttributes, FILE_ATTRIBUTE_COMPRESSED) ||
224             bitSet(uAttributes, FILE_ATTRIBUTE_ENCRYPTED)  ||
225             bitSet(uAttributes, FILE_ATTRIBUTE_SPARSE_FILE))) {
226            // requires special processing
227            E_UINT32 uResult = wipeCompressedFile(context);
228
229            if (uResult == WCF_FAILURE) {
230                context->m_saFailed.Add(defaultStream.m_strName);
231                return false;
232            } else if (uResult == WCF_NOACCESS) {
233                CString strError;
234                strError.Format("%s (Administrator privileges required)", defaultStream.m_strName);
235
236                context->m_saFailed.Add(strError);
237                return false;
238            } else if (uResult == WCF_SUCCESS) {
239                return true;
240            }
241
242            // if file was not really compressed, erase normally
243        }
244
245        // search for alternate data streams (NTFS only)
246        if (isWindowsNT && bitSet(context->m_lsSettings.m_uItems, fileAlternateStreams)) {
247            findAlternateDataStreams(context, defaultStream.m_strName, streams);
248        }
249
250        // add the default (unnamed) data stream
251        streams.Add(defaultStream);
252
253        if (wipeDataStreams(context, streams)) {
254            return eraserOK(eraserRemoveFile((LPVOID)(LPCTSTR)defaultStream.m_strName,
255                                             (E_UINT16)defaultStream.m_strName.GetLength()));
256        }
257    } catch (CException *e) {
258        handleException(e, context);
259    }
260
261    return false;
262}
Note: See TracBrowser for help on using the repository browser.