Changeset 899 for branches/eraser6/Manager/DirectExecutor.cs
- Timestamp:
- 4/28/2009 7:54:00 AM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/eraser6/Manager/DirectExecutor.cs
r898 r899 589 589 //Make a folder to dump our temporary files in 590 590 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)); 598 594 599 595 try … … 605 601 606 602 //Determine the total amount of data that needs to be written. 607 VolumeInfo volInfo = VolumeInfo.FromMountpoint(target.Drive);608 603 long totalSize = method.CalculateEraseDataSize(null, volInfo.TotalFreeSpace); 609 604 … … 614 609 { 615 610 //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); 621 612 622 613 //Create the stream … … 673 664 progress.Event.CurrentItemName = S._("Old resident file system table files"); 674 665 task.OnProgressChanged(progress.Event); 675 EraseOldFilesystemResidentFiles(info, method, null);666 fsManager.EraseOldFilesystemResidentFiles(volInfo, method, null); 676 667 } 677 668 finally … … 680 671 progress.Event.CurrentItemName = S._("Removing temporary files"); 681 672 task.OnProgressChanged(progress.Event); 682 RemoveFolder(info);673 fsManager.DeleteFolder(info); 683 674 } 684 675 … … 687 678 ProgressManager fsEntriesProgress = new ProgressManager(); 688 679 fsEntriesProgress.Start(); 689 EraseOldFilesystemEntries(info.Parent,680 fsManager.EraseDirectoryStructures(volInfo, 690 681 delegate(int currentFile, int totalFiles) 691 682 { 683 lock (currentTask) 684 if (currentTask.Cancelled) 685 throw new FatalException(S._("The task was cancelled.")); 686 692 687 //Compute the progress 693 688 fsEntriesProgress.Total = totalFiles; … … 869 864 #region Filesystem Object erasure functions 870 865 /// <summary> 871 /// The prototype of callbacks handling the file system table erase progress872 /// </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-byte879 /// files until disk is full. This will erase unused space which was used for880 /// files resident in the file system table.881 /// </summary>882 /// <param name="info">The directory information structure containing883 /// the path to store the temporary one-byte files. The file system table884 /// 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 try893 {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 stream899 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 task908 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, until927 /// the table grows.928 ///929 /// This will overwrite unused portions of the table which were previously930 /// used to store file entries.931 /// </summary>932 /// <param name="info">The directory information structure containing the path933 /// to store the temporary files.</param>934 /// <param name="callback">The callback function to handle the progress of the935 /// 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 files942 DirectoryInfo tempDir = info.CreateSubdirectory(GenerateRandomFileName(32));943 944 try945 {946 //Get the size of the MFT947 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 finally982 {983 //Clear up all the temporary files984 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 else997 throw new NotImplementedException(S._("Could not erase old file system " +998 "entries: Unsupported File system")); //Eraser.cpp@2348999 }1000 1001 /// <summary>1002 866 /// Erases a file or folder on the volume. 1003 867 /// </summary> … … 1028 892 progress.Event.CurrentTargetTotalPasses = method.Passes; 1029 893 task.OnProgressChanged(progress.Event); 1030 1031 894 895 //Get the filesystem provider to handle the secure file erasures 896 FileSystem fsManager = FileSystem.Get(VolumeInfo.FromMountpoint(paths[i])); 897 1032 898 //Remove the read-only flag, if it is set. 1033 899 StreamInfo info = new StreamInfo(paths[i]); … … 1095 961 FileInfo fileInfo = info.File; 1096 962 if (fileInfo != null) 1097 RemoveFile(fileInfo);963 fsManager.DeleteFile(fileInfo); 1098 964 } 1099 965 catch (UnauthorizedAccessException) … … 1118 984 if (target is Task.Folder) 1119 985 { 986 progress.Event.CurrentItemName = S._("Removing folders..."); 987 task.OnProgressChanged(progress.Event); 988 1120 989 Task.Folder fldr = (Task.Folder)target; 1121 990 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 } 1123 995 } 1124 996 … … 1126 998 if (target is Task.RecycleBin) 1127 999 { 1000 progress.Event.CurrentItemName = S._("Emptying recycle bin..."); 1001 task.OnProgressChanged(progress.Event); 1002 1128 1003 ShellAPI.SHEmptyRecycleBin(IntPtr.Zero, null, 1129 1004 ShellAPI.SHEmptyRecycleBinFlags.SHERB_NOCONFIRMATION | … … 1149 1024 1150 1025 /// <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 forensic1157 //detection1158 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 system1164 //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 another1170 //process locking the file. Defer, then rename again.1171 try1172 {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 is1181 //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 same1189 //volume and write it over.1190 if (Manager.ManagerLibrary.Instance.Settings.PlausibleDeniability)1191 {1192 //Get the template file to copy1193 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 do1200 {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 else1215 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 available1224 //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 end1235 if (bytesRead == 0)1236 break;1237 1238 //Dump the read contents onto the file to be deleted1239 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 try1248 {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 another1280 //process locking the file. Defer, then rename again.1281 try1282 {1283 info.MoveTo(newPath);1284 }1285 catch (IOException)1286 {1287 Thread.Sleep(100);1288 --i;1289 }1290 }1291 1292 //Remove the folder1293 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 name1304 PRNG prng = PRNGManager.GetInstance(ManagerLibrary.Instance.Settings.ActivePRNG);1305 byte[] newFileNameAry = new byte[length];1306 prng.NextBytes(newFileNameAry);1307 1308 //Validate the name1309 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 try1328 {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 else1347 result = ((FileInfo)entries[index]).FullName;1348 }1349 1350 return result;1351 }1352 1353 /// <summary>1354 1026 /// The thread object. 1355 1027 /// </summary>
Note: See TracChangeset
for help on using the changeset viewer.