source: branches/eraser6/XmlTaskLists/Eraser.DefaultPlugins/ErasureTargets/FolderErasureTarget.cs @ 2596

Revision 2596, 8.3 KB checked in by lowjoel, 3 years ago (diff)

*Properly* follow the contract specified in IXmlSerializable.ReadXml?: the reader is placed at the start of our element, and our ReadXml? implementation must read the entire element to the end of the element before returning.

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