Changeset 899


Ignore:
Timestamp:
4/28/2009 7:54:00 AM (6 years ago)
Author:
lowjoel
Message:

Refactored out the filesystem-specific code from DirectExecutor? to a FileSystem? class. Currently only implemented for NTFS

Location:
branches/eraser6/Manager
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • branches/eraser6/Manager/DirectExecutor.cs

    r898 r899  
    589589            //Make a folder to dump our temporary files in 
    590590            DirectoryInfo info = new DirectoryInfo(target.Drive); 
    591             { 
    592                 string directoryName; 
    593                 do 
    594                     directoryName = GenerateRandomFileName(18); 
    595                 while (Directory.Exists(info.FullName + Path.DirectorySeparatorChar + directoryName)); 
    596                 info = info.CreateSubdirectory(directoryName); 
    597             } 
     591            VolumeInfo volInfo = VolumeInfo.FromMountpoint(target.Drive); 
     592            FileSystem fsManager = FileSystem.Get(volInfo); 
     593            info = info.CreateSubdirectory(FileSystem.GenerateRandomFileName(info, 18)); 
    598594 
    599595            try 
     
    605601 
    606602                //Determine the total amount of data that needs to be written. 
    607                 VolumeInfo volInfo = VolumeInfo.FromMountpoint(target.Drive); 
    608603                long totalSize = method.CalculateEraseDataSize(null, volInfo.TotalFreeSpace); 
    609604 
     
    614609                { 
    615610                    //Generate a non-existant file name 
    616                     string currFile; 
    617                     do 
    618                         currFile = info.FullName + Path.DirectorySeparatorChar + 
    619                             GenerateRandomFileName(18); 
    620                     while (System.IO.File.Exists(currFile)); 
     611                    string currFile = FileSystem.GenerateRandomFileName(info, 18); 
    621612 
    622613                    //Create the stream 
     
    673664                progress.Event.CurrentItemName = S._("Old resident file system table files"); 
    674665                task.OnProgressChanged(progress.Event); 
    675                 EraseOldFilesystemResidentFiles(info, method, null); 
     666                fsManager.EraseOldFilesystemResidentFiles(volInfo, method, null); 
    676667            } 
    677668            finally 
     
    680671                progress.Event.CurrentItemName = S._("Removing temporary files"); 
    681672                task.OnProgressChanged(progress.Event); 
    682                 RemoveFolder(info); 
     673                fsManager.DeleteFolder(info); 
    683674            } 
    684675 
     
    687678            ProgressManager fsEntriesProgress = new ProgressManager(); 
    688679            fsEntriesProgress.Start(); 
    689             EraseOldFilesystemEntries(info.Parent, 
     680            fsManager.EraseDirectoryStructures(volInfo, 
    690681                delegate(int currentFile, int totalFiles) 
    691682                { 
     683                    lock (currentTask) 
     684                        if (currentTask.Cancelled) 
     685                            throw new FatalException(S._("The task was cancelled.")); 
     686 
    692687                    //Compute the progress 
    693688                    fsEntriesProgress.Total = totalFiles; 
     
    869864        #region Filesystem Object erasure functions 
    870865        /// <summary> 
    871         /// The prototype of callbacks handling the file system table erase progress 
    872         /// </summary> 
    873         /// <param name="currentFile">The current file being erased.</param> 
    874         /// <param name="totalFiles">The estimated number of files that must be erased.</param> 
    875         private delegate void FilesystemEntriesEraseProgress(int currentFile, int totalFiles); 
    876          
    877         /// <summary> 
    878         /// Erases old file system table-resident files. This creates small one-byte 
    879         /// files until disk is full. This will erase unused space which was used for 
    880         /// files resident in the file system table. 
    881         /// </summary> 
    882         /// <param name="info">The directory information structure containing 
    883         /// the path to store the temporary one-byte files. The file system table 
    884         /// of that drive will be erased.</param> 
    885         /// <param name="method">The method used to erase the files.</param> 
    886         private void EraseOldFilesystemResidentFiles(DirectoryInfo info, ErasureMethod method, 
    887             FilesystemEntriesEraseProgress callback) 
    888         { 
    889             VolumeInfo volInfo = VolumeInfo.FromMountpoint(info.FullName); 
    890             if (volInfo.VolumeFormat == "NTFS") 
    891             { 
    892                 try 
    893                 { 
    894                     //Squeeze one-byte files until the volume or the MFT is full. 
    895                     long oldMFTSize = NtfsAPI.GetMftValidSize(volInfo); 
    896                     for ( ; ; ) 
    897                     { 
    898                         //Open this stream 
    899                         using (FileStream strm = new FileStream(Path.Combine( 
    900                             info.FullName, GenerateRandomFileName(18)), 
    901                             FileMode.CreateNew, FileAccess.Write, FileShare.None, 8, 
    902                             FileOptions.WriteThrough)) 
    903                         { 
    904                             //Stretch the file size to use up some of the resident space. 
    905                             strm.SetLength(1); 
    906  
    907                             //Then run the erase task 
    908                             method.Erase(strm, long.MaxValue, 
    909                                 PRNGManager.GetInstance(ManagerLibrary.Instance.Settings.ActivePRNG), 
    910                                 null); 
    911                         } 
    912  
    913                         //We can stop when the MFT has grown. 
    914                         if (NtfsAPI.GetMftValidSize(volInfo) > oldMFTSize) 
    915                             break; 
    916                     } 
    917                 } 
    918                 catch (IOException) 
    919                 { 
    920                     //OK, enough squeezing. 
    921                 } 
    922             } 
    923         } 
    924  
    925         /// <summary> 
    926         /// Erases the unused space in the file system table by creating files, until 
    927         /// the table grows. 
    928         ///  
    929         /// This will overwrite unused portions of the table which were previously 
    930         /// used to store file entries. 
    931         /// </summary> 
    932         /// <param name="info">The directory information structure containing the path 
    933         /// to store the temporary files.</param> 
    934         /// <param name="callback">The callback function to handle the progress of the 
    935         /// file system entry erasure.</param> 
    936         private void EraseOldFilesystemEntries(DirectoryInfo info, FilesystemEntriesEraseProgress callback) 
    937         { 
    938             VolumeInfo volInfo = VolumeInfo.FromMountpoint(info.FullName); 
    939             if (volInfo.VolumeFormat == "NTFS") 
    940             { 
    941                 //Create a directory to hold all the temporary files 
    942                 DirectoryInfo tempDir = info.CreateSubdirectory(GenerateRandomFileName(32)); 
    943  
    944                 try 
    945                 { 
    946                     //Get the size of the MFT 
    947                     long mftSize = NtfsAPI.GetMftValidSize(volInfo); 
    948                     long mftRecordSegmentSize = NtfsAPI.GetMftRecordSegmentSize(volInfo); 
    949                     int pollingInterval = (int)Math.Max(1, (mftSize / volInfo.ClusterSize / 20)); 
    950                     int totalFiles = (int)Math.Max(1L, mftSize / mftRecordSegmentSize) * 
    951                         (FilenameErasePasses + 1); 
    952                     int filesCreated = 0; 
    953  
    954                     while (true) 
    955                     { 
    956                         ++filesCreated; 
    957                         using (FileStream strm = new FileStream(Path.Combine( 
    958                             tempDir.FullName, GenerateRandomFileName(220)), 
    959                             FileMode.CreateNew, FileAccess.Write)) 
    960                         { 
    961                         } 
    962  
    963                         if (filesCreated % pollingInterval == 0) 
    964                         { 
    965                             if (callback != null) 
    966                                 callback(filesCreated, totalFiles); 
    967  
    968                             lock (currentTask) 
    969                                 if (currentTask.Cancelled) 
    970                                     throw new FatalException(S._("The task was cancelled.")); 
    971  
    972                             //Check if the MFT has grown. 
    973                             if (mftSize < NtfsAPI.GetMftValidSize(volInfo)) 
    974                                 break; 
    975                         } 
    976                     } 
    977                 } 
    978                 catch (IOException) 
    979                 { 
    980                 } 
    981                 finally 
    982                 { 
    983                     //Clear up all the temporary files 
    984                     FileInfo[] files = tempDir.GetFiles("*", SearchOption.AllDirectories); 
    985                     int totalFiles = files.Length * (FilenameErasePasses + 1); 
    986                     for (int i = 0; i < files.Length; ++i) 
    987                     { 
    988                         if (callback != null && i % 50 == 0) 
    989                             callback(files.Length + i * FilenameErasePasses, totalFiles); 
    990                         RemoveFile(files[i]); 
    991                     } 
    992                      
    993                     RemoveFolder(tempDir); 
    994                 } 
    995             } 
    996             else 
    997                 throw new NotImplementedException(S._("Could not erase old file system " + 
    998                     "entries: Unsupported File system")); //Eraser.cpp@2348 
    999         } 
    1000  
    1001         /// <summary> 
    1002866        /// Erases a file or folder on the volume. 
    1003867        /// </summary> 
     
    1028892                progress.Event.CurrentTargetTotalPasses = method.Passes; 
    1029893                task.OnProgressChanged(progress.Event); 
    1030  
    1031894                 
     895                //Get the filesystem provider to handle the secure file erasures 
     896                FileSystem fsManager = FileSystem.Get(VolumeInfo.FromMountpoint(paths[i])); 
     897 
    1032898                //Remove the read-only flag, if it is set. 
    1033899                StreamInfo info = new StreamInfo(paths[i]); 
     
    1095961                    FileInfo fileInfo = info.File; 
    1096962                    if (fileInfo != null) 
    1097                         RemoveFile(fileInfo); 
     963                        fsManager.DeleteFile(fileInfo); 
    1098964                } 
    1099965                catch (UnauthorizedAccessException) 
     
    1118984            if (target is Task.Folder) 
    1119985            { 
     986                progress.Event.CurrentItemName = S._("Removing folders..."); 
     987                task.OnProgressChanged(progress.Event); 
     988 
    1120989                Task.Folder fldr = (Task.Folder)target; 
    1121990                if (fldr.DeleteIfEmpty) 
    1122                     RemoveFolder(new DirectoryInfo(fldr.Path)); 
     991                { 
     992                    FileSystem fsManager = FileSystem.Get(VolumeInfo.FromMountpoint(fldr.Path)); 
     993                    fsManager.DeleteFolder(new DirectoryInfo(fldr.Path)); 
     994                } 
    1123995            } 
    1124996 
     
    1126998            if (target is Task.RecycleBin) 
    1127999            { 
     1000                progress.Event.CurrentItemName = S._("Emptying recycle bin..."); 
     1001                task.OnProgressChanged(progress.Event); 
     1002 
    11281003                ShellAPI.SHEmptyRecycleBin(IntPtr.Zero, null, 
    11291004                    ShellAPI.SHEmptyRecycleBinFlags.SHERB_NOCONFIRMATION | 
     
    11491024 
    11501025        /// <summary> 
    1151         /// Securely removes files. 
    1152         /// </summary> 
    1153         /// <param name="info">The FileInfo object representing the file.</param> 
    1154         private static void RemoveFile(FileInfo info) 
    1155         { 
    1156             //Set the date of the file to be invalid to prevent forensic 
    1157             //detection 
    1158             info.CreationTime = info.LastWriteTime = info.LastAccessTime = 
    1159                 new DateTime(1980, 1, 1, 0, 0, 0); 
    1160             info.Attributes = FileAttributes.Normal; 
    1161             info.Attributes = FileAttributes.NotContentIndexed; 
    1162  
    1163             //Rename the file a few times to erase the entry from the file system 
    1164             //table. 
    1165             string newPath = info.DirectoryName + Path.DirectorySeparatorChar + 
    1166                 GenerateRandomFileName(info.Name.Length); 
    1167             for (int i = 0, tries = 0; i < FilenameErasePasses; ++tries) 
    1168             { 
    1169                 //Try to rename the file. If it fails, it is probably due to another 
    1170                 //process locking the file. Defer, then rename again. 
    1171                 try 
    1172                 { 
    1173                     info.MoveTo(newPath); 
    1174                     ++i; 
    1175                 } 
    1176                 catch (IOException) 
    1177                 { 
    1178                     Thread.Sleep(100); 
    1179  
    1180                     //If after FilenameEraseTries the file is still locked, some program is 
    1181                     //definitely using the file; throw an exception. 
    1182                     if (tries > FilenameEraseTries) 
    1183                         throw new IOException(S._("The file {0} is currently in use and " + 
    1184                             "cannot be removed.", info.FullName)); 
    1185                 } 
    1186             } 
    1187  
    1188             //If the user wants plausible deniability, find a random file on the same 
    1189             //volume and write it over. 
    1190             if (Manager.ManagerLibrary.Instance.Settings.PlausibleDeniability) 
    1191             { 
    1192                 //Get the template file to copy 
    1193                 FileInfo shadowFileInfo; 
    1194                 { 
    1195                     string shadowFile = null; 
    1196                     List<string> entries = ManagerLibrary.Instance.Settings.PlausibleDeniabilityFiles.GetRange( 
    1197                         0, ManagerLibrary.Instance.Settings.PlausibleDeniabilityFiles.Count); 
    1198                     PRNG prng = PRNGManager.GetInstance(ManagerLibrary.Instance.Settings.ActivePRNG); 
    1199                     do 
    1200                     { 
    1201                         if (entries.Count == 0) 
    1202                             throw new FatalException(S._("Plausible deniability was selected, " + 
    1203                                 "but no decoy files were found. The current file has been only " + 
    1204                                 "replaced with random data.")); 
    1205  
    1206                         int index = prng.Next(entries.Count - 1); 
    1207                         if ((System.IO.File.GetAttributes(entries[index]) & FileAttributes.Directory) != 0) 
    1208                         { 
    1209                             DirectoryInfo dir = new DirectoryInfo(entries[index]); 
    1210                             FileInfo[] files = dir.GetFiles("*", SearchOption.AllDirectories); 
    1211                             foreach (FileInfo f in files) 
    1212                                 entries.Add(f.FullName); 
    1213                         } 
    1214                         else 
    1215                             shadowFile = entries[index]; 
    1216  
    1217                         entries.RemoveAt(index); 
    1218                     } 
    1219                     while (shadowFile == null || shadowFile.Length == 0); 
    1220                     shadowFileInfo = new FileInfo(shadowFile); 
    1221                 } 
    1222  
    1223                 //Dump the copy (the first 4MB, or less, depending on the file size and available 
    1224                 //user space) 
    1225                 long amountToCopy = Math.Min(4 * 1024 * 1024, shadowFileInfo.Length); 
    1226                 using (FileStream shadowFileStream = shadowFileInfo.OpenRead()) 
    1227                 using (FileStream destFileStream = info.OpenWrite()) 
    1228                 { 
    1229                     while (destFileStream.Position < amountToCopy) 
    1230                     { 
    1231                         byte[] buf = new byte[524288]; 
    1232                         int bytesRead = shadowFileStream.Read(buf, 0, buf.Length); 
    1233  
    1234                         //Stop bothering if the input stream is at the end 
    1235                         if (bytesRead == 0) 
    1236                             break; 
    1237  
    1238                         //Dump the read contents onto the file to be deleted 
    1239                         destFileStream.Write(buf, 0, 
    1240                             (int)Math.Min(bytesRead, amountToCopy - destFileStream.Position)); 
    1241                     } 
    1242                 } 
    1243             } 
    1244  
    1245             //Then delete the file. 
    1246             for (int i = 0; i < FilenameEraseTries; ++i) 
    1247                 try 
    1248                 { 
    1249                     info.Delete(); 
    1250                     break; 
    1251                 } 
    1252                 catch (IOException) 
    1253                 { 
    1254                     if (i > FilenameEraseTries) 
    1255                         throw new IOException(S._("The file {0} is currently in use and " + 
    1256                             "cannot be removed.", info.FullName)); 
    1257                     Thread.Sleep(100); 
    1258                 } 
    1259         } 
    1260  
    1261         /// <summary> 
    1262         /// Removes the folder and all its contents. 
    1263         /// </summary> 
    1264         /// <param name="info">The folder to remove.</param> 
    1265         private static void RemoveFolder(DirectoryInfo info) 
    1266         { 
    1267             foreach (DirectoryInfo dir in info.GetDirectories()) 
    1268                 RemoveFolder(dir); 
    1269             foreach (FileInfo file in info.GetFiles()) 
    1270                 RemoveFile(file); 
    1271  
    1272             //Then clean up this folder. 
    1273             for (int i = 0; i < FilenameErasePasses; ++i) 
    1274             { 
    1275                 //Rename the folder. 
    1276                 string newPath = info.Parent.FullName + Path.DirectorySeparatorChar + 
    1277                     GenerateRandomFileName(info.Name.Length); 
    1278  
    1279                 //Try to rename the file. If it fails, it is probably due to another 
    1280                 //process locking the file. Defer, then rename again. 
    1281                 try 
    1282                 { 
    1283                     info.MoveTo(newPath); 
    1284                 } 
    1285                 catch (IOException) 
    1286                 { 
    1287                     Thread.Sleep(100); 
    1288                     --i; 
    1289                 } 
    1290             } 
    1291  
    1292             //Remove the folder 
    1293             info.Delete(true); 
    1294         } 
    1295  
    1296         /// <summary> 
    1297         /// Generates a random file name with the given length. 
    1298         /// </summary> 
    1299         /// <param name="length">The length of the file name to generate.</param> 
    1300         /// <returns>A random file name.</returns> 
    1301         private static string GenerateRandomFileName(int length) 
    1302         { 
    1303             //Get a random file name 
    1304             PRNG prng = PRNGManager.GetInstance(ManagerLibrary.Instance.Settings.ActivePRNG); 
    1305             byte[] newFileNameAry = new byte[length]; 
    1306             prng.NextBytes(newFileNameAry); 
    1307  
    1308             //Validate the name 
    1309             string validFileNameChars = "0123456789abcdefghijklmnopqrstuvwxyz" + 
    1310                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ _+=-()[]{}',`~!"; 
    1311             for (int j = 0, k = newFileNameAry.Length; j < k; ++j) 
    1312                 newFileNameAry[j] = (byte)validFileNameChars[ 
    1313                     (int)newFileNameAry[j] % validFileNameChars.Length]; 
    1314  
    1315             return new System.Text.UTF8Encoding().GetString(newFileNameAry); 
    1316         } 
    1317  
    1318         /// <summary> 
    1319         /// Gets a random file from within the provided directory. 
    1320         /// </summary> 
    1321         /// <param name="info">The directory to get a random file name from.</param> 
    1322         /// <returns>A string containing the full path to the file.</returns> 
    1323         private static string GetRandomFileName(DirectoryInfo info) 
    1324         { 
    1325             //First retrieve the list of files and folders in the provided directory. 
    1326             FileSystemInfo[] entries = null; 
    1327             try 
    1328             { 
    1329                 entries = info.GetFileSystemInfos(); 
    1330             } 
    1331             catch (Exception) 
    1332             { 
    1333                 return string.Empty; 
    1334             } 
    1335             if (entries.Length == 0) 
    1336                 return string.Empty; 
    1337  
    1338             //Find a random entry. 
    1339             PRNG prng = PRNGManager.GetInstance(ManagerLibrary.Instance.Settings.ActivePRNG); 
    1340             string result = string.Empty; 
    1341             while (result.Length == 0) 
    1342             { 
    1343                 int index = prng.Next(entries.Length - 1); 
    1344                 if (entries[index] is DirectoryInfo) 
    1345                     result = GetRandomFileName((DirectoryInfo)entries[index]); 
    1346                 else 
    1347                     result = ((FileInfo)entries[index]).FullName; 
    1348             } 
    1349  
    1350             return result; 
    1351         } 
    1352  
    1353         /// <summary> 
    13541026        /// The thread object. 
    13551027        /// </summary> 
  • branches/eraser6/Manager/Executor.cs

    r808 r899  
    198198                TaskProcessed(task); 
    199199        } 
    200  
    201         /// <summary> 
    202         /// The number of times file names are renamed to erase the file name from 
    203         /// the file system table. 
    204         /// </summary> 
    205         protected const int FilenameErasePasses = 7; 
    206  
    207         /// <summary> 
    208         /// The maximum number of times Eraser tries to erase a file/folder before 
    209         /// it gives up. 
    210         /// </summary> 
    211         protected const int FilenameEraseTries = 50; 
    212200    } 
    213201} 
  • branches/eraser6/Manager/Manager.csproj

    r845 r899  
    5151    <Compile Include="EntropySource.cs" /> 
    5252    <Compile Include="Executor.cs" /> 
     53    <Compile Include="FileSystem.cs" /> 
    5354    <Compile Include="Language.cs" /> 
    5455    <Compile Include="Logger.cs" /> 
Note: See TracChangeset for help on using the changeset viewer.