www.pudn.com > filesys.rar > dircntrl.c
/*************************************************************************
*
* File: dircntrl.c
*
* Module: Sample File System Driver (Kernel mode execution only)
*
* Description:
* Contains code to handle the "directory control" dispatch entry point.
*
* Author: R. Nagar
*
* (c) 1996-97 Rajeev Nagar, All Rights Reserved
*
*************************************************************************/
#include "sfsd.h"
// define the file specific bug-check id
#define SFSD_BUG_CHECK_ID SFSD_FILE_DIR_CONTROL
/*************************************************************************
*
* Function: SFsdDirControl()
*
* Description:
* The I/O Manager will invoke this routine to handle a directory control
* request
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
* to be deferred to a worker thread context)
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdDirControl(
PDEVICE_OBJECT DeviceObject, // the logical volume device object
PIRP Irp) // I/O Request Packet
{
NTSTATUS RC = STATUS_SUCCESS;
PtrSFsdIrpContext PtrIrpContext = NULL;
BOOLEAN AreWeTopLevel = FALSE;
FsRtlEnterFileSystem();
ASSERT(DeviceObject);
ASSERT(Irp);
// set the top level context
AreWeTopLevel = SFsdIsIrpTopLevel(Irp);
try {
// get an IRP context structure and issue the request
PtrIrpContext = SFsdAllocateIrpContext(Irp, DeviceObject);
ASSERT(PtrIrpContext);
RC = SFsdCommonDirControl(PtrIrpContext, Irp);
} except (SFsdExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
RC = SFsdExceptionHandler(PtrIrpContext, Irp);
SFsdLogEvent(SFSD_ERROR_INTERNAL_ERROR, RC);
}
if (AreWeTopLevel) {
IoSetTopLevelIrp(NULL);
}
FsRtlExitFileSystem();
return(RC);
}
/*************************************************************************
*
* Function: SFsdCommonDirControl()
*
* Description:
* The actual work is performed here. This routine may be invoked in one'
* of the two possible contexts:
* (a) in the context of a system worker thread
* (b) in the context of the original caller
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdCommonDirControl(
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp)
{
NTSTATUS RC = STATUS_SUCCESS;
PIO_STACK_LOCATION PtrIoStackLocation = NULL;
PFILE_OBJECT PtrFileObject = NULL;
PtrSFsdFCB PtrFCB = NULL;
PtrSFsdCCB PtrCCB = NULL;
PtrSFsdVCB PtrVCB = NULL;
// First, get a pointer to the current I/O stack location
PtrIoStackLocation = IoGetCurrentIrpStackLocation(PtrIrp);
ASSERT(PtrIoStackLocation);
PtrFileObject = PtrIoStackLocation->FileObject;
ASSERT(PtrFileObject);
// Get the FCB and CCB pointers
PtrCCB = (PtrSFsdCCB)(PtrFileObject->FsContext2);
ASSERT(PtrCCB);
PtrFCB = PtrCCB->PtrFCB;
ASSERT(PtrFCB);
// Get some of the parameters supplied to us
switch (PtrIoStackLocation->MinorFunction) {
case IRP_MN_QUERY_DIRECTORY:
RC = SFsdQueryDirectory(PtrIrpContext, PtrIrp, PtrIoStackLocation, PtrFileObject, PtrFCB, PtrCCB);
break;
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
RC = SFsdNotifyChangeDirectory(PtrIrpContext, PtrIrp, PtrIoStackLocation, PtrFileObject, PtrFCB, PtrCCB);
break;
default:
// This should not happen.
RC = STATUS_INVALID_DEVICE_REQUEST;
PtrIrp->IoStatus.Status = RC;
PtrIrp->IoStatus.Information = 0;
// Free up the Irp Context
SFsdReleaseIrpContext(PtrIrpContext);
// complete the IRP
IoCompleteRequest(PtrIrp, IO_NO_INCREMENT);
break;
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdQueryDirectory()
*
* Description:
* Query directory request.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdQueryDirectory(
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp,
PIO_STACK_LOCATION PtrIoStackLocation,
PFILE_OBJECT PtrFileObject,
PtrSFsdFCB PtrFCB,
PtrSFsdCCB PtrCCB)
{
NTSTATUS RC = STATUS_SUCCESS;
BOOLEAN CompleteRequest = TRUE;
BOOLEAN PostRequest = FALSE;
PtrSFsdNTRequiredFCB PtrReqdFCB = NULL;
BOOLEAN CanWait = FALSE;
PtrSFsdVCB PtrVCB = NULL;
BOOLEAN AcquiredFCB = FALSE;
unsigned long BufferLength = 0;
PSTRING PtrSearchPattern = NULL;
FILE_INFORMATION_CLASS FileInformationClass;
unsigned long FileIndex = 0;
BOOLEAN RestartScan = FALSE;
BOOLEAN ReturnSingleEntry = FALSE;
BOOLEAN IndexSpecified = FALSE;
unsigned char *Buffer = NULL;
BOOLEAN FirstTimeQuery = FALSE;
unsigned long StartingIndexForSearch = 0;
unsigned int BytesReturned = 0;
try {
// Validate the sent-in FCB
if ((PtrFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_VCB) || !(PtrFCB->FCBFlags & SFSD_FCB_DIRECTORY)) {
// We will only allow notify requests on directories.
RC = STATUS_INVALID_PARAMETER;
}
PtrReqdFCB = &(PtrFCB->NTRequiredFCB);
CanWait = ((PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);
PtrVCB = PtrFCB->PtrVCB;
// If the caller does not wish to block, it would be easier to
// simply post the request now.
if (!CanWait) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
// Obtain the callers parameters
BufferLength = PtrIoStackLocation->Parameters.QueryDirectory.Length;
PtrSearchPattern = PtrIoStackLocation->Parameters.QueryDirectory.FileName;
FileInformationClass = PtrIoStackLocation->Parameters.QueryDirectory.FileInformationClass;
FileIndex = PtrIoStackLocation->Parameters.QueryDirectory.FileIndex;
// Some additional arguments that affect the FSD behavior
RestartScan = (PtrIoStackLocation->Flags & SL_RESTART_SCAN);
ReturnSingleEntry = (PtrIoStackLocation->Flags & SL_RETURN_SINGLE_ENTRY);
IndexSpecified = (PtrIoStackLocation->Flags & SL_INDEX_SPECIFIED);
// I will acquire exclusive access to the FCB.
// This is not mandatory, however, and your FSD could choose to acquire
// the resource shared for increased parallelism.
ExAcquireResourceExclusiveLite(&(PtrReqdFCB->MainResource), TRUE);
AcquiredFCB = TRUE;
// We must determine the buffer pointer to be used. Since this
// routine could either be invoked directly in the context of the
// calling thread, or in the context of a worker thread, here is
// a general way of determining what we should use.
if (PtrIrp->MdlAddress) {
Buffer = MmGetSystemAddressForMdl(PtrIrp->MdlAddress);
} else {
Buffer = PtrIrp->UserBuffer;
}
// The method of determining where to look from and what to look for is
// unfortunately extremely confusing. However, here is a methodology you
// you can broadly adopt:
// (a) You have to maintain a search buffer per CCB structure.
// (b) This search buffer is initialized the very first time
// a query directory operation is performed using the file object.
// (For the sample FSD, the search buffer is stored in the
// DirectorySearchPattern field)
// However, the caller still has the option of "overriding" this stored
// search pattern by supplying a new one in a query directory operation.
//
if (PtrSearchPattern == NULL) {
// User has supplied a search pattern
// Now validate that the search pattern is legitimate; this is
// dependent upon the character set acceptable to your FSD.
// Once you have validated the search pattern, you must
// check whether you need to store this search pattern in
// the CCB.
if (PtrCCB->DirectorySearchPattern == NULL) {
// This must be the very first query request.
FirstTimeQuery = TRUE;
// Now, allocate enough memory to contain the caller
// supplied search pattern and fill in the DirectorySearchPattern
// field in the CCB
// PtrCCB->DirectorySearchPattern = ExAllocatePool(...);
} else {
// We should ignore the search pattern in the CCB and instead,
// use the user-supplied pattern for this particular query
// directory request.
}
} else if (PtrCCB->DirectorySearchPattern == NULL) {
// This MUST be the first directory query operation (else the
// DirectorySearchPattern field would never be NULL. Also, the caller
// has neglected to provide a pattern so we MUST invent one.
// Use "*" (following NT conventions) as your search pattern
// and store it in the PtrCCB->DirectorySearchPattern field.
PtrCCB->DirectorySearchPattern = ExAllocatePool(PagedPool, sizeof(L"*"));
ASSERT(PtrCCB->DirectorySearchPattern);
FirstTimeQuery = TRUE;
} else {
// The caller has not supplied any search pattern that we are
// forced to use. However, the caller had previously supplied
// a pattern (or we must have invented one) and we will use it.
// This is definitely not the first query operation on this
// directory using this particular file object.
PtrSearchPattern = PtrCCB->DirectorySearchPattern;
}
// There is one other piece of information that your FSD must store
// in the CCB structure for query directory support. This is the index
// value (i.e. the offset in your on-disk directory structure) from
// which you should start searching.
// However, the flags supplied with the IRP can make us override this
// as well.
if (FileIndex) {
// Caller has told us wherefrom to begin.
// You may need to round this to an appropriate directory entry
// entry alignment value.
StartingIndexForSearch = FileIndex;
} else if (RestartScan) {
StartingIndexForSearch = 0;
} else {
// Get the starting offset from the CCB.
// Remember to update this value on your way out from this function.
// But, do not update the CCB CurrentByteOffset field if you reach
// the end of the directory (or get an error reading the directory)
// while performing the search.
StartingIndexForSearch = PtrCCB->CurrentByteOffset.LowPart;
}
// Now, your FSD must determine the best way to read the directory
// contents from disk and search through them.
// If ReturnSingleEntry is TRUE, please return information on only
// one matching entry.
// One final note though:
// If you do not find a directory entry OR while searching you reach the
// end of the directory, then the return code should be set as follows:
// (a) If any files have been returned (i.e. ReturnSingleEntry was FALSE
// and you did find at least one match), then return STATUS_SUCCESS
// (b) If no entry is being returned then:
// (i) If this is the first query i.e. FirstTimeQuery is TRUE
// then return STATUS_NO_SUCH_FILE
// (ii) Otherwise, return STATUS_NO_MORE_FILES
try_exit: NOTHING;
// Remember to update the CurrentByteOffset field in the CCB if required.
// You should also set a flag in the FCB indicating that the directory
// contents were accessed.
} finally {
if (PostRequest) {
if (AcquiredFCB) {
SFsdReleaseResource(&(PtrReqdFCB->MainResource));
}
// Map the users buffer and then post the request.
RC = SFsdLockCallersBuffer(PtrIrp, TRUE, BufferLength);
ASSERT(NT_SUCCESS(RC));
RC = SFsdPostRequest(PtrIrpContext, PtrIrp);
} else if (!(PtrIrpContext->IrpContextFlags &
SFSD_IRP_CONTEXT_EXCEPTION)) {
if (AcquiredFCB) {
SFsdReleaseResource(&(PtrReqdFCB->MainResource));
}
// Complete the request.
PtrIrp->IoStatus.Status = RC;
PtrIrp->IoStatus.Information = BytesReturned;
// Free up the Irp Context
SFsdReleaseIrpContext(PtrIrpContext);
// complete the IRP
IoCompleteRequest(PtrIrp, IO_DISK_INCREMENT);
}
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdNotifyChangeDirectory()
*
* Description:
* Handle the notify request.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdNotifyChangeDirectory(
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp,
PIO_STACK_LOCATION PtrIoStackLocation,
PFILE_OBJECT PtrFileObject,
PtrSFsdFCB PtrFCB,
PtrSFsdCCB PtrCCB)
{
NTSTATUS RC = STATUS_SUCCESS;
BOOLEAN CompleteRequest = FALSE;
BOOLEAN PostRequest = FALSE;
PtrSFsdNTRequiredFCB PtrReqdFCB = NULL;
BOOLEAN CanWait = FALSE;
ULONG CompletionFilter = 0;
BOOLEAN WatchTree = FALSE;
PtrSFsdVCB PtrVCB = NULL;
BOOLEAN AcquiredFCB = FALSE;
try {
// Validate the sent-in FCB
if ((PtrFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_VCB) || !(PtrFCB->FCBFlags & SFSD_FCB_DIRECTORY)) {
// We will only allow notify requests on directories.
RC = STATUS_INVALID_PARAMETER;
CompleteRequest = TRUE;
}
PtrReqdFCB = &(PtrFCB->NTRequiredFCB);
CanWait = ((PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);
PtrVCB = PtrFCB->PtrVCB;
// Acquire the FCB resource shared
if (!ExAcquireResourceSharedLite(&(PtrReqdFCB->MainResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
AcquiredFCB = TRUE;
// Obtain some parameters sent by the caller
CompletionFilter = PtrIoStackLocation->Parameters.NotifyDirectory.CompletionFilter;
WatchTree = (PtrIoStackLocation->Flags & SL_WATCH_TREE ? TRUE : FALSE);
// If you wish to capture the subject context, you can do so as
// follows:
// {
// PSECURITY_SUBJECT_CONTEXT SubjectContext;
// SubjectContext = ExAllocatePool(PagedPool,
// sizeof(SECURITY_SUBJECT_CONTEXT));
// SeCaptureSubjectContext(SubjectContext);
// }
FsRtlNotifyFullChangeDirectory(&(PtrVCB->NotifyIRPMutex), &(PtrVCB->NextNotifyIRP), (void *)PtrCCB,
(PSTRING)(PtrFCB->FCBName->ObjectName.Buffer), WatchTree, FALSE, CompletionFilter, PtrIrp,
NULL, // SFsdTraverseAccessCheck(...) ?
NULL); // SubjectContext ?
RC = STATUS_PENDING;
try_exit: NOTHING;
} finally {
if (PostRequest) {
// Perform appropriate post related processing here
if (AcquiredFCB) {
SFsdReleaseResource(&(PtrReqdFCB->MainResource));
AcquiredFCB = FALSE;
}
RC = SFsdPostRequest(PtrIrpContext, PtrIrp);
} else if (CompleteRequest) {
PtrIrp->IoStatus.Status = RC;
PtrIrp->IoStatus.Information = 0;
// Free up the Irp Context
SFsdReleaseIrpContext(PtrIrpContext);
// complete the IRP
IoCompleteRequest(PtrIrp, IO_DISK_INCREMENT);
} else {
// Simply free up the IrpContext since the IRP has been queued
SFsdReleaseIrpContext(PtrIrpContext);
}
// Release the FCB resources if acquired.
if (AcquiredFCB) {
SFsdReleaseResource(&(PtrReqdFCB->MainResource));
AcquiredFCB = FALSE;
}
}
return(RC);
}