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

Revision 2468, 11.2 KB checked in by lowjoel, 3 years ago (diff)

Instead of pushing events to the client, we will update our Progress object and the client should pull progress updates instead. This prepares the code to be used by the remote executor (for service execution).

In the interim, there is no progress feedback for all erasures.

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