source: branches/eraser6/pluginsRewrite/Eraser.DefaultPlugins/ErasureTargets/FileSystemObjectErasureTarget.cs @ 2470

Revision 2470, 11.3 KB checked in by lowjoel, 2 years ago (diff)

Compile fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26
27using System.Runtime.Serialization;
28using System.Runtime.InteropServices;
29using System.Security.Permissions;
30using System.IO;
31
32using Eraser.Util;
33using Eraser.Util.ExtensionMethods;
34using Eraser.Plugins;
35using Eraser.Plugins.ExtensionPoints;
36using Eraser.Plugins.Registrars;
37
38namespace Eraser.DefaultPlugins
39{
40    /// <summary>
41    /// Class representing a tangible object (file/folder) to be erased.
42    /// </summary>
43    [Serializable]
44    abstract class FileSystemObjectErasureTarget : ErasureTargetBase
45    {
46        #region Serialization code
47        protected FileSystemObjectErasureTarget(SerializationInfo info, StreamingContext context)
48            : base(info, context)
49        {
50            Path = (string)info.GetValue("Path", typeof(string));
51        }
52
53        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
54        public override void GetObjectData(SerializationInfo info, StreamingContext context)
55        {
56            base.GetObjectData(info, context);
57            info.AddValue("Path", Path);
58        }
59        #endregion
60
61        /// <summary>
62        /// Constructor.
63        /// </summary>
64        protected FileSystemObjectErasureTarget()
65            : base()
66        {
67        }
68
69        /// <summary>
70        /// The Progress of this target's erasure. This property must be set
71        /// before <see cref="FileSystemObjectErasureTarget.Execute"/> is called.
72        /// </summary>
73        protected new SteppedProgressManager Progress
74        {
75            get
76            {
77                return (SteppedProgressManager)base.Progress;
78            }
79            set
80            {
81                base.Progress = value;
82            }
83        }
84
85        /// <summary>
86        /// Retrieves the list of files/folders to erase as a list.
87        /// </summary>
88        /// <returns>A list containing the paths to all the files to be erased.</returns>
89        protected abstract List<StreamInfo> GetPaths();
90
91        /// <summary>
92        /// Gets all files in the provided directory.
93        /// </summary>
94        /// <param name="info">The directory to look files in.</param>
95        /// <returns>A list of files found in the directory.</returns>
96        /// <remarks>This function does not recurse into directories which are
97        /// reparse points.</remarks>
98        protected static FileInfo[] GetFiles(DirectoryInfo info)
99        {
100            List<FileInfo> result = new List<FileInfo>();
101            if (info.Exists && (info.Attributes & FileAttributes.ReparsePoint) == 0)
102            {
103                try
104                {
105                    foreach (DirectoryInfo dir in info.GetDirectories())
106                        result.AddRange(GetFiles(dir));
107                    result.AddRange(info.GetFiles());
108                }
109                catch (UnauthorizedAccessException e)
110                {
111                    Logger.Log(S._("Could not erase files and subfolders in {0} because {1}",
112                        info.FullName, e.Message), LogLevel.Error);
113                }
114            }
115
116            return result.ToArray();
117        }
118
119        /// <summary>
120        /// Adds ADSes of the given file to the list, forcing the open handles to the
121        /// files closed if necessary.
122        /// </summary>
123        /// <param name="file">The file to look for ADSes</param>
124        protected static StreamInfo[] GetPathADSes(FileInfo file)
125        {
126            try
127            {
128                return file.GetADSes().ToArray();
129            }
130            catch (FileNotFoundException)
131            {
132            }
133            catch (SharingViolationException)
134            {
135                //The system cannot open the file, try to force the file handle to close.
136                if (!Host.Instance.Settings.ForceUnlockLockedFiles)
137                    throw;
138
139                StringBuilder processStr = new StringBuilder();
140                foreach (OpenHandle handle in OpenHandle.Close(file.FullName))
141                {
142                    try
143                    {
144                        processStr.AppendFormat(
145                            System.Globalization.CultureInfo.InvariantCulture,
146                            "{0}, ", handle.Process.MainModule.FileName);
147                    }
148                    catch (System.ComponentModel.Win32Exception)
149                    {
150                        processStr.AppendFormat(
151                            System.Globalization.CultureInfo.InvariantCulture,
152                            "Process ID {0}, ", handle.Process.Id);
153                    }
154                }
155
156                if (processStr.Length == 0)
157                    return GetPathADSes(file);
158                else
159                    throw;
160            }
161            catch (UnauthorizedAccessException e)
162            {
163                //The system cannot read the file, assume no ADSes for lack of
164                //more information.
165                Logger.Log(e.Message, LogLevel.Error);
166            }
167
168            return new StreamInfo[0];
169        }
170
171        /// <summary>
172        /// The path to the file or folder referred to by this object.
173        /// </summary>
174        public string Path { get; set; }
175
176        public sealed override IErasureMethod EffectiveMethod
177        {
178            get
179            {
180                if (Method != ErasureMethodRegistrar.Default)
181                    return base.EffectiveMethod;
182
183                return Host.Instance.ErasureMethods[
184                    Host.Instance.Settings.DefaultFileErasureMethod];
185            }
186        }
187
188        public override string ToString()
189        {
190            return Path;
191        }
192
193        /// <summary>
194        /// Erases the streams returned by the <see cref="GetPaths"/> function.
195        /// </summary>
196        /// <remarks>The <see cref="Progress"/> property must be defined prior
197        /// to the execution of this function.</remarks>
198        public override void Execute()
199        {
200            //Retrieve the list of files to erase.
201            List<StreamInfo> paths = GetPaths();
202            long dataTotal = paths.Sum(x => x.Length);
203
204            //Set the event's current target status.
205            if (Progress == null)
206                throw new InvalidOperationException("The Progress property must not be null.");
207            Task.Progress.Steps.Add(new SteppedProgressManagerStep(Progress,
208                1.0f / Task.Targets.Count));
209
210            //Iterate over every path, and erase the path.
211            for (int i = 0; i < paths.Count; ++i)
212            {
213                ProgressManager step = new ProgressManager();
214                Progress.Steps.Add(new SteppedProgressManagerStep(step,
215                    dataTotal == 0 ? 0.0f : paths[i].Length / (float)dataTotal,
216                    S._("Erasing files...")));
217                EraseStream(paths[i], step);
218                step.MarkComplete();
219            }
220        }
221
222        /// <summary>
223        /// Erases the provided stream, and updates progress using the provided
224        /// progress manager.
225        /// </summary>
226        /// <param name="info">The information regarding the stream that needs erasure.</param>
227        /// <param name="progress">The progress manager for the erasure of the current
228        /// stream.</param>
229        protected void EraseStream(StreamInfo info, ProgressManager progress)
230        {
231            //Check that the file exists - we do not want to bother erasing nonexistant files
232            if (!info.Exists)
233            {
234                Logger.Log(S._("The file {0} was not erased as the file does not exist.",
235                    info.FileName), LogLevel.Notice);
236                return;
237            }
238
239            //Get the filesystem provider to handle the secure file erasures
240            IFileSystem fsManager = Host.Instance.FileSystems[
241                VolumeInfo.FromMountPoint(info.DirectoryName)];
242
243            bool isReadOnly = false;
244
245            try
246            {
247                //Update the task progress
248                IErasureMethod method = EffectiveMethod;
249                /*OnProgressChanged(this, new ProgressChangedEventArgs(progress,
250                    new TaskProgressChangedEventArgs(info.FullName, 0, method.Passes)));*/
251
252                //Remove the read-only flag, if it is set.
253                if (isReadOnly = info.IsReadOnly)
254                    info.IsReadOnly = false;
255
256                //Define the callback function for progress reporting.
257                ErasureMethodProgressFunction callback =
258                    delegate(long lastWritten, long totalData, int currentPass)
259                    {
260                        if (Task.Canceled)
261                            throw new OperationCanceledException(S._("The task was cancelled."));
262
263                        progress.Total = totalData;
264                        progress.Completed += lastWritten;
265                        /*OnProgressChanged(this, new ProgressChangedEventArgs(progress,
266                            new TaskProgressChangedEventArgs(info.FullName, currentPass, method.Passes)));*/
267                    };
268
269                TryEraseStream(fsManager, method, info, callback);
270
271                //Remove the file.
272                FileInfo fileInfo = info.File;
273                if (fileInfo != null)
274                    fsManager.DeleteFile(fileInfo);
275                progress.MarkComplete();
276            }
277            catch (UnauthorizedAccessException)
278            {
279                Logger.Log(S._("The file {0} could not be erased because the file's " +
280                    "permissions prevent access to the file.", info.FullName), LogLevel.Error);
281            }
282            catch (SharingViolationException e)
283            {
284                Logger.Log(e.Message, LogLevel.Error);
285            }
286            finally
287            {
288                //Re-set the read-only flag if the file exists (i.e. there was an error)
289                if (isReadOnly && info.Exists && !info.IsReadOnly)
290                    info.IsReadOnly = isReadOnly;
291            }
292        }
293
294        /// <summary>
295        /// Attempts to erase a stream, trying to close all open handles if a process has
296        /// a lock on the file.
297        /// </summary>
298        /// <param name="fsManager">The file system provider used to erase the stream.</param>
299        /// <param name="method">The erasure method to use to erase the stream.</param>
300        /// <param name="info">The stream to erase.</param>
301        /// <param name="callback">The erasure progress callback.</param>
302        private void TryEraseStream(IFileSystem fsManager, IErasureMethod method, StreamInfo info,
303            ErasureMethodProgressFunction callback)
304        {
305            for (int i = 0; ; ++i)
306            {
307                try
308                {
309                    //Make sure the file does not have any attributes which may affect
310                    //the erasure process
311                    if ((info.Attributes & FileAttributes.Compressed) != 0 ||
312                        (info.Attributes & FileAttributes.Encrypted) != 0 ||
313                        (info.Attributes & FileAttributes.SparseFile) != 0)
314                    {
315                        //Log the error
316                        Logger.Log(S._("The file {0} could not be erased because the file was " +
317                            "either compressed, encrypted or a sparse file.", info.FullName),
318                            LogLevel.Error);
319                        return;
320                    }
321
322                    //Do not erase reparse points, as they will cause other references to the file
323                    //to be to garbage.
324                    if ((info.Attributes & FileAttributes.ReparsePoint) == 0)
325                        fsManager.EraseFileSystemObject(info, method, callback);
326                    else
327                        Logger.Log(S._("The file {0} is a hard link or a symbolic link thus the " +
328                            "contents of the file was not erased.", LogLevel.Notice));
329                    return;
330                }
331                catch (SharingViolationException)
332                {
333                    if (!Host.Instance.Settings.ForceUnlockLockedFiles)
334                        throw;
335
336                    //Try closing all open handles. If it succeeds, we can run the erase again.
337                    //To prevent Eraser from deadlocking, we will only attempt this once. Some
338                    //programs may be aggressive and keep a handle open in a tight loop.
339                    List<OpenHandle> remainingHandles = OpenHandle.Close(info.FullName);
340                    if (i == 0 && remainingHandles.Count == 0)
341                        continue;
342
343                    //Either we could not close all instances, or we already tried twice. Report
344                    //the error.
345                    StringBuilder processStr = new StringBuilder();
346                    foreach (OpenHandle handle in remainingHandles)
347                    {
348                        try
349                        {
350                            processStr.AppendFormat(
351                                System.Globalization.CultureInfo.InvariantCulture,
352                                "{0}, ", handle.Process.MainModule.FileName);
353                        }
354                        catch (System.ComponentModel.Win32Exception)
355                        {
356                            processStr.AppendFormat(
357                                System.Globalization.CultureInfo.InvariantCulture,
358                                "Process ID {0}, ", handle.Process.Id);
359                        }
360                    }
361
362                    throw new SharingViolationException(S._(
363                        "Could not force closure of file \"{0}\" {1}", info.FileName,
364                        S._("(locked by {0})",
365                            processStr.ToString().Remove(processStr.Length - 2)).Trim()),
366                        info.FileName);
367                }
368            }
369        }
370    }
371}
Note: See TracBrowser for help on using the repository browser.