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

Revision 1719, 12.0 KB checked in by lowjoel, 5 years ago (diff)

Backported changes from trunk.

r1718: Author: lowjoel: When we have problems starting Eraser from the shell extension check the return code for ERROR_ELEVATION_REQUIRED; if we get that, then we should re-run the operation as an administrator.
r1717: Author: lowjoel: If the directory we are deleting does not exist, we should just return -- there's nothing to be deleted.
r1716: Author: lowjoel: Catch IOExceptions when we try to connect to other running instances and show a error message when one occurs.
r1715: Author: lowjoel: Set that files are not meant to be indexed when it is meant for deletion before we even set the file times.
r1714: Author: lowjoel: Fixed race condition potentially created by initialising the remote executor server thread immediately upon construction since Run is not yet called.
r1713: Author: lowjoel: Since we only force the creation of the SchedulerPanel?'s handle in the constructor, InvokeRequired? should be called on the panel itself, and not on subcontrols as they are still delay-constructed. Fixes crash when Eraser is started quietly and a task is created remotely.
r1712: Author: lowjoel: ThreadAbortExceptions? should not trigger BlackBox? report creation.

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