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

Revision 191, 9.0 KB checked in by lowjoel, 7 years ago (diff)

Handle FatalExceptions? in the DirectExecutor? thread.

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