Страницы

Поиск по вопросам

вторник, 27 ноября 2018 г.

Склонировать диск (в т.ч системный) в ISO

Позволит ли .NET стек создать что-то подобное?
Или нужно или стоит пользоваться другими языками программирования и технологиями для решения поставленной задачи. Подозреваю что что-то вроде плюсов, ибо приходится работать наверняка с чем-то низко системным.


Ответ

Образ ISO - это простая посекторная копия диска (точнее, раздела, или "дорожки", применительно к CD), без сжатия и каких-либо служебных заголовков. Название произошло от файловой системы ISO-9660, которая была одной из первых файловых систем для CD, но фактически образ ISO может содержать любую файловую систему в зависимости от типа носителя: UDF для DVD, FAT для дискет, FAT32/NTFS для флешек и т.п. Чтобы создать такой образ, достаточно открыть диск на чтение и переписать все содержимое в файл, поэтому потребуется лишь несколько функций для работы с файлами из WinAPI. Да, конечно, это можно сделать в .NET.
Создать образ CD-ROM или другого подобного незаписываемого носителя довольно просто:
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace ConsoleApplication1 { class Program { [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetDiskFreeSpace(string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters, out uint lpTotalNumberOfClusters);
const uint GENERIC_READ = 0x80000000; const uint FILE_SHARE_READ = 0x00000001; const uint FILE_SHARE_WRITE = 0x00000002; const uint OPEN_EXISTING = 3; const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr CreateFile( [MarshalAs(UnmanagedType.LPTStr)] string filename, [MarshalAs(UnmanagedType.U4)] uint access, [MarshalAs(UnmanagedType.U4)] uint share, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] uint creationDisposition, [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, IntPtr templateFile);
[DllImport("kernel32.dll", SetLastError = true)] static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr hObject);
static void CreateISO(string letter, string outfile) { byte[] buf; uint dwRead = 0; ulong count = 0; uint dwSectorsPerCluster, dwBytesPerSector, dwNumberOfFreeClusters, dwTotalNumberOfClusters; uint blocksize; ulong totalsize;
const uint SECTOR_SIZE = 2048; //размер сектора для CD string fmt = "\\\\.\\{0}:"; //формат имени файла для открытия тома
//найдем размер сектора и объем данных на диске bool res = GetDiskFreeSpace(letter + ":\\", out dwSectorsPerCluster, out dwBytesPerSector, out dwNumberOfFreeClusters, out dwTotalNumberOfClusters);
//вычислим размер блока, кратный размеру сектора, и общий объем данных в байтах if (res) { blocksize = dwBytesPerSector * 20; totalsize = dwSectorsPerCluster * dwBytesPerSector * (ulong)dwTotalNumberOfClusters; } else { Console.WriteLine("GetDiskFreeSpace error {0}", Marshal.GetLastWin32Error()); blocksize = SECTOR_SIZE * 20; totalsize = 4700307456; //DVD } buf = new byte[blocksize];
string volume = String.Format(fmt, letter);
//открываем том для прямого доступа на чтение var handle = CreateFile(volume, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (handle.ToInt64() == -1) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
//открываем файл для записи var target = new System.IO.FileStream(outfile, System.IO.FileMode.Create);
try { while (true) { dwRead = 0; Array.Clear(buf, 0, buf.Length);
//считаем блок данных res = ReadFile(handle, buf, blocksize, out dwRead, IntPtr.Zero); if (res == false) { Console.WriteLine(); Console.WriteLine("ReadFile error {0}", Marshal.GetLastWin32Error()); break; }
if (dwRead == 0) break; //конец считывания
count += dwRead;
Console.Write(" Creating image: {0}% [{1} KB / {2} KB]
", (count * 100.0f / totalsize).ToString("F1").PadLeft(5), (count / 1024.0f).ToString("F2").PadLeft(10), (totalsize / 1024.0f).ToString("F2").PadLeft(10));
//запишем блок в файл target.Write(buf, 0, (int)dwRead); }
} finally { CloseHandle(handle); target.Close(); }
Console.WriteLine(); }
static void Main(string[] args) { CreateISO("E", "c:\\distr\\image.iso"); Console.ReadKey(); } } }
Для создания образа записываемого носителя (флешки и т.п.), необходимо сначала попросить пользователя закрыть все программы, которые его используют, и открывать диск монопольно (0 вместо FILE_SHARE_READ | FILE_SHARE_WRITE). Если этого не сделать, во время создания образа часть информации на диске может быть параллельно изменена другой программой, и образ получится в итоге в некорректном состоянии. Альтернативное решение этой проблемы - использование Volume Shadow-Copy Services (VSS).
Что касается жестких дисков, то создавать ISO-образ таким методом для них не выглядит хорошей идеей. Во-первых, открыть жесткий диск эксклюзивно не всегда получится (а системный и вовсе никогда). Во-вторых, формат ISO для них просто не предназначен, и многие утилиты вообще не смогут обработать ISO-образ для жесткого диска. Вместо этого лучше использовать специальный формат для образов жестких дисков, например VHD. Он поддерживается как средствами Windows, так и сторонними продуктами типа Акрониса. Опять же, для программного создания таких образов можно не изобретать что-то свое, а воспользоваться стандартной утилитой командной строки wbadmin, которая автоматически умеет использовать VSS и создавать образ в том числе системного диска.

Комментариев нет:

Отправить комментарий