968 lines
25 KiB
C
968 lines
25 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;
|
|||
|
}
|