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

Revision 198, 9.6 KB checked in by lowjoel, 6 years ago (diff)

-Round up the stream size to the cluster size so cluster tips are erased.
-Rename the file 7 times to erase the MFT record

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(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 = (uint)(i * 100) / (uint)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(uint currentProgress, uint currentPass)
222                        {
223                            eventArgs.currentPass = currentPass;
224                            eventArgs.currentItemProgress = currentProgress;
225                            task.OnProgressChanged(eventArgs);
226                        }
227                    );
228
229                    //Set the length of the file to 0.
230                    strm.Seek(0, SeekOrigin.Begin);
231                    strm.SetLength(0);
232                }
233
234                //Remove the file.
235                RemoveFile(info);
236            }
237        }
238
239        /// <summary>
240        /// Securely removes the filename of the file.
241        /// </summary>
242        /// <param name="info">The FileInfo object representing the file.</param>
243        private void RemoveFile(FileInfo info)
244        {
245            //Set the date of the file to be invalid to prevent forensic
246            //detection
247            info.CreationTime = info.LastWriteTime = info.LastAccessTime =
248                new DateTime(1800, 1, 1, 0, 0, 0);
249            info.Attributes = FileAttributes.Normal;
250            info.Attributes = FileAttributes.NotContentIndexed;
251
252            //Rename the file a few times to erase the record from the MFT.
253            for (uint i = 0; i < FilenameErasePasses; ++i)
254            {
255                //Get a random file name
256                PRNG prng = PRNGManager.GetInstance(Globals.Settings.ActivePRNG);
257                byte[] newFileNameAry = new byte[info.Name.Length];
258                prng.NextBytes(newFileNameAry);
259
260                //Validate the name
261                const string validFileNameChars = "0123456789abcdefghijklmnopqrs" +
262                    "tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
263                for (int j = 0, k = newFileNameAry.Length; j < k; ++j)
264                    newFileNameAry[j] = (byte)validFileNameChars[
265                        (int)newFileNameAry[j] % validFileNameChars.Length];
266
267                //Rename the file.
268                string newPath = info.DirectoryName + Path.DirectorySeparatorChar +
269                    (new System.Text.UTF8Encoding()).GetString(newFileNameAry);
270                info.MoveTo(newPath);
271            }
272
273            //Then delete the file.
274            info.Delete();
275        }
276
277        /// <summary>
278        /// The thread object.
279        /// </summary>
280        private Thread thread;
281
282        /// <summary>
283        /// The lock preventing concurrent access for the tasks list and the
284        /// tasks queue.
285        /// </summary>
286        private object tasksLock = new object();
287
288        /// <summary>
289        /// The list of tasks. Includes all immediate, reboot, and recurring tasks
290        /// </summary>
291        private Dictionary<uint, Task> tasks = new Dictionary<uint, Task>();
292
293        /// <summary>
294        /// The queue of tasks. This queue is executed when the first element's
295        /// timestamp (the key) has been past. This list assumes that all tasks
296        /// are sorted by timestamp, smallest one first.
297        /// </summary>
298        private SortedList<DateTime, Task> scheduledTasks =
299            new SortedList<DateTime, Task>();
300
301        /// <summary>
302        /// The list of task IDs for recycling.
303        /// </summary>
304        private List<uint> unusedIds = new List<uint>();
305
306        /// <summary>
307        /// Lock preventing concurrent access for the IDs.
308        /// </summary>
309        private object unusedIdsLock = new object();
310
311        /// <summary>
312        /// Incrementing ID. This value is incremented by one every time an ID
313        /// is required by no unused IDs remain.
314        /// </summary>
315        private uint nextId = 0;
316
317        /// <summary>
318        /// An automatically reset event allowing the addition of new tasks to
319        /// interrupt the thread's sleeping state waiting for the next recurring
320        /// task to be due.
321        /// </summary>
322        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
323    }
324}
Note: See TracBrowser for help on using the repository browser.