source: branches/eraser6/Manager/DirectExecutor.cs @ 213

Revision 213, 11.2 KB checked in by lowjoel, 7 years ago (diff)

-Define the Task start and completion events
-Added the UIText property. This was a copy from the GenerateTaskName? function in the Scheduler panel earlier
-Added the TaskEventArgs? base class to allow for other simpler events (mainly start and finish events)
-Added the current erasure target for erase tasks.

RevLine 
[148]1using System;
2using System.Collections.Generic;
3using System.Collections.Specialized;
4using System.Text;
5using System.Threading;
[180]6using System.IO;
[148]7
[198]8using Eraser.Util;
9
[148]10namespace Eraser.Manager
11{
12    /// <summary>
13    /// The DirectExecutor class is used by the Eraser GUI directly when the program
14    /// is run without the help of a Service.
15    /// </summary>
[171]16    public class DirectExecutor : Executor, IDisposable
[148]17    {
18        public DirectExecutor()
19        {
20            thread = new Thread(delegate()
21            {
[213]22                Main();
[148]23            });
[152]24
25            thread.Start();
26            Thread.Sleep(0);
[148]27        }
28
[171]29        void IDisposable.Dispose()
30        {
31            thread.Abort();
32            schedulerInterrupt.Set();
33        }
34
[148]35        public override void AddTask(ref Task task)
36        {
37            lock (unusedIdsLock)
38            {
39                if (unusedIds.Count != 0)
40                {
[213]41                    task.id = unusedIds[0];
[148]42                    unusedIds.RemoveAt(0);
43                }
44                else
[213]45                    task.id = ++nextId;
[148]46            }
[152]47
48            //Add the task to the set of tasks
49            lock (tasksLock)
50            {
51                tasks.Add(task.ID, task);
52
53                //If the task is scheduled to run now, break the waiting thread and
54                //run it immediately
55                if (task.Schedule == Schedule.RunNow)
56                {
57                    scheduledTasks.Add(DateTime.Now, task);
58                    schedulerInterrupt.Set();
59                }
60                //If the task is scheduled, add the next execution time to the list
61                //of schduled tasks.
62                else if (task.Schedule != Schedule.RunOnRestart)
63                {
64                    scheduledTasks.Add((task.Schedule as RecurringSchedule).NextRun, task);
65                }
66            }
[148]67        }
68
69        public override bool DeleteTask(uint taskId)
70        {
[152]71            lock (tasksLock)
72            {
73                if (!tasks.ContainsKey(taskId))
74                    return false;
[148]75
[152]76                lock (unusedIdsLock)
77                    unusedIds.Add(taskId);
78                tasks.Remove(taskId);
79            }
[148]80            return true;
81        }
82
83        public override Task GetTask(uint taskId)
84        {
[152]85            lock (tasksLock)
86            {
87                if (!tasks.ContainsKey(taskId))
88                    return null;
89                return tasks[taskId];
90            }
[148]91        }
92
[163]93        /// <summary>
94        /// The thread entry point for this object. This object operates on a queue
95        /// and hence the thread will sequentially execute tasks.
96        /// </summary>
[148]97        private void Main()
98        {
[152]99            //The waiting thread will utilize a polling loop to check for new
100            //scheduled tasks. This will be checked every 30 seconds. However,
101            //when the thread is waiting for a new task, it can be interrupted.
102            while (thread.ThreadState != ThreadState.AbortRequested)
103            {
104                //Check for a new task
105                Task task = null;
106                lock (tasksLock)
107                {
108                    if (scheduledTasks.Count != 0 &&
109                        (scheduledTasks.Values[0].Schedule == Schedule.RunNow ||
110                         scheduledTasks.Keys[0] <= DateTime.Now))
111                    {
112                        task = scheduledTasks.Values[0];
113                        scheduledTasks.RemoveAt(0);
114                    }
115                }
116
117                if (task != null)
118                {
[180]119                    try
120                    {
[213]121                        //Broadcast the task started event.
122                        task.OnTaskStarted(new TaskEventArgs(task));
123
[191]124                        //Run the task
125                        foreach (Task.ErasureTarget target in task.Entries)
126                            try
127                            {
128                                if (target is Task.UnusedSpace)
129                                    EraseUnusedSpace(task, (Task.UnusedSpace)target);
130                                else if (target is Task.FilesystemObject)
131                                    EraseFilesystemObject(task, (Task.FilesystemObject)target);
132                                else
133                                    throw new ArgumentException("Unknown erasure target.");
134                            }
135                            catch (FatalException)
136                            {
137                                throw;
138                            }
139                            catch (Exception e)
140                            {
141                                task.LogEntry(new LogEntry(e.Message, LogLevel.ERROR));
142                            }
[180]143                    }
[191]144                    catch (FatalException e)
[180]145                    {
[191]146                        task.LogEntry(new LogEntry(e.Message, LogLevel.FATAL));
[180]147                    }
[213]148                    finally
149                    {
150                        //And the task finished event.
151                        task.OnTaskFinished(new TaskEventArgs(task));
[163]152
[213]153                        //If the task is a recurring task, reschedule it since we are done.
154                        if (task.Schedule is RecurringSchedule)
155                            ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now);
156                    }
[152]157                }
158
159                //Wait for half a minute to check for the next scheduled task.
160                schedulerInterrupt.WaitOne(30000, false);
161            }
[148]162        }
163
[163]164        /// <summary>
[184]165        /// Executes a unused space erase.
[180]166        /// </summary>
[184]167        /// <param name="target">The target of the unused space erase.</param>
168        private void EraseUnusedSpace(Task task, Task.UnusedSpace target)
[180]169        {
[184]170            throw new NotImplementedException("Unused space erasures are not "+
[180]171                "currently implemented");
172        }
173
174        /// <summary>
175        /// Erases a file or folder on the volume.
176        /// </summary>
177        /// <param name="target">The target of the erasure.</param>
178        private void EraseFilesystemObject(Task task, Task.FilesystemObject target)
179        {
180            List<string> paths = target.GetPaths();
[201]181            TaskProgressEventArgs eventArgs = new TaskProgressEventArgs(task, 0, 0);
[180]182
[193]183            //Get the erasure method if the user specified he wants the default.
184            ErasureMethod method = target.Method;
185            if (method == ErasureMethodManager.Default)
186                method = ErasureMethodManager.GetInstance(Globals.Settings.DefaultFileErasureMethod);
187
[180]188            //Iterate over every path, and erase the path.
189            for (int i = 0; i < paths.Count; ++i)
190            {
191                //Update the task progress
[206]192                eventArgs.overallProgress = (i * 100) / paths.Count;
[213]193                eventArgs.currentTarget = target;
[180]194                eventArgs.currentItemName = paths[i];
195                eventArgs.currentItemProgress = 0;
[193]196                eventArgs.totalPasses = method.Passes;
[180]197                task.OnProgressChanged(eventArgs);
198
199                //Make sure the file does not have any attributes which may
200                //affect the erasure process
201                FileInfo info = new FileInfo(paths[i]);
202                if ((info.Attributes & FileAttributes.Compressed) != 0 ||
203                    (info.Attributes & FileAttributes.Encrypted) != 0 ||
204                    (info.Attributes & FileAttributes.SparseFile) != 0 ||
205                    (info.Attributes & FileAttributes.ReparsePoint) != 0)
206                {
207                    //Log the error
208                    throw new ArgumentException("Compressed, encrypted, or sparse" +
209                        "files cannot be erased with Eraser.");
210                }
211
212                //Remove the read-only flag, if it is set.
213                if ((info.Attributes & FileAttributes.ReadOnly) != 0)
214                    info.Attributes &= ~FileAttributes.ReadOnly;
215
216                //Create the file stream, and call the erasure method
217                //to write to the stream.
218                using (FileStream strm = new FileStream(info.FullName,
219                    FileMode.Open, FileAccess.Write, FileShare.None,
220                    8, FileOptions.WriteThrough))
221                {
[198]222                    //Set the end of the stream after the wrap-round the cluster size
223                    uint clusterSize = Drives.GetDriveClusterSize(info.Directory.Root.FullName);
224                    long roundUpFileLength = strm.Length % clusterSize;
225                    if (roundUpFileLength != 0)
226                        strm.SetLength(strm.Length + (clusterSize - roundUpFileLength));
227
228                    //Then erase the file.
[193]229                    method.Erase(strm, PRNGManager.GetInstance(Globals.Settings.ActivePRNG),
[206]230                        delegate(float currentProgress, int currentPass)
[180]231                        {
232                            eventArgs.currentPass = currentPass;
[206]233                            eventArgs.currentItemProgress = (int)
234                                ((float)currentProgress * 100.0);
235                            eventArgs.overallProgress = (int)
236                                (((i + currentProgress) / (float)paths.Count) * 100);
[180]237                            task.OnProgressChanged(eventArgs);
238                        }
239                    );
240
241                    //Set the length of the file to 0.
242                    strm.Seek(0, SeekOrigin.Begin);
243                    strm.SetLength(0);
244                }
245
246                //Remove the file.
247                RemoveFile(info);
248            }
[208]249
250            //If the user requested a folder removal, do it.
251            if (target is Task.Folder)
252            {
253                Task.Folder fldr = (Task.Folder)target;
254                if (fldr.DeleteIfEmpty)
255                    RemoveFolder(new DirectoryInfo(fldr.Path));
256            }
[180]257        }
258
259        /// <summary>
[208]260        /// Securely removes files.
[180]261        /// </summary>
262        /// <param name="info">The FileInfo object representing the file.</param>
263        private void RemoveFile(FileInfo info)
264        {
265            //Set the date of the file to be invalid to prevent forensic
266            //detection
267            info.CreationTime = info.LastWriteTime = info.LastAccessTime =
[198]268                new DateTime(1800, 1, 1, 0, 0, 0);
[180]269            info.Attributes = FileAttributes.Normal;
270            info.Attributes = FileAttributes.NotContentIndexed;
271
272            //Rename the file a few times to erase the record from the MFT.
[206]273            for (int i = 0; i < FilenameErasePasses; ++i)
[180]274            {
275                //Get a random file name
[198]276                PRNG prng = PRNGManager.GetInstance(Globals.Settings.ActivePRNG);
[180]277                byte[] newFileNameAry = new byte[info.Name.Length];
278                prng.NextBytes(newFileNameAry);
279
280                //Validate the name
281                const string validFileNameChars = "0123456789abcdefghijklmnopqrs" +
282                    "tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
[198]283                for (int j = 0, k = newFileNameAry.Length; j < k; ++j)
284                    newFileNameAry[j] = (byte)validFileNameChars[
285                        (int)newFileNameAry[j] % validFileNameChars.Length];
[180]286
287                //Rename the file.
[198]288                string newPath = info.DirectoryName + Path.DirectorySeparatorChar +
289                    (new System.Text.UTF8Encoding()).GetString(newFileNameAry);
290                info.MoveTo(newPath);
[180]291            }
[198]292
293            //Then delete the file.
294            info.Delete();
[180]295        }
296
[208]297        private void RemoveFolder(DirectoryInfo info)
298        {
299            foreach (FileInfo file in info.GetFiles())
300                RemoveFile(file);
301            foreach (DirectoryInfo dir in info.GetDirectories())
302                RemoveFolder(dir);
303
304            //Then clean up this folder.
305            for (int i = 0; i < FilenameErasePasses; ++i)
306            {
307                //Get a random file name
308                PRNG prng = PRNGManager.GetInstance(Globals.Settings.ActivePRNG);
309                byte[] newFileNameAry = new byte[info.Name.Length];
310                prng.NextBytes(newFileNameAry);
311
312                //Validate the name
313                const string validFileNameChars = "0123456789abcdefghijklmnopqrs" +
314                    "tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
315                for (int j = 0, k = newFileNameAry.Length; j < k; ++j)
316                    newFileNameAry[j] = (byte)validFileNameChars[
317                        (int)newFileNameAry[j] % validFileNameChars.Length];
318
319                //Rename the folder.
320                string newPath = info.Parent.FullName + Path.DirectorySeparatorChar +
321                    (new System.Text.UTF8Encoding()).GetString(newFileNameAry);
322                info.MoveTo(newPath);
323            }
324
325            //Remove the folder
326            info.Delete();
327        }
328
[180]329        /// <summary>
[163]330        /// The thread object.
331        /// </summary>
[148]332        private Thread thread;
333
[163]334        /// <summary>
335        /// The lock preventing concurrent access for the tasks list and the
336        /// tasks queue.
337        /// </summary>
[152]338        private object tasksLock = new object();
[163]339
340        /// <summary>
341        /// The list of tasks. Includes all immediate, reboot, and recurring tasks
342        /// </summary>
[148]343        private Dictionary<uint, Task> tasks = new Dictionary<uint, Task>();
[163]344
345        /// <summary>
346        /// The queue of tasks. This queue is executed when the first element's
347        /// timestamp (the key) has been past. This list assumes that all tasks
348        /// are sorted by timestamp, smallest one first.
349        /// </summary>
[148]350        private SortedList<DateTime, Task> scheduledTasks =
351            new SortedList<DateTime, Task>();
352
[163]353        /// <summary>
354        /// The list of task IDs for recycling.
355        /// </summary>
[148]356        private List<uint> unusedIds = new List<uint>();
[163]357
358        /// <summary>
359        /// Lock preventing concurrent access for the IDs.
360        /// </summary>
[148]361        private object unusedIdsLock = new object();
[163]362
363        /// <summary>
364        /// Incrementing ID. This value is incremented by one every time an ID
365        /// is required by no unused IDs remain.
366        /// </summary>
[148]367        private uint nextId = 0;
[152]368
[163]369        /// <summary>
370        /// An automatically reset event allowing the addition of new tasks to
371        /// interrupt the thread's sleeping state waiting for the next recurring
372        /// task to be due.
373        /// </summary>
[152]374        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
[148]375    }
376}
Note: See TracBrowser for help on using the repository browser.