source: branches/eraser6/Manager/Method.cs @ 337

Revision 337, 15.5 KB checked in by lowjoel, 7 years ago (diff)

Doc update.

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