//рекурсивынй метод для восстановления и сборки файлов. private void CreateFolderWithFiles(string parentPath, DriveDirectory folder) { foreach (var dir in folder.Directories) { if (!Directory.Exists(parentPath + GetClearFileName(dir.Name))) { Directory.CreateDirectory(parentPath + GetClearFileName(dir.Name)); } CreateFolderWithFiles(parentPath + GetClearFileName(dir.Name) + "\\", dir); } foreach (var file in folder.Files) { if (file.Clasters.Count > 0) //не делаем пустые файлы. Нафиг они не нужны. { FileStream fs = null; try { fs = new FileStream(parentPath + GetClearFileName(file.Name), FileMode.OpenOrCreate); for (int i = 0; i < file.Clasters.Count - 1; i++) { var Data = Read((file.Clasters[i] - 2) * ClasterSize, (int)ClasterSize); fs.WriteAsync(Data, 0, (int)ClasterSize); } //у последнего кластера мы убираем все нули в конце. Иначе файл будет некорректно восстановлен. var LastClasterData = Read((file.Clasters[file.Clasters.Count - 1] - 2) * ClasterSize, (int)ClasterSize); int countOfZeroBytes = 0; while (LastClasterData[LastClasterData.Length - countOfZeroBytes - 1] == 0) { countOfZeroBytes++; } fs.Write(LastClasterData, 0, (int)ClasterSize - countOfZeroBytes); } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); } finally { if (fs != null) { fs.Close(); } } } } }
private void chooseDriveBtn_Click(object sender, RoutedEventArgs e) { byte[] Data; //освобождаем доступ к флешке. if (DiskHandle != null) { DiskHandle.Close(); } #region maxRecoverySize try { MAX_RECOVERING_FILE_SIZE *= int.Parse(MaxRecoverySizeTB.Text); } catch { System.Windows.Forms.MessageBox.Show("Максимальный размер файла указан неверно. Установлено значение по умолчанию: 50 МБ."); MAX_RECOVERING_FILE_SIZE = 50 * 1024 * 1024; } #endregion try { FolderBrowserDialog folderDialog = new FolderBrowserDialog(); if (folderDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { //обновляем букву выбранного диска. tbName.Text = folderDialog.SelectedPath; //приводим имя выбранной флешки к нормальному формату var DiskName = folderDialog.SelectedPath.Split('\\')[0]; #region takingHandle //забираем handle для доступа к диску DiskHandle = CreateFile( lpFileName: @"\\.\" + DiskName, dwDesiredAccess: FileAccess.Read, dwShareMode: FileShare.ReadWrite, lpSecurityAttributes: IntPtr.Zero, dwCreationDisposition: FileMode.OpenOrCreate, dwFlagsAndAttributes: FileAttributes.Normal, hTemplateFile: IntPtr.Zero); #endregion //открываем поток на чтение diskStreamToRead = new FileStream(DiskHandle, FileAccess.Read); //читаем загрузочный сектор Data = Read(0, BOOT_SECTOR_SIZE); var FileSystemType = DecimalToASCII(Data, 82, 89); tbFileSystem.Text = FileSystemType; if (!FileSystemType.Contains("FAT32")) { System.Windows.Forms.MessageBox.Show("Программа работает только с системой FAT32"); return; } #region GetAndDisplaySpace ulong OutFreeBytesAvaliable = 0; ulong OutTotalNumberOfBytes = 0; ulong OutTotalNumberOfFreeBytes = 0; GetDiskFreeSpaceEx( folderDialog.SelectedPath, out OutFreeBytesAvaliable, out OutTotalNumberOfBytes, out OutTotalNumberOfFreeBytes); tbTotal.Text = Math.Round(((double)OutTotalNumberOfBytes / 1024 / 1024 / 1024), 2) + " Гб"; tbFree.Text = Math.Round(((double)OutTotalNumberOfFreeBytes / 1024 / 1024 / 1024), 2) + " Гб"; #endregion #region readOptions long SectorSize = HexToDecimal(Data, 11, 12); long NumberOfSectorsInClaster = HexToDecimal(Data, 13, 13); long NumberOfReservedSectors = HexToDecimal(Data, 14, 15); long NumberOfFatCopies = HexToDecimal(Data, 16, 16); long FatTableSizeInSectors = HexToDecimal(Data, 36, 39); long SectorsInFileSystem = HexToDecimal(Data, 32, 35); #endregion ClasterSize = NumberOfSectorsInClaster * SectorSize; //смещение к корневой директории. StartOfFileData = NumberOfReservedSectors * SectorSize + FatTableSizeInSectors * SectorSize * NumberOfFatCopies; var offsetToFAT = NumberOfReservedSectors * SectorSize; FAT = ReadFAT(offsetToFAT, (int)(FatTableSizeInSectors * SectorSize)); System.Windows.Forms.MessageBox.Show("Начато сканирование устройства."); DriveDirectory root = new DriveDirectory("recovered"); ProcessDirectory(root, 2); //корневая директория - всегда 2 кластер. System.Windows.Forms.MessageBox.Show("Сканирование завершено."); rootRecover = root; //тут нужно всё собрать. } } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); } }
//рекурсивный метод, строящий цепочку из директорий и содержащихся в них файлов private void ProcessDirectory(DriveDirectory directory, long claster) { //нумерация начинается с 2. //claster -= 2; #region calculateDirectoryData List <int> ClastersOfThisDirectody = new List <int>(); //как оказалось, директории могут располагаться не в одном кластере. List <byte[]> clastersData = new List <byte[]>(); while (!IsClasterEOF((int)claster) && !IsClasterFreeInFAT((int)claster)) { ClastersOfThisDirectody.Add((int)claster); clastersData.Add(Read((claster - 2) * ClasterSize, (int)ClasterSize)); claster = GetNextClaster((int)claster); } ClastersOfThisDirectody.Add((int)claster); clastersData.Add(Read((claster - 2) * ClasterSize, (int)ClasterSize)); var Data = clastersData[0]; //данные именно того кластера, который был передан в качестве параметра. for (int i = 1; i < clastersData.Count; i++) { Data = Unify(Data, clastersData[i]); } #endregion //читаем не с нулевой записи, потому что она указывает на текущую директорию. Нафиг надо. for (int i = 32; i < Data.Length - 32; i += 32) { try { var attribute = GetAttribute(Data[i + 11]); if (attribute == FileAttribute.Empty) //нечего читать. { break; } #region shortNameDirectory if (attribute == FileAttribute.Directory) { StringBuilder dirName = new StringBuilder(); long CalculatedClaster = HexToDecimal(new byte[] { Data[i + 26], Data[i + 27], Data[i + 20], Data[i + 21] }); //кластер директории dirName.Append(DecimalToASCII(Data, i + 1, i + 10)); if (Hex(Data[i + 0]).Contains("e5")) //удалённая директория. { dirName.Insert(0, 'Z'); //символ, которым мы заменяем пустое место. if (!IsClasterFreeInFAT((int)CalculatedClaster)) //если кластер, на который указывает директория, затёрт, то её не восстановить. { continue; } } else { dirName.Insert(0, DecimalToASCII(Data, i + 0, i + 0)); } if (dirName.ToString().Contains("..")) //если это указатель на верхнюю директорию, то уходим. { continue; } DriveDirectory dir = new DriveDirectory(dirName.ToString()); directory.Directories.Add(dir); ProcessDirectory(dir, CalculatedClaster); } #endregion #region shortNameFile if (attribute == FileAttribute.Archive) { StringBuilder fileName = new StringBuilder(); fileName.Append(DecimalToASCII(Data, i + 1, i + 10)); if (Hex(Data[i]).Contains("e5")) { fileName.Insert(0, 'Z'); DriveFile file = new DriveFile(); file.Name = fileName.ToString(); long fileSize = CalculateSize(Data, i + 28, i + 31); if (fileSize > MAX_RECOVERING_FILE_SIZE) //не восстанавливаем файлы слишком большого размера. { continue; } //число кластеров, в которых расположен файл. var NumberOfClastersForLocating = fileSize / ClasterSize; if (fileSize % ClasterSize != 0) { NumberOfClastersForLocating++; } var startClasterNumber = HexToDecimal(new byte[] { Data[i + 26], Data[i + 27], Data[i + 20], Data[i + 21] }); //ищем свободные кластеры, начиная с того кластера, на который указывает файл. for (int j = (int)startClasterNumber; NumberOfClastersForLocating > 0 && j < FAT.Length / 4 - 2; j++) { if (IsClasterFreeInFAT(j)) { file.Clasters.Add(j); NumberOfClastersForLocating--; } } if (NumberOfClastersForLocating == 0) { directory.Files.Add(file); } } else { continue; //нечего восстанавливать, если у файла нет пометки. } } #endregion #region LFN if (attribute == FileAttribute.LFN) { int j = i; bool isDeleted = false; //если это имя относится к файлу, то файл должен быть удалён. if (Hex(Data[j + 0]).Contains("e5")) { isDeleted = true; } StringBuilder name = new StringBuilder(" "); //собираем длинное имя. while (GetAttribute(Data[j + 11]) == FileAttribute.LFN && j < Data.Length - 32) { name.Insert(0, DecimalToUnicode(Data, j + 28, j + 31)); name.Insert(0, DecimalToUnicode(Data, j + 14, j + 25)); name.Insert(0, DecimalToUnicode(Data, j + 1, j + 10)); j += 32; } if (GetAttribute(Data[j + 11]) == FileAttribute.Directory) { var CalculatedClaster = HexToDecimal(new byte[] { Data[j + 26], Data[j + 27], Data[j + 20], Data[j + 21] }); DriveDirectory dir = new DriveDirectory(name.ToString()); if (isDeleted) { if (IsClasterFreeInFAT((int)CalculatedClaster)) { directory.Directories.Add(dir); ProcessDirectory(dir, CalculatedClaster); } } else { directory.Directories.Add(dir); ProcessDirectory(dir, CalculatedClaster); } } if (GetAttribute(Data[j + 11]) == FileAttribute.Archive && isDeleted) { DriveFile file = new DriveFile(name.ToString()); var StartClasterNumber = HexToDecimal(new byte[] { Data[j + 26], Data[j + 27], Data[j + 20], Data[j + 21] }); long fileSize = CalculateSize(Data, j + 28, j + 31); //число кластеров, в которых расположен файл. var NumberOfClastersForLocating = fileSize / ClasterSize; if (fileSize % ClasterSize != 0) { NumberOfClastersForLocating++; } if (fileSize < MAX_RECOVERING_FILE_SIZE) { for (int k = (int)StartClasterNumber; NumberOfClastersForLocating > 0 && k < FAT.Length / 4 - 2; k++) { if (IsClasterFreeInFAT(k)) { file.Clasters.Add(k); NumberOfClastersForLocating--; } } if (NumberOfClastersForLocating == 0) { directory.Files.Add(file); } } } i = j; } #endregion } catch (IndexOutOfRangeException e) { System.Windows.Forms.MessageBox.Show(e.Message); // за границу обычно номер кластера. } catch (ContextMarshalException e) { i = (int)ClasterSize; // если мы словили неверный атрибут, когда ходили по директории, то директория была затёрта. } catch (Exception e) { System.Windows.Forms.MessageBox.Show(e.Message); continue; } } }
public DirectoryWithClaster(DriveDirectory directory, long claster) { Directory = directory; Claster = claster; }