source: trunk/eraser6/Eraser.Manager/Method.cs @ 1360

Revision 1360, 18.9 KB checked in by lowjoel, 5 years ago (diff)

Eraser's still under development, so update the copyright notice.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 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;
25using System.IO;
26using System.Reflection;
27using Eraser.Util;
28
29namespace Eraser.Manager
30{
31    /// <summary>
32    /// An interface class representing the method for erasure. If classes only
33    /// inherit this class, then the method can only be used to erase abstract
34    /// streams, not unused drive space.
35    /// </summary>
36    public abstract class ErasureMethod
37    {
38        public override string ToString()
39        {
40            if (Passes == 0)
41                return Name;
42            return Passes == 1 ? S._("{0} (1 pass)", Name) :
43                S._("{0} ({1} passes)", Name, Passes);
44        }
45
46        /// <summary>
47        /// The name of this erase pass, used for display in the UI
48        /// </summary>
49        public abstract string Name
50        {
51            get;
52        }
53
54        /// <summary>
55        /// The number of erase passes for this erasure method.
56        /// </summary>
57        public abstract int Passes
58        {
59            get;
60        }
61
62        /// <summary>
63        /// The GUID for this erasure method.
64        /// </summary>
65        public abstract Guid Guid
66        {
67            get;
68        }
69
70        /// <summary>
71        /// Calculates the total size of the erasure data that needs to be written.
72        /// This is mainly for use by the Manager to determine how much data needs
73        /// to be written to disk.
74        /// </summary>
75        /// <param name="paths">The list containing the file paths to erase. This
76        /// may be null if the list of paths are unknown.</param>
77        /// <param name="targetSize">The precomputed value of the total size of
78        /// the files to be erased.</param>
79        /// <returns>The total size of the files that need to be erased.</returns>
80        /// <remarks>This function MAY be slow. Most erasure methods can
81        /// calculate this amount fairly quickly as the number of files and the
82        /// total size of the files (the ones that take most computation time)
83        /// are already provided. However some exceptional cases may take a
84        /// long time if the data set is large.</remarks>
85        public abstract long CalculateEraseDataSize(ICollection<string> paths, long targetSize);
86
87        /// <summary>
88        /// The main bit of the class! This function is called whenever data has
89        /// to be erased. Erase the stream passed in, using the given PRNG for
90        /// randomness where necessary.
91        ///
92        /// This function should be implemented thread-safe as using the same
93        /// instance, this function may be called across different threads.
94        /// </summary>
95        /// <param name="stream">The stream which needs to be erased.</param>
96        /// <param name="erasureLength">The length of the stream to erase. If all
97        /// data in the stream should be overwritten, then pass in the maximum
98        /// value for long, the function will take the minimum.</param>
99        /// <param name="prng">The PRNG source for random data.</param>
100        /// <param name="callback">The progress callback function.</param>
101        public abstract void Erase(Stream stream, long erasureLength, Prng prng,
102            ErasureMethodProgressFunction callback);
103
104        /// <summary>
105        /// Disk operation write unit. Chosen such that this value mod 3, 4, 512,
106        /// and 1024 is 0
107        /// </summary>
108        public const int DiskOperationUnit = 1536 * 4096;
109
110        /// <summary>
111        /// Unused space erasure file size. Each of the files used in erasing
112        /// unused space will be of this size.
113        /// </summary>
114        public const int FreeSpaceFileUnit = DiskOperationUnit * 36;
115
116        /// <summary>
117        /// Shuffles the passes in the input array, effectively randomizing the
118        /// order or rewrites.
119        /// </summary>
120        /// <param name="passes">The input set of passes.</param>
121        /// <returns>The shuffled set of passes.</returns>
122        protected static ErasureMethodPass[] ShufflePasses(ErasureMethodPass[] passes)
123        {
124            //Make a copy.
125            ErasureMethodPass[] result = new ErasureMethodPass[passes.Length];
126            passes.CopyTo(result, 0);
127
128            //Randomize.
129            Prng rand = PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng);
130            for (int i = 0; i < result.Length; ++i)
131            {
132                int val = rand.Next(result.Length - 1);
133                ErasureMethodPass tmpPass = result[val];
134                result[val] = result[i];
135                result[i] = tmpPass;
136            }
137
138            return result;
139        }
140
141        /// <summary>
142        /// Helper function. This function will write random data to the stream
143        /// using the provided PRNG.
144        /// </summary>
145        /// <param name="strm">The buffer to populate with data to write to disk.</param>
146        /// <param name="prng">The PRNG used.</param>
147        public static void WriteRandom(byte[] buffer, object value)
148        {
149            ((Prng)value).NextBytes(buffer);
150        }
151
152        /// <summary>
153        /// Helper function. This function will write the repeating pass constant.
154        /// to the provided buffer.
155        /// </summary>
156        /// <param name="strm">The buffer to populate with data to write to disk.</param>
157        /// <param name="value">The byte[] to write.</param>
158        public static void WriteConstant(byte[] buffer, object value)
159        {
160            byte[] constant = (byte[])value;
161            for (int i = 0; i < buffer.Length; ++i)
162                buffer[i] = constant[i % constant.Length];
163        }
164    }
165
166    /// <summary>
167    /// A simple callback for clients to retrieve progress information from
168    /// the erase method.
169    /// </summary>
170    /// <param name="lastWritten">The amount of data written to the stream since
171    /// the last call to the delegate.</param>
172    /// <param name="totalData">The total amount of data that must be written to
173    /// complete the erasure.</param>
174    /// <param name="currentPass">The current pass number. The total number
175    /// of passes can be found from the Passes property.</param>
176    public delegate void ErasureMethodProgressFunction(long lastWritten, long totalData,
177        int currentPass);
178
179    /// <summary>
180    /// A pass object. This object holds both the pass function, as well as the
181    /// data used for the pass (random, byte, or triplet)
182    /// </summary>
183    public class ErasureMethodPass
184    {
185        public override string ToString()
186        {
187            return OpaqueValue == null ? S._("Random") : OpaqueValue.ToString();
188        }
189
190        /// <summary>
191        /// Constructor.
192        /// </summary>
193        /// <param name="function">The delegate to the function.</param>
194        /// <param name="opaqueValue">The opaque value passed to the function.</param>
195        public ErasureMethodPass(ErasureMethodPassFunction function, object opaqueValue)
196        {
197            Function = function;
198            OpaqueValue = opaqueValue;
199        }
200
201        /// <summary>
202        /// Executes the pass.
203        /// </summary>
204        /// <param name="buffer">The buffer to populate with the data to write.</param>
205        /// <param name="prng">The PRNG used for random passes.</param>
206        public void Execute(byte[] buffer, Prng prng)
207        {
208            Function(buffer, OpaqueValue == null ? prng : OpaqueValue);
209        }
210
211        /// <summary>
212        /// The function to execute for this pass.
213        /// </summary>
214        public ErasureMethodPassFunction Function { get; set; }
215
216        /// <summary>
217        /// The value to be passed to the executing function.
218        /// </summary>
219        public object OpaqueValue { get; set; }
220    }
221
222    /// <summary>
223    /// The prototype of a pass.
224    /// </summary>
225    /// <param name="strm">The buffer to populate with data to write to disk.</param>
226    /// <param name="opaque">An opaque value, depending on the type of callback.</param>
227    public delegate void ErasureMethodPassFunction(byte[] buffer, object opaque);
228
229    /// <summary>
230    /// This class adds functionality to the ErasureMethod class to erase
231    /// unused drive space.
232    /// </summary>
233    public abstract class UnusedSpaceErasureMethod : ErasureMethod
234    {
235        /// <summary>
236        /// This function will allow clients to erase a file in a set of files
237        /// used to fill the disk, thus achieving disk unused space erasure.
238        ///
239        /// By default, this function will simply call the Erase method inherited
240        /// from the ErasureMethod class.
241        ///
242        /// This function should be implemented thread-safe as using the same
243        /// instance, this function may be called across different threads.
244        /// </summary>
245        /// <param name="strm">The stream which needs to be erased.</param>
246        /// <param name="prng">The PRNG source for random data.</param>
247        /// <param name="callback">The progress callback function.</param>
248        public virtual void EraseUnusedSpace(Stream stream, Prng prng, ErasureMethodProgressFunction callback)
249        {
250            Erase(stream, long.MaxValue, prng, callback);
251        }
252    }
253
254    /// <summary>
255    /// Pass-based erasure method. This subclass of erasure methods follow a fixed
256    /// pattern (constant or random data) for every pass, although the order of
257    /// passes can be randomized. This is to simplify definitions of classes in
258    /// plugins.
259    ///
260    /// Since instances of this class apply data by passes, they can by default
261    /// erase unused drive space as well.
262    /// </summary>
263    public abstract class PassBasedErasureMethod : UnusedSpaceErasureMethod
264    {
265        public override int Passes
266        {
267            get { return PassesSet.Length; }
268        }
269
270        /// <summary>
271        /// Whether the passes should be randomized before running them in random
272        /// order.
273        /// </summary>
274        protected abstract bool RandomizePasses
275        {
276            get;
277        }
278
279        /// <summary>
280        /// The set of Pass objects describing the passes in this erasure method.
281        /// </summary>
282        protected abstract ErasureMethodPass[] PassesSet
283        {
284            get;
285        }
286
287        public override long CalculateEraseDataSize(ICollection<string> paths, long targetSize)
288        {
289            //Simple. Amount of data multiplied by passes.
290            return targetSize * Passes;
291        }
292
293        public override void Erase(Stream stream, long erasureLength, Prng prng,
294            ErasureMethodProgressFunction callback)
295        {
296            //Randomize the order of the passes
297            ErasureMethodPass[] randomizedPasses = PassesSet;
298            if (RandomizePasses)
299                randomizedPasses = ShufflePasses(randomizedPasses);
300
301            //Remember the starting position of the stream.
302            long strmStart = stream.Position;
303            long strmLength = Math.Min(stream.Length - strmStart, erasureLength);
304            long totalData = CalculateEraseDataSize(null, strmLength);
305
306            //Allocate memory for a buffer holding data for the pass.
307            byte[] buffer = new byte[Math.Min(DiskOperationUnit, strmLength)];
308
309            //Run every pass!
310            for (int pass = 0; pass < Passes; ++pass)
311            {
312                //Do a progress callback first.
313                if (callback != null)
314                    callback(0, totalData, pass + 1);
315
316                //Start from the beginning again
317                stream.Seek(strmStart, SeekOrigin.Begin);
318
319                //Write the buffer to disk.
320                long toWrite = strmLength;
321                int dataStopped = buffer.Length;
322                while (toWrite > 0)
323                {
324                    //Calculate how much of the buffer to write to disk.
325                    int amount = (int)Math.Min(toWrite, buffer.Length - dataStopped);
326
327                    //If we have no data left, get more!
328                    if (amount == 0)
329                    {
330                        randomizedPasses[pass].Execute(buffer, prng);
331                        dataStopped = 0;
332                        continue;
333                    }
334
335                    //Write the data.
336                    stream.Write(buffer, dataStopped, amount);
337                    stream.Flush();
338                    toWrite -= amount;
339
340                    //Do a progress callback.
341                    if (callback != null)
342                        callback(amount, totalData, pass + 1);
343                }
344            }
345        }
346    }
347
348    /// <summary>
349    /// Class managing all the erasure methods. This class pairs GUIDs with constructor
350    /// prototypes, and when an instance of the erasure method is required, a new
351    /// instance is created. This is unique to erasure methods since the other managers
352    /// do not have run-time equivalents; they all are compile-time.
353    /// </summary>
354    public class ErasureMethodManager
355    {
356        #region Default Erasure method
357        private class DefaultMethod : ErasureMethod
358        {
359            public DefaultMethod()
360            {
361            }
362
363            public override string Name
364            {
365                get { return S._("(default)"); }
366            }
367
368            public override int Passes
369            {
370                get { return 0; }
371            }
372
373            public override Guid Guid
374            {
375                get { return Guid.Empty; }
376            }
377
378            public override long CalculateEraseDataSize(ICollection<string> paths, long targetSize)
379            {
380                throw new InvalidOperationException(S._("The DefaultMethod class should never " +
381                    "be used and should instead be replaced before execution!"));
382            }
383
384            public override void Erase(Stream strm, long erasureLength, Prng prng,
385                ErasureMethodProgressFunction callback)
386            {
387                throw new InvalidOperationException(S._("The DefaultMethod class should never " +
388                    "be used and should instead be replaced before execution!"));
389            }
390        }
391
392        /// <summary>
393        /// A dummy method placeholder used for representing the default erase
394        /// method. Do not use this variable when trying to call the erase function,
395        /// this is just a placeholder and will throw a InvalidOperationException.
396        /// </summary>
397        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
398        public static readonly ErasureMethod Default = new DefaultMethod();
399        #endregion
400
401        #region Registrar fields
402        /// <summary>
403        /// Retrieves all currently registered erasure methods.
404        /// </summary>
405        /// <returns>A mutable list, with an instance of each method.</returns>
406        public static Dictionary<Guid, ErasureMethod> Items
407        {
408            get
409            {
410                Dictionary<Guid, ErasureMethod> result = new Dictionary<Guid, ErasureMethod>();
411
412                lock (ManagerLibrary.Instance.ErasureMethodManager.methods)
413                {
414                    //Iterate over every item registered.
415                    Dictionary<Guid, MethodConstructorInfo>.Enumerator iter =
416                        ManagerLibrary.Instance.ErasureMethodManager.methods.GetEnumerator();
417                    while (iter.MoveNext())
418                    {
419                        MethodConstructorInfo info = iter.Current.Value;
420                        result.Add(iter.Current.Key,
421                            (ErasureMethod)info.Constructor.Invoke(info.Parameters));
422                    }
423                }
424
425                return result;
426            }
427        }
428
429        /// <summary>
430        /// Retrieves the instance of the erasure method with the given GUID.
431        /// </summary>
432        /// <param name="value">The GUID of the erasure method.</param>
433        /// <returns>The erasure method instance.</returns>
434        public static ErasureMethod GetInstance(Guid value)
435        {
436            lock (ManagerLibrary.Instance.ErasureMethodManager.methods)
437            {
438                if (!ManagerLibrary.Instance.ErasureMethodManager.methods.ContainsKey(value))
439                    throw new ErasureMethodNotFoundException(value);
440                MethodConstructorInfo info = ManagerLibrary.Instance.ErasureMethodManager.methods[value];
441                return (ErasureMethod)info.Constructor.Invoke(info.Parameters);
442            }
443        }
444
445        /// <summary>
446        /// Allows plug-ins to register methods with the main program. Thread-safe.
447        /// </summary>
448        /// <param name="method">The method to register. Only the type is examined.</param>
449        public static void Register(ErasureMethod method)
450        {
451            Register(method, new object[0]);
452        }
453
454        /// <summary>
455        /// Allows plug-ins to register methods with the main program. Thread-safe.
456        /// </summary>
457        /// <param name="method">The method to register. Only the type is examined.</param>
458        /// <param name="parameters">The parameter list to be passed to the constructor.</param>
459        public static void Register(ErasureMethod method, object[] parameters)
460        {
461            //Get the constructor for the class.
462            ConstructorInfo ctor = null;
463            if (parameters == null || parameters.Length == 0)
464                ctor = method.GetType().GetConstructor(Type.EmptyTypes);
465            else
466            {
467                Type[] parameterTypes = new Type[parameters.Length];
468                for (int i = 0; i < parameters.Length; ++i)
469                    parameterTypes[i] = parameters[i].GetType();
470                ctor = method.GetType().GetConstructor(parameterTypes);
471            }
472
473            //Check for a valid constructor.
474            if (ctor == null)
475                throw new ArgumentException(S._("Registered erasure methods must contain " +
476                    "a parameterless constructor that is called whenever clients request " +
477                    "for an instance of the method. If a constructor requires parameters, " +
478                    "specify it in the parameters parameter."));
479
480            //Insert the entry
481            lock (ManagerLibrary.Instance.ErasureMethodManager.methods)
482            {
483                MethodConstructorInfo info = new MethodConstructorInfo();
484                info.Constructor = ctor;
485                info.Parameters = parameters == null || parameters.Length == 0 ? null : parameters;
486                ManagerLibrary.Instance.ErasureMethodManager.methods.Add(method.Guid, info);
487            }
488
489            //Broadcast the event
490            OnMethodRegistered(new ErasureMethodRegistrationEventArgs(method.Guid));
491        }
492
493        /// <summary>
494        /// Unregisters an erasure method from the registrar.
495        /// </summary>
496        /// <param name="value">The erasure method to unregister.</param>
497        public static void Unregister(Guid value)
498        {
499            if (!ManagerLibrary.Instance.ErasureMethodManager.methods.ContainsKey(value))
500                throw new ArgumentException(S._("The GUID of the erasure method to remove " +
501                    "refers to an invalid erasure method."));
502
503            ManagerLibrary.Instance.ErasureMethodManager.methods.Remove(value);
504            OnMethodUnregistered(new ErasureMethodRegistrationEventArgs(value));
505        }
506
507        /// <summary>
508        /// Holds information on how to construct a new instance of an erasure method.
509        /// </summary>
510        private struct MethodConstructorInfo
511        {
512            /// <summary>
513            /// The reference to the constructor method.
514            /// </summary>
515            public ConstructorInfo Constructor;
516
517            /// <summary>
518            /// The parameter list.
519            /// </summary>
520            public object[] Parameters;
521        }
522
523        /// <summary>
524        /// The list of currently registered erasure methods.
525        /// </summary>
526        private Dictionary<Guid, MethodConstructorInfo> methods =
527            new Dictionary<Guid, MethodConstructorInfo>();
528
529        /// <summary>
530        /// Called whenever an erasure method is registered.
531        /// </summary>
532        public static EventHandler<ErasureMethodRegistrationEventArgs>
533            MethodRegistered { get; set; }
534       
535        /// <summary>
536        /// Called whenever an erasure method is unregistered.
537        /// </summary>
538        public static EventHandler<ErasureMethodRegistrationEventArgs>
539            MethodUnregistered { get; set; }
540
541        /// <summary>
542        /// Executes the MethodRegistered event handlers.
543        /// </summary>
544        /// <param name="guid">The GUID of the newly registered erasure method.</param>
545        private static void OnMethodRegistered(ErasureMethodRegistrationEventArgs e)
546        {
547            if (MethodRegistered != null)
548                MethodRegistered(ManagerLibrary.Instance.ErasureMethodManager, e);
549        }
550
551        /// <summary>
552        /// Performs the MethodUnregistered event handlers.
553        /// </summary>
554        /// <param name="guid">The GUID of the unregistered erasure method.</param>
555        private static void OnMethodUnregistered(ErasureMethodRegistrationEventArgs e)
556        {
557            if (MethodUnregistered != null)
558                MethodUnregistered(ManagerLibrary.Instance.ErasureMethodManager, e);
559        }
560        #endregion
561    }
562
563    public class ErasureMethodRegistrationEventArgs : EventArgs
564    {
565        /// <summary>
566        /// Constructor.
567        /// </summary>
568        /// <param name="value">The GUID of the newly registered/unregistered
569        /// erasure method.</param>
570        public ErasureMethodRegistrationEventArgs(Guid value)
571        {
572            Guid = value;
573        }
574
575        /// <summary>
576        /// The GUID of the newly registsered/unregistered erasure method.
577        /// </summary>
578        public Guid Guid { get; private set; }
579    }
580}
Note: See TracBrowser for help on using the repository browser.