mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1234 lines
40 KiB
1234 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dir.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to implement the NtQueryDirectoryFile,
|
|
and the NtNotifyChangeDirectoryFile system services for the NT I/O system.
|
|
|
|
Author:
|
|
|
|
Darryl E. Havens (darrylh) 21-Jun-1989
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "iomgr.h"
|
|
|
|
NTSTATUS
|
|
BuildQueryDirectoryIrp(
|
|
IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID FileInformation,
|
|
IN ULONG Length,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass,
|
|
IN BOOLEAN ReturnSingleEntry,
|
|
IN PUNICODE_STRING FileName OPTIONAL,
|
|
IN BOOLEAN RestartScan,
|
|
IN UCHAR MinorFunction,
|
|
OUT BOOLEAN *SynchronousIo,
|
|
OUT PDEVICE_OBJECT *DeviceObject,
|
|
OUT PIRP *Irp,
|
|
OUT PFILE_OBJECT *FileObject,
|
|
OUT KPROCESSOR_MODE *RequestorMode
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, BuildQueryDirectoryIrp)
|
|
#pragma alloc_text(PAGE, NtQueryDirectoryFile)
|
|
#pragma alloc_text(PAGE, NtNotifyChangeDirectoryFile)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
BuildQueryDirectoryIrp(
|
|
IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID FileInformation,
|
|
IN ULONG Length,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass,
|
|
IN BOOLEAN ReturnSingleEntry,
|
|
IN PUNICODE_STRING FileName OPTIONAL,
|
|
IN BOOLEAN RestartScan,
|
|
IN UCHAR MinorFunction,
|
|
OUT BOOLEAN *SynchronousIo,
|
|
OUT PDEVICE_OBJECT *DeviceObject,
|
|
OUT PIRP *Irp,
|
|
OUT PFILE_OBJECT *FileObject,
|
|
OUT KPROCESSOR_MODE *RequestorMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service operates on a directory file or OLE container specified by the
|
|
FileHandle parameter. The service returns information about files in the
|
|
directory or embeddings and streams in the container specified by the file
|
|
handle. The ReturnSingleEntry parameter specifies that only a single entry
|
|
should be returned rather than filling the buffer. The actual number of
|
|
files whose information is returned, is the smallest of the following:
|
|
|
|
o One entry, if the ReturnSingleEntry parameter is TRUE.
|
|
|
|
o The number of entries whose information fits into the specified
|
|
buffer.
|
|
|
|
o The number of entries that exist.
|
|
|
|
o One entry if the optional FileName parameter is specified.
|
|
|
|
If the optional FileName parameter is specified, then the only information
|
|
that is returned is for that single entries, if it exists. Note that the
|
|
file name may not specify any wildcard characters according to the naming
|
|
conventions of the target file system. The ReturnSingleEntry parameter is
|
|
simply ignored.
|
|
|
|
The information that is obtained about the entries in the directory or OLE
|
|
container is based on the FileInformationClass parameter. Legal values are
|
|
hard coded based on the MinorFunction.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Supplies a handle to the directory file or OLE container for
|
|
which information should be returned.
|
|
|
|
Event - Supplies an optional event to be set to the Signaled state when
|
|
the query is complete.
|
|
|
|
ApcRoutine - Supplies an optional APC routine to be executed when the
|
|
query is complete.
|
|
|
|
ApcContext - Supplies a context parameter to be passed to the ApcRoutine,
|
|
if an ApcRoutine was specified.
|
|
|
|
IoStatusBlock - Address of the caller's I/O status block.
|
|
|
|
FileInformation - Supplies a buffer to receive the requested information
|
|
returned about the contents of the directory.
|
|
|
|
Length - Supplies the length, in bytes, of the FileInformation buffer.
|
|
|
|
FileInformationClass - Specfies the type of information that is to be
|
|
returned about the files in the specified directory or OLE container.
|
|
|
|
ReturnSingleEntry - Supplies a BOOLEAN value that, if TRUE, indicates that
|
|
only a single entry should be returned.
|
|
|
|
FileName - Optionally supplies a file name within the specified directory
|
|
or OLE container.
|
|
|
|
RestartScan - Supplies a BOOLEAN value that, if TRUE, indicates that the
|
|
scan should be restarted from the beginning. This parameter must be
|
|
set to TRUE by the caller the first time the service is invoked.
|
|
|
|
MinorFunction - IRP_MN_QUERY_DIRECTORY or IRP_MN_QUERY_OLE_DIRECTORY
|
|
|
|
SynchronousIo - pointer to returned BOOLEAN; TRUE if synchronous I/O
|
|
|
|
DeviceObject - pointer to returned pointer to device object
|
|
|
|
Irp - pointer to returned pointer to device object
|
|
|
|
FileObject - pointer to returned pointer to file object
|
|
|
|
RequestorMode - pointer to returned requestor mode
|
|
|
|
Return Value:
|
|
|
|
The status returned is STATUS_SUCCESS if a valid irp was created for the
|
|
query operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PKEVENT eventObject = (PKEVENT) NULL;
|
|
KPROCESSOR_MODE requestorMode;
|
|
PCHAR auxiliaryBuffer = (PCHAR) NULL;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PMDL mdl;
|
|
PETHREAD CurrentThread;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the previous mode; i.e., the mode of the caller.
|
|
//
|
|
|
|
CurrentThread = PsGetCurrentThread ();
|
|
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
|
*RequestorMode = requestorMode;
|
|
|
|
try {
|
|
|
|
if (requestorMode != KernelMode) {
|
|
|
|
ULONG operationlength = 0; // assume invalid
|
|
|
|
//
|
|
// The caller's access mode is not kernel so probe and validate
|
|
// each of the arguments as necessary. If any failures occur,
|
|
// the condition handler will be invoked to handle them. It
|
|
// will simply cleanup and return an access violation status
|
|
// code back to the system service dispatcher.
|
|
//
|
|
|
|
//
|
|
// The IoStatusBlock parameter must be writeable by the caller.
|
|
//
|
|
|
|
ProbeForWriteIoStatusEx( IoStatusBlock, ApcRoutine);
|
|
|
|
//
|
|
// Ensure that the FileInformationClass parameter is legal for
|
|
// querying information about files in the directory or object.
|
|
//
|
|
|
|
if (FileInformationClass == FileDirectoryInformation) {
|
|
operationlength = sizeof(FILE_DIRECTORY_INFORMATION);
|
|
} else if (MinorFunction == IRP_MN_QUERY_DIRECTORY) {
|
|
switch (FileInformationClass)
|
|
{
|
|
case FileFullDirectoryInformation:
|
|
operationlength = sizeof(FILE_FULL_DIR_INFORMATION);
|
|
break;
|
|
|
|
case FileIdFullDirectoryInformation:
|
|
operationlength = sizeof(FILE_ID_FULL_DIR_INFORMATION);
|
|
break;
|
|
|
|
case FileBothDirectoryInformation:
|
|
operationlength = sizeof(FILE_BOTH_DIR_INFORMATION);
|
|
break;
|
|
|
|
case FileIdBothDirectoryInformation:
|
|
operationlength = sizeof(FILE_ID_BOTH_DIR_INFORMATION);
|
|
break;
|
|
|
|
case FileNamesInformation:
|
|
operationlength = sizeof(FILE_NAMES_INFORMATION);
|
|
break;
|
|
|
|
case FileObjectIdInformation:
|
|
operationlength = sizeof(FILE_OBJECTID_INFORMATION);
|
|
break;
|
|
|
|
case FileQuotaInformation:
|
|
operationlength = sizeof(FILE_QUOTA_INFORMATION);
|
|
break;
|
|
|
|
case FileReparsePointInformation:
|
|
operationlength = sizeof(FILE_REPARSE_POINT_INFORMATION);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the FileInformationClass parameter is illegal, fail now.
|
|
//
|
|
|
|
if (operationlength == 0) {
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
//
|
|
// Ensure that the caller's supplied buffer is at least large enough
|
|
// to contain the fixed part of the structure required for this
|
|
// query.
|
|
//
|
|
|
|
if (Length < operationlength) {
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
|
|
//
|
|
// The FileInformation buffer must be writeable by the caller.
|
|
//
|
|
|
|
#if defined(_X86_)
|
|
ProbeForWrite( FileInformation, Length, sizeof( ULONG ) );
|
|
#elif defined(_WIN64)
|
|
|
|
//
|
|
// If we are a wow64 process, follow the X86 rules
|
|
//
|
|
|
|
if (PsGetCurrentProcessByThread(CurrentThread)->Wow64Process) {
|
|
ProbeForWrite( FileInformation, Length, sizeof( ULONG ) );
|
|
} else {
|
|
ProbeForWrite( FileInformation,
|
|
Length,
|
|
IopQuerySetAlignmentRequirement[FileInformationClass] );
|
|
}
|
|
|
|
#else
|
|
ProbeForWrite( FileInformation,
|
|
Length,
|
|
IopQuerySetAlignmentRequirement[FileInformationClass] );
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// If the optional FileName parameter was specified, then it must be
|
|
// readable by the caller. Capture the file name string in a pool
|
|
// block. Note that if an error occurs during the copy, the cleanup
|
|
// code in the exception handler will deallocate the pool before
|
|
// returning an access violation status.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( FileName )) {
|
|
|
|
UNICODE_STRING fileName;
|
|
PUNICODE_STRING nameBuffer;
|
|
|
|
//
|
|
// Capture the string descriptor itself to ensure that the
|
|
// string is readable by the caller without the caller being
|
|
// able to change the memory while its being checked.
|
|
//
|
|
|
|
if (requestorMode != KernelMode) {
|
|
fileName = ProbeAndReadUnicodeString( FileName );
|
|
} else {
|
|
fileName = *FileName;
|
|
}
|
|
|
|
if (fileName.Length) {
|
|
|
|
//
|
|
// The length of the string is non-zero, so probe the
|
|
// buffer described by the descriptor if the caller was
|
|
// not kernel mode. Likewise, if the caller's mode was
|
|
// not kernel, then check the length of the name string
|
|
// to ensure that it is not too long.
|
|
//
|
|
|
|
if (requestorMode != KernelMode) {
|
|
ProbeForRead( fileName.Buffer,
|
|
fileName.Length,
|
|
sizeof( UCHAR ) );
|
|
//
|
|
// account for unicode
|
|
//
|
|
|
|
if (fileName.Length > MAXIMUM_FILENAME_LENGTH<<1) {
|
|
ExRaiseStatus( STATUS_INVALID_PARAMETER );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate an auxiliary buffer large enough to contain
|
|
// a file name descriptor and to hold the entire file
|
|
// name itself. Copy the body of the string into the
|
|
// buffer.
|
|
//
|
|
|
|
auxiliaryBuffer = ExAllocatePoolWithQuota( NonPagedPool,
|
|
fileName.Length + sizeof( UNICODE_STRING ) );
|
|
RtlCopyMemory( auxiliaryBuffer + sizeof( UNICODE_STRING ),
|
|
fileName.Buffer,
|
|
fileName.Length );
|
|
|
|
//
|
|
// Finally, build the Unicode string descriptor in the
|
|
// auxiliary buffer.
|
|
//
|
|
|
|
nameBuffer = (PUNICODE_STRING) auxiliaryBuffer;
|
|
nameBuffer->Length = fileName.Length;
|
|
nameBuffer->MaximumLength = fileName.Length;
|
|
nameBuffer->Buffer = (PWSTR) (auxiliaryBuffer + sizeof( UNICODE_STRING ) );
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// An exception was incurred while probing the caller's buffers,
|
|
// attempting to allocate a pool buffer, or while trying to copy
|
|
// the caller's data. Determine what happened, clean everything
|
|
// up, and return an appropriate error status code.
|
|
//
|
|
|
|
if (auxiliaryBuffer) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
|
|
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// There were no blatant errors so far, so reference the file object so
|
|
// the target device object can be found. Note that if the handle does
|
|
// not refer to a file object, or if the caller does not have the required
|
|
// access to the file, then it will fail.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle( FileHandle,
|
|
FILE_LIST_DIRECTORY,
|
|
IoFileObjectType,
|
|
requestorMode,
|
|
(PVOID *) &fileObject,
|
|
(POBJECT_HANDLE_INFORMATION) NULL );
|
|
if (!NT_SUCCESS( status )) {
|
|
if (auxiliaryBuffer) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
return status;
|
|
}
|
|
*FileObject = fileObject;
|
|
|
|
//
|
|
// If this file has an I/O completion port associated w/it, then ensure
|
|
// that the caller did not supply an APC routine, as the two are mutually
|
|
// exclusive methods for I/O completion notification.
|
|
//
|
|
|
|
if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
|
|
ObDereferenceObject( fileObject );
|
|
if (auxiliaryBuffer) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
//
|
|
// Get the address of the event object and set the event to the Not-
|
|
// Signaled state, if an event was specified. Note here, too, that if
|
|
// the handle does not refer to an event, or if the event cannot be
|
|
// written, then the reference will fail.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( Event )) {
|
|
status = ObReferenceObjectByHandle( Event,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
requestorMode,
|
|
(PVOID *) &eventObject,
|
|
(POBJECT_HANDLE_INFORMATION) NULL );
|
|
if (!NT_SUCCESS( status )) {
|
|
if (auxiliaryBuffer) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
ObDereferenceObject( fileObject );
|
|
return status;
|
|
} else {
|
|
KeClearEvent( eventObject );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make a special check here to determine whether this is a synchronous
|
|
// I/O operation. If it is, then wait here until the file is owned by
|
|
// the current thread.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
|
|
BOOLEAN interrupted;
|
|
|
|
if (!IopAcquireFastLock( fileObject )) {
|
|
status = IopAcquireFileObjectLock( fileObject,
|
|
requestorMode,
|
|
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
|
&interrupted );
|
|
if (interrupted) {
|
|
if (auxiliaryBuffer != NULL) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
if (eventObject != NULL) {
|
|
ObDereferenceObject( eventObject );
|
|
}
|
|
ObDereferenceObject( fileObject );
|
|
return status;
|
|
}
|
|
}
|
|
*SynchronousIo = TRUE;
|
|
} else {
|
|
*SynchronousIo = FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the file object to the Not-Signaled state.
|
|
//
|
|
|
|
KeClearEvent( &fileObject->Event );
|
|
|
|
//
|
|
// Get the address of the target device object.
|
|
//
|
|
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
*DeviceObject = deviceObject;
|
|
|
|
//
|
|
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
|
|
// The allocation is performed with an exception handler in case the
|
|
// caller does not have enough quota to allocate the packet.
|
|
|
|
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
|
|
if (!irp) {
|
|
|
|
//
|
|
// An IRP could not be allocated. Cleanup and return an appropriate
|
|
// error status code.
|
|
//
|
|
|
|
IopAllocateIrpCleanup( fileObject, eventObject );
|
|
if (auxiliaryBuffer) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
*Irp = irp;
|
|
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = CurrentThread;
|
|
irp->RequestorMode = requestorMode;
|
|
|
|
//
|
|
// Fill in the service independent parameters in the IRP.
|
|
//
|
|
|
|
irp->UserEvent = eventObject;
|
|
irp->UserIosb = IoStatusBlock;
|
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|
irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|
|
|
//
|
|
// Get a pointer to the stack location for the first driver. This will be
|
|
// used to pass the original function codes and parameters.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
|
|
irpSp->MinorFunction = MinorFunction;
|
|
irpSp->FileObject = fileObject;
|
|
|
|
// Also, copy the caller's parameters to the service-specific portion of
|
|
// the IRP.
|
|
//
|
|
|
|
irp->Tail.Overlay.AuxiliaryBuffer = auxiliaryBuffer;
|
|
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
|
|
irp->MdlAddress = (PMDL) NULL;
|
|
|
|
//
|
|
// Now determine whether this driver expects to have data buffered to it
|
|
// or whether it performs direct I/O. This is based on the DO_BUFFERED_IO
|
|
// flag in the device object. If the flag is set, then a system buffer is
|
|
// allocated and the driver's data will be copied into it. Otherwise, a
|
|
// Memory Descriptor List (MDL) is allocated and the caller's buffer is
|
|
// locked down using it.
|
|
//
|
|
|
|
if (deviceObject->Flags & DO_BUFFERED_IO) {
|
|
|
|
//
|
|
// The device does not support direct I/O. Allocate a system buffer
|
|
// and specify that it should be deallocated on completion. Also
|
|
// indicate that this is an input operation so the data will be copied
|
|
// into the caller's buffer. This is done using an exception handler
|
|
// that will perform cleanup if the operation fails.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Allocate the intermediary system buffer from nonpaged pool and
|
|
// charge quota for it.
|
|
//
|
|
|
|
irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuota( NonPagedPool, Length );
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// An exception was incurred while either probing the caller's
|
|
// buffer or allocate the system buffer. Determine what actually
|
|
// happened, clean everything up, and return an appropriate error
|
|
// status code.
|
|
//
|
|
|
|
IopExceptionCleanup( fileObject,
|
|
irp,
|
|
eventObject,
|
|
(PKEVENT) NULL );
|
|
|
|
if (auxiliaryBuffer != NULL) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
//
|
|
// Remember the address of the caller's buffer so the copy can take
|
|
// place during I/O completion. Also, set the flags so that the
|
|
// completion code knows to do the copy and to deallocate the buffer.
|
|
//
|
|
|
|
irp->UserBuffer = FileInformation;
|
|
irp->Flags = (ULONG) (IRP_BUFFERED_IO |
|
|
IRP_DEALLOCATE_BUFFER |
|
|
IRP_INPUT_OPERATION);
|
|
|
|
} else if (deviceObject->Flags & DO_DIRECT_IO) {
|
|
|
|
//
|
|
// This is a direct I/O operation. Allocate an MDL and invoke the
|
|
// memory management routine to lock the buffer into memory. This is
|
|
// done using an exception handler that will perform cleanup if the
|
|
// operation fails.
|
|
//
|
|
|
|
mdl = (PMDL) NULL;
|
|
|
|
try {
|
|
|
|
//
|
|
// Allocate an MDL, charging quota for it, and hang it off of the
|
|
// IRP. Probe and lock the pages associated with the caller's
|
|
// buffer for write access and fill in the MDL with the PFNs of
|
|
// those pages.
|
|
//
|
|
|
|
mdl = IoAllocateMdl( FileInformation, Length, FALSE, TRUE, irp );
|
|
if (mdl == NULL) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
MmProbeAndLockPages( mdl, requestorMode, IoWriteAccess );
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// An exception was incurred while either probing the caller's
|
|
// buffer or allocating the MDL. Determine what actually happened,
|
|
// clean everything up, and return an appropriate error status code.
|
|
//
|
|
|
|
IopExceptionCleanup( fileObject,
|
|
irp,
|
|
eventObject,
|
|
(PKEVENT) NULL );
|
|
|
|
if (auxiliaryBuffer != NULL) {
|
|
ExFreePool( auxiliaryBuffer );
|
|
}
|
|
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Pass the address of the user's buffer so the driver has access to
|
|
// it. It is now the driver's responsibility to do everything.
|
|
//
|
|
|
|
irp->UserBuffer = FileInformation;
|
|
|
|
}
|
|
|
|
//
|
|
// Copy the caller's parameters to the service-specific portion of the
|
|
// IRP.
|
|
//
|
|
|
|
irpSp->Parameters.QueryDirectory.Length = Length;
|
|
irpSp->Parameters.QueryDirectory.FileInformationClass = FileInformationClass;
|
|
irpSp->Parameters.QueryDirectory.FileIndex = 0;
|
|
irpSp->Parameters.QueryDirectory.FileName = (PSTRING) auxiliaryBuffer;
|
|
irpSp->Flags = 0;
|
|
if (RestartScan) {
|
|
irpSp->Flags = SL_RESTART_SCAN;
|
|
}
|
|
if (ReturnSingleEntry) {
|
|
irpSp->Flags |= SL_RETURN_SINGLE_ENTRY;
|
|
}
|
|
|
|
irp->Flags |= IRP_DEFER_IO_COMPLETION;
|
|
|
|
//
|
|
// Return with everything set up for the caller to complete the I/O.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NtQueryDirectoryFile(
|
|
IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID FileInformation,
|
|
IN ULONG Length,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass,
|
|
IN BOOLEAN ReturnSingleEntry,
|
|
IN PUNICODE_STRING FileName OPTIONAL,
|
|
IN BOOLEAN RestartScan
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service operates on a directory file specified by the FileHandle
|
|
parameter. The service returns information about files in the directory
|
|
specified by the file handle. The ReturnSingleEntry parameter specifies
|
|
that only a single entry should be returned rather than filling the buffer.
|
|
The actual number of files whose information is returned, is the smallest
|
|
of the following:
|
|
|
|
o One entry, if the ReturnSingleEntry parameter is TRUE.
|
|
|
|
o The number of files whose information fits into the specified
|
|
buffer.
|
|
|
|
o The number of files that exist.
|
|
|
|
o One entry if the optional FileName parameter is specified.
|
|
|
|
If the optional FileName parameter is specified, then the only information
|
|
that is returned is for that single file, if it exists. Note that the
|
|
file name may not specify any wildcard characters according to the naming
|
|
conventions of the target file system. The ReturnSingleEntry parameter is
|
|
simply ignored.
|
|
|
|
The information that is obtained about the files in the directory is based
|
|
on the FileInformationClass parameter. The legal values are as follows:
|
|
|
|
o FileNamesInformation
|
|
|
|
o FileDirectoryInformation
|
|
|
|
o FileFullDirectoryInformation
|
|
|
|
o FileBothDirectoryInformation
|
|
|
|
o FileIdBothDirectoryInformation
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Supplies a handle to the directory file for which information
|
|
should be returned.
|
|
|
|
Event - Supplies an optional event to be set to the Signaled state when
|
|
the query is complete.
|
|
|
|
ApcRoutine - Supplies an optional APC routine to be executed when the
|
|
query is complete.
|
|
|
|
ApcContext - Supplies a context parameter to be passed to the ApcRoutine,
|
|
if an ApcRoutine was specified.
|
|
|
|
IoStatusBlock - Address of the caller's I/O status block.
|
|
|
|
FileInformation - Supplies a buffer to receive the requested information
|
|
returned about the contents of the directory.
|
|
|
|
Length - Supplies the length, in bytes, of the FileInformation buffer.
|
|
|
|
FileInformationClass - Specfies the type of information that is to be
|
|
returned about the files in the specified directory.
|
|
|
|
ReturnSingleEntry - Supplies a BOOLEAN value that, if TRUE, indicates that
|
|
only a single entry should be returned.
|
|
|
|
FileName - Optionally supplies a file name within the specified directory.
|
|
|
|
RestartScan - Supplies a BOOLEAN value that, if TRUE, indicates that the
|
|
scan should be restarted from the beginning. This parameter must be
|
|
set to TRUE by the caller the first time the service is invoked.
|
|
|
|
Return Value:
|
|
|
|
The status returned is success if the query operation was properly queued
|
|
to the I/O system. Once the operation completes, the status of the query
|
|
can be determined by examining the Status field of the I/O status block.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN synchronousIo;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIRP irp;
|
|
PFILE_OBJECT fileObject;
|
|
KPROCESSOR_MODE requestorMode;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Build the irp with the appropriate minor function & allowed info levels.
|
|
//
|
|
|
|
status = BuildQueryDirectoryIrp( FileHandle,
|
|
Event,
|
|
ApcRoutine,
|
|
ApcContext,
|
|
IoStatusBlock,
|
|
FileInformation,
|
|
Length,
|
|
FileInformationClass,
|
|
ReturnSingleEntry,
|
|
FileName,
|
|
RestartScan,
|
|
IRP_MN_QUERY_DIRECTORY,
|
|
&synchronousIo,
|
|
&deviceObject,
|
|
&irp,
|
|
&fileObject,
|
|
&requestorMode);
|
|
if (status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Queue the packet, call the driver, and synchronize appopriately with
|
|
// I/O completion.
|
|
//
|
|
status = IopSynchronousServiceTail( deviceObject,
|
|
irp,
|
|
fileObject,
|
|
TRUE,
|
|
requestorMode,
|
|
synchronousIo,
|
|
OtherTransfer );
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NtNotifyChangeDirectoryFile(
|
|
IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN ULONG CompletionFilter,
|
|
IN BOOLEAN WatchTree
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service monitors a directory file for changes. Once a change is
|
|
made to the directory specified by the FileHandle parameter, the I/O
|
|
operation is completed.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Supplies a handle to the file whose EAs should be changed.
|
|
|
|
Event - Supplies an optional event to be set to the Signaled state when the
|
|
change is complete.
|
|
|
|
ApcRoutine - Supplies an optional APC routine to be executed when the change
|
|
is complete.
|
|
|
|
ApcContext - Supplies a context parameter to be passed to the ApcRoutine,
|
|
if an ApcRoutine was specified.
|
|
|
|
IoStatusBlock - Address of the caller's I/O status block.
|
|
|
|
Buffer - Address of variable to receive the names of the files or
|
|
directories that have changed since the last time that the service
|
|
was invoked.
|
|
|
|
Length - Length of the output buffer. On the first call, this parameter
|
|
also serves as a guideline for how large to make the system's
|
|
internal buffer. Specifying a buffer length of zero causes the request
|
|
to complete when changes are made, but no information about the
|
|
changes are returned.
|
|
|
|
CompletionFilter - Indicates the types of changes to files or directories
|
|
within the directory that will complete the I/O operation.
|
|
|
|
WatchTree - A BOOLEAN value that indicates whether or not changes to
|
|
directories below the directory referred to by the FileHandle
|
|
parameter cause the operation to complete.
|
|
|
|
Return Value:
|
|
|
|
The status returned is success if the operation was properly queued to the
|
|
I/O system. Once the operation completes, the status of the operation can
|
|
be determined by examining the Status field of the I/O status block.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PKEVENT eventObject = (PKEVENT) NULL;
|
|
KPROCESSOR_MODE requestorMode;
|
|
PIO_STACK_LOCATION irpSp;
|
|
BOOLEAN synchronousIo;
|
|
PETHREAD CurrentThread;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the previous mode; i.e., the mode of the caller.
|
|
//
|
|
|
|
CurrentThread = PsGetCurrentThread ();
|
|
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
|
|
|
if (requestorMode != KernelMode) {
|
|
|
|
//
|
|
// The caller's access mode is user, so probe each of the arguments
|
|
// and capture them as necessary. If any failures occur, the condition
|
|
// handler will be invoked to handle them. It will simply cleanup and
|
|
// return an access violation status code back to the system service
|
|
// dispatcher.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// The IoStatusBlock parameter must be writeable by the caller.
|
|
//
|
|
|
|
ProbeForWriteIoStatusEx( IoStatusBlock , ApcRoutine);
|
|
|
|
//
|
|
// The Buffer parameter must be writeable by the caller.
|
|
//
|
|
|
|
if (Length != 0) {
|
|
ProbeForWrite( Buffer,
|
|
Length,
|
|
sizeof( ULONG ) );
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// An exception was incurred probing the caller's I/O status
|
|
// block. Simply return the appropriate error status code.
|
|
//
|
|
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
//
|
|
// The CompletionFilter parameter must not contain any values which
|
|
// are illegal, nor may it not specifiy anything at all. Likewise,
|
|
// the caller must supply a non-null buffer.
|
|
//
|
|
|
|
if (((CompletionFilter & ~FILE_NOTIFY_VALID_MASK) ||
|
|
!CompletionFilter)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// There were no blatant errors so far, so reference the file object so
|
|
// the target device object can be found. Note that if the handle does
|
|
// not refer to a file object, or if the caller does not have the required
|
|
// access to the file, then it will fail.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle( FileHandle,
|
|
FILE_LIST_DIRECTORY,
|
|
IoFileObjectType,
|
|
requestorMode,
|
|
(PVOID *) &fileObject,
|
|
(POBJECT_HANDLE_INFORMATION) NULL );
|
|
if (!NT_SUCCESS( status )) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If this file has an I/O completion port associated w/it, then ensure
|
|
// that the caller did not supply an APC routine, as the two are mutually
|
|
// exclusive methods for I/O completion notification.
|
|
//
|
|
|
|
if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
|
|
ObDereferenceObject( fileObject );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Get the address of the event object and set the event to the Not-
|
|
// Signaled state, if an event was specified. Note here too, that if
|
|
// the handle does not refer to an event, or if the event cannot be
|
|
// written, then the reference will fail.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( Event )) {
|
|
status = ObReferenceObjectByHandle( Event,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
requestorMode,
|
|
(PVOID *) &eventObject,
|
|
(POBJECT_HANDLE_INFORMATION) NULL );
|
|
if (!NT_SUCCESS( status )) {
|
|
ObDereferenceObject( fileObject );
|
|
return status;
|
|
} else {
|
|
KeClearEvent( eventObject );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make a special check here to determine whether this is a synchronous
|
|
// I/O operation. If it is, then wait here until the file is owned by
|
|
// the current thread.
|
|
//
|
|
|
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|
|
|
BOOLEAN interrupted;
|
|
|
|
if (!IopAcquireFastLock( fileObject )) {
|
|
status = IopAcquireFileObjectLock( fileObject,
|
|
requestorMode,
|
|
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
|
&interrupted );
|
|
if (interrupted) {
|
|
if (eventObject != NULL) {
|
|
ObDereferenceObject( eventObject );
|
|
}
|
|
ObDereferenceObject( fileObject );
|
|
return status;
|
|
}
|
|
}
|
|
synchronousIo = TRUE;
|
|
} else {
|
|
synchronousIo = FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the file object to the Not-Signaled state.
|
|
//
|
|
|
|
KeClearEvent( &fileObject->Event );
|
|
|
|
//
|
|
// Get the address of the target device object.
|
|
//
|
|
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
|
|
//
|
|
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
|
|
// The allocation is performed with an exception handler in case the
|
|
// caller does not have enough quota to allocate the packet.
|
|
|
|
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
|
|
if (!irp) {
|
|
|
|
//
|
|
// An IRP could not be allocated. Cleanup and return an appropriate
|
|
// error status code.
|
|
//
|
|
|
|
IopAllocateIrpCleanup( fileObject, eventObject );
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = CurrentThread;
|
|
irp->RequestorMode = requestorMode;
|
|
|
|
//
|
|
// Fill in the service independent parameters in the IRP.
|
|
//
|
|
|
|
irp->UserEvent = eventObject;
|
|
irp->UserIosb = IoStatusBlock;
|
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|
irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|
|
|
//
|
|
// Get a pointer to the stack location for the first driver. This will be
|
|
// used to pass the original function codes and the parameters.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
|
|
irpSp->MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY;
|
|
irpSp->FileObject = fileObject;
|
|
|
|
//
|
|
// Now determine whether this device expects to have data buffered to it
|
|
// or whether it performs direct I/O. This is based on the DO_BUFFERED_IO
|
|
// flag in the device object. If the flag is set, then a system buffer is
|
|
// allocated and the driver's data will be copied into it. Otherwise, a
|
|
// Memory Descriptor List (MDL) is allocated and the caller's buffer is
|
|
// locked down using it.
|
|
//
|
|
|
|
if (Length != 0) {
|
|
|
|
if (deviceObject->Flags & DO_BUFFERED_IO) {
|
|
|
|
//
|
|
// The device does not support direct I/O. Allocate a system
|
|
// buffer and specify that it should be deallocated on completion.
|
|
// Also indicate that this is an input operation so the data will
|
|
// be copied into the caller's buffer. This is done using an
|
|
// exception handler that will perform cleanup if the operation
|
|
// fails.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Allocate the intermediary system buffer from nonpaged pool
|
|
// and charge quota for it.
|
|
//
|
|
|
|
irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuota( NonPagedPool, Length );
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// An exception was incurred while attempting to allocate the
|
|
// intermediary system buffer. Clean everything up and return
|
|
// an appropriate error status code.
|
|
//
|
|
|
|
IopExceptionCleanup( fileObject,
|
|
irp,
|
|
eventObject,
|
|
(PKEVENT) NULL );
|
|
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
//
|
|
// Remember the address of the caller's buffer so the copy can take
|
|
// place during I/O completion. Also, set the flags so that the
|
|
// completion code knows to do the copy and to deallocate the
|
|
// buffer.
|
|
//
|
|
|
|
irp->UserBuffer = Buffer;
|
|
irp->Flags = IRP_BUFFERED_IO |
|
|
IRP_DEALLOCATE_BUFFER |
|
|
IRP_INPUT_OPERATION;
|
|
|
|
} else if (deviceObject->Flags & DO_DIRECT_IO) {
|
|
|
|
//
|
|
// This is a direct I/O operation. Allocate an MDL and invoke the
|
|
// memory management routine to lock the buffer into memory. This
|
|
// is done using an exception handler that will perform cleanup if
|
|
// the operation fails.
|
|
//
|
|
|
|
PMDL mdl;
|
|
|
|
mdl = (PMDL) NULL;
|
|
|
|
try {
|
|
|
|
//
|
|
// Allocate an MDL, charging quota for it, and hang it off of
|
|
// the IRP. Probe and lock the pages associated with the
|
|
// caller's buffer for write access and fill in the MDL with
|
|
// the PFNs of those pages.
|
|
//
|
|
|
|
mdl = IoAllocateMdl( Buffer, Length, FALSE, TRUE, irp );
|
|
if (mdl == NULL) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
MmProbeAndLockPages( mdl, requestorMode, IoWriteAccess );
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// An exception was incurred while either probing the caller's
|
|
// buffer of allocating the MDL. Determine what actually
|
|
// happened, clean everything up, and return an appropriate
|
|
// error status code.
|
|
//
|
|
|
|
IopExceptionCleanup( fileObject,
|
|
irp,
|
|
eventObject,
|
|
(PKEVENT) NULL );
|
|
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Pass the address of the user's buffer so the driver has access
|
|
// to it. It is now the driver's responsibility to do everything.
|
|
//
|
|
|
|
irp->UserBuffer = Buffer;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the caller's parameters to the service-specific portion of the
|
|
// IRP.
|
|
//
|
|
|
|
irpSp->Parameters.NotifyDirectory.Length = Length;
|
|
irpSp->Parameters.NotifyDirectory.CompletionFilter = CompletionFilter;
|
|
if (WatchTree) {
|
|
irpSp->Flags = SL_WATCH_TREE;
|
|
}
|
|
|
|
//
|
|
// Queue the packet, call the driver, and synchronize appopriately with
|
|
// I/O completion.
|
|
//
|
|
|
|
return IopSynchronousServiceTail( deviceObject,
|
|
irp,
|
|
fileObject,
|
|
FALSE,
|
|
requestorMode,
|
|
synchronousIo,
|
|
OtherTransfer );
|
|
}
|