source: branches/eraser6/CodeReview/Eraser.DefaultPlugins/FileSystems/Windows.cs @ 1777

Revision 1777, 10.5 KB checked in by lowjoel, 5 years ago (diff)

Redesigned the Logger paradigm -- instead of a dealing with a logger object to do any form of logging, we have a static Logger class which code can log to, and define log targets to receive those logs. Also included is a LogSession? class which sets the log target for the current thread or for a few threads. Addresses #320: Logging improvements

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