2011-08-17 15 views
9

Estoy escribiendo una aplicación C# .Net para ejecutar en Windows que necesita tomar una imagen de un disco extraíble y colocarla en un USB Live de Linux. El Live USB se inserta en la máquina de destino y arranca, al iniciarse ejecuta una secuencia de comandos que usa el comando dd para mostrarlo en otra unidad:Implementación de Windows C# del comando dd de Linux

dd if =/ruta/a/archivo/desde/csharp/programa de =/dev/sdX

El problema que tengo es crear la imagen en el lado de Windows. He probado mi Live Linux con archivos que he creado en un sistema Linux usando dd y funciona bien, pero necesito poder crear estos archivos desde una aplicación C# .Net en Windows. Preferiría no tener que depender de cygwin o de alguna otra dependencia, así que intenté usar la función CreateFile de Win32 para abrir el dispositivo físico.

CreateFile se llama con el primer argumento se ajusta a "\ \ F:." (Si F: es la unidad Quiero imagen), así:

SafeFileHandle TheDevice = CreateFile(_DevicePath, (uint)FileAccess.Read, (uint)(FileShare.Write | FileShare.Read | FileShare.Delete), IntPtr.Zero, (uint)FileMode.Open, (uint)FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero); 
if (TheDevice.IsInvalid) 
{ 
    throw new IOException("Unable to access drive. Win32 Error Code " + Marshal.GetLastWin32Error()); 
} 
FileStream Dest = System.IO.File.Open(_SaveFile, FileMode.Create); 
FileStream Src = new FileStream(TheDevice, FileAccess.Read); 
Src.CopyTo(Dest); 
Dest.Flush(); 
Src.Close(); 
Dest.Close(); 

Pero cuando el archivo de salida es dd De nuevo en un disco con Live Linux USB, el resultado no es el esperado (el disco no es de arranque, etc., pero al examinar el archivo de salida en un editor hexadecimal, parece que hay un MBR al principio, etc.).

¿Es esto un problema con endianess o debería usar algo que no sea un FileStream para copiar los datos en el archivo.

Alternativamente hay un ejemplo de dd para el código fuente de Windows (C# o C++, he mirado el Delphi para http://www.chrysocome.net/dd y no lo entiendo del todo o tengo un IDE Delphi decente para separar el código) así que puede ver cómo funciona eso?

ACTUALIZACIÓN/EDIT:

Aquí es una cadena hexadecimal de los primeros 512 Bytes que la salida dd contiene:

33 C0 FA 8E D8 8E D0 BC 00 7C 89 E6 06 57 8E C0 FB FC BF 00 06 B9 00 01 F3 A5 EA 1F 06 
00 00 52 52 B4 41 BB AA 55 31 C9 30 F6 F9 CD 13 72 13 81 FB 55 AA 75 0D D1 E9 73 09 66 
C7 06 8D 06 B4 42 EB 15 5A B4 08 CD 13 83 E1 3F 51 0F B6 C6 40 F7 E1 52 50 66 31 C0 66 
99 E8 66 00 E8 21 01 4D 69 73 73 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 
65 6D 2E 0D 0A 66 60 66 31 D2 BB 00 7C 66 52 66 50 06 53 6A 01 6A 10 89 E6 66 F7 36 F4 
7B C0 E4 06 88 E1 88 C5 92 F6 36 F8 7B 88 C6 08 E1 41 B8 01 02 8A 16 FA 7B CD 13 8D 64 
10 66 61 C3 E8 C4 FF BE BE 7D BF BE 07 B9 20 00 F3 A5 C3 66 60 89 E5 BB BE 07 B9 04 00 
31 C0 53 51 F6 07 80 74 03 40 89 DE 83 C3 10 E2 F3 48 74 5B 79 39 59 5B 8A 47 04 3C 0F 
74 06 24 7F 3C 05 75 22 66 8B 47 08 66 8B 56 14 66 01 D0 66 21 D2 75 03 66 89 C2 E8 AC 
FF 72 03 E8 B6 FF 66 8B 46 1C E8 A0 FF 83 C3 10 E2 CC 66 61 C3 E8 62 00 4D 75 6C 74 69 
70 6C 65 20 61 63 74 69 76 65 20 70 61 72 74 69 74 69 6F 6E 73 2E 0D 0A 66 8B 44 08 66 
03 46 1C 66 89 44 08 E8 30 FF 72 13 81 3E FE 7D 55 AA 0F 85 06 FF BC FA 7B 5A 5F 07 FA 
FF E4 E8 1E 00 4F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 20 6C 6F 61 64 20 65 72 
72 6F 72 2E 0D 0A 5E AC B4 0E 8A 3E 62 04 B3 07 CD 10 3C 0A 75 F1 CD 18 F4 EB FD 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 19 16 9F 29 00 00 80 01 01 00 06 FE 3F 0E 3F 00 00 00 61 C8 03 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA 

y aquí está lo que mi código produce:

EB 76 90 4D 53 44 4F 53 35 2E 30 00 02 04 04 00 02 00 02 00 00 F8 F2 00 3F 00 FF 00 3F 
00 00 00 61 C8 03 00 80 00 29 7A E8 21 04 4E 4F 20 4E 41 4D 45 20 20 20 20 46 41 54 31 
36 20 20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 E9 05 01 B4 0E 53 33 DB CD 10 5B C3 8A 07 3C 00 74 06 E8 EE FF 43 EB F4 C3 
0D 4E 6F 20 42 50 42 3A 20 43 61 6E 27 74 20 62 6F 6F 74 20 75 73 69 6E 67 20 43 48 53 
20 66 75 6E 63 74 69 6F 6E 73 00 50 B0 2E E8 BC FF 58 33 DB 8E 06 E4 01 F6 06 DC 01 02 
75 42 F6 06 DC 01 04 75 07 80 3E E8 01 80 72 34 53 53 52 50 06 53 55 6A 10 8B F4 52 50 
8A 16 E8 01 B8 00 42 F9 CD 13 8A EC 58 5A 8D 64 10 72 14 80 FD 00 75 0F 03 C5 83 D2 00 
C3 BB 91 00 E8 78 FF F4 EB FD 83 3E 18 00 00 74 F0 52 50 8B CD F7 36 18 00 8B F2 03 D1 
3B 16 18 00 76 06 8B 0E 18 00 2B CE 33 D2 F7 36 1A 00 88 16 E9 01 8B F8 8B D7 51 8A C1 
8D 4C 01 C0 E6 06 0A CE 8A EA 8B 16 E8 01 B4 02 CD 13 59 73 15 80 FC 09 75 0A 49 EB DE 
8A C4 04 30 E8 18 FF B4 00 CD 13 EB D1 58 5A 03 C1 83 D2 00 2B E9 74 07 C1 E1 09 03 D9 
EB 94 C3 00 00 00 00 FA FC E8 00 00 5E 81 EE 85 01 2E 8B 84 E4 01 8E D8 8E C0 8E D0 2E 
C7 84 7C 01 AF 01 2E 89 84 7E 01 B9 00 01 BF 00 00 F3 2E A5 2E FF AC 7C FF BC 00 0A FB 
80 3E E8 01 FF 75 04 88 16 E8 01 83 06 E4 01 20 A1 E0 01 8B 16 E2 01 BD 02 00 E8 E9 FE 
50 52 EB 74 90 00 00 00 00 00 00 00 00 00 00 00 D3 20 00 00 00 30 80 00 FF 00 68 41 00 
40 09 FF 40 5A AC 04 00 00 AC 04 00 00 00 00 12 00 55 AA 

Esto fue tomado de exactamente la misma tarjeta CF sin ningún tipo de edición/escritura, etc. por lo que estoy confundido sobre por qué son tan diferentes, pero ambos e nd con los 55 bytes AA correctos también. ¿Windows destruye los MBR en las tarjetas cuando se accede de esta manera o hay alguna otra cosa extraña bajo el capó de la que no estoy al tanto?

+0

Supongo que utilizar la llamada "dd" de cygwin desde su código C# está fuera de la cuestión? – Greg

+0

Sería un último recurso, el problema que creo que es convertir la ruta //./F: en algo que dd entiende bajo cygwin. Entonces, el problema (para mí) se convierte en cómo dentro de la aplicación C# convertir mi //./F: ruta en/dev/sdX en cygwin. –

+1

@Kragen Creo que lo que está sucediendo aquí es que la versión de Windows está obteniendo el Registro de Inicio de Volumen no el Registro de Inicio Principal. Esto es probablemente porque lo estoy pasando //./F: y F: es solo la primera partición, no el Disco Físico.Sería bueno si alguien más pudiera confirmar si estoy en lo cierto o no antes de entrar en la siguiente pregunta sobre cómo acceder al Disco Físico (en lugar de partición/volumen). –

Respuesta

4

Creo que lo que tiene que funcionar - He intentado esto mismo usando una imagen de disquete de arranque (montado como una unidad virtual usando ImDisk) y el archivo resultante es binario idéntico a la imagen original.

Para completar aquí es el código utilicé (en su entirity):

using System; 
using System.IO; 
using System.Runtime.InteropServices; 
using Microsoft.Win32.SafeHandles; 

namespace ConsoleApplication1 
{ 
    public class Program 
    { 
     const int FILE_ATTRIBUTE_SYSTEM = 0x4; 
     const int FILE_FLAG_SEQUENTIAL_SCAN = 0x8; 

     [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
     public static extern SafeFileHandle CreateFile(string fileName, [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess, [MarshalAs(UnmanagedType.U4)] FileShare fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, int flags, IntPtr template); 

     [STAThread] 
     static void Main() 
     { 
      using (SafeFileHandle device = CreateFile(@"\\.\E:", FileAccess.Read, FileShare.Write | FileShare.Read | FileShare.Delete, IntPtr.Zero, FileMode.Open, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero)) 
      { 
       if (device.IsInvalid) 
       { 
        throw new IOException("Unable to access drive. Win32 Error Code " + Marshal.GetLastWin32Error()); 
       } 
       using (FileStream dest = File.Open("TempFile.bin", FileMode.Create)) 
       { 
        using (FileStream src = new FileStream(device, FileAccess.Read)) 
        { 
         src.CopyTo(dest); 
        } 
       } 
      } 
     } 
    } 
} 

Si esto no funciona, entonces parece indicar que:

  1. Hay un problema con el imagen original.
  2. El problema está en lo que está utilizando la imagen de disco que acaba de escribir.
  3. Hay algunas diferencias sutiles en el trato con el dispositivo específico que está accediendo (aunque no puedo pensar lo)

La causa más probable es el paso 2. ¿Qué es exactamente lo que está haciendo con el imagen de disco resultante?


Actualización: Esto está escrito en los comentarios, pero por lo completo que pensé que me gustaría añadir a mi respuesta - se ve como qué está sucediendo es que el contenido de la primera partición del disco está siendo escrito, cuando en cambio lo que se quiere es el contenido de todo el disco.

Al echar un vistazo a la segunda cadena hexadecimal (el producido por código de ejemplo) en algo así como HxD vemos esto:

ëv.MSDOS5.0..........øò.?.ÿ.?...aÈ..€.)zè!.NO NAME FAT16 .. 
........................................................é..´.S3Û 
Í.[Ê.<.t.èîÿCëôÃ.No BPB: Can't boot using CHS functions.P°.è¼ÿX 
3ÛŽ.ä.ö.Ü..uBö.Ü..u.€>è.€r4SSRP.SUj.‹ôRPŠ.è.¸.BùÍ.ŠìXZ.d.r.€ý.u. 
.ŃÒ.û‘.èxÿôëýƒ>...tðRP‹Í÷6..‹ò.Ñ;...v.‹...+Î3Ò÷6..ˆ.é.‹ø‹×QŠÁ. 
L.Àæ..Ίê‹.è.´.Í.Ys.€ü.u.IëÞŠÄ.0è.ÿ´.Í.ëÑXZ.ÁƒÒ.+ét.Áá..Ùë”Ã.... 
úüè..^.î…..‹„ä.ŽØŽÀŽÐ.Ç„|.¯..‰„~.¹..¿..ó.¥.ÿ¬|ÿ¼..û€>è.ÿu.ˆ.è.ƒ. 
ä. ¡à.‹.â.½..èéþPRët............Ó ...0€.ÿ[email protected]ÿ@Z¬...¬.......Uª 

Esto se parece a mí como el boot sector of a FAT16 partition - la presencia de las cadenas "MSDOS5.0", "NO NAME" y "FAT16" cerca del inicio es un obsequio irrecuperable.

comparar esto con la salida de la primera cadena hexadecimal (el producido por dd):

3ÀúŽØŽÐ¼.|‰æ.WŽÀûü¿..¹..ó¥ê....RR´A»ªU1É0öùÍ.r..ûUªu.Ñés.fÇ...´B 
ë.Z´.Í.ƒá?Q.¶Æ@÷áRPf1Àf™èf.è!.Missing operating system...f`f1Ò». 
|fRfP.Sj.j.‰æf÷6ô{Àä.ˆáˆÅ’ö6ø{ˆÆ.áA¸..Š.ú{Í..d.faÃèÄÿ¾¾}¿¾.¹ .ó¥ 
Ãf`‰å»¾.¹..1ÀSQö.€[email protected]‰ÞƒÃ.âóHt[y9Y[ŠG.<.t.$.<.u"f‹G.f‹V.f.Ðf!Òu. 
f‰Âè¬ÿr.è¶ÿf‹F.è ÿƒÃ.âÌfaÃèb.Multiple active partitions...f‹D.f. 
F.f‰D.è0ÿr..>þ}Uª.….ÿ¼ú{Z_.úÿäè..Operating system load error...^ 
¬´.Š>b.³.Í.<.uñÍ.ôëý......................................Ÿ)..€. 
...þ?.?...aÈ..................................................Uª 

Y vemos algo que se parece a mí muy parecido a un master boot record. ¿Por qué? Porque en el MBR todos los primeros 440 bytes son códigos de arranque, a diferencia de un sector de arranque FAT que contiene el bloque de parámetros de BIOS distintivo (parece basura arriba, pero si lo pones en un desensamblador obtienes algo que parece válido 16 bit código).

Además, ambos parecen sectores de arranque válidos y completamente diferentes (completos con mensajes de error). No hay forma de que un error de programación pueda haber "destrozado" uno para parecerse al otro, simplemente debe ser que se está leyendo algo incorrecto.


el fin de obtener CreateFile para devolver el disco en lugar de la partición parece que sólo tiene que pasar una cadena diferente, por ejemplo @"\\.\PhysicalDrive0" abre el primer disco físico.

Ver:

+0

Información de fondo, que explicará lo que estoy haciendo con más detalle. Nuestros viejos sistemas solían arrancar desde una tarjeta CF formateada FAT, por lo que para actualizar el software en el sistema, simplemente reflash la tarjeta CF usando una aplicación de Windows. El nuevo sistema no tiene ningún soporte para arrancar desde tarjetas CF, pero sí tiene un disco duro y un puerto USB. El plan era, como antes, usar la aplicación de Windows para flashear la tarjeta CF, y luego tomar una imagen de esa tarjeta CF (en Windows con mi aplicación C#) copiarla en el Live USB que luego mostraría el disco duro, desde el Entorno Live Linux usando dd, con la imagen de la tarjeta CF –

+0

No creo que sea el paso 2, como si pusiera una imagen que he creado desde el CF usando una VM Linux y dd en el Live USB. –

+0

@rb_ 7 Ah, veo, por lo que la imagen finalmente se escribe en un disco duro. En ese caso, no estoy seguro de por qué no está funcionando; siempre que el primer sector (512 bytes) finalice con los bytes de firma del "bootsector válido" (0x55, 0xAA), el BIOS debería continuar y reiniciar utilizando ese sector de arranque. Mi ** conjetura ** sería que el BIOS está usando ese sector de arranque, pero las diferencias de hardware significan que el cargador de arranque (que anteriormente funcionaba en el sistema anterior) está fallando en el nuevo sistema. – Justin

1

Esto es lo que he escrito para hacerlo llegar al \ \ ruta PhysicalDriveX para una letra de unidad dada.. Si paso la letra de la unidad a esto y tomo el valor de retorno y paso a CreateFile como el primer Param, ahora debería obtener algo similar a dd en Linux.

using System.Management; //Add in a reference to this as well in the project settings 
public static string GetPhysicalDevicePath(char DriveLetter) 
{ 
    ManagementClass devs = new ManagementClass(@"Win32_Diskdrive"); 
    { 
     ManagementObjectCollection moc = devs.GetInstances(); 
     foreach(ManagementObject mo in moc) 
     { 
      foreach (ManagementObject b in mo.GetRelated("Win32_DiskPartition")) 
      { 
       foreach (ManagementBaseObject c in b.GetRelated("Win32_LogicalDisk")) 
       { 
        string DevName = string.Format("{0}", c["Name"]); 
        if (DevName[0] == DriveLetter) 
         return string.Format("{0}", mo["DeviceId"]); 
       } 
      } 
     } 
    } 
    return ""; 
} 
Cuestiones relacionadas