/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: internal.c Abstract: This module contains the internal subroutines used by the I/O system. Author: Darryl E. Havens (darrylh) 18-Apr-1989 Environment: Kernel mode, local to I/O system Revision History: --*/ #include "iop.h" PIRP IopDeadIrp; VOID IopUserRundown( IN PKAPC Apc ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, IopAbortRequest) #pragma alloc_text(PAGE, IopAcquireFileObjectLock) #pragma alloc_text(PAGE, IopAllocateIrpCleanup) #pragma alloc_text(PAGE, IopCancelAlertedRequest) #pragma alloc_text(PAGE, IopDeallocateApc) #pragma alloc_text(PAGE, IopExceptionCleanup) #pragma alloc_text(PAGE, IopGetDriverNameFromKeyNode) #pragma alloc_text(PAGE, IopGetFileName) #pragma alloc_text(PAGE, IopGetRegistryKeyInformation) #pragma alloc_text(PAGE, IopGetRegistryValue) #pragma alloc_text(PAGE, IopGetRegistryValues) #pragma alloc_text(PAGE, IopLoadDriver) #pragma alloc_text(PAGE, IopLoadFileSystemDriver) #pragma alloc_text(PAGE, IopLoadUnloadDriver) #pragma alloc_text(PAGE, IopMountVolume) #pragma alloc_text(PAGE, IopOpenLinkOrRenameTarget) #pragma alloc_text(PAGE, IopOpenRegistryKey) #pragma alloc_text(PAGE, IopQueryXxxInformation) #pragma alloc_text(PAGE, IopReadyDeviceObjects) #pragma alloc_text(PAGE, IopSynchronousApiServiceTail) #pragma alloc_text(PAGE, IopSynchronousServiceTail) #pragma alloc_text(PAGE, IopUserCompletion) #pragma alloc_text(PAGE, IopUserRundown) #pragma alloc_text(PAGE, IopXxxControlFile) #pragma alloc_text(PAGE, IopLookupBusStringFromID) #endif VOID IopAbortRequest( IN PKAPC Apc ) /*++ Routine Description: This routine is invoked to abort an I/O request. It is invoked during the rundown of a thread. Arguments: Apc - Pointer to the kernel APC structure. This structure is contained within the I/O Request Packet (IRP) itself. Return Value: None. --*/ { PKNORMAL_ROUTINE normalRoutine = (PKNORMAL_ROUTINE) NULL; PVOID normalContext = (PVOID) NULL; PVOID systemArgument1 = CONTAINING_RECORD( Apc, IRP, Tail.Apc ); PVOID systemArgument2 = (PVOID) NULL; PIRP irp; PAGED_CODE(); // // Get the address of the IRP and indicate that this I/O did not complete // properly. // irp = (PIRP) systemArgument1; // // Invoke the normal special kernel APC routine. // IopCompleteRequest( Apc, &normalRoutine, &normalContext, &systemArgument1, &systemArgument2 ); } NTSTATUS IopAcquireFileObjectLock( IN PFILE_OBJECT FileObject, IN KPROCESSOR_MODE RequestorMode, IN BOOLEAN Alertable, OUT PBOOLEAN Interrupted ) /*++ Routine Description: This routine is invoked to acquire the lock for a file object whenever there is contention and obtaining the fast lock for the file failed. Arguments: FileObject - Pointer to the file object whose lock is to be acquired. RequestorMode - Processor access mode of the caller. Alertable - Indicates whether or not the lock should be obtained in an alertable manner. Interrupted - A variable to receive a BOOLEAN that indicates whether or not the attempt to acquire the lock was interrupted by an alert or an APC. Return Value: The function status is the final status of the operation. --*/ { NTSTATUS status; PAGED_CODE(); // // Assume that the function will not be interrupted by an alert or an // APC while attempting to acquire the lock. // *Interrupted = FALSE; // // Loop attempting to acquire the lock for the file object. // InterlockedIncrement (&FileObject->Waiters); for (;;) { if (!FileObject->Busy) { // // The file object appears to be un-owned, try to acquire it // if (IopAcquireFastLock ( FileObject ) ) { // // Object was acquired. Remove our count and return success // InterlockedDecrement (&FileObject->Waiters); return STATUS_SUCCESS; } } // // Wait for the event that indicates that the thread that currently // owns the file object has released it. // status = KeWaitForSingleObject( &FileObject->Lock, Executive, RequestorMode, Alertable, (PLARGE_INTEGER) NULL ); // // If the above wait was interrupted, then indicate so and return. // Before returning, however, check the state of the ownership of // the file object itself. If it is not currently owned (the busy // flag is clear), then check to see whether or not there are any // other waiters. If so, then set the event to the signaled state // again so that they wake up and check the state of the busy flag. // if (status == STATUS_USER_APC || status == STATUS_ALERTED) { InterlockedDecrement (&FileObject->Waiters); if (!FileObject->Busy && FileObject->Waiters) { KeSetEvent( &FileObject->Lock, 0, FALSE ); } *Interrupted = TRUE; return status; } } } PIRP IopAllocateIrp( IN CCHAR StackSize, IN BOOLEAN ChargeQuota ) /*++ Routine Description: This routine allocates an I/O Request Packet from the system nonpaged pool. The packet will be allocated to contain StackSize stack locations. The IRP will also be semi-initialized. Arguments: StackSize - Specifies the maximum number of stack locations required. ChargeQuota - Specifies whether quota should be charged against thread. Return Value: The function value is the address of the allocated/initialized IRP, or NULL if one could not be allocated. --*/ { USHORT allocateSize; UCHAR fixedSize; PIRP irp; PNPAGED_LOOKASIDE_LIST lookasideList; UCHAR mustSucceed; USHORT packetSize; PLONGLONG status; // // If the size of the packet required is less than or equal to those on // the lookaside lists, then attempt to allocate the packet from the // lookaside lists. // irp = NULL; fixedSize = 0; mustSucceed = 0; packetSize = IoSizeOfIrp( StackSize ); allocateSize = packetSize; if (StackSize <= (CCHAR) IopLargeIrpStackLocations) { fixedSize = IRP_ALLOCATED_FIXED_SIZE; lookasideList = &IopSmallIrpLookasideList; if (StackSize != 1) { allocateSize = IoSizeOfIrp( (CCHAR) IopLargeIrpStackLocations ); lookasideList = &IopLargeIrpLookasideList; } lookasideList->L.TotalAllocates += 1; irp = (PIRP) ExInterlockedPopEntrySList( &lookasideList->L.ListHead, &lookasideList->Lock ); } // // If an IRP was not allocated from the lookaside list, then allocate // the packet from nonpaged pool and charge quota if requested. // if (!irp) { if (fixedSize != 0) { lookasideList->L.AllocateMisses += 1; } // // There are no free packets on the lookaside list, or the packet is // too large to be allocated from one of the lists, so it must be // allocated from nonpaged pool. If quota is to be charged, charge it // against the current process. Otherwise, allocate the pool normally. // if (ChargeQuota) { try { irp = ExAllocatePoolWithQuotaTag( NonPagedPool, allocateSize, ' prI' ); } except(EXCEPTION_EXECUTE_HANDLER) { NOTHING; } } else { // // Attempt to allocate the pool from non-paged pool. If this // fails, and the caller's previous mode was kernel then allocate // the pool as must succeed. // irp = ExAllocatePoolWithTag( NonPagedPool, allocateSize, ' prI' ); if (!irp) { mustSucceed = IRP_ALLOCATED_MUST_SUCCEED; if (KeGetPreviousMode() == KernelMode ) { irp = ExAllocatePoolWithTag( NonPagedPoolMustSucceed, allocateSize, ' prI' ); } } } if (!irp) { return NULL; } } else { ChargeQuota = FALSE; } // // Semi-initialize the packet. // irp->Type = (CSHORT) IO_TYPE_IRP; irp->Size = (USHORT) packetSize; irp->StackCount = (CCHAR) StackSize; irp->CurrentLocation = (CCHAR) (StackSize + 1); irp->ApcEnvironment = KeGetCurrentApcEnvironment(); status = (PLONGLONG) (&irp->IoStatus.Status); *status = 0; irp->Tail.Overlay.CurrentStackLocation = ((PIO_STACK_LOCATION) ((UCHAR *) irp + sizeof( IRP ) + ( StackSize * sizeof( IO_STACK_LOCATION )))); irp->AllocationFlags = (fixedSize | mustSucceed); if (ChargeQuota) { irp->AllocationFlags |= IRP_QUOTA_CHARGED; } if (StackSize > 1) { PIO_STACK_LOCATION irpSp; irpSp = (PIO_STACK_LOCATION) (irp + 1); RtlZeroMemory( irpSp, (StackSize -1) * sizeof( IO_STACK_LOCATION ) ); } return irp; } VOID IopAllocateIrpCleanup( IN PFILE_OBJECT FileObject, IN PKEVENT EventObject OPTIONAL ) /*++ Routine Description: This routine is invoked internally by those system services that attempt to allocate an IRP and fail. This routine cleans up the file object and any event object that has been references and releases any locks that were taken out. Arguments: FileObject - Pointer to the file object being worked on. EventObject - Optional pointer to a referenced event to be dereferenced. Return Value: None. --*/ { PAGED_CODE(); // // Begin by dereferencing the event, if one was specified. // if (ARGUMENT_PRESENT( EventObject )) { ObDereferenceObject( EventObject ); } // // Release the synchronization semaphore if it is currently held and // dereference the file object. // if (FileObject->Flags & FO_SYNCHRONOUS_IO) { IopReleaseFileObjectLock( FileObject ); } ObDereferenceObject( FileObject ); return; } PIRP IopAllocateIrpMustSucceed( IN CCHAR StackSize ) /*++ Routine Description: This routine is invoked to allocate an IRP when there are no appropriate packets remaining on the look-aside list, and no memory was available from the general non-paged pool, and yet, the code path requiring the packet has no way of backing out and simply returning an error. There- fore, it must allocate an IRP. Hence, this routine is called to allocate that packet. Arguments: StackSize - Supplies the number of IRP I/O stack locations that the packet must have when allocated. Return Value: A pointer to the allocated I/O Request Packet. --*/ { PIRP irp; USHORT packetSize; // // Attempt to allocate the IRP normally and failing that, allocate the // IRP from nonpaged must succeed pool. // irp = IoAllocateIrp(StackSize, FALSE); if (!irp) { packetSize = IoSizeOfIrp(StackSize); irp = ExAllocatePoolWithTag(NonPagedPoolMustSucceed, packetSize, ' prI'); IoInitializeIrp(irp, packetSize, StackSize); irp->AllocationFlags |= IRP_ALLOCATED_MUST_SUCCEED; } return irp; } VOID IopApcHardError( IN PVOID StartContext ) /*++ Routine Description: This function is invoked when we need to do a hard error pop-up, but the Irp's originating thread is at APC level, ie. IoPageRead. We in a special purpose thread that will go away when the user responds to the pop-up. Arguments: StartContext - Startup context, contains a IOP_APC_HARD_ERROR_PACKET. Return Value: None. --*/ { PIOP_APC_HARD_ERROR_PACKET packet; packet = StartContext; IopRaiseHardError( packet->Irp, packet->Vpb, packet->RealDeviceObject ); ExFreePool( packet ); } VOID IopCancelAlertedRequest( IN PKEVENT Event, IN PIRP Irp ) /*++ Routine Description: This routine is invoked when a synchronous I/O operation that is blocked in the I/O system needs to be canceled because the thread making the request has either been alerted because it is going away or because of a CTRL/C. This routine carefully attempts to work its way out of the current operation so that local events or other local data will not be accessed once the service being interrupted returns. Arguments: Event - The address of a kernel event that will be set to the Signaled state by I/O completion when the request is complete. Irp - Pointer to the I/O Request Packet (IRP) representing the current request. Return Value: None. --*/ { KIRQL irql; LARGE_INTEGER deltaTime; BOOLEAN canceled; PAGED_CODE(); // // Begin by blocking special kernel APCs so that the request cannot // complete. // KeRaiseIrql( APC_LEVEL, &irql ); // // Check the state of the event to determine whether or not the // packet has already been completed. // if (KeReadStateEvent( Event ) == 0) { // // The packet has not been completed, so attempt to cancel it. // canceled = IoCancelIrp( Irp ); KeLowerIrql( irql ); if (canceled) { // // The packet had a cancel routine, so it was canceled. Loop, // waiting for the packet to complete. This should occur almost // immediately. // deltaTime.QuadPart = - 10 * 1000 * 10; while (KeReadStateEvent( Event ) == 0) { KeDelayExecutionThread( KernelMode, FALSE, &deltaTime ); } } else { // // The packet did not have a cancel routine, so simply wait for // the event to be set to the Signaled state. This will save // CPU time by not looping, since it is not known when the packet // will actually complete. Note, however, that the cancel flag // is set in the packet, so should a driver examine the flag // at some point in the future, it will immediately stop // processing the request. // (VOID) KeWaitForSingleObject( Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); } } else { // // The packet has already been completed, so simply lower the // IRQL back to its original value and exit. // KeLowerIrql( irql ); } } ULONG IopChecksum( IN PVOID Buffer, IN ULONG Length ) /*++ Routine Description: This routine generates a simple checksum for a buffer. Arguments: Buffer - Pointer to buffer for which the checksum is to be generated. Length - The length, in bytes, of the buffer. Return Value: The generated checksum value. --*/ { ULONG sum = 0; PUSHORT source; ULONG length = Length >> 1; // // Compute the word wise checksum allowing carries to occur into the // high order half of the checksum longword. // source = (PUSHORT) Buffer; while (length--) { sum += *source++; sum = (sum >> 16) + (sum & 0xffff); } // // Fold final carry into a full longword result and return the resultant // value. // return (sum >> 16) + sum; } VOID IopCompleteUnloadOrDelete( IN PDEVICE_OBJECT DeviceObject, IN KIRQL Irql ) /*++ Routine Description: This routine is invoked when the reference count on a device object transitions to a zero and the driver is mark for unload or device has been marked for delete. This means that it may be possible to actually unload the driver or delete the device ojbect. If all of the devices have a reference count of zero, then the driver is actually unloaded. Note that in order to ensure that this routine is not invoked twice, at the same time, on two different processors, the I/O database spin lock is still held at this point. Arguments: DeviceObject - Supplies a pointer to one of the driver's device objects, namely the one whose reference count just went to zero. Irql - Specifies the IRQL of the processor at the time that the I/O database lock was acquired. Return Value: None. --*/ { PDEVICE_OBJECT deviceObject; PDRIVER_OBJECT driverObject; BOOLEAN unload = TRUE; driverObject = DeviceObject->DriverObject; if (DeviceObject->DeviceObjectExtension->ExtensionFlags & DOE_DELETE_PENDING) { if ((DeviceObject->DeviceObjectExtension->ExtensionFlags & DOE_UNLOAD_PENDING) == 0 || driverObject->Flags & DRVO_UNLOAD_INVOKED) { unload = FALSE; } ExReleaseSpinLock( &IopDatabaseLock, Irql ); // // If another device is attached to this device, inform the former's // driver that the device is being deleted. // if (DeviceObject->AttachedDevice) { PFAST_IO_DISPATCH fastIoDispatch = DeviceObject->AttachedDevice->DriverObject->FastIoDispatch; if (fastIoDispatch && fastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET( FAST_IO_DISPATCH, FastIoDetachDevice ) && fastIoDispatch->FastIoDetachDevice) { (fastIoDispatch->FastIoDetachDevice)( DeviceObject->AttachedDevice, DeviceObject ); } } // // Deallocate the memory for the security descriptor that was allocated // for this device object. // if (DeviceObject->SecurityDescriptor != (PSECURITY_DESCRIPTOR) NULL) { ExFreePool( DeviceObject->SecurityDescriptor ); } // // Remove this device object from the driver object's list. // IopInsertRemoveDevice( DeviceObject->DriverObject, DeviceObject, FALSE ); // // Finally, dereference the object so it is deleted. // ObDereferenceObject( DeviceObject ); // // Return if the unload does not need to be done. // if (!unload) { return; } // // Reacquire the spin lock make sure the unload routine does has // not been called. // ExAcquireSpinLock( &IopDatabaseLock, &Irql ); if (driverObject->Flags & DRVO_UNLOAD_INVOKED) { // // Some other thread is doing the unload, release the lock and return. // ExReleaseSpinLock( &IopDatabaseLock, Irql ); return; } } // // Scan the list of device objects for this driver, looking for a // non-zero reference count. If any reference count is non-zero, then // the driver may not be unloaded. // deviceObject = driverObject->DeviceObject; while (deviceObject) { if (deviceObject->ReferenceCount || deviceObject->AttachedDevice || deviceObject->DeviceObjectExtension->ExtensionFlags & DOE_DELETE_PENDING) { unload = FALSE; break; } deviceObject = deviceObject->NextDevice; } if (unload) { driverObject->Flags |= DRVO_UNLOAD_INVOKED; } ExReleaseSpinLock( &IopDatabaseLock, Irql ); // // If the reference counts for all of the devices is zero, then this // driver can now be unloaded. // if (unload) { LOAD_PACKET loadPacket; KeInitializeEvent( &loadPacket.Event, NotificationEvent, FALSE ); loadPacket.DriverObject = driverObject; ExInitializeWorkItem( &loadPacket.WorkQueueItem, IopLoadUnloadDriver, &loadPacket ); ExQueueWorkItem( &loadPacket.WorkQueueItem, DelayedWorkQueue ); (VOID) KeWaitForSingleObject( &loadPacket.Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); MmUnloadSystemImage( driverObject->DriverSection ); ObMakeTemporaryObject( driverObject ); ObDereferenceObject( driverObject ); } } VOID IopCompletePageWrite( IN PKAPC Apc, IN PKNORMAL_ROUTINE *NormalRoutine, IN PVOID *NormalContext, IN PVOID *SystemArgument1, IN PVOID *SystemArgument2 ) /*++ Routine Description: This routine executes as a special kernel APC routine in the context of the Modified Page Writer (MPW) system thread when an out-page operation has completed. This routine performs the following tasks: o The I/O status is copied. o The Modified Page Writer's APC routine is invoked. Arguments: Apc - Supplies a pointer to kernel APC structure. NormalRoutine - Supplies a pointer to a pointer to the normal function that was specified when the APC was initialied. NormalContext - Supplies a pointer to a pointer to an arbitrary data structure that was specified when the APC was initialized. SystemArgument1 - Supplies a pointer to an argument that contains an argument that is unused by this routine. SystemArgument2 - Supplies a pointer to an argument that contains an argument that is unused by this routine. Return Value: None. --*/ { PIRP irp; PIO_APC_ROUTINE apcRoutine; PVOID apcContext; PIO_STATUS_BLOCK ioStatus; UNREFERENCED_PARAMETER( NormalRoutine ); UNREFERENCED_PARAMETER( NormalContext ); UNREFERENCED_PARAMETER( SystemArgument1 ); UNREFERENCED_PARAMETER( SystemArgument2 ); // // Begin by getting the address of the I/O Request Packet from the APC. // irp = CONTAINING_RECORD( Apc, IRP, Tail.Apc ); // // If this I/O operation did not complete successfully through the // dispatch routine of the driver, then drop everything on the floor // now and return to the original call point in the MPW. // if (!irp->PendingReturned && NT_ERROR( irp->IoStatus.Status )) { IoFreeIrp( irp ); return; } // // Copy the I/O status from the IRP into the caller's I/O status block. // *irp->UserIosb = irp->IoStatus; // // Copy the pertitnent information from the I/O Request Packet into locals // and free it. // apcRoutine = irp->Overlay.AsynchronousParameters.UserApcRoutine; apcContext = irp->Overlay.AsynchronousParameters.UserApcContext; ioStatus = irp->UserIosb; IoFreeIrp( irp ); // // Finally, invoke the MPW's APC routine. // apcRoutine( apcContext, ioStatus, 0 ); return; } VOID IopCompleteRequest( IN PKAPC Apc, IN PKNORMAL_ROUTINE *NormalRoutine, IN PVOID *NormalContext, IN PVOID *SystemArgument1, IN PVOID *SystemArgument2 ) /*++ Routine Description: This routine executes as a special kernel APC routine in the context of the thread which originally requested the I/O operation which is now being completed. This routine performs the following tasks: o A check is made to determine whether the specified request ended with an error status. If so, and the error code qualifies as one which should be reported to an error port, then an error port is looked for in the thread/process. If one exists, then this routine will attempt to set up an LPC to it. Otherwise, it will attempt to set up an LPC to the system error port. o Copy buffers. o Free MDLs. o Copy I/O status. o Set event, if any and dereference if appropriate. o Dequeue the IRP from the thread queue as pending I/O request. o Queue APC to thread, if any. o If no APC is to be queued, then free the packet now. Arguments: Apc - Supplies a pointer to kernel APC structure. NormalRoutine - Supplies a pointer to a pointer to the normal function that was specified when the APC was initialied. NormalContext - Supplies a pointer to a pointer to an arbitrary data structure that was specified when the APC was initialized. SystemArgument1 - Supplies a pointer to an argument that contains the address of the original file object for this I/O operation. SystemArgument2 - Supplies a pointer to an argument that contains an argument that is unused by this routine. Return Value: None. --*/ { #define SynchronousIo( Irp, FileObject ) ( \ (Irp->Flags & IRP_SYNCHRONOUS_API) || \ (FileObject == NULL ? 0 : FileObject->Flags & FO_SYNCHRONOUS_IO) ) PIRP irp; PMDL mdl, nextMdl; PETHREAD thread; PFILE_OBJECT fileObject; UNREFERENCED_PARAMETER( NormalRoutine ); UNREFERENCED_PARAMETER( NormalContext ); UNREFERENCED_PARAMETER( SystemArgument2 ); // // Begin by getting the address of the I/O Request Packet. Also, get // the address of the current thread and the address of the original file // object for this I/O operation. // irp = CONTAINING_RECORD( Apc, IRP, Tail.Apc ); thread = PsGetCurrentThread(); fileObject = (PFILE_OBJECT) *SystemArgument1; // // Ensure that the packet is not being completed with a minus one. This // is apparently a common problem in some drivers, and has no meaning // as a status code. // ASSERT( irp->IoStatus.Status != 0xffffffff ); // // Check to see whether there is any data in a system buffer which needs // to be copied to the caller's buffer. If so, copy the data and then // free the system buffer if necessary. // if (irp->Flags & IRP_BUFFERED_IO) { // // Copy the data if this was an input operation. Note that no copy // is performed if the status indicates that a verify operation is // required, or if the final status was an error-level severity. // if (irp->Flags & IRP_INPUT_OPERATION && irp->IoStatus.Status != STATUS_VERIFY_REQUIRED && !NT_ERROR( irp->IoStatus.Status )) { // // Copy the information from the system buffer to the caller's // buffer. This is done with an exception handler in case // the operation fails because the caller's address space // has gone away, or it's protection has been changed while // the service was executing. // try { RtlCopyMemory( irp->UserBuffer, irp->AssociatedIrp.SystemBuffer, irp->IoStatus.Information ); } except(EXCEPTION_EXECUTE_HANDLER) { // // An exception occurred while attempting to copy the // system buffer contents to the caller's buffer. Set // a new I/O completion status. // irp->IoStatus.Status = GetExceptionCode(); } } // // Free the buffer if needed. // if (irp->Flags & IRP_DEALLOCATE_BUFFER) { ExFreePool( irp->AssociatedIrp.SystemBuffer ); } } // // If there is an MDL (or MDLs) associated with this I/O request, // Free it (them) here. This is accomplished by walking the MDL list // hanging off of the IRP and deallocating each MDL encountered. // if (irp->MdlAddress) { for (mdl = irp->MdlAddress; mdl != NULL; mdl = nextMdl) { nextMdl = mdl->Next; IoFreeMdl( mdl ); } } // // Check to see whether or not the I/O operation actually completed. If // it did, then proceed normally. Otherwise, cleanup everything and get // out of here. // if (!NT_ERROR( irp->IoStatus.Status ) || (NT_ERROR( irp->IoStatus.Status ) && irp->PendingReturned && !SynchronousIo( irp, fileObject ))) { PVOID port = NULL; ULONG key; // // If there is an I/O competion port object associated w/this request, // save it here so that the file object can be dereferenced. // if (fileObject && fileObject->CompletionContext) { port = fileObject->CompletionContext->Port; key = fileObject->CompletionContext->Key; } // // Copy the I/O status from the IRP into the caller's I/O status // block. This is done using an exception handler in case the caller's // virtual address space for the I/O status block was deleted or // its protection was changed to readonly. Note that if the I/O // status block cannot be written, the error is simply ignored since // there is no way to tell the caller that something went wrong. // This is, of course, by definition, since the I/O status block // is where the caller will attempt to look for errors in the first // place! // try { *irp->UserIosb = irp->IoStatus; } except(EXCEPTION_EXECUTE_HANDLER) { // // An exception was incurred attempting to write the caller's // I/O status block. Simply continue executing as if nothing // ever happened since nothing can be done about it anyway. // NOTHING; } // // Determine whether the caller supplied an event that needs to be set // to the Signaled state. If so, then set it; otherwise, set the event // in the file object to the Signaled state. // // It is possible for the event to have been specified as a PKEVENT if // this was an I/O operation hand-built for an FSP or an FSD, or // some other types of operations such as synchronous I/O APIs. In // any of these cases, the event was not referenced since it is not an // object manager event, so it should not be dereferenced. // // Also, it is possible for there not to be a file object for this IRP. // This occurs when an FSP is doing I/O operations to a device driver on // behalf of a process doing I/O to a file. The file object cannot be // dereferenced if this is the case. If this operation was a create // operation then the object should not be dereferenced either. This // is because the reference count must be one or it will go away for // the caller (not much point in making an object that just got created // go away). // if (irp->UserEvent) { (VOID) KeSetEvent( irp->UserEvent, 0, FALSE ); if (fileObject) { if (!(irp->Flags & IRP_SYNCHRONOUS_API)) { ObDereferenceObject( irp->UserEvent ); } if (fileObject->Flags & FO_SYNCHRONOUS_IO && !(irp->Flags & IRP_OB_QUERY_NAME)) { (VOID) KeSetEvent( &fileObject->Event, 0, FALSE ); fileObject->FinalStatus = irp->IoStatus.Status; } if (!(irp->Flags & IRP_CREATE_OPERATION)) { ObDereferenceObject( fileObject ); } else { irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL; } } } else if (fileObject) { (VOID) KeSetEvent( &fileObject->Event, 0, FALSE ); fileObject->FinalStatus = irp->IoStatus.Status; if (!(irp->Flags & IRP_CREATE_OPERATION)) { ObDereferenceObject( fileObject ); } else { irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL; } } // // If this is normal I/O, update the transfer count for this process. // if (!(irp->Flags & IRP_CREATE_OPERATION)) { if (irp->Flags & IRP_READ_OPERATION) { IopUpdateReadTransferCount( irp->IoStatus.Information ); } else if (irp->Flags & IRP_WRITE_OPERATION) { IopUpdateWriteTransferCount( irp->IoStatus.Information ); } else { IopUpdateOtherTransferCount( irp->IoStatus.Information ); } } // // Dequeue the packet from the thread's pending I/O request list. // IopDequeueThreadIrp( irp ); // // If the caller requested an APC, queue it to the thread. If not, then // simply free the packet now. // if (irp->Overlay.AsynchronousParameters.UserApcRoutine) { KeInitializeApc( &irp->Tail.Apc, &thread->Tcb, CurrentApcEnvironment, IopUserCompletion, (PKRUNDOWN_ROUTINE) IopUserRundown, (PKNORMAL_ROUTINE) irp->Overlay.AsynchronousParameters.UserApcRoutine, irp->RequestorMode, irp->Overlay.AsynchronousParameters.UserApcContext ); KeInsertQueueApc( &irp->Tail.Apc, irp->UserIosb, NULL, 2 ); } else if (port && irp->Overlay.AsynchronousParameters.UserApcContext) { // // If there is a completion context associated w/this I/O operation, // send the message to the port. Tag completion packet as an Irp. // Mini packets (from NtSetCompletionPort have CurrentStackLocation // set to -1 // irp->Tail.CompletionKey = key; irp->Tail.Overlay.CurrentStackLocation = NULL; KeInsertQueue( (PKQUEUE) port, &irp->Tail.Overlay.ListEntry ); } else { // // Free the IRP now since it is no longer needed. // IoFreeIrp( irp ); } } else { if (irp->PendingReturned && fileObject) { // // This is an I/O operation that completed as an error for // which a pending status was returned and the I/O operation // is synchronous. For this case, the I/O system is waiting // on behalf of the caller. If the reason that the I/O was // synchronous is that the file object was opened for synchronous // I/O, then the event associated with the file object is set // to the signaled state. If the I/O operation was synchronous // because this is a synchronous API, then the event is set to // the signaled state. // // Note also that the status must be returned for both types // of synchronous I/O. If this is a synchronous API, then the // I/O system supplies its own status block so it can simply // be written; otherwise, the I/O system will obtain the final // status from the file object itself. // if (irp->Flags & IRP_SYNCHRONOUS_API) { *irp->UserIosb = irp->IoStatus; if (irp->UserEvent) { (VOID) KeSetEvent( irp->UserEvent, 0, FALSE ); } else { (VOID) KeSetEvent( &fileObject->Event, 0, FALSE ); } } else { fileObject->FinalStatus = irp->IoStatus.Status; (VOID) KeSetEvent( &fileObject->Event, 0, FALSE ); } } // // The operation was incomplete. Perform the general cleanup. Note // that everything is basically dropped on the floor without doing // anything. That is: // // IoStatusBlock - Do nothing. // Event - Dereference without setting to Signaled state. // FileObject - Dereference without setting to Signaled state. // ApcRoutine - Do nothing. // if (fileObject) { if (!(irp->Flags & IRP_CREATE_OPERATION)) { ObDereferenceObject( fileObject ); } } if (irp->UserEvent && fileObject && !(irp->Flags & IRP_SYNCHRONOUS_API)) { ObDereferenceObject( irp->UserEvent ); } IopDequeueThreadIrp( irp ); IoFreeIrp( irp ); } } VOID IopDisassociateThreadIrp( VOID ) /*++ Routine Description: This routine is invoked when the I/O requests for a thread are being cancelled, but there is a packet at the end of the thread's queue that has not been completed for such a long period of time that it has timed out. It is this routine's responsibility to try to disassociate that IRP with this thread. Arguments: None. Return Value: None. --*/ { KIRQL irql; KIRQL spIrql; PIRP irp; PETHREAD thread; PLIST_ENTRY entry; PIO_STACK_LOCATION irpSp; PDEVICE_OBJECT deviceObject; PDRIVER_OBJECT driverObject; WCHAR buffer[512]; POBJECT_NAME_INFORMATION nameInformation; ULONG nameLength; NTSTATUS status; ULONG response; // // Begin by ensuring that the packet has not already been removed from // the thread's queue. // KeRaiseIrql( APC_LEVEL, &irql ); thread = PsGetCurrentThread(); // // If there are no packets on the IRP list, then simply return now. // All of the packets have been fully completed, so the caller will also // simply return to its caller. // if (IsListEmpty( &thread->IrpList )) { KeLowerIrql( irql ); return; } // // Get a pointer to the first packet on the queue, and begin examining // it. Note that because the processor is at raised IRQL, and because // the packet can only be removed in the context of the currently // executing thread, that it is not possible for the packet to be removed // from the list. On the other hand, it IS possible for the packet to // be queued to the thread's APC list at this point, and this must be // blocked/synchronized in order to examine the request. // // Begin, therefore, by acquiring the I/O completion spinlock, so that // the packet can be safely examined. // ExAcquireSpinLock( &IopCompletionLock, &spIrql ); // // Check to see whether or not the packet has been completed (that is, // queued to the current thread). If not, change threads. // entry = thread->IrpList.Flink; irp = CONTAINING_RECORD( entry, IRP, ThreadListEntry ); if (irp->CurrentLocation == irp->StackCount + 2) { // // The request has just gone through enough of completion that // queueing it to the thread is inevitable. Simply release the // lock and return. // ExReleaseSpinLock( &IopCompletionLock, spIrql ); KeLowerIrql( irql ); return; } // // The packet has been located, and it is not going through completion // at this point. Switch threads, so that it will not complete through // this thread, remove the request from this thread's queue, and release // the spinlock. Final processing of the IRP will occur when I/O // completion notices that there is no thread associated with the // request. It will essentially drop the I/O on the floor. // // Also, while the request is still held, attempt to determine on which // device object the operation is being performed. // //// ////DbgPrint( "Disassociating Irp: %x\n", irp ); ////DbgBreakPoint(); //// IopDeadIrp = irp; irp->Tail.Overlay.Thread = (PETHREAD) NULL; entry = RemoveHeadList( &thread->IrpList ); irpSp = IoGetCurrentIrpStackLocation( irp ); if (irp->CurrentLocation <= irp->StackCount) { deviceObject = irpSp->DeviceObject; } else { deviceObject = (PDEVICE_OBJECT) NULL; } ExReleaseSpinLock( &IopCompletionLock, spIrql ); KeLowerIrql( irql ); // // If a device object could be identified, then its possible that the // name of the device driver can also be identified, provided that they // don't both go away at this point. Attempt to get the name of the // driver object through the device object. Note that this can fail // if the device is going away. // if (deviceObject) { ObReferenceObject( deviceObject ); driverObject = deviceObject->DriverObject; // // Now attempt to put up a popup explaining that the I/O has timed out // and will essentially be dropped on the floor. Pass in the name of // the driver and the address of the IRP as parameters. // nameInformation = (POBJECT_NAME_INFORMATION) buffer; status = ObQueryNameString( driverObject, nameInformation, sizeof( buffer ), &nameLength ); ObDereferenceObject( deviceObject ); if (NT_SUCCESS( status )) { { ULONG parameters = (ULONG) &nameInformation->Name; ExRaiseHardError( STATUS_DRIVER_CANCEL_TIMEOUT, 1, 1, ¶meters, OptionOk, &response ); } } } return; } VOID IopDeallocateApc( IN PKAPC Apc, IN PKNORMAL_ROUTINE *NormalRoutine, IN PVOID *NormalContext, IN PVOID *SystemArgument1, IN PVOID *SystemArgument2 ) /*++ Routine Description: This routine is invoked to deallocate an APC that was used to queue a request to a target thread. It simple deallocates the APC. Arguments: Apc - Supplies a pointer to kernel APC structure. NormalRoutine - Supplies a pointer to a pointer to the normal function that was specified when the APC was initialied. NormalContext - Supplies a pointer to a pointer to an arbitrary data structure that was specified when the APC was initialized. SystemArgument1, SystemArgument2 - Supplies a set of two pointers to two arguments that contain untyped data. Return Value: None. --*/ { UNREFERENCED_PARAMETER( NormalRoutine ); UNREFERENCED_PARAMETER( NormalContext ); UNREFERENCED_PARAMETER( SystemArgument1 ); UNREFERENCED_PARAMETER( SystemArgument2 ); PAGED_CODE(); // // Free the APC. // ExFreePool( Apc ); } VOID IopDropIrp( IN PIRP Irp, IN PFILE_OBJECT FileObject ) /*++ Routine Description: This routine attempts to drop everything about the specified IRP on the floor. Arguments: Irp - Supplies the I/O Request Packet to be completed to the bit bucket. Return Value: None. --*/ { PMDL mdl; PMDL nextMdl; // // Free the resources associated with the IRP. // if (Irp->Flags & IRP_DEALLOCATE_BUFFER) { ExFreePool( Irp->AssociatedIrp.SystemBuffer ); } if (Irp->MdlAddress) { for (mdl = Irp->MdlAddress; mdl; mdl = nextMdl) { nextMdl = mdl->Next; IoFreeMdl( mdl ); } } if (Irp->UserEvent && FileObject && !(Irp->Flags & IRP_SYNCHRONOUS_API)) { ObDereferenceObject( Irp->UserEvent ); } if (FileObject && !(Irp->Flags & IRP_CREATE_OPERATION)) { ObDereferenceObject( FileObject ); } // // Finally, free the IRP itself. // IoFreeIrp( Irp ); } LONG IopExceptionFilter( IN PEXCEPTION_POINTERS ExceptionPointer, OUT PNTSTATUS ExceptionCode ) /*++ Routine Description: This routine is invoked when an exception occurs to determine whether or not the exception was due to an error that caused an in-page error status code exception to be raised. If so, then this routine changes the code in the exception record to the actual error code that was originally raised. Arguments: ExceptionPointer - Pointer to the exception record. ExceptionCode - Variable to receive actual exception code. Return Value: The function value indicates that the exception handler is to be executed. --*/ { // // Simply check for an in-page error status code and, if the conditions // are right, replace it with the actual status code. // *ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode; if (*ExceptionCode == STATUS_IN_PAGE_ERROR && ExceptionPointer->ExceptionRecord->NumberParameters >= 3) { *ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionInformation[2]; } return EXCEPTION_EXECUTE_HANDLER; } VOID IopExceptionCleanup( IN PFILE_OBJECT FileObject, IN PIRP Irp, IN PKEVENT EventObject OPTIONAL, IN PKEVENT KernelEvent OPTIONAL ) /*++ Routine Description: This routine performs generalized cleanup for the I/O system services when an exception occurs during caller parameter processing. This routine performs the following steps: o If a system buffer was allocated it is freed. o If an MDL was allocated it is freed. o The IRP is freed. o If the file object is opened for synchronous I/O, the semaphore is released. o If an event object was referenced it is dereferenced. o If a kernel event was allocated, free it. o The file object is dereferenced. Arguments: FileObject - Pointer to the file object currently being worked on. Irp - Pointer to the IRP allocated to handle the I/O request. EventObject - Optional pointer to a referenced event object. KernelEvent - Optional pointer to an allocated kernel event. Return Value: None. --*/ { PAGED_CODE(); // // If a system buffer was allocated from nonpaged pool, free it. // if (Irp->AssociatedIrp.SystemBuffer != NULL) { ExFreePool( Irp->AssociatedIrp.SystemBuffer ); } // // If an MDL was allocated, free it. // if (Irp->MdlAddress != NULL) { IoFreeMdl( Irp->MdlAddress ); } // // Free the I/O Request Packet. // IoFreeIrp( Irp ); // // Finally, release the synchronization semaphore if it is currently // held, dereference the event if one was specified, free the kernel // event if one was allocated, and dereference the file object. // if (FileObject->Flags & FO_SYNCHRONOUS_IO) { IopReleaseFileObjectLock( FileObject ); } if (ARGUMENT_PRESENT( EventObject )) { ObDereferenceObject( EventObject ); } if (ARGUMENT_PRESENT( KernelEvent )) { ExFreePool( KernelEvent ); } ObDereferenceObject( FileObject ); return; } VOID IopFreeIrpAndMdls( IN PIRP Irp ) /*++ Routine Description: This routine frees the specified I/O Request Packet and all of its Memory Descriptor Lists. Arguments: Irp - Pointer to the I/O Request Packet to be freed. Return Value: None. --*/ { PMDL mdl; PMDL nextMdl; // // If there are any MDLs that need to be freed, free them now. // for (mdl = Irp->MdlAddress; mdl != (PMDL) NULL; mdl = nextMdl) { nextMdl = mdl->Next; IoFreeMdl( mdl ); } // // Free the IRP. // IoFreeIrp( Irp ); return; } NTSTATUS IopGetDriverNameFromKeyNode( IN HANDLE KeyHandle, OUT PUNICODE_STRING DriverName ) /*++ Routine Description: Given a handle to a driver service list key in the registry, return the name that represents the Object Manager name space string that should be used to locate/create the driver object. Arguments: KeyHandle - Supplies a handle to driver service entry in the registry. DriverName - Supplies a Unicode string descriptor variable in which the name of the driver is returned. Return Value: The function value is the final status of the operation. --*/ { PKEY_VALUE_FULL_INFORMATION keyValueInformation; PKEY_BASIC_INFORMATION keyBasicInformation; ULONG keyBasicLength; NTSTATUS status; PAGED_CODE(); // // Get the optional object name for this driver from the value for this // key. If one exists, then its name overrides the default name of the // driver. // status = IopGetRegistryValue( KeyHandle, L"ObjectName", &keyValueInformation ); if (NT_SUCCESS( status )) { PWSTR src, dst; ULONG i; // // The driver entry specifies an object name. This overrides the // default name for the driver. Use this name to open the driver // object. // if (!keyValueInformation->DataLength) { ExFreePool( keyValueInformation ); return STATUS_ILL_FORMED_SERVICE_ENTRY; } DriverName->Length = (USHORT) (keyValueInformation->DataLength - sizeof( WCHAR )); DriverName->MaximumLength = (USHORT) keyValueInformation->DataLength; src = (PWSTR) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset); dst = (PWSTR) keyValueInformation; for (i = DriverName->Length; i; i--) { *dst++ = *src++; } DriverName->Buffer = (PWSTR) keyValueInformation; } else { PULONG driverType; PWSTR baseObjectName; UNICODE_STRING remainderName; // // The driver node does not specify an object name, so determine // what the default name for the driver object should be based on // the information in the key. // status = IopGetRegistryValue( KeyHandle, L"Type", &keyValueInformation ); if (!NT_SUCCESS( status ) || !keyValueInformation->DataLength) { // // There must be some type of "Type" associated with this driver, // either DRIVER or FILE_SYSTEM. Otherwise, this node is ill- // formed. // if (NT_SUCCESS( status )) { ExFreePool( keyValueInformation ); } return STATUS_ILL_FORMED_SERVICE_ENTRY; } // // Now determine whether the type of this entry is a driver or a // file system. Begin by assuming that it is a device driver. // baseObjectName = L"\\Driver\\"; DriverName->Length = 8*2; driverType = (PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset); if (*driverType == FileSystemType || *driverType == RecognizerType) { baseObjectName = L"\\FileSystem\\"; DriverName->Length = 12*2; } // // Get the name of the key that is being used to describe this // driver. This will return just the last component of the name // string, which can be used to formulate the name of the driver. // status = NtQueryKey( KeyHandle, KeyBasicInformation, (PVOID) NULL, 0, &keyBasicLength ); keyBasicInformation = ExAllocatePool( NonPagedPool, keyBasicLength ); if (!keyBasicInformation) { ExFreePool( keyValueInformation ); return STATUS_INSUFFICIENT_RESOURCES; } status = NtQueryKey( KeyHandle, KeyBasicInformation, keyBasicInformation, keyBasicLength, &keyBasicLength ); if (!NT_SUCCESS( status )) { ExFreePool( keyBasicInformation ); ExFreePool( keyValueInformation ); return status; } // // Allocate a buffer from pool that is large enough to contain the // entire name string of the driver object. // DriverName->MaximumLength = (USHORT) (DriverName->Length + keyBasicInformation->NameLength); DriverName->Buffer = ExAllocatePool( NonPagedPool, DriverName->MaximumLength ); if (!DriverName->Buffer) { ExFreePool( keyBasicInformation ); ExFreePool( keyValueInformation ); return STATUS_INSUFFICIENT_RESOURCES; } // // Now form the name of the object to be opened. // DriverName->Length = 0; RtlAppendUnicodeToString( DriverName, baseObjectName ); remainderName.Length = (USHORT) keyBasicInformation->NameLength; remainderName.MaximumLength = remainderName.Length; remainderName.Buffer = &keyBasicInformation->Name[0]; RtlAppendUnicodeStringToString( DriverName, &remainderName ); ExFreePool( keyBasicInformation ); ExFreePool( keyValueInformation ); } // // Finally, simply return to the caller with the name filled in. Note // that the caller must free the buffer pointed to by the Buffer field // of the Unicode string descriptor. // return STATUS_SUCCESS; } NTSTATUS IopGetFileName( IN PFILE_OBJECT FileObject, IN ULONG Length, OUT PVOID FileInformation, OUT PULONG ReturnedLength ) /*++ Routine Description: This routine is invoked to asynchronously obtain the name of a file object when the file was opened for synchronous I/O, and the previous mode of the caller was kernel mode, and the query was done through the Object Manager. In this case, the situation is likely that the Lazy Writer has incurred a write error, and it is attempting to obtain the name of the file so that it can output a popup. In doing so, a deadlock can occur because another thread has locked the file object synchronous I/O lock. Hence, this routine obtains the name of the file w/o acquiring that lock. Arguments: FileObject - A pointer to the file object whose name is to be queried. Length - Supplies the length of the buffer to receive the name. FileInformation - A pointer to the buffer to receive the name. ReturnedLength - A variable to receive the length of the name returned. Return Value: The status returned is the final completion status of the operation. --*/ { PIRP irp; NTSTATUS status; PDEVICE_OBJECT deviceObject; KEVENT event; PIO_STACK_LOCATION irpSp; IO_STATUS_BLOCK localIoStatus; PAGED_CODE(); // // Reference the file object here so that no special checks need be made // in I/O completion to determine whether or not to dereference the file // object. // ObReferenceObject( FileObject ); // // Initialize an event that will be used to synchronize the completion of // the query operation. Note that this is the only way to synchronize this // since the file object itself cannot be used since it was opened for // synchronous I/O and may be busy. // KeInitializeEvent( &event, SynchronizationEvent, FALSE ); // // Get the address of the target device object. // deviceObject = IoGetRelatedDeviceObject( FileObject ); // // Allocate and initialize the I/O Request Packet (IRP) for this operation. // irp = IoAllocateIrp( deviceObject->StackSize, FALSE ); if (!irp) { // // An IRP could not be allocated. Cleanup and return an appropriate // error status code. // IopAllocateIrpCleanup( FileObject, (PKEVENT) NULL ); return STATUS_INSUFFICIENT_RESOURCES; } irp->Tail.Overlay.OriginalFileObject = FileObject; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->RequestorMode = KernelMode; // // Fill in the service independent parameters in the IRP. Note that the // setting of the special query name flag in the packet guarantees that the // standard completion for a synchronous file object will not occur because // this flag communicates to the I/O completion that it should not do so. // irp->UserEvent = &event; irp->Flags = IRP_SYNCHRONOUS_API | IRP_OB_QUERY_NAME; irp->UserIosb = &localIoStatus; irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL; // // Get a pointer to the stack location for the first driver. This will be // used to pass the original function codes and parameters. // irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = IRP_MJ_QUERY_INFORMATION; irpSp->FileObject = FileObject; // // Set the system buffer address to the address of the caller's buffer and // set the flags so that the buffer is not deallocated. // irp->AssociatedIrp.SystemBuffer = FileInformation; irp->Flags |= IRP_BUFFERED_IO; // // Copy the caller's parameters to the service-specific portion of the // IRP. // irpSp->Parameters.QueryFile.Length = Length; irpSp->Parameters.QueryFile.FileInformationClass = FileNameInformation; // // Insert the packet at the head of the IRP list for the thread. // IopQueueThreadIrp( irp ); // // Now simply invoke the driver at its dispatch entry with the IRP. // status = IoCallDriver( deviceObject, irp ); // // Now get the final status of the operation once the request completes // and return the length of the buffer written. // if (status == STATUS_PENDING) { (VOID) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); status = localIoStatus.Status; } *ReturnedLength = localIoStatus.Information; return status; } BOOLEAN IopGetMountFlag( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine is invoked to determine whether or not the specified device is mounted. Arguments: DeviceObject - Supplies a pointer to the device object for which the mount flag is tested. Return Value: The function value is TRUE if the specified device is mounted, otherwise FALSE. --*/ { KIRQL irql; BOOLEAN deviceMounted = FALSE; // // Check to see whether or not the device is mounted. Note that the caller // has probably already looked to see whether or not the device has a VPB // outside of owning the lock, so simply get the lock and check it again // to start with, rather than checking to see whether or not the device // still has a VPB without holding the lock. // ExAcquireFastLock( &IopVpbSpinLock, &irql ); if (DeviceObject->Vpb) { if (DeviceObject->Vpb->Flags & VPB_MOUNTED) { deviceMounted = TRUE; } } ExReleaseFastLock( &IopVpbSpinLock, irql ); return deviceMounted; } NTSTATUS IopGetRegistryKeyInformation( IN HANDLE KeyHandle, OUT PKEY_FULL_INFORMATION *Information ) /*++ Routine Description: This routine is invoked to retrieve the full key information for a registry key. This is done by querying the full key information of the key with a zero-length buffer to determine the size of the data, and then allocating a buffer and actually querying the data into the buffer. It is the responsibility of the caller to free the buffer. Arguments: KeyHandle - Supplies the key handle whose full key information is to be queried Information - Returns a pointer to the allocated data buffer. Return Value: The function value is the final status of the query operation. --*/ { NTSTATUS status; PKEY_FULL_INFORMATION infoBuffer; ULONG keyInfoLength; PAGED_CODE(); // // Figure out how big the data value is so that a buffer of the // appropriate size can be allocated. // status = ZwQueryKey( KeyHandle, KeyFullInformation, (PVOID) NULL, 0, &keyInfoLength ); if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { return status; } // // Allocate a buffer large enough to contain the entire key data. // infoBuffer = ExAllocatePool( NonPagedPool, keyInfoLength ); if (!infoBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } // // Query the full key data for the key. // status = ZwQueryKey( KeyHandle, KeyFullInformation, infoBuffer, keyInfoLength, &keyInfoLength ); if (!NT_SUCCESS( status )) { ExFreePool( infoBuffer ); return status; } // // Everything worked, so simply return the address of the allocated // buffer to the caller, who is now responsible for freeing it. // *Information = infoBuffer; return STATUS_SUCCESS; } NTSTATUS IopGetRegistryValue( IN HANDLE KeyHandle, IN PWSTR ValueName, OUT PKEY_VALUE_FULL_INFORMATION *Information ) /*++ Routine Description: This routine is invoked to retrieve the data for a registry key's value. This is done by querying the value of the key with a zero-length buffer to determine the size of the value, and then allocating a buffer and actually querying the value into the buffer. It is the responsibility of the caller to free the buffer. Arguments: KeyHandle - Supplies the key handle whose value is to be queried ValueName - Supplies the null-terminated Unicode name of the value. Information - Returns a pointer to the allocated data buffer. Return Value: The function value is the final status of the query operation. --*/ { UNICODE_STRING unicodeString; NTSTATUS status; PKEY_VALUE_FULL_INFORMATION infoBuffer; ULONG keyValueLength; PAGED_CODE(); RtlInitUnicodeString( &unicodeString, ValueName ); // // Figure out how big the data value is so that a buffer of the // appropriate size can be allocated. // status = ZwQueryValueKey( KeyHandle, &unicodeString, KeyValueFullInformation, (PVOID) NULL, 0, &keyValueLength ); if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { return status; } // // Allocate a buffer large enough to contain the entire key data value. // infoBuffer = ExAllocatePool( NonPagedPool, keyValueLength ); if (!infoBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } // // Query the data for the key value. // status = ZwQueryValueKey( KeyHandle, &unicodeString, KeyValueFullInformation, infoBuffer, keyValueLength, &keyValueLength ); if (!NT_SUCCESS( status )) { ExFreePool( infoBuffer ); return status; } // // Everything worked, so simply return the address of the allocated // buffer to the caller, who is now responsible for freeing it. // *Information = infoBuffer; return STATUS_SUCCESS; } NTSTATUS IopGetRegistryValues( IN HANDLE KeyHandle, IN PKEY_VALUE_FULL_INFORMATION *ValueList ) /*++ Routine Description: This routine is invoked to retrieve the *three* types of data for a registry key's. This is done by calling the IopGetRegistryValue function with the three valid key names. It is the responsibility of the caller to free the three buffers. Arguments: KeyHandle - Supplies the key handle whose value is to be queried ValueList - Pointer to a buffer in which the three pointers to the value entries will be stored. Return Value: The function value is the final status of the query operation. Note: The values are stored in the order represented by the I/O query device data format. --*/ { NTSTATUS status; PAGED_CODE(); // // Zero out all entries initially. // *ValueList = NULL; *(ValueList + 1) = NULL; *(ValueList + 2) = NULL; // // Get the information for each of the three types of entries available. // Each time, check if an internal error occured; If the object name was // not found, it only means not data was present, and this does not // constitute an error. // status = IopGetRegistryValue( KeyHandle, L"Identifier", ValueList ); if (!NT_SUCCESS( status ) && (status != STATUS_OBJECT_NAME_NOT_FOUND)) { return status; } status = IopGetRegistryValue( KeyHandle, L"Configuration Data", ++ValueList ); if (!NT_SUCCESS( status ) && (status != STATUS_OBJECT_NAME_NOT_FOUND)) { return status; } status = IopGetRegistryValue( KeyHandle, L"Component Information", ++ValueList ); if (!NT_SUCCESS( status ) && (status != STATUS_OBJECT_NAME_NOT_FOUND)) { return status; } return STATUS_SUCCESS; } VOID IopHardErrorThread( IN PVOID StartContext ) /*++ Routine Description: This function waits for work on the IopHardErrorQueue, and all calls IopRaiseInformationalHardError to actually perform the pop-ups. Arguments: StartContext - Startup context; not used. Return Value: None. --*/ { KIRQL oldIrql; PVOID entry; ULONG parameterPresent; ULONG errorParameter; ULONG errorResponse; BOOLEAN MoreEntries; PIOP_HARD_ERROR_PACKET hardErrorPacket; UNREFERENCED_PARAMETER( StartContext ); // // Loop, waiting forever for a hard error packet to be sent to this thread. // When one is placed onto the queue, wake up, process it, and continue // the loop. // MoreEntries = TRUE; do { (VOID) KeWaitForSingleObject( &IopHardError.WorkQueueSemaphore, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); ExAcquireFastLock( &IopHardError.WorkQueueSpinLock, &oldIrql ); // // The work queue structures are now exclusively owned, so remove the // first packet from the head of the list. // entry = RemoveHeadList( &IopHardError.WorkQueue ); hardErrorPacket = CONTAINING_RECORD( entry, IOP_HARD_ERROR_PACKET, WorkQueueLinks ); IopCurrentHardError = hardErrorPacket; ExReleaseFastLock( &IopHardError.WorkQueueSpinLock, oldIrql ); // // Simply raise the hard error if the system is ready to accept one. // errorParameter = (ULONG) &hardErrorPacket->String; parameterPresent = (hardErrorPacket->String.Buffer != NULL); if (ExReadyForErrors) { (VOID) ExRaiseHardError( hardErrorPacket->ErrorStatus, parameterPresent, parameterPresent, parameterPresent ? &errorParameter : NULL, OptionOk, &errorResponse ); } // // If this was the last entry, exit the thread and mark it as so. // ExAcquireFastLock( &IopHardError.WorkQueueSpinLock, &oldIrql ); IopCurrentHardError = NULL; if ( IsListEmpty( &IopHardError.WorkQueue ) ) { IopHardError.ThreadStarted = FALSE; MoreEntries = FALSE; } ExReleaseFastLock( &IopHardError.WorkQueueSpinLock, oldIrql ); // // Now free the packet and the buffer, if one was specified. // if (hardErrorPacket->String.Buffer) { ExFreePool( hardErrorPacket->String.Buffer ); } ExFreePool( hardErrorPacket ); } while ( MoreEntries ); } NTSTATUS IopInvalidDeviceRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function is the default dispatch routine for all driver entries not implemented by drivers that have been loaded into the system. Its responsibility is simply to set the status in the packet to indicate that the operation requested is invalid for this device type, and then complete the packet. Arguments: DeviceObject - Specifies the device object for which this request is bound. Ignored by this routine. Irp - Specifies the address of the I/O Request Packet (IRP) for this request. Return Value: The final status is always STATUS_INVALID_DEVICE_REQUEST. --*/ { UNREFERENCED_PARAMETER( DeviceObject ); // // Simply store the appropriate status, complete the request, and return // the same status stored in the packet. // Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS IopLoadDriver( IN HANDLE KeyHandle ) /*++ Routine Description: This routine is invoked to load a device or file system driver, either during system initialization, or dynamically while the system is running. Arguments: KeyHandle - Supplies a handle to the driver service node in the registry that describes the driver to be loaded. Return Value: The function value is the final status of the load operation. Notes: Note that this routine closes the KeyHandle before returning. --*/ { NTSTATUS status; PLIST_ENTRY nextEntry; PLDR_DATA_TABLE_ENTRY driverEntry; PKEY_BASIC_INFORMATION keyBasicInformation = NULL; PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL; ULONG keyBasicLength; UNICODE_STRING baseName; UNICODE_STRING serviceName = {0, 0, NULL}; OBJECT_ATTRIBUTES objectAttributes; PVOID sectionPointer; UNICODE_STRING driverName; PDRIVER_OBJECT driverObject; PIMAGE_NT_HEADERS ntHeaders; PVOID imageBaseAddress; ULONG entryPoint; HANDLE driverHandle; ULONG i; POBJECT_NAME_INFORMATION registryPath; #if DBG LARGE_INTEGER stime, etime; ULONG dtime; #endif PAGED_CODE(); driverName.Buffer = (PWSTR) NULL; // // Begin by formulating the name of the driver image file to be loaded. // Note that this is used to determine whether or not the driver has // already been loaded by the OS loader, not necessarily in actually // loading the driver image, since the node can override that name. // status = NtQueryKey( KeyHandle, KeyBasicInformation, (PVOID) NULL, 0, &keyBasicLength ); if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { status = STATUS_ILL_FORMED_SERVICE_ENTRY; goto IopLoadExit; } keyBasicInformation = ExAllocatePool( NonPagedPool, keyBasicLength + (4 * 2) ); if (!keyBasicInformation) { status = STATUS_INSUFFICIENT_RESOURCES; goto IopLoadExit; } status = NtQueryKey( KeyHandle, KeyBasicInformation, keyBasicInformation, keyBasicLength, &keyBasicLength ); if (!NT_SUCCESS( status )) { goto IopLoadExit; } // // Create a Unicode string descriptor which forms the name of the // driver. // baseName.Length = (USHORT) keyBasicInformation->NameLength; baseName.MaximumLength = (USHORT) (baseName.Length + (4 * 2)); baseName.Buffer = &keyBasicInformation->Name[0]; //#if _PNP_POWER_ serviceName.Buffer = ExAllocatePool(PagedPool, baseName.Length + sizeof(UNICODE_NULL)); if (serviceName.Buffer) { serviceName.Length = baseName.Length; serviceName.MaximumLength = serviceName.Length + sizeof(UNICODE_NULL); RtlMoveMemory(serviceName.Buffer, baseName.Buffer, baseName.Length); serviceName.Buffer[serviceName.Length / sizeof(WCHAR)] = UNICODE_NULL; } #if DBG else { DbgPrint("IopLoadDriver: No memory available for Service Keyname\n"); } #endif //#endif RtlAppendUnicodeToString( &baseName, L".SYS" ); // // See if this driver has already been loaded by the boot loader. // //KeEnterCriticalRegion(); ExAcquireResourceShared( &PsLoadedModuleResource, TRUE ); nextEntry = PsLoadedModuleList.Flink; while (nextEntry != &PsLoadedModuleList) { // // Look at the next boot driver in the list. // driverEntry = CONTAINING_RECORD( nextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); // // If this is not the kernel image (ntoskrnl) and not the HAL (hal), // then this is a driver, so initialize it. // if ((driverEntry->Flags & LDRP_ENTRY_PROCESSED) && RtlEqualString( (PSTRING) &baseName, (PSTRING) &driverEntry->FullDllName, TRUE )) { status = STATUS_IMAGE_ALREADY_LOADED; ExReleaseResource( &PsLoadedModuleResource ); //KeLeaveCriticalRegion(); goto IopLoadExit; } nextEntry = nextEntry->Flink; } ExReleaseResource( &PsLoadedModuleResource ); //KeLeaveCriticalRegion(); //#if _PNP_POWER_ // // First check should this driver be loaded. If yes, the enum subkey // of the service will be prepared. // status = IopPrepareDriverLoading (&serviceName, KeyHandle); if (!NT_SUCCESS(status)) { goto IopLoadExit; } //#endif // // This driver has not already been loaded by the OS loader. Form the // full path name for this driver. Begin by attempting to determine // whether or not the file has an image path. If so, then use that, // otherwise, form one from the above driver name by putting the // appropriate path name in front of it. // status = IopGetRegistryValue( KeyHandle, L"ImagePath", &keyValueInformation ); if (NT_SUCCESS( status ) && keyValueInformation->DataLength) { // // The driver service node contained an image path name from which // the driver is to be loaded. // ExFreePool( keyBasicInformation ); keyBasicInformation = NULL; baseName.Length = (USHORT) keyValueInformation->DataLength; if (baseName.Length > 0) { baseName.Length -= sizeof( WCHAR ); } baseName.MaximumLength = baseName.Length; baseName.Buffer = (PWSTR) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset); if (baseName.Buffer[0] != L'\\') { UNICODE_STRING prefixName; UNICODE_STRING tmpName; PWCHAR fileName; RtlInitUnicodeString( &prefixName, L"\\SystemRoot\\" ); fileName = ExAllocatePool( NonPagedPool, prefixName.Length + baseName.Length ); if (!fileName) { status = STATUS_INSUFFICIENT_RESOURCES; goto IopLoadExit; } tmpName.Length = baseName.Length; tmpName.Buffer = baseName.Buffer; baseName.MaximumLength = (USHORT) (prefixName.Length + baseName.Length); baseName.Length = 0; baseName.Buffer = fileName; RtlAppendUnicodeStringToString( &baseName, &prefixName ); RtlAppendUnicodeStringToString( &baseName, &tmpName ); ExFreePool( keyValueInformation ); keyValueInformation = (PKEY_VALUE_FULL_INFORMATION) fileName; } } else { UNICODE_STRING prefixName; UNICODE_STRING fileName; RtlInitUnicodeString( &prefixName, L"\\SystemRoot\\System32\\Drivers\\" ); // // Ensure that the driver entry did not actually contain an image path // name, and if it did, free the appropriate pool because it was a key // without a value. // if (NT_SUCCESS( status )) { ExFreePool( keyValueInformation ); } // // The driver entry did not contain an image path name, so the above // default name for the driver image is name of the file. Form a // fully qualified path to get to the image file. // keyValueInformation = ExAllocatePool( NonPagedPool, baseName.MaximumLength + prefixName.Length ); if (!keyValueInformation) { status = STATUS_INSUFFICIENT_RESOURCES; goto IopLoadExit; } fileName.Length = baseName.Length; fileName.MaximumLength = baseName.MaximumLength; fileName.Buffer = baseName.Buffer; baseName.Length = 0; baseName.MaximumLength = (USHORT) (fileName.Length + prefixName.Length); baseName.Buffer = (PWSTR) keyValueInformation; RtlAppendUnicodeStringToString( &baseName, &prefixName ); RtlAppendUnicodeStringToString( &baseName, &fileName ); ExFreePool( keyBasicInformation ); keyBasicInformation = NULL; } // // Now get the name of the driver object. // status = IopGetDriverNameFromKeyNode( KeyHandle, &driverName ); if (!NT_SUCCESS( status )) { goto IopLoadExit; } // // Load the driver image into memory. If this fails partway through // the operation, then it will automatically be unloaded. // status = MmLoadSystemImage( &baseName, §ionPointer, (PVOID *) &imageBaseAddress ); if (!NT_SUCCESS( status )) { goto IopLoadExit; } // // The driver image has now been loaded into memory. Create the driver // object that represents this image. // InitializeObjectAttributes( &objectAttributes, &driverName, OBJ_PERMANENT, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = ObCreateObject( KeGetPreviousMode(), IoDriverObjectType, &objectAttributes, KernelMode, (PVOID) NULL, //#if _PNP_POWER_ (ULONG) (sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION )), //#else #if 0 (ULONG) sizeof( DRIVER_OBJECT ), #endif 0, 0, (PVOID *) &driverObject ); if (!NT_SUCCESS( status )) { MmUnloadSystemImage( sectionPointer ); goto IopLoadExit; } // // Initialize this driver object and insert it into the object table. // //#if _PNP_POWER_ RtlZeroMemory( driverObject, sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION) ); driverObject->DriverExtension = (PDRIVER_EXTENSION) (driverObject + 1); driverObject->DriverExtension->DriverObject = driverObject; //#else #if 0 RtlZeroMemory( driverObject, sizeof( DRIVER_OBJECT ) ); #endif for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { driverObject->MajorFunction[i] = IopInvalidDeviceRequest; } driverObject->Type = IO_TYPE_DRIVER; driverObject->Size = sizeof( DRIVER_OBJECT ); ntHeaders = RtlImageNtHeader( imageBaseAddress ); entryPoint = ntHeaders->OptionalHeader.AddressOfEntryPoint; entryPoint += (ULONG) imageBaseAddress; driverObject->DriverInit = (PDRIVER_INITIALIZE) entryPoint; driverObject->DriverSection = sectionPointer; driverObject->DriverStart = imageBaseAddress; driverObject->DriverSize = ntHeaders->OptionalHeader.SizeOfImage; status = ObInsertObject( driverObject, (PACCESS_STATE) NULL, FILE_READ_DATA, 0, (PVOID *) NULL, &driverHandle ); if (!NT_SUCCESS( status )) { MmUnloadSystemImage( sectionPointer ); goto IopLoadExit; } // // Reference the handle and obtain a pointer to the driver object so that // the handle can be deleted without the object going away. // status = ObReferenceObjectByHandle( driverHandle, 0, IoDriverObjectType, KeGetPreviousMode(), (PVOID *) &driverObject, (POBJECT_HANDLE_INFORMATION) NULL ); NtClose( driverHandle ); // // Load the Regsitry information in the appropriate fields of the device // object. // driverObject->HardwareDatabase = &CmRegistryMachineHardwareDescriptionSystemName; // // Store the name of the device driver in the driver object so that it // can be easily found by the error log thread. // driverObject->DriverName.Buffer = ExAllocatePool( PagedPool, driverName.MaximumLength ); if (driverObject->DriverName.Buffer) { driverObject->DriverName.MaximumLength = driverName.MaximumLength; driverObject->DriverName.Length = driverName.Length; RtlCopyMemory( driverObject->DriverName.Buffer, driverName.Buffer, driverName.MaximumLength ); } // // Query the name of the registry path for this driver so that it can // be passed to the driver. // registryPath = ExAllocatePool( NonPagedPool, PAGE_SIZE ); if (!registryPath) { MmUnloadSystemImage( driverObject->DriverSection ); ObMakeTemporaryObject( driverObject ); ObDereferenceObject( driverObject ); status = STATUS_INSUFFICIENT_RESOURCES; goto IopLoadExit; } status = NtQueryObject( KeyHandle, ObjectNameInformation, registryPath, PAGE_SIZE, &i ); if (!NT_SUCCESS( status )) { MmUnloadSystemImage( driverObject->DriverSection ); ObMakeTemporaryObject( driverObject ); ObDereferenceObject( driverObject ); ExFreePool( registryPath ); goto IopLoadExit; } #if DBG KeQuerySystemTime (&stime); #endif // // Store the service key name of the device driver in the driver object // if (serviceName.Buffer) { driverObject->DriverExtension->ServiceKeyName.Buffer = ExAllocatePool( NonPagedPool, serviceName.MaximumLength ); if (driverObject->DriverExtension->ServiceKeyName.Buffer) { driverObject->DriverExtension->ServiceKeyName.MaximumLength = serviceName.MaximumLength; driverObject->DriverExtension->ServiceKeyName.Length = serviceName.Length; RtlCopyMemory( driverObject->DriverExtension->ServiceKeyName.Buffer, serviceName.Buffer, serviceName.MaximumLength ); } } // // Now invoke the driver's initialization routine to initialize itself. // status = driverObject->DriverInit( driverObject, ®istryPath->Name ); #if DBG // // If DriverInit took longer than 5 seconds, print a message. // KeQuerySystemTime (&etime); dtime = (ULONG) ((etime.QuadPart - stime.QuadPart) / 1000000); if (dtime > 50) { DbgPrint( "IOLOAD: Driver %wZ took %d.%ds to %s\n", &driverName, dtime/10, dtime%10, NT_SUCCESS(status) ? "initialize" : "fail initialization" ); } #endif // // If DriverInit doesn't work, then simply unload the image and mark the driver // object as temporary. This will cause everything to be deleted. // ExFreePool( registryPath ); if (!NT_SUCCESS( status )) { MmUnloadSystemImage( driverObject->DriverSection ); ObMakeTemporaryObject( driverObject ); ObDereferenceObject( driverObject ); } else { // // Free the memory occuppied by the driver's initialization routines. // MmFreeDriverInitialization( driverObject->DriverSection ); IopReadyDeviceObjects( driverObject ); } IopLoadExit: // // Free any pool that was allocated by this routine that has not yet // been freed. // if (driverName.Buffer != NULL) { ExFreePool( driverName.Buffer ); } if (keyValueInformation != NULL) { ExFreePool( keyValueInformation ); } if (keyBasicInformation != NULL) { ExFreePool( keyBasicInformation ); } if (serviceName.Buffer != NULL) { ExFreePool(serviceName.Buffer); } // // If this routine is about to return a failure, then let the Configuration // Manager know about it. But, if STATUS_PLUGPLAY_NO_DEVICE, the device was // disabled by hardware profile. In this case we don't need to report it. // if (!NT_SUCCESS( status ) && (status != STATUS_PLUGPLAY_NO_DEVICE)) { NTSTATUS lStatus; PULONG errorControl; if (status != STATUS_IMAGE_ALREADY_LOADED) { // // If driver was loaded, do not call IopDriverLoadingFailed to change // the driver loading status. Because, obviously, the driver is // running. // IopDriverLoadingFailed(KeyHandle, NULL); } lStatus = IopGetRegistryValue( KeyHandle, L"ErrorControl", &keyValueInformation ); if (!NT_SUCCESS( lStatus ) || !keyValueInformation->DataLength) { if (NT_SUCCESS( lStatus )) { ExFreePool( keyValueInformation ); } } else { errorControl = (PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset); CmBootLastKnownGood( *errorControl ); ExFreePool( keyValueInformation ); } } // // Close the caller's handle and return the final status from the load // operation. // NtClose( KeyHandle ); return status; } VOID IopDecrementDeviceObjectRef( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN AlwaysUnload ) /*++ Routine Description: The routine decrements the reference count on a device object. If the reference count goes to zero and the device object is canidate for deletion then IopCompleteUnloadOrDelete is called. A device object is subject for deletion if the AlwayUnload flag is true, or the device object is pending deletion or the driver is pending unload. Arguments: DeviceObject - Supplies the device object whos reference count is to be decremented. AlwaysUnload - Indicates if the driver should be unloaded regardless of the state of the unload flag. Return Value: None. --*/ { KIRQL irql; // // Decrement the reference count on the device object. If this is the last // last reason that this mini-file system recognizer needs to stay around, // then unload it. // ExAcquireSpinLock( &IopDatabaseLock, &irql ); ASSERT( DeviceObject->ReferenceCount > 0 ); DeviceObject->ReferenceCount--; if (!DeviceObject->ReferenceCount && (AlwaysUnload || DeviceObject->DeviceObjectExtension->ExtensionFlags & (DOE_DELETE_PENDING | DOE_UNLOAD_PENDING))) { IopCompleteUnloadOrDelete( DeviceObject, irql ); } else { ExReleaseSpinLock( &IopDatabaseLock, irql ); } } VOID IopLoadFileSystemDriver( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine is invoked when a mini-file system recognizer driver recognizes a volume as being a particular file system, but the driver for that file system has not yet been loaded. This function allows the mini-driver to load the real file system, and remove itself from the system, so that the real file system can mount the device in question. Arguments: DeviceObject - Registered file system device object for the mini-driver. Return Value: None. --*/ { KEVENT event; NTSTATUS status; IO_STATUS_BLOCK ioStatus; PIRP irp; PIO_STACK_LOCATION irpSp; PAGED_CODE(); // // Begin by building an I/O Request Packet to have the mini-file system // driver load the real file system. // KeInitializeEvent( &event, NotificationEvent, FALSE ); irp = IoBuildDeviceIoControlRequest( IRP_MJ_DEVICE_CONTROL, DeviceObject, (PVOID) NULL, 0, (PVOID) NULL, 0, FALSE, &event, &ioStatus ); if (irp) { // // Change the actual major and minor function codes to be a file system // control with a minor function code of load FS driver. // irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; irpSp->MinorFunction = IRP_MN_LOAD_FILE_SYSTEM; // // Now issue the request. // status = IoCallDriver( DeviceObject, irp ); if (status == STATUS_PENDING) { (VOID) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); } } // // Decrement the reference count on the device object. If this is the last // last reason that this mini-file system recognizer needs to stay around, // then unload it. // IopDecrementDeviceObjectRef(DeviceObject, TRUE); return; } VOID IopLoadUnloadDriver( IN PVOID Parameter ) /*++ Routine Description: This routine is executed as an EX worker thread routine when a driver is to be loaded or unloaded dynamically. It is used because some drivers need to create system threads in the context of the system process, which cannot be done in the context of the caller of the system service that was invoked to load or unload the specified driver. Arguments: Parameter - Pointer to the load packet describing what work is to be done. Return Value: None. --*/ { PLOAD_PACKET loadPacket; NTSTATUS status; HANDLE keyHandle; PAGED_CODE(); // // Begin by getting a pointer to the load packet. // loadPacket = (PLOAD_PACKET) Parameter; // // If the driver object field of the packet is non-NULL, then this is // a request to complete the unload of a driver. Simply invoke the // driver's unload routine. Note that the final status of the unload // is ignored, so it is not set here. // if (loadPacket->DriverObject) { loadPacket->DriverObject->DriverUnload( loadPacket->DriverObject ); status = STATUS_SUCCESS; } else { PLIST_ENTRY entry; PREINIT_PACKET reinitEntry; // // The driver specified by the DriverServiceName is to be loaded. // Begin by opening the registry node for this driver. Note // that if this is successful, then the load driver routine is // responsible for closing the handle. // status = IopOpenRegistryKey( &keyHandle, (HANDLE) NULL, loadPacket->DriverServiceName, KEY_READ, FALSE ); if (NT_SUCCESS( status )) { // // Invoke the internal common routine to perform the work. // This is the same routine that is used by the I/O system // initialization code to load drivers. // status = IopLoadDriver( keyHandle ); // // Walk the list reinitialization list in case this driver, or // some other driver, has requested to be invoked at a re- // initialization entry point. // while (entry = ExInterlockedRemoveHeadList( &IopDriverReinitializeQueueHead, &IopDatabaseLock )) { reinitEntry = CONTAINING_RECORD( entry, REINIT_PACKET, ListEntry ); //#if _PNP_POWER_ reinitEntry->DriverObject->DriverExtension->Count++; reinitEntry->DriverReinitializationRoutine( reinitEntry->DriverObject, reinitEntry->Context, reinitEntry->DriverObject->DriverExtension->Count ); //#else #if 0 reinitEntry->DriverObject->Count++; reinitEntry->DriverReinitializationRoutine( reinitEntry->DriverObject, reinitEntry->Context, reinitEntry->DriverObject->Count ); #endif // _PNP_POWER_ ExFreePool( reinitEntry ); } } } // // Set the final status of the load or unload operation, and indicate to // the caller that the operation is now complete. // loadPacket->FinalStatus = status; (VOID) KeSetEvent( &loadPacket->Event, 0, FALSE ); } NTSTATUS IopMountVolume( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN AllowRawMount, IN BOOLEAN DeviceLockAlreadyHeld ) /*++ Routine Description: This routine is used to mount a volume on the specified device. The Volume Parameter Block (VPB) for the specified device is a "clean" VPB. That is, it indicates that the volume has never been mounted. It is up to the file system that eventually mounts the volume to determine whether the volume is, or has been, mounted elsewhere. Arguments: DeviceObject - Pointer to device object on which the volume is to be mounted. AllowRawMount - This parameter tells us if we should continue our filesystem search to include the Raw file system. This flag will only be passed in as TRUE as a result of a DASD open. DeviceLockAlreadyHeld - If TRUE, then the caller has already acquired the device lock and we should not attempt to acquire it. This is currently passed in as TRUE when called from IoVerifyVolume. Return Value: The function value is a successful status code if a volume was successfully mounted on the device. Otherwise, an error code is returned. --*/ { NTSTATUS status; KEVENT event; PIRP irp; PDEVICE_OBJECT fsDeviceObject; PDEVICE_OBJECT attachedDevice; PLIST_ENTRY entry; PLIST_ENTRY queueHeader; IO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION irpSp; ULONG extraStack; LIST_ENTRY dummy; PAGED_CODE(); // // Begin by acquiring the resource database lock for the I/O system to // perform this operation. This resource protects access to the file system // queue. // //KeEnterCriticalRegion(); (VOID) ExAcquireResourceShared( &IopDatabaseResource, TRUE ); // // Now obtain the lock for the device to be mounted. This guarantees that // only one thread is attempting to mount this particular device at a time. // if (!DeviceLockAlreadyHeld) { status = KeWaitForSingleObject( &DeviceObject->DeviceLock, Executive, KeGetPreviousMode(), TRUE, (PLARGE_INTEGER) NULL ); // // If the wait ended because of an alert or an APC, release the resource // and return now without mounting the device. Note that as the wait // for the event was unsuccessfull, we do not set it on exit. // if (status == STATUS_ALERTED || status == STATUS_USER_APC) { ExReleaseResource( &IopDatabaseResource ); //KeLeaveCriticalRegion(); return status; } } // // Check the 'mounted' flag of the VPB to ensure that it is still clear. // If it is, then no one has gotten in before this to mount the volume. // Attempt to mount the volume in this case. // if (!(DeviceObject->Vpb->Flags & VPB_MOUNTED)) { // // This volume has never been mounted. Initialize the event and set the // status to unsuccessful to set up for the loop. Also if the device // has the verify bit set, clear it. // KeInitializeEvent( &event, NotificationEvent, FALSE ); status = STATUS_UNSUCCESSFUL; DeviceObject->Flags &= ~DO_VERIFY_VOLUME; // // Get the actual device that this volume is to be mounted on. This // device is the final device in the list of devices which are attached // to the specified real device. // attachedDevice = DeviceObject; while (attachedDevice->AttachedDevice) { attachedDevice = attachedDevice->AttachedDevice; } // // Determine which type of file system should be invoked based on // the device type of the device being mounted. // if (DeviceObject->DeviceType == FILE_DEVICE_DISK || DeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK) { queueHeader = &IopDiskFileSystemQueueHead; } else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) { queueHeader = &IopCdRomFileSystemQueueHead; } else { queueHeader = &IopTapeFileSystemQueueHead; } // // Now loop through each of the file systems which have been loaded in // the system to see whether anyone understands the media in the device. // for (entry = queueHeader->Flink; entry != queueHeader && !NT_SUCCESS( status ); entry = entry->Flink) { // // If this is the final entry (Raw file system), and it is also // not the first entry, and a raw mount is not permitted, then // break out of the loop at this point, as this volume cannot // be mounted for the caller's purposes. // if (!AllowRawMount && entry->Flink == queueHeader && entry != queueHeader->Flink) { break; } fsDeviceObject = CONTAINING_RECORD( entry, DEVICE_OBJECT, Queue.ListEntry ); // // It is possible that the file system has been attached to, so // walk the attached list for the file system. The number of stack // locations that must be allocated in the IRP must include one for // the file system itself, and then one for each driver that is // attached to it. Account for all of the stack locations required // to get through the mount process. // extraStack = 1; while (fsDeviceObject->AttachedDevice) { fsDeviceObject = fsDeviceObject->AttachedDevice; extraStack++; } // // Another file system has been found and the volume has still not // been mounted. Attempt to mount the volume using this file // system. // // Begin by resetting the event being used for synchronization with // the I/O operation. // KeClearEvent( &event ); // // Allocate and initialize an IRP for this mount operation. Notice // that the flags for this operation appear the same as a page read // operation. This is because the completion code for both of the // operations is exactly the same logic. // irp = IoAllocateIrp( (CCHAR) (attachedDevice->StackSize + extraStack), FALSE ); if (!irp) { irp = IopAllocateIrpMustSucceed( (CCHAR) (attachedDevice->StackSize + 1) ); } irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO; irp->RequestorMode = KernelMode; irp->UserEvent = &event; irp->UserIosb = &ioStatus; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; irpSp->MinorFunction = IRP_MN_MOUNT_VOLUME; irpSp->Flags = AllowRawMount; irpSp->Parameters.MountVolume.Vpb = DeviceObject->Vpb; irpSp->Parameters.MountVolume.DeviceObject = attachedDevice; status = IoCallDriver( fsDeviceObject, irp ); // // Wait for the I/O operation to complete. // if (NT_SUCCESS( status )) { (VOID) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); } else { // // Ensure that the proper status value gets picked up. // ioStatus.Status = status; ioStatus.Information = 0; } // // If the operation was successful then set the VPB as mounted. // if (NT_SUCCESS( ioStatus.Status )) { status = ioStatus.Status; DeviceObject->Vpb->Flags = VPB_MOUNTED; DeviceObject->Vpb->DeviceObject->StackSize = (UCHAR) (attachedDevice->StackSize + 1); } else { // // The mount operation failed. Make a special check here to // determine whether or not a popup was enabled, and if so, // check to see whether or not the operation was to be aborted. // If so, bail out now and return the error to the caller. // status = ioStatus.Status; if (IoIsErrorUserInduced(status) && ioStatus.Information == IOP_ABORT) { break; } // // Also check to see whether or not this is a volume that has // been recognized, but the file system for it needs to be // loaded. If so, drop the locks held at this point, tell the // mini-file system recognizer to load the driver, and then // reacquire the locks. // if (status == STATUS_FS_DRIVER_REQUIRED) { // // Increment the number of reasons that this driver cannot // be unloaded. Note that this must be done while still // holding the database resource. // ExInterlockedAddUlong( &fsDeviceObject->ReferenceCount, 1, &IopDatabaseLock ); // // Release the locks, load the new file system, and unload // the recognizer. // if (!DeviceLockAlreadyHeld) { KeSetEvent( &DeviceObject->DeviceLock, 0, FALSE ); } ExReleaseResource( &IopDatabaseResource ); //KeLeaveCriticalRegion(); IopLoadFileSystemDriver( fsDeviceObject ); // // Now reacquire the locks, in the correct order, and check // to see if the volume has been mounted before we could // get back. If so, exit; otherwise, restart the file // file system queue scan from the beginning. // //KeEnterCriticalRegion(); (VOID) ExAcquireResourceShared( &IopDatabaseResource, TRUE ); if (!DeviceLockAlreadyHeld) { status = KeWaitForSingleObject( &DeviceObject->DeviceLock, Executive, KeGetPreviousMode(), TRUE, (PLARGE_INTEGER) NULL ); if (status == STATUS_ALERTED || status == STATUS_USER_APC) { ExReleaseResource( &IopDatabaseResource ); //KeLeaveCriticalRegion(); return status; } } if (!(DeviceObject->Vpb->Flags & VPB_MOUNTED)) { // // Reset the list back to the beginning and start over // again. // dummy.Flink = queueHeader->Flink; entry = &dummy; status = STATUS_UNRECOGNIZED_VOLUME; } } // // If the error wasn't STATUS_UNRECOGNIZED_VOLUME, and this // request is not going to the Raw file system, then there // is no reason to continue looping. // if (!AllowRawMount && (status != STATUS_UNRECOGNIZED_VOLUME) && FsRtlIsTotalDeviceFailure(status)) { break; } } } } else { // // The volume for this device has already been mounted. Return a // success code. // status = STATUS_SUCCESS; } // // Release the I/O database resource lock and the synchronization event for // the device. // if (!DeviceLockAlreadyHeld) { KeSetEvent( &DeviceObject->DeviceLock, 0, FALSE ); } ExReleaseResource( &IopDatabaseResource ); //KeLeaveCriticalRegion(); // // Finally, if the mount operation failed, and the target device is the // boot partition, then bugcheck the system. It is not possible for the // system to run properly if the system's boot partition cannot be mounted. // // Note: Don't bugcheck if the system is already booted. // if (!NT_SUCCESS( status ) && DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION && InitializationPhase < 2) { KeBugCheckEx( INACCESSIBLE_BOOT_DEVICE, (ULONG) DeviceObject, 0, 0, 0 ); } return status; } NTSTATUS IopOpenLinkOrRenameTarget( OUT PHANDLE TargetHandle, IN PIRP Irp, IN PVOID RenameBuffer, IN PFILE_OBJECT FileObject ) /*++ Routine Description: This routine is invoked by the rename, set link and set copy-on-write code in the I/O system's NtSetInformationFile system service when the caller has specified a fully qualified file name as the target of a rename, set link, or set copy-on-write operation. This routine attempts to open the parent of the specified file and checks the following: o If the file itself exists, then the caller must have specified that the target is to be replaced, otherwise an error is returned. o Ensures that the target file specification refers to the same volume upon which the source file exists. Arguments: TargetHandle - Supplies the address of a variable to return the handle to the opened target file if no errors have occurred. Irp - Supplies a pointer to the IRP that represents the current rename request. RenameBuffer - Supplies a pointer to the system intermediate buffer that contains the caller's rename parameters. FileObject - Supplies a pointer to the file object representing the file being renamed. Return Value: The function value is the final status of the operation. Note: This function assumes that the layout of a rename, set link and set copy-on-write information structure are exactly the same. --*/ { NTSTATUS status; IO_STATUS_BLOCK ioStatus; HANDLE handle; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING newFileName; PIO_STACK_LOCATION irpSp; PFILE_OBJECT targetFileObject; OBJECT_HANDLE_INFORMATION handleInformation; PFILE_RENAME_INFORMATION renameBuffer = RenameBuffer; PAGED_CODE(); ASSERT( sizeof( FILE_RENAME_INFORMATION ) == sizeof( FILE_LINK_INFORMATION ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, ReplaceIfExists ) == FIELD_OFFSET( FILE_LINK_INFORMATION, ReplaceIfExists ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, RootDirectory ) == FIELD_OFFSET( FILE_LINK_INFORMATION, RootDirectory ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, FileNameLength ) == FIELD_OFFSET( FILE_LINK_INFORMATION, FileNameLength ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) == FIELD_OFFSET( FILE_LINK_INFORMATION, FileName ) ); ASSERT( sizeof( FILE_RENAME_INFORMATION ) == sizeof( FILE_COPY_ON_WRITE_INFORMATION ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, ReplaceIfExists ) == FIELD_OFFSET( FILE_COPY_ON_WRITE_INFORMATION, ReplaceIfExists ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, RootDirectory ) == FIELD_OFFSET( FILE_COPY_ON_WRITE_INFORMATION, RootDirectory ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, FileNameLength ) == FIELD_OFFSET( FILE_COPY_ON_WRITE_INFORMATION, FileNameLength ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) == FIELD_OFFSET( FILE_COPY_ON_WRITE_INFORMATION, FileName ) ); ASSERT( sizeof( FILE_RENAME_INFORMATION ) == sizeof( FILE_MOVE_CLUSTER_INFORMATION ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, ReplaceIfExists ) == FIELD_OFFSET( FILE_MOVE_CLUSTER_INFORMATION, ClusterCount ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, RootDirectory ) == FIELD_OFFSET( FILE_MOVE_CLUSTER_INFORMATION, RootDirectory ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, FileNameLength ) == FIELD_OFFSET( FILE_MOVE_CLUSTER_INFORMATION, FileNameLength ) ); ASSERT( FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) == FIELD_OFFSET( FILE_MOVE_CLUSTER_INFORMATION, FileName ) ); // // A fully qualified file name was specified. Begin by attempting to open // the parent directory of the specified target file. // newFileName.Length = (USHORT) renameBuffer->FileNameLength; newFileName.MaximumLength = (USHORT) renameBuffer->FileNameLength; newFileName.Buffer = renameBuffer->FileName; InitializeObjectAttributes( &objectAttributes, &newFileName, FileObject->Flags & FO_OPENED_CASE_SENSITIVE ? 0 : OBJ_CASE_INSENSITIVE, renameBuffer->RootDirectory, (PSECURITY_DESCRIPTOR) NULL ); status = IoCreateFile( &handle, FILE_WRITE_DATA | SYNCHRONIZE, &objectAttributes, &ioStatus, (PLARGE_INTEGER) NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, (PVOID) NULL, 0L, CreateFileTypeNone, (PVOID) NULL, IO_NO_PARAMETER_CHECKING | IO_OPEN_TARGET_DIRECTORY | IO_FORCE_ACCESS_CHECK ); if (NT_SUCCESS( status )) { // // The open operation for the target file's parent directory was // successful. Check to see whether or not the file exists. // irpSp = IoGetNextIrpStackLocation( Irp ); if (irpSp->Parameters.SetFile.FileInformationClass == FileLinkInformation && !renameBuffer->ReplaceIfExists && ioStatus.Information == FILE_EXISTS) { // // The target file exists, and the caller does not want to replace // it. This is a name collision error so cleanup and return. // NtClose( handle ); status = STATUS_OBJECT_NAME_COLLISION; } else { // // Everything up to this point is fine, so dereference the handle // to a pointer to the file object and ensure that the two file // specifications refer to the same device. // status = ObReferenceObjectByHandle( handle, FILE_WRITE_DATA, IoFileObjectType, UserMode, (PVOID *) &targetFileObject, &handleInformation ); if (NT_SUCCESS( status )) { ObDereferenceObject( targetFileObject ); if (IoGetRelatedDeviceObject( targetFileObject) != IoGetRelatedDeviceObject( FileObject )) { // // The two files refer to different devices. Clean everything // up and return an appropriate error. // NtClose( handle ); status = STATUS_NOT_SAME_DEVICE; } else { // // Otherwise, everything worked, so allow the rename operation // to continue. // irpSp->Parameters.SetFile.FileObject = targetFileObject; *TargetHandle = handle; status = STATUS_SUCCESS; } } else { // // There was an error referencing the handle to what should // have been the target directory. This generally means that // there was a resource problem or the handle was invalid, etc. // Simply attempt to close the handle and return the error. // NtClose( handle ); } } } // // Return the final status of the operation. // return status; } NTSTATUS IopOpenRegistryKey( OUT PHANDLE Handle, IN HANDLE BaseHandle OPTIONAL, IN PUNICODE_STRING KeyName, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Create ) /*++ Routine Description: Opens or creates a VOLATILE registry key using the name passed in based at the BaseHandle node. Arguments: Handle - Pointer to the handle which will contain the registry key that was opened. BaseHandle - Handle to the base path from which the key must be opened. KeyName - Name of the Key that must be opened/created. DesiredAccess - Specifies the desired access that the caller needs to the key. Create - Determines if the key is to be created if it does not exist. Return Value: The function value is the final status of the operation. --*/ { OBJECT_ATTRIBUTES objectAttributes; ULONG disposition; PAGED_CODE(); // // Initialize the object for the key. // InitializeObjectAttributes( &objectAttributes, KeyName, OBJ_CASE_INSENSITIVE, BaseHandle, (PSECURITY_DESCRIPTOR) NULL ); // // Create the key or open it, as appropriate based on the caller's // wishes. // if (Create) { return ZwCreateKey( Handle, DesiredAccess, &objectAttributes, 0, (PUNICODE_STRING) NULL, REG_OPTION_VOLATILE, &disposition ); } else { return ZwOpenKey( Handle, DesiredAccess, &objectAttributes ); } } NTSTATUS IopQueryXxxInformation( IN PFILE_OBJECT FileObject, IN ULONG InformationClass, IN ULONG Length, OUT PVOID Information, OUT PULONG ReturnedLength, IN BOOLEAN FileInformation ) /*++ Routine Description: This routine returns the requested information about a specified file or volume. The information returned is determined by the class that is specified, and it is placed into the caller's output buffer. Arguments: FileObject - Supplies a pointer to the file object about which the requested information is returned. FsInformationClass - Specifies the type of information which should be returned about the file/volume. Length - Supplies the length of the buffer in bytes. FsInformation - Supplies a buffer to receive the requested information returned about the file. This buffer must not be pageable and must reside in system space. ReturnedLength - Supplies a variable that is to receive the length of the information written to the buffer. FileInformation - Boolean that indicates whether the information requested is for a file or a volume. Return Value: The status returned is the final completion status of the operation. --*/ { PIRP irp; NTSTATUS status; PDEVICE_OBJECT deviceObject; KEVENT event; PIO_STACK_LOCATION irpSp; IO_STATUS_BLOCK localIoStatus; BOOLEAN synchronousIo; PAGED_CODE(); // // Reference the file object here so that no special checks need be made // in I/O completion to determine whether or not to dereference the file // object. // ObReferenceObject( FileObject ); // // Make a special check here to determine whether this is a synchronous // I/O operation. If it is, then wait here until the file is owned by // the current thread. If this is not a (serialized) synchronous I/O // operation, then initialize the local event. // if (FileObject->Flags & FO_SYNCHRONOUS_IO) { BOOLEAN interrupted; if (!IopAcquireFastLock( FileObject )) { status = IopAcquireFileObjectLock( FileObject, KernelMode, (BOOLEAN) ((FileObject->Flags & FO_ALERTABLE_IO) != 0), &interrupted ); if (interrupted) { ObDereferenceObject( FileObject ); return status; } } KeClearEvent( &FileObject->Event ); synchronousIo = TRUE; } else { KeInitializeEvent( &event, SynchronizationEvent, FALSE ); synchronousIo = FALSE; } // // Get the address of the target device object. // deviceObject = IoGetRelatedDeviceObject( FileObject ); // // Allocate and initialize the I/O Request Packet (IRP) for this operation. // The allocation is performed with an exception handler in case the // caller does not have enough quota to allocate the packet. // irp = IoAllocateIrp( deviceObject->StackSize, TRUE ); if (!irp) { // // An IRP could not be allocated. Cleanup and return an appropriate // error status code. // IopAllocateIrpCleanup( FileObject, (PKEVENT) NULL ); return STATUS_INSUFFICIENT_RESOURCES; } irp->Tail.Overlay.OriginalFileObject = FileObject; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->RequestorMode = KernelMode; // // Fill in the service independent parameters in the IRP. // if (synchronousIo) { irp->UserEvent = (PKEVENT) NULL; } else { irp->UserEvent = &event; irp->Flags = IRP_SYNCHRONOUS_API; } irp->UserIosb = &localIoStatus; irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL; // // Get a pointer to the stack location for the first driver. This will be // used to pass the original function codes and parameters. // irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = FileInformation ? IRP_MJ_QUERY_INFORMATION : IRP_MJ_QUERY_VOLUME_INFORMATION; irpSp->FileObject = FileObject; // // Set the system buffer address to the address of the caller's buffer and // set the flags so that the buffer is not deallocated. // irp->AssociatedIrp.SystemBuffer = Information; irp->Flags |= IRP_BUFFERED_IO; // // Copy the caller's parameters to the service-specific portion of the // IRP. // if (FileInformation) { irpSp->Parameters.QueryFile.Length = Length; irpSp->Parameters.QueryFile.FileInformationClass = InformationClass; } else { irpSp->Parameters.QueryVolume.Length = Length; irpSp->Parameters.QueryVolume.FsInformationClass = InformationClass; } // // Insert the packet at the head of the IRP list for the thread. // IopQueueThreadIrp( irp ); // // Now simply invoke the driver at its dispatch entry with the IRP. // status = IoCallDriver( deviceObject, irp ); // // If this operation was a synchronous I/O operation, check the return // status to determine whether or not to wait on the file object. If // the file object is to be waited on, wait for the operation to complete // and obtain the final status from the file object itself. // if (synchronousIo) { if (status == STATUS_PENDING) { status = KeWaitForSingleObject( &FileObject->Event, Executive, KernelMode, (BOOLEAN) ((FileObject->Flags & FO_ALERTABLE_IO) != 0), (PLARGE_INTEGER) NULL ); if (status == STATUS_ALERTED) { IopCancelAlertedRequest( &FileObject->Event, irp ); } status = FileObject->FinalStatus; } IopReleaseFileObjectLock( FileObject ); } else { // // This is a normal synchronous I/O operation, as opposed to a // serialized synchronous I/O operation. For this case, wait // for the local event and copy the final status information // back to the caller. // if (status == STATUS_PENDING) { (VOID) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); status = localIoStatus.Status; } } *ReturnedLength = localIoStatus.Information; return status; } VOID IopRaiseHardError( IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This routine raises a hard error popup in the context of the current thread. The APC was used to get into the context of this thread so that the popup would be sent to the appropriate port. Arguments: NormalContext - Supplies a pointer to the I/O Request Packet (IRP) that was initially used to request the operation that has failed. SystemArgument1 - Supplies a pointer to the media's volume parameter block. See IoRaiseHardError documentation for more information. SystemArgument2 - Supplies a pointer to the real device object. See IoRaiseHardError documentation for more information. Return Value: None. --*/ { ULONG parameters[2]; ULONG numberOfParameters; ULONG parameterMask; ULONG response; NTSTATUS status; PIRP irp = (PIRP) NormalContext; PVPB vpb = (PVPB) SystemArgument1; PDEVICE_OBJECT realDeviceObject = (PDEVICE_OBJECT) SystemArgument2; ULONG length; POBJECT_NAME_INFORMATION objectName; UNICODE_STRING labelName; // // Determine the name of the device and the volume label of the offending // media. Start by determining the size of the DeviceName, and allocate // enough storage for both the ObjectName structure and the string // because "that's the ways Steve's routine works". // ObQueryNameString( realDeviceObject, NULL, 0, &length ); if ((objectName = ExAllocatePool(PagedPool, length)) == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } else { status = STATUS_SUCCESS; } if (!NT_SUCCESS( status ) || !NT_SUCCESS( status = ObQueryNameString( realDeviceObject, objectName, length, &response ) )) { // // Allocation of the pool to put up this popup did not work or // something else failed, so there isn't really much that can be // done here. Simply return an error back to the user. // if (objectName) { ExFreePool( objectName ); } irp->IoStatus.Status = status; irp->IoStatus.Information = 0; IoCompleteRequest( irp, IO_DISK_INCREMENT ); return; } // // The volume label has a max size of 32 characters (Unicode). Convert // it to a Unicode string for output in the popup message. // if (vpb != NULL && vpb->Flags & VPB_MOUNTED) { labelName.Buffer = &vpb->VolumeLabel[0]; labelName.Length = vpb->VolumeLabelLength; labelName.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; } else { RtlInitUnicodeString( &labelName, NULL ); } // // Different pop-ups have different printf formats. Depending on the // specific error value, adjust the parameters. // switch( irp->IoStatus.Status ) { case STATUS_MEDIA_WRITE_PROTECTED: case STATUS_WRONG_VOLUME: numberOfParameters = 2; parameterMask = 3; parameters[0] = (ULONG) &labelName; parameters[1] = (ULONG) &objectName->Name; break; case STATUS_DEVICE_NOT_READY: case STATUS_IO_TIMEOUT: case STATUS_NO_MEDIA_IN_DEVICE: case STATUS_UNRECOGNIZED_MEDIA: numberOfParameters = 1; parameterMask = 1; parameters[0] = (ULONG) &objectName->Name; parameters[1] = 0; break; default: numberOfParameters = 0; parameterMask = 0; } // // Simply raise the hard error. // if (ExReadyForErrors) { status = ExRaiseHardError( irp->IoStatus.Status, numberOfParameters, parameterMask, parameters, OptionAbortRetryIgnore, &response ); } else { status = STATUS_UNSUCCESSFUL; response = ResponseReturnToCaller; } // // Free any pool or other resources that were allocated to output the // popup. // ExFreePool( objectName ); // // If there was a problem, or the user didn't want to retry, just // complete the request. Otherwise simply call the driver entry // point and retry the IRP as if it had never been tried before. // if (!NT_SUCCESS( status ) || response != ResponseRetry) { // // Before completing the request, make one last check. If this was // a mount request, and the reason for the failure was t/o, no media, // or unrecognized media, then set the Information field of the status // block to indicate whether or not an abort was performed. // if (response == ResponseAbort) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( irp ); if (irpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && irpSp->MinorFunction == IRP_MN_MOUNT_VOLUME) { irp->IoStatus.Information = IOP_ABORT; } else { irp->IoStatus.Status = STATUS_REQUEST_ABORTED; } } // // An error was incurred, so zero out the information field before // completing the request if this was an input operation. Otherwise, // IopCompleteRequest will try to copy to the user's buffer. // if (irp->Flags & IRP_INPUT_OPERATION) { irp->IoStatus.Information = 0; } IoCompleteRequest( irp, IO_DISK_INCREMENT ); } else { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( irp ); PDEVICE_OBJECT fsDeviceObject = irpSp->DeviceObject; PDRIVER_OBJECT driverObject = fsDeviceObject->DriverObject; // // Retry the request from the top. // driverObject->MajorFunction[irpSp->MajorFunction]( fsDeviceObject, irp ); } } VOID IopRaiseInformationalHardError( IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This routine performs the actual pop-up. It will called from either the hard-error thread, or a APC routine in a user thread after exiting the file system. Arguments: NormalContext - Contains the information for the pop-up SystemArgument1 - not used. SystemArgument1 - not used. Return Value: None. --*/ { ULONG parameterPresent; ULONG errorParameter; ULONG errorResponse; PIOP_HARD_ERROR_PACKET hardErrorPacket; UNREFERENCED_PARAMETER( SystemArgument1 ); UNREFERENCED_PARAMETER( SystemArgument2 ); hardErrorPacket = (PIOP_HARD_ERROR_PACKET) NormalContext; // // Simply raise the hard error if the system is ready to accept one. // errorParameter = (ULONG) &hardErrorPacket->String; parameterPresent = (hardErrorPacket->String.Buffer != NULL); if (ExReadyForErrors) { (VOID) ExRaiseHardError( hardErrorPacket->ErrorStatus, parameterPresent, parameterPresent, parameterPresent ? &errorParameter : NULL, OptionOk, &errorResponse ); } // // Now free the packet and the buffer, if one was specified. // if (hardErrorPacket->String.Buffer) { ExFreePool( hardErrorPacket->String.Buffer ); } ExFreePool( hardErrorPacket ); } VOID IopReadyDeviceObjects( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine is invoked to mark all of the device objects owned by the specified driver as having been fully initialized and therefore ready for access by other drivers/clients. Arguments: DriverObject - Supplies a pointer to the driver object for the driver whose devices are to be marked as being "ready". Return Value: None. --*/ { PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; PAGED_CODE(); // // Loop through all of the driver's device objects, clearing the // DO_DEVICE_INITIALIZING flag. while (deviceObject) { deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; deviceObject = deviceObject->NextDevice; } } VOID IopStartApcHardError( IN PVOID StartContext ) /*++ Routine Description: This function is invoked in an ExWorker thread when we need to do a hard error pop-up, but the Irp's originating thread is at APC level, ie. IoPageRead. It starts a thread to hold the pop-up. Arguments: StartContext - Startup context, contains a IOP_APC_HARD_ERROR_PACKET. Return Value: None. --*/ { HANDLE thread; NTSTATUS status; // // Create the hard error pop-up thread. If for whatever reason we // can't do this then just complete the Irp with the error. // status = PsCreateSystemThread( &thread, 0, (POBJECT_ATTRIBUTES)NULL, (HANDLE)0, (PCLIENT_ID)NULL, IopApcHardError, StartContext ); if ( !NT_SUCCESS( status ) ) { ExFreePool( StartContext ); IoCompleteRequest( ((PIOP_APC_HARD_ERROR_PACKET)StartContext)->Irp, IO_DISK_INCREMENT ); return; } // // Close thread handle // ZwClose(thread); } NTSTATUS IopSynchronousApiServiceTail( IN NTSTATUS ReturnedStatus, IN PKEVENT Event, IN PIRP Irp, IN KPROCESSOR_MODE RequestorMode, IN PIO_STATUS_BLOCK LocalIoStatus, OUT PIO_STATUS_BLOCK IoStatusBlock ) /*++ Routine Description: This routine is invoked when a synchronous API is invoked for a file that has been opened for asynchronous I/O. This function synchronizes the completion of the I/O operation on the file. Arguments: ReturnedStatus - Supplies the status that was returned from the call to IoCallDriver. Event - Address of the allocated kernel event to be used for synchronization of the I/O operation. Irp - Address of the I/O Request Packet submitted to the driver. RequestorMode - Processor mode of the caller when the operation was requested. LocalIoStatus - Address of the I/O status block used to capture the final status by the service itself. IoStatusBlock - Address of the I/O status block supplied by the caller of the system service. Return Value: The function value is the final status of the operation. --*/ { NTSTATUS status; PAGED_CODE(); // // This is a normal synchronous I/O operation, as opposed to a // serialized synchronous I/O operation. For this case, wait for // the local event and copy the final status information back to // the caller. // status = ReturnedStatus; if (status == STATUS_PENDING) { status = KeWaitForSingleObject( Event, Executive, RequestorMode, FALSE, (PLARGE_INTEGER) NULL ); if (status == STATUS_ALERTED || status == STATUS_USER_APC) { // // The wait request has ended either because the thread was // alerted or an APC was queued to this thread, because of // thread rundown or CTRL/C processing. In either case, try // to bail out of this I/O request carefully so that the IRP // completes before this routine exists or the event will not // be around to set to the Signaled state. // IopCancelAlertedRequest( Event, Irp ); } status = LocalIoStatus->Status; } try { *IoStatusBlock = *LocalIoStatus; } except(EXCEPTION_EXECUTE_HANDLER) { // // An exception occurred attempting to write the caller's I/O // status block. Simply change the final status of the operation // to the exception code. // status = GetExceptionCode(); } ExFreePool( Event ); return status; } NTSTATUS IopSynchronousServiceTail( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PFILE_OBJECT FileObject, IN BOOLEAN DeferredIoCompletion, IN KPROCESSOR_MODE RequestorMode, IN BOOLEAN SynchronousIo, IN TRANSFER_TYPE TransferType ) /*++ Routine Description: This routine is invoked to complete the operation of a system service. It queues the IRP to the thread's queue, updates the transfer count, calls the driver, and finally synchronizes completion of the I/O. Arguments: DeviceObject - Device on which the I/O is to occur. Irp - I/O Request Packet representing the I/O operation. FileObject - File object for this open instantiation. DeferredIoCompletion - Indicates whether deferred completion is possible. RequestorMode - Mode in which request was made. SynchronousIo - Indicates whether the operation is to be synchronous. TransferType - Type of transfer being performed: read, write, or other. Return Value: The function value is the final status of the operation. --*/ { NTSTATUS status; PAGED_CODE(); // // Insert the packet at the head of the IRP list for the thread. // IopQueueThreadIrp( Irp ); // // Update the operation count statistic for the current process. // switch( TransferType ) { case ReadTransfer: IopUpdateReadOperationCount(); break; case WriteTransfer: IopUpdateWriteOperationCount(); break; case OtherTransfer: IopUpdateOtherOperationCount(); break; } // // Now simply invoke the driver at its dispatch entry with the IRP. // status = IoCallDriver( DeviceObject, Irp ); // // If deferred I/O completion is possible, check for pending returned // from the driver. If the driver did not return pending, then the // packet has not actually been completed yet, so complete it here. // if (DeferredIoCompletion) { if (status != STATUS_PENDING) { // // The I/O operation was completed without returning a status of // pending. This means that at this point, the IRP has not been // fully completed. Complete it now. // PKNORMAL_ROUTINE normalRoutine; PVOID normalContext; KIRQL irql; ASSERT( !Irp->PendingReturned ); KeRaiseIrql( APC_LEVEL, &irql ); IopCompleteRequest( &Irp->Tail.Apc, &normalRoutine, &normalContext, (PVOID *) &FileObject, &normalContext ); KeLowerIrql( irql ); } } // // If this operation was a synchronous I/O operation, check the return // status to determine whether or not to wait on the file object. If // the file object is to be waited on, wait for the operation to complete // and obtain the final status from the file object itself. // if (SynchronousIo) { if (status == STATUS_PENDING) { status = KeWaitForSingleObject( &FileObject->Event, Executive, RequestorMode, (BOOLEAN) ((FileObject->Flags & FO_ALERTABLE_IO) != 0), (PLARGE_INTEGER) NULL ); if (status == STATUS_ALERTED || status == STATUS_USER_APC) { // // The wait request has ended either because the thread was alerted // or an APC was queued to this thread, because of thread rundown or // CTRL/C processing. In either case, try to bail out of this I/O // request carefully so that the IRP completes before this routine // exists so that synchronization with the file object will remain // intact. // IopCancelAlertedRequest( &FileObject->Event, Irp ); } status = FileObject->FinalStatus; } IopReleaseFileObjectLock( FileObject ); } return status; } VOID IopTimerDispatch( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This routine scans the I/O system timer database and invokes each driver that has enabled a timer in the list, once every second. Arguments: Dpc - Supplies a pointer to a control object of type DPC. DeferredContext - Optional deferred context; not used. SystemArgument1 - Optional argument 1; not used. SystemArgument2 - Optional argument 2; not used. Return Value: None. --*/ { PLIST_ENTRY timerEntry; PIO_TIMER timer; LARGE_INTEGER deltaTime; KIRQL irql; ULONG i; UNREFERENCED_PARAMETER( Dpc ); UNREFERENCED_PARAMETER( DeferredContext ); UNREFERENCED_PARAMETER( SystemArgument1 ); UNREFERENCED_PARAMETER( SystemArgument2 ); // // Check to see whether or not there are any timers in the queue that // have been enabled. If so, then walk the list and invoke all of the // drivers' routines. Note that if the counter changes, which it can // because the spin lock is not owned, then a timer routine may be // missed. However, this is acceptable, since the driver inserting the // entry could be context switched away from, etc. Therefore, this is // not a critical resource for the most part. // if (IopTimerCount) { // // There is at least one timer entry in the queue that is enabled. // Walk the queue and invoke each specified timer routine. // ExAcquireSpinLock( &IopTimerLock, &irql ); i = IopTimerCount; timerEntry = IopTimerQueueHead.Flink; // // For each entry found that is enabled, invoke the driver's routine // with its specified context parameter. The local count is used // to abort the queue traversal when there are more entries in the // queue, but they are not enabled. // for (timerEntry = IopTimerQueueHead.Flink; (timerEntry != &IopTimerQueueHead) && i; timerEntry = timerEntry->Flink ) { timer = CONTAINING_RECORD( timerEntry, IO_TIMER, TimerList ); if (timer->TimerFlag) { timer->TimerRoutine( timer->DeviceObject, timer->Context ); i--; } } ExReleaseSpinLock( &IopTimerLock, irql ); } } VOID IopUserCompletion( IN PKAPC Apc, IN PKNORMAL_ROUTINE *NormalRoutine, IN PVOID *NormalContext, IN PVOID *SystemArgument1, IN PVOID *SystemArgument2 ) /*++ Routine Description: This routine is invoked in the final processing of an IRP. Everything has been completed except that the caller's APC routine must be invoked. The system will do this as soon as this routine exits. The only processing remaining to be completed by the I/O system is to free the I/O Request Packet itself. Arguments: Apc - Supplies a pointer to kernel APC structure. NormalRoutine - Supplies a pointer to a pointer to the normal function that was specified when the APC was initialied. NormalContext - Supplies a pointer to a pointer to an arbitrary data structure that was specified when the APC was initialized. SystemArgument1, SystemArgument2 - Supplies a set of two pointers to two arguments that contain untyped data. Return Value: None. Note: If no other processing is ever needed, and the APC can be placed at the beginning of the IRP, then this routine could be replaced by simply specifying the address of the pool deallocation routine in the APC instead of the address of this routine. Caution: This routine is also invoked as a general purpose rundown routine for APCs. Should this code ever need to directly access any of the other parameters other than Apc, this routine will need to be split into two separate routines. The rundown routine should perform exactly the following code's functionality. --*/ { UNREFERENCED_PARAMETER( NormalRoutine ); UNREFERENCED_PARAMETER( NormalContext ); UNREFERENCED_PARAMETER( SystemArgument1 ); UNREFERENCED_PARAMETER( SystemArgument2 ); PAGED_CODE(); // // Free the packet. // IoFreeIrp( CONTAINING_RECORD( Apc, IRP, Tail.Apc ) ); } VOID IopUserRundown( IN PKAPC Apc ) /*++ Routine Description: This routine is invoked during thread termination as the rundown routine for it simply calls IopUserCompletion. Arguments: Apc - Supplies a pointer to kernel APC structure. Return Value: None. --*/ { PAGED_CODE(); // // Free the packet. // IoFreeIrp( CONTAINING_RECORD( Apc, IRP, Tail.Apc ) ); } NTSTATUS IopXxxControlFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG IoControlCode, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength, IN BOOLEAN DeviceIoControl ) /*++ Routine Description: This service builds descriptors or MDLs for the supplied buffer(s) and passes the untyped data to the driver associated with the file handle. handle. It is up to the driver to check the input data and function IoControlCode for validity, as well as to make the appropriate access checks. Arguments: FileHandle - Supplies a handle to the file on which the service is being performed. Event - Supplies an optional event to be set to the Signaled state when the service is complete. ApcRoutine - Supplies an optional APC routine to be executed when the service is complete. ApcContext - Supplies a context parameter to be passed to the ApcRoutine, if an ApcRoutine was specified. IoStatusBlock - Address of the caller's I/O status block. IoControlCode - Subfunction code to determine exactly what operation is being performed. InputBuffer - Optionally supplies an input buffer to be passed to the driver. Whether or not the buffer is actually optional is dependent on the IoControlCode. InputBufferLength - Length of the InputBuffer in bytes. OutputBuffer - Optionally supplies an output buffer to receive information from the driver. Whether or not the buffer is actually optional is dependent on the IoControlCode. OutputBufferLength - Length of the OutputBuffer in bytes. DeviceIoControl - Determines whether this is a Device or File System Control function. Return Value: The status returned is success if the control operation was properly queued to the I/O system. Once the operation completes, the status can be determined by examining the Status field of the I/O status block. --*/ { PIRP irp; NTSTATUS status; PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; PKEVENT eventObject = (PKEVENT) NULL; KPROCESSOR_MODE requestorMode; PIO_STACK_LOCATION irpSp; ULONG method; OBJECT_HANDLE_INFORMATION handleInformation; BOOLEAN synchronousIo; IO_STATUS_BLOCK localIoStatus; PFAST_IO_DISPATCH fastIoDispatch; POOL_TYPE poolType; PULONG majorFunction; PAGED_CODE(); // // Get the method that the buffers are being passed. // method = IoControlCode & 3; // // Get the previous mode; i.e., the mode of the caller. // requestorMode = KeGetPreviousMode(); if (requestorMode != KernelMode) { // // The caller's access mode is not kernel so probe each of the arguments // and capture them as necessary. If any failures occur, the condition // handler will be invoked to handle them. It will simply cleanup and // return an access violation status code back to the system service // dispatcher. // try { // // The IoStatusBlock parameter must be writeable by the caller. // ProbeForWriteIoStatus( IoStatusBlock ); // // The output buffer can be used in any one of the following three ways, // if it is specified: // // 0) It can be a normal, buffered output buffer. // // 1) It can be a DMA input buffer. // // 2) It can be a DMA output buffer. // // Which way the buffer is to be used it based on the low-order two bits // of the IoControlCode. // // If the method is 0 we probe the output buffer for write access. // If the method is not 3 we probe the input buffer for read access. // if (method == 0) { if (ARGUMENT_PRESENT( OutputBuffer )) { ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof( UCHAR ) ); } else { OutputBufferLength = 0; } } if (method != 3) { if (ARGUMENT_PRESENT( InputBuffer )) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( UCHAR ) ); } else { InputBufferLength = 0; } } } except(EXCEPTION_EXECUTE_HANDLER) { // // An exception was incurred while attempting to probe or write // one of the caller's parameters. Simply return an appropriate // error status code. // return GetExceptionCode(); } } // // There were no blatant errors so far, so reference the file object so // the target device object can be found. Note that if the handle does // not refer to a file object, or if the caller does not have the required // access to the file, then it will fail. // status = ObReferenceObjectByHandle( FileHandle, 0L, IoFileObjectType, requestorMode, (PVOID *) &fileObject, &handleInformation ); if (!NT_SUCCESS( status )) { return status; } // // If this file has an I/O completion port associated w/it, then ensure // that the caller did not supply an APC routine, as the two are mutually // exclusive methods for I/O completion notification. // if (fileObject->CompletionContext && ARGUMENT_PRESENT( ApcRoutine )) { ObDereferenceObject( fileObject ); return STATUS_INVALID_PARAMETER; } // // Now check the access type for this control code to ensure that the // caller has the appropriate access to this file object to perform the // operation. // if (requestorMode != KernelMode) { ULONG accessMode = (IoControlCode >> 14) & 3; if (accessMode != FILE_ANY_ACCESS) { // // This I/O control requires that the caller have read, write, // or read/write access to the object. If this is not the case, // then cleanup and return an appropriate error status code. // if (!(SeComputeGrantedAccesses( handleInformation.GrantedAccess, accessMode ))) { ObDereferenceObject( fileObject ); return STATUS_ACCESS_DENIED; } } } // // Get the address of the event object and set the event to the Not- // Signaled state, if an event was specified. Note here, too, that if // the handle does not refer to an event, or if the event cannot be // written, then the reference will fail. // if (ARGUMENT_PRESENT( Event )) { status = ObReferenceObjectByHandle( Event, EVENT_MODIFY_STATE, ExEventObjectType, requestorMode, (PVOID *) &eventObject, NULL ); if (!NT_SUCCESS( status )) { ObDereferenceObject( fileObject ); return status; } else { KeClearEvent( eventObject ); } } // // Make a special check here to determine whether this is a synchronous // I/O operation. If it is, then wait here until the file is owned by // the current thread. // if (fileObject->Flags & FO_SYNCHRONOUS_IO) { BOOLEAN interrupted; if (!IopAcquireFastLock( fileObject )) { status = IopAcquireFileObjectLock( fileObject, requestorMode, (BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0), &interrupted ); if (interrupted) { if (eventObject) { ObDereferenceObject( eventObject ); } ObDereferenceObject( fileObject ); return status; } } synchronousIo = TRUE; } else { synchronousIo = FALSE; } if (DeviceIoControl) { // // Get the address of the target device object. If this file represents // a device that was opened directly, then simply use the device or its // attached device(s) directly. Also get the address of the Fast Io // dispatch structure. // if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) { deviceObject = IoGetRelatedDeviceObject( fileObject ); } else { deviceObject = IoGetAttachedDevice( fileObject->DeviceObject ); } fastIoDispatch = deviceObject->DriverObject->FastIoDispatch; // // Turbo device control support. If the device has a fast I/O entry // point for DeviceIoControlFile, call the entry point and give it a // chance to try to complete the request. Note if FastIoDeviceControl // returns FALSE or we get an I/O error, we simply fall through and // go the "long way" and create an Irp. // if (fastIoDispatch && fastIoDispatch->FastIoDeviceControl) { // // Before we actually call the fast I/O routine in the driver, // we must probe OutputBuffer if the method is 1 or 2. // if (requestorMode != KernelMode && ARGUMENT_PRESENT(OutputBuffer)) { try { if (method == 1) { ProbeForRead( OutputBuffer, OutputBufferLength, sizeof( UCHAR ) ); } else if (method == 2) { ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof( UCHAR ) ); } } except(EXCEPTION_EXECUTE_HANDLER) { // // An exception was incurred while attempting to probe // the output buffer. Clean up and return an // appropriate error status code. // if (synchronousIo) { IopReleaseFileObjectLock( fileObject ); } ObDereferenceObject( fileObject ); return GetExceptionCode(); } } // // Call the driver's fast I/O routine. // if (fastIoDispatch->FastIoDeviceControl( fileObject, TRUE, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, IoControlCode, &localIoStatus, deviceObject )) { // // The driver successfully performed the I/O in it's // fast device control routine. Carefully return the // I/O status. // try { *IoStatusBlock = localIoStatus; } except( EXCEPTION_EXECUTE_HANDLER ) { localIoStatus.Status = GetExceptionCode(); localIoStatus.Information = 0; } // // If an event was specified, set it. // if (ARGUMENT_PRESENT( Event )) { KeSetEvent( eventObject, 0, FALSE ); ObDereferenceObject( eventObject ); } // // Note that the file object event need not be set to the // Signaled state, as it is already set. Release the // file object lock, if necessary. // if (synchronousIo) { IopReleaseFileObjectLock( fileObject ); } // // If this file object has a completion port associated with it // and this request has a non-NULL APC context then a completion // message needs to be queued. // if (fileObject->CompletionContext && ARGUMENT_PRESENT( ApcContext )) { PIOP_MINI_COMPLETION_PACKET miniPacket = NULL; try { miniPacket = ExAllocatePoolWithQuotaTag( NonPagedPool, sizeof( *miniPacket ), ' pcI' ); } except( EXCEPTION_EXECUTE_HANDLER ) { NOTHING; } if (miniPacket) { miniPacket->TypeFlag = 0xffffffff; miniPacket->KeyContext = fileObject->CompletionContext->Key; miniPacket->ApcContext = ApcContext; miniPacket->IoStatus = localIoStatus.Status; miniPacket->IoStatusInformation = localIoStatus.Information; KeInsertQueue( (PKQUEUE) fileObject->CompletionContext->Port, &miniPacket->ListEntry ); } else { localIoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; } } // // Cleanup and return. // ObDereferenceObject( fileObject ); return localIoStatus.Status; } } } else { // // This is a file system control. Simply get the address of the target // device object. // deviceObject = IoGetRelatedDeviceObject( fileObject ); } // // Set the file object to the Not-Signaled state. // KeClearEvent( &fileObject->Event ); // // Allocate and initialize the I/O Request Packet (IRP) for this operation. irp = IopAllocateIrp( deviceObject->StackSize, TRUE ); if (!irp) { // // An IRP could not be allocated. Cleanup and return an appropriate // error status code. // IopAllocateIrpCleanup( fileObject, eventObject ); return STATUS_INSUFFICIENT_RESOURCES; } irp->Tail.Overlay.OriginalFileObject = fileObject; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->Tail.Overlay.AuxiliaryBuffer = (PVOID) NULL; irp->RequestorMode = requestorMode; irp->PendingReturned = FALSE; irp->Cancel = FALSE; irp->CancelRoutine = (PDRIVER_CANCEL) NULL; // // Fill in the service independent parameters in the IRP. // irp->UserEvent = eventObject; irp->UserIosb = IoStatusBlock; irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine; irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext; // // Get a pointer to the stack location for the first driver. This will be // used to pass the original function codes and parameters. Note that // setting the major function here also sets: // // MinorFunction = 0; // Flags = 0; // Control = 0; // irpSp = IoGetNextIrpStackLocation( irp ); majorFunction = (PULONG) (&irpSp->MajorFunction); *majorFunction = DeviceIoControl ? IRP_MJ_DEVICE_CONTROL : IRP_MJ_FILE_SYSTEM_CONTROL; irpSp->FileObject = fileObject; // // Copy the caller's parameters to the service-specific portion of the // IRP for those parameters that are the same for all three methods. // irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength; irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength; irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode; // // Set the pool type based on the type of function being performed. // poolType = DeviceIoControl ? NonPagedPoolCacheAligned : NonPagedPool; // // Based on the method that the buffer are being passed, either allocate // buffers or build MDLs. Note that in some cases no probing has taken // place so the exception handler must catch access violations. // irp->MdlAddress = (PMDL) NULL; irp->AssociatedIrp.SystemBuffer = (PVOID) NULL; switch ( method ) { case 0: // // For this case, allocate a buffer that is large enough to contain // both the input and the output buffers. Copy the input buffer to // the allocated buffer and set the appropriate IRP fields. // irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID) NULL; try { if (InputBufferLength || OutputBufferLength) { irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuota( poolType, (InputBufferLength > OutputBufferLength) ? InputBufferLength : OutputBufferLength ); if (ARGUMENT_PRESENT( InputBuffer )) { RtlCopyMemory( irp->AssociatedIrp.SystemBuffer, InputBuffer, InputBufferLength ); } irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; irp->UserBuffer = OutputBuffer; if (ARGUMENT_PRESENT( OutputBuffer )) { irp->Flags |= IRP_INPUT_OPERATION; } } else { irp->Flags = 0; irp->UserBuffer = (PVOID) NULL; } } except(EXCEPTION_EXECUTE_HANDLER) { // // An exception was incurred while either allocating the // the system buffer or moving the caller's data. Determine // what actually happened, cleanup accordingly, and return // an appropriate error status code. // IopExceptionCleanup( fileObject, irp, eventObject, (PKEVENT) NULL ); return GetExceptionCode(); } break; case 1: case 2: // // For these two cases, allocate a buffer that is large enough to // contain the input buffer, if any, and copy the information to // the allocated buffer. Then build an MDL for either read or write // access, depending on the method, for the output buffer. Note // that the buffer length parameters have been jammed to zero for // users if the buffer parameter was not passed. (Kernel callers // should be calling the service correctly in the first place.) // // Note also that it doesn't make a whole lot of sense to specify // either method #1 or #2 if the IOCTL does not require the caller // to specify an output buffer. // irp->Flags = 0; irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID) NULL; try { if (InputBufferLength && ARGUMENT_PRESENT( InputBuffer )) { irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuota( poolType, InputBufferLength ); RtlCopyMemory( irp->AssociatedIrp.SystemBuffer, InputBuffer, InputBufferLength ); irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; } if (OutputBufferLength != 0) { irp->MdlAddress = IoAllocateMdl( OutputBuffer, OutputBufferLength, FALSE, TRUE, irp ); if (irp->MdlAddress == NULL) { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); } MmProbeAndLockPages( irp->MdlAddress, requestorMode, (LOCK_OPERATION) ((method == 1) ? IoReadAccess : IoWriteAccess) ); } } except(EXCEPTION_EXECUTE_HANDLER) { // // An exception was incurred while either allocating the // system buffer, copying the caller's data, allocating the // MDL, or probing and locking the caller's buffer. Determine // what actually happened, cleanup accordingly, and return // an appropriate error status code. // IopExceptionCleanup( fileObject, irp, eventObject, (PKEVENT) NULL ); return GetExceptionCode(); } break; case 3: // // For this case, do nothing. Everything is up to the driver. // Simply give the driver a copy of the caller's parameters and // let the driver do everything itself. // irp->Flags = 0; irp->UserBuffer = OutputBuffer; irpSp->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer; } // // Defer I/O completion for FSCTL requests, but not for IOCTL requests, // since file systems set pending properly but device driver do not. // if (!DeviceIoControl) { irp->Flags |= IRP_DEFER_IO_COMPLETION; } // // Update the transfer count statistic for the current process for // operations other than read and write. // IopUpdateOtherTransferCount( InputBufferLength ); // // Queue the packet, call the driver, and synchronize appopriately with // I/O completion. // return IopSynchronousServiceTail( deviceObject, irp, fileObject, (BOOLEAN)!DeviceIoControl, requestorMode, synchronousIo, OtherTransfer ); } NTSTATUS IopLookupBusStringFromID ( IN HANDLE KeyHandle, IN INTERFACE_TYPE InterfaceType, OUT PWCHAR Buffer, IN ULONG Length, OUT PULONG BusFlags OPTIONAL ) /*++ Routine Description: Translates INTERFACE_TYPE to its corresponding WCHAR[] string. Arguments: KeyHandle - Supplies a handle to the opened registry key, HKLM\System\CurrentControlSet\Control\SystemResources\BusValues. InterfaceType - Supplies the interface type for which a descriptive name is to be retrieved. Buffer - Supplies a pointer to a unicode character buffer that will receive the bus name. Since this buffer is used in an intermediate step to retrieve a KEY_VALUE_FULL_INFORMATION structure, it must be large enough to contain this structure (including the longest value name & data length under KeyHandle). Length - Supplies the length, in bytes, of the Buffer. BusFlags - Optionally receives the flags specified in the second DWORD of the matching REG_BINARY value. Return Value: The function value is the final status of the operation. --*/ { NTSTATUS status; ULONG Index, junk, i, j; PULONG pl; PKEY_VALUE_FULL_INFORMATION KeyInformation; WCHAR c; PAGED_CODE(); Index = 0; KeyInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer; for (; ;) { status = ZwEnumerateValueKey ( KeyHandle, Index++, KeyValueFullInformation, Buffer, Length, &junk ); if (!NT_SUCCESS (status)) { return status; } if (KeyInformation->Type != REG_BINARY) { continue; } pl = (PULONG) ((PUCHAR) KeyInformation + KeyInformation->DataOffset); if ((ULONG) InterfaceType != pl[0]) { continue; } // // Found a match - move the name to the start of the buffer // if(ARGUMENT_PRESENT(BusFlags)) { *BusFlags = pl[1]; } j = KeyInformation->NameLength / sizeof (WCHAR); for (i=0; i < j; i++) { c = KeyInformation->Name[i]; Buffer[i] = c; } Buffer[i] = 0; return STATUS_SUCCESS; } }