Dump, una pequeña herramienta

Esta es una pequeña herramienta que permite copiar un archivo en otro. Ya se que, a simple vista, no parece tener mucha utilidad, pero la verdad es que sabiéndola usar nos permitirá hacer algunas cosas muy interesantes.

El codigo (es una aplicion de consola):

program dump;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils,
  Classes;
 
type
  EGetOptError = class(Exception);
 
var
  Options: record
    BlockSize: Integer;
    Count: Integer;
    InputFile: String;
    InputMode: Integer;
    Mult: Integer;
    OutputFile: String;
    OutputMode: Integer;
  end;
 
procedure WriteHelp;
begin
  Writeln;
  Writeln('Uso: dump [opciones]');
  Writeln('  --bs');
  Writeln('    Numero de bytes que componen un bloque');
  Writeln('  --count');
  Writeln('    Numero de bloques a copiar');
  Writeln('  --if');
  Writeln('    Ruta del archivo de entrada');
  Writeln('  --of');
  Writeln('    Ruta del archivo de salida');
  Writeln('  --im');
  Writeln('    Modo de apertura del archivo de entrada');
  Writeln('  --om');
  Writeln('    Modo de apertura del archivo de salida');
  Writeln('  --x');
  Writeln('    Factor por el que multiplicar --bs (1=Kb, 2=Mb, 3=Gb)');
  Writeln;
  Writeln('Posibles valores para "--im" y "--om":');
  Writeln('  01	Crea un archivo nuevo (Create)');
  Writeln('  02	Abre el archivo solo para lectura (OpenRead)');
  Writeln('  04	Abre el archivo solo para escritura (OpenWrite)');
  Writeln('  08	Abre el archivo para su modificacion (OpenReadWrite)');
  Writeln('Se pueden combinar con los siguientes valores:');
  Writeln('  16	Otras aplicaciones pueden leer y escribir el archivo (ShareDenyNone)');
  Writeln('  32	Otras apliaciones pueden leer el archivo (ShareDenyWrite)');
  Writeln('  64	No permite que otras aplicaciones abran el archivo (ShareExclusive)');
  Writeln('El valor por defecto para "--im" es 18, y para "--om" es 1');
  Writeln;
  Writeln('Ejemplo:');
  Writeln('  dump --if=\\.\F: --of=imagen.bin --bs=4096');
  Writeln('    Guarda una imagen del disco F en el archivo "imagen.bin"');
  Writeln;
end;
 
function Starts(Sub, Str: String): Boolean;
begin
  Result:= SameText(Sub,Copy(Str,1,Length(sub)));
end;
 
procedure Getopt;
var
  i: Integer;
  Str: String;
begin
  FillChar(Options,Sizeof(Options),#0);
  Options.Count:= -1;
  if ParamCount > 0 then
    for i:= 1 to ParamCount do
    begin
      Str:= ParamStr(i);
      if (Copy(Str,1,2) = '--') and (Length(Str) > 2) then
      begin
        if Starts('--if=',Str) then
        begin
          if Options.InputFile = EmptyStr then
            Options.InputFile:= Copy(Str,Length('--if=') + 1,MAXINT)
          else
            raise EGetOptError.Create(Str);
        end else if Starts('--of=',Str) then
        begin
          if Options.OutputFile = EmptyStr then
            Options.OutputFile:= Copy(Str,Length('--of=') + 1,MAXINT)
          else
            raise EGetOptError.Create(Str);
        end else if Starts('--bs=',Str) then
        begin
          if Options.BlockSize = 0 then
            Options.BlockSize:= StrToInt(Copy(Str,Length('--bs=') + 1,MAXINT))
          else
            raise EGetOptError.Create(Str);
        end else if Starts('--count=',Str) then
        begin
          if Options.Count = -1 then
            Options.Count:= StrToInt(Copy(Str,Length('--count=') + 1,MAXINT))
          else
            raise EGetOptError.Create(Str);
        end else if Starts('--im=',Str) then
        begin
          if Options.InputMode = 0 then
          begin
            case StrToInt(Copy(Str,Length('--im=') + 1,MAXINT)) and $0F of
              1: Options.InputMode:= fmCreate;
              2: Options.InputMode:= fmOpenRead;
              4: Options.InputMode:= fmOpenWrite;
              8: Options.InputMode:= fmOpenReadWrite;
            else
              raise EGetOptError.Create(Str);
            end;
            case StrToInt(Copy(Str,Length('--im=') + 1,MAXINT)) and $F0 of
              0: ; 
              16: Options.InputMode:= Options.InputMode + fmShareDenyNone;
              32: Options.InputMode:= Options.InputMode + fmShareDenyWrite;
              64: Options.InputMode:= Options.InputMode + fmShareExclusive;
            else
              raise EGetOptError.Create(Str);
            end;
          end else
            raise EGetOptError.Create(Str);
        end else if Starts('--om=',Str) then
        begin
          if Options.OutputMode = 0 then
          begin
            case StrToInt(Copy(Str,Length('--om=') + 1,MAXINT)) and $0F of
              1: Options.OutputMode:= fmCreate;
              2: Options.OutputMode:= fmOpenRead;
              4: Options.OutputMode:= fmOpenWrite;
              8: Options.OutputMode:= fmOpenReadWrite;
            else
              raise EGetOptError.Create(Str);
            end;
            case StrToInt(Copy(Str,Length('--om=') + 1,MAXINT)) and $F0 of
              0: ;
              16: Options.OutputMode:= Options.OutputMode + fmShareDenyNone;
              32: Options.OutputMode:= Options.OutputMode + fmShareDenyWrite;
              64: Options.OutputMode:= Options.OutputMode + fmShareExclusive;
            else
              raise EGetOptError.Create(Str);
            end;
          end else
            raise EGetOptError.Create(Str);
        end else if Starts('--x=',Str) then
        begin
          if Options.Mult = 0 then
            case StrToInt(Copy(Str,Length('--x=') + 1,MAXINT)) of
              1: Options.Mult:= 1024;
              2: Options.Mult:= 1024*1024;
              3: Options.Mult:= 1024*1024*1024;
            else
              raise EGetOptError.Create(Str);
            end
          else
            raise EGetOptError.Create(Str);
        end else if SameText('--help',Str) then
        begin
          WriteHelp;
          Halt;
        end else
          raise EGetOptError.Create(Str);
      end else if (Copy(Str,1,1) = '-') and (Length(Str) > 1) then
      begin
        if SameText('-h',Str) then
        begin
          WriteHelp;
          Halt;
        end else
          raise EGetOptError.Create(Str);
      end else
      begin
        raise EGetOptError.Create(Str);
      end;
    end else
    begin
      WriteHelp;
      Halt;
    end;
end;
 
procedure WriteHeader;
begin
  Writeln;
  Writeln('Dump 0.2 - Copyright (c) 2007 Domingo Seoane');
  Writeln('<a href="http://delphi.jmrds.com'">http://delphi.jmrds.com'</a>);
  Writeln;
end;   
 
var
  i: Integer;
  Buffer: PChar;
  Src, Dst: TFileStream;
begin
  WriteHeader;
  try
    GetOpt;
    if Options.Mult > 0 then
      Options.BlockSize:= Options.BlockSize * Options.Mult;
    if Options.BlockSize < 1 then
      Options.BlockSize:= 8 * 1024 * 1024;
    if Options.InputMode = 0 then
      Options.InputMode:= fmOpenRead or fmShareDenyNone;
    if Options.OutputMode = 0 then
      Options.OutputMode:= fmCreate;
    GetMem(Buffer,Options.BlockSize);
    try
      Src:= TFileStream.Create(Options.InputFile,Options.InputMode);
      try
        Dst:= TFileStream.Create(Options.OutputFile,Options.OutputMode);
        try
          i:= Src.Read(Buffer^,Options.BlockSize);
          while i > 0 do
          begin
            Dst.WriteBuffer(Buffer^,i);
            if Options.Count > 0 then
            begin
              dec(Options.Count);
              if Options.Count = 0 then
                break;
            end;
            i:= Src.Read(Buffer^,Options.BlockSize);
          end;
        finally
          Dst.Free;
        end;
      finally
        Src.Free;
      end;
    finally
      FreeMem(Buffer);
    end;
  except
    On E: Exception do
      Writeln('Error: ' + E.Message);
  end;
end.

El código y el programa ya compilado puedes bajarlo de aquí

La primera utilidad que se me ocurre es crear una imagen de un disco duro, disquete, memoria usb o incluso un Cdrom. Solo hay que recordar que para leer este tipo de dispositivos hay que hacerlo en bloques de un tamaño determinado, normalmente ese tamaño tiene que cumplir la condición de ser múltiplo de 512 (el tamaño de un sector). Esto se explica mejor con un ejemplo:

dump --if=\\.\F: --of=imagen.bin --bs=4096
Guarda una imagen del disco F en el archivo "imagen.bin"
dump --of=\\.\a: --if=imagen.bin --bs=1 --x=2 --om=24
Graba una imagen en un disquete, solo hay que tener en cuenta que para escribir en un disco el parámetro "--om" debe ser 24.

ACTUALIZACIÓN: En Windows Vista y Windows 7 para poder escribir en un disco de debe ejecutar la aplicación como administrador, y tener acceso exclusivo. Por eso hay que utilizar el parámetro "--om=72", asegurándose primero de que ninguna otra aplicación esta utilizando el disco.

Comentarios

Hola Domingo,

Como siempre, muy interesante tu entrada. Te felicito por ello. Saludos.

PD. Hay una errata que impide bajar el programa siguiendo el enlace: en lugar de "href" verás que pone "hre".

Me he llevado una sorpresa. Había visto que un dd para Windows que anda por ahí usaba esa notación para las unidades de disco. Pero pensaba que era algo que hace el programa, no sabía que lo permite el sistema operativo.

Por cierto que está chulo el programita :-)

Si, mucha gente no lo sabe pero windows permite leer varios dispositivos como si de archivos se trataran (discos, puerto serie, ...). Y aunque no llega al nivel de linux, en que "casi todo" se puede tratar como un archivo (solo hay que ver la carpeta "/dev"), si tiene posibilidades interesantes, como borrar ... perdón, copiar el MBR, jejeje. Solo hay que tener cuidado con las características propias de cada dispositivo, por ejemplo, los discos solo se pueden leer en bloques múltiplos de 512 y se deben de abrir con los permisos que menciono arriba, el puerto serie no soporta la función seek, etc ...

Si te interesa el tema, en este otro articulo sobre como recuperar fotos jpg borradas de un disco, puedes ver un código que se puede compilar tanto en windows como en linux (freepascal) y la única diferencia entre usarlo en windows o en linux es que mientras en windows el parámetro "--if" seria algo así "\\.\C:" en linux seria así "/dev/hda1".

Fabuloso :)

Sí, fabuloso, aunque ¡¡¡no se puede descargar el archivo!!! :D:D:D

Vaya, no perdonáis una, jejeje. Era domingo por la tarde y una "f" se le olvida a cualquiera. El enlace ya funciona.

El comando dd tambien está para Windows, y usa una sintaxis de unidades como la que expones:
http://www.chrysocome.net/dd
Por otra parte: joder, qué bueno eres.