/** * Initiate a filedisk hot-swap to another file. * * @v filedisk The filedisk to be hot-swapped. * @v file The ANSI filename for swapping to. */ VOID STDCALL WvFilediskHotSwap(IN WV_SP_FILEDISK_T filedisk, IN PCHAR file) { static WVL_S_THREAD thread = { /* Main */ { /* Link */ {0}, /* Func */ WvFilediskHotSwapThread_, }, /* State */ WvlThreadStateNotStarted, }; WV_SP_FILEDISK_HOT_SWAPPER_ hot_swapper; ANSI_STRING file_ansi; NTSTATUS status; hot_swapper = wv_malloc(sizeof *hot_swapper); if (!hot_swapper) { DBG("Non-critical: Couldn't allocate work item.\n"); return; } /* Build the Unicode string. */ RtlInitAnsiString(&file_ansi, file); hot_swapper->filename->Buffer = NULL; status = RtlAnsiStringToUnicodeString( hot_swapper->filename, &file_ansi, TRUE ); if (!NT_SUCCESS(status)) { DBG("Non-critical: Couldn't allocate unicode string.\n"); wv_free(hot_swapper); return; } /* Build the rest of the work item. */ hot_swapper->item->Func = WvFilediskHotSwapThread_; hot_swapper->filedisk = filedisk; /* Start the thread. If it's already been started, no matter. */ WvlThreadStart(&thread); /* Add the hot-swapper work item. */ if (!WvlThreadAddItem(&thread, hot_swapper->item)) { DBG("Non-critical: Couldn't add work item.\n"); RtlFreeUnicodeString(hot_swapper->filename); wv_free(hot_swapper); } /* The thread is responsible for freeing the work item and file path. */ return; }
/* Filedisk PnP ID query-response routine. */ static NTSTATUS STDCALL WvFilediskPnpQueryId_( IN PDEVICE_OBJECT dev_obj, IN PIRP irp, IN WVL_SP_DISK_T disk ) { static const WCHAR * hw_ids[WvlDiskMediaTypes] = { WVL_M_WLIT L"\\FileFloppyDisk", WVL_M_WLIT L"\\FileHardDisk", WVL_M_WLIT L"\\FileOpticalDisc" }; WCHAR (*buf)[512]; NTSTATUS status; WV_SP_FILEDISK_T filedisk = CONTAINING_RECORD(disk, WV_S_FILEDISK_T, disk); BUS_QUERY_ID_TYPE query_type; /* Allocate a buffer. */ buf = wv_mallocz(sizeof *buf); if (!buf) { status = STATUS_INSUFFICIENT_RESOURCES; goto err_buf; } /* Populate the buffer with IDs. */ query_type = IoGetCurrentIrpStackLocation(irp)->Parameters.QueryId.IdType; switch (query_type) { case BusQueryDeviceID: swprintf(*buf, hw_ids[disk->Media]); break; case BusQueryInstanceID: /* "Location". */ swprintf(*buf, L"Hash_%08X", filedisk->hash); break; case BusQueryHardwareIDs: swprintf( *buf + swprintf(*buf, hw_ids[disk->Media]) + 1, WvlDiskCompatIds[disk->Media] ); break; case BusQueryCompatibleIDs: swprintf(*buf, WvlDiskCompatIds[disk->Media]); default: DBG("Unknown query type %d for %p!\n", query_type, filedisk); status = STATUS_INVALID_PARAMETER; goto err_query_type; } DBG("IRP_MN_QUERY_ID for file-backed disk %p.\n", filedisk); return WvlIrpComplete(irp, (ULONG_PTR) buf, STATUS_SUCCESS); err_query_type: wv_free(buf); err_buf: return WvlIrpComplete(irp, 0, status); }
/** * Check if a disk might be the matching backing disk for * a GRUB4DOS sector-mapped disk by checking for some ISO9660 * filesystem magic (in the first volume descriptor). * * @v file HANDLE to an open disk. * @v filedisk Points to the filedisk to match against. */ static BOOLEAN STDCALL WvFilediskG4dCheckDiskMatchIsoSig_( IN HANDLE file, IN WV_SP_FILEDISK_T filedisk ) { #ifdef _MSC_VER # pragma pack(1) #endif struct vol_desc { UCHAR type; UCHAR id[5]; } __attribute__((__packed__)); #ifdef _MSC_VER # pragma pack() #endif enum { first_vol_desc_byte_offset = 32768 }; static CHAR iso_magic[] = "CD001"; BOOLEAN ok = FALSE; struct vol_desc * buf; LARGE_INTEGER start; NTSTATUS status; IO_STATUS_BLOCK io_status; /* Allocate a buffer for testing for some ISO9660 magic. */ buf = wv_malloc(filedisk->disk->SectorSize); if (buf == NULL) { goto err_alloc; } /* Calculate where a volume descriptor might be. */ start.QuadPart = filedisk->offset.QuadPart + first_vol_desc_byte_offset; /* Read a potential ISO9660 volume descriptor. */ status = ZwReadFile( file, NULL, NULL, NULL, &io_status, buf, filedisk->disk->SectorSize, &start, NULL ); if (!NT_SUCCESS(status)) goto err_read; /* Check for the ISO9660 magic. */ if (wv_memcmpeq(buf->id, iso_magic, sizeof iso_magic - 1)) ok = TRUE; err_read: wv_free(buf); err_alloc: return ok; }
NTSTATUS STDCALL WvDiskPnpQueryDevText( IN PDEVICE_OBJECT dev_obj, IN PIRP irp, IN WVL_SP_DISK_T disk ) { IN WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj); WCHAR (*str)[512]; PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; UINT32 str_len; /* Allocate a string buffer. */ str = wv_mallocz(sizeof *str); if (str == NULL) { DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto alloc_str; } /* Determine the query type. */ switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) { case DeviceTextDescription: str_len = swprintf(*str, WVL_M_WLIT L" Disk") + 1; irp->IoStatus.Information = (ULONG_PTR) wv_palloc(str_len * sizeof *str); if (irp->IoStatus.Information == 0) { DBG("wv_palloc DeviceTextDescription\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto alloc_info; } RtlCopyMemory( (PWCHAR) irp->IoStatus.Information, str, str_len * sizeof *str ); status = STATUS_SUCCESS; goto alloc_info; case DeviceTextLocationInformation: if (disk->disk_ops.PnpQueryId) { io_stack_loc->MinorFunction = IRP_MN_QUERY_ID; io_stack_loc->Parameters.QueryId.IdType = BusQueryInstanceID; return disk->disk_ops.PnpQueryId(dev_obj, irp, disk); } /* Else, fall through... */ default: irp->IoStatus.Information = 0; status = STATUS_NOT_SUPPORTED; } /* irp->IoStatus.Information not freed. */ alloc_info: wv_free(str); alloc_str: return WvlIrpComplete(irp, irp->IoStatus.Information, status); }
/* The thread responsible for hot-swapping filedisks. */ static VOID STDCALL WvFilediskHotSwapThread_(IN OUT WVL_SP_THREAD_ITEM item) { LARGE_INTEGER timeout; WVL_SP_THREAD thread = CONTAINING_RECORD(item, WVL_S_THREAD, Main); WVL_SP_THREAD_ITEM work_item; WV_SP_FILEDISK_HOT_SWAPPER_ info; /* Wake up at least every 10 seconds. */ timeout.QuadPart = -100000000LL; while ( (thread->State == WvlThreadStateStarted) || (thread->State == WvlThreadStateStopping) ) { /* Wait for the work signal or the timeout. */ KeWaitForSingleObject( &thread->Signal, Executive, KernelMode, FALSE, &timeout ); work_item = WvlThreadGetItem(thread); if (!work_item) continue; if (work_item->Func != WvFilediskHotSwapThread_) { DBG("Unknown work item.\n"); continue; } info = CONTAINING_RECORD( work_item, WV_S_FILEDISK_HOT_SWAPPER_, item[0] ); /* Attempt a hot swap. */ if (WvFilediskHotSwap_(info->filedisk, info->filename)) { /* Success. */ RtlFreeUnicodeString(info->filename); wv_free(info); } else { /* Re-enqueue. */ WvlThreadAddItem(thread, info->item); } /* Reset the work signal. */ KeResetEvent(&thread->Signal); } /* while thread is running. */ return; }
VOID STDCALL WvlDebugIrpEnd(IN PIRP Irp, IN NTSTATUS Status) { PDEBUG_IRPLIST Record; KIRQL Irql; if ((Record = Debug_IrpListRecord(Irp)) == NULL) { DBG( "Irp not found in Debug_Globals_IrpList!!" " (returned 0x%08x, Status)\n" ); return; } /* * There is no race condition between getting the record and unlinking in * Debug_Globals_IrpList, unless WvlDebugIrpEnd is called more than once on * an irp (which itself is a bug, it should only be called one time). */ KeAcquireSpinLock(&WvlDebugLock_, &Irql); if (Record->Previous == NULL) { Debug_Globals_IrpList = Record->Next; } else { Record->Previous->Next = Record->Next; } if (Record->Next != NULL) { Record->Next->Previous = Record->Previous; } KeReleaseSpinLock(&WvlDebugLock_, Irql); DBG( "IRP %d: %s -> 0x%08x\n", Record->Number, Record->DebugMessage, Status ); wv_free(Record->DebugMessage); wv_free(Record); }
/** * Check if a disk might be the matching backing disk for * a GRUB4DOS sector-mapped disk by checking for an MBR signature. * * @v file HANDLE to an open disk. * @v filedisk Points to the filedisk to match against. */ static BOOLEAN STDCALL WvFilediskG4dCheckDiskMatchMbrSig_( IN HANDLE file, IN WV_SP_FILEDISK_T filedisk ) { BOOLEAN ok = FALSE; WVL_SP_DISK_MBR buf; NTSTATUS status; IO_STATUS_BLOCK io_status; /* Allocate a buffer for testing for an MBR signature. */ buf = wv_malloc(sizeof *buf); if (buf == NULL) { goto err_alloc; } /* Read a potential MBR. */ status = ZwReadFile( file, NULL, NULL, NULL, &io_status, buf, sizeof *buf, &filedisk->offset, NULL ); if (!NT_SUCCESS(status)) goto err_read; /* Check for the MBR signature. */ if (buf->mbr_sig == 0xAA55) ok = TRUE; err_read: wv_free(buf); err_alloc: return ok; }
/** * Initiate a search for a GRUB4DOS backing disk. * * @v filedisk The filedisk whose backing disk we need. */ static BOOLEAN STDCALL WvFilediskG4dFindBackingDisk( IN WV_SP_FILEDISK_T filedisk ) { SP_WV_FILEDISK_G4D_FIND_BACKING_DISK finder; KIRQL irql; finder = wv_malloc(sizeof *finder); if (!finder) { DBG("Couldn't allocate work item!\n"); goto err_finder; } KeAcquireSpinLock(&WvFindDiskLock, &irql); ++WvFindDisk; KeReleaseSpinLock(&WvFindDiskLock, irql); finder->item->Func = WvFilediskG4dFindBackingDisk_; finder->filedisk = filedisk; /* Add the hot-swapper work item. */ if (!WvlThreadAddItem(filedisk->Thread, finder->item)) { DBG("Couldn't add work item!\n"); goto err_work_item; } /* The thread is responsible for freeing the work item. */ return TRUE; err_work_item: KeAcquireSpinLock(&WvFindDiskLock, &irql); --WvFindDisk; KeReleaseSpinLock(&WvFindDiskLock, irql); wv_free(finder); err_finder: return FALSE; }
static WV_S_DUMMY_IDS * WvSafeHookDummyIds( IN A_WV_SAFE_HOOK_VENDOR_STRING * vendor, IN S_X86_SEG16OFF16 * whence ) { UCHAR * ptr; WV_S_DUMMY_IDS * dummy_ids; WCHAR * dest; INT copied; A_WV_SAFE_HOOK_WIDE_VENDOR_STRING wide_vendor; /* Generate the safe hook vendor string */ WvSafeHookVendorString(&wide_vendor, vendor); ptr = wv_malloc(CvWvSafeHookDummyIdsSize); if (!ptr) goto err_dummy_ids; /* Populate with defaults */ dummy_ids = (VOID *) ptr; RtlCopyMemory( dummy_ids, WvSafeHookDefaultDummyIds, CvWvSafeHookDummyIdsSize ); /* Generate the specific details */ ptr += dummy_ids->Offset; /* The instance ID */ dest = (VOID *) ptr; dest += dummy_ids->InstanceOffset; copied = swprintf( dest, M_WV_SAFE_HOOK_INSTANCE_ID, M_X86_SEG16OFF16_ADDR(whence) ); if (copied <= 0) goto err_copied; /* The vendor ID */ dest = (VOID *) ptr; dest += dummy_ids->HardwareOffset; copied = swprintf(dest, M_WV_SAFE_HOOK_VENDOR_ID, wide_vendor); if (copied <= 0) goto err_copied; /* The compatible IDs */ dest = (VOID *) ptr; dest += dummy_ids->CompatOffset; copied = swprintf(dest, M_WV_SAFE_HOOK_VENDOR_ID, wide_vendor); if (copied <= 0) goto err_copied; return dummy_ids; err_copied: wv_free(dummy_ids); err_dummy_ids: return NULL; }
WVL_M_LIB NTSTATUS STDCALL WvlCreateSafeHookDevice( IN S_X86_SEG16OFF16 * hook_addr, IN OUT DEVICE_OBJECT ** dev_obj ) { static const UCHAR sig[] = M_WV_SAFE_HOOK_SIGNATURE; UCHAR * phys_mem; NTSTATUS status; UINT32 hook_phys_addr; WV_S_PROBE_SAFE_MBR_HOOK * safe_mbr_hook; WV_S_DUMMY_IDS * dummy_ids; A_WV_SAFE_HOOK_VENDOR_STRING vendor; DEVICE_OBJECT * new_dev_obj; S_WVL_DUMMY_PDO * dummy; ASSERT(hook_addr); ASSERT(dev_obj); /* Map the first MiB of memory */ phys_mem = WvlMapUnmapLowMemory(NULL); if (!phys_mem) { DBG("Could not map low memory\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto err_map; } /* Special-case the IVT entry for INT 0x13 */ if ( hook_addr->Segment == 0 && hook_addr->Offset == (sizeof *hook_addr * 0x13) ) { hook_addr = (VOID *) (phys_mem + sizeof *hook_addr * 0x13); } hook_phys_addr = M_X86_SEG16OFF16_ADDR(hook_addr); DBG( "Probing for safe hook at %04X:%04X (0x%08X)...\n", hook_addr->Segment, hook_addr->Offset, hook_phys_addr ); /* Check for the signature */ safe_mbr_hook = (VOID *) (phys_mem + hook_phys_addr); if (!wv_memcmpeq(safe_mbr_hook->Signature, sig, sizeof sig - 1)) { DBG("Invalid safe INT 0x13 hook signature. End of chain\n"); status = STATUS_NOT_SUPPORTED; goto err_sig; } /* Found one */ DBG("Found safe hook with vendor ID: %.8s\n", safe_mbr_hook->VendorId); /* Build the IDs */ RtlCopyMemory( vendor, safe_mbr_hook->VendorId, sizeof safe_mbr_hook->VendorId ); dummy_ids = WvSafeHookDummyIds(&vendor, hook_addr); if (!dummy_ids) { status = STATUS_INSUFFICIENT_RESOURCES; goto err_dummy_ids; } new_dev_obj = NULL; status = WvDummyAdd( /* Mini-driver: Us */ WvSafeHookMiniDriver, /* Dummy IDs */ dummy_ids, /* Dummy IDs offset */ CvWvSafeHookDummyIdsOffset, /* Extra data offset */ CvWvSafeHookDummyIdsExtraDataOffset, /* Extra data size */ sizeof *hook_addr, /* Device name: Not specified */ NULL, /* Exclusive access? */ FALSE, /* PDO pointer to populate */ &new_dev_obj ); if (!NT_SUCCESS(status)) goto err_new_dev_obj; ASSERT(new_dev_obj); dummy = new_dev_obj->DeviceExtension; ASSERT(dummy); *((S_X86_SEG16OFF16 *) dummy->ExtraData) = *hook_addr; if (dev_obj) *dev_obj = new_dev_obj; status = STATUS_SUCCESS; goto out; WvlDeleteDevice(new_dev_obj); err_new_dev_obj: out: wv_free(dummy_ids); err_dummy_ids: err_sig: WvlMapUnmapLowMemory(phys_mem); err_map: return status; }
static NTSTATUS STDCALL AoeBusPnpQueryDevText_( IN WVL_SP_BUS_T bus, IN PIRP irp ) { WCHAR (*str)[512]; PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; UINT32 str_len; /* Allocate a string buffer. */ str = wv_mallocz(sizeof *str); if (str == NULL) { DBG("wv_malloc IRP_MN_QUERY_DEVICE_TEXT\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto alloc_str; } /* Determine the query type. */ switch (io_stack_loc->Parameters.QueryDeviceText.DeviceTextType) { case DeviceTextDescription: str_len = swprintf(*str, L"AoE Bus") + 1; irp->IoStatus.Information = (ULONG_PTR) wv_palloc(str_len * sizeof **str); if (irp->IoStatus.Information == 0) { DBG("wv_palloc DeviceTextDescription\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto alloc_info; } RtlCopyMemory( (PWCHAR) irp->IoStatus.Information, str, str_len * sizeof **str ); status = STATUS_SUCCESS; goto alloc_info; case DeviceTextLocationInformation: str_len = AoeBusPnpId_( NULL, BusQueryInstanceID, str ); irp->IoStatus.Information = (ULONG_PTR) wv_palloc(str_len * sizeof **str); if (irp->IoStatus.Information == 0) { DBG("wv_palloc DeviceTextLocationInformation\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto alloc_info; } RtlCopyMemory( (PWCHAR) irp->IoStatus.Information, str, str_len * sizeof **str ); status = STATUS_SUCCESS; goto alloc_info; default: irp->IoStatus.Information = 0; status = STATUS_NOT_SUPPORTED; } /* irp->IoStatus.Information not freed. */ alloc_info: wv_free(str); alloc_str: return WvlIrpComplete(irp, irp->IoStatus.Information, status); }
/** * Default disk deletion operation. * * @v dev Points to the disk device to delete. */ static VOID STDCALL WvDiskDevFree_(IN WV_SP_DEV_T dev) { WVL_SP_DISK_T disk = disk__get_ptr(dev); wv_free(disk); }
/** * Stalls the arrival of a GRUB4DOS sector-mapped disk until * the backing disk is found, and stalls driver re-initialization. */ static VOID STDCALL WvFilediskG4dFindBackingDisk_( IN OUT WVL_SP_THREAD_ITEM item ) { static U_WV_LARGE_INT delay_time = {-10000000LL}; SP_WV_FILEDISK_G4D_FIND_BACKING_DISK finder = CONTAINING_RECORD( item, S_WV_FILEDISK_G4D_FIND_BACKING_DISK, item[0] ); WV_SP_FILEDISK_T filedisk_ptr; NTSTATUS status; GUID disk_guid = GUID_DEVINTERFACE_DISK; PWSTR sym_links; PWCHAR pos; HANDLE file; int count = 0; KIRQL irql; /* Establish pointer to the filedisk. */ filedisk_ptr = finder->filedisk; /* Free the work item. */ wv_free(item); do { /* * Find the backing disk and use it. We walk a list * of unicode disk device names and check each one. */ status = IoGetDeviceInterfaces(&disk_guid, NULL, 0, &sym_links); if (!NT_SUCCESS(status)) goto retry; pos = sym_links; while (*pos != UNICODE_NULL) { UNICODE_STRING path; OBJECT_ATTRIBUTES obj_attrs; IO_STATUS_BLOCK io_status; RtlInitUnicodeString(&path, pos); InitializeObjectAttributes( &obj_attrs, &path, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL ); /* Try this disk. */ file = 0; status = ZwCreateFile( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs, &io_status, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); /* If we could open it, check it out. */ if (NT_SUCCESS(status)) { /* If we liked this disk as our backing disk, stop looking. */ if (WvFilediskG4dCheckDiskMatch_(file, filedisk_ptr)) break; } /* We could not open this disk or didn't like it. Try the next one. */ if (file) ZwClose(file); while (*pos != UNICODE_NULL) pos++; pos++; } /* while */ wv_free(sym_links); /* If we did not find the backing disk, we are a dud. */ if (!NT_SUCCESS(status)) goto retry; /* Use the backing disk and report the sector-mapped disk. */ filedisk_ptr->file = file; /* Release the driver re-initialization stall. */ KeAcquireSpinLock(&WvFindDiskLock, &irql); --WvFindDisk; KeReleaseSpinLock(&WvFindDiskLock, irql); DBG("Found backing disk for filedisk %p\n", (PVOID) filedisk_ptr); return; retry: /* Sleep. */ DBG("Sleeping..."); KeDelayExecutionThread(KernelMode, FALSE, &delay_time.large_int); } while (count++ < 10); }
/** * Check if a disk might be the matching backing disk for * a GRUB4DOS sector-mapped disk by checking for a .VHD footer. * * @v file HANDLE to an open disk. * @v filedisk_ptr Points to the filedisk to match against. */ static BOOLEAN STDCALL WvFilediskG4dCheckDiskMatchVHD_( IN HANDLE file, IN WV_SP_FILEDISK_T filedisk_ptr ) { BOOLEAN ok = FALSE; WV_SP_MSVHD_FOOTER buf; NTSTATUS status; LARGE_INTEGER end_part; IO_STATUS_BLOCK io_status; /* Allocate a buffer for testing for a MS .VHD footer. */ buf = wv_malloc(sizeof *buf); if (buf == NULL) { goto err_alloc; } /* * Read in the buffer. Note that we adjust for the .VHD footer (plus * prefixed padding for an .ISO) that we truncated from the reported disk * size earlier when the disk mapping was found. */ end_part.QuadPart = filedisk_ptr->offset.QuadPart + (filedisk_ptr->disk->LBADiskSize * filedisk_ptr->disk->SectorSize) + filedisk_ptr->disk->SectorSize - sizeof *buf; status = ZwReadFile( file, NULL, NULL, NULL, &io_status, buf, sizeof *buf, &end_part, NULL ); if (!NT_SUCCESS(status)) goto err_read; /* Adjust the footer's byte ordering. */ msvhd__footer_swap_endian(buf); /* Examine .VHD fields for validity. */ if (!wv_memcmpeq(&buf->cookie, "conectix", sizeof buf->cookie)) goto err_cookie; if (buf->file_ver.val != 0x10000) goto err_ver; if (buf->data_offset.val != 0xffffffff) goto err_data_offset; if (buf->orig_size.val != buf->cur_size.val) goto err_size; if (buf->type.val != 2) goto err_type; /* Match against our expected disk size. */ if ( (filedisk_ptr->disk->LBADiskSize * filedisk_ptr->disk->SectorSize ) != buf->cur_size.val ) goto err_real_size; /* Passed the tests. We expect it to be our backing disk. */ ok = TRUE; err_real_size: err_type: err_size: err_data_offset: err_ver: err_cookie: err_read: wv_free(buf); err_alloc: return ok; }
/** * Find and hot-swap to a backing file. Internal. * * We search all filesystems for a particular filename, then swap * to using it as the backing store. This is currently useful for * sector-mapped disks which should really have a file-in-use lock * for the file they represent. Once the backing file is established, * the work item is freed. Otherwise, it is re-enqueued and the * thread will keep trying. */ static BOOLEAN STDCALL WvFilediskHotSwap_( IN WV_SP_FILEDISK_T filedisk, IN PUNICODE_STRING filename ) { NTSTATUS status; GUID vol_guid = GUID_DEVINTERFACE_VOLUME; PWSTR sym_links; PWCHAR pos; KIRQL irql; /* Do we currently have a backing disk? If not, re-enqueue for later. */ if (filedisk->file == NULL) return FALSE; /* * Find the backing volume and use it. We walk a list * of unicode volume device names and check each one for the file. */ status = IoGetDeviceInterfaces(&vol_guid, NULL, 0, &sym_links); if (!NT_SUCCESS(status)) return FALSE; pos = sym_links; status = STATUS_UNSUCCESSFUL; while (*pos != UNICODE_NULL) { UNICODE_STRING path; PFILE_OBJECT vol_file_obj; PDEVICE_OBJECT vol_dev_obj; UNICODE_STRING vol_dos_name; UNICODE_STRING filepath; static const WCHAR obj_path_prefix[] = L"\\??\\"; static const WCHAR path_sep = L'\\'; OBJECT_ATTRIBUTES obj_attrs; HANDLE file = 0; IO_STATUS_BLOCK io_status; RtlInitUnicodeString(&path, pos); /* Get some object pointers for the volume. */ status = IoGetDeviceObjectPointer( &path, FILE_READ_DATA, &vol_file_obj, &vol_dev_obj ); if (!NT_SUCCESS(status)) goto err_obj_ptrs; /* Get the DOS name. */ vol_dos_name.Buffer = NULL; vol_dos_name.Length = vol_dos_name.MaximumLength = 0; status = RtlVolumeDeviceToDosName( vol_file_obj->DeviceObject, &vol_dos_name ); if (!NT_SUCCESS(status)) goto err_dos_name; /* Build the file path. Ugh, what a mess. */ filepath.Length = filepath.MaximumLength = sizeof obj_path_prefix - sizeof UNICODE_NULL + vol_dos_name.Length + sizeof path_sep + filename->Length; filepath.Buffer = wv_malloc(filepath.Length); if (filepath.Buffer == NULL) { status = STATUS_UNSUCCESSFUL; goto err_alloc_buf; } { PCHAR buf = (PCHAR) filepath.Buffer; RtlCopyMemory( buf, obj_path_prefix, sizeof obj_path_prefix - sizeof UNICODE_NULL ); buf += sizeof obj_path_prefix - sizeof UNICODE_NULL; RtlCopyMemory(buf, vol_dos_name.Buffer, vol_dos_name.Length); buf += vol_dos_name.Length; RtlCopyMemory(buf, &path_sep, sizeof path_sep); buf += sizeof path_sep; RtlCopyMemory(buf, filename->Buffer, filename->Length); } /* buf scope */ InitializeObjectAttributes( &obj_attrs, &filepath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL ); /* Look for the file on this volume. */ status = ZwCreateFile( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs, &io_status, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (!NT_SUCCESS(status)) goto err_open; /* We could open it. Do the hot-swap. */ { HANDLE old = filedisk->file; filedisk->file = 0; filedisk->offset.QuadPart = 0; filedisk->file = file; ZwClose(old); } /* old scope */ err_open: wv_free(filepath.Buffer); err_alloc_buf: wv_free(vol_dos_name.Buffer); err_dos_name: ObDereferenceObject(vol_file_obj); err_obj_ptrs: /* Walk to the next terminator. */ while (*pos != UNICODE_NULL) pos++; /* If everything succeeded, stop. Otherwise try the next volume. */ if (!NT_SUCCESS(status)) pos++; } /* while */ wv_free(sym_links); /* Success? */ if (NT_SUCCESS(status)) { DBG("Finished hot-swapping.\n"); return TRUE; } return FALSE; }
/** * IRP_MJ_PNP:IRP_MN_QUERY_DEVICE_RELATIONS handler * * IRQL == PASSIVE_LEVEL * Completed by PDO, must accumulate relations downwards (upwards optional) * BusRelations: system thread * Yes: FDO * Maybe: filter * TargetDeviceRelation: any thread * Yes: PDO * RemovalRelations: system thread * Maybe: FDO * Maybe: filter * PowerRelations >= Windows 7: system thread * Maybe: FDO * Maybe: filter * EjectionRelations: system thread * Maybe: PDO * * @param Parameters.QueryDeviceRelations.Type (I/O stack location) * * @param FileObject (I/O stack location) * * @return * Success: * Irp->IoStatus.Status == STATUS_SUCCESS * Irp->IoStatus.Information populated from paged memory * Error: * Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES */ static NTSTATUS STDCALL WvMemdiskPnpQueryDeviceRelations( IN DEVICE_OBJECT * dev_obj, IN IRP * irp ) { S_WV_MEMDISK_BUS * bus; IO_STACK_LOCATION * io_stack_loc; NTSTATUS status; DEVICE_OBJECT * child; DEVICE_RELATIONS * dev_relations; DEVICE_RELATIONS * higher_dev_relations; DEVICE_RELATIONS * response; ULONG i; ASSERT(dev_obj); bus = dev_obj->DeviceExtension; ASSERT(bus); higher_dev_relations = (VOID *) irp->IoStatus.Information; ASSERT(irp); io_stack_loc = IoGetCurrentIrpStackLocation(irp); switch (io_stack_loc->Parameters.QueryDeviceRelations.Type) { case BusRelations: /* * Have we already created PDOs for the previous * INT 0x13 handler and the MEMDISK disk? */ switch (bus->BusRelations->Count) { case 0: /* Try to create the MEMDISK disk */ child = NULL; status = WvMemdiskCreateDisk(bus->PhysicalDeviceObject, &child); if (child) { bus->BusRelations->Count = 1; bus->BusRelations->Objects[0] = child; /* TODO: Uncomment when RAM disks are mini-driver */ if (0) if (!WvlAssignDeviceToBus(child, dev_obj)) { DBG("Couldn't add MEMDISK disk PDO\n"); WvlDeleteDevice(child); break; } } else { break; } /* Fall through */ case 1: /* Try to create the previous safe hook in the chain */ child = NULL; status = WvlCreateSafeHookDevice( bus->PreviousInt13hHandler, &child ); if (child) { bus->BusRelations->Count = 2; bus->BusRelations->Objects[1] = child; if (!WvlAssignDeviceToBus(child, dev_obj)) { DBG("Couldn't add safe hook PDO\n"); WvlDeleteDevice(child); } } break; case 2: /* All PDOs already produced */ break; default: DBG("Unexpected count of BusRelations!\n"); ASSERT(0); } dev_relations = bus->BusRelations; break; case TargetDeviceRelation: case RemovalRelations: case EjectionRelations: default: DBG("Unsupported device relations query\n"); return WvlPassIrpDown(dev_obj, irp); } /* We need to include any higher driver's PDOs */ higher_dev_relations = (VOID *) irp->IoStatus.Information; response = WvlMergeDeviceRelations(dev_relations, higher_dev_relations); if (!response) { /* * It's not clear whether or not we should do the following, * but it can't hurt... Right? */ if (higher_dev_relations) { for (i = 0; i < higher_dev_relations->Count; ++i) ObDereferenceObject(higher_dev_relations->Objects[i]); wv_free(higher_dev_relations); } irp->IoStatus.Status = status = STATUS_INSUFFICIENT_RESOURCES; irp->IoStatus.Information = 0; WvlPassIrpUp(dev_obj, irp, IO_NO_INCREMENT); return status; } /* Reference our PDOs */ for (i = 0; i < dev_relations->Count; ++i) ObReferenceObject(dev_relations->Objects[i]); /* Send the IRP down */ irp->IoStatus.Status = STATUS_SUCCESS; irp->IoStatus.Information = (ULONG_PTR) response; return WvlPassIrpDown(dev_obj, irp); }
/* Produce a dummy PDO node on the main bus. Internal */ static NTSTATUS STDCALL WvDummyAdd_( IN WV_SP_DUMMY_IDS DummyIds, IN PDEVICE_OBJECT * Pdo ) { NTSTATUS status; PDEVICE_OBJECT pdo = NULL; WV_SP_DEV_T dev; WV_SP_DUMMY_IDS new_dummy_ids; status = IoCreateDevice( WvDriverObj, sizeof (WV_S_DEV_EXT), NULL, DummyIds->DevType, DummyIds->DevCharacteristics, FALSE, &pdo ); if (!NT_SUCCESS(status) || !pdo) { DBG("Couldn't create dummy device.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto err_create_pdo; } dev = wv_malloc(sizeof *dev); if (!dev) { DBG("Couldn't allocate dummy device.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto err_dev; } new_dummy_ids = wv_malloc(DummyIds->Len); if (!new_dummy_ids) { DBG("Couldn't allocate dummy IDs.\n"); status = STATUS_INSUFFICIENT_RESOURCES; goto err_dummy_ids; } /* Copy the IDs' offsets and lengths. */ RtlCopyMemory(new_dummy_ids, DummyIds, DummyIds->Len); /* Ok! */ WvDevInit(dev); dev->ext = new_dummy_ids; /* TODO: Implement a dummy free. Leaking. */ dev->Self = pdo; /* Optionally fill the caller's PDO pointer. */ if (Pdo) *Pdo = pdo; WvDevForDevObj(pdo, dev); WvDevSetIrpHandler(pdo, WvDummyIrpDispatch); WvlBusInitNode(&dev->BusNode, pdo); /* Associate the parent bus. */ dev->Parent = WvBus.Fdo; /* Add the new PDO device to the bus' list of children. */ WvlBusAddNode(&WvBus, &dev->BusNode); pdo->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; wv_free(new_dummy_ids); err_dummy_ids: wv_free(dev); err_dev: IoDeleteDevice(pdo); err_create_pdo: return status; }