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
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;
|
|
}
|