source: trunk/eraser/Eraser.Manager/ErasureTarget.cs @ 2020

Revision 2020, 17.4 KB checked in by lowjoel, 5 years ago (diff)

Rearchitect the API for the ErasureTarget? class to be simpler.

  • 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.Text.RegularExpressions;
28using System.Security.Permissions;
29using System.IO;
30using System.Runtime.Serialization;
31
32using Eraser.Util;
33using Eraser.Util.ExtensionMethods;
34
35namespace Eraser.Manager
36{
37    /// <summary>
38    /// Represents a generic target of erasure
39    /// </summary>
40    [Serializable]
41    public abstract class ErasureTarget : ISerializable
42    {
43        #region Serialization code
44        protected ErasureTarget(SerializationInfo info, StreamingContext context)
45        {
46            Guid methodGuid = (Guid)info.GetValue("Method", typeof(Guid));
47            if (methodGuid == Guid.Empty)
48                Method = ErasureMethodRegistrar.Default;
49            else
50                Method = ManagerLibrary.Instance.ErasureMethodRegistrar[methodGuid];
51        }
52
53        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
54        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
55        {
56            info.AddValue("Method", Method.Guid);
57        }
58        #endregion
59
60        /// <summary>
61        /// Constructor.
62        /// </summary>
63        protected ErasureTarget()
64        {
65            Method = ErasureMethodRegistrar.Default;
66        }
67
68        /// <summary>
69        /// The method used for erasing the file.
70        /// </summary>
71        public ErasureMethod Method
72        {
73            get;
74            set;
75        }
76
77        /// <summary>
78        /// Gets the effective erasure method for the current target (i.e., returns
79        /// the correct erasure method for cases where the <see cref="Method"/>
80        /// property is <see cref="ErasureMethodRegistrar.Default"/>
81        /// </summary>
82        /// <returns>The Erasure method which the target should be erased with.
83        /// This function will never return <see cref="ErasureMethodRegistrar.Default"/></returns>
84        public virtual ErasureMethod EffectiveMethod
85        {
86            get
87            {
88                if (Method != ErasureMethodRegistrar.Default)
89                    return Method;
90
91                throw new InvalidOperationException("The effective method of the erasure " +
92                    "target cannot be ErasureMethodRegistrar.Default");
93            }
94        }
95
96        /// <summary>
97        /// The task which owns this target.
98        /// </summary>
99        public Task Task { get; internal set; }
100
101        /// <summary>
102        /// Retrieves the text to display representing this task.
103        /// </summary>
104        public abstract string UIText
105        {
106            get;
107        }
108
109        /// <summary>
110        /// The progress of this target.
111        /// </summary>
112        public ProgressManagerBase Progress
113        {
114            get;
115            internal set;
116        }
117    }
118
119    /// <summary>
120    /// Class representing a tangible object (file/folder) to be erased.
121    /// </summary>
122    [Serializable]
123    public abstract class FileSystemObjectTarget : ErasureTarget
124    {
125        #region Serialization code
126        protected FileSystemObjectTarget(SerializationInfo info, StreamingContext context)
127            : base(info, context)
128        {
129            Path = (string)info.GetValue("Path", typeof(string));
130        }
131
132        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
133        public override void GetObjectData(SerializationInfo info, StreamingContext context)
134        {
135            base.GetObjectData(info, context);
136            info.AddValue("Path", Path);
137        }
138        #endregion
139
140        /// <summary>
141        /// Constructor.
142        /// </summary>
143        protected FileSystemObjectTarget()
144            : base()
145        {
146        }
147
148        /// <summary>
149        /// Retrieves the list of files/folders to erase as a list.
150        /// </summary>
151        /// <param name="totalSize">Returns the total size in bytes of the
152        /// items.</param>
153        /// <returns>A list containing the paths to all the files to be erased.</returns>
154        internal abstract List<string> GetPaths(out long totalSize);
155
156        /// <summary>
157        /// Adds ADSes of the given file to the list.
158        /// </summary>
159        /// <param name="list">The list to add the ADS paths to.</param>
160        /// <param name="file">The file to look for ADSes</param>
161        protected void GetPathADSes(ICollection<string> list, out long totalSize, string file)
162        {
163            totalSize = 0;
164
165            try
166            {
167                //Get the ADS names
168                IList<string> adses = new FileInfo(file).GetADSes();
169
170                //Then prepend the path.
171                foreach (string adsName in adses)
172                {
173                    string adsPath = file + ':' + adsName;
174                    list.Add(adsPath);
175                    StreamInfo info = new StreamInfo(adsPath);
176                    totalSize += info.Length;
177                }
178            }
179            catch (FileNotFoundException)
180            {
181            }
182            catch (SharingViolationException)
183            {
184                //The system cannot open the file, try to force the file handle to close.
185                if (!ManagerLibrary.Settings.ForceUnlockLockedFiles)
186                    throw;
187
188                foreach (OpenHandle handle in OpenHandle.Items)
189                    if (handle.Path == file && handle.Close())
190                    {
191                        GetPathADSes(list, out totalSize, file);
192                        return;
193                    }
194            }
195            catch (UnauthorizedAccessException e)
196            {
197                //The system cannot read the file, assume no ADSes for lack of
198                //more information.
199                Logger.Log(e.Message, LogLevel.Error);
200            }
201        }
202
203        /// <summary>
204        /// The path to the file or folder referred to by this object.
205        /// </summary>
206        public string Path { get; set; }
207
208        public sealed override ErasureMethod EffectiveMethod
209        {
210            get
211            {
212                if (Method == ErasureMethodRegistrar.Default)
213                    return base.EffectiveMethod;
214
215                return ManagerLibrary.Instance.ErasureMethodRegistrar[
216                    ManagerLibrary.Settings.DefaultFileErasureMethod];
217            }
218        }
219
220        public override string UIText
221        {
222            get
223            {
224                string fileName = System.IO.Path.GetFileName(Path);
225                string directoryName = System.IO.Path.GetDirectoryName(Path);
226                return string.IsNullOrEmpty(fileName) ?
227                        (string.IsNullOrEmpty(directoryName) ? Path : directoryName)
228                    : fileName;
229            }
230        }
231    }
232
233    /// <summary>
234    /// Class representing a unused space erase.
235    /// </summary>
236    [Serializable]
237    public class UnusedSpaceTarget : ErasureTarget
238    {
239        #region Serialization code
240        protected UnusedSpaceTarget(SerializationInfo info, StreamingContext context)
241            : base(info, context)
242        {
243            Drive = (string)info.GetValue("Drive", typeof(string));
244            EraseClusterTips = (bool)info.GetValue("EraseClusterTips", typeof(bool));
245        }
246
247        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
248        public override void GetObjectData(SerializationInfo info, StreamingContext context)
249        {
250            base.GetObjectData(info, context);
251            info.AddValue("Drive", Drive);
252            info.AddValue("EraseClusterTips", EraseClusterTips);
253        }
254        #endregion
255
256        /// <summary>
257        /// Constructor.
258        /// </summary>
259        public UnusedSpaceTarget()
260            : base()
261        {
262        }
263
264        public sealed override ErasureMethod EffectiveMethod
265        {
266            get
267            {
268                if (Method == ErasureMethodRegistrar.Default)
269                    return base.EffectiveMethod;
270
271                return ManagerLibrary.Instance.ErasureMethodRegistrar[
272                    ManagerLibrary.Settings.DefaultUnusedSpaceErasureMethod];
273            }
274        }
275
276        public override string UIText
277        {
278            get { return S._("Unused disk space ({0})", Drive); }
279        }
280
281        /// <summary>
282        /// The drive to erase
283        /// </summary>
284        public string Drive { get; set; }
285
286        /// <summary>
287        /// Whether cluster tips should be erased.
288        /// </summary>
289        public bool EraseClusterTips { get; set; }
290    }
291
292    /// <summary>
293    /// Class representing a file to be erased.
294    /// </summary>
295    [Serializable]
296    public class FileTarget : FileSystemObjectTarget
297    {
298        #region Serialization code
299        protected FileTarget(SerializationInfo info, StreamingContext context)
300            : base(info, context)
301        {
302        }
303        #endregion
304
305        /// <summary>
306        /// Constructor.
307        /// </summary>
308        public FileTarget()
309        {
310        }
311
312        internal override List<string> GetPaths(out long totalSize)
313        {
314            totalSize = 0;
315            List<string> result = new List<string>();
316            FileInfo fileInfo = new FileInfo(Path);
317
318            if (fileInfo.Exists)
319            {
320                GetPathADSes(result, out totalSize, Path);
321                totalSize += fileInfo.Length;
322            }
323
324            result.Add(Path);
325            return result;
326        }
327    }
328
329    /// <summary>
330    /// Represents a folder and its files which are to be erased.
331    /// </summary>
332    [Serializable]
333    public class FolderTarget : FileSystemObjectTarget
334    {
335        #region Serialization code
336        protected FolderTarget(SerializationInfo info, StreamingContext context)
337            : base(info, context)
338        {
339            IncludeMask = (string)info.GetValue("IncludeMask", typeof(string));
340            ExcludeMask = (string)info.GetValue("ExcludeMask", typeof(string));
341            DeleteIfEmpty = (bool)info.GetValue("DeleteIfEmpty", typeof(bool));
342        }
343
344        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
345        public override void GetObjectData(SerializationInfo info, StreamingContext context)
346        {
347            base.GetObjectData(info, context);
348            info.AddValue("IncludeMask", IncludeMask);
349            info.AddValue("ExcludeMask", ExcludeMask);
350            info.AddValue("DeleteIfEmpty", DeleteIfEmpty);
351        }
352        #endregion
353
354        /// <summary>
355        /// Constructor.
356        /// </summary>
357        public FolderTarget()
358        {
359            IncludeMask = string.Empty;
360            ExcludeMask = string.Empty;
361            DeleteIfEmpty = true;
362        }
363
364        internal override List<string> GetPaths(out long totalSize)
365        {
366            //Get a list to hold all the resulting paths.
367            List<string> result = new List<string>();
368
369            //Open the root of the search, including every file matching the pattern
370            DirectoryInfo dir = new DirectoryInfo(Path);
371
372            //List recursively all the files which match the include pattern.
373            FileInfo[] files = GetFiles(dir);
374
375            //Then exclude each file and finalize the list and total file size
376            totalSize = 0;
377            if (ExcludeMask.Length != 0)
378            {
379                string regex = Regex.Escape(ExcludeMask).Replace("\\*", ".*").
380                    Replace("\\?", ".");
381                Regex excludePattern = new Regex(regex, RegexOptions.IgnoreCase);
382                foreach (FileInfo file in files)
383                    if (file.Exists &&
384                        (file.Attributes & FileAttributes.ReparsePoint) == 0 &&
385                        excludePattern.Matches(file.FullName).Count == 0)
386                    {
387                        totalSize += file.Length;
388                        GetPathADSes(result, out totalSize, file.FullName);
389                        result.Add(file.FullName);
390                    }
391            }
392            else
393                foreach (FileInfo file in files)
394                {
395                    if (!file.Exists || (file.Attributes & FileAttributes.ReparsePoint) != 0)
396                        continue;
397
398                    //Get the size of the file and its ADSes
399                    totalSize += file.Length;
400                    long adsesSize = 0;
401                    GetPathADSes(result, out adsesSize, file.FullName);
402                    totalSize += adsesSize;
403
404                    //Append this file to the list of files to erase.
405                    result.Add(file.FullName);
406                }
407
408            //Return the filtered list.
409            return result;
410        }
411
412        /// <summary>
413        /// Gets all files in the provided directory.
414        /// </summary>
415        /// <param name="info">The directory to look files in.</param>
416        /// <returns>A list of files found in the directory matching the IncludeMask
417        /// property.</returns>
418        private FileInfo[] GetFiles(DirectoryInfo info)
419        {
420            List<FileInfo> result = new List<FileInfo>();
421            if (info.Exists)
422            {
423                try
424                {
425                    foreach (DirectoryInfo dir in info.GetDirectories())
426                        result.AddRange(GetFiles(dir));
427
428                    if (IncludeMask.Length == 0)
429                        result.AddRange(info.GetFiles());
430                    else
431                        result.AddRange(info.GetFiles(IncludeMask, SearchOption.TopDirectoryOnly));
432                }
433                catch (UnauthorizedAccessException e)
434                {
435                    Logger.Log(S._("Could not erase files and subfolders in {0} because {1}",
436                        info.FullName, e.Message), LogLevel.Error);
437                }
438            }
439
440            return result.ToArray();
441        }
442
443        /// <summary>
444        /// A wildcard expression stating the condition for the set of files to include.
445        /// The include mask is applied before the exclude mask is applied. If this value
446        /// is empty, all files and folders within the folder specified is included.
447        /// </summary>
448        public string IncludeMask { get; set; }
449
450        /// <summary>
451        /// A wildcard expression stating the condition for removing files from the set
452        /// of included files. If this value is omitted, all files and folders extracted
453        /// by the inclusion mask is erased.
454        /// </summary>
455        public string ExcludeMask { get; set; }
456
457        /// <summary>
458        /// Determines if Eraser should delete the folder after the erase process.
459        /// </summary>
460        public bool DeleteIfEmpty { get; set; }
461    }
462
463    [Serializable]
464    public class RecycleBinTarget : FileSystemObjectTarget
465    {
466        #region Serialization code
467        protected RecycleBinTarget(SerializationInfo info, StreamingContext context)
468            : base(info, context)
469        {
470        }
471        #endregion
472
473        public RecycleBinTarget()
474        {
475        }
476
477        internal override List<string> GetPaths(out long totalSize)
478        {
479            totalSize = 0;
480            List<string> result = new List<string>();
481            string[] rootDirectory = new string[] {
482                    "$RECYCLE.BIN",
483                    "RECYCLER"
484                };
485
486            foreach (DriveInfo drive in DriveInfo.GetDrives())
487            {
488                foreach (string rootDir in rootDirectory)
489                {
490                    DirectoryInfo dir = new DirectoryInfo(
491                        System.IO.Path.Combine(
492                            System.IO.Path.Combine(drive.Name, rootDir),
493                            System.Security.Principal.WindowsIdentity.GetCurrent().
494                                User.ToString()));
495                    if (!dir.Exists)
496                        continue;
497
498                    GetRecyclerFiles(dir, result, ref totalSize);
499                }
500            }
501
502            return result;
503        }
504
505        /// <summary>
506        /// Retrieves all files within this folder, without exclusions.
507        /// </summary>
508        /// <param name="info">The DirectoryInfo object representing the folder to traverse.</param>
509        /// <param name="paths">The list of files to store path information in.</param>
510        /// <param name="totalSize">Receives the total size of the files.</param>
511        private void GetRecyclerFiles(DirectoryInfo info, List<string> paths,
512            ref long totalSize)
513        {
514            try
515            {
516                foreach (FileInfo fileInfo in info.GetFiles())
517                {
518                    if (!fileInfo.Exists || (fileInfo.Attributes & FileAttributes.ReparsePoint) != 0)
519                        continue;
520
521                    long adsSize = 0;
522                    GetPathADSes(paths, out adsSize, fileInfo.FullName);
523                    totalSize += adsSize;
524                    totalSize += fileInfo.Length;
525                    paths.Add(fileInfo.FullName);
526                }
527
528                foreach (DirectoryInfo directoryInfo in info.GetDirectories())
529                    if ((directoryInfo.Attributes & FileAttributes.ReparsePoint) == 0)
530                        GetRecyclerFiles(directoryInfo, paths, ref totalSize);
531            }
532            catch (UnauthorizedAccessException e)
533            {
534                Logger.Log(e.Message, LogLevel.Error);
535            }
536        }
537
538        /// <summary>
539        /// Retrieves the text to display representing this task.
540        /// </summary>
541        public override string UIText
542        {
543            get
544            {
545                return S._("Recycle Bin");
546            }
547        }
548    }
549
550    /// <summary>
551    /// Maintains a collection of erasure targets.
552    /// </summary>
553    [Serializable]
554    public class ErasureTargetsCollection : IList<ErasureTarget>, ISerializable
555    {
556        #region Constructors
557        internal ErasureTargetsCollection(Task owner)
558        {
559            this.list = new List<ErasureTarget>();
560            this.owner = owner;
561        }
562
563        internal ErasureTargetsCollection(Task owner, int capacity)
564            : this(owner)
565        {
566            list.Capacity = capacity;
567        }
568
569        internal ErasureTargetsCollection(Task owner, IEnumerable<ErasureTarget> targets)
570            : this(owner)
571        {
572            list.AddRange(targets);
573        }
574        #endregion
575
576        #region Serialization Code
577        protected ErasureTargetsCollection(SerializationInfo info, StreamingContext context)
578        {
579            list = (List<ErasureTarget>)info.GetValue("list", typeof(List<ErasureTarget>));
580        }
581
582        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
583        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
584        {
585            info.AddValue("list", list);
586        }
587        #endregion
588
589        #region IEnumerable<ErasureTarget> Members
590        public IEnumerator<ErasureTarget> GetEnumerator()
591        {
592            return list.GetEnumerator();
593        }
594        #endregion
595
596        #region IEnumerable Members
597        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
598        {
599            return GetEnumerator();
600        }
601        #endregion
602
603        #region ICollection<ErasureTarget> Members
604        public void Add(ErasureTarget item)
605        {
606            item.Task = owner;
607            list.Add(item);
608        }
609
610        public void Clear()
611        {
612            foreach (ErasureTarget item in list)
613                Remove(item);
614        }
615
616        public bool Contains(ErasureTarget item)
617        {
618            return list.Contains(item);
619        }
620
621        public void CopyTo(ErasureTarget[] array, int arrayIndex)
622        {
623            list.CopyTo(array, arrayIndex);
624        }
625
626        public int Count
627        {
628            get
629            {
630                return list.Count;
631            }
632        }
633
634        public bool IsReadOnly
635        {
636            get
637            {
638                return false;
639            }
640        }
641
642        public bool Remove(ErasureTarget item)
643        {
644            int index = list.IndexOf(item);
645            if (index < 0)
646                return false;
647
648            RemoveAt(index);
649            return true;
650        }
651        #endregion
652
653        #region IList<ErasureTarget> Members
654        public int IndexOf(ErasureTarget item)
655        {
656            return list.IndexOf(item);
657        }
658
659        public void Insert(int index, ErasureTarget item)
660        {
661            item.Task = owner;
662            list.Insert(index, item);
663        }
664
665        public void RemoveAt(int index)
666        {
667            list.RemoveAt(index);
668        }
669
670        public ErasureTarget this[int index]
671        {
672            get
673            {
674                return list[index];
675            }
676            set
677            {
678                list[index] = value;
679            }
680        }
681        #endregion
682
683        /// <summary>
684        /// The owner of this list of targets.
685        /// </summary>
686        public Task Owner
687        {
688            get
689            {
690                return owner;
691            }
692            internal set
693            {
694                owner = value;
695                foreach (ErasureTarget target in list)
696                    target.Task = owner;
697            }
698        }
699
700        /// <summary>
701        /// The owner of this list of targets. All targets added to this list
702        /// will have the owner set to this object.
703        /// </summary>
704        private Task owner;
705
706        /// <summary>
707        /// The list bring the data store behind this object.
708        /// </summary>
709        private List<ErasureTarget> list;
710    }
711}
Note: See TracBrowser for help on using the repository browser.