source: branches/eraser6/6.0/Eraser.DefaultPlugins/FileSystems/Ntfs.cs @ 1441

Revision 1441, 5.8 KB checked in by lowjoel, 4 years ago (diff)

Properly calculate erase progress when erasing the directory structures and resident files of NTFS volumes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 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.Text;
25
26using System.IO;
27using Eraser.Manager;
28using Eraser.Util;
29
30namespace Eraser.DefaultPlugins
31{
32    /// <summary>
33    /// Provides functions to handle erasures specific to NTFS volumes.
34    /// </summary>
35    public class NtfsFileSystem : WindowsFileSystem
36    {
37        public override bool Supports(string fileSystemName)
38        {
39            if (fileSystemName == "NTFS")
40                return true;
41            return false;
42        }
43
44        public override void EraseOldFileSystemResidentFiles(VolumeInfo volume,
45            DirectoryInfo tempDirectory, ErasureMethod method,
46            FileSystemEntriesEraseProgress callback)
47        {
48            try
49            {
50                //Squeeze one-byte files until the volume or the MFT is full.
51                long oldMFTSize = NtfsApi.GetMftValidSize(volume);
52
53                for (; ; )
54                {
55                    //Open this stream
56                    using (FileStream strm = new FileStream(
57                        GenerateRandomFileName(tempDirectory, 18), FileMode.CreateNew,
58                        FileAccess.Write, FileShare.None, 8, FileOptions.WriteThrough))
59                    {
60                        long streamSize = 0;
61                        try
62                        {
63                            while (true)
64                            {
65                                //Stretch the file size to use up some of the resident space.
66                                strm.SetLength(++streamSize);
67
68                                //Then run the erase task
69                                method.Erase(strm, long.MaxValue,
70                                    PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng),
71                                    null);
72
73                                //Call the callback function if one is provided. We'll provide a dummy
74                                //value since we really have no idea how much of the MFT we can clean.
75                                if (callback != null)
76                                    callback(0, 1);
77                            }
78                        }
79                        catch (IOException)
80                        {
81                            if (streamSize == 1)
82                                return;
83                        }
84                    }
85
86                    //We can stop when the MFT has grown.
87                    if (NtfsApi.GetMftValidSize(volume) > oldMFTSize)
88                        break;
89                }
90            }
91            catch (IOException)
92            {
93                //OK, enough squeezing: there isn't enough space to even create a new MFT record.
94            }
95        }
96
97        public override void EraseDirectoryStructures(VolumeInfo info,
98            FileSystemEntriesEraseProgress callback)
99        {
100            //Create a directory to hold all the temporary files
101            DirectoryInfo tempDir = new DirectoryInfo(FileSystem.GenerateRandomFileName(
102                new DirectoryInfo(info.MountPoints[0]), 32));
103            tempDir.Create();
104
105            try
106            {
107                //Get the size of the MFT
108                long mftSize = NtfsApi.GetMftValidSize(info);
109                long mftRecordSegmentSize = NtfsApi.GetMftRecordSegmentSize(info);
110                int pollingInterval = (int)Math.Min(Math.Max(1, mftSize / info.ClusterSize / 20), 128);
111                int totalFiles = (int)Math.Max(1L, mftSize / mftRecordSegmentSize);
112                int filesCreated = 0;
113
114                while (true)
115                {
116                    ++filesCreated;
117                    using (FileStream strm = new FileStream(FileSystem.GenerateRandomFileName(
118                        tempDir, 220), FileMode.CreateNew, FileAccess.Write))
119                    {
120                    }
121
122                    if (filesCreated % pollingInterval == 0)
123                    {
124                        //Call back to our progress function: this is the first half of the
125                        //procedure so divide the effective progress by 2.
126                        if (callback != null)
127                        {
128                            int halfFilesCreated = filesCreated / 2;
129                            callback(halfFilesCreated, Math.Min(halfFilesCreated, totalFiles));
130                        }
131
132                        //Check if the MFT has grown.
133                        if (mftSize < NtfsApi.GetMftValidSize(info))
134                            break;
135                    }
136                }
137            }
138            catch (IOException)
139            {
140            }
141            finally
142            {
143                //Clear up all the temporary files
144                FileInfo[] files = tempDir.GetFiles("*", SearchOption.AllDirectories);
145                for (int i = 0; i < files.Length; ++i)
146                {
147                    if (callback != null && i % 50 == 0)
148                        callback(files.Length + i, files.Length * 2);
149                    DeleteFile(files[i]);
150                }
151
152                DeleteFolder(tempDir);
153            }
154        }
155
156        public override void EraseFileSystemObject(StreamInfo info, ErasureMethod method,
157            ErasureMethodProgressFunction callback)
158        {
159            //Check if the file fits in one cluster - if it does it may be MFT resident
160            //TODO: any more deterministic way of finding out?
161            VolumeInfo volume = VolumeInfo.FromMountpoint(info.DirectoryName);
162            if (info.Length < Math.Max(volume.ClusterSize, 1024))
163            {
164                //Yes it does, erase exactly to the file length
165                using (FileStream strm = info.Open(FileMode.Open, FileAccess.Write,
166                    FileShare.None))
167                {
168                    method.Erase(strm, long.MaxValue,
169                        PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng), null);
170                }
171            }
172
173            //Create the file stream, and call the erasure method to write to
174            //the stream.
175            long fileArea = GetFileArea(info.FullName);
176
177            //If the stream is empty, there's nothing to overwrite. Continue
178            //to the next entry
179            if (fileArea == 0)
180                return;
181
182            using (FileStream strm = info.Open(FileMode.Open, FileAccess.Write,
183                FileShare.None, FileOptions.WriteThrough))
184            {
185                //Set the end of the stream after the wrap-round the cluster size
186                strm.SetLength(fileArea);
187
188                //Then erase the file.
189                method.Erase(strm, long.MaxValue,
190                    PrngManager.GetInstance(ManagerLibrary.Settings.ActivePrng),
191                    callback
192                );
193
194                //Set the length of the file to 0.
195                strm.Seek(0, SeekOrigin.Begin);
196                strm.SetLength(0);
197            }
198        }
199
200        protected override DateTime MinTimestamp
201        {
202            get
203            {
204                return new DateTime(1601, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
205            }
206        }
207    }
208}
Note: See TracBrowser for help on using the repository browser.