1097 lines
29 KiB
C
1097 lines
29 KiB
C
/*++
|
||
|
||
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 = *(PUNICODE_STRING)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;
|
||
}
|