htm=articulo/volio.htm ok articulo/volio.htm

Leer y escribir sectores de disco.

Se describe el modo de realizar lecturas y escrituras directas en los sectores de las particiones o unidades lógicas en DOS, Winodws 9x y NT/W2000/XP.


Los discos usados por los PCs se encuentran formateados en sectores de 512 bytes.   Los sectores de un disco se direccionan por medio del número de pista (cilindro), el número de cabeza y el número de sector.

Las unidades físicas de disco se dividen en particiones, tambien llamados volúmenes o unidades lógicas. Los seccores se direccionan dentro de una unidad lógica por medio del número de sector. Se prescinde de la geometría real del disco y se numeran los sectores de 0 a n-1 -siendo n el número de sectores de la partición. Los sectores lógicos, siguen siendo de 512 bytes.

Una unidad lógica, particion o volumen, existe tras particionar el disco con fdisk o una utilidad similar, independientemente del tipo de particion que se cree, incluso en el caso de que la particion no se haya formateado

Un tipo de abstraccion superior se presenta en las unidades formateadas. En algunos tipos de particiones, los sectores una vez formateada la particion se agrupan en clusters y por lo tanto la unidad mínima que se lee o escribe es de varios KBytes.

Aquí vamos a tratar el acceso (lectura y escritura) a los sectores de una particion. Es decir que se trata el acceso a los sectores de una unidad lógica, direccionado estos sectores  por su número de sector (0, ... n-1) y siendo su tamaño de 512 bytes.

DOS y Windows en sus distintas versiones permite acceder a los sectores de una particion, siempre que la partición esté formateada y sea un tipo de formato reconocido por el SO. Sin embargo el método de acceso  bajo DOS, Windows 9x  y en la familia NT (W2000, ...) es diferente. 


Acceso bajo DOS, código de 16 bits.

DOS provee dos interrupciones (int 25h e int 26h) para leer y escribir sectores de una unidad lógica. Los parámetros (unidad lógica de disco, primer numero de sector, cantidad de sectores y direccion del buffer) se pasan en los registros.

A la vuelta de la interrupcion, los flags se encuentran todavía en la pila (por lo que hay que añadir un POP adicional). Si hay algún error, en bit carry se encuentra activado y el codiog de error se devuelve en AX.

Viendo el código siguiente, no es necesaria más aclaración:

int LeerSectores(
   int nDrive,            //0 = A:  2=C: .....
   int primer_sector,     //el primer sector es el 0
   int numero_sectores,   // contador de sectores, 
   void * buffer)         //puntero de 16 bits, dentro del segmento DS
{
        int  result = 0;
        asm{
                mov ax, nDrive
                mov bx, buffer
                mov cx, numero_sectores
                mov dx, primer_sector
                int 25h
                jnc noerror
                mov result, ax
noerror:
                popf
        }
	return result;      //devuelve 0 si no hay error
}

int GrabarSectores(
   int nDrive,            //0 = A:  2=C: .....
   int primer_sector,     //el primer sector es el 0
   int numero_sectores,   // contador de sectores, 
   void * buffer)         //puntero de 16 bits, dentro del segmento DS
{
        int  result = 0;
        asm{
                mov ax, nDrive
                mov bx, buffer
                mov cx, numero_sectores
                mov dx, primer_sector
                int 26h
                jnc noerror
                mov result, ax
noerror:
                popf
        }
	return result;      //devuelve 0 si no hay error
}

Evidentemente, las INT 25h y 26h usadas de este modo no permiten acceder a sectores situados más allá de los primeros 32 Mb de la partición.

Existe otro modo de usar las INT 25h y 26, poniendo CX = 0xFFFF  y BX apuntando a una estructura auxiliar DISKIO. Este modo de usar las INT 25h y 26h se muestra en el código propuesto para Windows 9x.


Acceso bajo Windows 9x/Me (driver ).

En Windows 95, Windows 98 y Me, existe un driver -VWin32- que permite a cualquier aplicacion ejecutar indirectamente las interrupciones 25h, 26h. Para usar este driver es preciso obtener un handle usando la funcion CreateFile para abrir \\.\vwin32 (En C será necesario doblar cada barra invertida) .  Al terminar, se libera el handle usando la funcion CloseHandle.

HANDLE hVWin32 = INVALID_HANDLE_VALUE;

BOOL OpenVWin32()
{
	hVWin32 = CreateFile(
		"\\\\.\\vwin32",
		GENERIC_READ | GENERIC_WRITE, 
		FILE_SHARE_READ | FILE_SHARE_WRITE, 
		NULL, 
		OPEN_EXISTING, 
		FILE_FLAG_DELETE_ON_CLOSE, 
		NULL);

	return hVWin32 != INVALID_HANDLE_VALUE;
} 
void CloseVWin32()
{
	if(hVWin32 != INVALID_HANDLE_VALUE){
		CloseHandle(hVWin32);
		hVWin32 = INVALID_HANDLE_VALUE;
	}
}

A continuaión se muestra el código necesario para ejecutar las INT 25h y 26h   usando este driver, los registros se envían y se reciben del driver usando la estructura  DIOC_REGISTERS. Se usa la extructura auxiliar DISKIO (veasé el código), esto permite acceder a particiones mayores de 32 Mb.

   //código procedente de la MSDN Library
   #define VWIN32_DIOC_DOS_INT25     2
   #define VWIN32_DIOC_DOS_INT26     3
   #define VWIN32_DIOC_DOS_DRIVEINFO 6

   typedef struct _DIOC_REGISTERS {
       DWORD reg_EBX;
       DWORD reg_EDX;
       DWORD reg_ECX;
       DWORD reg_EAX;
       DWORD reg_EDI;
       DWORD reg_ESI;
       DWORD reg_Flags;
   } DIOC_REGISTERS, *PDIOC_REGISTERS;

   #define CARRY_FLAG 1

   #pragma pack(1)
   typedef struct _DISKIO {
      DWORD  dwStartSector;   // starting logical sector number
      WORD   wSectors;        // number of sectors
      DWORD  dwBuffer;        // address of read/write buffer
   } DISKIO, * PDISKIO;
   #pragma pack()

   /*------------------------------------------------------------------
   ReadLogicalSectors (hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
      Reads sectors from a logical drive.  Uses Int 25h.

   Parameters:
      hDev
         Handle of VWIN32

      bDrive
         The MS-DOS logical drive number. 1 = A, 2 = B, 3 = C, etc.

      dwStartSector
         The first logical sector to read

      wSectors
         The number of sectors to read

      lpSectBuff
         The caller-supplied buffer that will contain the sector data

   Return Value:
      Returns TRUE if successful, or FALSE if failure.

   Comments:
      This function does not validate its parameters.
   ------------------------------------------------------------------*/
   BOOL ReadLogicalSectors (HANDLE hDev,
                            BYTE   bDrive,
                            DWORD  dwStartSector,
                            WORD   wSectors,
                            LPBYTE lpSectBuff)
   {
      BOOL           fResult;
      DWORD          cb;
      DIOC_REGISTERS reg = {0};
      DISKIO         dio = {0};

      dio.dwStartSector = dwStartSector;
      dio.wSectors      = wSectors;
      dio.dwBuffer      = (DWORD)lpSectBuff;

      reg.reg_EAX = bDrive - 1;    // Int 25h drive numbers are 0-based.
      reg.reg_EBX = (DWORD)&dio;
      reg.reg_ECX = 0xFFFF;        // use DISKIO struct

      fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_INT25,
                                ®, sizeof(reg),
                                ®, sizeof(reg), &cb, 0);

      // Determine if the DeviceIoControl call and the read succeeded.
      fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

      return fResult;
   }


   /*------------------------------------------------------------------
   WriteLogicalSectors (hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
      Writes sectors to a logical drive. Uses Int 26h

   Parameters:
      hDev
         Handle of VWIN32

      bDrive
         The MS-DOS logical drive number. 1 = A, 2 = B, 3 = C, etc.

      dwStartSector
         The first logical sector to write

      wSectors
         The number of sectors to write

      lpSectBuff
         The caller-supplied buffer that contains the sector data

   Return Value:
      Returns TRUE if successful, or FALSE if failure.

   Comments:
      This function does not validate its parameters.
   ------------------------------------------------------------------*/
   BOOL WriteLogicalSectors (HANDLE hDev,
                             BYTE   bDrive,
                             DWORD  dwStartSector,
                             WORD   wSectors,
                             LPBYTE lpSectBuff)
   {
      BOOL           fResult;
      DWORD          cb;
      DIOC_REGISTERS reg = {0};
      DISKIO         dio = {0};

      dio.dwStartSector = dwStartSector;
      dio.wSectors      = wSectors;
      dio.dwBuffer      = (DWORD)lpSectBuff;

      reg.reg_EAX = bDrive - 1;    // Int 26h drive numbers are 0-based.
      reg.reg_EBX = (DWORD)&dio;
      reg.reg_ECX = 0xFFFF;        // use DISKIO struct

      fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_INT26,
                                ®, sizeof(reg),
                                ®, sizeof(reg), &cb, 0);

      // Determine if the DeviceIoControl call and the write succeeded.
      fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

      return fResult;
   }

Sin embargo, las INT 25h y 26h tradicionalemnte usadas en DOS, no soportan las nuevas particiones FAT32. A partir de Windows 95 OSR2, junto con las particiones  FAT32, el DOS incluye la INT 21h Function 7305h,  y esta función tambien se puede usar desde Windows 9x (a partir de W95 OSR2) a través del driver VWin32.

El código siguiente muestar cómo leer y escribir sectores usando la INT 21h Function 7305h.

   /*------------------------------------------------------------------
   NewReadSectors(hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
     Reads the specified number of sectors into a caller-supplied
     buffer. Uses Int 21h function 7305h

   Parameters:
     hDev
        Handle of VWIN32

     bDrive
        The MS-DOS logical drive number. 0 = default, 1 = A, 2 = B,
        3 = C, etc.

     dwStartSector
        The first sector to read.

     wSectors
        The number of sectors to read.

     lpSectBuff
        The caller-supplied buffer to read into.

   Return Value:
     Returns TRUE if successful, or FALSE if failure.

   Comments:
     This function does not validate its parameters.  It assumes that
     lpSectBuff is allocated by the caller and is large enough to
     hold all of the data from all of the sectors being read.
   ------------------------------------------------------------------*/
   BOOL NewReadSectors (HANDLE hDev,
                        BYTE   bDrive,
                        DWORD  dwStartSector,
                        WORD   wSectors,
                        LPBYTE lpSectBuff)
   {
     BOOL           fResult;
     DWORD          cb;
     DIOC_REGISTERS reg = {0};
     DISKIO         dio;

     dio.dwStartSector = dwStartSector;
     dio.wSectors      = wSectors;
     dio.lpBuffer      = (DWORD)lpSectBuff;

     reg.reg_EAX = 0x7305;   // Ext_ABSDiskReadWrite
     reg.reg_EBX = (DWORD)&dio;
     reg.reg_ECX = -1;
     reg.reg_EDX = bDrive;   // Int 21h, fn 7305h drive numbers are 1-based

     fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_DRIVEINFO,
                               ®, sizeof(reg),
                               ®, sizeof(reg), &cb, 0);

     // Determine if the DeviceIoControl call and the read succeeded.
     fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

     return fResult;
   }


   /*------------------------------------------------------------------
   NewWriteSectors(hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
     Writes the specified number of sectors from a caller-supplied
     buffer. Uses Int 21h function 7305h

   Parameters:
     hDev
        Handle of VWIN32

     bDrive
        The MS-DOS logical drive number. 0 = default, 1 = A, 2 = B,
        3 = C, etc.

     dwStartSector
        The first sector to write.

     wSectors
        The number of sectors to write.

     lpSectBuff
        The caller-supplied buffer from which to write.

   Return Value:
     Returns TRUE if successful, or FALSE if failure.

   Comments:
     This function does not validate its parameters.  It assumes that
     lpSectBuff is allocated by the caller and is large enough to
     hold all of the data to be written.
   ------------------------------------------------------------------*/
   BOOL NewWriteSectors (HANDLE hDev,
                        BYTE   bDrive,
                        DWORD  dwStartSector,
                        WORD   wSectors,
                        LPBYTE lpSectBuff)
   {
     BOOL           fResult;
     DWORD          cb;
     DIOC_REGISTERS reg = {0};
     DISKIO         dio;

     dio.dwStartSector = dwStartSector;
     dio.wSectors      = wSectors;
     dio.lpBuffer      = (DWORD)lpSectBuff;

     reg.reg_EAX = 0x7305;   // Ext_ABSDiskReadWrite
     reg.reg_EBX = (DWORD)&dio;
     reg.reg_ECX = -1;
     reg.reg_EDX = bDrive;   // Int 21h, fn 7305h drive numbers are 1-based

     reg.reg_ESI = 0x6001;   // Normal file data (See function
                             // documentation for other values)


     fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_DRIVEINFO,
                               ®, sizeof(reg),
                               ®, sizeof(reg), &cb, 0);

     // Determine if the DeviceIoControl call and the write succeeded.
     fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

     return fResult;
   }

En cualquier caso, desde DOS o desde Windows 9x, solo se puede acceder a particiones FAT que son las únicas reconocidas en este sistema operativo.


Acceso bajo NT/W200/XP.

En la familia de sistemas operativos Windows NT (es decir NT, Windows 2000, ...) no existe el driver VWin32, el acceso a bajo niver a las particiones se hace obteniendo un handle por medio de la funcion CreateFile, y operando normalmente con este handle con las funciones ReadFile y WriteFile.

Para obtener el handle, hay que pasarle a la funcion CreateFile el nombre del volumen o particion, el string debe tener la forma (para la unidad C:)    "\\.\C:"        En C/C++ hay que doblar las barras invertidas, por lo que nombre sería algo así:    char nombre_volumen[]="\\\\.\\C:";

HANDLE hVolume = INVALID_HANDLE_VALUE;

BOOL OpenWinNT(LPCSTR volume_name)
{
	hVolume = = CreateFile(
		volume_name,	
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,	
		OPEN_EXISTING,
		0,	
		NULL);
	return hVolume != INVALID_HANDLE_VALUE;
} 

void CloseWinNT()
{
	if(hVolume != INVALID_HANDLE_VALUE){
		CloseHandle(hVWin32);
		hVWin32 = INVALID_HANDLE_VALUE;
	}
}

Solo queda usar ReadFile y WriteFile, tal como se haría con un fichero normal. En el código de ejemplo que se muestra a continuación, se da por supuesto que la funcion Error aborta el programa o lanza una excepción. Además para particiones grandes (más de 4 Gb), será necesario mejorar el tratamiento de SetFilePointer, manejando enteros de 64 bits.

void LeerSectorNT(HANDLE hDev,
                  DWORD  dwStartSector,
                  WORD   wSectors,
                  LPBYTE lpSectBuff)
{
	DWORD  dw;
	BOOL   b;
  
	dw = SetFilePointer(hDev, dwStartSector * 512, NULL, FILE_BEGIN); 
	if(dw != dwStartSector * 512)
		Error("SetFilePointer - ReadDevice");

	b = ReadFile(hDev, lpSectBuff, wSectors * 512, &dw, NULL);
	if(b == FALSE || dw != wSectors * 512)
		Error("ReadFile - ReadDevice");
}

void GrabarSectorNT(HANDLE hDev,
                   DWORD  dwStartSector,
                   WORD   wSectors,
                   LPBYTE lpSectBuff)
{
	DWORD  dw;
	BOOL   b;
  
	dw = SetFilePointer(hDev, dwStartSector * 512, NULL, FILE_BEGIN); 
	if(dw != dwStartSector * 512)
		Error("SetFilePointer - WriteDevice");

	b = WriteFile(hDev, lpSectBuff, wSectors * 512, &dw, NULL);
	if(b == FALSE || dw != wSectors * 512)
		Error("WriteFile - WriteDevice");
}


© info3@maicas.net