/* vfddrv.c Virtual Floppy Drive for Windows NT platform Kernel mode driver: miscellaneous driver functions Copyright (C) 2003-2005 Ken Kato */ #include "imports.h" #include "vfddrv.h" #include "vfddbg.h" // // driver reinitialize routine // -- create a drive letter for each device // #ifdef __cplusplus extern "C" #endif // __cplusplus static VOID NTAPI VfdReinitialize( IN PDRIVER_OBJECT DriverObject, IN PVOID Context, IN ULONG Count); // // specify code segment // #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(PAGE, VfdReinitialize) #pragma alloc_text(PAGE, VfdUnloadDriver) #pragma alloc_text(PAGE, VfdCreateClose) #pragma alloc_text(PAGE, VfdCopyUnicode) #pragma alloc_text(PAGE, VfdFreeUnicode) #endif // ALLOC_PRAGMA // // operating system version // #ifndef __REACTOS__ extern ULONG OsMajorVersion = 0; extern ULONG OsMinorVersion = 0; extern ULONG OsBuildNumber = 0; #else ULONG OsMajorVersion = 0; ULONG OsMinorVersion = 0; ULONG OsBuildNumber = 0; #endif // // Trace level flag // #if DBG #ifndef __REACTOS__ extern ULONG TraceFlags = (ULONG)-1; #else ULONG TraceFlags = (ULONG)-1; #endif #endif // DBG // // Driver Entry routine // NTSTATUS NTAPI DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS status; PVFD_DRIVER_EXTENSION driver_extension; ULONG number_of_devices = VFD_DEFAULT_DEVICES; ASSERT(DriverObject); // Get operating system version PsGetVersion(&OsMajorVersion, &OsMinorVersion, &OsBuildNumber, NULL); #ifdef VFD_PNP #define VFD_PNP_TAG "(Plug & Play version)" #else #define VFD_PNP_TAG #endif VFDTRACE(0, ("[VFD] %s %s" VFD_PNP_TAG "\n", VFD_PRODUCT_NAME, VFD_DRIVER_VERSION_STR)); VFDTRACE(0, ("[VFD] Running on Windows NT %lu.%lu build %lu\n", OsMajorVersion, OsMinorVersion, OsBuildNumber)); VFDTRACE(0, ("[VFD] Build Target Environment: %d\n", VER_PRODUCTBUILD)); #ifdef VFD_PNP // Create device_extension for the driver object to store driver specific // information. Device specific information are stored in device extension // for each device object. status = IoAllocateDriverObjectExtension( DriverObject, VFD_DRIVER_EXTENSION_ID, sizeof(VFD_DRIVER_EXTENSION), &driver_extension); if(!NT_SUCCESS(status)) { VFDTRACE(0, ("[VFD] IoAllocateDriverObjectExtension - %s\n", GetStatusName(status))); return status; } #else // VFD_PNP // Windows NT doesn't have the IoAllocateDriverObjectExtension // function and I think there's little point in making a non-PnP // driver incompatible with Windows NT. driver_extension = (PVFD_DRIVER_EXTENSION)ExAllocatePoolWithTag( PagedPool, sizeof(VFD_DRIVER_EXTENSION), VFD_POOL_TAG); if (!driver_extension) { VFDTRACE(0, ("[VFD] failed to allocate the driver extension.\n")); return STATUS_INSUFFICIENT_RESOURCES; } #endif // VFD_PNP RtlZeroMemory(driver_extension, sizeof(VFD_DRIVER_EXTENSION)); // // Copy the registry path into the driver extension so we can use it later // if (VfdCopyUnicode(&(driver_extension->RegistryPath), RegistryPath)) { // // Read config values from the registry // RTL_QUERY_REGISTRY_TABLE params[3]; ULONG default_devs = VFD_DEFAULT_DEVICES; #if DBG ULONG default_trace = (ULONG)-1; #endif RtlZeroMemory(params, sizeof(params)); VFDTRACE(0, ("[VFD] Registry Path: %ws\n", driver_extension->RegistryPath.Buffer)); params[0].Flags = RTL_QUERY_REGISTRY_DIRECT; params[0].Name = VFD_REG_DEVICE_NUMBER; params[0].EntryContext = &number_of_devices; params[0].DefaultType = REG_DWORD; params[0].DefaultData = &default_devs; params[0].DefaultLength = sizeof(ULONG); #if DBG params[1].Flags = RTL_QUERY_REGISTRY_DIRECT; params[1].Name = VFD_REG_TRACE_FLAGS; params[1].EntryContext = &TraceFlags; params[1].DefaultType = REG_DWORD; params[1].DefaultData = &default_trace; params[1].DefaultLength = sizeof(ULONG); #endif // DBG status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, driver_extension->RegistryPath.Buffer, params, NULL, NULL); if (!NT_SUCCESS(status) || number_of_devices == 0 || number_of_devices > VFD_MAXIMUM_DEVICES) { number_of_devices = VFD_DEFAULT_DEVICES; } VFDTRACE(0,("[VFD] NumberOfDevices = %lu\n", number_of_devices)); VFDTRACE(0,("[VFD] TraceFlags = 0x%08x\n", TraceFlags)); } else { VFDTRACE(0, ("[VFD] failed to allocate the registry path buffer.\n")); // this error is not fatal } // // Create VFD device objects // do { #ifdef VFD_PNP status = VfdCreateDevice(DriverObject, NULL); #else // VFD_PNP status = VfdCreateDevice(DriverObject, driver_extension); #endif // VFD_PNP if (!NT_SUCCESS(status)) { break; } } while (driver_extension->NumberOfDevices < number_of_devices); if (!driver_extension->NumberOfDevices) { // Failed to create even one device VfdFreeUnicode(&(driver_extension->RegistryPath)); return status; } // Setup dispatch table DriverObject->MajorFunction[IRP_MJ_CREATE] = VfdCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = VfdCreateClose; DriverObject->MajorFunction[IRP_MJ_READ] = VfdReadWrite; DriverObject->MajorFunction[IRP_MJ_WRITE] = VfdReadWrite; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VfdDeviceControl; #ifdef VFD_PNP DriverObject->MajorFunction[IRP_MJ_PNP] = VfdPlugAndPlay; DriverObject->MajorFunction[IRP_MJ_POWER] = VfdPowerControl; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = VfdSystemControl; DriverObject->DriverExtension->AddDevice = VfdAddDevice; #endif // VFDPNP DriverObject->DriverUnload = VfdUnloadDriver; // Register the driver reinitialize routine to be called // *after* the DriverEntry routine returns IoRegisterDriverReinitialization( DriverObject, VfdReinitialize, NULL); VFDTRACE(VFDINFO, ("[VFD] driver initialized with %lu devices.\n", driver_extension->NumberOfDevices)); return STATUS_SUCCESS; } // // Driver unload routine // Cleans up the device objects and other resources // VOID NTAPI VfdUnloadDriver ( IN PDRIVER_OBJECT DriverObject) { PDEVICE_OBJECT device_object; PVFD_DRIVER_EXTENSION driver_extension; VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - IN\n")); device_object = DriverObject->DeviceObject; #ifdef VFD_PNP driver_extension = IoGetDriverObjectExtension( DriverObject, VFD_DRIVER_EXTENSION_ID); #else if (device_object && device_object->DeviceExtension) { driver_extension = ((PDEVICE_EXTENSION)(device_object->DeviceExtension))->DriverExtension; } else { driver_extension = NULL; } #endif // VFD_PNP // // Delete all remaining device objects // while (device_object) { PDEVICE_OBJECT next_device = device_object->NextDevice; VfdDeleteDevice(device_object); device_object = next_device; } // // Release the driver extension and the registry path buffer // if (driver_extension) { if (driver_extension->RegistryPath.Buffer) { VFDTRACE(0, ("[VFD] Releasing the registry path buffer\n")); ExFreePool(driver_extension->RegistryPath.Buffer); } #ifndef VFD_PNP // The system takes care of freeing the driver extension // allocated with IoAllocateDriverObjectExtension in a PnP driver. VFDTRACE(0, ("[VFD] Releasing the driver extension\n")); ExFreePool(driver_extension); #endif // VFD_PNP } VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - OUT\n")); } // // IRP_MJ_CREATE and IRP_MJ_CLOSE handler // Really nothing to do here... // NTSTATUS NTAPI VfdCreateClose ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { #if DBG if (DeviceObject && DeviceObject->DeviceExtension && ((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer) { VFDTRACE(VFDINFO, ("[VFD] %-40s %ws\n", GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction), ((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer)); } else { VFDTRACE(VFDINFO, ("[VFD] %-40s %p\n", GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction), DeviceObject)); } #endif // DBG Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } // // Called after the DriverEntry routine has returned // (Re)Create a persistent drive letter for each device // VOID NTAPI VfdReinitialize( IN PDRIVER_OBJECT DriverObject, IN PVOID Context, IN ULONG Count) { PDEVICE_OBJECT device_object; PDEVICE_EXTENSION device_extension; UNREFERENCED_PARAMETER(Context); UNREFERENCED_PARAMETER(Count); VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - IN\n")); device_object = DriverObject->DeviceObject; while (device_object) { device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension; #ifdef VFD_MOUNT_MANAGER if (OsMajorVersion >= 5) { // Windows 2000 / XP // Notify the mount manager of a VFD volume arrival VfdMountMgrNotifyVolume(device_extension); if (device_extension->DriveLetter) { // Create a drive letter via the mount manager. // The mount manager may have created a drive letter // in response to the volume arrival notification above. // In that case, the following call just fails. VfdMountMgrMountPoint( device_extension, device_extension->DriveLetter); // ignoring the error for it is not fatal here } } else #endif // VFD_MOUNT_MANAGER { // Windows NT style drive letter assignment // Simply create a symbolic link here if (device_extension->DriveLetter) { VfdSetLink( device_extension, device_extension->DriveLetter); // ignoring the error for it is not fatal here } } device_object = device_object->NextDevice; } VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - OUT\n")); } // // Device dedicated thread routine // Dispatch read, write and device I/O request // redirected from the driver dispatch routines // VOID NTAPI VfdDeviceThread ( IN PVOID ThreadContext) { PDEVICE_OBJECT device_object; PDEVICE_EXTENSION device_extension; PLIST_ENTRY request; PIRP irp; PIO_STACK_LOCATION io_stack; ASSERT(ThreadContext != NULL); device_object = (PDEVICE_OBJECT)ThreadContext; device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension; KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY); for (;;) { // wait for the request event to be signalled KeWaitForSingleObject( &device_extension->RequestEvent, Executive, KernelMode, FALSE, NULL); // terminate request ? if (device_extension->TerminateThread) { VFDTRACE(0, ("[VFD] Exitting the I/O thread\n")); PsTerminateSystemThread(STATUS_SUCCESS); } // perform requested tasks while ((request = ExInterlockedRemoveHeadList( &device_extension->ListHead, &device_extension->ListLock)) != NULL) { irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry); io_stack = IoGetCurrentIrpStackLocation(irp); irp->IoStatus.Information = 0; switch (io_stack->MajorFunction) { case IRP_MJ_READ: VfdReadData(device_extension, irp, io_stack->Parameters.Read.Length, &io_stack->Parameters.Read.ByteOffset); break; case IRP_MJ_WRITE: VfdWriteData(device_extension, irp, io_stack->Parameters.Write.Length, &io_stack->Parameters.Write.ByteOffset); break; case IRP_MJ_DEVICE_CONTROL: VfdIoCtlThread(device_extension, irp, io_stack->Parameters.DeviceIoControl.IoControlCode); break; default: // This shouldn't happen... VFDTRACE(0, ("[VFD] %s passed to the I/O thread\n", GetMajorFuncName(io_stack->MajorFunction))); irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR; } IoCompleteRequest(irp, (CCHAR)(NT_SUCCESS(irp->IoStatus.Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT)); #ifdef VFD_PNP IoReleaseRemoveLock(&device_extension->RemoveLock, irp); #endif // VFD_PNP } // while } // for (;;) } // // Copy a UNICODE_STRING adding a trailing NULL characer // PWSTR VfdCopyUnicode( PUNICODE_STRING dst, PUNICODE_STRING src) { RtlZeroMemory(dst, sizeof(UNICODE_STRING)); dst->MaximumLength = (USHORT)(src->MaximumLength + sizeof(UNICODE_NULL)); dst->Buffer = (PWSTR)ExAllocatePoolWithTag( PagedPool, dst->MaximumLength, VFD_POOL_TAG); if(dst->Buffer) { dst->Length = src->Length; RtlZeroMemory(dst->Buffer, dst->MaximumLength); if (src->Length) { RtlCopyMemory(dst->Buffer, src->Buffer, src->Length); } } return dst->Buffer; } // // Free a UNICODE_STRING buffer // VOID VfdFreeUnicode( PUNICODE_STRING str) { if (str->Buffer) { ExFreePool(str->Buffer); } RtlZeroMemory(str, sizeof(UNICODE_STRING)); }