mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1269 lines
42 KiB
1269 lines
42 KiB
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
objsup.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the object support routine for the NT I/O system.
|
|
|
|
Author:
|
|
|
|
Darryl E. Havens (darrylh) 30-May-1989
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "iop.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IopCloseFile)
|
|
#pragma alloc_text(PAGE, IopDeleteDriver)
|
|
#pragma alloc_text(PAGE, IopGetSetSecurityObject)
|
|
#endif
|
|
|
|
VOID
|
|
IopCloseFile(
|
|
IN PEPROCESS Process OPTIONAL,
|
|
IN PVOID Object,
|
|
IN ULONG GrantedAccess,
|
|
IN ULONG ProcessHandleCount,
|
|
IN ULONG SystemHandleCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked whenever a handle to a file is deleted. If the
|
|
handle being deleted is the last handle to the file (the ProcessHandleCount
|
|
parameter is one), then all locks for the file owned by the specified
|
|
process must be released.
|
|
|
|
Likewise, if the SystemHandleCount is one then this is the last handle
|
|
for this for file object across all processes. For this case, the file
|
|
system is notified so that it can perform any necessary cleanup on the
|
|
file.
|
|
|
|
Arguments:
|
|
|
|
Process - A pointer to the process that closed the handle.
|
|
|
|
Object - A pointer to the file object that the handle referenced.
|
|
|
|
GrantedAccess - Access that was granted to the object through the handle.
|
|
|
|
ProcessHandleCount - Count of handles outstanding to the object for the
|
|
process specified by the Process argument. If the count is one
|
|
then this is the last handle to this file by that process.
|
|
|
|
SystemHandleCount - Count of handles outstanding to the object for the
|
|
entire system. If the count is one then this is the last handle
|
|
to this file in the system.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PFAST_IO_DISPATCH fastIoDispatch;
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
PFILE_OBJECT fileObject;
|
|
KIRQL irql;
|
|
|
|
UNREFERENCED_PARAMETER( Process );
|
|
UNREFERENCED_PARAMETER( GrantedAccess );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the handle count is not one then this is not the last close of
|
|
// this file for the specified process so there is nothing to do.
|
|
//
|
|
|
|
if (ProcessHandleCount != 1) {
|
|
return;
|
|
}
|
|
|
|
fileObject = (PFILE_OBJECT) Object;
|
|
|
|
if (fileObject->LockOperation && SystemHandleCount != 1) {
|
|
|
|
IO_STATUS_BLOCK localIoStatus;
|
|
|
|
//
|
|
// This is the last handle for the specified process and the process
|
|
// called the NtLockFile or NtUnlockFile system services at least once.
|
|
// Also, this is not the last handle for this file object system-wide
|
|
// so unlock all of the pending locks for this process. Note that
|
|
// this check causes an optimization so that if this is the last
|
|
// system-wide handle to this file object the cleanup code will take
|
|
// care of releasing any locks on the file rather than having to
|
|
// send the file system two different packets to get them shut down.
|
|
|
|
//
|
|
// Get the address of the target device object and the Fast I/O dispatch
|
|
//
|
|
|
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
} else {
|
|
deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
|
|
}
|
|
fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
|
|
|
|
//
|
|
// If this file is open for synchronous I/O, wait until this thread
|
|
// owns it exclusively since there may still be a thread using it.
|
|
// This occurs when a system service owns the file because it owns
|
|
// the semaphore, but the I/O completion code has already dereferenced
|
|
// the file object itself. Without waiting here for the same semaphore
|
|
// there would be a race condition in the service who owns it now. The
|
|
// service needs to be able to access the object w/o it going away after
|
|
// its wait for the file event is satisfied.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
|
|
BOOLEAN interrupted;
|
|
|
|
if (!IopAcquireFastLock( fileObject )) {
|
|
(VOID) IopAcquireFileObjectLock( fileObject,
|
|
KernelMode,
|
|
FALSE,
|
|
&interrupted );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Turbo unlock support. If the fast Io Dispatch specifies a fast lock
|
|
// routine then we'll first try and calling it with the specified lock
|
|
// parameters. If this is all successful then we do not need to do
|
|
// the Irp based unlock all call.
|
|
//
|
|
|
|
if (fastIoDispatch &&
|
|
fastIoDispatch->FastIoUnlockAll &&
|
|
fastIoDispatch->FastIoUnlockAll( fileObject,
|
|
PsGetCurrentProcess(),
|
|
&localIoStatus,
|
|
deviceObject )) {
|
|
|
|
NOTHING;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Initialize the local event that will be used to synchronize access
|
|
// to the driver completing this I/O operation.
|
|
//
|
|
|
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|
|
|
//
|
|
// Reset the event in the file object.
|
|
//
|
|
|
|
KeClearEvent( &fileObject->Event );
|
|
|
|
//
|
|
// Allocate and initialize the I/O Request Packet (IRP) for this
|
|
// operation.
|
|
//
|
|
|
|
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
|
|
if (!irp) {
|
|
irp = IopAllocateIrpMustSucceed( deviceObject->StackSize );
|
|
}
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
irp->RequestorMode = KernelMode;
|
|
|
|
//
|
|
// Fill in the service independent parameters in the IRP.
|
|
//
|
|
|
|
irp->UserEvent = &event;
|
|
irp->UserIosb = &irp->IoStatus;
|
|
irp->Flags = IRP_SYNCHRONOUS_API;
|
|
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. No
|
|
// function-specific parameters are required for this operation.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->MajorFunction = IRP_MJ_LOCK_CONTROL;
|
|
irpSp->MinorFunction = IRP_MN_UNLOCK_ALL;
|
|
irpSp->FileObject = fileObject;
|
|
|
|
//
|
|
// Reference the fileobject again for the IRP (cleared on completion)
|
|
//
|
|
|
|
ObReferenceObject( fileObject );
|
|
|
|
//
|
|
// Insert the packet at the head of the IRP list for the thread.
|
|
//
|
|
|
|
IopQueueThreadIrp( irp );
|
|
|
|
//
|
|
// Invoke the driver at its appropriate dispatch entry with the IRP.
|
|
//
|
|
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
//
|
|
// If no error was incurred, wait for the I/O operation to complete.
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this operation was a synchronous I/O operation, release the
|
|
// semaphore so that the file can be used by other threads.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
IopReleaseFileObjectLock( fileObject );
|
|
}
|
|
}
|
|
|
|
if (SystemHandleCount == 1) {
|
|
|
|
//
|
|
// The last handle to this file object for all of the processes in the
|
|
// system has just been closed, so invoke the driver's "cleanup" handler
|
|
// for this file. This is the file system's opportunity to remove any
|
|
// share access information for the file, to indicate that if the file
|
|
// is opened for a caching operation and this is the last file object
|
|
// to the file, then it can do whatever it needs with memory management
|
|
// to cleanup any information.
|
|
//
|
|
// Begin by getting the address of the target device object.
|
|
//
|
|
|
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
} else {
|
|
deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
|
|
}
|
|
|
|
//
|
|
// Ensure that the I/O system believes that this file has a handle
|
|
// associated with it in case it doesn't actually get one from the
|
|
// Object Manager. This is done because sometimes the Object Manager
|
|
// actually creates a handle, but the I/O system never finds out
|
|
// about it so it attempts to send two cleanups for the same file.
|
|
//
|
|
|
|
fileObject->Flags |= FO_HANDLE_CREATED;
|
|
|
|
//
|
|
// If this file is open for synchronous I/O, wait until this thread
|
|
// owns it exclusively since there may still be a thread using it.
|
|
// This occurs when a system service owns the file because it owns
|
|
// the semaphore, but the I/O completion code has already dereferenced
|
|
// the file object itself. Without waiting here for the same semaphore
|
|
// there would be a race condition in the service who owns it now. The
|
|
// service needs to be able to access the object w/o it going away after
|
|
// its wait for the file event is satisfied.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
|
|
BOOLEAN interrupted;
|
|
|
|
if (!IopAcquireFastLock( fileObject )) {
|
|
(VOID) IopAcquireFileObjectLock( fileObject,
|
|
KernelMode,
|
|
FALSE,
|
|
&interrupted );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the local event that will be used to synchronize access
|
|
// to the driver completing this I/O operation.
|
|
//
|
|
|
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|
|
|
//
|
|
// Reset the event in the file object.
|
|
//
|
|
|
|
KeClearEvent( &fileObject->Event );
|
|
|
|
//
|
|
// Allocate and initialize the I/O Request Packet (IRP) for this
|
|
// operation.
|
|
//
|
|
|
|
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
|
|
if (!irp) {
|
|
irp = IopAllocateIrpMustSucceed( deviceObject->StackSize );
|
|
}
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
irp->RequestorMode = KernelMode;
|
|
|
|
//
|
|
// Fill in the service independent parameters in the IRP.
|
|
//
|
|
|
|
irp->UserEvent = &event;
|
|
irp->UserIosb = &irp->IoStatus;
|
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
|
|
irp->Flags = IRP_SYNCHRONOUS_API | IRP_CLOSE_OPERATION;
|
|
|
|
//
|
|
// Get a pointer to the stack location for the first driver. This will
|
|
// be used to pass the original function codes and parameters. No
|
|
// function-specific parameters are required for this operation.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->MajorFunction = IRP_MJ_CLEANUP;
|
|
irpSp->FileObject = fileObject;
|
|
|
|
//
|
|
// Insert the packet at the head of the IRP list for the thread.
|
|
//
|
|
|
|
IopQueueThreadIrp( irp );
|
|
|
|
//
|
|
// Update the operation count statistic for the current process for
|
|
// operations other than read and write.
|
|
//
|
|
|
|
IopUpdateOtherOperationCount();
|
|
|
|
//
|
|
// Invoke the driver at its appropriate dispatch entry with the IRP.
|
|
//
|
|
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
//
|
|
// If no error was incurred, wait for the I/O operation to complete.
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
}
|
|
|
|
//
|
|
// The following code tears down the IRP by hand since it may not
|
|
// either be possible to it to be completed (because this code was
|
|
// invoked as APC_LEVEL in the first place - or because the reference
|
|
// count on the object cannot be incremented due to this routine
|
|
// being invoked by the delete file procedure below). Cleanup IRPs
|
|
// therefore use close sematics (the close operation flag is set
|
|
// in the IRP) so that the I/O complete request routine itself sets
|
|
// the event to the Signaled state.
|
|
//
|
|
|
|
KeRaiseIrql( APC_LEVEL, &irql );
|
|
IopDequeueThreadIrp( irp );
|
|
KeLowerIrql( irql );
|
|
|
|
//
|
|
// Also, free the IRP.
|
|
//
|
|
|
|
IoFreeIrp( irp );
|
|
|
|
//
|
|
// If this operation was a synchronous I/O operation, release the
|
|
// semaphore so that the file can be used by other threads.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
IopReleaseFileObjectLock( fileObject );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
IopDeleteFile(
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when the last handle to a specific file handle is
|
|
being closed and the file object is going away. It is the responsibility
|
|
of this routine to perform the following functions:
|
|
|
|
o Notify the device driver that the file object is open on that the
|
|
file is being closed.
|
|
|
|
o Dereference the user's error port for the file object, if there
|
|
is one associated with the file object.
|
|
|
|
o Decrement the device object reference count.
|
|
|
|
Arguments:
|
|
|
|
Object - Pointer to the file object being deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_OBJECT deviceObject;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
KIRQL irql;
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
KEVENT event;
|
|
PVPB vpb;
|
|
BOOLEAN referenceCountDecremented;
|
|
|
|
//
|
|
// Obtain a pointer to the file object.
|
|
//
|
|
|
|
fileObject = (PFILE_OBJECT) Object;
|
|
|
|
//
|
|
// Get a pointer to the first device driver which should be notified that
|
|
// this file is going away. If the device driver field is NULL, then this
|
|
// file is being shut down due to an error attempting to get it open in the
|
|
// first place, so do not do any further processing.
|
|
//
|
|
|
|
if (fileObject->DeviceObject) {
|
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
} else {
|
|
deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
|
|
}
|
|
|
|
//
|
|
// If this file has never had a file handle created for it, and yet
|
|
// it exists, invoke the close file procedure so that the file system
|
|
// gets the cleanup IRP it is expecting before sending the close IRP.
|
|
//
|
|
|
|
if (!(fileObject->Flags & FO_HANDLE_CREATED)) {
|
|
IopCloseFile( (PEPROCESS) NULL,
|
|
Object,
|
|
0,
|
|
1,
|
|
1 );
|
|
}
|
|
|
|
//
|
|
// If this file is open for synchronous I/O, wait until this thread
|
|
// owns it exclusively since there may still be a thread using it.
|
|
// This occurs when a system service owns the file because it owns
|
|
// the semaphore, but the I/O completion code has already dereferenced
|
|
// the file object itself. Without waiting here for the same semaphore
|
|
// there would be a race condition in the service who owns it now. The
|
|
// service needs to be able to access the object w/o it going away after
|
|
// its wait for the file event is satisfied.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
|
|
BOOLEAN interrupted;
|
|
|
|
if (!IopAcquireFastLock( fileObject )) {
|
|
(VOID) IopAcquireFileObjectLock( fileObject,
|
|
KernelMode,
|
|
FALSE,
|
|
&interrupted );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset a local event that can be used to wait for the device driver
|
|
// to close the file.
|
|
//
|
|
|
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|
|
|
//
|
|
// Reset the event in the file object.
|
|
//
|
|
|
|
KeClearEvent( &fileObject->Event );
|
|
|
|
//
|
|
// Allocate an I/O Request Packet (IRP) to be used in communicating with
|
|
// the appropriate device driver that the file is being closed. Notice
|
|
// that the allocation of this packet is done without charging quota so
|
|
// that the operation will not fail. This is done because there is no
|
|
// way to return an error to the caller at this point.
|
|
//
|
|
|
|
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
|
|
if (!irp) {
|
|
irp = IopAllocateIrpMustSucceed( deviceObject->StackSize );
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the stack location for the first driver. This is
|
|
// where the function codes and parameters are placed.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
//
|
|
// Fill in the IRP, indicating that this file object is being deleted.
|
|
//
|
|
|
|
irpSp->MajorFunction = IRP_MJ_CLOSE;
|
|
irpSp->FileObject = fileObject;
|
|
irp->UserIosb = &ioStatusBlock;
|
|
irp->UserEvent = &event;
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
|
|
irp->Flags = IRP_CLOSE_OPERATION | IRP_SYNCHRONOUS_API;
|
|
|
|
//
|
|
// Place this packet in the thread's I/O pending queue.
|
|
//
|
|
|
|
IopQueueThreadIrp( irp );
|
|
|
|
//
|
|
// Decrement the reference count on the VPB, if necessary. We
|
|
// have to do this BEFORE handing the Irp to the file system
|
|
// because of a trick the file systems play with close, and
|
|
// believe me, you really don't want to know what it is.
|
|
//
|
|
// Since there is not a error path here (close cannot fail),
|
|
// and the file system is the only ome who can actually synchronize
|
|
// with the actual completion of close processing, the file system
|
|
// is the one responsible for Vpb deletion.
|
|
//
|
|
|
|
vpb = fileObject->Vpb;
|
|
|
|
if (vpb && !(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|
ExInterlockedAddUlong( &vpb->ReferenceCount,
|
|
0xffffffff,
|
|
&IopVpbSpinLock );
|
|
}
|
|
|
|
//
|
|
// If this device object has stated for a fact that it knows it will
|
|
// never have the final non-zero reference count among the other
|
|
// device objects associated with our driver object, then decrement
|
|
// our reference count here BEFORE calling the file system. This
|
|
// is required because for a special class of device objects, the
|
|
// file system may delete them.
|
|
//
|
|
|
|
if (fileObject->DeviceObject->Flags & DO_NEVER_LAST_DEVICE) {
|
|
ExInterlockedAddUlong( &fileObject->DeviceObject->ReferenceCount,
|
|
0xffffffff,
|
|
&IopDatabaseLock );
|
|
|
|
referenceCountDecremented = TRUE;
|
|
} else {
|
|
referenceCountDecremented = FALSE;
|
|
}
|
|
|
|
//
|
|
// Give the device driver the packet. If this request does not work,
|
|
// there is nothing that can be done about it. This is unfortunate
|
|
// because the driver may have had problems that it was about to
|
|
// report about other operations (e.g., write behind failures, etc.)
|
|
// that it can no longer report. The reason is that this routine
|
|
// is really initially invoked by NtClose, which has already closed
|
|
// the caller's handle, and that's what the return status from close
|
|
// indicates: the handle has successfully been closed.
|
|
//
|
|
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
}
|
|
|
|
//
|
|
// Perform any completion operations that need to be performed on
|
|
// the IRP that was used for this request. This is done here as
|
|
// as opposed to in normal completion code because there is a race
|
|
// condition between when this routine executes if it was invoked
|
|
// from a special kernel APC (e.g., some IRP was just completed and
|
|
// dereferenced this file object for the last time), and when the
|
|
// special kernel APC because of this packet's completion executing.
|
|
//
|
|
// This problem is solved by not having to queue a special kernel
|
|
// APC routine for completion of this packet. Rather, it is treated
|
|
// much like a synchronous paging I/O operation, except that the
|
|
// packet is not even freed during I/O completion. This is because
|
|
// the packet is still in this thread's queue, and there is no way
|
|
// to get it out except at APC_LEVEL. Unfortunately, the part of
|
|
// I/O completion that needs to dequeue the packet is running at
|
|
// DISPATCH_LEVEL.
|
|
//
|
|
// Hence, the packet must be removed from the queue (synchronized,
|
|
// of course), and then it must be freed.
|
|
//
|
|
|
|
KeRaiseIrql( APC_LEVEL, &irql );
|
|
IopDequeueThreadIrp( irp );
|
|
KeLowerIrql( irql );
|
|
|
|
IoFreeIrp( irp );
|
|
|
|
//
|
|
// Free the file name string buffer if there was one.
|
|
//
|
|
|
|
if (fileObject->FileName.Length != 0) {
|
|
ExFreePool( fileObject->FileName.Buffer );
|
|
}
|
|
|
|
//
|
|
// If there was an completion port associated w/this file object, dereference
|
|
// it now, and deallocate the completion context pool.
|
|
//
|
|
|
|
if (fileObject->CompletionContext) {
|
|
ObDereferenceObject( fileObject->CompletionContext->Port );
|
|
ExFreePool( fileObject->CompletionContext );
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the real device object so its reference count
|
|
// can be decremented.
|
|
//
|
|
|
|
deviceObject = fileObject->DeviceObject;
|
|
|
|
//
|
|
// Decrement the reference count on the device object. Note that
|
|
// if the driver has been marked for an unload operation, and the
|
|
// reference count goes to zero, then the driver may need to be
|
|
// unloaded at this point.
|
|
//
|
|
// Note: only do this if the reference count was not already done
|
|
// above. The device object may be gone in this case.
|
|
//
|
|
|
|
if (!referenceCountDecremented) {
|
|
IopDecrementDeviceObjectRef( deviceObject, FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IopDeleteDriver(
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when the reference count for a driver object
|
|
becomes zero. That is, the last reference for the driver has gone away.
|
|
This routine ensures that the object is cleaned up.
|
|
|
|
Arguments:
|
|
|
|
Object - Pointer to the driver object whose reference count has gone
|
|
to zero.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) Object;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( !driverObject->DeviceObject );
|
|
|
|
//
|
|
// Free the pool associated with the name of the driver.
|
|
//
|
|
|
|
if (driverObject->DriverName.Buffer) {
|
|
ExFreePool( driverObject->DriverName.Buffer );
|
|
}
|
|
#if _PNP_POWER_
|
|
|
|
//
|
|
// When the driver object is deleted. Delete the NtDevicePaths of the driver.
|
|
//
|
|
|
|
if (driverObject->DriverExtension->ServiceKeyName.Length != 0) {
|
|
HANDLE enumHandle;
|
|
UNICODE_STRING unicodeName;
|
|
|
|
if (NT_SUCCESS(IopOpenServiceEnumKeys(
|
|
&driverObject->DriverExtension->ServiceKeyName,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&enumHandle,
|
|
FALSE
|
|
))) {
|
|
|
|
//
|
|
// Remove the NeDevicePaths under service enum key.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NTDEVICEPATHS);
|
|
ZwDeleteValueKey(enumHandle, &unicodeName);
|
|
ZwClose(enumHandle);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Free the pool associated with the service key name of the driver.
|
|
//
|
|
|
|
if (driverObject->DriverExtension->ServiceKeyName.Buffer) {
|
|
ExFreePool( driverObject->DriverExtension->ServiceKeyName.Buffer );
|
|
}
|
|
}
|
|
|
|
#ifdef _PNP_POWER_
|
|
VOID
|
|
IopDeleteDevice(
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when the reference count for a device object
|
|
becomes zero. That is, the last reference for the device has gone away.
|
|
This routine ensures that the object is cleaned up.
|
|
|
|
Arguments:
|
|
|
|
Object - Pointer to the driver object whose reference count has gone
|
|
to zero.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT deviceObject = (PDEVICE_OBJECT) Object;
|
|
|
|
PAGED_CODE();
|
|
|
|
PoRunDownDeviceObject (deviceObject);
|
|
}
|
|
#endif // _PNP_POWER_
|
|
|
|
NTSTATUS
|
|
IopGetSetSecurityObject(
|
|
IN PVOID Object,
|
|
IN SECURITY_OPERATION_CODE OperationCode,
|
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN OUT PULONG CapturedLength,
|
|
IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
|
|
IN POOL_TYPE PoolType,
|
|
IN PGENERIC_MAPPING GenericMapping
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to either query or set the security descriptor
|
|
for a file, directory, volume, or device. It implements these functions
|
|
by either performing an in-line check if the file is a device or a
|
|
volume, or an I/O Request Packet (IRP) is generated and given to the
|
|
driver to perform the operation.
|
|
|
|
Arguments:
|
|
|
|
Object - Pointer to the file or device object representing the open object.
|
|
|
|
SecurityInformation - Information about what is being done to or obtained
|
|
from the object's security descriptor.
|
|
|
|
SecurityDescriptor - Supplies the base security descriptor and returns
|
|
the final security descriptor. Note that if this buffer is coming
|
|
from user space, it has already been probed by the object manager
|
|
to length "CapturedLength", otherwise it points to kernel space and
|
|
should not be probed. It must, however, be referenced in a try
|
|
clause.
|
|
|
|
CapturedLength - For a query operation this specifies the size, in
|
|
bytes, of the output security descriptor buffer and on return
|
|
contains the number of bytes needed to store the complete security
|
|
descriptor. If the length needed is greater than the length
|
|
supplied the operation will fail. This parameter is ignored for
|
|
the set and delete operations. It is expected to point into
|
|
system space, ie, it need not be probed and it will not change.
|
|
|
|
ObjectsSecurityDescriptor - Supplies and returns the object's security
|
|
descriptor.
|
|
|
|
PoolType - Specifies from which type of pool memory is to be allocated.
|
|
|
|
GenericMapping - Supplies the generic mapping for the object type.
|
|
|
|
Return Value:
|
|
|
|
The final status of the operation is returned as the function value.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
BOOLEAN synchronousIo;
|
|
|
|
UNREFERENCED_PARAMETER( ObjectsSecurityDescriptor );
|
|
UNREFERENCED_PARAMETER( PoolType );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Begin by determining whether the security operation is to be performed
|
|
// in this routine or by the driver. This is based upon whether the
|
|
// object represents a device object, or it represents a file object
|
|
// to a device, or a file on the device.
|
|
//
|
|
|
|
if (((PDEVICE_OBJECT) (Object))->Type == IO_TYPE_DEVICE) {
|
|
deviceObject = (PDEVICE_OBJECT) Object;
|
|
fileObject = (PFILE_OBJECT) NULL;
|
|
} else {
|
|
fileObject = (PFILE_OBJECT) Object;
|
|
deviceObject = fileObject->DeviceObject;
|
|
}
|
|
|
|
if (!fileObject ||
|
|
(!fileObject->FileName.Length && !fileObject->RelatedFileObject)) {
|
|
|
|
//
|
|
// This security operation is for the device itself, either through
|
|
// a file object, or directly to the device object. For the latter
|
|
// case, assignment operations are also possible. Also note that
|
|
// this may be a stream file object, which do not have security.
|
|
// The security for a stream file is actually represented by the
|
|
// security descriptor on the file itself, or the volume, or the
|
|
// device.
|
|
//
|
|
|
|
if (OperationCode == AssignSecurityDescriptor) {
|
|
|
|
//
|
|
// Simply assign the security descriptor to the device object,
|
|
// if this is a device object.
|
|
//
|
|
|
|
if (fileObject == NULL || !(fileObject->Flags & FO_STREAM_FILE)) {
|
|
deviceObject->SecurityDescriptor = SecurityDescriptor;
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else if (OperationCode == SetSecurityDescriptor) {
|
|
|
|
PSECURITY_DESCRIPTOR OldSecurityDescriptor;
|
|
|
|
//
|
|
// This is a set operation. The SecurityInformation parameter
|
|
// determines what part of the SecurityDescriptor is going to
|
|
// be applied to the ObjectsSecurityDescriptor.
|
|
//
|
|
|
|
OldSecurityDescriptor = deviceObject->SecurityDescriptor;
|
|
status = SeSetSecurityDescriptorInfo( NULL,
|
|
SecurityInformation,
|
|
SecurityDescriptor,
|
|
&deviceObject->SecurityDescriptor,
|
|
PoolType,
|
|
GenericMapping );
|
|
|
|
if (NT_SUCCESS( status )) {
|
|
ExFreePool( OldSecurityDescriptor );
|
|
}
|
|
|
|
} else if (OperationCode == QuerySecurityDescriptor) {
|
|
|
|
//
|
|
// This is a get operation. The SecurityInformation parameter
|
|
// determines what part of the SecurityDescriptor is going to
|
|
// be returned from the ObjectsSecurityDescriptor.
|
|
//
|
|
|
|
status = SeQuerySecurityDescriptorInfo( SecurityInformation,
|
|
SecurityDescriptor,
|
|
CapturedLength,
|
|
&deviceObject->SecurityDescriptor );
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a delete operation. Simply indicate that everything
|
|
// worked just fine.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
} else if (OperationCode == DeleteSecurityDescriptor) {
|
|
|
|
//
|
|
// This is a delete operation for the security descriptor on a file
|
|
// object. This function will be performed by the file system once
|
|
// the FCB itself is deleted. Simply indicate that the operation
|
|
// was successful.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK localIoStatus;
|
|
KEVENT event;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KPROCESSOR_MODE requestorMode;
|
|
|
|
//
|
|
// This file object does not refer to the device itself. Rather, it
|
|
// refers to either a file or a directory on the device. This means
|
|
// that the request must be passed to the file system for processing.
|
|
// Note that the only requests that are passed through in this manner
|
|
// are SET or QUERY security operations. DELETE operations have
|
|
// already been taken care of above since the file system which just
|
|
// drop the storage on the floor when it really needs to, and ASSIGN
|
|
// operations are irrelevant to file systems since they never
|
|
// generate one because they never assign the security descriptor
|
|
// to the object in the first place, they just assign it to the FCB.
|
|
//
|
|
|
|
requestorMode = KeGetPreviousMode();
|
|
|
|
//
|
|
// Begin by referencing the object by pointer. Note that the object
|
|
// handle has already been checked for the appropriate access by the
|
|
// object system caller. This reference must be performed because
|
|
// standard I/O completion will dereference the 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,
|
|
requestorMode,
|
|
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
|
&interrupted );
|
|
if (interrupted) {
|
|
ObDereferenceObject( fileObject );
|
|
return status;
|
|
}
|
|
}
|
|
synchronousIo = TRUE;
|
|
} else {
|
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|
synchronousIo = FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the file object to the Not-Signaled state.
|
|
//
|
|
|
|
KeClearEvent( &fileObject->Event );
|
|
|
|
//
|
|
// 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 = requestorMode;
|
|
|
|
//
|
|
// Fill in the service independent parameters in the IRP.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
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 );
|
|
|
|
//
|
|
// Now determine whether this is a set or a query operation.
|
|
//
|
|
|
|
if (OperationCode == QuerySecurityDescriptor) {
|
|
|
|
//
|
|
// This is a query operation. Fill in the appropriate fields in
|
|
// the stack location for the packet, as well as the fixed part
|
|
// of the packet. Note that each of these parameters has been
|
|
// captured as well, so there is no need to perform any probing.
|
|
// The only exception is the UserBuffer memory may change, but
|
|
// that is the file system's responsibility to check. Note that
|
|
// it has already been probed, so the pointer is at least not
|
|
// in an address space that the caller should not be accessing
|
|
// because of mode.
|
|
//
|
|
|
|
irpSp->MajorFunction = IRP_MJ_QUERY_SECURITY;
|
|
irpSp->Parameters.QuerySecurity.SecurityInformation = *SecurityInformation;
|
|
irpSp->Parameters.QuerySecurity.Length = *CapturedLength;
|
|
irp->UserBuffer = SecurityDescriptor;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a set operation. Fill in the appropriate fields in
|
|
// the stack location for the packet. Note that access to the
|
|
// SecurityInformation parameter is safe, as the parameter was
|
|
// captured by the caller. Likewise, the SecurityDescriptor
|
|
// refers to a captured copy of the descriptor.
|
|
//
|
|
|
|
irpSp->MajorFunction = IRP_MJ_SET_SECURITY;
|
|
irpSp->Parameters.SetSecurity.SecurityInformation = *SecurityInformation;
|
|
irpSp->Parameters.SetSecurity.SecurityDescriptor = SecurityDescriptor;
|
|
|
|
}
|
|
|
|
irpSp->FileObject = fileObject;
|
|
|
|
//
|
|
// Insert the packet at the head of the IRP list for the thread.
|
|
//
|
|
|
|
IopQueueThreadIrp( irp );
|
|
|
|
//
|
|
// Update the operation count statistic for the current process for
|
|
// operations other than read and write.
|
|
//
|
|
|
|
IopUpdateOtherOperationCount();
|
|
|
|
//
|
|
// Everything has been properly set up, so simply invoke the driver.
|
|
//
|
|
|
|
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 be
|
|
// completed and obtain the final status from the file object itself.
|
|
//
|
|
|
|
if (synchronousIo) {
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &fileObject->Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
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 return the final status information
|
|
// back to the caller.
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
status = localIoStatus.Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this operation was just attempted on a file system or a device
|
|
// driver of some kind that does not implement security, then return
|
|
// a normal null security descriptor.
|
|
//
|
|
|
|
if (status == STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
//
|
|
// The file system does not implement a security policy. Determine
|
|
// what type of operation this was and implement the correct
|
|
// semantics for the file system.
|
|
//
|
|
|
|
if (OperationCode == QuerySecurityDescriptor) {
|
|
|
|
//
|
|
// The operation is a query. If the caller's buffer is too
|
|
// small, then indicate that this is the case and let him know
|
|
// what size buffer is required. Otherwise, attempt to return
|
|
// a null security descriptor.
|
|
//
|
|
|
|
try {
|
|
status = SeAssignWorldSecurityDescriptor(
|
|
SecurityDescriptor,
|
|
CapturedLength,
|
|
SecurityInformation
|
|
);
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
//
|
|
// An exception was incurred while attempting to
|
|
// access the caller's buffer. Clean everything
|
|
// up and return an appropriate status code.
|
|
//
|
|
|
|
status = GetExceptionCode();
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This was an operation other than a query. Simply indicate
|
|
// that the operation was successful.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else if (OperationCode == QuerySecurityDescriptor) {
|
|
|
|
//
|
|
// The final return status from the file system was something
|
|
// other than invalid device request. This means that the file
|
|
// system actually implemented the query. Copy the size of the
|
|
// returned data, or the size of the buffer required in order
|
|
// to query the security descriptor. Note that once again the
|
|
// assignment is performed inside of an exception handler in case
|
|
// the caller's buffer is inaccessible. Also note that in order
|
|
// for the Information field of the I/O status block to be set,
|
|
// the file system must return a warning status. Return the
|
|
// status that the caller expects if the buffer really is too
|
|
// small.
|
|
//
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
try {
|
|
|
|
*CapturedLength = localIoStatus.Information;
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
status = GetExceptionCode();
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|