2.3. Доступ к дискам с использованием Win32 api
Функция DeviceIoControl, осуществляет взаимодействие непосредственно с драйверами устройств, позволяя, в том числе, получить информацию о физической геометрии диска, разбиении диска на разделы, а также многое другое.
В функцию передается дескриптор устройства, для получения которого можно воспользоваться функцией CreateFile (см. л.р. №5), передав ей имя файла, в виде \\.\С: - для доступа, например, к тому С, или \\.\PhysicalDrive0 - для доступа к физическому жесткому диску 0. (В тексте программы на языке Си символы «обратная косая черта» должны быть повторены дважды, например «\\\\.\\С:». Также следует отметить, что для доступа к дискам в Windows NT и выше необходимо иметь права доступа администратора системы).
BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
hDevice - Дескриптор устройства - обычно том, каталог, файл или поток.
dwIoControlCode - Управляющий код для операции. Это значение идентифицирует конкретную операцию для выполнения и тип устройства, на котором она должна осуществиться. Краткий перечень управляющих кодов приведен в таблице 5:
Таблица 5 – Краткий перечень кодов операций для функции DeviceIoControl
Код операции |
Описание |
IOCTL_DISK_CHECK_VERIFY |
Проверка файла носителя данных для устройства или замены |
IOCTL_DISK_GET_DRIVE_GEOMETRY |
Получение информации о физической геометрии |
IOCTL_DISK_GET_DRIVE_LAYOUT |
Получение информации о разделах диска |
IOCTL_DISK_FORMAT_TRACKS |
Форматирование нескольких дорожек диска |
IOCTL_DISK_GET_PARTITION_INFO |
Получение информации о разделе диска |
lpInBuffer - Указатель на буфер ввода данных, который содержит данные необходимые, чтобы, выполнить операцию. Формат этих данных зависит от значения параметра dwIoControlCode. Этот параметр может быть установлен в NULL, если dwIoControlCode определяет операцию, которая не требует ввода данных.
nInBufferSize - Размер буфера ввода данных, в байтах.
lpOutBuffer - Указатель на буфер вывода данных, который должен получить данные, возвращенные операцией. Формат этих данных зависит от значения параметра dwIoControlCode. Этот параметр может быть установлен в NULL, если dwIoControlCode определяет операцию, которая не возвращает данные.
nOutBufferSize - Размер буфера вывода данных, в байтах.
lpBytesReturned - Указатель на переменную, которая получает размер данных, сохраненных в буфере вывода данных, в байтах.
lpOverlapped - Указатель на структуру OVERLAPPED. Данная структура содержит информацию, используемую в асинхронном (или перекрывающем) вводе и выводе данных (I/O)
Если операция завершается успешно, DeviceIoControl возвращает ненулевое значение.
Если операция завершается ошибкой, DeviceIoControl возвращает нуль. Чтобы получить дополнительную информацию об ошибке, необходимо вызвать функцию GetLastError.
Получение информации о разделах диска
Ниже приведен пример использования функции DeviceIoControl для получения информации о разделах диска:
// описатель дискового устройства
HANDLE hDrive;
// получаем hDrive
hDrive = CreateFile(
_T("\\\\.\\PhysicalDrive0"), // диск
GENERIC_READ, // чтение диска разрешено
FILE_SHARE_READ, // попытка постороннего
// процесса открыть файл с
// флагом GENERIC_WRITE не удастся
NULL, // атрибуты безопасности
OPEN_EXISTING, // функция вернет ошибку если
// указанный диск не существует
0, // флаги и атрибуты
NULL
);
// в случае неудачи ничего не делаем
if (hDrive == INVALID_HANDLE_VALUE) {
return;
}
// указатель на структуру для записи информации о разделах диска
_DRIVE_LAYOUT_INFORMATION *layoutInfo;
// размер буфера выходных данных
DWORD outBufSize = 4096;
// буфер для выходных данных
char *outBuf = new char [outBufSize];
// количество полученных байт
DWORD butesReturned;
// прямое обращение к драйверу дискового устройства
bool res = DeviceIoControl(
hDrive,
IOCTL_DISK_GET_DRIVE_LAYOUT,
NULL,
0,
outBuf,
outBufSize,
&butesReturned,
NULL
);
// обращение к драйверу закончилось неудачей
if (!res) {
return;
}
// получаем информацию о разделах диска
layoutInfo = (_DRIVE_LAYOUT_INFORMATION *)outBuf;
// закрываем указатель на том
CloseHandle(hDrive);
В данном примере используется структура _DRIVE_LAYOUT_INFORMATION, содержащая в себе информацию о разделах диска:
struct _DRIVE_LAYOUT_INFORMATION {
DWORD PartitionCount;
DWORD Signature;
PARTITION_INFORMATION PartitionEntry[1];
};
PartitionCount - Количество разделов на диске. Как отмечалось выше, на дисках с MBR, это значение равно 4. В противном случае неиспользуемые разделы, имеют тип PARTITION_ENTRY_UNUSED.
Signature – значение сигнатуры диска
PartitionEntry – переменная типа _PARTITION_INFORMATION, одна структура для каждого раздела диска:
typedef struct _PARTITION_INFORMATION {
LARGE_INTEGER StartingOffset;
LARGE_INTEGER PartitionLength;
DWORD HiddenSectors;
DWORD PartitionNumber;
BYTE PartitionType;
BOOLEAN BootIndicator;
BOOLEAN RecognizedPartition;
BOOLEAN RewritePartition;
}
StartingOffset - начальное смещение раздела
PartitionLength - Размер раздела, в байтах.
HiddenSectors - Количество скрытых секторов в разделе.
PartitionNumber – Номер раздела
PartitionType – Тип раздела. Возможные константы типов разделов приведены в таблице 6:
Таблица 6 – Возможные константы типов разделов
Тип раздела |
Описание |
PARTITION_ENTRY_UNUSED |
Неиспользуемый раздел |
PARTITION_EXTENDED |
Расширенный раздел |
PARTITION_FAT_12 |
Раздел файловой системы FAT12 |
PARTITION_FAT_16 |
Раздел файловой системы FAT16 |
PARTITION_FAT32 |
Раздел файловой системы FAT32 |
PARTITION_IFS |
Раздел IFS |
PARTITION_LDM |
Менеджер логических дисков |
PARTITION_NTFT |
Раздел NTFT. |
VALID_NTFT |
Правильный раздел NTFT. Данный тип раздела указывает, что раздел является частью NTFT Raid 0. |
BootIndicator - Если этот элемент имеет значение ИСТИНА, раздел является загрузочным.
RecognizedPartition - Если этот элемент имеет значение ИСТИНА, то данный раздел опознанного типа.
RewritePartition - Если этот элемент имеет значение ИСТИНА, информация о разделе изменилась. При изменении раздела (с IOCTL_DISK_SET_DRIVE_LAYOUT), система использует данный флаг, чтобы определить, какие разделы были изменены.
Получение информации о физической геометрии диска
Ниже приведен пример использования функции DeviceIoControl для получения информации о физической геометрии диска:
// указатель на структуру, хранящую геометрию диска
_DISK_GEOMETRY * diskGeoInfo;
// размер буфера выходных данных
DWORD outBufSize = 4096;
// буфер для выходных данных
char *outBuf = new char [outBufSize];
// количество полученных байт
DWORD butesReturned;
// прямое обращение к драйверу дискового устройства
bool res = DeviceIoControl(
hDrive,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
outBuf,
outBufSize,
&butesReturned,
NULL
);
// обращение к драйверу закончилось неудачей
if (!res) {
return;
}
// получаем информацию о физической геометрии диска
diskGeoInfo = (_DISK_GEOMETRY *)outBuf;
В данном примере используется структура _DISK_GEOMETRY, описание которой приведено ниже:
struct _DISK_GEOMETRY {
LARGE_INTEGER Cylinders;
MEDIA_TYPE MediaType;
DWORD TracksPerCylinder;
DWORD SectorsPerTrack;
DWORD BytesPerSector;
};
Cylinders – количество цилиндров
MediaType – тип носителя(Unknown, F3_1Pt44_512 (3.5", 1.44MB, 512 байт/сектор), RemovableMedia(съемный диск), FixedMedia (фиксированный жесткий диск))
TracksPerCylinder – количество дорожек на цилиндре
SectorsPerTrack – количество секторов на дорожке
BytesPerSector – количество байт в секторе
Чтение главной загрузочной записи функциями CreateFile() и ReadFile()
Для чтения MBR можно воспользоваться и функциями CreateFile и ReadFile(см. л.р. №5):
typedef unsigned __int16 word;
typedef unsigned __int32 dword;
struct PART {
byte Boot;
byte Begin_Hd;
word Begin_SecTrk;
byte SysCode;
byte End_Hd;
word End_SecTrk;
dword RelSec;
dword Size;
};
struct MBR {
char LoadCode[0x1be];
struct PART rt[4];
word EndFlag;
};
MBR read_mbr()
{
// MBR
MBR mbrObj;
// описатель (дескриптор) дискового устройства
HANDLE hDrive;
// получаем hDrive
AnsiString driveName = "\\\\.\\C:";
hDrive = CreateFile(
driveName.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
// в случае неудачи ничего не делаем
if (hDrive == INVALID_HANDLE_VALUE) {
ShowMessage("Не удалось прочесть файл");
return mbrObj;
}
// размер буфера выходных данных = размеру MBR
DWORD outBufSize = 4096;
// буфер для выходных данных
char *outBuf = new char [outBufSize];
// количество полученных байт
DWORD butesReturned;
DWORD dwPtr = SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
BOOL res = ReadFile(
hDrive,
outBuf,
outBufSize,
&butesReturned,
NULL
);
// в случае неудачи ничего не делаем
if (!res) {
CloseHandle(hDrive);
return mbrObj;
}
int ptr;
// Загрузочная запись
for (ptr=0; ptr<sizeof(mbrObj.LoadCode); ptr++)
{
mbrObj.LoadCode[ptr] = outBuf[ptr];
}
// 4 элемента таблицы разделов
for (int i=0; i<4; i++)
{
mbrObj.rt[i].Boot = outBuf[ptr++];
mbrObj.rt[i].Begin_Hd = outBuf[ptr++];
mbrObj.rt[i].Begin_SecTrk = outBuf[ptr++];
mbrObj.rt[i].Begin_SecTrk = mbrObj.rt[i].Begin_SecTrk << 8;
mbrObj.rt[i].Begin_SecTrk = outBuf[ptr++];
mbrObj.rt[i].SysCode = outBuf[ptr++];
mbrObj.rt[i].End_Hd = outBuf[ptr++];
mbrObj.rt[i].End_SecTrk = outBuf[ptr++];
mbrObj.rt[i].End_SecTrk = mbrObj.rt[i].Begin_SecTrk << 8;
mbrObj.rt[i].End_SecTrk = outBuf[ptr++];
mbrObj.rt[i].RelSec =
(__int64(outBuf[ptr]) << 24) +
(__int64(outBuf[ptr+1]) << 16) +
(__int64(outBuf[ptr+2]) << 8) +
(__int64(outBuf[ptr+3]));
ptr += 4;
mbrObj.rt[i].Size =
(__int64(outBuf[ptr]) << 24) +
(__int64(outBuf[ptr+1]) << 16) +
(__int64(outBuf[ptr+2]) << 8) +
(__int64(outBuf[ptr+3]));
ptr += 4;
}
// Сигнатура (55AAh)
mbrObj.EndFlag = (__int16(outBuf[ptr]) << 8) + outBuf[ptr+1];
// закрываем указатель на том
CloseHandle(hDrive);
return mbrObj;
}