/*++ Copyright (c) Microsoft Corporation. All rights reserved. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Module Name: trivial.c Abstract: Derived from cancel.c (which demonstrates the new cancel-safe queue mechanism). Adapted from the original cancel sample (KB Q188276) available in MSDN. --*/ #include #include "..\inc\trivialioctl.h" #define TRIVIAL_DEVICE_NAME_U L"\\Device\\Trivial" #define TRIVIAL_DOS_DEVICE_NAME_U L"\\DosDevices\\Trivial" typedef struct _TRACE_BUFFER { CHAR Buffer[16384]; SIZE_T Length; } TRACE_BUFFER, *PTRACE_BUFFER; TRACE_BUFFER TraceBuffer; typedef struct _DEVICE_EXTENSION{ LARGE_INTEGER RegistryCallbackCookie; KMUTEX TraceBufferMutex; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING registryPath ); NTSTATUS TrivialCreateClose ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS TrivialRead ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS TrivialDeviceControl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID TrivialUnload( IN PDRIVER_OBJECT DriverObject ); NTSTATUS TrivialCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); void TrivialpAcquire(PKMUTEX pKMutex); void TrivialpRelease(PKMUTEX pKMutex); NTSTATUS TrivialRegistryCallback( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 ); #ifdef ALLOC_PRAGMA #pragma alloc_text( INIT, DriverEntry ) #pragma alloc_text( PAGE, TrivialCreateClose) #pragma alloc_text( PAGE, TrivialRead) #pragma alloc_text( PAGE, TrivialDeviceControl) #pragma alloc_text( PAGE, TrivialUnload) #pragma alloc_text( PAGE, TrivialRegistryCallback) #pragma alloc_text( PAGE, TrivialpAcquire) #pragma alloc_text( PAGE, TrivialpRelease) #endif // ALLOC_PRAGMA NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Installable driver initialization entry point. This entry point is called directly by the I/O system. Arguments: DriverObject - pointer to the driver object registryPath - pointer to a unicode string representing the path, to driver-specific key in the registry. Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING unicodeDeviceName; UNICODE_STRING unicodeDosDeviceName; PDEVICE_OBJECT deviceObject; PDEVICE_EXTENSION devExtension; HANDLE threadHandle; UNREFERENCED_PARAMETER (RegistryPath); // // Initialize UNICODE_STRING structures with NT and DOS device names // (void) RtlInitUnicodeString( &unicodeDeviceName, TRIVIAL_DEVICE_NAME_U); (void) RtlInitUnicodeString( &unicodeDosDeviceName, TRIVIAL_DOS_DEVICE_NAME_U ); // // Create DOS device // status = IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), &unicodeDeviceName, FILE_DEVICE_UNKNOWN, 0, (BOOLEAN) FALSE, &deviceObject ); if (!NT_SUCCESS(status)) { return status; } // // Create the DOS name for the device // status = IoCreateSymbolicLink( (PUNICODE_STRING) &unicodeDosDeviceName, (PUNICODE_STRING) &unicodeDeviceName ); if (!NT_SUCCESS(status)) { IoDeleteDevice(deviceObject); return status; } // Initialize our data lock devExtension = deviceObject->DeviceExtension; KeInitializeMutex(&devExtension->TraceBufferMutex, 0); // // Initialize our IRP handling and unload functions // DriverObject->MajorFunction[IRP_MJ_CREATE] = TrivialCreateClose; DriverObject->MajorFunction[IRP_MJ_READ] = TrivialRead; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TrivialDeviceControl; DriverObject->MajorFunction[IRP_MJ_CLOSE] = TrivialCreateClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = TrivialCleanup; DriverObject->DriverUnload = TrivialUnload; // Arrange for I/O Manager to copy from the system buffer to the user buffer deviceObject->Flags |= DO_BUFFERED_IO; return status; } NTSTATUS TrivialCreateClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Process the Create and close IRPs sent to this device. Arguments: DeviceObject - pointer to a device object. Irp - pointer to an I/O Request Packet. Return Value: NT Status code --*/ { PIO_STACK_LOCATION irpStack; NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION devExtension = DeviceObject->DeviceExtension; PAGED_CODE (); // // Get a pointer to the current location in the Irp. // irpStack = IoGetCurrentIrpStackLocation(Irp); switch(irpStack->MajorFunction) { case IRP_MJ_CREATE: // // The dispatch routine for IRP_MJ_CREATE is called when a // file object associated with the device is created. // This is typically because of a call to CreateFile() in // a user-mode program or because a another driver is // layering itself over a this driver. A driver is // required to supply a dispatch routine for IRP_MJ_CREATE. // Irp->IoStatus.Information = 0; // // Register for registry callbacks // status = CmRegisterCallback(TrivialRegistryCallback, devExtension, &devExtension->RegistryCallbackCookie); break; case IRP_MJ_CLOSE: // // The IRP_MJ_CLOSE dispatch routine is called when a file object // opened on the driver is being removed from the system; that is, // all file object handles have been closed and the reference count // of the file object is down to 0. // Irp->IoStatus.Information = 0; break; default: status = STATUS_INVALID_PARAMETER; break; } // // Save Status for return and complete Irp // Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } NTSTATUS TrivialCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This dispatch routine is called when the last handle (in the whole system) to a file object is closed. In other words, the open handle count for the file object goes to 0. A driver that holds pending IRPs internally must implement a routine for IRP_MJ_CLEANUP. When the routine is called, the driver should cancel all the pending IRPs that belong to the file object identified by the IRP_MJ_CLEANUP call. In other words, it should cancel all the IRPs that have the same file-object pointer as the one supplied in the current I/O stack location of the IRP for the IRP_MJ_CLEANUP call. Of course, IRPs belonging to other file objects should not be canceled. Also, if an outstanding IRP is completed immediately, the driver does not have to cancel it. Arguments: DeviceObject -- pointer to the device object Irp -- pointer to the requesing Irp Return Value: STATUS_SUCCESS -- if the poll succeeded, --*/ { PDEVICE_EXTENSION devExtension; LIST_ENTRY tempQueue; PLIST_ENTRY thisEntry; PIRP pendingIrp; PIO_STACK_LOCATION pendingIrpStack, irpStack; //irpStack = IoGetCurrentIrpStackLocation(Irp); devExtension = DeviceObject->DeviceExtension; // // Unregister for registry callbacks // (void) CmUnRegisterCallback(devExtension->RegistryCallbackCookie); devExtension->RegistryCallbackCookie.QuadPart = 0; // // Finally complete the cleanup IRP // Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID TrivialUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Free all the allocated resources, etc. Arguments: DriverObject - pointer to a driver object. Return Value: VOID --*/ { PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; UNICODE_STRING uniWin32NameString; PDEVICE_EXTENSION devExtension = deviceObject->DeviceExtension; PAGED_CODE(); // // Delete our DOS link and then our device // RtlInitUnicodeString( &uniWin32NameString, TRIVIAL_DOS_DEVICE_NAME_U ); IoDeleteSymbolicLink( &uniWin32NameString ); IoDeleteDevice( deviceObject ); return; } void TrivialpAcquire(PKMUTEX pKMutex) { (void) KeWaitForSingleObject (pKMutex, Executive, KernelMode, FALSE, NULL); } void TrivialpRelease(PKMUTEX pKMutex) { KeReleaseMutex (pKMutex, FALSE); } NTSTATUS TrivialRead ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Read dispatch routine Arguments: DeviceObject - pointer to a device object. Irp - pointer to current Irp Return Value: NT status code. --*/ { NTSTATUS status; PDEVICE_EXTENSION devExtension; PIO_STACK_LOCATION irpStack; PCHAR iobuffer; PAGED_CODE(); // // Get a pointer to the device extension and irp stack. // devExtension = DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); TrivialpAcquire(&devExtension->TraceBufferMutex); // // First make sure there is enough room in our buffer. // if (irpStack->Parameters.Read.Length < TraceBuffer.Length) { Irp->IoStatus.Status = status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; } else { iobuffer = (PCHAR)Irp->AssociatedIrp.SystemBuffer; RtlCopyMemory ( iobuffer, &TraceBuffer.Buffer, TraceBuffer.Length ); Irp->IoStatus.Status = status = STATUS_SUCCESS; Irp->IoStatus.Information = TraceBuffer.Length; TraceBuffer.Length = 0; } TrivialpRelease(&devExtension->TraceBufferMutex); IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } NTSTATUS TrivialDeviceControl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: DeviceControl dispatch routine Arguments: DeviceObject - pointer to a device object. Irp - pointer to current Irp Return Value: NT status code. --*/ { NTSTATUS status; ULONG information = 0; PDEVICE_EXTENSION devExtension; PIO_STACK_LOCATION irpStack; LARGE_INTEGER currentTime; ULONG inBufLength; // Input buffer length ULONG outBufLength; // Output buffer length PCHAR inBuf, outBuf; // pointer to Input and output buffer PAGED_CODE(); // // Get a pointer to the device extension and irp stack. // devExtension = DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); // // We use METHOD_BUFFERED for all our IOCTLs in this driver // inBufLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; outBufLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; inBuf = Irp->AssociatedIrp.SystemBuffer; outBuf = Irp->AssociatedIrp.SystemBuffer; // // Switch on the specified IOCTL code // switch ( irpStack->Parameters.DeviceIoControl.IoControlCode ) { case IOCTL_TRIVIAL_GETLENGTH: if (outBufLength == sizeof(TraceBuffer.Length)) { information = sizeof(TraceBuffer.Length); RtlCopyMemory (outBuf, &TraceBuffer.Length, information); status = STATUS_SUCCESS; } break; default: status = STATUS_INVALID_DEVICE_REQUEST; break; } Irp->IoStatus.Information = information; Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } void TrivialpLogRegistryCallback ( PKMUTEX pKMutex, PWCHAR p, ULONG nbytes ) { PTRACE_BUFFER pBuffer = &TraceBuffer; ULONG maxLength = sizeof(TraceBuffer.Buffer); ULONG nchars; ULONG newLength; PCHAR q; if (nbytes & 1) return; nchars = nbytes / sizeof(WCHAR); TrivialpAcquire(pKMutex); // // Copy if we have enough space. // newLength = TraceBuffer.Length + nchars + sizeof(CHAR); if (newLength <= maxLength) { q = TraceBuffer.Buffer + TraceBuffer.Length; while (nchars > 0) { *q++ = ( *p++ & 0xff ); // Unicode -> Ansi nchars--; } *q++ = '\n'; TraceBuffer.Length = newLength; } if (newLength < maxLength) TraceBuffer.Buffer[newLength] = '\0'; //////////////////// XXX: TrivialpRelease(pKMutex); } NTSTATUS TrivialRegistryCallback( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 ) { REG_NOTIFY_CLASS Type = (REG_NOTIFY_CLASS) Argument1; PDEVICE_EXTENSION devExtension = (PDEVICE_EXTENSION) CallbackContext; switch( Type ) { case RegNtPreCreateKeyEx: { PREG_CREATE_KEY_INFORMATION pCreate = (PREG_CREATE_KEY_INFORMATION) Argument2; // // Code to handle Pre - CreateKey // } break; case RegNtPreDeleteKey: { PREG_DELETE_KEY_INFORMATION pDelete = (PREG_DELETE_KEY_INFORMATION)Argument2; // // Code to handle NtDeleteKey // } break; case RegNtPreSetValueKey: { PREG_SET_VALUE_KEY_INFORMATION pSetValue = (PREG_SET_VALUE_KEY_INFORMATION)Argument2; // // Code to handle NtSetValueKey // TrivialpLogRegistryCallback(&devExtension->TraceBufferMutex, pSetValue->ValueName->Buffer, pSetValue->ValueName->Length); } break; case RegNtPreDeleteValueKey: { PREG_DELETE_VALUE_KEY_INFORMATION pDeteteValue = (PREG_DELETE_VALUE_KEY_INFORMATION)Argument2; // // Code to handle NtDeleteValueKey // } break; default: // // we are not interested to hook these APIs. Let the call pass through and return STATUS_SUCCESS // break; } return STATUS_SUCCESS; }