968 lines
26 KiB
C
968 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Dir.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the File Directory routines for the Named Pipe
|
||
file system by the dispatch driver.
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 28-Dec-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "NpProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (NPFS_BUG_CHECK_DIR)
|
||
|
||
//
|
||
// Local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_DIR)
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NpCheckForNotify)
|
||
#pragma alloc_text(PAGE, NpCommonDirectoryControl)
|
||
#pragma alloc_text(PAGE, NpFsdDirectoryControl)
|
||
#pragma alloc_text(PAGE, NpQueryDirectory)
|
||
#pragma alloc_text(PAGE, NpNotifyChangeDirectory)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
NpFsdDirectoryControl (
|
||
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the FSD routine that handles directory control
|
||
functions (i.e., query and notify).
|
||
|
||
Arguments:
|
||
|
||
NpfsDeviceObject - Supplies the device object for the directory function.
|
||
|
||
Irp - Supplies the IRP to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The appropriate result status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "NpFsdDirectoryControl\n", 0);
|
||
|
||
//
|
||
// Call the common Direcotry Control routine.
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
NpAcquireExclusiveVcb( );
|
||
|
||
Status = NpCommonDirectoryControl( NpfsDeviceObject, Irp );
|
||
|
||
NpReleaseVcb();
|
||
FsRtlExitFileSystem();
|
||
|
||
if (Status != STATUS_PENDING) {
|
||
NpCompleteRequest (Irp, Status);
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "NpFsdDirectoryControl -> %08lx\n", Status );
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
NpCheckForNotify (
|
||
IN PDCB Dcb,
|
||
IN BOOLEAN CheckAllOutstandingIrps,
|
||
IN PLIST_ENTRY DeferredList
|
||
)
|
||
|
||
/*++
|
||
|
||
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 if is has any notify Irps outstanding
|
||
|
||
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;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We'll always signal the notify full queue entries. They want
|
||
// to be notified if every any change is made to a directory
|
||
//
|
||
|
||
while (!IsListEmpty( &Dcb->Specific.Dcb.NotifyFullQueue )) {
|
||
|
||
//
|
||
// Remove the Irp from the head of the queue, and complete it
|
||
// with success.
|
||
//
|
||
|
||
Links = RemoveHeadList( &Dcb->Specific.Dcb.NotifyFullQueue );
|
||
|
||
Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
|
||
|
||
if (IoSetCancelRoutine (Irp, NULL) != NULL) {
|
||
NpDeferredCompleteRequest( Irp, STATUS_SUCCESS, DeferredList );
|
||
} else {
|
||
InitializeListHead (&Irp->Tail.Overlay.ListEntry);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now check if we should also do the partial notify queue.
|
||
//
|
||
|
||
if (CheckAllOutstandingIrps) {
|
||
|
||
while (!IsListEmpty( &Dcb->Specific.Dcb.NotifyPartialQueue )) {
|
||
|
||
//
|
||
// Remove the Irp from the head of the queue, and complete it
|
||
// with success.
|
||
//
|
||
|
||
Links = RemoveHeadList( &Dcb->Specific.Dcb.NotifyPartialQueue );
|
||
|
||
Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
|
||
|
||
if (IoSetCancelRoutine (Irp, NULL) != NULL) {
|
||
NpDeferredCompleteRequest( Irp, STATUS_SUCCESS, DeferredList );
|
||
} else {
|
||
InitializeListHead (&Irp->Tail.Overlay.ListEntry);
|
||
}
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NpCommonDirectoryControl (
|
||
IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the common code for directory control functions.
|
||
|
||
Arguments:
|
||
|
||
NpfsDeviceObject - Supplies the named pipe device object
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PFCB Fcb;
|
||
PROOT_DCB_CCB Ccb;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the current stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "NpCommonDirectoryControl...\n", 0);
|
||
DebugTrace( 0, Dbg, "Irp = %08lx\n", 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 (NpDecodeFileObject( IrpSp->FileObject,
|
||
&Fcb,
|
||
(PCCB *)&Ccb,
|
||
NULL ) != NPFS_NTC_ROOT_DCB) {
|
||
|
||
DebugTrace(0, Dbg, "Not a directory\n", 0);
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
|
||
DebugTrace(-1, Dbg, "NpCommonDirectoryControl -> %08lx\n", Status );
|
||
return 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 = NpQueryDirectory( Fcb, Ccb, Irp );
|
||
break;
|
||
|
||
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
|
||
|
||
Status = NpNotifyChangeDirectory( Fcb, 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;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "NpCommonDirectoryControl -> %08lx\n", Status);
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NpQueryDirectory (
|
||
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;
|
||
|
||
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, "NpQueryDirectory\n", 0 );
|
||
DebugTrace( 0, Dbg, "RootDcb = %08lx\n", RootDcb);
|
||
DebugTrace( 0, Dbg, "Ccb = %08lx\n", Ccb);
|
||
DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
|
||
DebugTrace( 0, Dbg, "Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length);
|
||
DebugTrace( 0, Dbg, "FileName = %Z\n", 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));
|
||
|
||
//
|
||
// Save references to the input parameters within the Irp
|
||
//
|
||
|
||
SystemBufferLength = IrpSp->Parameters.QueryDirectory.Length;
|
||
|
||
FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex;
|
||
|
||
FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass;
|
||
|
||
RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
|
||
ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
|
||
IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
|
||
|
||
if (IrpSp->Parameters.QueryDirectory.FileName != NULL) {
|
||
|
||
FileName = *(PUNICODE_STRING)IrpSp->Parameters.QueryDirectory.FileName;
|
||
|
||
//
|
||
// Make sure that the user called us with a proper unicode string.
|
||
// We will reject odd length file names (i.e., lengths with the low
|
||
// bit set)
|
||
//
|
||
|
||
if (FileName.Length & 0x1) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
} 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 = 2;
|
||
FileName.Buffer = ⋆
|
||
}
|
||
|
||
DebugTrace(0, Dbg, "Set query template -> %Z\n", &FileName);
|
||
|
||
//
|
||
// Allocate space for the query template
|
||
//
|
||
|
||
Ccb->QueryTemplate = NpAllocatePagedPoolWithQuota(sizeof(UNICODE_STRING) + FileName.Length, 'qFpN' );
|
||
if (Ccb->QueryTemplate == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Initialize the query template and copy over the string
|
||
//
|
||
|
||
Ccb->QueryTemplate->Length = FileName.Length;
|
||
Ccb->QueryTemplate->Buffer = (PWCH)Ccb->QueryTemplate +
|
||
sizeof(UNICODE_STRING) / sizeof(WCHAR);
|
||
|
||
RtlCopyMemory( Ccb->QueryTemplate->Buffer,
|
||
FileName.Buffer,
|
||
FileName.Length );
|
||
|
||
//
|
||
// Now zero out the FileName so we won't think we're to use it
|
||
// as a subsearch string.
|
||
//
|
||
|
||
FileName.Length = 0;
|
||
FileName.Buffer = NULL;
|
||
}
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
Buffer = NpMapUserBuffer( Irp );
|
||
|
||
//
|
||
// 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->NodeTypeCode == NPFS_NTC_FCB);
|
||
|
||
DebugTrace(0, Dbg, "Top of Loop\n", 0);
|
||
DebugTrace(0, Dbg, "Fcb = %08lx\n", 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 named pipe 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) {
|
||
|
||
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. Protect our access
|
||
// because it is the user's buffer.
|
||
//
|
||
|
||
LengthAdded = BaseLength + BytesToCopy;
|
||
|
||
try {
|
||
|
||
RtlZeroMemory( &Buffer[NextEntry], BaseLength );
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
try_return (Status = GetExceptionCode ());
|
||
}
|
||
|
||
//
|
||
// Now fill the base parts of the strucure that are
|
||
// applicable.
|
||
//
|
||
|
||
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. Protect
|
||
// our access because it is the user's buffer.
|
||
//
|
||
|
||
DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
|
||
|
||
try {
|
||
|
||
DirInfo->EndOfFile.QuadPart = Fcb->OpenCount;
|
||
DirInfo->AllocationSize.QuadPart = Fcb->Specific.Fcb.MaximumInstances;
|
||
|
||
DirInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||
|
||
DirInfo->FileNameLength = Fcb->LastFileName.Length;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
try_return (Status = GetExceptionCode ());
|
||
}
|
||
|
||
break;
|
||
|
||
case FileNamesInformation:
|
||
|
||
DebugTrace(0, Dbg, "Getting names information\n", 0);
|
||
|
||
//
|
||
// Proctect our access because it is the user's buffer
|
||
//
|
||
|
||
NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
|
||
|
||
try {
|
||
|
||
NamesInfo->FileNameLength = Fcb->LastFileName.Length;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
try_return (Status = GetExceptionCode ());
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
NpBugCheck( FileInformationClass, 0, 0 );
|
||
}
|
||
|
||
//
|
||
// Protect our access because it is the user's buffer
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( &Buffer[NextEntry + BaseLength],
|
||
Fcb->LastFileName.Buffer,
|
||
BytesToCopy );
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
try_return (Status = GetExceptionCode ());
|
||
}
|
||
|
||
//
|
||
// 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. Protect our
|
||
// access because it is the user's buffer.
|
||
//
|
||
|
||
try {
|
||
|
||
*((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
try_return (Status = GetExceptionCode ());
|
||
}
|
||
|
||
//
|
||
// 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 Fcb 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, "NpQueryDirectory -> %08lx\n", Status);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NpNotifyChangeDirectory (
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PLIST_ENTRY Head;
|
||
|
||
UNREFERENCED_PARAMETER( Ccb );
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the current stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "NpNotifyChangeDirectory\n", 0 );
|
||
DebugTrace( 0, Dbg, "RootDcb = %08lx", RootDcb);
|
||
DebugTrace( 0, Dbg, "Ccb = %08lx", Ccb);
|
||
|
||
|
||
if (IrpSp->Parameters.NotifyDirectory.CompletionFilter &
|
||
~FILE_NOTIFY_CHANGE_NAME) {
|
||
Head = &RootDcb->Specific.Dcb.NotifyFullQueue;
|
||
} else {
|
||
Head = &RootDcb->Specific.Dcb.NotifyPartialQueue;
|
||
}
|
||
|
||
IoSetCancelRoutine( Irp, NpCancelChangeNotifyIrp );
|
||
|
||
if (Irp->Cancel && IoSetCancelRoutine( Irp, NULL ) != NULL) {
|
||
return STATUS_CANCELLED;
|
||
} else {
|
||
//
|
||
// Mark the Irp pending and insert into list.
|
||
//
|
||
IoMarkIrpPending( Irp );
|
||
InsertTailList( Head,
|
||
&Irp->Tail.Overlay.ListEntry );
|
||
return STATUS_PENDING;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
NpCancelChangeNotifyIrp (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the cancel function for an IRP saved in a change notify
|
||
queue
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - ignored
|
||
|
||
Irp - Supplies the Irp being cancelled. A pointer to the proper dcb queue
|
||
is stored in the information field of the Irp Iosb field.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY ListHead;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// Get exclusive access to the named pipe vcb so we can now do our work
|
||
//
|
||
FsRtlEnterFileSystem();
|
||
NpAcquireExclusiveVcb();
|
||
|
||
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
||
|
||
NpReleaseVcb();
|
||
FsRtlExitFileSystem();
|
||
|
||
NpCompleteRequest( Irp, STATUS_CANCELLED );
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return;
|
||
}
|