source: trunk/eraser/Eraser.DefaultPlugins/ErasureTargets/FolderErasureTarget.cs @ 2969

Revision 2964, 9.5 KB checked in by gtrant, 5 weeks ago (diff)

Fix for DeleteIfEmpty? not being correctly set on exit

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Rev URL
Line 
1/*
2 * $Id$
3 * Copyright 2008-2015 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.Text.RegularExpressions;
28using System.Runtime.Serialization;
29using System.Runtime.InteropServices;
30using System.Xml;
31using System.Xml.Serialization;
32using System.Security.Permissions;
33using System.IO;
34using System.Globalization;
35
36using Eraser.Util;
37using Eraser.Plugins;
38using Eraser.Plugins.ExtensionPoints;
39
40namespace Eraser.DefaultPlugins
41{
42    /// <summary>
43    /// Represents a folder and its files which are to be erased.
44    /// </summary>
45    [Serializable]
46    [Guid("F50B0A44-3AB1-4cab-B81E-1713AC3D28C9")]
47    public class FolderErasureTarget : FileSystemObjectErasureTarget
48    {
49        #region Serialization code
50        protected FolderErasureTarget(SerializationInfo info, StreamingContext context)
51            : base(info, context)
52        {
53            IncludeMask = (string)info.GetValue("IncludeMask", typeof(string));
54            ExcludeMask = (string)info.GetValue("ExcludeMask", typeof(string));
55            DeleteIfEmpty = (bool)info.GetValue("DeleteIfEmpty", typeof(bool));
56        }
57
58        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
59        public override void GetObjectData(SerializationInfo info, StreamingContext context)
60        {
61            base.GetObjectData(info, context);
62            info.AddValue("IncludeMask", IncludeMask);
63            info.AddValue("ExcludeMask", ExcludeMask);
64            info.AddValue("DeleteIfEmpty", DeleteIfEmpty);
65        }
66
67        protected override void ReadXml(XmlReader reader, bool advance)
68        {
69           
70            IncludeMask = reader.GetAttribute("includeMask");
71            ExcludeMask = reader.GetAttribute("excludeMask");
72            bool deleteIfEmpty = true;
73            bool.TryParse(reader.GetAttribute("deleteIfEmpty"), out deleteIfEmpty);
74            DeleteIfEmpty = deleteIfEmpty;
75
76            base.ReadXml(reader, false);
77           
78            /*if (reader.HasAttributes)
79            {
80                bool deleteIfEmpty = true;
81                bool.TryParse(reader.GetAttribute("deleteIfEmpty"), out deleteIfEmpty);
82                DeleteIfEmpty = deleteIfEmpty;
83            }*/
84
85            if (advance)
86                reader.Read();
87        }
88
89        public override void WriteXml(XmlWriter writer)
90        {
91            writer.WriteAttributeString("includeMask", IncludeMask);
92            writer.WriteAttributeString("excludeMask", ExcludeMask);
93            writer.WriteAttributeString("deleteIfEmpty",
94                DeleteIfEmpty.ToString(CultureInfo.InvariantCulture));
95            base.WriteXml(writer);
96        }
97        #endregion
98
99        /// <summary>
100        /// Constructor.
101        /// </summary>
102        public FolderErasureTarget()
103        {
104            IncludeMask = string.Empty;
105            ExcludeMask = string.Empty;
106            DeleteIfEmpty = true;
107        }
108
109        public override Guid Guid
110        {
111            get { return GetType().GUID; }
112        }
113
114        public override string Name
115        {
116            get { return S._("Files in Folder"); }
117        }
118
119        public override IErasureTargetConfigurer Configurer
120        {
121            get { return new FolderErasureTargetConfigurer(); }
122        }
123
124        protected override List<StreamInfo> GetPaths()
125        {
126            //Get a list to hold all the resulting streams.
127            List<StreamInfo> result = new List<StreamInfo>();
128
129            //Open the root of the search, including every file matching the pattern
130            DirectoryInfo dir = new DirectoryInfo(Path);
131
132            //List recursively all the files which match the include pattern.
133            FileInfo[] files = GetFiles(dir);
134
135            //Then exclude each file and finalize the list and total file size
136            Regex includePattern = string.IsNullOrEmpty(IncludeMask) ? null :
137                new Regex(
138                    Regex.Escape(ExcludeMask).Replace("\\*", ".*").Replace("\\?", "."),
139                    RegexOptions.IgnoreCase | RegexOptions.Compiled);
140            Regex excludePattern = string.IsNullOrEmpty(ExcludeMask) ? null :
141                new Regex(
142                    Regex.Escape(ExcludeMask).Replace("\\*", ".*").Replace("\\?", "."),
143                    RegexOptions.IgnoreCase | RegexOptions.Compiled);
144            foreach (FileInfo file in files)
145            {
146                //Check that the file is included
147                if (includePattern != null && !includePattern.Match(file.FullName).Success)
148                    continue;
149
150                //Check that the file is not excluded
151                if (excludePattern != null && excludePattern.Match(file.FullName).Success)
152                    continue;
153
154                try
155                {
156                    //Add the size of the file and its alternate data streams
157                    result.AddRange(GetPathADSes(file));
158
159                    //And the file itself
160                    result.Add(new StreamInfo(file.FullName));
161                }
162                catch (FileNotFoundException)
163                {
164                    Logger.Log(S._("The file {0} was not erased because it was deleted " +
165                        "before it could be erased.", file.FullName), LogLevel.Information);
166                }
167                catch (DirectoryNotFoundException)
168                {
169                    Logger.Log(S._("The file {0} was not erased because the containing " +
170                        "directory was deleted before it could be erased.", file.FullName),
171                        LogLevel.Information);
172                }
173                catch (SharingViolationException)
174                {
175                    Logger.Log(S._("Could not list the Alternate Data Streams for file {0} " +
176                        "because the file is being used by another process. The file will not " +
177                        "be erased.", file.FullName), LogLevel.Error);
178                }
179            }
180
181            //Return the filtered list.
182            return result;
183        }
184
185        /// <summary>
186        /// A wildcard expression stating the condition for the set of files to include.
187        /// The include mask is applied before the exclude mask is applied. If this value
188        /// is empty, all files and folders within the folder specified is included.
189        /// </summary>
190        public string IncludeMask { get; set; }
191
192        /// <summary>
193        /// A wildcard expression stating the condition for removing files from the set
194        /// of included files. If this value is omitted, all files and folders extracted
195        /// by the inclusion mask is erased.
196        /// </summary>
197        public string ExcludeMask { get; set; }
198
199        /// <summary>
200        /// Determines if Eraser should delete the folder after the erase process.
201        /// </summary>
202        public bool DeleteIfEmpty { get; set; }
203
204        public override void Execute()
205        {
206            Progress = new SteppedProgressManager();
207
208            try
209            {
210                base.Execute();
211
212                //Remove the contents of the folder, deleting the folder if it is empty
213                //at the end of it.
214                EraseFolder();
215            }
216            finally
217            {
218                Progress = null;
219            }
220        }
221
222        /// <summary>
223        /// Erases the folder after all files have been deleted. This folder does not
224        /// delete folders which have files within it.
225        /// </summary>
226        private void EraseFolder()
227        {
228            //Update the progress to show that folders are being removed.
229            ProgressManager step = new ProgressManager();
230            Progress.Steps.Add(new SteppedProgressManagerStep(step,
231                0.0f, S._("Removing folders...")));
232
233            //Erase all subdirectories which are not reparse points.
234            DirectoryInfo directory = new DirectoryInfo(Path);
235            if ((directory.Attributes & FileAttributes.ReparsePoint) == 0)
236                foreach (DirectoryInfo subDir in directory.GetDirectories())
237                    EraseFolder(subDir, step);
238
239            //Does the user want this directory to be erased if there are no more
240            //entries within it?
241            if (DeleteIfEmpty)
242            {
243                //See if this is the root of a volume.
244                bool isVolumeRoot = directory.Parent == null;
245                foreach (VolumeInfo volume in VolumeInfo.Volumes)
246                    if (volume.IsReady)
247                        foreach (DirectoryInfo mountPoint in volume.MountPoints)
248                            if (directory.FullName == mountPoint.FullName)
249                                isVolumeRoot = true;
250
251                //If the folder is a mount point, then don't delete it. If it isn't,
252                //search for files under the folder to see if it is empty.
253                if (!isVolumeRoot && directory.Exists)
254                {
255                    IFileSystem fsManager = Host.Instance.FileSystems[
256                        VolumeInfo.FromMountPoint(Path)];
257                    if ((directory.Attributes & FileAttributes.ReparsePoint) == 0)
258                    {
259                        if (directory.GetFiles("*", SearchOption.AllDirectories).Length == 0)
260                            fsManager.DeleteFolder(directory, true);
261                    }
262                    else
263                    {
264                        fsManager.DeleteFolder(directory, false);
265                    }
266                }
267            }
268        }
269
270        private void EraseFolder(DirectoryInfo info, ProgressManager progress)
271        {
272            //Skip all symbolic links and junctions as we want to retain the
273            //contents of those directories.
274            if ((info.Attributes & FileAttributes.ReparsePoint) != 0)
275                return;
276
277            //Iterate over each directory and erase the subdirectories.
278            foreach (DirectoryInfo subDir in info.GetDirectories())
279                EraseFolder(subDir, progress);
280
281            //Public progress updates.
282            progress.Tag = info.FullName;
283
284            //Ensure that the current directory is empty before deleting.
285            FileSystemInfo[] files = info.GetFileSystemInfos();
286            if (files.Length == 0)
287            {
288                try
289                {
290                    Host.Instance.FileSystems[VolumeInfo.FromMountPoint(Path)].
291                        DeleteFolder(info, true);
292                }
293                catch (DirectoryNotFoundException)
294                {
295                    Logger.Log(new LogEntry(S._("The folder {0} was not erased because " +
296                        "the containing directory was deleted before it could be erased.",
297                        info.FullName), LogLevel.Information));
298                }
299                catch (UnauthorizedAccessException)
300                {
301                    Logger.Log(new LogEntry(S._("The folder {0} could not be deleted because " +
302                        "the folder's permissions prevents the deletion of the folder.",
303                        info.FullName), LogLevel.Error));
304                }
305            }
306        }
307    }
308}
Note: See TracBrowser for help on using the repository browser.