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

Revision 193, 9.2 KB checked in by lowjoel, 6 years ago (diff)

Don't directly utilize the DefaultMethod? variable.

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            //Get the erasure method if the user specified he wants the default.
174            ErasureMethod method = target.Method;
175            if (method == ErasureMethodManager.Default)
176                method = ErasureMethodManager.GetInstance(Globals.Settings.DefaultFileErasureMethod);
177
178            //Iterate over every path, and erase the path.
179            for (int i = 0; i < paths.Count; ++i)
180            {
181                //Update the task progress
182                eventArgs.overallProgress = (uint)(i * 100) / (uint)paths.Count;
183                eventArgs.currentItemName = paths[i];
184                eventArgs.currentItemProgress = 0;
185                eventArgs.totalPasses = method.Passes;
186                task.OnProgressChanged(eventArgs);
187
188                //Make sure the file does not have any attributes which may
189                //affect the erasure process
190                FileInfo info = new FileInfo(paths[i]);
191                if ((info.Attributes & FileAttributes.Compressed) != 0 ||
192                    (info.Attributes & FileAttributes.Encrypted) != 0 ||
193                    (info.Attributes & FileAttributes.SparseFile) != 0 ||
194                    (info.Attributes & FileAttributes.ReparsePoint) != 0)
195                {
196                    //Log the error
197                    throw new ArgumentException("Compressed, encrypted, or sparse" +
198                        "files cannot be erased with Eraser.");
199                }
200
201                //Remove the read-only flag, if it is set.
202                if ((info.Attributes & FileAttributes.ReadOnly) != 0)
203                    info.Attributes &= ~FileAttributes.ReadOnly;
204
205                //Create the file stream, and call the erasure method
206                //to write to the stream.
207                using (FileStream strm = new FileStream(info.FullName,
208                    FileMode.Open, FileAccess.Write, FileShare.None,
209                    8, FileOptions.WriteThrough))
210                {
211                    method.Erase(strm, PRNGManager.GetInstance(Globals.Settings.ActivePRNG),
212                        delegate(uint currentProgress, uint currentPass)
213                        {
214                            eventArgs.currentPass = currentPass;
215                            eventArgs.currentItemProgress = currentProgress;
216                            task.OnProgressChanged(eventArgs);
217                        }
218                    );
219
220                    //Set the length of the file to 0.
221                    strm.Seek(0, SeekOrigin.Begin);
222                    strm.SetLength(0);
223                }
224
225                //Remove the file.
226                RemoveFile(info);
227            }
228        }
229
230        /// <summary>
231        /// Securely removes the filename of the file.
232        /// </summary>
233        /// <param name="info">The FileInfo object representing the file.</param>
234        private void RemoveFile(FileInfo info)
235        {
236            //Set the date of the file to be invalid to prevent forensic
237            //detection
238            info.CreationTime = info.LastWriteTime = info.LastAccessTime =
239                DateTime.MinValue;
240            info.Attributes = FileAttributes.Normal;
241            info.Attributes = FileAttributes.NotContentIndexed;
242
243            //Rename the file a few times to erase the record from the MFT.
244            for (uint i = 0; i < FilenameErasePasses; ++i)
245            {
246                //Get a random file name
247                PRNG prng = null;
248                byte[] newFileNameAry = new byte[info.Name.Length];
249                prng.NextBytes(newFileNameAry);
250                string newFileName = (new System.Text.ASCIIEncoding()).
251                    GetString(newFileNameAry);
252
253                //Validate the name
254                const string validFileNameChars = "0123456789abcdefghijklmnopqrs" +
255                    "tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
256                for (int j = 0, k = newFileName.Length; j < k; ++j)
257                    if (!Char.IsLetterOrDigit(newFileName[j]))
258                    {
259                        newFileName.Insert(j, validFileNameChars[
260                            (int)newFileName[j] % validFileNameChars.Length].ToString());
261                        newFileName.Remove(j + 1, 1);
262                    }
263
264                //Rename the file.
265                info.MoveTo(info.DirectoryName + Path.DirectorySeparatorChar + newFileName);
266            }
267        }
268
269        /// <summary>
270        /// The thread object.
271        /// </summary>
272        private Thread thread;
273
274        /// <summary>
275        /// The lock preventing concurrent access for the tasks list and the
276        /// tasks queue.
277        /// </summary>
278        private object tasksLock = new object();
279
280        /// <summary>
281        /// The list of tasks. Includes all immediate, reboot, and recurring tasks
282        /// </summary>
283        private Dictionary<uint, Task> tasks = new Dictionary<uint, Task>();
284
285        /// <summary>
286        /// The queue of tasks. This queue is executed when the first element's
287        /// timestamp (the key) has been past. This list assumes that all tasks
288        /// are sorted by timestamp, smallest one first.
289        /// </summary>
290        private SortedList<DateTime, Task> scheduledTasks =
291            new SortedList<DateTime, Task>();
292
293        /// <summary>
294        /// The list of task IDs for recycling.
295        /// </summary>
296        private List<uint> unusedIds = new List<uint>();
297
298        /// <summary>
299        /// Lock preventing concurrent access for the IDs.
300        /// </summary>
301        private object unusedIdsLock = new object();
302
303        /// <summary>
304        /// Incrementing ID. This value is incremented by one every time an ID
305        /// is required by no unused IDs remain.
306        /// </summary>
307        private uint nextId = 0;
308
309        /// <summary>
310        /// An automatically reset event allowing the addition of new tasks to
311        /// interrupt the thread's sleeping state waiting for the next recurring
312        /// task to be due.
313        /// </summary>
314        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
315    }
316}
Note: See TracBrowser for help on using the repository browser.