source: branches/eraser6/CodeReview/Eraser.Manager/DirectExecutor.cs @ 1552

Revision 1552, 27.8 KB checked in by lowjoel, 5 years ago (diff)

Combined the functions in AdvApi?, MsCorEEApi and WintrustApi? to Security.cs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By: Kasra Nassiri <cjax@users.sourceforge.net> @17/10/2008
6 * Modified By:
7 *
8 * This file is part of Eraser.
9 *
10 * Eraser is free software: you can redistribute it and/or modify it under the
11 * terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 3 of the License, or (at your option) any later
13 * version.
14 *
15 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
16 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 *
19 * A copy of the GNU General Public License can be found at
20 * <http://www.gnu.org/licenses/>.
21 */
22
23using System;
24using System.Collections.Generic;
25using System.Collections.Specialized;
26using System.Text;
27using System.Threading;
28using System.IO;
29
30using Eraser.Util;
31using System.Security.Principal;
32using System.Runtime.Serialization;
33using System.Runtime.Serialization.Formatters.Binary;
34using System.Security.Permissions;
35
36namespace Eraser.Manager
37{
38    /// <summary>
39    /// The DirectExecutor class is used by the Eraser GUI directly when the program
40    /// is run without the help of a Service.
41    /// </summary>
42    public class DirectExecutor : Executor
43    {
44        public DirectExecutor()
45        {
46            TaskAdded += OnTaskAdded;
47            TaskDeleted += OnTaskDeleted;
48            tasks = new DirectExecutorTasksCollection(this);
49            thread = new Thread(Main);
50        }
51
52        protected override void Dispose(bool disposing)
53        {
54            if (disposing)
55            {
56                thread.Abort();
57                schedulerInterrupt.Set();
58                schedulerInterrupt.Close();
59            }
60
61            base.Dispose(disposing);
62        }
63
64        public override void Run()
65        {
66            thread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
67            thread.Start();
68        }
69
70        public override void QueueTask(Task task)
71        {
72            lock (tasksLock)
73            {
74                //Queue the task to be run immediately.
75                DateTime executionTime = DateTime.Now;
76                if (!scheduledTasks.ContainsKey(executionTime))
77                    scheduledTasks.Add(executionTime, new List<Task>());
78                scheduledTasks[executionTime].Add(task);
79                schedulerInterrupt.Set();
80            }
81        }
82
83        public override void ScheduleTask(Task task)
84        {
85            RecurringSchedule schedule = task.Schedule as RecurringSchedule;
86            if (schedule == null)
87                return;
88
89            DateTime executionTime = (schedule.MissedPreviousSchedule &&
90                ManagerLibrary.Settings.ExecuteMissedTasksImmediately) ?
91                    DateTime.Now : schedule.NextRun;
92
93            lock (tasksLock)
94            {
95                if (!scheduledTasks.ContainsKey(executionTime))
96                    scheduledTasks.Add(executionTime, new List<Task>());
97                scheduledTasks[executionTime].Add(task);
98            }
99        }
100
101        public override void QueueRestartTasks()
102        {
103            lock (tasksLock)
104            {
105                foreach (Task task in Tasks)
106                    if (task.Schedule == Schedule.RunOnRestart)
107                        QueueTask(task);
108            }
109        }
110
111        public override void UnqueueTask(Task task)
112        {
113            lock (tasksLock)
114                for (int i = 0; i != scheduledTasks.Count; ++i)
115                    for (int j = 0; j < scheduledTasks.Values[i].Count; )
116                    {
117                        Task currentTask = scheduledTasks.Values[i][j];
118                        if (currentTask == task &&
119                            (!(currentTask.Schedule is RecurringSchedule) ||
120                                ((RecurringSchedule)currentTask.Schedule).NextRun != scheduledTasks.Keys[i]))
121                        {
122                            scheduledTasks.Values[i].RemoveAt(j);
123                        }
124                        else
125                        {
126                            ++j;
127                        }
128                    }
129        }
130
131        internal override bool IsTaskQueued(Task task)
132        {
133            lock (tasksLock)
134                foreach (KeyValuePair<DateTime, List<Task>> tasks in scheduledTasks)
135                    foreach (Task i in tasks.Value)
136                        if (task == i)
137                            if (task.Schedule is RecurringSchedule)
138                            {
139                                if (((RecurringSchedule)task.Schedule).NextRun != tasks.Key)
140                                    return true;
141                            }
142                            else
143                                return true;
144
145            return false;
146        }
147
148        private void OnTaskAdded(object sender, TaskEventArgs e)
149        {
150            e.Task.TaskEdited += OnTaskEdited;
151        }
152
153        private void OnTaskEdited(object sender, TaskEventArgs e)
154        {
155            //Find all schedule entries containing the task - since the user cannot make
156            //edits to the task when it is queued (only if it is scheduled) remove
157            //all task references and add them back
158            lock (tasksLock)
159                for (int i = 0; i != scheduledTasks.Count; ++i)
160                    for (int j = 0; j < scheduledTasks.Values[i].Count; )
161                    {
162                        Task currentTask = scheduledTasks.Values[i][j];
163                        if (currentTask == e.Task)
164                            scheduledTasks.Values[i].RemoveAt(j);
165                        else
166                            j++;
167                    }
168
169            //Then reschedule the task
170            if (e.Task.Schedule is RecurringSchedule)
171                ScheduleTask(e.Task);
172        }
173
174        private void OnTaskDeleted(object sender, TaskEventArgs e)
175        {
176            e.Task.TaskEdited -= OnTaskEdited;
177        }
178
179        public override ExecutorTasksCollection Tasks
180        {
181            get
182            {
183                return tasks;
184            }
185        }
186
187        /// <summary>
188        /// The thread entry point for this object. This object operates on a queue
189        /// and hence the thread will sequentially execute tasks.
190        /// </summary>
191        private void Main()
192        {
193            //The waiting thread will utilize a polling loop to check for new
194            //scheduled tasks. This will be checked every 30 seconds. However,
195            //when the thread is waiting for a new task, it can be interrupted.
196            while (thread.ThreadState != ThreadState.AbortRequested)
197            {
198                //Check for a new task
199                Task task = null;
200                lock (tasksLock)
201                {
202                    while (scheduledTasks.Count != 0)
203                        if (scheduledTasks.Values[0].Count == 0)
204                        {
205                            //Clean all all time slots at the start of the queue which are
206                            //empty
207                            scheduledTasks.RemoveAt(0);
208                        }
209                        else
210                        {
211                            if (scheduledTasks.Keys[0] <= DateTime.Now)
212                            {
213                                List<Task> tasks = scheduledTasks.Values[0];
214                                task = tasks[0];
215                                tasks.RemoveAt(0);
216                            }
217
218                            //Do schedule queue maintenance: clean up all empty timeslots
219                            if (task == null)
220                            {
221                                for (int i = 0; i < scheduledTasks.Count; )
222                                    if (scheduledTasks.Values[i].Count == 0)
223                                        scheduledTasks.RemoveAt(i);
224                                    else
225                                        ++i;
226                            }
227
228                            break;
229                        }
230                }
231
232                if (task != null)
233                {
234                    //Set the currently executing task.
235                    currentTask = task;
236
237                    try
238                    {
239                        //Prevent the system from sleeping.
240                        KernelApi.SetThreadExecutionState(ThreadExecutionState.Continuous |
241                            ThreadExecutionState.SystemRequired);
242
243                        //Broadcast the task started event.
244                        task.Canceled = false;
245                        task.OnTaskStarted(new TaskEventArgs(task));
246                        OnTaskProcessing(new TaskEventArgs(task));
247
248                        //Start a new log session to separate this session's events
249                        //from previous ones.
250                        task.Log.Entries.NewSession();
251
252                        //Run the task
253                        foreach (ErasureTarget target in task.Targets)
254                            try
255                            {
256                                UnusedSpaceTarget unusedSpaceTarget =
257                                    target as UnusedSpaceTarget;
258                                FileSystemObjectTarget fileSystemObjectTarget =
259                                    target as FileSystemObjectTarget;
260
261                                if (unusedSpaceTarget != null)
262                                    EraseUnusedSpace(task, unusedSpaceTarget);
263                                else if (fileSystemObjectTarget != null)
264                                    EraseFilesystemObject(task, fileSystemObjectTarget);
265                                else
266                                    throw new ArgumentException(S._("Unknown erasure target."));
267                            }
268                            catch (FatalException)
269                            {
270                                throw;
271                            }
272                            catch (OperationCanceledException)
273                            {
274                                throw;
275                            }
276                            catch (Exception e)
277                            {
278                                task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error));
279                            }
280                    }
281                    catch (FatalException e)
282                    {
283                        task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Fatal));
284                    }
285                    catch (OperationCanceledException e)
286                    {
287                        task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Fatal));
288                    }
289                    catch (Exception e)
290                    {
291                        task.Log.LastSessionEntries.Add(new LogEntry(e.Message, LogLevel.Error));
292                    }
293                    finally
294                    {
295                        //Allow the system to sleep again.
296                        KernelApi.SetThreadExecutionState(ThreadExecutionState.Continuous);
297
298                        //If the task is a recurring task, reschedule it since we are done.
299                        if (task.Schedule is RecurringSchedule)
300                            ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now);
301
302                        //If the task is an execute on restart task, it is only run
303                        //once and can now be restored to an immediately executed task
304                        if (task.Schedule == Schedule.RunOnRestart)
305                            task.Schedule = Schedule.RunNow;
306
307                        //And the task finished event.
308                        task.OnTaskFinished(new TaskEventArgs(task));
309                        OnTaskProcessed(new TaskEventArgs(task));
310
311                        //Remove the actively executing task from our instance variable
312                        currentTask = null;
313                    }
314                }
315
316                //Wait for half a minute to check for the next scheduled task.
317                schedulerInterrupt.WaitOne(30000, false);
318            }
319        }
320
321        /// <summary>
322        /// Executes a unused space erase.
323        /// </summary>
324        /// <param name="task">The task currently being executed</param>
325        /// <param name="target">The target of the unused space erase.</param>
326        private void EraseUnusedSpace(Task task, UnusedSpaceTarget target)
327        {
328            //Check for sufficient privileges to run the unused space erasure.
329            if (!Security.IsAdministrator())
330            {
331                if (Environment.OSVersion.Platform == PlatformID.Win32NT &&
332                    Environment.OSVersion.Version >= new Version(6, 0))
333                {
334                    throw new UnauthorizedAccessException(S._("The program does not have the " +
335                        "required permissions to erase the unused space on disk. Run the program " +
336                        "as an administrator and retry the operation."));
337                }
338                else
339                    throw new UnauthorizedAccessException(S._("The program does not have the " +
340                        "required permissions to erase the unused space on disk."));
341            }
342
343            //Check whether System Restore has any available checkpoints.
344            if (SystemRestore.GetInstances().Count != 0)
345            {
346                task.Log.LastSessionEntries.Add(new LogEntry(S._("The drive {0} has System " +
347                    "Restore or Volume Shadow Copies enabled. This may allow copies of files " +
348                    "stored on the disk to be recovered and pose a security concern.",
349                    target.Drive), LogLevel.Warning));
350            }
351           
352            //If the user is under disk quotas, log a warning message
353            if (VolumeInfo.FromMountpoint(target.Drive).HasQuota)
354                task.Log.LastSessionEntries.Add(new LogEntry(S._("The drive {0} has disk quotas " +
355                    "active. This will prevent the complete erasure of unused space and may pose " +
356                    "a security concern.", target.Drive), LogLevel.Warning));
357
358            //Get the erasure method if the user specified he wants the default.
359            ErasureMethod method = target.Method;
360
361            //Make a folder to dump our temporary files in
362            DirectoryInfo info = new DirectoryInfo(target.Drive);
363            VolumeInfo volInfo = VolumeInfo.FromMountpoint(target.Drive);
364            FileSystem fsManager = FileSystemManager.Get(volInfo);
365
366            //Start sampling the speed of the task.
367            SteppedProgressManager progress = new SteppedProgressManager();
368            target.Progress = progress;
369            task.Progress.Steps.Add(new SteppedProgressManager.Step(
370                progress, 1.0f / task.Targets.Count));
371
372            //Erase the cluster tips of every file on the drive.
373            if (target.EraseClusterTips)
374            {
375                //Define the callback handlers
376                ProgressManager tipSearch = new ProgressManager();
377                progress.Steps.Add(new SteppedProgressManager.Step(tipSearch, 
378                    0.0f, S._("Searching for files' cluster tips...")));
379                tipSearch.Total = 1;
380                ClusterTipsSearchProgress searchProgress = delegate(string path)
381                    {
382                        if (currentTask.Canceled)
383                            throw new OperationCanceledException(S._("The task was cancelled."));
384
385                        task.OnProgressChanged(target,
386                            new ProgressChangedEventArgs(tipSearch,
387                                new TaskProgressChangedEventArgs(path, 0, 0)));
388                    };
389
390                ProgressManager tipProgress = new ProgressManager();
391                progress.Steps.Add(new SteppedProgressManager.Step(tipProgress, 0.1f,
392                    S._("Erasing cluster tips...")));
393                ClusterTipsEraseProgress eraseProgress =
394                    delegate(int currentFile, int totalFiles, string currentFilePath)
395                    {
396                        tipSearch.Completed = tipSearch.Total;
397                        tipProgress.Total = totalFiles;
398                        tipProgress.Completed = currentFile;
399                        task.OnProgressChanged(target,
400                            new ProgressChangedEventArgs(tipProgress,
401                                new TaskProgressChangedEventArgs(currentFilePath, 0, 0)));
402
403                        if (currentTask.Canceled)
404                            throw new OperationCanceledException(S._("The task was cancelled."));
405                    };
406
407                //Start counting statistics
408                fsManager.EraseClusterTips(VolumeInfo.FromMountpoint(target.Drive),
409                    method, task.Log, searchProgress, eraseProgress);
410            }
411
412            info = info.CreateSubdirectory(Path.GetFileName(
413                FileSystem.GenerateRandomFileName(info, 18)));
414            try
415            {
416                //Set the folder's compression flag off since we want to use as much
417                //space as possible
418                if (Eraser.Util.File.IsCompressed(info.FullName))
419                    Eraser.Util.File.SetCompression(info.FullName, false);
420
421                ProgressManager mainProgress = new ProgressManager();
422                progress.Steps.Add(new SteppedProgressManager.Step(mainProgress,
423                    target.EraseClusterTips ? 0.8f : 0.9f, S._("Erasing unused space...")));
424
425                //Continue creating files while there is free space.
426                while (volInfo.AvailableFreeSpace > 0)
427                {
428                    //Generate a non-existant file name
429                    string currFile = FileSystem.GenerateRandomFileName(info, 18);
430
431                    //Create the stream
432                    using (FileStream stream = new FileStream(currFile, FileMode.CreateNew,
433                        FileAccess.Write, FileShare.None, 8, FileOptions.WriteThrough))
434                    {
435                        //Set the length of the file to be the amount of free space left
436                        //or the maximum size of one of these dumps.
437                        mainProgress.Total = mainProgress.Completed + volInfo.AvailableFreeSpace;
438                        long streamLength = Math.Min(ErasureMethod.FreeSpaceFileUnit,
439                            mainProgress.Total);
440
441                        //Handle IO exceptions gracefully, because the filesystem
442                        //may require more space than demanded by us for file allocation.
443                        while (true)
444                            try
445                            {
446                                stream.SetLength(streamLength);
447                                break;
448                            }
449                            catch (IOException)
450                            {
451                                if (streamLength > volInfo.ClusterSize)
452                                    streamLength -= volInfo.ClusterSize;
453                                else
454                                    throw;
455                            }
456
457                        //Then run the erase task
458                        method.Erase(stream, long.MaxValue,
459                            PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng),
460                            delegate(long lastWritten, long totalData, int currentPass)
461                            {
462                                mainProgress.Completed += lastWritten;
463                                task.OnProgressChanged(target,
464                                    new ProgressChangedEventArgs(mainProgress,
465                                        new TaskProgressChangedEventArgs(target.Drive, currentPass, method.Passes)));
466
467                                if (currentTask.Canceled)
468                                    throw new OperationCanceledException(S._("The task was cancelled."));
469                            }
470                        );
471                    }
472                }
473
474                //Mark the main bulk of the progress as complete
475                mainProgress.Completed = mainProgress.Total;
476
477                //Erase old resident file system table files
478                ProgressManager residentProgress = new ProgressManager();
479                progress.Steps.Add(new SteppedProgressManager.Step(residentProgress,
480                    0.05f, S._("Old resident file system table files")));
481                fsManager.EraseOldFileSystemResidentFiles(volInfo, info, method,
482                    delegate(int currentFile, int totalFiles)
483                    {
484                        residentProgress.Completed = currentFile;
485                        residentProgress.Total = totalFiles;
486                        task.OnProgressChanged(target,
487                            new ProgressChangedEventArgs(residentProgress,
488                                new TaskProgressChangedEventArgs(string.Empty, 0, 0)));
489
490                        if (currentTask.Canceled)
491                            throw new OperationCanceledException(S._("The task was cancelled."));
492                    }
493                );
494
495                residentProgress.Completed = residentProgress.Total = 1;
496            }
497            finally
498            {
499                //Remove the folder holding all our temporary files.
500                ProgressManager tempFiles = new ProgressManager();
501                progress.Steps.Add(new SteppedProgressManager.Step(tempFiles,
502                    0.0f, S._("Removing temporary files...")));
503                task.OnProgressChanged(target, new ProgressChangedEventArgs(tempFiles,
504                    new TaskProgressChangedEventArgs(string.Empty, 0, 0)));
505                fsManager.DeleteFolder(info);
506                tempFiles.Completed = tempFiles.Total = 1;
507            }
508
509            //Then clean the old file system entries
510            ProgressManager structureProgress = new ProgressManager();
511            progress.Steps.Add(new SteppedProgressManager.Step(structureProgress,
512                0.05f, S._("Erasing unused directory structures...")));
513            fsManager.EraseDirectoryStructures(volInfo,
514                delegate(int currentFile, int totalFiles)
515                {
516                    if (currentTask.Canceled)
517                        throw new OperationCanceledException(S._("The task was cancelled."));
518
519                    //Compute the progress
520                    structureProgress.Total = totalFiles;
521                    structureProgress.Completed = currentFile;
522
523                    //Set the event parameters, then broadcast the progress event.
524                    task.OnProgressChanged(target,
525                        new ProgressChangedEventArgs(structureProgress,
526                            new TaskProgressChangedEventArgs(string.Empty, 0, 0)));
527                }
528            );
529
530            structureProgress.Completed = structureProgress.Total;
531            target.Progress = null;
532        }
533
534        /// <summary>
535        /// Erases a file or folder on the volume.
536        /// </summary>
537        /// <param name="task">The task currently being processed.</param>
538        /// <param name="target">The target of the erasure.</param>
539        /// <param name="progress">The progress manager for the current task.</param>
540        private void EraseFilesystemObject(Task task, FileSystemObjectTarget target)
541        {
542            //Retrieve the list of files to erase.
543            long dataTotal = 0;
544            List<string> paths = target.GetPaths(out dataTotal);
545
546            //Get the erasure method if the user specified he wants the default.
547            ErasureMethod method = target.Method;
548
549            //Calculate the total amount of data required to finish the wipe.
550            //dataTotal = method.CalculateEraseDataSize(paths, dataTotal);
551
552            //Set the event's current target status.
553            TaskEventArgs eventArgs = new TaskEventArgs(task);
554            SteppedProgressManager progress = new SteppedProgressManager();
555            target.Progress = progress;
556            task.Progress.Steps.Add(new SteppedProgressManager.Step(progress, 1.0f / task.Targets.Count));
557
558            //Iterate over every path, and erase the path.
559            for (int i = 0; i < paths.Count; ++i)
560            {
561                //Update the task progress
562                ProgressManager step = new ProgressManager();
563                progress.Steps.Add(new SteppedProgressManager.Step(step,
564                    1.0f / paths.Count, S._("Erasing files...")));
565                task.OnProgressChanged(target,
566                    new ProgressChangedEventArgs(step,
567                        new TaskProgressChangedEventArgs(paths[i], 0, method.Passes)));
568               
569                //Get the filesystem provider to handle the secure file erasures
570                StreamInfo info = new StreamInfo(paths[i]);
571                FileSystem fsManager = FileSystemManager.Get(
572                    VolumeInfo.FromMountpoint(info.DirectoryName));
573
574                //Check that the file exists - we do not want to bother erasing nonexistant files
575                if (!info.Exists)
576                {
577                    task.Log.LastSessionEntries.Add(new LogEntry(S._("The file {0} was not erased " +
578                        "as the file does not exist.", paths[i]), LogLevel.Notice));
579                    continue;
580                }
581
582                bool isReadOnly = false;
583               
584                try
585                {
586                    //Remove the read-only flag, if it is set.
587                    if (isReadOnly = info.IsReadOnly)
588                        info.IsReadOnly = false;
589
590                    //Make sure the file does not have any attributes which may affect
591                    //the erasure process
592                    if ((info.Attributes & FileAttributes.Compressed) != 0 || 
593                        (info.Attributes & FileAttributes.Encrypted) != 0 ||
594                        (info.Attributes & FileAttributes.SparseFile) != 0)
595                    {
596                        //Log the error
597                        task.Log.LastSessionEntries.Add(new LogEntry(S._("The file {0} could " +
598                            "not be erased because the file was either compressed, encrypted or " +
599                            "a sparse file.", info.FullName), LogLevel.Error));
600                    }
601
602                    fsManager.EraseFileSystemObject(info, method,
603                        delegate(long lastWritten, long totalData, int currentPass)
604                        {
605                            if (currentTask.Canceled)
606                                throw new OperationCanceledException(S._("The task was cancelled."));
607
608                            step.Completed += lastWritten;
609                            step.Total = totalData;
610                            task.OnProgressChanged(target,
611                                new ProgressChangedEventArgs(step,
612                                    new TaskProgressChangedEventArgs(info.FullName, currentPass, method.Passes)));
613                        });
614
615                    //Remove the file.
616                    FileInfo fileInfo = info.File;
617                    if (fileInfo != null)
618                        fsManager.DeleteFile(fileInfo);
619                    step.Completed = step.Total = 1;
620                }
621                catch (UnauthorizedAccessException)
622                {
623                    task.Log.LastSessionEntries.Add(new LogEntry(S._("The file {0} could not " +
624                        "be erased because the file's permissions prevent access to the file.",
625                        info.FullName), LogLevel.Error));
626                }
627                catch (FileLoadException)
628                {
629                    if (!ManagerLibrary.Settings.ForceUnlockLockedFiles)
630                        throw;
631
632                    List<System.Diagnostics.Process> processes = new List<System.Diagnostics.Process>();
633                    foreach (OpenHandle handle in OpenHandle.Items)
634                        if (handle.Path == paths[i])
635                            processes.Add(System.Diagnostics.Process.GetProcessById(handle.ProcessId));
636
637                    StringBuilder processStr = new StringBuilder();
638                    foreach (System.Diagnostics.Process process in processes)
639                        processStr.AppendFormat(System.Globalization.CultureInfo.InvariantCulture,
640                            "{0}, ", process.MainModule.FileName);
641
642                    task.Log.LastSessionEntries.Add(new LogEntry(S._(
643                        "Could not force closure of file \"{0}\" (locked by {1})",
644                        paths[i], processStr.ToString().Remove(processStr.Length - 2)), LogLevel.Error));
645                }
646                finally
647                {
648                    //Re-set the read-only flag if the file exists (i.e. there was an error)
649                    if (isReadOnly && info.Exists && !info.IsReadOnly)
650                        info.IsReadOnly = isReadOnly;
651                }
652            }
653
654            //If the user requested a folder removal, do it.
655            if (target is FolderTarget)
656            {
657                ProgressManager step = new ProgressManager();
658                progress.Steps.Add(new SteppedProgressManager.Step(step,
659                    0.0f, S._("Removing folders...")));
660               
661                //Remove all subfolders which are empty.
662                FolderTarget fldr = (FolderTarget)target;
663                FileSystem fsManager = FileSystemManager.Get(VolumeInfo.FromMountpoint(fldr.Path));
664                Action<DirectoryInfo> eraseEmptySubFolders = null;
665                eraseEmptySubFolders = delegate(DirectoryInfo info)
666                {
667                     foreach (DirectoryInfo subDir in info.GetDirectories())
668                         eraseEmptySubFolders(subDir);
669                     task.OnProgressChanged(target,
670                         new ProgressChangedEventArgs(step,
671                             new TaskProgressChangedEventArgs(info.FullName, 0, 0)));
672
673                     FileSystemInfo[] files = info.GetFileSystemInfos();
674                     if (files.Length == 0)
675                         fsManager.DeleteFolder(info);
676                };
677                eraseEmptySubFolders(new DirectoryInfo(fldr.Path));
678
679                if (fldr.DeleteIfEmpty)
680                {
681                    DirectoryInfo info = new DirectoryInfo(fldr.Path);
682                    task.OnProgressChanged(target,
683                        new ProgressChangedEventArgs(step,
684                            new TaskProgressChangedEventArgs(info.FullName, 0, 0)));
685
686                    //See if this is the root of a volume.
687                    bool isVolumeRoot = info.Parent == null;
688                    foreach (VolumeInfo volume in VolumeInfo.Volumes)
689                        foreach (string mountPoint in volume.MountPoints)
690                            if (info.FullName == mountPoint)
691                                isVolumeRoot = true;
692
693                    //If the folder is a mount point, then don't delete it. If it isn't,
694                    //search for files under the folder to see if it is empty.
695                    if (!isVolumeRoot && info.Exists && info.GetFiles("*", SearchOption.AllDirectories).Length == 0)
696                        fsManager.DeleteFolder(info);
697                }
698            }
699
700            //If the user was erasing the recycle bin, clear the bin.
701            if (target is RecycleBinTarget)
702            {
703                ProgressManager step = new ProgressManager();
704                progress.Steps.Add(new SteppedProgressManager.Step(step,
705                    0.0f, S._("Emptying recycle bin...")));
706                task.OnProgressChanged(target,
707                    new ProgressChangedEventArgs(step,
708                        new TaskProgressChangedEventArgs(string.Empty, 0, 0)));
709
710                ShellApi.EmptyRecycleBin(EmptyRecycleBinOptions.NoConfirmation |
711                    EmptyRecycleBinOptions.NoProgressUI | EmptyRecycleBinOptions.NoSound);
712            }
713
714            target.Progress = null;
715        }
716
717        /// <summary>
718        /// The thread object.
719        /// </summary>
720        private Thread thread;
721
722        /// <summary>
723        /// The lock preventing concurrent access for the tasks list and the
724        /// tasks queue.
725        /// </summary>
726        private object tasksLock = new object();
727
728        /// <summary>
729        /// The queue of tasks. This queue is executed when the first element's
730        /// timestamp (the key) has been past. This list assumes that all tasks
731        /// are sorted by timestamp, smallest one first.
732        /// </summary>
733        private SortedList<DateTime, List<Task>> scheduledTasks =
734            new SortedList<DateTime, List<Task>>();
735
736        /// <summary>
737        /// The task list associated with this executor instance.
738        /// </summary>
739        private DirectExecutorTasksCollection tasks;
740
741        /// <summary>
742        /// The currently executing task.
743        /// </summary>
744        Task currentTask;
745
746        /// <summary>
747        /// An automatically reset event allowing the addition of new tasks to
748        /// interrupt the thread's sleeping state waiting for the next recurring
749        /// task to be due.
750        /// </summary>
751        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
752
753        private class DirectExecutorTasksCollection : ExecutorTasksCollection
754        {
755            /// <summary>
756            /// Constructor.
757            /// </summary>
758            /// <param name="executor">The <see cref="DirectExecutor"/> object owning
759            /// this list.</param>
760            public DirectExecutorTasksCollection(DirectExecutor executor)
761                : base(executor)
762            {
763            }
764
765            #region IList<Task> Members
766            public override int IndexOf(Task item)
767            {
768                return list.IndexOf(item);
769            }
770
771            public override void Insert(int index, Task item)
772            {
773                item.Executor = Owner;
774                lock (list)
775                    list.Insert(index, item);
776
777                //Call all the event handlers who registered to be notified of tasks
778                //being added.
779                Owner.OnTaskAdded(new TaskEventArgs(item));
780
781                //If the task is scheduled to run now, break the waiting thread and
782                //run it immediately
783                if (item.Schedule == Schedule.RunNow)
784                {
785                    Owner.QueueTask(item);
786                }
787                //If the task is scheduled, add the next execution time to the list
788                //of schduled tasks.
789                else if (item.Schedule != Schedule.RunOnRestart)
790                {
791                    Owner.ScheduleTask(item);
792                }
793            }
794
795            public override void RemoveAt(int index)
796            {
797                lock (list)
798                {
799                    Task task = list[index];
800                    task.Cancel();
801                    task.Executor = null;
802                    list.RemoveAt(index);
803
804                    //Call all event handlers registered to be notified of task deletions.
805                    Owner.OnTaskDeleted(new TaskEventArgs(task));
806                }
807            }
808
809            public override Task this[int index]
810            {
811                get
812                {
813                    lock (list)
814                        return list[index];
815                }
816                set
817                {
818                    lock (list)
819                        list[index] = value;
820                }
821            }
822            #endregion
823
824            #region ICollection<Task> Members
825            public override void Add(Task item)
826            {
827                Insert(Count, item);
828            }
829
830            public override void Clear()
831            {
832                foreach (Task task in list)
833                    Remove(task);
834            }
835
836            public override bool Contains(Task item)
837            {
838                lock (list)
839                    return list.Contains(item);
840            }
841
842            public override void CopyTo(Task[] array, int arrayIndex)
843            {
844                lock (list)
845                    list.CopyTo(array, arrayIndex);
846            }
847
848            public override int Count
849            {
850                get
851                {
852                    lock (list)
853                        return list.Count;
854                }
855            }
856
857            public override bool Remove(Task item)
858            {
859                lock (list)
860                {
861                    int index = list.IndexOf(item);
862                    if (index < 0)
863                        return false;
864
865                    RemoveAt(index);
866                }
867
868                return true;
869            }
870            #endregion
871
872            #region IEnumerable<Task> Members
873            public override IEnumerator<Task> GetEnumerator()
874            {
875                return list.GetEnumerator();
876            }
877            #endregion
878
879            public override void SaveToStream(Stream stream)
880            {
881                lock (list)
882                    new BinaryFormatter().Serialize(stream, list);
883            }
884
885            public override void LoadFromStream(Stream stream)
886            {
887                //Load the list into the dictionary
888                StreamingContext context = new StreamingContext(
889                    StreamingContextStates.All, Owner);
890                BinaryFormatter formatter = new BinaryFormatter(null, context);
891                List<Task> deserialised = (List<Task>)formatter.Deserialize(stream);
892                list.AddRange(deserialised);
893
894                foreach (Task task in deserialised)
895                {
896                    Owner.OnTaskAdded(new TaskEventArgs(task));
897                    if (task.Schedule is RecurringSchedule)
898                        Owner.ScheduleTask(task);
899                }
900            }
901
902            /// <summary>
903            /// The data store for this object.
904            /// </summary>
905            private List<Task> list = new List<Task>();
906        }
907    }
908}
Note: See TracBrowser for help on using the repository browser.