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

Revision 2561, 7.5 KB checked in by lowjoel, 2 years ago (diff)

Part 2 fix for https://eraser.heidi.ie/forum/viewtopic.php?f=2&t=8684&p=25969#p25966 since erasing the actual reparse point would cause Eraser to crash if the reparse point references an invalid target.

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