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.

Line 
1using System;
2using System.Collections.Generic;
3using System.Collections.Specialized;
4using System.Text;
5using System.Threading;
6using System.IO;
7
8using Eraser.Util;
9
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>
16    public class DirectExecutor : Executor, IDisposable
17    {
18        public DirectExecutor()
19        {
20            thread = new Thread(delegate()
21            {
22                Main();
23            });
24
25            thread.Start();
26            Thread.Sleep(0);
27        }
28
29        void IDisposable.Dispose()
30        {
31            thread.Abort();
32            schedulerInterrupt.Set();
33        }
34
35        public override void AddTask(ref Task task)
36        {
37            lock (unusedIdsLock)
38            {
39                if (unusedIds.Count != 0)
40                {
41                    task.id = unusedIds[0];
42                    unusedIds.RemoveAt(0);
43                }
44                else
45                    task.id = ++nextId;
46            }
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            }
67        }
68
69        public override bool DeleteTask(uint taskId)
70        {
71            lock (tasksLock)
72            {
73                if (!tasks.ContainsKey(taskId))
74                    return false;
75
76                lock (unusedIdsLock)
77                    unusedIds.Add(taskId);
78                tasks.Remove(taskId);
79            }
80            return true;
81        }
82
83        public override Task GetTask(uint taskId)
84        {
85            lock (tasksLock)
86            {
87                if (!tasks.ContainsKey(taskId))
88                    return null;
89                return tasks[taskId];
90            }
91        }
92
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>
97        private void Main()
98        {
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                {
119                    try
120                    {
121                        //Broadcast the task started event.
122                        task.OnTaskStarted(new TaskEventArgs(task));
123
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                            }
143                    }
144                    catch (FatalException e)
145                    {
146                        task.LogEntry(new LogEntry(e.Message, LogLevel.FATAL));
147                    }
148                    finally
149                    {
150                        //And the task finished event.
151                        task.OnTaskFinished(new TaskEventArgs(task));
152
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                    }
157                }
158
159                //Wait for half a minute to check for the next scheduled task.
160                schedulerInterrupt.WaitOne(30000, false);
161            }
162        }
163
164        /// <summary>
165        /// Executes a unused space erase.
166        /// </summary>
167        /// <param name="target">The target of the unused space erase.</param>
168        private void EraseUnusedSpace(Task task, Task.UnusedSpace target)
169        {
170            throw new NotImplementedException("Unused space erasures are not "+
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();
181            TaskProgressEventArgs eventArgs = new TaskProgressEventArgs(task, 0, 0);
182
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
188            //Iterate over every path, and erase the path.
189            for (int i = 0; i < paths.Count; ++i)
190            {
191                //Update the task progress
192                eventArgs.overallProgress = (i * 100) / paths.Count;
193                eventArgs.currentTarget = target;
194                eventArgs.currentItemName = paths[i];
195                eventArgs.currentItemProgress = 0;
196                eventArgs.totalPasses = method.Passes;
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                {
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.
229                    method.Erase(strm, PRNGManager.GetInstance(Globals.Settings.ActivePRNG),
230                        delegate(float currentProgress, int currentPass)
231                        {
232                            eventArgs.currentPass = currentPass;
233                            eventArgs.currentItemProgress = (int)
234                                ((float)currentProgress * 100.0);
235                            eventArgs.overallProgress = (int)
236                                (((i + currentProgress) / (float)paths.Count) * 100);
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            }
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            }
257        }
258
259        /// <summary>
260        /// Securely removes files.
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 =
268                new DateTime(1800, 1, 1, 0, 0, 0);
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.
273            for (int i = 0; i < FilenameErasePasses; ++i)
274            {
275                //Get a random file name
276                PRNG prng = PRNGManager.GetInstance(Globals.Settings.ActivePRNG);
277                byte[] newFileNameAry = new byte[info.Name.Length];
278                prng.NextBytes(newFileNameAry);
279
280                //Validate the name
281                const string validFileNameChars = "0123456789abcdefghijklmnopqrs" +
282                    "tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
283                for (int j = 0, k = newFileNameAry.Length; j < k; ++j)
284                    newFileNameAry[j] = (byte)validFileNameChars[
285                        (int)newFileNameAry[j] % validFileNameChars.Length];
286
287                //Rename the file.
288                string newPath = info.DirectoryName + Path.DirectorySeparatorChar +
289                    (new System.Text.UTF8Encoding()).GetString(newFileNameAry);
290                info.MoveTo(newPath);
291            }
292
293            //Then delete the file.
294            info.Delete();
295        }
296
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
329        /// <summary>
330        /// The thread object.
331        /// </summary>
332        private Thread thread;
333
334        /// <summary>
335        /// The lock preventing concurrent access for the tasks list and the
336        /// tasks queue.
337        /// </summary>
338        private object tasksLock = new object();
339
340        /// <summary>
341        /// The list of tasks. Includes all immediate, reboot, and recurring tasks
342        /// </summary>
343        private Dictionary<uint, Task> tasks = new Dictionary<uint, Task>();
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>
350        private SortedList<DateTime, Task> scheduledTasks =
351            new SortedList<DateTime, Task>();
352
353        /// <summary>
354        /// The list of task IDs for recycling.
355        /// </summary>
356        private List<uint> unusedIds = new List<uint>();
357
358        /// <summary>
359        /// Lock preventing concurrent access for the IDs.
360        /// </summary>
361        private object unusedIdsLock = new object();
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>
367        private uint nextId = 0;
368
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>
374        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
375    }
376}
Note: See TracBrowser for help on using the repository browser.