source: branches/eraser6/XmlTaskLists/Eraser.DefaultPlugins/ErasureTargets/SecureMoveErasureTarget.cs @ 2584

Revision 2584, 10.7 KB checked in by lowjoel, 3 years ago (diff)

Switch the Default Plugin's erasure targets to follow the new IXmlSerializable interface.

  • 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.Runtime.InteropServices;
28using System.Xml;
29using System.Xml.Serialization;
30using System.IO;
31
32using Eraser.Util;
33using Eraser.Util.ExtensionMethods;
34using Eraser.Plugins;
35using Eraser.Plugins.ExtensionPoints;
36
37namespace Eraser.DefaultPlugins
38{
39    /// <summary>
40    /// Class representing a path that needs to be moved.
41    /// </summary>
42    [Serializable]
43    [Guid("18FB3523-4012-4718-8B9A-BADAA9084214")]
44    class SecureMoveErasureTarget : FileSystemObjectErasureTarget
45    {
46        #region Serialization code
47        public override void ReadXml(XmlReader reader)
48        {
49            base.ReadXml(reader);
50            Destination = reader.GetAttribute("destination");
51        }
52
53        public override void WriteXml(XmlWriter writer)
54        {
55            base.WriteXml(writer);
56            writer.WriteAttributeString("destination", Destination);
57        }
58        #endregion
59
60        public SecureMoveErasureTarget()
61        {
62        }
63
64        public override Guid Guid
65        {
66            get { return GetType().GUID; }
67        }
68
69        public override string Name
70        {
71            get { return S._("Secure move"); }
72        }
73
74        public override string ToString()
75        {
76            return S._("Securely move {0}", Path);
77        }
78
79        /// <summary>
80        /// The destination of the move.
81        /// </summary>
82        public string Destination
83        {
84            get;
85            set;
86        }
87
88        public override IErasureTargetConfigurer Configurer
89        {
90            get { return new SecureMoveErasureTargetConfigurer(); }
91        }
92
93        protected override List<StreamInfo> GetPaths()
94        {
95            List<StreamInfo> result = new List<StreamInfo>();
96            if (!(File.Exists(Path) || Directory.Exists(Path)))
97                return result;
98
99            FileInfo[] files = null;
100            if ((File.GetAttributes(Path) & FileAttributes.Directory) == 0)
101                files = new FileInfo[] { new FileInfo(Path) };
102            else
103                files = GetFiles(new DirectoryInfo(Path));
104
105            foreach (FileInfo info in files)
106            {
107                //Add the alternate data streams
108                result.AddRange(GetPathADSes(info));
109
110                //And the file itself
111                result.Add(new StreamInfo(info.FullName));
112            }
113
114            return result;
115        }
116
117        public override void Execute()
118        {
119            //If the path doesn't exist, exit.
120            if (!(File.Exists(Path) || Directory.Exists(Path)))
121                return;
122
123            //Create the progress manager.
124            Progress = new SteppedProgressManager();
125           
126            try
127            {
128                //Depending on whether the path is a file or directory, execute the
129                //correct function.
130                if ((File.GetAttributes(Path) & FileAttributes.Directory) != 0)
131                {
132                    DirectoryInfo info = new DirectoryInfo(Path);
133                    CopyDirectory(info);
134                }
135                else
136                {
137                    FileInfo info = new FileInfo(Path);
138                    CopyFile(info);
139                }
140            }
141            finally
142            {
143                Progress = null;
144            }
145        }
146
147        private void CopyDirectory(DirectoryInfo info)
148        {
149            //Check the the destination is not a subfolder of the source.
150            if (PathUtil.IsRootedAt(info, Destination))
151            {
152                Logger.Log(S._("The destination directory cannot be within the source directory."),
153                    LogLevel.Error);
154                return;
155            }
156
157            //We need to get the files from the list of streams
158            List<StreamInfo> streams = GetPaths();
159            List<FileInfo> files = new List<FileInfo>(
160                streams.Distinct(new StreamInfoFileEqualityComparer()).
161                Select(x => x.File));
162            long totalSize = streams.Sum(x => x.Length);
163
164            foreach (FileInfo file in files)
165            {
166                //Compute the total size of the file on the disk (including ADSes)
167                List<StreamInfo> fileStreams = new List<StreamInfo>(file.GetADSes());
168                fileStreams.Add(new StreamInfo(file.FullName));
169                long fileSize = fileStreams.Sum(x => x.Length);
170
171                SteppedProgressManager fileProgress = new SteppedProgressManager();
172                Progress.Steps.Add(new SteppedProgressManagerStep(fileProgress,
173                    fileSize / (float)totalSize, S._("Securely moving files and folders...")));
174
175                //Add the copying step to the file progress.
176                ProgressManager copyProgress = new ProgressManager();
177                int totalPasses = 1 + EffectiveMethod.Passes;
178                fileProgress.Steps.Add(new SteppedProgressManagerStep(copyProgress,
179                    1f / totalPasses));
180
181                try
182                {
183                    //Compute the path to the new directory.
184                    DirectoryInfo sourceDirectory = file.Directory;
185                    DirectoryInfo destDirectory = new DirectoryInfo(
186                        SourceToDestinationPath(file.DirectoryName));
187
188                    //Make sure all necessary folders exist before the copy.
189                    if (!destDirectory.Exists)
190                        destDirectory.Create();
191                   
192                    //Then copy the file.
193                    file.CopyTo(System.IO.Path.Combine(destDirectory.FullName, file.Name),
194                        delegate(long TotalFileSize, long TotalBytesTransferred)
195                        {
196                            return CopyProgress(copyProgress, file, TotalFileSize,
197                                TotalBytesTransferred);
198                        });
199                }
200                catch (OperationCanceledException)
201                {
202                    //The copy was cancelled: Complete the copy part.
203                    copyProgress.MarkComplete();
204
205                    //We need to erase the partially copied copy of the file.
206                    SteppedProgressManager destroyProgress = new SteppedProgressManager();
207                    Progress.Steps.Add(new SteppedProgressManagerStep(destroyProgress, 0.5f,
208                        S._("Erasing incomplete destination file")));
209                    EraseFile(file, destroyProgress);
210
211                    //Rethrow the exception.
212                    throw;
213                }
214
215                //We copied the file over; erase the source file
216                SteppedProgressManager eraseProgress = new SteppedProgressManager();
217                fileProgress.Steps.Add(new SteppedProgressManagerStep(eraseProgress,
218                    (totalPasses - 1) / (float)totalPasses,
219                    S._("Erasing source files...")));
220                EraseFile(file, eraseProgress);
221            }
222
223            //Then copy the timestamps from the source folders and delete the source.
224            ProgressManager folderDeleteProgress = new ProgressManager();
225            Progress.Steps.Add(new SteppedProgressManagerStep(folderDeleteProgress, 0.0f,
226                S._("Removing folders...")));
227
228            Action<DirectoryInfo> CopyTimesAndDelete = null;
229            CopyTimesAndDelete = delegate(DirectoryInfo subDirectory)
230            {
231                foreach (DirectoryInfo child in subDirectory.GetDirectories())
232                    CopyTimesAndDelete(child);
233
234                //Update progress.
235                folderDeleteProgress.Tag = subDirectory.FullName;
236
237                //Get the directory which we copied to and copy the file times to the
238                //destination directory
239                DirectoryInfo destDirectory = new DirectoryInfo(
240                    SourceToDestinationPath(subDirectory.FullName));
241                if (!destDirectory.Exists)
242                    destDirectory.Create();
243                destDirectory.CopyTimes(subDirectory);
244
245                //Then delete the source directory.
246                IFileSystem fsManager = Host.Instance.FileSystems[
247                    VolumeInfo.FromMountPoint(Path)];
248                fsManager.DeleteFolder(subDirectory, true);
249            };
250            CopyTimesAndDelete(info);
251        }
252
253        /// <summary>
254        /// Converts the source path to the destination path.
255        /// </summary>
256        /// <param name="sourcePath">The source path to convert.</param>
257        /// <returns>The destination path that the file would have been moved to.</returns>
258        private string SourceToDestinationPath(string sourcePath)
259        {
260            DirectoryInfo source = new DirectoryInfo(Path);
261            string baseDir = System.IO.Path.Combine(Destination, source.Name);
262            return System.IO.Path.Combine(baseDir,
263                PathUtil.MakeRelativeTo(source, sourcePath));
264        }
265
266        private void CopyFile(FileInfo info)
267        {
268            ProgressManager copyProgress = new ProgressManager();
269            int totalPasses = 1 + EffectiveMethod.Passes;
270            Progress.Steps.Add(new SteppedProgressManagerStep(copyProgress, 1.0f / totalPasses,
271                S._("Copying source files to destination...")));
272
273            try
274            {
275                //Make sure all necessary folders exist before the copy.
276                Directory.CreateDirectory(Destination);
277
278                //Then copy the file.
279                string path = System.IO.Path.Combine(Destination, info.Name);
280                info.CopyTo(path, delegate(long TotalFileSize, long TotalBytesTransferred)
281                    {
282                        return CopyProgress(copyProgress, info, TotalFileSize,
283                            TotalBytesTransferred);
284                    });
285            }
286            catch (OperationCanceledException)
287            {
288                //The copy was cancelled: Complete the copy part.
289                copyProgress.MarkComplete();
290
291                //We need to erase the partially copied copy of the file.
292                SteppedProgressManager destroyProgress = new SteppedProgressManager();
293                Progress.Steps.Add(new SteppedProgressManagerStep(destroyProgress, 0.5f,
294                    S._("Erasing incomplete destination file")));
295                EraseFile(new FileInfo(Destination), destroyProgress);
296
297                //Rethrow the exception.
298                throw;
299            }
300
301            //Mark the copy as complete.
302            copyProgress.MarkComplete();
303
304            //Erase the source copy.
305            SteppedProgressManager eraseProgress = new SteppedProgressManager();
306            Progress.Steps.Add(new SteppedProgressManagerStep(eraseProgress,
307                (totalPasses - 1) / (float)totalPasses,
308                S._("Erasing source files...")));
309            EraseFile(info, eraseProgress);
310        }
311
312        /// <summary>
313        /// Wrapper around <see cref="FileSystemObjectErasureTarget.EraseStream"/>
314        /// that will erase every stream in the provided file.
315        /// </summary>
316        /// <param name="info">The file to erase.</param>
317        /// <param name="eraseProgress">The progress manager for the entire
318        /// erasure of the file.</param>
319        private void EraseFile(FileInfo info, SteppedProgressManager eraseProgress)
320        {
321            List<StreamInfo> streams = new List<StreamInfo>(info.GetADSes());
322            streams.Add(new StreamInfo(info.FullName));
323            long fileSize = streams.Sum(x => x.Length);
324
325            foreach (StreamInfo stream in streams)
326            {
327                ProgressManager progress = new ProgressManager();
328                eraseProgress.Steps.Add(new SteppedProgressManagerStep(progress,
329                    stream.Length / (float)fileSize,
330                    S._("Erasing incomplete destination file")));
331                EraseStream(stream, progress);
332            }
333        }
334
335        private Methods.CopyProgressFunctionResult CopyProgress(ProgressManager progress,
336            FileInfo file, long TotalFileSize, long TotalBytesTransferred) 
337        {
338            progress.Completed = TotalBytesTransferred;
339            progress.Total = TotalFileSize;
340            progress.Tag = file.FullName;
341
342            if (Task.Canceled)
343                return Methods.CopyProgressFunctionResult.Stop;
344            return Methods.CopyProgressFunctionResult.Continue;
345        }
346
347        private class StreamInfoFileEqualityComparer : IEqualityComparer<StreamInfo>
348        {
349            #region IEqualityComparer<StreamInfo> Members
350
351            public bool Equals(StreamInfo x, StreamInfo y)
352            {
353                return x.FileName == y.FileName;
354            }
355
356            public int GetHashCode(StreamInfo obj)
357            {
358                return obj.FileName.GetHashCode();
359            }
360
361            #endregion
362        }
363    }
364}
Note: See TracBrowser for help on using the repository browser.