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