source: branches/eraser6/pluginsRewrite/Eraser.DefaultPlugins/ErasureTargets/UnusedSpaceErasureTarget.cs @ 2344

Revision 2344, 11.3 KB checked in by lowjoel, 3 years ago (diff)

Synchronise the Plugins Rewrite branch with trunk@r2341

  • 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.Linq;
25using System.Text;
26
27using System.Runtime.Serialization;
28using System.Runtime.InteropServices;
29using System.Security.Permissions;
30using System.IO;
31
32using Eraser.Manager;
33using Eraser.Util;
34using Eraser.Util.ExtensionMethods;
35
36namespace Eraser.DefaultPlugins
37{
38    /// <summary>
39    /// Class representing a unused space erase.
40    /// </summary>
41    [Serializable]
42    [Guid("A627BEC4-CAFC-46ce-92AD-209157C3177A")]
43    public class UnusedSpaceErasureTarget : ErasureTarget
44    {
45        #region Serialization code
46        protected UnusedSpaceErasureTarget(SerializationInfo info, StreamingContext context)
47            : base(info, context)
48        {
49            Drive = (string)info.GetValue("Drive", typeof(string));
50            EraseClusterTips = (bool)info.GetValue("EraseClusterTips", typeof(bool));
51        }
52
53        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
54        public override void GetObjectData(SerializationInfo info, StreamingContext context)
55        {
56            base.GetObjectData(info, context);
57            info.AddValue("Drive", Drive);
58            info.AddValue("EraseClusterTips", EraseClusterTips);
59        }
60        #endregion
61
62        /// <summary>
63        /// Constructor.
64        /// </summary>
65        public UnusedSpaceErasureTarget()
66            : base()
67        {
68        }
69
70        public override Guid Guid
71        {
72            get { return GetType().GUID; }
73        }
74
75        public override string Name
76        {
77            get { return S._("Unused disk space"); }
78        }
79
80        public sealed override ErasureMethod EffectiveMethod
81        {
82            get
83            {
84                if (Method != ErasureMethodRegistrar.Default)
85                    return base.EffectiveMethod;
86
87                return ManagerLibrary.Instance.ErasureMethodRegistrar[
88                    ManagerLibrary.Settings.DefaultUnusedSpaceErasureMethod];
89            }
90        }
91
92        public override bool SupportsMethod(ErasureMethod method)
93        {
94            return method == ErasureMethodRegistrar.Default ||
95                method is UnusedSpaceErasureMethod;
96        }
97
98        /// <summary>
99        /// Override the base class property so that we won't need to keep casting
100        /// </summary>
101        protected new SteppedProgressManager Progress
102        {
103            get
104            {
105                return (SteppedProgressManager)base.Progress;
106            }
107            set
108            {
109                base.Progress = value;
110            }
111        }
112
113        public override string UIText
114        {
115            get { return S._("Unused disk space ({0})", Drive); }
116        }
117
118        public override IErasureTargetConfigurer Configurer
119        {
120            get { return new UnusedSpaceErasureTargetConfigurer(); }
121        }
122
123        /// <summary>
124        /// The drive to erase
125        /// </summary>
126        public string Drive { get; set; }
127
128        /// <summary>
129        /// Whether cluster tips should be erased.
130        /// </summary>
131        public bool EraseClusterTips { get; set; }
132
133        public override void Execute()
134        {
135            //Check for sufficient privileges to run the unused space erasure.
136            if (!Security.IsAdministrator())
137            {
138                if (Environment.OSVersion.Platform == PlatformID.Win32NT &&
139                    Environment.OSVersion.Version >= new Version(6, 0))
140                {
141                    Logger.Log(S._("The program does not have the required permissions to erase " +
142                        "the unused space on disk. Run the program as an administrator and retry " +
143                        "the operation."), LogLevel.Error);
144                }
145                else
146                {
147                    Logger.Log(S._("The program does not have the required permissions to erase " +
148                        "the unused space on disk."), LogLevel.Error);
149                }
150
151                return;
152            }
153
154            //Check whether System Restore has any available checkpoints.
155            if (SystemRestore.GetInstances().Count != 0)
156            {
157                Logger.Log(S._("This computer has had System Restore or Volume Shadow Copies " +
158                    "enabled. This may allow copies of files stored on the disk to be recovered " +
159                    "and pose a security concern.", Drive), LogLevel.Warning);
160            }
161
162            //If the user is under disk quotas, log a warning message
163            if (VolumeInfo.FromMountPoint(Drive).HasQuota)
164            {
165                Logger.Log(S._("The drive {0} has disk quotas active. This will prevent the " +
166                    "complete erasure of unused space and may pose a security concern.",
167                    Drive), LogLevel.Warning);
168            }
169
170            //Get the erasure method if the user specified he wants the default.
171            ErasureMethod method = EffectiveMethod;
172
173            //Make a folder to dump our temporary files in
174            DirectoryInfo info = new DirectoryInfo(Drive);
175            VolumeInfo volInfo = VolumeInfo.FromMountPoint(Drive);
176            FileSystem fsManager = ManagerLibrary.Instance.FileSystemRegistrar[volInfo];
177
178            //Start sampling the speed of the task.
179            Progress = new SteppedProgressManager();
180            Task.Progress.Steps.Add(new SteppedProgressManagerStep(
181                Progress, 1.0f / Task.Targets.Count));
182
183            //Erase the cluster tips of every file on the drive.
184            if (EraseClusterTips)
185            {
186                //Define the callback handlers
187                ProgressManager tipSearch = new ProgressManager();
188                tipSearch.MarkIndeterminate();
189                Progress.Steps.Add(new SteppedProgressManagerStep(tipSearch,
190                    0.0f, S._("Searching for files' cluster tips...")));
191                ClusterTipsSearchProgress searchProgress = delegate(string path)
192                {
193                    if (Task.Canceled)
194                        throw new OperationCanceledException(S._("The task was cancelled."));
195
196                    OnProgressChanged(this, new ProgressChangedEventArgs(tipSearch,
197                        new TaskProgressChangedEventArgs(path, 0, 0)));
198                };
199
200                ProgressManager tipProgress = new ProgressManager();
201                Progress.Steps.Add(new SteppedProgressManagerStep(tipProgress, 0.1f,
202                    S._("Erasing cluster tips...")));
203                ClusterTipsEraseProgress eraseProgress =
204                    delegate(int currentFile, int totalFiles, string currentFilePath)
205                    {
206                        tipSearch.MarkComplete();
207                        tipProgress.Total = totalFiles;
208                        tipProgress.Completed = currentFile;
209                        OnProgressChanged(this, new ProgressChangedEventArgs(tipProgress,
210                            new TaskProgressChangedEventArgs(currentFilePath, 0, 0)));
211
212                        if (Task.Canceled)
213                            throw new OperationCanceledException(S._("The task was cancelled."));
214                    };
215
216                //Start counting statistics
217                fsManager.EraseClusterTips(VolumeInfo.FromMountPoint(Drive),
218                    method, searchProgress, eraseProgress);
219                tipProgress.MarkComplete();
220            }
221
222            bool lowDiskSpaceNotifications = Shell.LowDiskSpaceNotificationsEnabled;
223            info = info.CreateSubdirectory(Path.GetFileName(
224                FileSystem.GenerateRandomFileName(info, 18)));
225            try
226            {
227                //Set the folder's compression flag off since we want to use as much
228                //space as possible
229                if (info.IsCompressed())
230                    info.Uncompress();
231
232                //Disable the low disk space notifications
233                Shell.LowDiskSpaceNotificationsEnabled = false;
234
235                //Fill the disk
236                EraseUnusedSpace(volInfo, info, fsManager, method);
237
238                //Erase old resident file system table files
239                ProgressManager residentProgress = new ProgressManager();
240                Progress.Steps.Add(new SteppedProgressManagerStep(residentProgress,
241                    0.05f, S._("Old resident file system table files")));
242                fsManager.EraseOldFileSystemResidentFiles(volInfo, info, method,
243                    delegate(int currentFile, int totalFiles)
244                    {
245                        residentProgress.Completed = currentFile;
246                        residentProgress.Total = totalFiles;
247                        OnProgressChanged(this, new ProgressChangedEventArgs(residentProgress,
248                            new TaskProgressChangedEventArgs(string.Empty, 0, 0)));
249
250                        if (Task.Canceled)
251                            throw new OperationCanceledException(S._("The task was cancelled."));
252                    }
253                );
254
255                residentProgress.MarkComplete();
256            }
257            finally
258            {
259                //Remove the folder holding all our temporary files.
260                ProgressManager tempFiles = new ProgressManager();
261                Progress.Steps.Add(new SteppedProgressManagerStep(tempFiles,
262                    0.0f, S._("Removing temporary files...")));
263                OnProgressChanged(this, new ProgressChangedEventArgs(tempFiles,
264                    new TaskProgressChangedEventArgs(string.Empty, 0, 0)));
265                info.Delete(true);
266                tempFiles.MarkComplete();
267
268                //Reset the low disk space notifications
269                Shell.LowDiskSpaceNotificationsEnabled = lowDiskSpaceNotifications;
270            }
271
272            //Then clean the old file system entries
273            ProgressManager structureProgress = new ProgressManager();
274            Progress.Steps.Add(new SteppedProgressManagerStep(structureProgress,
275                0.05f, S._("Erasing unused directory structures...")));
276            fsManager.EraseDirectoryStructures(volInfo,
277                delegate(int currentFile, int totalFiles)
278                {
279                    if (Task.Canceled)
280                        throw new OperationCanceledException(S._("The task was cancelled."));
281
282                    //Compute the progress
283                    structureProgress.Total = totalFiles;
284                    structureProgress.Completed = currentFile;
285
286                    //Set the event parameters, then broadcast the progress event.
287                    OnProgressChanged(this, new ProgressChangedEventArgs(structureProgress,
288                        new TaskProgressChangedEventArgs(string.Empty, 0, 0)));
289                }
290            );
291
292            structureProgress.MarkComplete();
293            Progress = null;
294        }
295
296        private void EraseUnusedSpace(VolumeInfo volInfo, DirectoryInfo info, FileSystem fsInfo,
297            ErasureMethod method)
298        {
299            ProgressManager mainProgress = new ProgressManager();
300            Progress.Steps.Add(new SteppedProgressManagerStep(mainProgress,
301                EraseClusterTips ? 0.8f : 0.9f, S._("Erasing unused space...")));
302
303            //Continue creating files while there is free space.
304            while (volInfo.AvailableFreeSpace > 0)
305            {
306                //Generate a non-existant file name
307                string currFile = FileSystem.GenerateRandomFileName(info, 18);
308
309                //Create the stream
310                FileStream stream = new FileStream(currFile, FileMode.CreateNew,
311                    FileAccess.Write, FileShare.None, 8, FileOptions.WriteThrough);
312                try
313                {
314                    //Set the length of the file to be the amount of free space left
315                    //or the maximum size of one of these dumps.
316                    mainProgress.Total = mainProgress.Completed +
317                        method.CalculateEraseDataSize(null, volInfo.AvailableFreeSpace);
318                    long streamLength = Math.Min(ErasureMethod.FreeSpaceFileUnit,
319                        volInfo.AvailableFreeSpace);
320
321                    //Handle IO exceptions gracefully, because the filesystem
322                    //may require more space than demanded by us for file allocation.
323                    while (true)
324                        try
325                        {
326                            stream.SetLength(streamLength);
327                            break;
328                        }
329                        catch (IOException)
330                        {
331                            if (streamLength > volInfo.ClusterSize)
332                                streamLength -= volInfo.ClusterSize;
333                            else
334                                throw;
335                        }
336
337                    //Then run the erase task
338                    method.Erase(stream, long.MaxValue,
339                        ManagerLibrary.Instance.PrngRegistrar[ManagerLibrary.Settings.ActivePrng],
340                        delegate(long lastWritten, long totalData, int currentPass)
341                        {
342                            mainProgress.Completed += lastWritten;
343                            OnProgressChanged(this, new ProgressChangedEventArgs(mainProgress,
344                                new TaskProgressChangedEventArgs(Drive, currentPass, method.Passes)));
345
346                            if (Task.Canceled)
347                                throw new OperationCanceledException(S._("The task was cancelled."));
348                        }
349                    );
350                }
351                finally
352                {
353                    stream.Close();
354                    fsInfo.ResetFileTimes(new FileInfo(currFile));
355                }
356            }
357
358            //Mark the main bulk of the progress as complete
359            mainProgress.MarkComplete();
360        }
361    }
362}
Note: See TracBrowser for help on using the repository browser.