Курсовая работа: Разработка драйвера виртуального жесткого диска
Поле DiskRegInfo описывает параметры, которые расположены в реестре и используются драйвером для создания диска:
typedef struct _DISK_INFO
{
ULONG DiskSize; // Размер диска в байтах
ULONG RootDirEntries; // Количество элементов в корне
ULONG SectorsPerCluster; // Секторов на кластер
UNICODE_STRING DriveLetter; // Буква диска
} DISK_INFO, *PDISK_INFO;
3.3 Блокировка выгрузки устройства
Когда система производит удаление нижестоящего устройства, она посылает ему PnP-запрос с функцией IRP_MN_REMOVE_DEVICE. Если же в этот момент драйвер чем-то занят, то он должен защитить своё устройство от преждевременной выгрузки из памяти. Для этого в системе предусмотрен объект «блокировка выгрузки» (remove lock). Он может быть «захвачен» и «освобождён». Внутри этого объекта имеется счётчик. Этот счётчик устанавливается в ноль при инициализации объекта (функция IoInitializeRemoveLock). Счётчик увеличивается на единицу при захвате объекта (IoAcquireRemoveLock) перед обработкой запроса и уменьшается при освобождении (IoReleaseRemoveLock).
Обработчик PnP-запроса с функцией IRP_MN_REMOVE_DEVICE гарантированно исполняется в контексте системного процесса. В этом обработчике перед удалением объекта-устройства над блокировкой выполняется действие «освободить и ждать» (IoReleaseRemoveLockAndWait). Оно уменьшает счётчик на единицу и блокирует системный процесс, пока счётчик не станет равным нулю. Следовательно, пока драйвер обрабатывает хотя бы один запрос к устройству, это устройство не будет удалено.
3.4 Процедуры драйвера виртуального диска
3.4.1 Инициализация драйвера
Начнём с процедуры, вызываемой при инициализации драйвера — DriverEntry. Она определяется следующим образом:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
Тип NTSATUS, соответствующий возвращаемому значению, определяет тип ошибки. Многие функции драйвера возвращают значение этого типа. Если работа проходит успешно, результат принимает значение STATUS_SUCCESS.
В данной процедуре необходимо сообщить системе остальные процедуры обработки пакетов IRP:
Поскольку нам нужно сохранить параметры о пути реестра и признак того, что инициализировалось ли само устройство, то в структуре расширения драйвера заведем следующие поля:
DriverObject->MajorFunction[IRP_MJ_CREATE] = RamDskCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = RamDskCreateClose; DriverObject->MajorFunction[IRP_MJ_READ] = RamDskReadWrite; DriverObject->MajorFunction[IRP_MJ_WRITE] = RamDskReadWrite; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RamDskIOCtl; DriverObject->MajorFunction[IRP_MJ_PNP] = RamDskDispatchPnp; DriverObject->MajorFunction[IRP_MJ_POWER] = RamDskDispatchPower; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = RamDskDispatchSystemControl; DriverObject->DriverExtension->AddDevice = RamDskAddDevice; DriverObject->DriverUnload = RamDskUnload;
Массив MajorFunction устанавливает соответствие между кодом типа запроса IRP_MJ_NNN и диспетчерской функцией, которая его обрабатывает. Наш драйвер обрабатывает запросы на открытие и закрытие устройства (RamDskCreateClose), чтение и запись (RamDskReadWrite), расширенные запросы обработки IOCTL(RamDskIOCtl), запросы Plug and Play (RamDskDispatchPnp), функции управления питанием (RamDskDispatchPower), запросы от подсистемы инструментария Windows (RamDskDispatchSystemControl).
Сам объект функционального устройства, который будет отвечать за наше устройство, создается далее, когда система вызовет процедуру RamDskAddDevice. Но для его создания необходимо узнать путь реестра с параметрами драйвера, а также необходим признак, что устройство добавлено. Данные параметры будем хранить в структуре расширения драйвера. Поля и размер структуры выбираются разработчиком, для хранения нужных ему данных. Объявим структуру расширения драйвера следующим образом:
typedef struct _RAMDSK_DRIVER_EXTENSION { UNICODE_STRING RegistryPath; ULONG DeviceInitialized; } RAMDSK_DRIVER_EXTENSION, *PRAMDSK_DRIVER_EXTENSION;
В процедуре DriverEntry мы создаем экземпляр структуры и инициализируем ее поля: копируем путь реестра RegistryPath, который передается в параметрах процедуры, и сбрасываем признак DeviceInitialized того, что устройство добавлено.
status = IoAllocateDriverObjectExtension(DriverObject, RAMDSK_DRIVER_EXTENSION_KEY, sizeof(RAMDSK_DRIVER_EXTENSION), &driverExtension); // Сохраним путь реестра driverExtension->RegistryPath.Length = RegistryPath->Length; driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength + sizeof(UNICODE_NULL); driverExtension->RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, driverExtension->RegistryPath.MaximumLength, RAMDSK_TAG_GENERAL); RtlCopyUnicodeString( &(driverExtension->RegistryPath), RegistryPath); driverExtension->DeviceInitialized = FALSE;
Процедура RamDskAddDevice будет вызвана, когда будет обнаружено устройство. Ее действие заключается в создании объекта устройства диска и создании символических ссылок для него.
Проверим, может быть уже создан объект устройство, а процедура RamDskAddDevice вызывается повторно. Для этого проанализируем признак DeviceInitialized расширения драйвера:
// Может присутствоать только одно устройство, если повторно вызывается запрос
// AddDevice для следующего устройства, отклоним запрос
if ( driverExtension->DeviceInitialized == TRUE )
{
DBGPRINT( DBG_COMP_INIT, DBG_LEVEL_ERROR, ("Device exists\n") );
return STATUS_DEVICE_ALREADY_ATTACHED;