|
|
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.
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.
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.
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");
}