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

Revision 1677, 11.9 KB checked in by lowjoel, 5 years ago (diff)

Updated copyright information: since Eraser is still under development we should update our copyright status.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 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            //Check that this folder is not the root of a drive since we can't delete
137            //roots of drives.
138            if (info.Parent == null)
139                return;
140
141            //Then clean up this folder.
142            for (int i = 0, tries = 0; i < FileNameErasePasses; ++tries)
143            {
144                //Rename the folder.
145                string newPath = GenerateRandomFileName(info.Parent, info.Name.Length);
146
147                //Try to rename the file. If it fails, it is probably due to another
148                //process locking the file. Defer, then rename again.
149                try
150                {
151                    info.MoveTo(newPath);
152                    ++i;
153                }
154                catch (IOException e)
155                {
156                    switch (System.Runtime.InteropServices.Marshal.GetLastWin32Error())
157                    {
158                        case 5: //ERROR_ACCESS_DENIED
159                            throw new UnauthorizedAccessException(S._("The file {0} could not " +
160                                "be erased because the file's permissions prevent access to the file.",
161                                info.FullName), e);
162
163                        case 32: //ERROR_SHARING_VIOLATION
164                            //If after FilenameEraseTries the file is still locked, some program is
165                            //definitely using the file; throw an exception.
166                            if (tries > FileNameEraseTries)
167                                throw new IOException(S._("The file {0} is currently in use and " +
168                                    "cannot be removed.", info.FullName), e);
169
170                            //Let the process locking the file release the lock
171                            Thread.Sleep(100);
172                            break;
173
174                        default:
175                            throw;
176                    }
177                }
178            }
179
180            //Set the date of the directory to be invalid to prevent forensic
181            //detection
182            info.CreationTime = info.LastWriteTime = info.LastAccessTime = MinTimestamp;
183
184            //Remove the folder
185            info.Delete(true);
186        }
187
188        public override void EraseClusterTips(VolumeInfo info, ErasureMethod method,
189            Logger log, ClusterTipsSearchProgress searchCallback,
190            ClusterTipsEraseProgress eraseCallback)
191        {
192            //List all the files which can be erased.
193            List<string> files = new List<string>();
194            if (!info.IsMounted)
195                throw new InvalidOperationException(S._("Could not erase cluster tips in {0} " +
196                    "as the volume is not mounted.", info.VolumeId));
197            ListFiles(new DirectoryInfo(info.MountPoints[0]), files, log, searchCallback);
198
199            //For every file, erase the cluster tips.
200            for (int i = 0, j = files.Count; i != j; ++i)
201            {
202                //Get the file attributes for restoring later
203                StreamInfo streamInfo = new StreamInfo(files[i]);
204                FileAttributes fileAttr = streamInfo.Attributes;
205
206                try
207                {
208                    //Reset the file attributes.
209                    streamInfo.Attributes = FileAttributes.Normal;
210                    EraseFileClusterTips(files[i], method);
211                }
212                catch (UnauthorizedAccessException)
213                {
214                    log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
215                        "cluster tips erased because you do not have the required permissions to " +
216                        "erase the file cluster tips.", files[i]), LogLevel.Information));
217                }
218                catch (IOException e)
219                {
220                    log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
221                        "cluster tips erased. The error returned was: {1}", files[i],
222                        e.Message), LogLevel.Error));
223                }
224                finally
225                {
226                    streamInfo.Attributes = fileAttr;
227                }
228                eraseCallback(i, files.Count, files[i]);
229            }
230        }
231
232        private void ListFiles(DirectoryInfo info, List<string> files, Logger log,
233            ClusterTipsSearchProgress searchCallback)
234        {
235            try
236            {
237                //Skip this directory if it is a reparse point
238                if ((info.Attributes & FileAttributes.ReparsePoint) != 0)
239                {
240                    log.LastSessionEntries.Add(new LogEntry(S._("Files in {0} did " +
241                        "not have their cluster tips erased because it is a hard link or " +
242                        "a symbolic link.", info.FullName), LogLevel.Information));
243                    return;
244                }
245
246                foreach (FileInfo file in info.GetFiles())
247                    if (Util.File.IsProtectedSystemFile(file.FullName))
248                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
249                            "its cluster tips erased, because it is a system file",
250                            file.FullName), LogLevel.Information));
251                    else if ((file.Attributes & FileAttributes.ReparsePoint) != 0)
252                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
253                            "its cluster tips erased because it is a hard link or a " +
254                            "symbolic link.", file.FullName), LogLevel.Information));
255                    else if ((file.Attributes & FileAttributes.Compressed) != 0 ||
256                        (file.Attributes & FileAttributes.Encrypted) != 0 ||
257                        (file.Attributes & FileAttributes.SparseFile) != 0)
258                    {
259                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
260                            "its cluster tips erased because it is compressed, encrypted " +
261                            "or a sparse file.", file.FullName), LogLevel.Information));
262                    }
263                    else
264                    {
265                        try
266                        {
267                            foreach (string i in Util.File.GetADSes(file))
268                                files.Add(file.FullName + ':' + i);
269
270                            files.Add(file.FullName);
271                        }
272                        catch (UnauthorizedAccessException e)
273                        {
274                            log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " +
275                                "have its cluster tips erased because of the following " +
276                                "error: {1}", info.FullName, e.Message), LogLevel.Error));
277                        }
278                        catch (IOException e)
279                        {
280                            log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " +
281                                "have its cluster tips erased because of the following " +
282                                "error: {1}", info.FullName, e.Message), LogLevel.Error));
283                        }
284                    }
285
286                foreach (DirectoryInfo subDirInfo in info.GetDirectories())
287                {
288                    searchCallback(subDirInfo.FullName);
289                    ListFiles(subDirInfo, files, log, searchCallback);
290                }
291            }
292            catch (UnauthorizedAccessException e)
293            {
294                log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
295                    "cluster tips erased because of the following error: {1}",
296                    info.FullName, e.Message), LogLevel.Error));
297            }
298            catch (IOException e)
299            {
300                log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
301                    "cluster tips erased because of the following error: {1}",
302                    info.FullName, e.Message), LogLevel.Error));
303            }
304        }
305
306        /// <summary>
307        /// Erases the cluster tips of the given file.
308        /// </summary>
309        /// <param name="file">The file to erase.</param>
310        /// <param name="method">The erasure method to use.</param>
311        private void EraseFileClusterTips(string file, ErasureMethod method)
312        {
313            //Get the file access times
314            StreamInfo streamInfo = new StreamInfo(file);
315            DateTime lastAccess = streamInfo.LastAccessTime;
316            DateTime lastWrite = streamInfo.LastWriteTime;
317            DateTime created = streamInfo.CreationTime;
318
319            //And get the file lengths to know how much to overwrite
320            long fileArea = GetFileArea(file);
321            long fileLength = streamInfo.Length;
322
323            //If the file length equals the file area there is no cluster tip to overwrite
324            if (fileArea == fileLength)
325                return;
326
327            //Otherwise, create the stream, lengthen the file, then tell the erasure
328            //method to erase the cluster tips.
329            using (FileStream stream = streamInfo.Open(FileMode.Open, FileAccess.Write,
330                FileShare.None, FileOptions.WriteThrough))
331            {
332                try
333                {
334                    stream.SetLength(fileArea);
335                    stream.Seek(fileLength, SeekOrigin.Begin);
336
337                    //Erase the file
338                    method.Erase(stream, long.MaxValue, PrngManager.GetInstance(
339                        ManagerLibrary.Settings.ActivePrng), null);
340                }
341                finally
342                {
343                    //Make sure the file length is restored!
344                    stream.SetLength(fileLength);
345
346                    //Reset the file times
347                    streamInfo.LastAccessTime = lastAccess;
348                    streamInfo.LastWriteTime = lastWrite;
349                    streamInfo.CreationTime = created;
350                }
351            }
352        }
353
354        public override long GetFileArea(string filePath)
355        {
356            StreamInfo info = new StreamInfo(filePath);
357            VolumeInfo volume = VolumeInfo.FromMountpoint(info.Directory.FullName);
358            long clusterSize = volume.ClusterSize;
359            return (info.Length + (clusterSize - 1)) & ~(clusterSize - 1);
360        }
361
362        /// <summary>
363        /// The minimum timestamp the file system can take. This is for secure file
364        /// deletion.
365        /// </summary>
366        protected abstract DateTime MinTimestamp { get; }
367    }
368}
Note: See TracBrowser for help on using the repository browser.