source: trunk/eraser/Eraser.Manager/ErasureMethod.cs @ 2035

Revision 2035, 13.4 KB checked in by lowjoel, 4 years ago (diff)

Name the source file after the name of the class within the file.

  • 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;
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 : IRegisterable
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 = ManagerLibrary.Instance.PrngRegistrar[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 ErasureMethodRegistrar : Registrar<ErasureMethod>
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("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("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
402    public class ErasureMethodRegistrationEventArgs : EventArgs
403    {
404        /// <summary>
405        /// Constructor.
406        /// </summary>
407        /// <param name="value">The GUID of the newly registered/unregistered
408        /// erasure method.</param>
409        public ErasureMethodRegistrationEventArgs(Guid value)
410        {
411            Guid = value;
412        }
413
414        /// <summary>
415        /// The GUID of the newly registsered/unregistered erasure method.
416        /// </summary>
417        public Guid Guid { get; private set; }
418    }
419}
Note: See TracBrowser for help on using the repository browser.