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

Revision 2283, 11.2 KB checked in by lowjoel, 4 years ago (diff)

Move the MakeRelativeTo? method and the IsRootedAt? methods to the Path class, part of the Eraser.Util.ExtensionMethods? namespace.

  • 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.Manager;
33using Eraser.Util;
34using Eraser.Util.ExtensionMethods;
35
36namespace Eraser.DefaultPlugins
37{
38    /// <summary>
39    /// Class representing a path that needs to be moved.
40    /// </summary>
41    [Serializable]
42    [Guid("18FB3523-4012-4718-8B9A-BADAA9084214")]
43    public class SecureMoveErasureTarget : FileSystemObjectErasureTarget
44    {
45        #region Serialization code
46        protected SecureMoveErasureTarget(SerializationInfo info, StreamingContext context)
47            : base(info, context)
48        {
49            Destination = (string)info.GetValue("Destination", typeof(string));
50        }
51
52        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
53        public override void GetObjectData(SerializationInfo info, StreamingContext context)
54        {
55            base.GetObjectData(info, context);
56            info.AddValue("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 UIText
75        {
76            get { 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            Task.Progress.Steps.Add(new SteppedProgressManagerStep(Progress,
126                1.0f / Task.Targets.Count));
127
128            try
129            {
130                //Depending on whether the path is a file or directory, execute the
131                //correct function.
132                if ((File.GetAttributes(Path) & FileAttributes.Directory) != 0)
133                {
134                    DirectoryInfo info = new DirectoryInfo(Path);
135                    CopyDirectory(info);
136                }
137                else
138                {
139                    FileInfo info = new FileInfo(Path);
140                    CopyFile(info);
141                }
142            }
143            finally
144            {
145                Progress = null;
146            }
147        }
148
149        private void CopyDirectory(DirectoryInfo info)
150        {
151            //Check the the destination is not a subfolder of the source.
152            if (Eraser.Util.ExtensionMethods.Path.IsRootedAt(info, Destination))
153            {
154                Logger.Log(S._("The destination directory cannot be within the source directory."),
155                    LogLevel.Error);
156                return;
157            }
158
159            //We need to get the files from the list of streams
160            List<StreamInfo> streams = GetPaths();
161            List<FileInfo> files = new List<FileInfo>(
162                streams.Distinct(new StreamInfoFileEqualityComparer()).
163                Select(x => x.File));
164            long totalSize = streams.Sum(x => x.Length);
165
166            foreach (FileInfo file in files)
167            {
168                //Compute the total size of the file on the disk (including ADSes)
169                List<StreamInfo> fileStreams = new List<StreamInfo>(file.GetADSes());
170                fileStreams.Add(new StreamInfo(file.FullName));
171                long fileSize = fileStreams.Sum(x => x.Length);
172
173                SteppedProgressManager fileProgress = new SteppedProgressManager();
174                Progress.Steps.Add(new SteppedProgressManagerStep(fileProgress,
175                    fileSize / (float)totalSize, S._("Securely moving files and folders...")));
176
177                //Add the copying step to the file progress.
178                ProgressManager copyProgress = new ProgressManager();
179                int totalPasses = 1 + EffectiveMethod.Passes;
180                fileProgress.Steps.Add(new SteppedProgressManagerStep(copyProgress,
181                    1f / totalPasses));
182
183                try
184                {
185                    //Compute the path to the new directory.
186                    DirectoryInfo sourceDirectory = file.Directory;
187                    DirectoryInfo destDirectory = new DirectoryInfo(
188                        SourceToDestinationPath(file.DirectoryName));
189
190                    //Make sure all necessary folders exist before the copy.
191                    if (!destDirectory.Exists)
192                        destDirectory.Create();
193                   
194                    //Then copy the file.
195                    file.CopyTo(System.IO.Path.Combine(destDirectory.FullName, file.Name),
196                        delegate(long TotalFileSize, long TotalBytesTransferred)
197                        {
198                            return CopyProgress(copyProgress, file, TotalFileSize,
199                                TotalBytesTransferred);
200                        });
201                }
202                catch (OperationCanceledException)
203                {
204                    //The copy was cancelled: Complete the copy part.
205                    copyProgress.MarkComplete();
206
207                    //We need to erase the partially copied copy of the file.
208                    SteppedProgressManager destroyProgress = new SteppedProgressManager();
209                    Progress.Steps.Add(new SteppedProgressManagerStep(destroyProgress, 0.5f,
210                        S._("Erasing incomplete destination file")));
211                    EraseFile(file, destroyProgress);
212
213                    //Rethrow the exception.
214                    throw;
215                }
216
217                //We copied the file over; erase the source file
218                SteppedProgressManager eraseProgress = new SteppedProgressManager();
219                fileProgress.Steps.Add(new SteppedProgressManagerStep(eraseProgress,
220                    (totalPasses - 1) / (float)totalPasses,
221                    S._("Erasing source files...")));
222                EraseFile(file, eraseProgress);
223            }
224
225            //Then copy the timestamps from the source folders and delete the source.
226            ProgressManager folderDeleteProgress = new ProgressManager();
227            Progress.Steps.Add(new SteppedProgressManagerStep(folderDeleteProgress, 0.0f,
228                S._("Removing folders...")));
229
230            Action<DirectoryInfo> CopyTimesAndDelete = null;
231            CopyTimesAndDelete = delegate(DirectoryInfo subDirectory)
232            {
233                foreach (DirectoryInfo child in subDirectory.GetDirectories())
234                    CopyTimesAndDelete(child);
235
236                //Update progress.
237                OnProgressChanged(this, new ProgressChangedEventArgs(folderDeleteProgress,
238                    new TaskProgressChangedEventArgs(subDirectory.FullName, 1, 1)));
239
240                //Get the directory which we copied to and copy the file times to the
241                //destination directory
242                DirectoryInfo destDirectory = new DirectoryInfo(
243                    SourceToDestinationPath(subDirectory.FullName));
244                if (!destDirectory.Exists)
245                    destDirectory.Create();
246                destDirectory.CopyTimes(subDirectory);
247
248                //Then delete the source directory.
249                FileSystem fsManager = ManagerLibrary.Instance.FileSystemRegistrar[
250                    VolumeInfo.FromMountPoint(Path)];
251                fsManager.DeleteFolder(subDirectory);
252            };
253            CopyTimesAndDelete(info);
254        }
255
256        /// <summary>
257        /// Converts the source path to the destination path.
258        /// </summary>
259        /// <param name="sourcePath">The source path to convert.</param>
260        /// <returns>The destination path that the file would have been moved to.</returns>
261        private string SourceToDestinationPath(string sourcePath)
262        {
263            DirectoryInfo source = new DirectoryInfo(Path);
264            string baseDir = System.IO.Path.Combine(Destination, source.Name);
265            return System.IO.Path.Combine(baseDir,
266                Eraser.Util.ExtensionMethods.Path.MakeRelativeTo(source, sourcePath));
267        }
268
269        private void CopyFile(FileInfo info)
270        {
271            ProgressManager copyProgress = new ProgressManager();
272            int totalPasses = 1 + EffectiveMethod.Passes;
273            Progress.Steps.Add(new SteppedProgressManagerStep(copyProgress, 1.0f / totalPasses,
274                S._("Copying source files to destination...")));
275
276            try
277            {
278                //Make sure all necessary folders exist before the copy.
279                Directory.CreateDirectory(Destination);
280
281                //Then copy the file.
282                string path = System.IO.Path.Combine(Destination, info.Name);
283                info.CopyTo(path, delegate(long TotalFileSize, long TotalBytesTransferred)
284                    {
285                        return CopyProgress(copyProgress, info, TotalFileSize,
286                            TotalBytesTransferred);
287                    });
288            }
289            catch (OperationCanceledException)
290            {
291                //The copy was cancelled: Complete the copy part.
292                copyProgress.MarkComplete();
293
294                //We need to erase the partially copied copy of the file.
295                SteppedProgressManager destroyProgress = new SteppedProgressManager();
296                Progress.Steps.Add(new SteppedProgressManagerStep(destroyProgress, 0.5f,
297                    S._("Erasing incomplete destination file")));
298                EraseFile(new FileInfo(Destination), destroyProgress);
299
300                //Rethrow the exception.
301                throw;
302            }
303
304            //Mark the copy as complete.
305            copyProgress.MarkComplete();
306
307            //Erase the source copy.
308            SteppedProgressManager eraseProgress = new SteppedProgressManager();
309            Progress.Steps.Add(new SteppedProgressManagerStep(eraseProgress,
310                (totalPasses - 1) / (float)totalPasses,
311                S._("Erasing source files...")));
312            EraseFile(info, eraseProgress);
313        }
314
315        /// <summary>
316        /// Wrapper around <see cref="FileSystemObjectErasureTarget.EraseStream"/>
317        /// that will erase every stream in the provided file.
318        /// </summary>
319        /// <param name="info">The file to erase.</param>
320        /// <param name="eraseProgress">The progress manager for the entire
321        /// erasure of the file.</param>
322        private void EraseFile(FileInfo info, SteppedProgressManager eraseProgress)
323        {
324            List<StreamInfo> streams = new List<StreamInfo>(info.GetADSes());
325            streams.Add(new StreamInfo(info.FullName));
326            long fileSize = streams.Sum(x => x.Length);
327
328            foreach (StreamInfo stream in streams)
329            {
330                ProgressManager progress = new ProgressManager();
331                eraseProgress.Steps.Add(new SteppedProgressManagerStep(progress,
332                    stream.Length / (float)fileSize,
333                    S._("Erasing incomplete destination file")));
334                EraseStream(stream, progress);
335            }
336        }
337
338        private Methods.CopyProgressFunctionResult CopyProgress(ProgressManager progress,
339            FileInfo file, long TotalFileSize, long TotalBytesTransferred) 
340        {
341            progress.Completed = TotalBytesTransferred;
342            progress.Total = TotalFileSize;
343            OnProgressChanged(this, new ProgressChangedEventArgs(Progress,
344                new TaskProgressChangedEventArgs(file.FullName, 1, 1)));
345
346            if (Task.Canceled)
347                return Methods.CopyProgressFunctionResult.Stop;
348            return Methods.CopyProgressFunctionResult.Continue;
349        }
350
351        private class StreamInfoFileEqualityComparer : IEqualityComparer<StreamInfo>
352        {
353            #region IEqualityComparer<StreamInfo> Members
354
355            public bool Equals(StreamInfo x, StreamInfo y)
356            {
357                return x.FileName == y.FileName;
358            }
359
360            public int GetHashCode(StreamInfo obj)
361            {
362                return obj.FileName.GetHashCode();
363            }
364
365            #endregion
366        }
367    }
368}
Note: See TracBrowser for help on using the repository browser.