windows-nt/Source/XPSP1/NT/base/fs/npfs/dir.c
2020-09-26 16:20:57 +08:00

968 lines
25 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}