Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1818 lines
61 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
Init.c
Abstract:
This module implements the Driver Initialization routine for the WebDav
miniredir.
Author:
Joe Linn
Rohan Kumar [RohanK] 10-March-1999
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "ntverp.h"
#include "netevent.h"
#include "nvisible.h"
#include "webdav.h"
#include "ntddmup.h"
#include "rxdata.h"
#include "fsctlbuf.h"
#include "tdikrnl.h"
//
// Global data declarations.
//
PEPROCESS MRxDAVSystemProcess;
FAST_MUTEX MRxDAVSerializationMutex;
KIRQL MRxDAVGlobalSpinLockSavedIrql;
KSPIN_LOCK MRxDAVGlobalSpinLock;
BOOLEAN MRxDAVGlobalSpinLockAcquired;
BOOLEAN MRxDAVTransportReady = FALSE;
HANDLE MRxDAVTdiNotificationHandle = NULL;
//
// The Exchange Registry key from where we read their DeviceObject name.
//
#define DavExchangeRegistryKey L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Lsifs\\Parameters"
//
// The exchange device name will be stored in this KEY_VALUE_PARTIAL_INFORMATION
// structure.
//
PBYTE DavExchangeDeviceName = NULL;
//
// The DavWinInetCachePath which is used in satisfying volume related queries.
//
WCHAR DavWinInetCachePath[MAX_PATH];
//
// The ProcessId of the svchost.exe process that loads the webclnt.dll.
//
ULONG DavSvcHostProcessId = 0;
//
// Name cache stuff. These values are read from the registry during init time.
//
ULONG FileInformationCacheLifeTimeInSec = 0;
ULONG FileNotFoundCacheLifeTimeInSec = 0;
ULONG NameCacheMaxEntries = 0;
#define MRXDAV_DEBUG_KEY L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\MRxDAV\\Parameters"
#define NAME_CACHE_OBJ_GET_FILE_ATTRIB_LIFETIME L"FileInformationCacheLifeTimeInSec"
#define NAME_CACHE_OBJ_NAME_NOT_FOUND_LIFETIME L"FileNotFoundCacheLifeTimeInSec"
#define NAME_CACHE_NETROOT_MAX_ENTRIES L"NameCacheMaxEntries"
#define CREATE_REQUEST_TIMEOUT_IN_SEC L"CreateRequestTimeoutInSec"
#define CREATEVNETROOT_REQUEST_TIMEOUT_IN_SEC L"CreateVNetRootRequestTimeoutInSec"
#define QUERYDIRECTORY_REQUEST_TIMEOUT_IN_SEC L"QueryDirectoryRequestTimeoutInSec"
#define CLOSE_REQUEST_TIMEOUT_IN_SEC L"CloseRequestTimeoutInSec"
#define CREATESRVCALL_REQUEST_TIMEOUT_IN_SEC L"CreateSrvCallRequestTimeoutInSec"
#define FINALIZESRVCALL_REQUEST_TIMEOUT_IN_SEC L"FinalizeSrvCallRequestTimeoutInSec"
#define FINALIZEFOBX_REQUEST_TIMEOUT_IN_SEC L"FinalizeFobxRequestTimeoutInSec"
#define FINALIZEVNETROOT_REQUEST_TIMEOUT_IN_SEC L"FinalizeVNetRootRequestTimeoutInSec"
#define RENAME_REQUEST_TIMEOUT_IN_SEC L"ReNameRequestTimeoutInSec"
#define SETFILEINFO_REQUEST_TIMEOUT_IN_SEC L"SetFileInfoRequestTimeoutInSec"
#define QUERYFILEINFO_REQUEST_TIMEOUT_IN_SEC L"QueryFileInfoRequestTimeoutInSec"
#define QUERYVOLUMEINFO_REQUEST_TIMEOUT_IN_SEC L"QueryVolumeInfoRequestTimeoutInSec"
#define LOCKREFRESH_REQUEST_TIMEOUT_IN_SEC L"LockRefreshRequestTimeoutInSec"
#if DBG
#define MRXDAV_DEBUG_VALUE L"DAVDebugFlag"
#endif
//
// Define the size of the shared memory area that we allocate as a heap
// between user and server.
//
#define DAV_SHARED_MEMORY_SIZE (1024 * 512)
//
// The Debug vector flags that control the amount of tracing in the debugger.
//
#if DBG
ULONG MRxDavDebugVector = 0;
#endif
//
// Mini Redirector global variables.
//
struct _MINIRDR_DISPATCH MRxDAVDispatch;
PWEBDAV_DEVICE_OBJECT MRxDAVDeviceObject;
FAST_IO_DISPATCH MRxDAVFastIoDispatch;
#define DAV_SVCHOST_NAME_SIZE 22
UNICODE_STRING uniSvcHost = {DAV_SVCHOST_NAME_SIZE+2,DAV_SVCHOST_NAME_SIZE+2,L"svchost.exe"};
FAST_MUTEX MRxDAVFileInfoCacheLock;
//
// Mentioned below are the prototypes of functions tht are used only within
// this module (file). These functions should not be exposed outside.
//
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
VOID
MRxDAVInitUnwind(
IN PDRIVER_OBJECT DriverObject,
IN WEBDAV_INIT_STATES MRxDAVInitState
);
VOID
MRxDAVUnload(
IN PDRIVER_OBJECT DriverObject
);
VOID
MRxDAVInitializeTables(
VOID
);
NTSTATUS
MRxDAVFsdDispatch (
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
IN PIRP Irp
);
VOID
MRxDAVDeregisterAndCleanupDeviceObject (
PUMRX_DEVICE_OBJECT UMRdrDeviceObject
);
NTSTATUS
MRxDAVRegisterForPnpNotifications(
VOID
);
NTSTATUS
MRxDAVDeregisterForPnpNotifications(
VOID
);
VOID
MRxDAVPnPBindingHandler(
IN TDI_PNP_OPCODE PnPOpcode,
IN PUNICODE_STRING pTransportName,
IN PWSTR BindingList
);
VOID
MRxDAVInitializeTheTimeValues(
VOID
);
NTSTATUS
MRxDAVSkipIrps(
IN PIRP Irp,
IN PUNICODE_STRING pFileName,
IN BOOL fCheckAny
);
UCHAR *
PsGetProcessImageFileName(
PEPROCESS Process
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, DriverEntry)
#pragma alloc_text(PAGE, MRxDAVInitUnwind)
#pragma alloc_text(PAGE, MRxDAVUnload)
#pragma alloc_text(PAGE, MRxDAVInitializeTables)
#pragma alloc_text(PAGE, MRxDAVFsdDispatch)
#pragma alloc_text(PAGE, MRxDAVDeregisterAndCleanupDeviceObject)
#pragma alloc_text(PAGE, MRxDAVFlush)
#pragma alloc_text(PAGE, MRxDAVPnPBindingHandler)
#pragma alloc_text(PAGE, MRxDAVRegisterForPnpNotifications)
#pragma alloc_text(PAGE, MRxDAVDeregisterForPnpNotifications)
#pragma alloc_text(PAGE, MRxDAVProbeForReadWrite)
#pragma alloc_text(PAGE, MRxDAVSkipIrps)
#pragma alloc_text(PAGE, MRxDAVInitializeTheTimeValues)
#endif
//
// Implementation of functions begins here.
//
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This is the initialization routine for the usermode reflector.
Arguments:
DriverObject - Pointer to driver object created by the system.
Return Value:
RXSTATUS - The function value is the final status from the initialization
operation.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NTSTATUS RegNtStatus = STATUS_SUCCESS;
WEBDAV_INIT_STATES MRxDAVInitState = 0;
UNICODE_STRING MRxDAVMiniRedirectorName;
PUMRX_DEVICE_OBJECT UMRefDeviceObject;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE KeyHandle = INVALID_HANDLE_VALUE;
UNICODE_STRING UnicodeRegKeyName, UnicodeValueName;
ULONG RequiredLength = 0;
PKEY_VALUE_PARTIAL_INFORMATION DavKeyValuePartialInfo = NULL;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering DriverEntry!!!!\n", PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: DriverEntry: Starting MRxDAV. DriverObject: %08lx.\n",
PsGetCurrentThreadId(), DriverObject));
//
// The first thing we do is set some globals in the driver by calling
// MRxDAVInitializeTheTimeValues().
//
MRxDAVInitializeTheTimeValues();
#ifdef MONOLITHIC_MINIRDR
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: DriverEntry: Calling RxDriverEntry.\n",
PsGetCurrentThreadId()));
NtStatus = RxDriverEntry(DriverObject, RegistryPath);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: DriverEntry: Back from RxDriverEntry. NtStatus: %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry/RxDriverEntry: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
return(NtStatus);
}
#endif
//
// The Dav redirector needs to register for PNP notifications to handle the
// following scenario. The SMB redirector does not accept connections till
// the net is ready as indicated by a PNP event. If during this time DAV
// forwards the connection requests to WinInet it will in turn spin up RAS
// connections. By registering for PNP notifications we provide an easy
// mechanism for short circuiting the requests till transports are ready.
//
MRxDAVRegisterForPnpNotifications();
MRxDAVSystemProcess = RxGetRDBSSProcess();
ExInitializeFastMutex(&MRxDAVSerializationMutex);
KeInitializeSpinLock(&MRxDAVGlobalSpinLock);
MRxDAVGlobalSpinLockAcquired = FALSE;
//
// 1. We need to initialize the TimerObject which will be used by the timer
// thread.
// 2. Set TimerThreadShutDown to FALSE. This will be set to TRUE
// when the system is being shutdown.
// 3. Initialize the resource that is used to synchronize the timer thread
// when the service is stopped.
// 4. Initialize the event that is signalled by the timer thread just
// before it terminates itself.
//
KeInitializeTimerEx( &(DavTimerObject), NotificationTimer );
TimerThreadShutDown = FALSE;
ExInitializeResourceLite( &(MRxDAVTimerThreadLock) );
KeInitializeEvent( &(TimerThreadEvent), NotificationEvent, FALSE );
//
// Initialize the global LockTokenEntryList and the resource that is used
// to synchronize access to it.
//
InitializeListHead( &(LockTokenEntryList) );
ExInitializeResourceLite( &(LockTokenEntryListLock) );
//
// Initialize the global LockConflictEntryList and the resource that is used
// to synchronize access to it.
//
InitializeListHead( &(LockConflictEntryList) );
ExInitializeResourceLite( &(LockConflictEntryListLock) );
//
// If QueueLockRefreshWorkItem is TRUE, the TimerThread (which cancels all the
// AsyncEngineContexts that haven't completed in a specified time) queues a
// WorkItem to refresh the locks. We initialize it to TRUE and the lock
// which is used to synchronize it.
//
QueueLockRefreshWorkItem = TRUE;
ExInitializeResourceLite( &(QueueLockRefreshWorkItemLock) );
//
// Zero the WinInetCachePath global. This will be initialized to the local
// WinInetCachePath value when the MiniRedir is started.
//
RtlZeroMemory ( DavWinInetCachePath, MAX_PATH * sizeof(WCHAR) );
try {
MRxDAVInitState = MRxDAVINIT_START;
RtlInitUnicodeString(&MRxDAVMiniRedirectorName, DD_DAV_DEVICE_NAME_U);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: DriverEntry: Registering the Mini-Rdr with RDBSS.\n",
PsGetCurrentThreadId()));
NtStatus = RxRegisterMinirdr((PRDBSS_DEVICE_OBJECT *)(&MRxDAVDeviceObject),
DriverObject,
&MRxDAVDispatch,
RX_REGISTERMINI_FLAG_DONT_PROVIDE_MAILSLOTS,
&MRxDAVMiniRedirectorName,
WEBDAV_DEVICE_OBJECT_EXTENSION_SIZE,
FILE_DEVICE_NETWORK_FILE_SYSTEM,
FILE_REMOTE_DEVICE);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry/RxRegisterMinirdr: NtStatus "
"= %08lx\n", PsGetCurrentThreadId(), NtStatus));
try_return(NtStatus);
}
MRxDAVInitState = MRxDAVINIT_MINIRDR_REGISTERED;
//
// Now initialize the reflector's portion of the Device object.
//
UMRefDeviceObject = (PUMRX_DEVICE_OBJECT)&(MRxDAVDeviceObject->UMRefDeviceObject);
NtStatus = UMRxInitializeDeviceObject(UMRefDeviceObject,
1024,
512,
DAV_SHARED_MEMORY_SIZE);
if (!NT_SUCCESS(NtStatus)) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry/UMRxInitializeDeviceObject:"
" NtStatus = %08lx\n", PsGetCurrentThreadId(), NtStatus));
try_return(NtStatus);
}
//
// Initialize the DAV Mini-Redir specific fields of the device object.
//
MRxDAVDeviceObject->IsStarted = FALSE;
MRxDAVDeviceObject->CachedRxDeviceFcb = NULL;
MRxDAVDeviceObject->RegisteringProcess = IoGetCurrentProcess();
try_exit: NOTHING;
} finally {
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry: Calling MRxDAVInitUnwind.\n",
PsGetCurrentThreadId()));
MRxDAVInitUnwind(DriverObject, MRxDAVInitState);
}
}
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry failed with NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
return(NtStatus);
}
//
// Initialize the dispatch vector used by RDBSS.
//
MRxDAVInitializeTables();
//
// Initialize the major function dispatch vector of the Driver object.
//
{
DWORD i;
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction[i] = (PDRIVER_DISPATCH)MRxDAVFsdDispatch;
}
}
//
// Setup Unload Routine for the Driver Object.
//
DriverObject->DriverUnload = MRxDAVUnload;
//
// Set the Driver Object's FastIoDispatch function.
//
DriverObject->FastIoDispatch = &(MRxDAVFastIoDispatch);
MRxDAVFastIoDispatch.SizeOfFastIoDispatch = sizeof(MRxDAVFastIoDispatch);
MRxDAVFastIoDispatch.FastIoDeviceControl = MRxDAVFastIoDeviceControl;
MRxDAVFastIoDispatch.FastIoRead = MRxDAVFastIoRead;
MRxDAVFastIoDispatch.FastIoWrite = MRxDAVFastIoWrite;
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
InitializeListHead( &(DavGlobalFileTable) );
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
//
// Since the Exchange Redir is not shipping with Whistler, we don't need
// to execute the code below. We can exit right away.
//
goto EXIT_THE_FUNCTION;
//
// Finally find out if the Exchange Redir is installed on this machine. If
// it is, get its Device Name.
//
RtlInitUnicodeString( &(UnicodeRegKeyName), DavExchangeRegistryKey );
InitializeObjectAttributes(&(ObjectAttributes),
&(UnicodeRegKeyName),
OBJ_CASE_INSENSITIVE,
0,
NULL);
//
// Open a handle to the Exchange Key.
//
RegNtStatus = ZwOpenKey(&(KeyHandle), KEY_READ, &(ObjectAttributes));
if (RegNtStatus != STATUS_SUCCESS) {
KeyHandle = INVALID_HANDLE_VALUE;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry/ZwOpenKey: NtStatus = %08lx\n",
PsGetCurrentThreadId(), RegNtStatus));
goto EXIT_THE_FUNCTION;
}
//
// We are looking for the DeviceName Value.
//
RtlInitUnicodeString( &(UnicodeValueName), L"DeviceName" );
// RtlInitUnicodeString( &(UnicodeValueName), L"Name" );
//
// Find out the number of bytes needed to store this value.
//
RegNtStatus = ZwQueryValueKey(KeyHandle,
&(UnicodeValueName),
KeyValuePartialInformation,
NULL,
0,
&(RequiredLength));
if (RegNtStatus != STATUS_BUFFER_TOO_SMALL) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry/ZwQueryValueKey(1): NtStatus = %08lx\n",
PsGetCurrentThreadId(), RegNtStatus));
goto EXIT_THE_FUNCTION;
}
DavExchangeDeviceName = RxAllocatePoolWithTag(PagedPool, RequiredLength, DAV_EXCHANGE_POOLTAG);
if (DavExchangeDeviceName == NULL) {
RegNtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: DriverEntry/RxAllocatePoolWithTag. NtStatus = %08lx\n",
PsGetCurrentThreadId(), RegNtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(DavExchangeDeviceName, RequiredLength);
DavKeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)DavExchangeDeviceName;
RegNtStatus = ZwQueryValueKey(KeyHandle,
&(UnicodeValueName),
KeyValuePartialInformation,
(PVOID)DavKeyValuePartialInfo,
RequiredLength,
&(RequiredLength));
if (RegNtStatus != STATUS_SUCCESS || DavKeyValuePartialInfo->Type != REG_SZ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: DriverEntry/ZwQueryValueKey(2): NtStatus = %08lx\n",
PsGetCurrentThreadId(), RegNtStatus));
goto EXIT_THE_FUNCTION;
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: DriverEntry: ExchangeDeviceName = %ws\n",
PsGetCurrentThreadId(), DavKeyValuePartialInfo->Data));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: DriverEntry: ExchangeDeviceNameLength = %d\n",
PsGetCurrentThreadId(), DavKeyValuePartialInfo->DataLength));
EXIT_THE_FUNCTION:
//
// We are done with the handle now, so close it.
//
if (KeyHandle != INVALID_HANDLE_VALUE) {
ZwClose(KeyHandle);
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving DriverEntry with NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
return NtStatus;
}
VOID
MRxDAVInitUnwind(
IN PDRIVER_OBJECT DriverObject,
IN WEBDAV_INIT_STATES MRxDAVInitState
)
/*++
Routine Description:
This routine does the common uninit work for unwinding from a bad driver entry or for unloading.
Arguments:
RxInitState - tells how far we got into the intialization
Return Value:
None
--*/
{
PAGED_CODE();
switch (MRxDAVInitState) {
case MRxDAVINIT_MINIRDR_REGISTERED:
RxUnregisterMinirdr(&MRxDAVDeviceObject->RxDeviceObject);
//
// Lack of break intentional.
//
case MRxDAVINIT_START:
break;
}
}
VOID
MRxDAVUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This is the unload routine for the usermode reflector.
Arguments:
DriverObject - pointer to the driver object for the UMRx
Return Value:
None
--*/
{
PUMRX_DEVICE_OBJECT UMRefDeviceObject = NULL;
PAGED_CODE();
UMRefDeviceObject = (PUMRX_DEVICE_OBJECT)&(MRxDAVDeviceObject->UMRefDeviceObject);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVUnload!!!!\n", PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVUnload: DriverObject = %08lx.\n",
PsGetCurrentThreadId(), DriverObject));
//
// If we allocated memory for the exchange device name, we need to free it
// now.
//
if (DavExchangeDeviceName != NULL) {
RxFreePool(DavExchangeDeviceName);
}
//
// Deregister the device object before calling RxUnload.
//
MRxDAVDeregisterAndCleanupDeviceObject(UMRefDeviceObject);
//
// Wait for the timer thread to finish before we delete the global locks
// MRxDAVTimerThreadLock and the others (see below) used to synchronize
// TimerThreadShutDown and other global variables.
//
KeWaitForSingleObject(&(TimerThreadEvent),
Executive,
KernelMode,
FALSE,
NULL);
ExDeleteResourceLite( &(MRxDAVTimerThreadLock) );
ExDeleteResourceLite( &(LockTokenEntryListLock) );
ExDeleteResourceLite( &(LockConflictEntryListLock) );
ExDeleteResourceLite( &(QueueLockRefreshWorkItemLock) );
//
// The TDI registration needs to be undone.
//
MRxDAVDeregisterForPnpNotifications();
#ifdef MONOLITHIC_MINIRDR
RxUnload(DriverObject);
#endif
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVUnload.\n", PsGetCurrentThreadId()));
return;
}
VOID
MRxDAVInitializeTables(
VOID
)
/*++
Routine Description:
This routine sets up the mini redirector dispatch vector and also calls to initialize any other tables needed.
Return Value:
RXSTATUS - The return status for the operation
--*/
{
PAGED_CODE();
//
// Local minirdr dispatch table init.
//
ZeroAndInitializeNodeType(&MRxDAVDispatch,
RDBSS_NTC_MINIRDR_DISPATCH,
sizeof(MINIRDR_DISPATCH));
//
// Reflector extension sizes and allocation policies.
// CODE.IMPROVEMENT. Currently we do not allocate the NET_ROOT and
// SRV_CALL extensions in the wrapper. Except for V_NET_ROOT wherein it is
// shared across multiple instances in the wrapper all the other data
// structure management should be left to the wrappers.
//
MRxDAVDispatch.MRxFlags = (RDBSS_MANAGE_FCB_EXTENSION |
RDBSS_MANAGE_SRV_OPEN_EXTENSION |
RDBSS_MANAGE_FOBX_EXTENSION |
RDBSS_MANAGE_V_NET_ROOT_EXTENSION |
RDBSS_NO_DEFERRED_CACHE_READAHEAD);
MRxDAVDispatch.MRxSrvCallSize = 0;
MRxDAVDispatch.MRxNetRootSize = 0;
MRxDAVDispatch.MRxVNetRootSize = sizeof(WEBDAV_V_NET_ROOT);
MRxDAVDispatch.MRxFcbSize = sizeof(WEBDAV_FCB);
MRxDAVDispatch.MRxSrvOpenSize = sizeof(WEBDAV_SRV_OPEN);
MRxDAVDispatch.MRxFobxSize = sizeof(WEBDAV_FOBX);
//
// Mini redirector cancel routine.
//
MRxDAVDispatch.MRxCancel = NULL;
//
// Mini redirector Start/Stop
//
MRxDAVDispatch.MRxStart = MRxDAVStart;
MRxDAVDispatch.MRxStop = MRxDAVStop;
MRxDAVDispatch.MRxDevFcbXXXControlFile = MRxDAVDevFcbXXXControlFile;
//
// Mini redirector name resolution
//
MRxDAVDispatch.MRxCreateSrvCall = MRxDAVCreateSrvCall;
MRxDAVDispatch.MRxSrvCallWinnerNotify = MRxDAVSrvCallWinnerNotify;
MRxDAVDispatch.MRxCreateVNetRoot = MRxDAVCreateVNetRoot;
MRxDAVDispatch.MRxUpdateNetRootState = MRxDAVUpdateNetRootState;
MRxDAVDispatch.MRxExtractNetRootName = MRxDAVExtractNetRootName;
MRxDAVDispatch.MRxFinalizeSrvCall = MRxDAVFinalizeSrvCall;
MRxDAVDispatch.MRxFinalizeNetRoot = MRxDAVFinalizeNetRoot;
MRxDAVDispatch.MRxFinalizeVNetRoot = MRxDAVFinalizeVNetRoot;
//
// File System Object Creation/Deletion.
//
MRxDAVDispatch.MRxCreate = MRxDAVCreate;
MRxDAVDispatch.MRxCollapseOpen = MRxDAVCollapseOpen;
MRxDAVDispatch.MRxShouldTryToCollapseThisOpen = MRxDAVShouldTryToCollapseThisOpen;
MRxDAVDispatch.MRxExtendForCache = MRxDAVExtendForCache;
MRxDAVDispatch.MRxExtendForNonCache = MRxDAVExtendForNonCache;
MRxDAVDispatch.MRxTruncate = MRxDAVTruncate;
MRxDAVDispatch.MRxCleanupFobx = MRxDAVCleanupFobx;
MRxDAVDispatch.MRxCloseSrvOpen = MRxDAVCloseSrvOpen;
MRxDAVDispatch.MRxFlush = MRxDAVFlush;
MRxDAVDispatch.MRxForceClosed = MRxDAVForcedClose;
MRxDAVDispatch.MRxDeallocateForFcb = MRxDAVDeallocateForFcb;
MRxDAVDispatch.MRxDeallocateForFobx = MRxDAVDeallocateForFobx;
// MRxDAVDispatch.MRxIsLockRealizable = UMRxIsLockRealizable;
//
// File System Objects query/Set.
//
MRxDAVDispatch.MRxQueryDirectory = MRxDAVQueryDirectory;
MRxDAVDispatch.MRxQueryVolumeInfo = MRxDAVQueryVolumeInformation;
MRxDAVDispatch.MRxQueryEaInfo = MRxDAVQueryEaInformation;
MRxDAVDispatch.MRxSetEaInfo = MRxDAVSetEaInformation;
// MRxDAVDispatch.MRxQuerySdInfo = UMRxQuerySecurityInformation;
// MRxDAVDispatch.MRxSetSdInfo = UMRxSetSecurityInformation;
MRxDAVDispatch.MRxQueryFileInfo = MRxDAVQueryFileInformation;
MRxDAVDispatch.MRxSetFileInfo = MRxDAVSetFileInformation;
// MRxDAVDispatch.MRxSetFileInfoAtCleanup = UMRxSetFileInformationAtCleanup;
MRxDAVDispatch.MRxIsValidDirectory= MRxDAVIsValidDirectory;
//
// Buffering state change.
//
MRxDAVDispatch.MRxComputeNewBufferingState = MRxDAVComputeNewBufferingState;
//
// File System Object I/O.
//
MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_READ] = MRxDAVRead;
MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_WRITE] = MRxDAVWrite;
// MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_SHAREDLOCK] = UMRxLocks;
// MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_EXCLUSIVELOCK] = UMRxLocks;
// MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_UNLOCK] = UMRxLocks;
// MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_UNLOCK_MULTIPLE] = UMRxLocks;
MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_FSCTL] = MRxDAVFsCtl;
// MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_IOCTL] = UMRxIoCtl;
//
// Shouldn't flush come through lowio?
//
// MRxDAVDispatch.MRxLowIOSubmit[LOWIO_OP_NOTIFY_CHANGE_DIRECTORY] =
// UMRxNotifyChangeDirectory;
//
// Miscellanous.
//
// MRxDAVDispatch.MRxCompleteBufferingStateChangeRequest =
// UMRxCompleteBufferingStateChangeRequest;
// initialize the mutex which protect the file info cache expire timer
ExInitializeFastMutex(&MRxDAVFileInfoCacheLock);
return;
}
NTSTATUS
MRxDAVFsdDispatch(
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD dispatch for the DAV miniredir.
Arguments:
RxDeviceObject - Supplies the device object for the packet being processed.
Irp - Supplies the Irp being processed.
Return Value:
RXSTATUS - The Fsd status for the Irp
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
UCHAR MajorFunctionCode = IrpSp->MajorFunction;
UCHAR MinorFunctionCode = IrpSp->MinorFunction;
PFILE_OBJECT FileObject = IrpSp->FileObject;
PWCHAR SaveInitialString = NULL;
BOOL JustAServer = FALSE;
ULONG IoControlCode = 0;
PQUERY_PATH_REQUEST qpRequest = NULL;
PWCHAR QueryPathBuffer = NULL;
ULONG QueryPathBufferLength = 0; // Length in Bytes of QueryPathBuffer.
KPROCESSOR_MODE ReqMode = 0;
PAGED_CODE();
//
// Check if the PNP event indicating that the transports are ready has been
// received. Till that time there is no point in forwarding requests to
// the user mode agent since this could put WinInet in a wierd state.
//
if (!MRxDAVTransportReady) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch. MRxDAVTransportReady == FALSE\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_REDIRECTOR_NOT_STARTED;
goto COMPLETE_THE_REQUEST;
}
//
// The first thing we need to check is whehter we got a DeviceIoControl
// from MUP "IOCTL_REDIR_QUERY_PATH", to figure out if some UNC path is
// owned by DAV or not. We need to check to see if the share supplied
// in the path is one of the special SMB shares. These include PIPE, IPC$
// and mailslot. If its one of these, then we reject the path at this stage
// with a STATUS_BAD_NETOWRK_PATH response. This is better than rejecting
// it at the creation of netroot becuase we save a network trip to the
// server while creating the SrvCall.
//
try {
if (MajorFunctionCode == IRP_MJ_DEVICE_CONTROL) {
ReqMode = Irp->RequestorMode;
//
// Get the IoControlCode from IrpSp.
//
IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
//
// If the IoControlCode is "IOCTL_REDIR_QUERY_PATH", we need to do the
// following. We basically check to see if the request came down for
// any of the special SMB shares. If it did, then we return.
//
if (IoControlCode == IOCTL_REDIR_QUERY_PATH) {
PWCHAR QPPtr1 = NULL;
BOOL FirstWack = TRUE, SpecialShare = FALSE;
UNICODE_STRING UnicodeShareName, uniFileName;
ULONG ShareNameLengthInBytes = 0;
//
// This particular IOCTL should only come to us from the MUP and
// hence the requestor mode of the IRP should always be
// KernelMode. If its not we return STATUS_INVALID_DEVICE_REQUEST.
//
if (ReqMode != KernelMode) {
NtStatus = STATUS_INVALID_DEVICE_REQUEST;
goto COMPLETE_THE_REQUEST;
}
qpRequest = METHODNEITHER_OriginalInputBuffer(IrpSp);
//
// If the requestor mode is not Kernel, we need to probe the buffer.
// Probe the buffer that was supplied by the caller of the IOCTL to
// make sure that its valid. This is to prevent hacker programs from
// using this IOCTL to pass in invalid buffers.
//
if (ReqMode != KernelMode) {
NtStatus = MRxDAVProbeForReadWrite((PBYTE)qpRequest, sizeof(QUERY_PATH_REQUEST), TRUE, FALSE);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch/MRxDAVProbeForReadWrite(1). "
"NtStatus = %08lx\n", PsGetCurrentThreadId(), NtStatus));
goto COMPLETE_THE_REQUEST;
}
}
QueryPathBuffer = (PWCHAR)(qpRequest->FilePathName);
ASSERT(QueryPathBuffer != NULL);
QueryPathBufferLength = qpRequest->PathNameLength;
//
// If the requestor mode is not Kernel, we need to probe the buffer.
// Probe the file name buffer (which is a part of the structure)
// that was supplied by the caller of the IOCTL to make sure that
// its valid.
//
if (ReqMode != KernelMode) {
NtStatus = MRxDAVProbeForReadWrite((PBYTE)QueryPathBuffer, QueryPathBufferLength, TRUE, FALSE);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch/MRxDAVProbeForReadWrite(2). "
"NtStatus = %08lx\n", PsGetCurrentThreadId(), NtStatus));
goto COMPLETE_THE_REQUEST;
}
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFsdDispatch: Type3InputBuffer = %ws\n",
PsGetCurrentThreadId(), QueryPathBuffer));
//
// The Type3InputBuffer is of the form \server\share or
// \server\share\ or \server\share\path. We make the
// QueryPathBuffer point to the char after the \ character.
//
QueryPathBuffer += 1;
ASSERT(QueryPathBuffer != NULL);
//
// We subtract ( sizeof(WCHAR) ) from the buffer length because
// the QueryPathBuffer points starting from the server name. It
// skips the first WCHAR which is \.
//
QueryPathBufferLength -= sizeof(WCHAR);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFsdDispatch: QueryPathBufferLength = %d\n",
PsGetCurrentThreadId(), QueryPathBufferLength));
//
// If we just got a \ down from the MUP, then the value of
// QueryPathBufferLength will now be zero since we have already
// taken out 2 bytes above. We return right away in such a situation.
//
if (QueryPathBufferLength == 0) {
NtStatus = STATUS_BAD_NETWORK_PATH;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch: QueryPathBufferLength == 0\n",
PsGetCurrentThreadId()));
goto COMPLETE_THE_REQUEST;
}
//
// The loop below is to set the start of the sharename and to
// calculate the length of the sharename in bytes.
//
while (TRUE) {
if ( *QueryPathBuffer == L'\\' ) {
if (FirstWack) {
QPPtr1 = QueryPathBuffer;
FirstWack = FALSE;
} else {
break;
}
}
if (!FirstWack) {
ShareNameLengthInBytes += sizeof(WCHAR);
}
QueryPathBufferLength -= sizeof(WCHAR);
if (QueryPathBufferLength == 0) {
break;
}
QueryPathBuffer++;
}
//
// If only a server name was specified then QPPrt1 will be NULL or
// QPPtr1 will not be NULL but ShareNameLengthInBytes == sizeof(WCHAR).
// QPPtr1 == NULL ==> \server
// ShareNameLengthInBytes == sizeof(WCHAR) ==> \server\
//
if ( QPPtr1 == NULL || ShareNameLengthInBytes == sizeof(WCHAR) ) {
NtStatus = STATUS_BAD_NETWORK_PATH;
if (QPPtr1 == NULL) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch: QPPtr1 == NULL\n",
PsGetCurrentThreadId()));
} else {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch: "
"ShareNameLengthInBytes == sizeof(WCHAR)\n",
PsGetCurrentThreadId()));
}
goto COMPLETE_THE_REQUEST;
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFsdDispatch: QPPtr1 = %ws\n",
PsGetCurrentThreadId(), QPPtr1));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFsdDispatch: ShareNameLengthInBytes = %d\n",
PsGetCurrentThreadId(), ShareNameLengthInBytes));
//
// Set the Unicode string. The OPPtr1 pointer points to the \ before
// the share name. So if the path was \server\share\dir,
// \server\share\dir
// ^
// |
// QPPtr1
// Accordingly, the ShareNameLengthInBytes contains an extra
// sizeof(WCHAR) bytes for the \ char.
//
UnicodeShareName.Buffer = QPPtr1;
UnicodeShareName.Length = (USHORT)ShareNameLengthInBytes;
UnicodeShareName.MaximumLength = (USHORT)ShareNameLengthInBytes;
//
// We now take this name and see if it matches any of the special
// SMB shares. If it does, we return STATUS_BAD_NETWORK_PATH.
//
SpecialShare = RtlEqualUnicodeString(&(UnicodeShareName),
&(s_PipeShareName),
TRUE);
if (SpecialShare) {
NtStatus = STATUS_BAD_NETWORK_PATH;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVFsdDispatch: PIPE == TRUE\n",
PsGetCurrentThreadId()));
goto COMPLETE_THE_REQUEST;
}
SpecialShare = RtlEqualUnicodeString(&(UnicodeShareName),
&(s_MailSlotShareName),
TRUE);
if (SpecialShare) {
NtStatus = STATUS_BAD_NETWORK_PATH;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVFsdDispatch: MAILSLOT == TRUE\n",
PsGetCurrentThreadId()));
goto COMPLETE_THE_REQUEST;
}
SpecialShare = RtlEqualUnicodeString(&(UnicodeShareName),
&(s_IpcShareName),
TRUE);
if (SpecialShare) {
NtStatus = STATUS_BAD_NETWORK_PATH;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVFsdDispatch: IPC$ == TRUE\n",
PsGetCurrentThreadId()));
goto COMPLETE_THE_REQUEST;
}
//
// Check whether we need to skip some files. See the explanation
// below (in the function definition) for why IRPs are skipped.
//
uniFileName.Buffer=(PWCHAR)(qpRequest->FilePathName);
uniFileName.Length = uniFileName.MaximumLength = (USHORT)(qpRequest->PathNameLength);
if (MRxDAVSkipIrps(Irp, &uniFileName, TRUE) == STATUS_SUCCESS)
{
NtStatus = STATUS_BAD_NETWORK_PATH;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVFsdDispatch: Skipped\n",
PsGetCurrentThreadId()));
goto COMPLETE_THE_REQUEST;
}
}
}
if (MajorFunctionCode == IRP_MJ_CREATE) {
//
// See the explanation below (in the function definition) for why
// IRPs are skipped. Send the filename in the fileobject.
//
if (MRxDAVSkipIrps(Irp, &FileObject->FileName, FALSE) == STATUS_SUCCESS)
{
NtStatus = STATUS_BAD_NETWORK_PATH;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVFsdDispatch: Skipped\n",
PsGetCurrentThreadId()));
goto COMPLETE_THE_REQUEST;
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {
NtStatus = STATUS_INVALID_PARAMETER;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch: Exception!!!\n",
PsGetCurrentThreadId()));
goto COMPLETE_THE_REQUEST;
}
//
// Save the filename passed in by the I/O manager. This is freed up later.
//
if (FileObject) {
SaveInitialString = FileObject->FileName.Buffer;
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFsdDispatch. MajorFunction = %d, MinorFunction = %d"
", FileObject = %08lx.\n", PsGetCurrentThreadId(),
MajorFunctionCode, MinorFunctionCode, FileObject));
if (SaveInitialString) {
ULONG MaxNameLengthInWChars = 0;
MaxNameLengthInWChars = ( FileObject->FileName.Length / sizeof(WCHAR) );
//
// If the first and the second chars are '\'s, then its possible that
// the name is just a \\server. Its possible that the name is of the
// form \;X:0\path and hence we check for the second \ as well. So,
// only if the first two chars are \ and \ we proceed to check whether
// the create is just for just a server.
//
if ( MaxNameLengthInWChars >= 2 &&
SaveInitialString[0] == L'\\' && SaveInitialString[1] == L'\\' ) {
PWCHAR wcPtr1 = NULL;
//
// We assume that this is of the form \\server. If its not, then
// this value is changed to FALSE below.
//
JustAServer = TRUE;
//
// Is the FileName just a server? Its possible that the FileName is
// of the form \\server.
//
wcPtr1 = &(SaveInitialString[2]);
//
// If we have a '\' after the first two chars and atleast a single
// char after that, it means that the name is not of the form
// \\server or \\server\.
//
while ( (MaxNameLengthInWChars - 2) > 0 ) {
if ( *wcPtr1 == L'\\' && *(wcPtr1 + 1) != L'\0' ) {
JustAServer = FALSE;
break;
}
MaxNameLengthInWChars--;
wcPtr1++;
}
}
}
//
// If JustAServer is TRUE then the network path name is invalid.
//
if (JustAServer) {
NtStatus = STATUS_BAD_NETWORK_PATH;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFsdDispatch: JustAServer == TRUE. SaveInitialString = %ws\n",
PsGetCurrentThreadId(), SaveInitialString));
goto COMPLETE_THE_REQUEST;
}
//
// Call RxFsdDispatch.
//
NtStatus = RxFsdDispatch(RxDeviceObject, Irp);
if (NtStatus != STATUS_SUCCESS && NtStatus != STATUS_PENDING) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVFsdDispatch with NtStatus(2) = %08lx,"
" FileObject = %08lx, MjFn = %d, MiFn = %d.\n",
PsGetCurrentThreadId(), NtStatus, FileObject,
MajorFunctionCode, MinorFunctionCode));
}
goto EXIT_THE_FUNCTION;
COMPLETE_THE_REQUEST:
//
// We come here if we did not call into RDBSS and need to complete the
// IRP ourselves.
//
Irp->IoStatus.Status = NtStatus;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
EXIT_THE_FUNCTION:
return NtStatus;
}
VOID
MRxDAVDeregisterAndCleanupDeviceObject(
PUMRX_DEVICE_OBJECT UMRefDeviceObject
)
/*++
Routine Description:
Note: The mutex is already acquired and we're already off the list.
Arguments:
UMRdrDeviceObject - The device object being deregistered and cleaned.
Return Value:
None.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVDeregisterAndCleanupDeviceObject!!!!\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVDeregisterAndCleanupDeviceObject: "
"UMRefDeviceObject: %08lx.\n",
PsGetCurrentThreadId(), UMRefDeviceObject));
NtStatus = UMRxCleanUpDeviceObject(UMRefDeviceObject);
if (!NT_SUCCESS(NtStatus)) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVDeregisterAndCleanupDeviceObject/"
"UMRxCleanUpDeviceObject: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
}
RxUnregisterMinirdr(&UMRefDeviceObject->RxDeviceObject);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVDeregisterAndCleanupDeviceObject.\n",
PsGetCurrentThreadId()));
}
NTSTATUS
MRxDAVFlush(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine handles the "File Flush" requests.
Arguments:
RxContext - The context created by RDBSS.
Return Value:
NTSTATUS or the appropriate NT error code.
--*/
{
PAGED_CODE();
return STATUS_SUCCESS;
}
VOID
MRxDAVPnPBindingHandler(
IN TDI_PNP_OPCODE PnPOpcode,
IN PUNICODE_STRING pTransportName,
IN PWSTR BindingList
)
/*++
Routine Description:
The TDI callbacks routine for binding changes.
Arguments:
PnPOpcode - The PNP op code.
pTransportName - The transport name.
BindingList - The binding order.
Return Value:
None.
--*/
{
PAGED_CODE();
switch (PnPOpcode) {
case TDI_PNP_OP_NETREADY: {
MRxDAVTransportReady = TRUE;
}
break;
default:
break;
}
return;
}
NTSTATUS
MRxDAVRegisterForPnpNotifications(
VOID
)
/*++
Routine Description:
This routine registers with TDI for receiving transport notifications.
Arguments:
None.
Return Value:
The NTSTATUS code.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
if ( MRxDAVTdiNotificationHandle == NULL ) {
UNICODE_STRING ClientName;
TDI_CLIENT_INTERFACE_INFO ClientInterfaceInfo;
RtlInitUnicodeString( &(ClientName), L"WebClient");
ClientInterfaceInfo.MajorTdiVersion = 2;
ClientInterfaceInfo.MinorTdiVersion = 0;
ClientInterfaceInfo.Unused = 0;
ClientInterfaceInfo.ClientName = &ClientName;
ClientInterfaceInfo.BindingHandler = MRxDAVPnPBindingHandler;
ClientInterfaceInfo.AddAddressHandler = NULL;
ClientInterfaceInfo.DelAddressHandler = NULL;
ClientInterfaceInfo.PnPPowerHandler = NULL;
NtStatus = TdiRegisterPnPHandlers ( &(ClientInterfaceInfo),
sizeof(ClientInterfaceInfo),
&(MRxDAVTdiNotificationHandle) );
}
return NtStatus;
}
NTSTATUS
MRxDAVDeregisterForPnpNotifications(
VOID
)
/*++
Routine Description:
This routine deregisters the TDI notification mechanism.
Arguments:
None.
Return Value:
The NTSTATUS code.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
if ( MRxDAVTdiNotificationHandle != NULL ) {
NtStatus = TdiDeregisterPnPHandlers( MRxDAVTdiNotificationHandle );
if( NT_SUCCESS( NtStatus ) ) {
MRxDAVTdiNotificationHandle = NULL;
}
}
return NtStatus;
}
NTSTATUS
MRxDAVProbeForReadWrite(
IN PBYTE BufferToBeValidated,
IN DWORD BufferSize,
IN BOOL doProbeForRead,
IN BOOL doProbeForWrite
)
/*++
Routine Description:
This function probes the buffer that is supplied by the caller for read/write
access. This is done because the caller of an IOCTL might supply a invalid
buffer accessing which might cause a bugcheck.
Arguments:
BufferToBeValidated - The Buffer which has to be validated for read/write
access.
BufferSize - The size of the buffer being validated.
doProbeForRead - If TRUE, then probe the buffer for read.
doProbeForWrite - If TRUE, then probe the buffer for write.
Return Value:
STATUS_SUCCESS or STATUS_INVALID_USER_BUFFER.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
//
// We call the functions ProbeForRead and ProbeForWrite in a try/except
// loop because these functions throw an exception if the buffer supplied
// is invalid. We catch the exception and set the appropriate NtStatus
// value.
//
try {
if (BufferToBeValidated != NULL) {
if (doProbeForRead) {
ProbeForRead(BufferToBeValidated, BufferSize, 1);
}
if (doProbeForWrite) {
ProbeForWrite(BufferToBeValidated, BufferSize, 1);
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
NtStatus = STATUS_INVALID_USER_BUFFER;
}
return NtStatus;
}
NTSTATUS
MRxDAVSkipIrps(
IN PIRP Irp,
IN PUNICODE_STRING fileName,
IN BOOL fCheckAny
)
/*++
Routine Description:
This routine skips IRPs coming to the DAV redir which may cause deadlock.
Webdav's user mode component uses wininet to get to DAV servers. When a
service which is running this server process satisfying key wininet needs
makes a remote call we deadlock. The two such services are winsock and Sense.
When another service in the svchost which they are running under tries to do
a loadlibrary located on a remote machine (in our famous example of \\davis\
tools\ifsproxy.dll), loader APIs get invoked. These APis take the loader lock
and issue an NtQueryAttributes call. This call is translated into QUERY_PATH
ioctl by the MUP whci it send to all redirs, including webdav. Webdav refelcts
it up to the usermode and the webdav service issues wininet call to look for
the server (davis in the above example). Wininet issues a call to winsock to
makes a sockets call. This call ends up issuing an rpc to the NLA service in
another svchost which is the same svchost process that initiated the loadlibrary
call. The server now tries to take the loader lock and the webdav redir is now
deadlocked.
This scheme also protects us from looping back to ourselves because of
wininet's loadlibrary calls as webdav service also runs as part of an svchost.
This routine looks for the process issuing the irp to webdav and if it is an
svchost process and it is trying to look for a dll or an exe then we return it
as being not found. This implies that dlls and exes kept on a webdav server
cannot be loaded from svchosts till we get away from wininet.
Arguments:
Irp - The irp that came to webdav.
filename - Name of the file if any.
fCheckAny - If this is TRUE, then we reject this IRP if the process is
svchost.exe. If this is FALSE, then we only reject the IRP if
the filename has the extension dll or exe and the process is
svchost.exe.
Return Value:
STATUS_SUCCESS - Skip this IRP.
STATUS_UNSUCCESSFUL - Do not skip this IRP.
--*/
{
WCHAR ImageFileName[DAV_SVCHOST_NAME_SIZE]; //keep some reasonable stack space
ULONG UnicodeSize = 0;
UNICODE_STRING uniImageFileName;
UCHAR *pchImageFileName = PsGetProcessImageFileName(PsGetCurrentProcess());
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PAGED_CODE();
RtlZeroMemory(ImageFileName, sizeof(ImageFileName));
RtlMultiByteToUnicodeN(ImageFileName, sizeof(ImageFileName), &UnicodeSize, pchImageFileName, 16);
uniImageFileName.Buffer = ImageFileName;
uniImageFileName.Length = uniImageFileName.MaximumLength = uniSvcHost.Length;
//
// Check whether the calling process is svchost.exe.
//
if (!RtlCompareUnicodeString(&uniImageFileName, &uniSvcHost, TRUE))
{
if (!fCheckAny)
{
UNICODE_STRING exe = { 3*sizeof(WCHAR), 3*sizeof(WCHAR), L"exe" };
UNICODE_STRING dll = { 3*sizeof(WCHAR), 3*sizeof(WCHAR), L"dll" };
UNICODE_STRING s;
//
// If the filename ends in .DLL or .exe, we return success, which will
// end up failing the operation.
//
if( fileName->Length > 4 * sizeof(WCHAR) &&
fileName->Buffer[ fileName->Length/sizeof(WCHAR) - 4 ] == L'.'){
s.Length = s.MaximumLength = 3 * sizeof( WCHAR );
s.Buffer = &fileName->Buffer[ (fileName->Length - s.Length)/sizeof(WCHAR) ];
if( RtlCompareUnicodeString( &s, &exe, TRUE ) == 0 ||
RtlCompareUnicodeString( &s, &dll, TRUE ) == 0 ) {
return STATUS_SUCCESS;
}
}
}
else
{
return STATUS_SUCCESS;
}
}
return STATUS_UNSUCCESSFUL;
}
VOID
MRxDAVInitializeTheTimeValues(
VOID
)
/*++
Routine Description:
This routine reads some time values (various timeout values, namecache etc.)
from the registry and initialized the corresponding global variables in the
driver. If a particular time value is not present in the registry, then it
is set to some default value. It also sets TimerThreadSleepTimeInSec to be
the minimum of all the operation timeout values.
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS RegNtStatus = STATUS_SUCCESS;
PAGED_CODE();
//
// Read the name cache related timeout values.
//
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
NAME_CACHE_OBJ_GET_FILE_ATTRIB_LIFETIME,
&(FileInformationCacheLifeTimeInSec));
if (RegNtStatus != STATUS_SUCCESS) {
FileInformationCacheLifeTimeInSec = 60;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
NAME_CACHE_OBJ_NAME_NOT_FOUND_LIFETIME,
&(FileNotFoundCacheLifeTimeInSec));
if (RegNtStatus != STATUS_SUCCESS) {
FileNotFoundCacheLifeTimeInSec = 60;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
NAME_CACHE_NETROOT_MAX_ENTRIES,
&(NameCacheMaxEntries));
if (RegNtStatus != STATUS_SUCCESS) {
NameCacheMaxEntries = 300;
}
//
// Read the timeout values for the various operations. Set the value of
// TimerThreadSleepTimeInSec to be the minimum of all the timeout values.
//
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
CREATE_REQUEST_TIMEOUT_IN_SEC,
&(CreateRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
CreateRequestTimeoutValueInSec = (10 * 60);
}
TimerThreadSleepTimeInSec = CreateRequestTimeoutValueInSec;
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
CREATEVNETROOT_REQUEST_TIMEOUT_IN_SEC,
&(CreateVNetRootRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
CreateVNetRootRequestTimeoutValueInSec = 60;
}
if (CreateVNetRootRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = CreateVNetRootRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
QUERYDIRECTORY_REQUEST_TIMEOUT_IN_SEC,
&(QueryDirectoryRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
QueryDirectoryRequestTimeoutValueInSec = (10 * 60);
}
if (QueryDirectoryRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = QueryDirectoryRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
CLOSE_REQUEST_TIMEOUT_IN_SEC,
&(CloseRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
CloseRequestTimeoutValueInSec = (10 * 60);
}
if (CloseRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = CloseRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
CREATESRVCALL_REQUEST_TIMEOUT_IN_SEC,
&(CreateSrvCallRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
CreateSrvCallRequestTimeoutValueInSec = 60;
}
if (CreateSrvCallRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = CreateSrvCallRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
FINALIZESRVCALL_REQUEST_TIMEOUT_IN_SEC,
&(FinalizeSrvCallRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
FinalizeSrvCallRequestTimeoutValueInSec = (10 * 60);
}
if (FinalizeSrvCallRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = FinalizeSrvCallRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
FINALIZEFOBX_REQUEST_TIMEOUT_IN_SEC,
&(FinalizeFobxRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
FinalizeFobxRequestTimeoutValueInSec = (10 * 60);
}
if (FinalizeFobxRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = FinalizeFobxRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
FINALIZEVNETROOT_REQUEST_TIMEOUT_IN_SEC,
&(FinalizeVNetRootRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
FinalizeVNetRootRequestTimeoutValueInSec = (10 * 60);
}
if (FinalizeVNetRootRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = FinalizeVNetRootRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
RENAME_REQUEST_TIMEOUT_IN_SEC,
&(ReNameRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
ReNameRequestTimeoutValueInSec = (10 * 60);
}
if (ReNameRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = ReNameRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
SETFILEINFO_REQUEST_TIMEOUT_IN_SEC,
&(SetFileInfoRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
SetFileInfoRequestTimeoutValueInSec = (10 * 60);
}
if (SetFileInfoRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = SetFileInfoRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
QUERYFILEINFO_REQUEST_TIMEOUT_IN_SEC,
&(QueryFileInfoRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
QueryFileInfoRequestTimeoutValueInSec = (10 * 60);
}
if (QueryFileInfoRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = QueryFileInfoRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
QUERYVOLUMEINFO_REQUEST_TIMEOUT_IN_SEC,
&(QueryVolumeInfoRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
QueryVolumeInfoRequestTimeoutValueInSec = (10 * 60);
}
if (QueryVolumeInfoRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = QueryVolumeInfoRequestTimeoutValueInSec;
}
RegNtStatus = UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY,
LOCKREFRESH_REQUEST_TIMEOUT_IN_SEC,
&(LockRefreshRequestTimeoutValueInSec));
if (RegNtStatus != STATUS_SUCCESS) {
LockRefreshRequestTimeoutValueInSec = (10 * 60);
}
if (LockRefreshRequestTimeoutValueInSec < TimerThreadSleepTimeInSec) {
TimerThreadSleepTimeInSec = LockRefreshRequestTimeoutValueInSec;
}
// DbgPrint("MRxDAVInitializeTheTimeValues: TimerThreadSleepTimeInSec = %d\n", TimerThreadSleepTimeInSec);
//
// Initialize the debug tracing for the Mini-Redir.
//
#if DBG
UMRxReadDWORDFromTheRegistry(MRXDAV_DEBUG_KEY, MRXDAV_DEBUG_VALUE, &(MRxDavDebugVector));
#endif
return;
}