source: branches/eraser6/6.0/Eraser.Manager/FileSystem.cs @ 2249

Revision 2249, 13.2 KB checked in by lowjoel, 4 years ago (diff)

Backported r2150 r2155 and r2161 from trunk: do not throw an out-of-disk space error when erasing unused space. This is triggered by us renaming files even when there is insufficient disk space (apparently NTFS needs space even when renaming filenames to one of the same length.)

  • 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: Kasra Nassiri <cjax@users.sourceforge.net> @17/10/2008
6 * Modified By:
7 *
8 * This file is part of Eraser.
9 *
10 * Eraser is free software: you can redistribute it and/or modify it under the
11 * terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 3 of the License, or (at your option) any later
13 * version.
14 *
15 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
16 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 *
19 * A copy of the GNU General Public License can be found at
20 * <http://www.gnu.org/licenses/>.
21 */
22
23using System;
24using System.Collections.Generic;
25using System.Text;
26using System.IO;
27using Eraser.Util;
28
29namespace Eraser.Manager
30{
31    /// <summary>
32    /// Provides functions to handle erasures specfic to file systems.
33    /// </summary>
34    public abstract class FileSystem
35    {
36        /// <summary>
37        /// Generates a random file name with the given length.
38        /// </summary>
39        /// <remarks>The generated file name is guaranteed not to exist.</remarks>
40        /// <param name="info">The directory to generate the file name in. This
41        /// parameter can be null to indicate merely a random file name</param>
42        /// <param name="length">The length of the file name to generate.</param>
43        /// <returns>A full path to a file containing random file name.</returns>
44        public static string GenerateRandomFileName(DirectoryInfo info, int length)
45        {
46            //Initialise the base name, if any.
47            Prng prng = PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng);
48            string resultPrefix = info == null ? string.Empty : info.FullName +
49                Path.DirectorySeparatorChar;
50
51            //Variables to store the intermediates.
52            byte[] resultAry = new byte[length];
53            string result = string.Empty;
54            List<string> prohibitedFileNames = new List<string>(new string[] {
55                "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
56                "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3",
57                "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
58            });
59
60            do
61            {
62                prng.NextBytes(resultAry);
63
64                //Validate the name
65                string validFileNameChars = "0123456789abcdefghijklmnopqrstuvwxyz" +
66                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ _+=-()[]{}',`~!";
67                for (int j = 0, k = resultAry.Length; j < k; ++j)
68                {
69                    resultAry[j] = (byte)validFileNameChars[
70                        (int)resultAry[j] % validFileNameChars.Length];
71
72                    if (j == 0 || j == k - 1)
73                        //The first or last character cannot be whitespace
74                        while (Char.IsWhiteSpace((char)resultAry[j]))
75                            resultAry[j] = (byte)validFileNameChars[
76                                (int)resultAry[j] % validFileNameChars.Length];
77                }
78
79                result = Encoding.UTF8.GetString(resultAry);
80            }
81            while (info != null &&
82                prohibitedFileNames.IndexOf(Path.GetFileNameWithoutExtension(result)) != -1 ||
83                (Directory.Exists(resultPrefix + result) ||
84                    System.IO.File.Exists(resultPrefix + result))
85            );
86            return resultPrefix + result;
87        }
88
89        /// <summary>
90        /// Gets a random file from within the provided directory.
91        /// </summary>
92        /// <param name="info">The directory to get a random file name from.</param>
93        /// <returns>A string containing the full path to the file.</returns>
94        public static string GetRandomFile(DirectoryInfo info)
95        {
96            //First retrieve the list of files and folders in the provided directory.
97            FileSystemInfo[] entries = null;
98            try
99            {
100                entries = info.GetFileSystemInfos();
101            }
102            catch (DirectoryNotFoundException)
103            {
104                return string.Empty;
105            }
106            if (entries.Length == 0)
107                return string.Empty;
108
109            //Find a random entry.
110            Prng prng = PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng);
111            string result = string.Empty;
112            while (result.Length == 0)
113            {
114                int index = prng.Next(entries.Length - 1);
115                if (entries[index] is DirectoryInfo)
116                    result = GetRandomFile((DirectoryInfo)entries[index]);
117                else
118                    result = ((FileInfo)entries[index]).FullName;
119            }
120
121            return result;
122        }
123
124        /// <summary>
125        /// Writes a file for plausible deniability over the current stream.
126        /// </summary>
127        /// <param name="stream">The stream to write the data to.</param>
128        protected static void CopyPlausibleDeniabilityFile(Stream stream)
129        {
130            //Get the template file to copy
131            FileInfo shadowFileInfo;
132            {
133                string shadowFile = null;
134                List<string> entries = new List<string>(
135                    ManagerLibrary.Settings.PlausibleDeniabilityFiles);
136                Prng prng = PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng);
137                do
138                {
139                    if (entries.Count == 0)
140                        throw new FatalException(S._("Plausible deniability was selected, " +
141                            "but no decoy files were found. The current file has been only " +
142                            "replaced with random data."));
143
144                    int index = prng.Next(entries.Count - 1);
145                    if ((System.IO.File.GetAttributes(entries[index]) & FileAttributes.Directory) != 0)
146                    {
147                        DirectoryInfo dir = new DirectoryInfo(entries[index]);
148                        FileInfo[] files = dir.GetFiles("*", SearchOption.AllDirectories);
149                        foreach (FileInfo f in files)
150                            entries.Add(f.FullName);
151                    }
152                    else
153                        shadowFile = entries[index];
154
155                    entries.RemoveAt(index);
156                }
157                while (shadowFile == null || shadowFile.Length == 0 ||
158                    !System.IO.File.Exists(shadowFile));
159                shadowFileInfo = new FileInfo(shadowFile);
160            }
161
162            //Dump the copy (the first 4MB, or less, depending on the file size and size of
163            //the original file)
164            long amountToCopy = Math.Min(stream.Length,
165                Math.Min(4 * 1024 * 1024, shadowFileInfo.Length));
166            using (FileStream shadowFileStream = shadowFileInfo.OpenRead())
167            {
168                while (stream.Position < amountToCopy)
169                {
170                    byte[] buf = new byte[524288];
171                    int bytesRead = shadowFileStream.Read(buf, 0, buf.Length);
172
173                    //Stop bothering if the input stream is at the end
174                    if (bytesRead == 0)
175                        break;
176
177                    //Dump the read contents onto the file to be deleted
178                    stream.Write(buf, 0,
179                        (int)Math.Min(bytesRead, amountToCopy - stream.Position));
180                }
181            }
182        }
183
184        /// <summary>
185        /// Checks whether the given file system is supported by the current provider.
186        /// </summary>
187        /// <param name="fileSystemName">The file system name to check.</param>
188        /// <returns>True if the current provider supports the file system.</returns>
189        public abstract bool Supports(string fileSystemName);
190
191        /// <summary>
192        /// Resets the created, modified, accessed and last update times for the given
193        /// <paramref name="info"/>.
194        /// </summary>
195        /// <param name="info">The file to reset times.</param>
196        public abstract void ResetFileTimes(FileSystemInfo info);
197
198        /// <summary>
199        /// Securely deletes the file reference from the directory structures
200        /// as well as resetting the Date Created, Date Accessed and Date Modified
201        /// records.
202        /// </summary>
203        /// <param name="info">The file to delete.</param>
204        public abstract void DeleteFile(FileInfo info);
205
206        /// <summary>
207        /// Securely deletes the folder reference from the directory structures
208        /// as well as all subfolders and files, resetting the Date Created, Date
209        /// Accessed and Date Modified records.
210        /// </summary>
211        /// <param name="info">The folder to delete</param>
212        /// <param name="recursive">True if the folder and all its subfolders and
213        /// files to be securely deleted.</param>
214        public abstract void DeleteFolder(DirectoryInfo info, bool recursive);
215
216        /// <seealso cref="DeleteFolder"/>
217        /// <param name="info">The folder to delete.</param>
218        public void DeleteFolder(DirectoryInfo info)
219        {
220            DeleteFolder(info, true);
221        }
222
223        /// <summary>
224        /// Erases all file cluster tips in the given volume.
225        /// </summary>
226        /// <param name="info">The volume to search for file cluster tips and erase them.</param>
227        /// <param name="method">The erasure method being employed.</param>
228        /// <param name="log">The log manager instance that tracks log messages.</param>
229        /// <param name="searchCallback">The callback function for search progress.</param>
230        /// <param name="eraseCallback">The callback function for erasure progress.</param>
231        public abstract void EraseClusterTips(VolumeInfo info, ErasureMethod method,
232            Logger log, ClusterTipsSearchProgress searchCallback,
233            ClusterTipsEraseProgress eraseCallback);
234
235        /// <summary>
236        /// Erases old file system table-resident files. This creates small one-byte
237        /// files until disk is full. This will erase unused space which was used for
238        /// files resident in the file system table.
239        /// </summary>
240        /// <param name="volume">The directory information structure containing
241        /// the path to store the temporary one-byte files. The file system table
242        /// of that drive will be erased.</param>
243        /// <param name="tempDirectory">The directory structure containing the path
244        /// to store temporary files used for resident file cleaning.</param>
245        /// <param name="method">The method used to erase the files.</param>
246        public abstract void EraseOldFileSystemResidentFiles(VolumeInfo volume,
247            DirectoryInfo tempDirectory, ErasureMethod method,
248            FileSystemEntriesEraseProgress callback);
249
250        /// <summary>
251        /// Erases the unused space in the main filesystem structures by creating,
252        /// files until the table grows.
253        ///
254        /// This will overwrite unused portions of the table which were previously
255        /// used to store file entries.
256        /// </summary>
257        /// <param name="info">The directory information structure containing
258        /// the path to store the temporary files.</param>
259        /// <param name="callback">The callback function to handle the progress
260        /// of the file system entry erasure.</param>
261        public abstract void EraseDirectoryStructures(VolumeInfo info,
262            FileSystemEntriesEraseProgress callback);
263
264        /// <summary>
265        /// Erases the file system object from the drive.
266        /// </summary>
267        /// <param name="info"></param>
268        public abstract void EraseFileSystemObject(StreamInfo info, ErasureMethod method,
269            ErasureMethodProgressFunction callback);
270
271        /// <summary>
272        /// Retrieves the size of the file on disk, calculated by the amount of
273        /// clusters allocated by it.
274        /// </summary>
275        /// <param name="filePath">The path to the file.</param>
276        /// <returns>The area of the file.</returns>
277        public abstract long GetFileArea(string filePath);
278
279        /// <summary>
280        /// The number of times file names are renamed to erase the file name from
281        /// the file system table.
282        /// </summary>
283        public const int FileNameErasePasses = 7;
284
285        /// <summary>
286        /// The maximum number of times Eraser tries to erase a file/folder before
287        /// it gives up.
288        /// </summary>
289        public const int FileNameEraseTries = 50;
290    }
291
292    /// <summary>
293    /// The function prototype for cluster tip search progress callbacks. This is
294    /// called when the cluster tips are being searched.
295    /// </summary>
296    /// <param name="currentPath">The directory being searched</param>
297    public delegate void ClusterTipsSearchProgress(string currentPath);
298
299    /// <summary>
300    /// The function prototype for cluster tip erasure callbacks. This is called when
301    /// the cluster tips are being erased.
302    /// </summary>
303    /// <param name="currentFile">The current file index being erased.</param>
304    /// <param name="totalFiles">The total number of files to be erased.</param>
305    /// <param name="currentFilePath">The path to the current file being erased.</param>
306    public delegate void ClusterTipsEraseProgress(int currentFile, int totalFiles,
307        string currentFilePath);
308
309    /// <summary>
310    /// The prototype of callbacks handling the file system table erase progress.
311    /// </summary>
312    /// <param name="currentFile">The current file being erased.</param>
313    /// <param name="totalFiles">The estimated number of files that must be
314    /// erased.</param>
315    public delegate void FileSystemEntriesEraseProgress(int currentFile, int totalFiles);
316
317    public class FileSystemManager
318    {
319        #region Registrar fields
320        /// <summary>
321        /// Gets the FileSystem object that implements the FileSystem interface
322        /// for the given file system.
323        /// </summary>
324        /// <param name="volume">The volume to get the FileSystem provider for.</param>
325        /// <returns>The FileSystem object providing interfaces to handle the
326        /// given volume.</returns>
327        /// <exception cref="NotSupportedException">Thrown when an unimplemented
328        /// file system is requested.</exception>
329        public static FileSystem Get(VolumeInfo volume)
330        {
331            lock (ManagerLibrary.Instance.FileSystemManager.FileSystems)
332                foreach (FileSystem filesystem in ManagerLibrary.Instance.FileSystemManager.FileSystems)
333                    if (filesystem.Supports(volume.VolumeFormat))
334                        return filesystem;
335
336            throw new NotSupportedException(S._("The file system on the drive {0} is not " +
337                "supported.", volume.IsMounted ? volume.MountPoints[0] : volume.VolumeId));
338        }
339
340        /// <summary>
341        /// Allows plug-ins to register file system providers with the main program.
342        /// Thread-safe.
343        /// </summary>
344        /// <param name="method">The filesystem to register.</param>
345        public static void Register(FileSystem filesystem)
346        {
347            //Insert the entry
348            lock (ManagerLibrary.Instance.FileSystemManager.FileSystems)
349            {
350                ManagerLibrary.Instance.FileSystemManager.FileSystems.Add(filesystem);
351            }
352        }
353
354        /// <summary>
355        /// The list of currently registered erasure methods.
356        /// </summary>
357        private List<FileSystem> FileSystems = new List<FileSystem>();
358        #endregion
359    }
360}
Note: See TracBrowser for help on using the repository browser.