source: branches/eraser6/6.0/Eraser.DefaultPlugins/FileSystems/Windows.cs @ 1470

Revision 1470, 11.7 KB checked in by lowjoel, 5 years ago (diff)

Compilo. Strange, I thought it compiled fine. Finally fixes #293

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25
26using System.IO;
27using System.Threading;
28using Eraser.Manager;
29using Eraser.Util;
30
31namespace Eraser.DefaultPlugins
32{
33    /// <summary>
34    /// Base class for all Windows filesystems.
35    /// </summary>
36    public abstract class WindowsFileSystem : FileSystem
37    {
38        public override void DeleteFile(FileInfo info)
39        {
40            //Set the date of the file to be invalid to prevent forensic
41            //detection
42            info.CreationTime = info.LastWriteTime = info.LastAccessTime = MinTimestamp;
43            info.Attributes = FileAttributes.Normal;
44            info.Attributes = FileAttributes.NotContentIndexed;
45
46            //Rename the file a few times to erase the entry from the file system
47            //table.
48            string newPath = GenerateRandomFileName(info.Directory, info.Name.Length);
49            for (int i = 0, tries = 0; i < FileNameErasePasses; ++tries)
50            {
51                //Try to rename the file. If it fails, it is probably due to another
52                //process locking the file. Defer, then rename again.
53                try
54                {
55                    info.MoveTo(newPath);
56                    ++i;
57                }
58                catch (IOException e)
59                {
60                    switch (System.Runtime.InteropServices.Marshal.GetLastWin32Error())
61                    {
62                        case 5: //ERROR_ACCESS_DENIED
63                            throw new UnauthorizedAccessException(S._("The file {0} could not " +
64                                "be erased because the file's permissions prevent access to the file.",
65                                info.FullName));
66
67                        case 32: //ERROR_SHARING_VIOLATION
68                            //If after FilenameEraseTries the file is still locked, some program is
69                            //definitely using the file; throw an exception.
70                            if (tries > FileNameEraseTries)
71                                throw new IOException(S._("The file {0} is currently in use and " +
72                                    "cannot be removed.", info.FullName), e);
73
74                            //Let the process locking the file release the lock
75                            Thread.Sleep(100);
76                            break;
77
78                        default:
79                            throw;
80                    }
81                }
82            }
83
84            //If the user wants plausible deniability, find a random file on the same
85            //volume and write it over.
86            if (Manager.ManagerLibrary.Settings.PlausibleDeniability)
87            {
88                CopyPlausibleDeniabilityFile(info.OpenWrite());
89            }
90
91            //Then delete the file.
92            for (int i = 0; i < FileNameEraseTries; ++i)
93                try
94                {
95                    info.Delete();
96                    break;
97                }
98                catch (IOException e)
99                {
100                    switch (System.Runtime.InteropServices.Marshal.GetLastWin32Error())
101                    {
102                        case 5: //ERROR_ACCESS_DENIED
103                            throw new UnauthorizedAccessException(S._("The file {0} could not " +
104                                "be erased because the file's permissions prevent access to the file.",
105                                info.FullName), e);
106
107                        case 32: //ERROR_SHARING_VIOLATION
108                            //If after FilenameEraseTries the file is still locked, some program is
109                            //definitely using the file; throw an exception.
110                            if (i > FileNameEraseTries)
111                                throw new IOException(S._("The file {0} is currently in use and " +
112                                    "cannot be removed.", info.FullName), e);
113
114                            //Let the process locking the file release the lock
115                            Thread.Sleep(100);
116                            break;
117
118                        default:
119                            throw;
120                    }
121                }
122        }
123
124        public override void DeleteFolder(DirectoryInfo info, bool recursive)
125        {
126            if (!recursive && info.GetFileSystemInfos().Length != 0)
127                throw new InvalidOperationException(S._("The folder {0} cannot be deleted as it is " +
128                    "not empty."));
129
130            //TODO: check for reparse points
131            foreach (DirectoryInfo dir in info.GetDirectories())
132                DeleteFolder(dir);
133            foreach (FileInfo file in info.GetFiles())
134                DeleteFile(file);
135
136            //Then clean up this folder.
137            for (int i = 0, tries = 0; i < FileNameErasePasses; ++tries)
138            {
139                //Rename the folder.
140                string newPath = GenerateRandomFileName(info.Parent, info.Name.Length);
141
142                //Try to rename the file. If it fails, it is probably due to another
143                //process locking the file. Defer, then rename again.
144                try
145                {
146                    info.MoveTo(newPath);
147                    ++i;
148                }
149                catch (IOException e)
150                {
151                    switch (System.Runtime.InteropServices.Marshal.GetLastWin32Error())
152                    {
153                        case 5: //ERROR_ACCESS_DENIED
154                            throw new UnauthorizedAccessException(S._("The file {0} could not " +
155                                "be erased because the file's permissions prevent access to the file.",
156                                info.FullName), e);
157
158                        case 32: //ERROR_SHARING_VIOLATION
159                            //If after FilenameEraseTries the file is still locked, some program is
160                            //definitely using the file; throw an exception.
161                            if (tries > FileNameEraseTries)
162                                throw new IOException(S._("The file {0} is currently in use and " +
163                                    "cannot be removed.", info.FullName), e);
164
165                            //Let the process locking the file release the lock
166                            Thread.Sleep(100);
167                            break;
168
169                        default:
170                            throw;
171                    }
172                }
173            }
174
175            //Set the date of the directory to be invalid to prevent forensic
176            //detection
177            info.CreationTime = info.LastWriteTime = info.LastAccessTime = MinTimestamp;
178
179            //Remove the folder
180            info.Delete(true);
181        }
182
183        public override void EraseClusterTips(VolumeInfo info, ErasureMethod method,
184            Logger log, ClusterTipsSearchProgress searchCallback,
185            ClusterTipsEraseProgress eraseCallback)
186        {
187            //List all the files which can be erased.
188            List<string> files = new List<string>();
189            if (!info.IsMounted)
190                throw new InvalidOperationException(S._("Could not erase cluster tips in {0} " +
191                    "as the volume is not mounted.", info.VolumeId));
192            ListFiles(new DirectoryInfo(info.MountPoints[0]), files, log, searchCallback);
193
194            //For every file, erase the cluster tips.
195            for (int i = 0, j = files.Count; i != j; ++i)
196            {
197                //Get the file attributes for restoring later
198                StreamInfo streamInfo = new StreamInfo(files[i]);
199                FileAttributes fileAttr = streamInfo.Attributes;
200
201                try
202                {
203                    //Reset the file attributes.
204                    streamInfo.Attributes = FileAttributes.Normal;
205                    EraseFileClusterTips(files[i], method);
206                }
207                catch (UnauthorizedAccessException)
208                {
209                    log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
210                        "cluster tips erased because you do not have the required permissions to " +
211                        "erase the file cluster tips.", files[i]), LogLevel.Information));
212                }
213                catch (IOException e)
214                {
215                    log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
216                        "cluster tips erased. The error returned was: {1}", files[i],
217                        e.Message), LogLevel.Error));
218                }
219                finally
220                {
221                    streamInfo.Attributes = fileAttr;
222                }
223                eraseCallback(i, files.Count, files[i]);
224            }
225        }
226
227        private void ListFiles(DirectoryInfo info, List<string> files, Logger log,
228            ClusterTipsSearchProgress searchCallback)
229        {
230            try
231            {
232                //Skip this directory if it is a reparse point
233                if ((info.Attributes & FileAttributes.ReparsePoint) != 0)
234                {
235                    log.LastSessionEntries.Add(new LogEntry(S._("Files in {0} did " +
236                        "not have their cluster tips erased because it is a hard link or " +
237                        "a symbolic link.", info.FullName), LogLevel.Information));
238                    return;
239                }
240
241                foreach (FileInfo file in info.GetFiles())
242                    if (Util.File.IsProtectedSystemFile(file.FullName))
243                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
244                            "its cluster tips erased, because it is a system file",
245                            file.FullName), LogLevel.Information));
246                    else if ((file.Attributes & FileAttributes.ReparsePoint) != 0)
247                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
248                            "its cluster tips erased because it is a hard link or a " +
249                            "symbolic link.", file.FullName), LogLevel.Information));
250                    else if ((file.Attributes & FileAttributes.Compressed) != 0 ||
251                        (file.Attributes & FileAttributes.Encrypted) != 0 ||
252                        (file.Attributes & FileAttributes.SparseFile) != 0)
253                    {
254                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
255                            "its cluster tips erased because it is compressed, encrypted " +
256                            "or a sparse file.", file.FullName), LogLevel.Information));
257                    }
258                    else
259                    {
260                        try
261                        {
262                            foreach (string i in Util.File.GetADSes(file))
263                                files.Add(file.FullName + ':' + i);
264
265                            files.Add(file.FullName);
266                        }
267                        catch (UnauthorizedAccessException e)
268                        {
269                            log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " +
270                                "have its cluster tips erased because of the following " +
271                                "error: {1}", info.FullName, e.Message), LogLevel.Error));
272                        }
273                        catch (IOException e)
274                        {
275                            log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " +
276                                "have its cluster tips erased because of the following " +
277                                "error: {1}", info.FullName, e.Message), LogLevel.Error));
278                        }
279                    }
280
281                foreach (DirectoryInfo subDirInfo in info.GetDirectories())
282                {
283                    searchCallback(subDirInfo.FullName);
284                    ListFiles(subDirInfo, files, log, searchCallback);
285                }
286            }
287            catch (UnauthorizedAccessException e)
288            {
289                log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
290                    "cluster tips erased because of the following error: {1}",
291                    info.FullName, e.Message), LogLevel.Error));
292            }
293            catch (IOException e)
294            {
295                log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
296                    "cluster tips erased because of the following error: {1}",
297                    info.FullName, e.Message), LogLevel.Error));
298            }
299        }
300
301        /// <summary>
302        /// Erases the cluster tips of the given file.
303        /// </summary>
304        /// <param name="file">The file to erase.</param>
305        /// <param name="method">The erasure method to use.</param>
306        private void EraseFileClusterTips(string file, ErasureMethod method)
307        {
308            //Get the file access times
309            StreamInfo streamInfo = new StreamInfo(file);
310            DateTime lastAccess = streamInfo.LastAccessTime;
311            DateTime lastWrite = streamInfo.LastWriteTime;
312            DateTime created = streamInfo.CreationTime;
313
314            //And get the file lengths to know how much to overwrite
315            long fileArea = GetFileArea(file);
316            long fileLength = streamInfo.Length;
317
318            //If the file length equals the file area there is no cluster tip to overwrite
319            if (fileArea == fileLength)
320                return;
321
322            //Otherwise, create the stream, lengthen the file, then tell the erasure
323            //method to erase the cluster tips.
324            using (FileStream stream = streamInfo.Open(FileMode.Open, FileAccess.Write,
325                FileShare.None, FileOptions.WriteThrough))
326            {
327                try
328                {
329                    stream.SetLength(fileArea);
330                    stream.Seek(fileLength, SeekOrigin.Begin);
331
332                    //Erase the file
333                    method.Erase(stream, long.MaxValue, PrngManager.GetInstance(
334                        ManagerLibrary.Settings.ActivePrng), null);
335                }
336                finally
337                {
338                    //Make sure the file length is restored!
339                    stream.SetLength(fileLength);
340
341                    //Reset the file times
342                    streamInfo.LastAccessTime = lastAccess;
343                    streamInfo.LastWriteTime = lastWrite;
344                    streamInfo.CreationTime = created;
345                }
346            }
347        }
348
349        public override long GetFileArea(string filePath)
350        {
351            StreamInfo info = new StreamInfo(filePath);
352            VolumeInfo volume = VolumeInfo.FromMountpoint(info.Directory.FullName);
353            long clusterSize = volume.ClusterSize;
354            return (info.Length + (clusterSize - 1)) & ~(clusterSize - 1);
355        }
356
357        /// <summary>
358        /// The minimum timestamp the file system can take. This is for secure file
359        /// deletion.
360        /// </summary>
361        protected abstract DateTime MinTimestamp { get; }
362    }
363}
Note: See TracBrowser for help on using the repository browser.