Hola a tutti,
siccome è un'argomento abbastanza intricato sul quale sto sbattendo la testa da ieri per fare qualche esperimento con la tabella delle partizioni e la fat32/ntfs posto qua il codice necessario con delle spiegazioni!

*** Accesso ai dischi

[TEORIA]
Windows NT e successivi, quindi windows 2000, windows XP e windows 2003, permettono l'accesso al disco fisico utilizzano le normali api utilizzate per la gestione dei file, ovvero CreateFile, CloseHandle, ReadFile, WriteFile e cosi via.
Questo permette, soprattutto a chi ha confidenza con l'utilizzo di queste api, di essere infinitamente facilitato! Infatti al posto di mettere, ad esempio, come path c:\file_da_leggere.xyz si mette la path UNC relativa all'harddisk o alla partizione ed è possibile accederci per leggere e modificare il contenuto.
Cosa sono le path UNC? Sono path particolari che permettono di accedere a tutti gli oggetti disponibili nel sistema, ovvero computer remoti, harddisk, partizioni, named pipes, periferiche usb, drivers che espongono un'oggetto e cosi via.
Utilizzandole, tramite createfile, è possibile fare tutto e di più! Ovviamente è necessario avere un minimo di conoscenze per potersi muovere in questo campo.
Purtroppo non è possibile utilizzare il .NET direttamente per accedere ai dischi perché non permette di utilizzare le path UNC e FileStream, anche se supporta gli handle, va ad eseguire operazioni non supportate dai dischi o da altre periferiche in generale quindi è necessario utilizzare direttamente le api

[CODICE]
Il codice è relativamente semplice è l'ho assemblato prendendolo da pinvoke, msdn e qualche modifichetta mia di piccola portata.

codice:
	#region Win32 IO Enumerations
	[Flags]
	public enum FileAccess : uint
	{
		GenericRead = 0x80000000,
		GenericWrite = 0x40000000,
		GenericExecute = 0x20000000,
		GenericAll = 0x10000000,
	}

	[Flags]
	public enum FileShare : uint
	{
		None = 0x00000000,
		Read = 0x00000001,
		Write = 0x00000002,
		Delete = 0x00000004,
	}

	public enum CreationDisposition : uint
	{
		New = 1,
		CreateAlways = 2,
		OpenExisting = 3,
		OpenAlways = 4,
		TruncateExisting = 5,
	}

	[Flags]
	public enum FileAttributes : uint
	{
		Readonly = 0x00000001,
		Hidden = 0x00000002,
		System = 0x00000004,
		Directory = 0x00000010,
		Archive = 0x00000020,
		Device = 0x00000040,
		Normal = 0x00000080,
		Temporary = 0x00000100,
		SparseFile = 0x00000200,
		ReparsePoint = 0x00000400,
		Compressed = 0x00000800,
		Offline= 0x00001000,
		NotContentIndexed = 0x00002000,
		Encrypted = 0x00004000,
		Write_Through = 0x80000000,
		Overlapped = 0x40000000,
		NoBuffering = 0x20000000,
		RandomAccess = 0x10000000,
		SequentialScan = 0x08000000,
		DeleteOnClose = 0x04000000,
		BackupSemantics = 0x02000000,
		PosixSemantics = 0x01000000,
		OpenReparsePoint = 0x00200000,
		OpenNoRecall = 0x00100000,
		FirstPipeInstance = 0x00080000
	}
	#endregion

	/// <summary>
	/// Descrizione di riepilogo per IOWin32Helper.
	/// </summary>
	public class IOWin32Helper
	{
		#region Win32 IO DllImport
		[DllImport("kernel32", SetLastError = true)]
		static extern unsafe System.IntPtr CreateFile
		(
			string FileName,							// file name
			FileAccess DesiredAccess,					// access mode
			FileShare ShareMode,						// share mode
			IntPtr SecurityAttributes,					// Security Attributes
			CreationDisposition CreationDisposition,	// how to create
			FileAttributes FlagsAndAttributes,			// file attributes
			IntPtr hTemplateFile						// handle to template file
		);

		[DllImport("kernel32", SetLastError = true)]
		static extern unsafe bool ReadFile
		(
			IntPtr hObject,								// handle to file
			void* pBuffer,								// data buffer
			int NumberOfBytesToRead,					// number of bytes to read
			int* pNumberOfBytesRead,					// number of bytes read
			int Overlapped								// overlapped buffer
		);

		[DllImport("kernel32", SetLastError = true)]
		static extern unsafe bool WriteFile
		(
			IntPtr hObject,								// handle to file
			void* pBuffer,								// data buffer
			int NumberOfBytesToWrite,					// number of bytes to write
			int* pNumberOfBytesWritten,					// number of bytes written
			int Overlapped								// overlapped buffer
		);

		[DllImport("kernel32", SetLastError = true)]
		static extern unsafe bool CloseHandle
		(
			IntPtr hObject								// handle to object
		);
		#endregion

		public static unsafe IntPtr Create(string FileName, FileAccess FileAccess, FileShare FileShare, CreationDisposition CreationDisposition, FileAttributes FileAttributes)
		{
			// Apre il file in base ai parametri richiesti
			IntPtr handle = IOWin32Helper.CreateFile(FileName, FileAccess, FileShare, IntPtr.Zero, CreationDisposition, FileAttributes, IntPtr.Zero);

			// Verifica se l'operazione è riuscita
			if (handle == IntPtr.Zero)
			{
				// Lancia un'eccezione
				Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
			}

			// Restituisce il puntatore
			return handle;
		}

		public static unsafe int Read(IntPtr hObject, byte[] Buffer, int Index, int Count)
		{
			// Bytes letti
			int readBytes = 0;

			// Acquisisce il puntatore del buffer
			fixed (byte* pBuffer = Buffer)
			{
				// Legge id ati
				if (ReadFile(hObject, pBuffer + Index, Count, &readBytes, 0) == false)
				{
					// Lancia un'eccezione dato che l'operazione è fallita
					Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
				}
			}

			// Restituisce il numero di bytes letti
			return readBytes;
		}

		public static unsafe int Write(IntPtr hObject, byte[] Buffer, int Index, int Count)
		{
			// Bytes letti
			int writtenBytes = 0;

			// Acquisisce il puntatore del buffer
			fixed (byte* pBuffer = Buffer)
			{
				// Scrive i dati
				if (WriteFile(hObject, pBuffer + Index, Count, &writtenBytes, 0) == false)
				{
					// Lancia un'eccezione dato che l'operazione è fallita
					Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
				}
			}

			// Restituisce il numero di bytes letti
			return writtenBytes;
		}

		public static unsafe bool Close(IntPtr hObject)
		{
			// Chiude l'handle
			return CloseHandle(hObject);
		}
	}
Buona parte del codice postato sono dichiarazioni varie. Il codice vero e proprio è verso la fine e per la precisione, al momento, sono 4 metodi che si riferiscono a 4 API. A breve ne aggiungerò altre per spostare il puntatore, altrimenti non si ci può spostare, ad esempio, a inizio partizione o altro ... in pratica va implementato il Seek

L'unico codice che spiego sarà quello di ReadFile e WriteFile perché CloseHandle è lampante e CreateFile lo è pure se è stato usato, almeno un po, il namespace System.IO ovvero se si ha lavorato con i file

ReadFile e WriteFile sono identiche ma fanno, ovviamente, due operazioni ben distinte: la prima legge dei dati dall'handler passato e li scrive, per la lunghezza richiesta, nel puntatore passato restituendo il numero di byte letti, mentre la seconda scrive i byte passati nel puntatore sul disco per il numero di byte richiesti restituendo il numero di byte effettivamente scritti. Entrambe iniziano a lavorare dalla posizione corrente che all'inizio è ZERO e siccome a noi interessa partire da zero per leggere il primo settore del disco questo ci sta più che bene!

IMPORTANTISSIMO: va eseguita una lettura/scrittura "allineata" con i settori del disco quando si lavora con queste periferiche! Ovvero vanno SEMPRE letti e SEMPRE scritti 512 byte altrimenti scoppia tutto (Si un settore è di 512 byte e le dimensioni dei cluster sono multipli di 512 per questo motivo, altrimenti fare i calcoli corretti ad ogni scrittura/lettura sarebbe troppo pesante ed il numero di operazioni sarebbe parecchio elevato!). Ovviamente anche quando si ci sposta si ci sposta allineandosi ai settori! Anche per questo motivo all'interno dell'MBR dei dischi non ci sta scritto la posizione in byte di inizio e fine partizione ma la posizione in facce, cilindri e settori (applicando una formuletta si ottiene la posizione sul disco ... anche se qua poi entra in ballo pure la gestione dell'LBA e quindi la cosa un po cambia ma questo è un'altro discorso)

Questo ci permette quindi di fare
[/CODE]
Quanto spiegato fino ad adesso ci permette di fare
IntPtr diskHandle = IOWin32Helper.Create(
Disk,
FileAccess.GenericRead,
FileShare.Read | FileShare.Write,
CreationDisposition.OpenExisting,
FileAttributes.Device | FileAttributes.NoBuffering | FileAttributes.Write_Through);
[/CODE]

accedendo instantaneamente al disco in sola lettura, permettendo ad altri applicativi di leggere e scrivere, forzando ad aprire un'elemento già esistente senza crearlo in caso di assenza e per finire dicendogli che il "file" o oggetto in questione è una Device, non deve usare il buffering e deve quindi scrivere immediatamente sul disco (anche se quest'ultima opzione non ci interessa dato che non dobbiamo scrivere)

continua...