Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1096 lines
30 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dir.c
Abstract:
This module implements the file directory routines for the mailslot
file system by the dispatch driver.
Author:
Manny Weiser (mannyw) 1-Feb-1991
Revision History:
--*/
#include "mailslot.h"
//
// Local debug trace level
//
#define Dbg (DEBUG_TRACE_DIR)
NTSTATUS
MsCommonDirectoryControl (
IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
IN PIRP Irp
);
NTSTATUS
MsQueryDirectory (
IN PROOT_DCB RootDcb,
IN PROOT_DCB_CCB Ccb,
IN PIRP Irp
);
NTSTATUS
MsNotifyChangeDirectory (
IN PROOT_DCB RootDcb,
IN PROOT_DCB_CCB Ccb,
IN PIRP Irp
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, MsCommonDirectoryControl )
#pragma alloc_text( PAGE, MsFsdDirectoryControl )
#pragma alloc_text( PAGE, MsQueryDirectory )
#endif
NTSTATUS
MsFsdDirectoryControl (
IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the FSD routine that handles directory control
functions (i.e., query and notify).
Arguments:
MsfsDeviceObject - Supplies the device object for the directory function.
Irp - Supplies the IRP to process.
Return Value:
NTSTATUS - The result status.
--*/
{
NTSTATUS status;
PAGED_CODE();
DebugTrace(+1, Dbg, "MsFsdDirectoryControl\n", 0);
//
// Call the common direcotry control routine.
//
FsRtlEnterFileSystem();
status = MsCommonDirectoryControl( MsfsDeviceObject, Irp );
FsRtlExitFileSystem();
//
// Return to the caller.
//
DebugTrace(-1, Dbg, "MsFsdDirectoryControl -> %08lx\n", status );
return status;
}
VOID
MsFlushNotifyForFile (
IN PDCB Dcb,
IN PFILE_OBJECT FileObject
)
/*++
Routine Description:
This routine checks the notify queues of a DCB and completes any
outstanding IRPS that match the given file object. This is used at cleanup time.
Arguments:
Dcb - Supplies the DCB to check for outstanding notify IRPs.
FileObject - File object that IRP must be associated with.
Return Value:
None.
--*/
{
PLIST_ENTRY Links;
PIRP Irp;
KIRQL OldIrql;
PLIST_ENTRY Head;
PIO_STACK_LOCATION IrpSp;
LIST_ENTRY CompletionList;
Head = &Dcb->Specific.Dcb.NotifyFullQueue;
InitializeListHead (&CompletionList);
KeAcquireSpinLock (&Dcb->Specific.Dcb.SpinLock, &OldIrql);
Links = Head->Flink;
while (1) {
if (Links == Head) {
//
// We are at the end of this queue.
//
if (Head == &Dcb->Specific.Dcb.NotifyFullQueue) {
Head = &Dcb->Specific.Dcb.NotifyPartialQueue;
Links = Head->Flink;
if (Links == Head) {
break;
}
} else {
break;
}
}
Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// If this IRP is for the matching file object then remove and save for completion
//
if (IrpSp->FileObject == FileObject) {
Links = Links->Flink;
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
//
// Remove cancel routine and detect if its already started running
//
if (IoSetCancelRoutine (Irp, NULL)) {
//
// Cancel isn't active and won't become active.
//
InsertTailList (&CompletionList, &Irp->Tail.Overlay.ListEntry);
} else {
//
// Cancel is already active but is stalled before lock acquire. Initialize the
// list head so the second remove is a noop. This is a rare case.
//
InitializeListHead (&Irp->Tail.Overlay.ListEntry);
}
} else {
Links = Links->Flink;
}
}
KeReleaseSpinLock (&Dcb->Specific.Dcb.SpinLock, OldIrql);
while (!IsListEmpty (&CompletionList)) {
Links = RemoveHeadList (&CompletionList);
Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
MsCompleteRequest( Irp, STATUS_CANCELLED );
}
return;
}
VOID
MsCheckForNotify (
IN PDCB Dcb,
IN BOOLEAN CheckAllOutstandingIrps,
IN NTSTATUS FinalStatus
)
/*++
Routine Description:
This routine checks the notify queues of a DCB and completes any
outstanding IRPS.
Note that the caller of this procedure must guarantee that the DCB
is acquired for exclusive access.
Arguments:
Dcb - Supplies the DCB to check for outstanding notify IRPs.
CheckAllOutstandingIrps - Indicates if only the NotifyFullQueue should be
checked. If TRUE then all notify queues are checked, and if FALSE
then only the NotifyFullQueue is checked.
Return Value:
None.
--*/
{
PLIST_ENTRY links;
PIRP irp;
KIRQL OldIrql;
PLIST_ENTRY Head;
//
// We'll always signal the notify full queue entries. They want
// to be notified if every any change is made to a directory.
//
Head = &Dcb->Specific.Dcb.NotifyFullQueue;
KeAcquireSpinLock (&Dcb->Specific.Dcb.SpinLock, &OldIrql);
while (1) {
links = RemoveHeadList (Head);
if (links == Head) {
//
// This queue is empty. See if we need to skip to another.
//
if (Head == &Dcb->Specific.Dcb.NotifyFullQueue && CheckAllOutstandingIrps) {
Head = &Dcb->Specific.Dcb.NotifyPartialQueue;
links = RemoveHeadList (Head);
if (links == Head) {
break;
}
} else {
break;
}
}
//
// Remove the Irp from the head of the queue, and complete it
// with a success status.
//
irp = CONTAINING_RECORD( links, IRP, Tail.Overlay.ListEntry );
//
// Remove cancel routine and detect if its already started running
//
if (IoSetCancelRoutine (irp, NULL)) {
//
// Cancel isn't active and won't become active. Release the spinlock for the complete.
//
KeReleaseSpinLock (&Dcb->Specific.Dcb.SpinLock, OldIrql);
MsCompleteRequest( irp, FinalStatus );
KeAcquireSpinLock (&Dcb->Specific.Dcb.SpinLock, &OldIrql);
} else {
//
// Cancel is already active but is stalled before lock acquire. Initialize the
// list head so the second remove is a noop. This is a rare case.
//
InitializeListHead (&irp->Tail.Overlay.ListEntry);
}
}
KeReleaseSpinLock (&Dcb->Specific.Dcb.SpinLock, OldIrql);
return;
}
NTSTATUS
MsCommonDirectoryControl (
IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does the common code for directory control functions.
Arguments:
MsfsDeviceObject - Supplies the mailslot device object.
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
NODE_TYPE_CODE nodeTypeCode;
PROOT_DCB_CCB ccb;
PROOT_DCB rootDcb;
PAGED_CODE();
//
// Get the current stack location
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "CommonDirectoryControl...\n", 0);
DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
//
// Decode the file object to figure out who we are. If the result
// is not the root DCB then its an illegal parameter.
//
if ((nodeTypeCode = MsDecodeFileObject( irpSp->FileObject,
(PVOID *)&rootDcb,
(PVOID *)&ccb )) == NTC_UNDEFINED) {
DebugTrace(0, Dbg, "Not a directory\n", 0);
MsCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
status = STATUS_INVALID_PARAMETER;
DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
return status;
}
if (nodeTypeCode != MSFS_NTC_ROOT_DCB) {
DebugTrace(0, Dbg, "Not a directory\n", 0);
MsDereferenceNode( &rootDcb->Header );
MsCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
status = STATUS_INVALID_PARAMETER;
DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
return status;
}
//
// Acquire exclusive access to the root DCB.
//
MsAcquireExclusiveFcb( (PFCB)rootDcb );
//
// Check if its been cleaned up yet.
//
status = MsVerifyDcbCcb (ccb);
if (NT_SUCCESS (status)) {
//
// We know this is a directory control so we'll case on the
// minor function, and call the appropriate work routines.
//
switch (irpSp->MinorFunction) {
case IRP_MN_QUERY_DIRECTORY:
status = MsQueryDirectory( rootDcb, ccb, Irp );
break;
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
status = MsNotifyChangeDirectory( rootDcb, ccb, Irp );
break;
default:
//
// For all other minor function codes we say they're invalid
// and complete the request.
//
DebugTrace(0, DEBUG_TRACE_ERROR, "Invalid FS Control Minor Function Code %08lx\n", irpSp->MinorFunction);
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
MsReleaseFcb( (PFCB)rootDcb );
MsDereferenceRootDcb( rootDcb );
if (status != STATUS_PENDING) {
MsCompleteRequest( Irp, status );
}
return status;
}
NTSTATUS
MsQueryDirectory (
IN PROOT_DCB RootDcb,
IN PROOT_DCB_CCB Ccb,
IN PIRP Irp
)
/*++
Routine Description:
This is the work routine for querying a directory.
Arugments:
RootDcb - Supplies the dcb being queried
Ccb - Supplies the context of the caller
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The return status for the operation.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
PUCHAR buffer;
CLONG systemBufferLength;
UNICODE_STRING fileName;
ULONG fileIndex;
FILE_INFORMATION_CLASS fileInformationClass;
BOOLEAN restartScan;
BOOLEAN returnSingleEntry;
BOOLEAN indexSpecified;
#if 0
UNICODE_STRING unicodeString;
ULONG unicodeStringLength;
#endif
BOOLEAN ansiStringAllocated = FALSE;
static WCHAR star = L'*';
BOOLEAN caseInsensitive = TRUE; //*** Make searches case insensitive
ULONG currentIndex;
ULONG lastEntry;
ULONG nextEntry;
PLIST_ENTRY links;
PFCB fcb;
PFILE_DIRECTORY_INFORMATION dirInfo;
PFILE_NAMES_INFORMATION namesInfo;
PAGED_CODE();
//
// Get the current stack location.
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "MsQueryDirectory\n", 0 );
DebugTrace( 0, Dbg, "RootDcb = %08lx\n", (ULONG)RootDcb);
DebugTrace( 0, Dbg, "Ccb = %08lx\n", (ULONG)Ccb);
DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length);
DebugTrace( 0, Dbg, "FileName = %08lx\n", (ULONG)irpSp->Parameters.QueryDirectory.FileName);
DebugTrace( 0, Dbg, "FileIndex = %08lx\n", irpSp->Parameters.QueryDirectory.FileIndex);
DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", irpSp->Parameters.QueryDirectory.FileInformationClass);
DebugTrace( 0, Dbg, "RestartScan = %08lx\n", FlagOn(irpSp->Flags, SL_RESTART_SCAN));
DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", FlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY));
DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", FlagOn(irpSp->Flags, SL_INDEX_SPECIFIED));
//
// Make local copies of the input parameters.
//
systemBufferLength = irpSp->Parameters.QueryDirectory.Length;
fileIndex = irpSp->Parameters.QueryDirectory.FileIndex;
fileInformationClass =
irpSp->Parameters.QueryDirectory.FileInformationClass;
restartScan = FlagOn(irpSp->Flags, SL_RESTART_SCAN);
indexSpecified = FlagOn(irpSp->Flags, SL_INDEX_SPECIFIED);
returnSingleEntry = FlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY);
if (irpSp->Parameters.QueryDirectory.FileName != NULL) {
fileName = *(irpSp->Parameters.QueryDirectory.FileName);
//
// Ensure that the name is reasonable
//
if( (fileName.Buffer == NULL && fileName.Length) ||
FlagOn( fileName.Length, 1 ) ) {
status = STATUS_OBJECT_NAME_INVALID;
return status;
}
} else {
fileName.Length = 0;
fileName.Buffer = NULL;
}
//
// Check if the CCB already has a query template attached. If it
// does not already have one then we either use the string we are
// given or we attach our own containing "*"
//
if (Ccb->QueryTemplate == NULL) {
//
// This is our first time calling query directory so we need
// to either set the query template to the user specified string
// or to "*".
//
if (fileName.Buffer == NULL) {
DebugTrace(0, Dbg, "Set template to *\n", 0);
fileName.Length = sizeof( WCHAR );
fileName.Buffer = ☆
}
DebugTrace(0, Dbg, "Set query template -> %wZ\n", (ULONG)&fileName);
//
// Allocate space for the query template.
//
Ccb->QueryTemplate = MsAllocatePagedPoolWithQuota ( sizeof(UNICODE_STRING) + fileName.Length,
'tFsM' );
if (Ccb->QueryTemplate == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
return status;
}
//
// Initialize the query template and copy over the string.
//
Ccb->QueryTemplate->Length = fileName.Length;
Ccb->QueryTemplate->Buffer = (PWCH)((PSZ)Ccb->QueryTemplate + sizeof(UNICODE_STRING));
RtlCopyMemory (Ccb->QueryTemplate->Buffer,
fileName.Buffer,
fileName.Length);
//
// Set the search to start at the beginning of the directory.
//
fileIndex = 0;
} else {
//
// Check if we were given an index to start with or if we need to
// restart the scan or if we should use the index that was saved in
// the CCB.
//
if (restartScan) {
fileIndex = 0;
} else if (!indexSpecified) {
fileIndex = Ccb->IndexOfLastCcbReturned + 1;
}
}
//
// Now we are committed to completing the Irp, we do that in
// the finally clause of the following try.
//
try {
ULONG baseLength;
ULONG lengthAdded;
BOOLEAN Match;
//
// Map the user buffer.
//
MsMapUserBuffer( Irp, KernelMode, (PVOID *)&buffer );
//
// At this point we are about to enter our query loop. We have
// already decided which Fcb index we need to return. The variables
// LastEntry and NextEntry are used to index into the user buffer.
// LastEntry is the last entry we added to the user buffer, and
// NextEntry is the current one we're working on. CurrentIndex
// is the Fcb index that we are looking at next. Logically the
// way the loop works is as follows.
//
// Scan all of the Fcb in the directory
//
// if the Fcb matches the query template then
//
// if the CurrentIndex is >= the FileIndex then
//
// process this fcb, and decide if we should
// continue the main loop
//
// end if
//
// Increment the current index
//
// end if
//
// end scan
//
currentIndex = 0;
lastEntry = 0;
nextEntry =0;
switch (fileInformationClass) {
case FileDirectoryInformation:
baseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
FileName[0] );
break;
case FileFullDirectoryInformation:
baseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
FileName[0] );
break;
case FileNamesInformation:
baseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
FileName[0] );
break;
case FileBothDirectoryInformation:
baseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
FileName[0] );
break;
default:
try_return( status = STATUS_INVALID_INFO_CLASS );
}
for (links = RootDcb->Specific.Dcb.ParentDcbQueue.Flink;
links != &RootDcb->Specific.Dcb.ParentDcbQueue;
links = links->Flink) {
fcb = CONTAINING_RECORD(links, FCB, ParentDcbLinks);
ASSERT(fcb->Header.NodeTypeCode == MSFS_NTC_FCB);
DebugTrace(0, Dbg, "Top of Loop\n", 0);
DebugTrace(0, Dbg, "Fcb = %08lx\n", (ULONG)fcb);
DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", currentIndex);
DebugTrace(0, Dbg, "FileIndex = %08lx\n", fileIndex);
DebugTrace(0, Dbg, "LastEntry = %08lx\n", lastEntry);
DebugTrace(0, Dbg, "NextEntry = %08lx\n", nextEntry);
//
// Check if the Fcb represents a mailslot that is part of
// our query template.
//
try {
Match = FsRtlIsNameInExpression( Ccb->QueryTemplate,
&fcb->LastFileName,
caseInsensitive,
NULL );
} except (EXCEPTION_EXECUTE_HANDLER) {
try_return (status = GetExceptionCode ());
}
if (Match) {
//
// The FCB is in the query template so now check if
// this is the index we should start returning.
//
if (currentIndex >= fileIndex) {
//
// Yes it is one to return so case on the requested
// information class.
//
ULONG bytesToCopy;
ULONG bytesRemainingInBuffer;
//
// Here are the rules concerning filling up the buffer:
//
// 1. The Io system garentees that there will always be
// enough room for at least one base record.
//
// 2. If the full first record (including file name) cannot
// fit, as much of the name as possible is copied and
// STATUS_BUFFER_OVERFLOW is returned.
//
// 3. If a subsequent record cannot completely fit into the
// buffer, none of it (as in 0 bytes) is copied, and
// STATUS_SUCCESS is returned. A subsequent query will
// pick up with this record.
//
bytesRemainingInBuffer = systemBufferLength - nextEntry;
if ( (nextEntry != 0) &&
( (baseLength + fcb->LastFileName.Length >
bytesRemainingInBuffer) ||
(systemBufferLength < nextEntry) ) ) {
DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
try_return( status = STATUS_SUCCESS );
}
ASSERT( bytesRemainingInBuffer >= baseLength );
//
// See how much of the name we will be able to copy into
// the system buffer. This also dictates out return
// value.
//
if ( baseLength + fcb->LastFileName.Length <=
bytesRemainingInBuffer ) {
bytesToCopy = fcb->LastFileName.Length;
status = STATUS_SUCCESS;
} else {
bytesToCopy = bytesRemainingInBuffer - baseLength;
status = STATUS_BUFFER_OVERFLOW;
}
//
// Note how much of buffer we are consuming and zero
// the base part of the structure.
//
lengthAdded = baseLength + bytesToCopy;
try {
RtlZeroMemory( &buffer[nextEntry], baseLength );
switch (fileInformationClass) {
case FileBothDirectoryInformation:
//
// We don't need short name
//
DebugTrace(0, Dbg, "Getting directory full information\n", 0);
case FileFullDirectoryInformation:
//
// We don't use EaLength, so fill in nothing here.
//
DebugTrace(0, Dbg, "Getting directory full information\n", 0);
case FileDirectoryInformation:
DebugTrace(0, Dbg, "Getting directory information\n", 0);
//
// The eof indicates the number of instances and
// allocation size is the maximum allowed
//
dirInfo = (PFILE_DIRECTORY_INFORMATION)&buffer[nextEntry];
dirInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL;
dirInfo->CreationTime = fcb->Specific.Fcb.CreationTime;
dirInfo->LastAccessTime = fcb->Specific.Fcb.LastAccessTime;
dirInfo->LastWriteTime = fcb->Specific.Fcb.LastModificationTime;
dirInfo->ChangeTime = fcb->Specific.Fcb.LastChangeTime;
dirInfo->FileNameLength = fcb->LastFileName.Length;
break;
case FileNamesInformation:
DebugTrace(0, Dbg, "Getting names information\n", 0);
namesInfo = (PFILE_NAMES_INFORMATION)&buffer[nextEntry];
namesInfo->FileNameLength = fcb->LastFileName.Length;
break;
default:
KeBugCheck( MAILSLOT_FILE_SYSTEM );
}
RtlCopyMemory (&buffer[nextEntry + baseLength],
fcb->LastFileName.Buffer,
bytesToCopy);
//
// Update the CCB to the index we've just used.
//
Ccb->IndexOfLastCcbReturned = currentIndex;
//
// And indicate how much of the system buffer we have
// currently used up. We must compute this value before
// we long align outselves for the next entry.
//
Irp->IoStatus.Information = nextEntry + lengthAdded;
//
// Setup the previous next entry offset.
//
*((PULONG)(&buffer[lastEntry])) = nextEntry - lastEntry;
} except( EXCEPTION_EXECUTE_HANDLER ) {
status = GetExceptionCode();
try_return( status );
}
//
// Check if the last entry didn't completely fit
//
if ( status == STATUS_BUFFER_OVERFLOW ) {
try_return( NOTHING );
}
//
// Check if we are only to return a single entry
//
if (returnSingleEntry) {
try_return( status = STATUS_SUCCESS );
}
//
// Set ourselves up for the next iteration
//
lastEntry = nextEntry;
nextEntry += (ULONG)QuadAlign( lengthAdded );
}
//
// Increment the current index by one
//
currentIndex += 1;
}
}
//
// At this point we've scanned the entire list of FCBs so if
// the NextEntry is zero then we haven't found anything so we
// will return no more files, otherwise we return success.
//
if (nextEntry == 0) {
status = STATUS_NO_MORE_FILES;
} else {
status = STATUS_SUCCESS;
}
try_exit: NOTHING;
} finally {
DebugTrace(-1, Dbg, "MsQueryDirectory -> %08lx\n", status);
}
return status;
}
VOID
MsNotifyChangeDirectoryCancel (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the cancel routine for the directory notify request.
Arugments:
DeviceObject - Supplies the device object for the request being canceled.
Irp - Supplies the Irp being canceled.
Return Value:
None
--*/
{
KIRQL OldIrql;
PKSPIN_LOCK pSpinLock;
//
// First drop the cancel spinlock. We don't use this for this path
//
IoReleaseCancelSpinLock (Irp->CancelIrql);
//
// Grab the spinlock address. Easier that tracing the pointers or assuming that there is
// only one DCB
//
pSpinLock = Irp->Tail.Overlay.DriverContext[0];
//
// Acquire the spinlock protecting these queues.
//
KeAcquireSpinLock (pSpinLock, &OldIrql);
//
// Remove the entry from the list. We will always be in one of the lists or this entry has
// been initializes as an empty list by one of the completion routines when it detected
// this routine was active.
//
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLock (pSpinLock, OldIrql);
//
// Complete the IRP
//
MsCompleteRequest( Irp, STATUS_CANCELLED );
return;
}
NTSTATUS
MsNotifyChangeDirectory (
IN PROOT_DCB RootDcb,
IN PROOT_DCB_CCB Ccb,
IN PIRP Irp
)
/*++
Routine Description:
This is the common routine for doing the notify change directory.
Arugments:
RootDcb - Supplies the DCB being queried.
Ccb - Supplies the context of the caller.
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - STATUS_PENDING or STATUS_CANCELLED
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS Status;
KIRQL OldIrql;
PLIST_ENTRY Head;
//
// Get the current stack location.
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "MsNotifyChangeDirectory\n", 0 );
DebugTrace( 0, Dbg, "RootDcb = %p", RootDcb);
DebugTrace( 0, Dbg, "Ccb = %p", Ccb);
//
// Mark the Irp pending.
//
if (irpSp->Parameters.NotifyDirectory.CompletionFilter & (~FILE_NOTIFY_CHANGE_NAME)) {
Head = &RootDcb->Specific.Dcb.NotifyFullQueue;
} else {
Head = &RootDcb->Specific.Dcb.NotifyPartialQueue;
}
//
// Make it easy for the cancel routine to find this spinlock
//
Irp->Tail.Overlay.DriverContext[0] = &RootDcb->Specific.Dcb.SpinLock;
//
// Acquire the spinlock protecting these queues.
//
KeAcquireSpinLock (&RootDcb->Specific.Dcb.SpinLock, &OldIrql);
IoSetCancelRoutine (Irp, MsNotifyChangeDirectoryCancel);
//
// See if the IRP was already canceled before we enabled cancelation
//
if (Irp->Cancel &&
IoSetCancelRoutine (Irp, NULL) != NULL) {
KeReleaseSpinLock (&RootDcb->Specific.Dcb.SpinLock, OldIrql);
Status = STATUS_CANCELLED;
} else {
IoMarkIrpPending( Irp );
InsertTailList( Head,
&Irp->Tail.Overlay.ListEntry );
KeReleaseSpinLock (&RootDcb->Specific.Dcb.SpinLock, OldIrql);
Status = STATUS_PENDING;
}
//
// Return to our caller.
//
DebugTrace(-1, Dbg, "NotifyChangeDirectory status %X\n", Status);
return Status;
}