source: branches/eraser6/6.0/Eraser.Manager/Task.cs @ 2663

Revision 2663, 29.9 KB checked in by lowjoel, 2 years ago (diff)

Backport from Eraser trunk: Handle situations where the list of files/directories we obtained earlier had files or directories removed from disk by simple deletion. Also, handle situations when files are locked by other processes and the file's ADSes cannot be emumerated.

  • 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.Text.RegularExpressions;
27using System.Runtime.Serialization;
28using System.ComponentModel;
29using Eraser.Util;
30using System.Security.Permissions;
31using System.Threading;
32
33namespace Eraser.Manager
34{
35    /// <summary>
36    /// Deals with an erase task.
37    /// </summary>
38    [Serializable]
39    public class Task : ISerializable
40    {
41        #region Serialization code
42        protected Task(SerializationInfo info, StreamingContext context)
43        {
44            Name = (string)info.GetValue("Name", typeof(string));
45            Executor = context.Context as Executor;
46            Targets = (ErasureTargetsCollection)info.GetValue("Targets", typeof(ErasureTargetsCollection));
47            Targets.Owner = this;
48            Log = (Logger)info.GetValue("Log", typeof(Logger));
49            Canceled = false;
50
51            Schedule schedule = (Schedule)info.GetValue("Schedule", typeof(Schedule));
52            if (schedule.GetType() == Schedule.RunManually.GetType())
53                Schedule = Schedule.RunManually;
54            else if (schedule.GetType() == Schedule.RunNow.GetType())
55                Schedule = Schedule.RunNow;
56            else if (schedule.GetType() == Schedule.RunOnRestart.GetType())
57                Schedule = Schedule.RunOnRestart;
58            else if (schedule is RecurringSchedule)
59                Schedule = schedule;
60            else
61                throw new InvalidDataException(S._("An invalid type was found when loading " +
62                    "the task schedule"));
63        }
64
65        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
66        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
67        {
68            info.AddValue("Name", Name);
69            info.AddValue("Schedule", Schedule);
70            info.AddValue("Targets", Targets);
71            info.AddValue("Log", Log);
72        }
73        #endregion
74
75        /// <summary>
76        /// Constructor.
77        /// </summary>
78        public Task()
79        {
80            Name = string.Empty;
81            Targets = new ErasureTargetsCollection(this);
82            Schedule = Schedule.RunNow;
83            Canceled = false;
84            Log = new Logger();
85        }
86
87        /// <summary>
88        /// Cancels the task from running, or, if the task is queued for running,
89        /// removes the task from the queue.
90        /// </summary>
91        public void Cancel()
92        {
93            Executor.UnqueueTask(this);
94            Canceled = true;
95        }
96
97        /// <summary>
98        /// The Executor object which is managing this task.
99        /// </summary>
100        public Executor Executor { get; internal set; }
101
102        /// <summary>
103        /// The name for this task. This is just an opaque value for the user to
104        /// recognize the task.
105        /// </summary>
106        public string Name { get; set; }
107
108        /// <summary>
109        /// The name of the task, used for display in UI elements.
110        /// </summary>
111        public string UIText
112        {
113            get
114            {
115                //Simple case, the task name was given by the user.
116                if (Name.Length != 0)
117                    return Name;
118
119                string result = string.Empty;
120                if (Targets.Count < 3)
121                    //Simpler case, small set of data.
122                    foreach (ErasureTarget tgt in Targets)
123                        result += tgt.UIText + ", ";
124                else
125                    //Ok, we've quite a few entries, get the first, the mid and the end.
126                    for (int i = 0; i < Targets.Count; i += Targets.Count / 3)
127                        result += Targets[i].UIText + ", ";
128                return result.Substring(0, result.Length - 2);
129            }
130        }
131
132        /// <summary>
133        /// Gets the status of the task - whether it is being executed.
134        /// </summary>
135        public bool Executing { get; private set; }
136
137        /// <summary>
138        /// Gets whether this task is currently queued to run. This is true only
139        /// if the queue it is in is an explicit request, i.e will run when the
140        /// executor is idle.
141        /// </summary>
142        public bool Queued
143        {
144            get
145            {
146                return Executor.IsTaskQueued(this);
147            }
148        }
149
150        /// <summary>
151        /// Gets whether the task has been cancelled from execution.
152        /// </summary>
153        public bool Canceled
154        {
155            get
156            {
157                return canceled;
158            }
159
160            internal set
161            {
162                canceled = value;
163            }
164        }
165
166        /// <summary>
167        /// The set of data to erase when this task is executed.
168        /// </summary>
169        public ErasureTargetsCollection Targets { get; private set; }
170
171        /// <summary>
172        /// The schedule for running the task.
173        /// </summary>
174        public Schedule Schedule
175        {
176            get
177            {
178                return schedule;
179            }
180            set
181            {
182                if (value.Owner != null)
183                    throw new ArgumentException(S._("The schedule provided can only " +
184                        "belong to one task at a time"));
185
186                if (schedule is RecurringSchedule)
187                    ((RecurringSchedule)schedule).Owner = null;
188                schedule = value;
189                if (schedule is RecurringSchedule)
190                    ((RecurringSchedule)schedule).Owner = this;
191                OnTaskEdited();
192            }
193        }
194
195        /// <summary>
196        /// The log entries which this task has accumulated.
197        /// </summary>
198        public Logger Log { get; private set; }
199
200        private Schedule schedule;
201
202        /// <see cref="Canceled"/>
203        private volatile bool canceled;
204
205        #region Events
206        /// <summary>
207        /// The task has been edited.
208        /// </summary>
209        public EventHandler<TaskEventArgs> TaskEdited { get; set; }
210
211        /// <summary>
212        /// The start of the execution of a task.
213        /// </summary>
214        public EventHandler<TaskEventArgs> TaskStarted { get; set; }
215
216        /// <summary>
217        /// The event object holding all event handlers.
218        /// </summary>
219        public EventHandler<TaskProgressEventArgs> ProgressChanged { get; set; }
220
221        /// <summary>
222        /// The completion of the execution of a task.
223        /// </summary>
224        public EventHandler<TaskEventArgs> TaskFinished { get; set; }
225
226        /// <summary>
227        /// Broadcasts the task edited event.
228        /// </summary>
229        internal void OnTaskEdited()
230        {
231            if (TaskEdited != null)
232                TaskEdited(this, new TaskEventArgs(this));
233        }
234
235        /// <summary>
236        /// Broadcasts the task execution start event.
237        /// </summary>
238        /// <param name="e"></param>
239        internal void OnTaskStarted(TaskEventArgs e)
240        {
241            if (TaskStarted != null)
242                TaskStarted(this, e);
243            Executing = true;
244        }
245
246        /// <summary>
247        /// Broadcasts a ProgressChanged event.
248        /// </summary>
249        /// <param name="e">The new progress value.</param>
250        internal void OnProgressChanged(TaskProgressEventArgs e)
251        {
252            if (ProgressChanged != null)
253                ProgressChanged(this, e);
254        }
255
256        /// <summary>
257        /// Broadcasts the task execution completion event.
258        /// </summary>
259        /// <param name="e"></param>
260        internal void OnTaskFinished(TaskEventArgs e)
261        {
262            Executing = false;
263            if (TaskFinished != null)
264                TaskFinished(this, e);
265        }
266        #endregion
267    }
268
269    /// <summary>
270    /// Represents a generic target of erasure
271    /// </summary>
272    [Serializable]
273    public abstract class ErasureTarget : ISerializable
274    {
275        #region Serialization code
276        protected ErasureTarget(SerializationInfo info, StreamingContext context)
277        {
278            Guid methodGuid = (Guid)info.GetValue("Method", typeof(Guid));
279            if (methodGuid == Guid.Empty)
280            {
281                method = ErasureMethodManager.Default;
282            }
283            else
284            {
285                method = ErasureMethodManager.GetInstance(methodGuid);
286                MethodDefined = true;
287            }
288        }
289
290        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
291        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
292        {
293            info.AddValue("Method", method.Guid);
294        }
295        #endregion
296
297        /// <summary>
298        /// Constructor.
299        /// </summary>
300        protected ErasureTarget()
301        {
302        }
303
304        /// <summary>
305        /// The method used for erasing the file. If the variable is equal to
306        /// ErasureMethodManager.Default then the default is queried for the
307        /// task type. Check the <see cref="MethodDefined"/> property to see if
308        /// this variable was set on deliberately or if the result of the get
309        /// call is from the inferred default.
310        /// </summary>
311        public virtual ErasureMethod Method
312        {
313            get
314            {
315                return method;
316            }
317            set
318            {
319                method = value;
320                MethodDefined = method != ErasureMethodManager.Default;
321            }
322        }
323
324        /// <summary>
325        /// Checks whether a method has been selected for this target. This is
326        /// because the Method property will return non-default erasure methods
327        /// only.
328        /// </summary>
329        public bool MethodDefined { get; private set; }
330
331        /// <summary>
332        /// The task which owns this target.
333        /// </summary>
334        public Task Task { get; internal set; }
335
336        /// <summary>
337        /// Retrieves the text to display representing this task.
338        /// </summary>
339        public abstract string UIText
340        {
341            get;
342        }
343
344        /// <summary>
345        /// Retrieves the amount of data that needs to be written in order to
346        /// complete the erasure.
347        /// </summary>
348        public abstract long TotalData
349        {
350            get;
351        }
352
353        /// <summary>
354        /// Erasure method to use for the target.
355        /// </summary>
356        private ErasureMethod method;
357    }
358
359    /// <summary>
360    /// Class representing a tangible object (file/folder) to be erased.
361    /// </summary>
362    [Serializable]
363    public abstract class FileSystemObjectTarget : ErasureTarget
364    {
365        #region Serialization code
366        protected FileSystemObjectTarget(SerializationInfo info, StreamingContext context)
367            : base(info, context)
368        {
369            Path = (string)info.GetValue("Path", typeof(string));
370        }
371
372        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
373        public override void GetObjectData(SerializationInfo info, StreamingContext context)
374        {
375            base.GetObjectData(info, context);
376            info.AddValue("Path", Path);
377        }
378        #endregion
379
380        /// <summary>
381        /// Constructor.
382        /// </summary>
383        protected FileSystemObjectTarget()
384            : base()
385        {
386            Method = ErasureMethodManager.Default;
387        }
388
389        /// <summary>
390        /// Retrieves the list of files/folders to erase as a list.
391        /// </summary>
392        /// <param name="totalSize">Returns the total size in bytes of the
393        /// items.</param>
394        /// <returns>A list containing the paths to all the files to be erased.</returns>
395        internal abstract List<string> GetPaths(out long totalSize);
396
397        /// <summary>
398        /// Adds ADSes of the given file to the list.
399        /// </summary>
400        /// <param name="list">The list to add the ADS paths to.</param>
401        /// <param name="file">The file to look for ADSes</param>
402        protected void GetPathADSes(ICollection<string> list, out long totalSize, string file)
403        {
404            int attempts = 0;
405            GetPathADSes(list, out totalSize, file, ref attempts);
406        }
407
408        private void GetPathADSes(ICollection<string> list, out long totalSize, string file, ref int attempts)
409        {
410            totalSize = 0;
411
412            try
413            {
414                //Get the ADS names
415                ICollection<string> adses = Util.File.GetADSes(new FileInfo(file));
416
417                //Then prepend the path.
418                foreach (string adsName in adses)
419                {
420                    string adsPath = file + ':' + adsName;
421                    list.Add(adsPath);
422                    StreamInfo info = new StreamInfo(adsPath);
423                    totalSize += info.Length;
424                }
425            }
426            catch (FileNotFoundException)
427            {
428            }
429            catch (FileLoadException)
430            {
431                //The system cannot open the file, try to force the file handle to close.
432                if (!ManagerLibrary.Settings.ForceUnlockLockedFiles)
433                    throw;
434
435                bool handlesClosed = false;
436                foreach (OpenHandle handle in OpenHandle.Items)
437                    if (handle.Path == file && handle.Close())
438                        handlesClosed = true;
439
440                if (handlesClosed)
441                {
442                    //Retry closing the file 10 times. If we can't do that, we should abort
443                    //since we may not be able to get the process information of processes
444                    //running with higher privileges.
445                    if (++attempts <= 10)
446                    {
447                        GetPathADSes(list, out totalSize, file, ref attempts);
448                        return;
449                    }
450                    else
451                        throw;
452                }
453            }
454            catch (UnauthorizedAccessException e)
455            {
456                //The system cannot read the file, assume no ADSes for lack of
457                //more information.
458                Task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error));
459            }
460        }
461
462        /// <summary>
463        /// The path to the file or folder referred to by this object.
464        /// </summary>
465        public string Path { get; set; }
466
467        public sealed override ErasureMethod Method
468        {
469            get
470            {
471                if (base.MethodDefined)
472                    return base.Method;
473                return ErasureMethodManager.GetInstance(
474                    ManagerLibrary.Settings.DefaultFileErasureMethod);
475            }
476            set
477            {
478                base.Method = value;
479            }
480        }
481
482        public override string UIText
483        {
484            get { return Path; }
485        }
486
487        public override long TotalData
488        {
489            get
490            {
491                long totalSize = 0;
492                List<string> paths = GetPaths(out totalSize);
493                return Method.CalculateEraseDataSize(paths, totalSize);
494            }
495        }
496    }
497
498    /// <summary>
499    /// Class representing a unused space erase.
500    /// </summary>
501    [Serializable]
502    public class UnusedSpaceTarget : ErasureTarget
503    {
504        #region Serialization code
505        protected UnusedSpaceTarget(SerializationInfo info, StreamingContext context)
506            : base(info, context)
507        {
508            Drive = (string)info.GetValue("Drive", typeof(string));
509            EraseClusterTips = (bool)info.GetValue("EraseClusterTips", typeof(bool));
510        }
511
512        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
513        public override void GetObjectData(SerializationInfo info, StreamingContext context)
514        {
515            base.GetObjectData(info, context);
516            info.AddValue("Drive", Drive);
517            info.AddValue("EraseClusterTips", EraseClusterTips);
518        }
519        #endregion
520
521        /// <summary>
522        /// Constructor.
523        /// </summary>
524        public UnusedSpaceTarget()
525            : base()
526        {
527            Method = ErasureMethodManager.Default;
528        }
529
530        public override sealed ErasureMethod Method
531        {
532            get
533            {
534                if (base.MethodDefined)
535                    return base.Method;
536                return ErasureMethodManager.GetInstance(
537                    ManagerLibrary.Settings.DefaultUnusedSpaceErasureMethod);
538            }
539            set
540            {
541                base.Method = value;
542            }
543        }
544
545        public override string UIText
546        {
547            get { return S._("Unused disk space ({0})", Drive); }
548        }
549
550        public override long TotalData
551        {
552            get
553            {
554                VolumeInfo info = VolumeInfo.FromMountpoint(Drive);
555                return Method.CalculateEraseDataSize(null, info.AvailableFreeSpace);
556            }
557        }
558
559        /// <summary>
560        /// The drive to erase
561        /// </summary>
562        public string Drive { get; set; }
563
564        /// <summary>
565        /// Whether cluster tips should be erased.
566        /// </summary>
567        public bool EraseClusterTips { get; set; }
568    }
569
570    /// <summary>
571    /// Class representing a file to be erased.
572    /// </summary>
573    [Serializable]
574    public class FileTarget : FileSystemObjectTarget
575    {
576        #region Serialization code
577        protected FileTarget(SerializationInfo info, StreamingContext context)
578            : base(info, context)
579        {
580        }
581        #endregion
582
583        /// <summary>
584        /// Constructor.
585        /// </summary>
586        public FileTarget()
587        {
588        }
589
590        internal override List<string> GetPaths(out long totalSize)
591        {
592            totalSize = 0;
593            List<string> result = new List<string>();
594            FileInfo fileInfo = new FileInfo(Path);
595
596            try
597            {
598                GetPathADSes(result, out totalSize, Path);
599                totalSize += fileInfo.Length;
600                result.Add(Path);
601            }
602            catch (FileLoadException)
603            {
604                Task.Log.LastSessionEntries.Add(new LogEntry(
605                    S._("Could not list the Alternate Data Streams for file {0} " +
606                        "because the file is being used by another process. The file " +
607                        "will not be erased.", fileInfo.FullName),
608                    LogLevel.Error));
609            }
610            catch (FileNotFoundException)
611            {
612            }
613            catch (DirectoryNotFoundException)
614            {
615            }
616
617            return result;
618        }
619    }
620
621    /// <summary>
622    /// Represents a folder and its files which are to be erased.
623    /// </summary>
624    [Serializable]
625    public class FolderTarget : FileSystemObjectTarget
626    {
627        #region Serialization code
628        protected FolderTarget(SerializationInfo info, StreamingContext context)
629            : base(info, context)
630        {
631            IncludeMask = (string)info.GetValue("IncludeMask", typeof(string));
632            ExcludeMask = (string)info.GetValue("ExcludeMask", typeof(string));
633            DeleteIfEmpty = (bool)info.GetValue("DeleteIfEmpty", typeof(bool));
634        }
635
636        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
637        public override void GetObjectData(SerializationInfo info, StreamingContext context)
638        {
639            base.GetObjectData(info, context);
640            info.AddValue("IncludeMask", IncludeMask);
641            info.AddValue("ExcludeMask", ExcludeMask);
642            info.AddValue("DeleteIfEmpty", DeleteIfEmpty);
643        }
644        #endregion
645
646        /// <summary>
647        /// Constructor.
648        /// </summary>
649        public FolderTarget()
650        {
651            IncludeMask = string.Empty;
652            ExcludeMask = string.Empty;
653            DeleteIfEmpty = true;
654        }
655
656        internal override List<string> GetPaths(out long totalSize)
657        {
658            //Get a list to hold all the resulting paths.
659            List<string> result = new List<string>();
660
661            //Open the root of the search, including every file matching the pattern
662            DirectoryInfo dir = new DirectoryInfo(Path);
663
664            //List recursively all the files which match the include pattern.
665            FileInfo[] files = GetFiles(dir);
666
667            //Then exclude each file and finalize the list and total file size
668            totalSize = 0;
669            if (ExcludeMask.Length != 0)
670            {
671                string regex = Regex.Escape(ExcludeMask).Replace("\\*", ".*").
672                    Replace("\\?", ".");
673                Regex excludePattern = new Regex(regex, RegexOptions.IgnoreCase);
674                foreach (FileInfo file in files)
675                    if (file.Exists &&
676                        (file.Attributes & FileAttributes.ReparsePoint) == 0 &&
677                        excludePattern.Matches(file.FullName).Count == 0)
678                    {
679                        try
680                        {
681                            totalSize += file.Length;
682                            long adsesSize = 0;
683                            GetPathADSes(result, out adsesSize, file.FullName);
684                            totalSize += adsesSize;
685                            result.Add(file.FullName);
686                        }
687                        catch (FileNotFoundException)
688                        {
689                            Task.Log.LastSessionEntries.Add(new LogEntry(
690                                S._("The file {0} was not erased because it was deleted " +
691                                    "before it could be erased.", file.FullName),
692                                LogLevel.Information));
693                        }
694                        catch (DirectoryNotFoundException)
695                        {
696                            Task.Log.LastSessionEntries.Add(new LogEntry(
697                                S._("The file {0} was not erased because the containing " +
698                                    "directory was deleted before it could be erased.",
699                                    file.FullName),
700                                LogLevel.Information));
701                        }
702                        catch (FileLoadException)
703                        {
704                            Task.Log.LastSessionEntries.Add(new LogEntry(
705                                S._("Could not list the Alternate Data Streams for file {0} " +
706                                    "because the file is being used by another process. The " +
707                                    "file will not be erased.", file.FullName),
708                                LogLevel.Error));
709                        }
710                    }
711            }
712            else
713                foreach (FileInfo file in files)
714                {
715                    if (!file.Exists || (file.Attributes & FileAttributes.ReparsePoint) != 0)
716                        continue;
717
718                    try
719                    {
720                        //Get the size of the file and its ADSes
721                        totalSize += file.Length;
722                        long adsesSize = 0;
723                        GetPathADSes(result, out adsesSize, file.FullName);
724                        totalSize += adsesSize;
725
726                        //Append this file to the list of files to erase.
727                        result.Add(file.FullName);
728                    }
729                    catch (FileNotFoundException)
730                    {
731                        Task.Log.LastSessionEntries.Add(new LogEntry(
732                            S._("The file {0} was not erased because it was deleted " +
733                                "before it could be erased.", file.FullName),
734                            LogLevel.Information));
735                    }
736                    catch (DirectoryNotFoundException)
737                    {
738                        Task.Log.LastSessionEntries.Add(new LogEntry(
739                            S._("The file {0} was not erased because the containing " +
740                                "directory was deleted before it could be erased.",
741                                file.FullName),
742                            LogLevel.Information));
743                    }
744                    catch (FileLoadException)
745                    {
746                        Task.Log.LastSessionEntries.Add(new LogEntry(
747                            S._("Could not list the Alternate Data Streams for file {0} " +
748                                "because the file is being used by another process. The " +
749                                "file will not be erased.", file.FullName),
750                            LogLevel.Error));
751                    }
752                }
753
754            //Return the filtered list.
755            return result;
756        }
757
758        /// <summary>
759        /// Gets all files in the provided directory.
760        /// </summary>
761        /// <param name="info">The directory to look files in.</param>
762        /// <returns>A list of files found in the directory matching the IncludeMask
763        /// property.</returns>
764        private FileInfo[] GetFiles(DirectoryInfo info)
765        {
766            List<FileInfo> result = new List<FileInfo>();
767            if (info.Exists)
768            {
769                try
770                {
771                    foreach (DirectoryInfo dir in info.GetDirectories())
772                        result.AddRange(GetFiles(dir));
773
774                    if (IncludeMask.Length == 0)
775                        result.AddRange(info.GetFiles());
776                    else
777                        result.AddRange(info.GetFiles(IncludeMask, SearchOption.TopDirectoryOnly));
778                }
779                catch (UnauthorizedAccessException e)
780                {
781                    Task.Log.LastSessionEntries.Add(new LogEntry(S._("Could not erase files and " +
782                        "subfolders in {0} because {1}", info.FullName, e.Message), LogLevel.Error));
783                }
784            }
785
786            return result.ToArray();
787        }
788
789        /// <summary>
790        /// A wildcard expression stating the condition for the set of files to include.
791        /// The include mask is applied before the exclude mask is applied. If this value
792        /// is empty, all files and folders within the folder specified is included.
793        /// </summary>
794        public string IncludeMask { get; set; }
795
796        /// <summary>
797        /// A wildcard expression stating the condition for removing files from the set
798        /// of included files. If this value is omitted, all files and folders extracted
799        /// by the inclusion mask is erased.
800        /// </summary>
801        public string ExcludeMask { get; set; }
802
803        /// <summary>
804        /// Determines if Eraser should delete the folder after the erase process.
805        /// </summary>
806        public bool DeleteIfEmpty { get; set; }
807    }
808
809    [Serializable]
810    public class RecycleBinTarget : FileSystemObjectTarget
811    {
812        #region Serialization code
813        protected RecycleBinTarget(SerializationInfo info, StreamingContext context)
814            : base(info, context)
815        {
816        }
817        #endregion
818
819        public RecycleBinTarget()
820        {
821        }
822
823        internal override List<string> GetPaths(out long totalSize)
824        {
825            totalSize = 0;
826            List<string> result = new List<string>();
827            string[] rootDirectory = new string[] {
828                    "$RECYCLE.BIN",
829                    "RECYCLER",
830                    "RECYCLED"
831                };
832            string userSid = System.Security.Principal.WindowsIdentity.GetCurrent().
833                User.ToString();
834
835            foreach (DriveInfo drive in DriveInfo.GetDrives())
836            {
837                foreach (string rootDir in rootDirectory)
838                {
839                    //First get the global recycle bin for the current drive
840                    string recycleBinPath = System.IO.Path.Combine(
841                        drive.Name, rootDir);
842                    if (!Directory.Exists(recycleBinPath))
843                        continue;
844
845                    //Try to see if we can get the user's own recycle bin
846                    if (Directory.Exists(System.IO.Path.Combine(recycleBinPath, userSid)))
847                        recycleBinPath = System.IO.Path.Combine(recycleBinPath, userSid);
848                    GetRecyclerFiles(new DirectoryInfo(recycleBinPath), result, ref totalSize);
849                }
850            }
851
852            return result;
853        }
854
855        /// <summary>
856        /// Retrieves all files within this folder, without exclusions.
857        /// </summary>
858        /// <param name="info">The DirectoryInfo object representing the folder to traverse.</param>
859        /// <param name="paths">The list of files to store path information in.</param>
860        /// <param name="totalSize">Receives the total size of the files.</param>
861        private void GetRecyclerFiles(DirectoryInfo info, List<string> paths,
862            ref long totalSize)
863        {
864            try
865            {
866                foreach (FileInfo fileInfo in info.GetFiles())
867                {
868                    try
869                    {
870                        if (!fileInfo.Exists || (fileInfo.Attributes & FileAttributes.ReparsePoint) != 0)
871                            continue;
872
873                        totalSize += fileInfo.Length;
874                        GetPathADSes(paths, out totalSize, fileInfo.FullName);
875                        paths.Add(fileInfo.FullName);
876                    }
877                    catch (FileNotFoundException)
878                    {
879                        Task.Log.LastSessionEntries.Add(new LogEntry(
880                            S._("The file {0} was not erased because it was deleted " +
881                                "before it could be erased.", fileInfo.FullName),
882                            LogLevel.Information));
883                    }
884                    catch (DirectoryNotFoundException)
885                    {
886                        Task.Log.LastSessionEntries.Add(new LogEntry(
887                            S._("The file {0} was not erased because the containing " +
888                                "directory was deleted before it could be erased.",
889                                fileInfo.FullName),
890                            LogLevel.Information));
891                    }
892                    catch (FileLoadException)
893                    {
894                        Task.Log.LastSessionEntries.Add(new LogEntry(
895                            S._("Could not list the Alternate Data Streams for file {0} " +
896                                "because the file is being used by another process. The " +
897                                "file will not be erased.", fileInfo.FullName),
898                            LogLevel.Error));
899                    }
900                }
901
902                foreach (DirectoryInfo directoryInfo in info.GetDirectories())
903                    if ((directoryInfo.Attributes & FileAttributes.ReparsePoint) == 0)
904                        GetRecyclerFiles(directoryInfo, paths, ref totalSize);
905            }
906            catch (UnauthorizedAccessException e)
907            {
908                Task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error));
909            }
910        }
911
912        /// <summary>
913        /// Retrieves the text to display representing this task.
914        /// </summary>
915        public override string UIText
916        {
917            get
918            {
919                return S._("Recycle Bin");
920            }
921        }
922    }
923
924    /// <summary>
925    /// Maintains a collection of erasure targets.
926    /// </summary>
927    [Serializable]
928    public class ErasureTargetsCollection : IList<ErasureTarget>, ICollection<ErasureTarget>,
929        IEnumerable<ErasureTarget>, ISerializable
930    {
931        #region Constructors
932        internal ErasureTargetsCollection(Task owner)
933        {
934            this.list = new List<ErasureTarget>();
935            this.owner = owner;
936        }
937
938        internal ErasureTargetsCollection(Task owner, int capacity)
939            : this(owner)
940        {
941            list.Capacity = capacity;
942        }
943
944        internal ErasureTargetsCollection(Task owner, IEnumerable<ErasureTarget> targets)
945            : this(owner)
946        {
947            list.AddRange(targets);
948        }
949        #endregion
950
951        #region Serialization Code
952        protected ErasureTargetsCollection(SerializationInfo info, StreamingContext context)
953        {
954            list = (List<ErasureTarget>)info.GetValue("list", typeof(List<ErasureTarget>));
955        }
956
957        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
958        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
959        {
960            info.AddValue("list", list);
961        }
962        #endregion
963
964        #region IEnumerable<ErasureTarget> Members
965        public IEnumerator<ErasureTarget> GetEnumerator()
966        {
967            return list.GetEnumerator();
968        }
969        #endregion
970
971        #region IEnumerable Members
972        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
973        {
974            return GetEnumerator();
975        }
976        #endregion
977
978        #region ICollection<ErasureTarget> Members
979        public void Add(ErasureTarget item)
980        {
981            item.Task = owner;
982            list.Add(item);
983        }
984
985        public void Clear()
986        {
987            foreach (ErasureTarget item in list)
988                Remove(item);
989        }
990
991        public bool Contains(ErasureTarget item)
992        {
993            return list.Contains(item);
994        }
995
996        public void CopyTo(ErasureTarget[] array, int arrayIndex)
997        {
998            list.CopyTo(array, arrayIndex);
999        }
1000
1001        public int Count
1002        {
1003            get
1004            {
1005                return list.Count;
1006            }
1007        }
1008
1009        public bool IsReadOnly
1010        {
1011            get
1012            {
1013                return false;
1014            }
1015        }
1016
1017        public bool Remove(ErasureTarget item)
1018        {
1019            int index = list.IndexOf(item);
1020            if (index < 0)
1021                return false;
1022
1023            RemoveAt(index);
1024            return true;
1025        }
1026        #endregion
1027
1028        #region IList<ErasureTarget> Members
1029        public int IndexOf(ErasureTarget item)
1030        {
1031            return list.IndexOf(item);
1032        }
1033
1034        public void Insert(int index, ErasureTarget item)
1035        {
1036            item.Task = owner;
1037            list.Insert(index, item);
1038        }
1039
1040        public void RemoveAt(int index)
1041        {
1042            list.RemoveAt(index);
1043        }
1044
1045        public ErasureTarget this[int index]
1046        {
1047            get
1048            {
1049                return list[index];
1050            }
1051            set
1052            {
1053                list[index] = value;
1054            }
1055        }
1056        #endregion
1057
1058        /// <summary>
1059        /// The owner of this list of targets.
1060        /// </summary>
1061        public Task Owner
1062        {
1063            get
1064            {
1065                return owner;
1066            }
1067            internal set
1068            {
1069                owner = value;
1070                foreach (ErasureTarget target in list)
1071                    target.Task = owner;
1072            }
1073        }
1074
1075        /// <summary>
1076        /// The owner of this list of targets. All targets added to this list
1077        /// will have the owner set to this object.
1078        /// </summary>
1079        private Task owner;
1080
1081        /// <summary>
1082        /// The list bring the data store behind this object.
1083        /// </summary>
1084        private List<ErasureTarget> list;
1085    }
1086
1087    /// <summary>
1088    /// A base event class for all event arguments involving a task.
1089    /// </summary>
1090    public class TaskEventArgs : EventArgs
1091    {
1092        /// <summary>
1093        /// Constructor.
1094        /// </summary>
1095        /// <param name="task">The task being referred to by this event.</param>
1096        public TaskEventArgs(Task task)
1097        {
1098            Task = task;
1099        }
1100
1101        /// <summary>
1102        /// The executing task.
1103        /// </summary>
1104        public Task Task { get; private set; }
1105    }
1106
1107    /// <summary>
1108    /// A Event argument object containing the progress of the task.
1109    /// </summary>
1110    public class TaskProgressEventArgs : TaskEventArgs
1111    {
1112        /// <summary>
1113        /// Constructor.
1114        /// </summary>
1115        /// <param name="task">The task being run.</param>
1116        public TaskProgressEventArgs(Task task)
1117            : base(task)
1118        {
1119            CurrentItemPass = 1;
1120        }
1121
1122        /// <summary>
1123        /// A number from 0 to 1 detailing the overall progress of the task.
1124        /// </summary>
1125        public float OverallProgress
1126        {
1127            get { return overallProgress; }
1128        }
1129
1130        /// <summary>
1131        /// The amount of time left for the operation to complete, in seconds.
1132        /// </summary>
1133        public TimeSpan TimeLeft { get; internal set; }
1134
1135        /// <summary>
1136        /// The current erasure target - the current item being erased.
1137        /// </summary>
1138        public ErasureTarget CurrentTarget { get; internal set; }
1139
1140        /// <summary>
1141        /// The current index of the target.
1142        /// </summary>
1143        public int CurrentTargetIndex { get; internal set; }
1144
1145        /// <summary>
1146        /// The total number of passes to complete before this erasure method is
1147        /// completed.
1148        /// </summary>
1149        public int CurrentTargetTotalPasses { get; internal set; }
1150
1151        /// <summary>
1152        /// The stage of the erasure the executor is at.
1153        /// </summary>
1154        public string CurrentTargetStatus { get; internal set; }
1155
1156        /// <summary>
1157        /// A number from 0 to 1 detailing the overall progress of the item.
1158        /// Negative numbers indicate indeterminate progress.
1159        /// </summary>
1160        public float CurrentItemProgress { get; internal set; }
1161
1162        /// <summary>
1163        /// The file name of the item being erased.
1164        /// </summary>
1165        public string CurrentItemName { get; internal set; }
1166
1167        /// <summary>
1168        /// The pass number of a multi-pass erasure method.
1169        /// </summary>
1170        public int CurrentItemPass { get; internal set; }
1171
1172        /// <summary>
1173        /// The progress made by the current target.
1174        /// </summary>
1175        internal float CurrentTargetProgress
1176        {
1177            set
1178            {
1179                overallProgress = Math.Min(
1180                    (value + (float)(CurrentTargetIndex - 1)) / Task.Targets.Count,
1181                    1.0f);
1182            }
1183        }
1184
1185        private float overallProgress;
1186    }
1187}
Note: See TracBrowser for help on using the repository browser.