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

Revision 184, 8.7 KB checked in by lowjoel, 6 years ago (diff)

Removed the Executor.GetIterator? function since it's rather... weird.

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                    //Run the task
118                    foreach (Task.ErasureTarget target in task.Entries)
119                    try
120                    {
121                        if (target is Task.UnusedSpace)
122                            EraseUnusedSpace(task, (Task.UnusedSpace)target);
123                        else if (target is Task.FilesystemObject)
124                            EraseFilesystemObject(task, (Task.FilesystemObject)target);
125                        else
126                            throw new ArgumentException("Unknown erasure target.");
127                    }
128                    catch (Exception e)
129                    {
130                        task.LogEntry(new LogEntry(e.Message, LogLevel.ERROR));
131                    }
132
133                    //If the task is a recurring task, reschedule it since we are done.
134                    if (task.Schedule is RecurringSchedule)
135                        ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now);
136                }
137
138                //Wait for half a minute to check for the next scheduled task.
139                schedulerInterrupt.WaitOne(30000, false);
140            }
141        }
142
143        /// <summary>
144        /// Executes a unused space erase.
145        /// </summary>
146        /// <param name="target">The target of the unused space erase.</param>
147        private void EraseUnusedSpace(Task task, Task.UnusedSpace target)
148        {
149            throw new NotImplementedException("Unused space erasures are not "+
150                "currently implemented");
151        }
152
153        /// <summary>
154        /// Erases a file or folder on the volume.
155        /// </summary>
156        /// <param name="target">The target of the erasure.</param>
157        private void EraseFilesystemObject(Task task, Task.FilesystemObject target)
158        {
159            List<string> paths = target.GetPaths();
160            TaskProgressEventArgs eventArgs = new TaskProgressEventArgs(0, 0);
161
162            //Iterate over every path, and erase the path.
163            for (int i = 0; i < paths.Count; ++i)
164            {
165                //Update the task progress
166                eventArgs.overallProgress = (uint)(i * 100) / (uint)paths.Count;
167                eventArgs.currentItemName = paths[i];
168                eventArgs.currentItemProgress = 0;
169                eventArgs.totalPasses = target.Method.Passes;
170                task.OnProgressChanged(eventArgs);
171
172                //Make sure the file does not have any attributes which may
173                //affect the erasure process
174                FileInfo info = new FileInfo(paths[i]);
175                if ((info.Attributes & FileAttributes.Compressed) != 0 ||
176                    (info.Attributes & FileAttributes.Encrypted) != 0 ||
177                    (info.Attributes & FileAttributes.SparseFile) != 0 ||
178                    (info.Attributes & FileAttributes.ReparsePoint) != 0)
179                {
180                    //Log the error
181                    throw new ArgumentException("Compressed, encrypted, or sparse" +
182                        "files cannot be erased with Eraser.");
183                }
184
185                //Remove the read-only flag, if it is set.
186                if ((info.Attributes & FileAttributes.ReadOnly) != 0)
187                    info.Attributes &= ~FileAttributes.ReadOnly;
188
189                //Create the file stream, and call the erasure method
190                //to write to the stream.
191                using (FileStream strm = new FileStream(info.FullName,
192                    FileMode.Open, FileAccess.Write, FileShare.None,
193                    8, FileOptions.WriteThrough))
194                {
195                    target.Method.Erase(strm, null,
196                        delegate(uint currentProgress, uint currentPass)
197                        {
198                            eventArgs.currentPass = currentPass;
199                            eventArgs.currentItemProgress = currentProgress;
200                            task.OnProgressChanged(eventArgs);
201                        }
202                    );
203
204                    //Set the length of the file to 0.
205                    strm.Seek(0, SeekOrigin.Begin);
206                    strm.SetLength(0);
207                }
208
209                //Remove the file.
210                RemoveFile(info);
211            }
212        }
213
214        /// <summary>
215        /// Securely removes the filename of the file.
216        /// </summary>
217        /// <param name="info">The FileInfo object representing the file.</param>
218        private void RemoveFile(FileInfo info)
219        {
220            //Set the date of the file to be invalid to prevent forensic
221            //detection
222            info.CreationTime = info.LastWriteTime = info.LastAccessTime =
223                DateTime.MinValue;
224            info.Attributes = FileAttributes.Normal;
225            info.Attributes = FileAttributes.NotContentIndexed;
226
227            //Rename the file a few times to erase the record from the MFT.
228            for (uint i = 0; i < FilenameErasePasses; ++i)
229            {
230                //Get a random file name
231                PRNG prng = null;
232                byte[] newFileNameAry = new byte[info.Name.Length];
233                prng.NextBytes(newFileNameAry);
234                string newFileName = (new System.Text.ASCIIEncoding()).
235                    GetString(newFileNameAry);
236
237                //Validate the name
238                const string validFileNameChars = "0123456789abcdefghijklmnopqrs" +
239                    "tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
240                for (int j = 0, k = newFileName.Length; j < k; ++j)
241                    if (!Char.IsLetterOrDigit(newFileName[j]))
242                    {
243                        newFileName.Insert(j, validFileNameChars[
244                            (int)newFileName[j] % validFileNameChars.Length].ToString());
245                        newFileName.Remove(j + 1, 1);
246                    }
247
248                //Rename the file.
249                info.MoveTo(info.DirectoryName + Path.DirectorySeparatorChar + newFileName);
250            }
251        }
252
253        /// <summary>
254        /// The thread object.
255        /// </summary>
256        private Thread thread;
257
258        /// <summary>
259        /// The lock preventing concurrent access for the tasks list and the
260        /// tasks queue.
261        /// </summary>
262        private object tasksLock = new object();
263
264        /// <summary>
265        /// The list of tasks. Includes all immediate, reboot, and recurring tasks
266        /// </summary>
267        private Dictionary<uint, Task> tasks = new Dictionary<uint, Task>();
268
269        /// <summary>
270        /// The queue of tasks. This queue is executed when the first element's
271        /// timestamp (the key) has been past. This list assumes that all tasks
272        /// are sorted by timestamp, smallest one first.
273        /// </summary>
274        private SortedList<DateTime, Task> scheduledTasks =
275            new SortedList<DateTime, Task>();
276
277        /// <summary>
278        /// The list of task IDs for recycling.
279        /// </summary>
280        private List<uint> unusedIds = new List<uint>();
281
282        /// <summary>
283        /// Lock preventing concurrent access for the IDs.
284        /// </summary>
285        private object unusedIdsLock = new object();
286
287        /// <summary>
288        /// Incrementing ID. This value is incremented by one every time an ID
289        /// is required by no unused IDs remain.
290        /// </summary>
291        private uint nextId = 0;
292
293        /// <summary>
294        /// An automatically reset event allowing the addition of new tasks to
295        /// interrupt the thread's sleeping state waiting for the next recurring
296        /// task to be due.
297        /// </summary>
298        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
299    }
300}
Note: See TracBrowser for help on using the repository browser.