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

Revision 206, 9.7 KB checked in by lowjoel, 7 years ago (diff)

Don't be overparanoid with the use of uint.

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