source: trunk/eraser6/Eraser.DefaultPlugins/FileSystems/Windows.cs @ 1358

Revision 1358, 9.9 KB checked in by lowjoel, 5 years ago (diff)

Set svn:keywords and svn:eol-style

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008 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)
59                {
60                    Thread.Sleep(100);
61
62                    //If after FilenameEraseTries the file is still locked, some program is
63                    //definitely using the file; throw an exception.
64                    if (tries > FileNameEraseTries)
65                        throw new IOException(S._("The file {0} is currently in use and " +
66                            "cannot be removed.", info.FullName));
67                }
68            }
69
70            //If the user wants plausible deniability, find a random file on the same
71            //volume and write it over.
72            if (Manager.ManagerLibrary.Settings.PlausibleDeniability)
73            {
74                CopyPlausibleDeniabilityFile(info.OpenWrite());
75            }
76
77            //Then delete the file.
78            for (int i = 0; i < FileNameEraseTries; ++i)
79                try
80                {
81                    info.Delete();
82                    break;
83                }
84                catch (IOException)
85                {
86                    if (i > FileNameEraseTries)
87                        throw new IOException(S._("The file {0} is currently in use and " +
88                            "cannot be removed.", info.FullName));
89                    Thread.Sleep(100);
90                }
91        }
92
93        public override void DeleteFolder(DirectoryInfo info, bool recursive)
94        {
95            if (!recursive && info.GetFileSystemInfos().Length != 0)
96                throw new InvalidOperationException(S._("The folder {0} cannot be deleted as it is " +
97                    "not empty."));
98
99            //TODO: check for reparse points
100            foreach (DirectoryInfo dir in info.GetDirectories())
101                DeleteFolder(dir);
102            foreach (FileInfo file in info.GetFiles())
103                DeleteFile(file);
104
105            //Then clean up this folder.
106            for (int i = 0; i < FileNameErasePasses; ++i)
107            {
108                //Rename the folder.
109                string newPath = GenerateRandomFileName(info.Parent, info.Name.Length);
110
111                //Try to rename the file. If it fails, it is probably due to another
112                //process locking the file. Defer, then rename again.
113                try
114                {
115                    info.MoveTo(newPath);
116                }
117                catch (IOException)
118                {
119                    Thread.Sleep(100);
120                    --i;
121                }
122            }
123
124            //Set the date of the directory to be invalid to prevent forensic
125            //detection
126            info.CreationTime = info.LastWriteTime = info.LastAccessTime = MinTimestamp;
127
128            //Remove the folder
129            info.Delete(true);
130        }
131
132        public override void EraseClusterTips(VolumeInfo info, ErasureMethod method,
133            Logger log, ClusterTipsSearchProgress searchCallback,
134            ClusterTipsEraseProgress eraseCallback)
135        {
136            //List all the files which can be erased.
137            List<string> files = new List<string>();
138            if (!info.IsMounted)
139                throw new InvalidOperationException(S._("Could not erase cluster tips in {0} " +
140                    "as the volume is not mounted.", info.VolumeId));
141            ListFiles(new DirectoryInfo(info.MountPoints[0]), files, log, searchCallback);
142
143            //For every file, erase the cluster tips.
144            for (int i = 0, j = files.Count; i != j; ++i)
145            {
146                //Get the file attributes for restoring later
147                StreamInfo streamInfo = new StreamInfo(files[i]);
148                FileAttributes fileAttr = streamInfo.Attributes;
149
150                try
151                {
152                    //Reset the file attributes.
153                    streamInfo.Attributes = FileAttributes.Normal;
154                    EraseFileClusterTips(files[i], method);
155                }
156                catch (UnauthorizedAccessException)
157                {
158                    log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
159                        "cluster tips erased because you do not have the required permissions to " +
160                        "erase the file cluster tips.", files[i]), LogLevel.Error));
161                }
162                catch (IOException e)
163                {
164                    log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
165                        "cluster tips erased. The error returned was: {1}", files[i],
166                        e.Message), LogLevel.Error));
167                }
168                finally
169                {
170                    streamInfo.Attributes = fileAttr;
171                }
172                eraseCallback(i, files.Count, files[i]);
173            }
174        }
175
176        private void ListFiles(DirectoryInfo info, List<string> files, Logger log,
177            ClusterTipsSearchProgress searchCallback)
178        {
179            try
180            {
181                //Skip this directory if it is a reparse point
182                if ((info.Attributes & FileAttributes.ReparsePoint) != 0)
183                {
184                    log.LastSessionEntries.Add(new LogEntry(S._("Files in {0} did " +
185                        "not have their cluster tips erased because it is a hard link or " +
186                        "a symbolic link.", info.FullName), LogLevel.Information));
187                    return;
188                }
189
190                foreach (FileInfo file in info.GetFiles())
191                    if (Util.File.IsProtectedSystemFile(file.FullName))
192                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
193                            "its cluster tips erased, because it is a system file",
194                            file.FullName), LogLevel.Information));
195                    else if ((file.Attributes & FileAttributes.ReparsePoint) != 0)
196                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
197                            "its cluster tips erased because it is a hard link or a " +
198                            "symbolic link.", file.FullName), LogLevel.Information));
199                    else if ((file.Attributes & FileAttributes.Compressed) != 0 ||
200                        (file.Attributes & FileAttributes.Encrypted) != 0 ||
201                        (file.Attributes & FileAttributes.SparseFile) != 0)
202                    {
203                        log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have " +
204                            "its cluster tips erased because it is compressed, encrypted " +
205                            "or a sparse file.", file.FullName), LogLevel.Information));
206                    }
207                    else
208                    {
209                        try
210                        {
211                            foreach (string i in Util.File.GetADSes(file))
212                                files.Add(file.FullName + ':' + i);
213
214                            files.Add(file.FullName);
215                        }
216                        catch (UnauthorizedAccessException e)
217                        {
218                            log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " +
219                                "have its cluster tips erased because of the following " +
220                                "error: {1}", info.FullName, e.Message), LogLevel.Error));
221                        }
222                        catch (IOException e)
223                        {
224                            log.LastSessionEntries.Add(new LogEntry(S._("{0} did not " +
225                                "have its cluster tips erased because of the following " +
226                                "error: {1}", info.FullName, e.Message), LogLevel.Error));
227                        }
228                    }
229
230                foreach (DirectoryInfo subDirInfo in info.GetDirectories())
231                {
232                    searchCallback(subDirInfo.FullName);
233                    ListFiles(subDirInfo, files, log, searchCallback);
234                }
235            }
236            catch (UnauthorizedAccessException e)
237            {
238                log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
239                    "cluster tips erased because of the following error: {1}",
240                    info.FullName, e.Message), LogLevel.Error));
241            }
242            catch (IOException e)
243            {
244                log.LastSessionEntries.Add(new LogEntry(S._("{0} did not have its " +
245                    "cluster tips erased because of the following error: {1}",
246                    info.FullName, e.Message), LogLevel.Error));
247            }
248        }
249
250        /// <summary>
251        /// Erases the cluster tips of the given file.
252        /// </summary>
253        /// <param name="file">The file to erase.</param>
254        /// <param name="method">The erasure method to use.</param>
255        private void EraseFileClusterTips(string file, ErasureMethod method)
256        {
257            //Get the file access times
258            StreamInfo streamInfo = new StreamInfo(file);
259            DateTime lastAccess = streamInfo.LastAccessTime;
260            DateTime lastWrite = streamInfo.LastWriteTime;
261            DateTime created = streamInfo.CreationTime;
262
263            //And get the file lengths to know how much to overwrite
264            long fileArea = GetFileArea(file);
265            long fileLength = streamInfo.Length;
266
267            //If the file length equals the file area there is no cluster tip to overwrite
268            if (fileArea == fileLength)
269                return;
270
271            //Otherwise, create the stream, lengthen the file, then tell the erasure
272            //method to erase the cluster tips.
273            using (FileStream stream = streamInfo.Open(FileMode.Open, FileAccess.Write,
274                FileShare.None, FileOptions.WriteThrough))
275            {
276                try
277                {
278                    stream.SetLength(fileArea);
279                    stream.Seek(fileLength, SeekOrigin.Begin);
280
281                    //Erase the file
282                    method.Erase(stream, long.MaxValue, PrngManager.GetInstance(
283                        ManagerLibrary.Settings.ActivePrng), null);
284                }
285                finally
286                {
287                    //Make sure the file length is restored!
288                    stream.SetLength(fileLength);
289
290                    //Reset the file times
291                    streamInfo.LastAccessTime = lastAccess;
292                    streamInfo.LastWriteTime = lastWrite;
293                    streamInfo.CreationTime = created;
294                }
295            }
296        }
297
298        public override long GetFileArea(string filePath)
299        {
300            StreamInfo info = new StreamInfo(filePath);
301            VolumeInfo volume = VolumeInfo.FromMountpoint(info.Directory.FullName);
302            long clusterSize = volume.ClusterSize;
303            return (info.Length + (clusterSize - 1)) & ~(clusterSize - 1);
304        }
305
306        /// <summary>
307        /// The minimum timestamp the file system can take. This is for secure file
308        /// deletion.
309        /// </summary>
310        protected abstract DateTime MinTimestamp { get; }
311    }
312}
Note: See TracBrowser for help on using the repository browser.