source: branches/eraser6/pluginsRewrite/Eraser.DefaultPlugins/ErasureTargets/SecureMoveErasureTarget.cs @ 2486

Revision 2486, 10.9 KB checked in by lowjoel, 3 years ago (diff)

Report progress updates by pushing information to the SteppedProgressManager? instance associated with each erasure target. Furthermore, do not manipulate the state of the Task object, instead, let the Task object manage its own Progress state.

Because of this, a new property Tag is created in ProgressManagerBase?, to hold state information.

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