|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
vdm.c
Abstract:
This module supplies the entry point to the system for manipulating vdms.
Author:
Dave Hastings (daveh) 6-Apr-1992
Revision History:
--*/
#if defined (_X86_)
#include "vdmp.h"
#endif
#include <ntos.h>
#include <vdmntos.h>
#include <ntvdmp.h>
#include <zwapi.h>
#include <fsrtl.h>
typedef struct _QueryDirPoolData { KEVENT kevent; UNICODE_STRING FileName; WCHAR FileNameBuf[1]; } QDIR_POOLDATA, *PQDIR_POOLDATA;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VdmQueryDirectoryFile)
#endif
#if !defined(i386)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtVdmControl)
#endif
NTSTATUS NtVdmControl( IN VDMSERVICECLASS Service, IN OUT PVOID ServiceData ) /*++
Routine Description:
This routine is the entry point for controlling Vdms. On risc it returns STATUS_NOT_IMPLEMENTED. On 386 the entry point is in i386\vdmentry.c
Arguments:
Service -- Specifies what service is to be performed ServiceData -- Supplies a pointer to service specific data
Return Value:
--*/ { PAGED_CODE();
if (Service == VdmQueryDir) { return VdmQueryDirectoryFile(ServiceData); }
return STATUS_NOT_IMPLEMENTED;
} #endif
extern POBJECT_TYPE IoFileObjectType;
NTSTATUS VdmQueryDirectoryFile( PVDMQUERYDIRINFO pVdmQueryDir )
/*++
This VDM specific service allows vdm to restart searches at a specified location in the dir search by using the FileIndex, FileName parameters passed back from previous query calls.
See NtQueryDirectoryFile for additional documentation.
Arguments: PVDMQUERYDIRINFO pVdmQueryDir
FileHandle - Supplies a handle to the directory file for which information should be returned.
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.
FileName - Supplies a file name within the specified directory.
FileIndex - Supplies a file index within the specified directory.
The FileInformationClass is assumed to be FILE_BOTH_DIR_INFORMATION The Caller's mode is assumed to be UserMode Synchronous IO is used
--*/
{ KIRQL irql; NTSTATUS status; PKEVENT Event;
HANDLE FileHandle; IO_STATUS_BLOCK IoStatusBlock; PVOID FileInformation; ULONG Length; UNICODE_STRING FileName; PUNICODE_STRING pFileNameSrc; ULONG FileIndex;
PQDIR_POOLDATA QDirPoolData = NULL;
PMDL mdl; PIRP irp; PIO_STACK_LOCATION irpSp; PCHAR SystemBuffer; PFILE_OBJECT fileObject; PDEVICE_OBJECT DeviceObject;
PAGED_CODE();
//
// We assume that the caller is usermode, so verify all parameters
// accordingly
//
try {
//
// Copy out the callers service data into local variables
//
ProbeForRead( pVdmQueryDir, sizeof(VDMQUERYDIRINFO), sizeof(ULONG));
FileHandle = pVdmQueryDir->FileHandle; FileInformation = pVdmQueryDir->FileInformation; Length = pVdmQueryDir->Length; FileIndex = pVdmQueryDir->FileIndex; pFileNameSrc = pVdmQueryDir->FileName;
//
// Ensure that we have a valid file name string
//
//
// check for pVdmQueryDir->Filename validity first
//
if (NULL == pFileNameSrc) { return(STATUS_INVALID_PARAMETER); }
FileName = ProbeAndReadUnicodeString(pFileNameSrc); if (!FileName.Length || FileName.Length > MAXIMUM_FILENAME_LENGTH<<1) { return(STATUS_INVALID_PARAMETER); }
ProbeForRead(FileName.Buffer, FileName.Length, sizeof( UCHAR ));
//
// The FileInformation buffer must be writeable by the caller.
//
ProbeForWrite( FileInformation, Length, sizeof( ULONG ) );
//
// 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 < sizeof(FILE_BOTH_DIR_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; }
//
// Allocate from nonpaged pool a buffer large enough to contain
// the file name, and the kevent used to wait for io.
//
QDirPoolData = (PQDIR_POOLDATA) ExAllocatePoolWithQuotaTag( NonPagedPool, sizeof(QDIR_POOLDATA) + FileName.Length, ' MDV');
//
// Capture the file name string into the nonpaged pool block.
//
QDirPoolData->FileName.Length = FileName.Length; QDirPoolData->FileName.MaximumLength = FileName.Length; QDirPoolData->FileName.Buffer = QDirPoolData->FileNameBuf; RtlCopyMemory( QDirPoolData->FileNameBuf, FileName.Buffer, FileName.Length );
} except(EXCEPTION_EXECUTE_HANDLER) {
if (QDirPoolData) { ExFreePool(QDirPoolData); }
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, UserMode, (PVOID *) &fileObject, (POBJECT_HANDLE_INFORMATION) NULL ); if (!NT_SUCCESS( status )) { if (QDirPoolData) { ExFreePool(QDirPoolData); } return status; }
//
// We don't handle FO_SYNCHRONOUS_IO, because it requires
// io internal functionality. Ntvdm can get away with this
// because it serializes access to the dir handle.
//
//
// Initialize the kernel event that will signal I/O completion
//
Event = &QDirPoolData->kevent; KeInitializeEvent(Event, SynchronizationEvent, 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.
//
ObDereferenceObject( fileObject ); if (QDirPoolData) { ExFreePool(QDirPoolData); }
return STATUS_INSUFFICIENT_RESOURCES; }
//
// Fill in the service independent parameters in the IRP.
//
irp->Flags = (ULONG)IRP_SYNCHRONOUS_API; irp->RequestorMode = UserMode;
irp->UserIosb = &IoStatusBlock; irp->UserEvent = Event;
irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL; irp->AssociatedIrp.SystemBuffer = (PVOID) NULL; SystemBuffer = NULL;
irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->Tail.Overlay.OriginalFileObject = fileObject; irp->Tail.Overlay.AuxiliaryBuffer = NULL; irp->MdlAddress = NULL;
//
// Get a pointer to the stack location for the first driver. This will be
// used to pass the function codes and parameters.
//
irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = IRP_MJ_DIRECTORY_CONTROL; irpSp->MinorFunction = IRP_MN_QUERY_DIRECTORY; irpSp->FileObject = fileObject;
//
// Copy the caller's parameters to the service-specific portion of the
// IRP.
//
irpSp->Parameters.QueryDirectory.Length = Length; irpSp->Parameters.QueryDirectory.FileInformationClass = FileBothDirectoryInformation; irpSp->Parameters.QueryDirectory.FileIndex = FileIndex;
if (QDirPoolData->FileName.Length) { irpSp->Parameters.QueryDirectory.FileName = (PSTRING)&QDirPoolData->FileName; } else { irpSp->Parameters.QueryDirectory.FileName = NULL; }
irpSp->Flags = SL_INDEX_SPECIFIED;
//
// 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 file system wants buffered I/O. Pass the address of the
// "system buffer" in the IRP. Note that we don't want the buffer
// deallocated, nor do we want the I/O system to copy to a user
// buffer, so we don't set the corresponding flags in irp->Flags.
//
try {
//
// Allocate the intermediary system buffer from nonpaged pool and
// charge quota for it.
//
SystemBuffer = ExAllocatePoolWithQuotaTag( NonPagedPool, Length, ' MDV' );
irp->AssociatedIrp.SystemBuffer = SystemBuffer;
} except(EXCEPTION_EXECUTE_HANDLER) {
IoFreeIrp(irp);
ObDereferenceObject( fileObject );
if (QDirPoolData) { ExFreePool(QDirPoolData); }
return GetExceptionCode(); }
} 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, UserMode, IoWriteAccess );
} except(EXCEPTION_EXECUTE_HANDLER) {
if (irp->MdlAddress != NULL) { IoFreeMdl( irp->MdlAddress ); }
IoFreeIrp(irp);
ObDereferenceObject( fileObject );
if (QDirPoolData) { ExFreePool(QDirPoolData); }
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;
}
//
// Insert the packet at the head of the IRP list for the thread.
//
KeRaiseIrql( APC_LEVEL, &irql ); InsertHeadList( &irp->Tail.Overlay.Thread->IrpList, &irp->ThreadListEntry ); KeLowerIrql( irql );
//
// invoke the driver and wait for it to complete
//
status = IoCallDriver(DeviceObject, irp);
if (status == STATUS_PENDING) { status = KeWaitForSingleObject( Event, UserRequest, UserMode, FALSE, NULL ); }
if (NT_SUCCESS(status)) { status = IoStatusBlock.Status; if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { if (SystemBuffer) { try { RtlCopyMemory( FileInformation, SystemBuffer, IoStatusBlock.Information );
} except(EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } } } }
//
// Cleanup any memory allocated
//
if (QDirPoolData) { ExFreePool(QDirPoolData); }
if (SystemBuffer) { ExFreePool(SystemBuffer); }
return status; }
|