3236 lines
93 KiB
C
3236 lines
93 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
transport.c
|
||
|
||
Abstract:
|
||
|
||
This module implements all transport related functions in the SMB connection engine
|
||
|
||
Revision History:
|
||
|
||
Balan Sethu Raman [SethuR] 6-March-1995
|
||
Will Lees (wlees) 08-Sep-1997 Initialize MoTcp Device
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include <nbtioctl.h>
|
||
#pragma hdrstop
|
||
|
||
#include "ntddbrow.h"
|
||
#include "tdikrnl.h"
|
||
#include "dfsfsctl.h"
|
||
|
||
NTSTATUS
|
||
SmbCeIsServerAvailable(
|
||
PUNICODE_STRING Name
|
||
);
|
||
|
||
VOID
|
||
SmbCeServerIsUnavailable(
|
||
PUNICODE_STRING Name,
|
||
NTSTATUS Status
|
||
);
|
||
|
||
VOID
|
||
SmbCeDiscardUnavailableServerList( );
|
||
|
||
VOID
|
||
MRxSmbpOverrideBindingPriority(
|
||
PUNICODE_STRING pTransportName,
|
||
PULONG pPriority
|
||
);
|
||
|
||
VOID
|
||
MRxSmbPnPBindingHandler(
|
||
IN TDI_PNP_OPCODE PnPOpcode,
|
||
IN PUNICODE_STRING pTransportName,
|
||
IN PWSTR BindingList
|
||
);
|
||
|
||
NTSTATUS
|
||
MRxSmbPnPPowerHandler(
|
||
IN PUNICODE_STRING DeviceName,
|
||
IN PNET_PNP_EVENT PowerEvent,
|
||
IN PTDI_PNP_CONTEXT Context1,
|
||
IN PTDI_PNP_CONTEXT Context2
|
||
);
|
||
|
||
VOID
|
||
SmbMRxNotifyChangesToNetBt(
|
||
IN TDI_PNP_OPCODE PnPOpcode,
|
||
IN PUNICODE_STRING DeviceName,
|
||
IN PWSTR MultiSZBindList
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, SmbCeFindTransport)
|
||
#pragma alloc_text(PAGE, SmbCepInitializeServerTransport)
|
||
#pragma alloc_text(PAGE, SmbCeInitializeExchangeTransport)
|
||
#pragma alloc_text(PAGE, SmbCeUninitializeExchangeTransport)
|
||
#pragma alloc_text(PAGE, SmbCepDereferenceTransport)
|
||
#pragma alloc_text(PAGE, MRxSmbpBindTransportCallback)
|
||
#pragma alloc_text(PAGE, MRxSmbpBindTransportWorkerThreadRoutine)
|
||
#pragma alloc_text(PAGE, MRxSmbBindTransportCallback)
|
||
#pragma alloc_text(PAGE, MRxSmbUnbindTransportCallback)
|
||
#pragma alloc_text(PAGE, MRxSmbRegisterForPnpNotifications)
|
||
#pragma alloc_text(PAGE, MRxSmbDeregisterForPnpNotifications)
|
||
#pragma alloc_text(PAGE, MRxSmbpBindTransportCallback)
|
||
#pragma alloc_text(PAGE, MRxSmbpBindTransportWorkerThreadRoutine)
|
||
#pragma alloc_text(PAGE, MRxSmbpUnbindTransportCallback)
|
||
#pragma alloc_text(PAGE, MRxSmbpOverrideBindingPriority)
|
||
#pragma alloc_text(PAGE, MRxSmbPnPBindingHandler)
|
||
#pragma alloc_text(PAGE, MRxSmbRegisterForPnpNotifications)
|
||
#pragma alloc_text(PAGE, MRxSmbDeregisterForPnpNotifications)
|
||
#pragma alloc_text(PAGE, SmbCePnpBindBrowser)
|
||
#pragma alloc_text(PAGE, SmbCeDereferenceTransportArray)
|
||
#pragma alloc_text(PAGE, SmbCeIsServerAvailable)
|
||
#pragma alloc_text(PAGE, SmbCeServerIsUnavailable)
|
||
#pragma alloc_text(PAGE, SmbCeDiscardUnavailableServerList)
|
||
#endif
|
||
|
||
SMBCE_TRANSPORTS MRxSmbTransports;
|
||
|
||
//
|
||
// The head of the list of servers that are currently unavailable
|
||
//
|
||
LIST_ENTRY UnavailableServerList = { &UnavailableServerList, &UnavailableServerList };
|
||
|
||
//
|
||
// Each entry in the UnavailableServerList is one of these:
|
||
//
|
||
typedef struct {
|
||
LIST_ENTRY ListEntry;
|
||
UNICODE_STRING Name; // Name of server that is unavailable
|
||
NTSTATUS Status; // Status received when we tried to connect to it
|
||
LARGE_INTEGER Time; // Time when we last attempted to connect
|
||
} *PUNAVAILABLE_SERVER;
|
||
|
||
//
|
||
// Protects UnavailableServerList
|
||
//
|
||
ERESOURCE UnavailableServerListResource = {0};
|
||
|
||
//
|
||
// Time (seconds) that we keep an entry in the UnavailableServerList.
|
||
// We will not retry a connection attempt to a server
|
||
// for UNAVAILABLE_SERVER_TIME seconds
|
||
//
|
||
#define UNAVAILABLE_SERVER_TIME 10
|
||
|
||
RXDT_DefineCategory(TRANSPRT);
|
||
#define Dbg (DEBUG_TRACE_TRANSPRT)
|
||
|
||
extern NTSTATUS
|
||
SmbCePnpBindBrowser(
|
||
PUNICODE_STRING pTransportName,
|
||
BOOLEAN IsBind);
|
||
|
||
|
||
NTSTATUS
|
||
MRxSmbInitializeTransport()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the transport related data structures
|
||
|
||
Returns:
|
||
|
||
STATUS_SUCCESS if the transport data structures was successfully initialized
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
KeInitializeSpinLock(&MRxSmbTransports.Lock);
|
||
|
||
MRxSmbTransports.pTransportArray = NULL;
|
||
|
||
ExInitializeResource( &UnavailableServerListResource );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MRxSmbUninitializeTransport()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uninitializes the transport related data structures
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
PSMBCE_TRANSPORT pTransport;
|
||
KIRQL SavedIrql;
|
||
ULONG TransportCount = 0;
|
||
PSMBCE_TRANSPORT_ARRAY pTransportArray = NULL;
|
||
|
||
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
||
|
||
if (MRxSmbTransports.pTransportArray != NULL) {
|
||
pTransportArray = MRxSmbTransports.pTransportArray;
|
||
MRxSmbTransports.pTransportArray = NULL;
|
||
}
|
||
|
||
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
||
|
||
if (pTransportArray != NULL) {
|
||
SmbCeDereferenceTransportArray(pTransportArray);
|
||
}
|
||
|
||
SmbCeDiscardUnavailableServerList();
|
||
|
||
ExDeleteResource( &UnavailableServerListResource );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SmbCeAddTransport(
|
||
PSMBCE_TRANSPORT pNewTransport)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds a new instance to the known list of transports
|
||
|
||
Parameters:
|
||
|
||
pNewTransport -- the transport instance to be added
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
KIRQL SavedIrql;
|
||
|
||
LONG Count;
|
||
PSMBCE_TRANSPORT_ARRAY pNewTransportArray = NULL;
|
||
PSMBCE_TRANSPORT_ARRAY pOldTransportArray;
|
||
PSMBCE_TRANSPORT *pTransports = NULL;
|
||
PRXCE_ADDRESS *LocalAddresses = NULL;
|
||
BOOLEAN SignalCscAgent = FALSE;
|
||
|
||
SmbCeAcquireResource();
|
||
|
||
pOldTransportArray = SmbCeReferenceTransportArray();
|
||
|
||
if (pOldTransportArray != NULL)
|
||
Count = pOldTransportArray->Count + 1;
|
||
else
|
||
Count = 1;
|
||
|
||
pNewTransportArray = (PSMBCE_TRANSPORT_ARRAY)RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(SMBCE_TRANSPORT_ARRAY),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
if (pNewTransportArray == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
pTransports = (PSMBCE_TRANSPORT *)RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
Count * sizeof(PSMBCE_TRANSPORT),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
if (pTransports == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
LocalAddresses = (PRXCE_ADDRESS *)RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
Count * sizeof(PRXCE_ADDRESS),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
if (LocalAddresses == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
LONG i;
|
||
|
||
if (Count > 1) {
|
||
PSMBCE_TRANSPORT *pOldTransports;
|
||
|
||
pOldTransports = pOldTransportArray->SmbCeTransports;
|
||
|
||
for (i=0;i<Count-1;i++) {
|
||
if (pNewTransport->Priority < pOldTransports[i]->Priority) { // The lower number, the higher priority
|
||
break;
|
||
}
|
||
pTransports[i] = pOldTransports[i];
|
||
LocalAddresses[i] = &pOldTransports[i]->RxCeAddress;
|
||
}
|
||
pTransports[i] = pNewTransport;
|
||
LocalAddresses[i] = &pNewTransport->RxCeAddress;
|
||
for (;i<Count-1;i++) {
|
||
pTransports[i+1] = pOldTransports[i];
|
||
LocalAddresses[i+1] = &pOldTransports[i]->RxCeAddress;
|
||
}
|
||
|
||
} else {
|
||
pTransports[0] = pNewTransport;
|
||
LocalAddresses[0] = &pNewTransport->RxCeAddress;
|
||
}
|
||
|
||
for(i=0;i<Count;i++)
|
||
SmbCeReferenceTransport(pTransports[i]);
|
||
|
||
pNewTransportArray->ReferenceCount = 1;
|
||
pNewTransportArray->Count = Count;
|
||
pNewTransportArray->SmbCeTransports = &pTransports[0];
|
||
pNewTransportArray->LocalAddresses = &LocalAddresses[0];
|
||
|
||
// signal the CSC agent if this is the first transport
|
||
SignalCscAgent = (pNewTransportArray->Count == 1);
|
||
|
||
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
||
MRxSmbTransports.pTransportArray = pNewTransportArray;
|
||
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
||
|
||
// Double dereferencing is necessary to ensure that
|
||
// the old transport array is destroyed.
|
||
|
||
SmbCeDereferenceTransportArray(pOldTransportArray);
|
||
}
|
||
|
||
SmbCeDereferenceTransportArray(pOldTransportArray);
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
MRxSmbCscSignalNetStatus(TRUE, SignalCscAgent);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
if (pNewTransportArray != NULL) {
|
||
RxFreePool(pNewTransportArray);
|
||
}
|
||
if (pTransports != NULL) {
|
||
RxFreePool(pTransports);
|
||
}
|
||
if (LocalAddresses != NULL) {
|
||
RxFreePool(LocalAddresses);
|
||
}
|
||
}
|
||
|
||
SmbCeDiscardUnavailableServerList();
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeRemoveTransport(
|
||
PSMBCE_TRANSPORT pTransport)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes a transport from the list of known transports
|
||
|
||
Parameters:
|
||
|
||
pTransport - the transport instance to be removed.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
KIRQL SavedIrql;
|
||
|
||
LONG Count;
|
||
PSMBCE_TRANSPORT_ARRAY pTransportArray = NULL;
|
||
PSMBCE_TRANSPORT_ARRAY pOldTransportArray = NULL;
|
||
PSMBCE_TRANSPORT *pTransports = NULL;
|
||
PRXCE_ADDRESS *pLocalAddresses = NULL;
|
||
|
||
BOOLEAN SignalCscAgent = FALSE, fReportRemovalToCSC=FALSE;
|
||
|
||
SmbCeAcquireResource();
|
||
|
||
pOldTransportArray = SmbCeReferenceTransportArray();
|
||
|
||
if (pOldTransportArray != NULL) {
|
||
LONG Index;
|
||
BOOLEAN Found = FALSE;
|
||
PSMBCE_TRANSPORT *pOldTransports;
|
||
|
||
// Establish the fact that the given transport is part of the array.
|
||
// if it is not then no further action is necessary
|
||
|
||
pOldTransports = pOldTransportArray->SmbCeTransports;
|
||
|
||
for (Index = 0; Index < (LONG)pOldTransportArray->Count; Index++) {
|
||
if (pTransport == pOldTransports[Index]) {
|
||
Found = TRUE;
|
||
}
|
||
}
|
||
|
||
if (Found) {
|
||
Count = pOldTransportArray->Count - 1;
|
||
fReportRemovalToCSC = (pOldTransportArray->Count != 0);
|
||
|
||
if (Count > 0) {
|
||
|
||
|
||
pTransportArray = (PSMBCE_TRANSPORT_ARRAY)RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(SMBCE_TRANSPORT_ARRAY),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
if (pTransportArray == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
pTransports = (PSMBCE_TRANSPORT *)RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
Count * sizeof(PSMBCE_TRANSPORT),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
if (pTransports == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
pLocalAddresses = (PRXCE_ADDRESS *)RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
Count * sizeof(PRXCE_ADDRESS),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
if (pLocalAddresses == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
LONG i, j;
|
||
|
||
for (i=0, j=0;i<Count+1;i++) {
|
||
if (pTransport != pOldTransports[i]) {
|
||
pTransports[j] = pOldTransports[i];
|
||
pLocalAddresses[j] = &pOldTransports[i]->RxCeAddress;
|
||
j++;
|
||
}
|
||
}
|
||
|
||
for(i=0;i<Count;i++)
|
||
SmbCeReferenceTransport(pTransports[i]);
|
||
|
||
pTransportArray->ReferenceCount = 1;
|
||
pTransportArray->Count = Count;
|
||
pTransportArray->SmbCeTransports = &pTransports[0];
|
||
pTransportArray->LocalAddresses = &pLocalAddresses[0];
|
||
}
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
// signal the CSC agent if this is the last transport
|
||
SignalCscAgent = (pTransportArray == NULL);
|
||
|
||
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
||
MRxSmbTransports.pTransportArray = pTransportArray;
|
||
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
||
|
||
// Double dereferencing is necessary to ensure that
|
||
// the old transport array is destroyed.
|
||
|
||
SmbCeDereferenceTransportArray(pOldTransportArray);
|
||
} else {
|
||
if (pTransportArray != NULL) {
|
||
RxFreePool(pTransportArray);
|
||
}
|
||
|
||
if (pTransports != NULL) {
|
||
RxFreePool(pTransports);
|
||
}
|
||
|
||
if (pLocalAddresses != NULL) {
|
||
RxFreePool(pLocalAddresses);
|
||
}
|
||
}
|
||
}
|
||
|
||
SmbCeDereferenceTransportArray(pOldTransportArray);
|
||
}
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
if (fReportRemovalToCSC)
|
||
{
|
||
MRxSmbCscSignalNetStatus(FALSE, SignalCscAgent);
|
||
}
|
||
|
||
SmbCeDiscardUnavailableServerList();
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
PSMBCE_TRANSPORT
|
||
SmbCeFindTransport(
|
||
PUNICODE_STRING pTransportName)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine maps a transport name to the appropriate
|
||
PSMBCE_TRANSPORT instance
|
||
|
||
Arguments:
|
||
|
||
pTransportName - the transport name
|
||
|
||
Return Value:
|
||
|
||
a valid PSMBCE_TRANSPORT if one exists otherwise NULL
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
KIRQL SavedIrql;
|
||
PLIST_ENTRY pEntry;
|
||
PSMBCE_TRANSPORT pTransport;
|
||
BOOLEAN Found = FALSE;
|
||
PSMBCE_TRANSPORT_ARRAY pTransportArray;
|
||
|
||
PAGED_CODE();
|
||
|
||
pTransportArray = SmbCeReferenceTransportArray();
|
||
|
||
if (pTransportArray == NULL) {
|
||
RxDbgTrace(0, Dbg, ("SmbCeFindTransport : Transport not available.\n"));
|
||
return NULL;
|
||
}
|
||
|
||
if (pTransportArray != NULL) {
|
||
ULONG i;
|
||
|
||
for (i=0;i<pTransportArray->Count;i++) {
|
||
pTransport = pTransportArray->SmbCeTransports[i];
|
||
|
||
if (RtlEqualUnicodeString(
|
||
&pTransport->RxCeTransport.Name,
|
||
pTransportName,
|
||
TRUE)) {
|
||
SmbCeReferenceTransport(pTransport);
|
||
Found = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!Found) {
|
||
pTransport = NULL;
|
||
}
|
||
|
||
SmbCeDereferenceTransportArray(pTransportArray);
|
||
|
||
return pTransport;
|
||
}
|
||
|
||
|
||
VOID
|
||
SmbCepTearDownServerTransport(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uninitializes the transport information corresponding to a server
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry instance in the database
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
SMBCEDB_SERVER_TYPE ServerType = SmbCeGetServerType(pServerEntry);
|
||
|
||
BOOLEAN WaitForMailSlotTransportRundown = FALSE;
|
||
BOOLEAN WaitForTransportRundown = FALSE;
|
||
BOOLEAN TearDown = FALSE;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
if (!pServerEntry->IsTransportDereferenced) {
|
||
|
||
// ServerEntry takes only one reference count of transport, which should only be
|
||
// dereferenced once when it comes to tear down transport. Multiple dereference called
|
||
// from construct server transport and PNP unbind transport needs to be prevented.
|
||
pServerEntry->IsTransportDereferenced = TRUE;
|
||
TearDown = TRUE;
|
||
|
||
KeInitializeEvent(&pServerEntry->MailSlotTransportRundownEvent,NotificationEvent,FALSE);
|
||
KeInitializeEvent(&pServerEntry->TransportRundownEvent,NotificationEvent,FALSE);
|
||
|
||
if (pServerEntry->pTransport != NULL) {
|
||
pServerEntry->pTransport->State = SMBCEDB_MARKED_FOR_DELETION;
|
||
pServerEntry->pTransport->pRundownEvent = &pServerEntry->TransportRundownEvent;
|
||
|
||
WaitForTransportRundown = TRUE;
|
||
}
|
||
|
||
if (pServerEntry->pMailSlotTransport != NULL) {
|
||
pServerEntry->pMailSlotTransport->State = SMBCEDB_MARKED_FOR_DELETION;
|
||
pServerEntry->pMailSlotTransport->pRundownEvent = &pServerEntry->MailSlotTransportRundownEvent;
|
||
|
||
WaitForMailSlotTransportRundown = TRUE;
|
||
}
|
||
} else {
|
||
if (pServerEntry->pTransport != NULL) {
|
||
WaitForTransportRundown = TRUE;
|
||
}
|
||
|
||
if (pServerEntry->pMailSlotTransport != NULL) {
|
||
WaitForMailSlotTransportRundown = TRUE;
|
||
}
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (TearDown) {
|
||
if (pServerEntry->pTransport != NULL) {
|
||
SmbCeDereferenceServerTransport(&pServerEntry->pTransport);
|
||
}
|
||
|
||
if (pServerEntry->pMailSlotTransport != NULL) {
|
||
SmbCeDereferenceServerTransport(&pServerEntry->pMailSlotTransport);
|
||
}
|
||
}
|
||
|
||
if (WaitForTransportRundown) {
|
||
KeWaitForSingleObject(
|
||
&pServerEntry->TransportRundownEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
}
|
||
|
||
if (WaitForMailSlotTransportRundown) {
|
||
KeWaitForSingleObject(
|
||
&pServerEntry->MailSlotTransportRundownEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
}
|
||
}
|
||
|
||
VOID
|
||
SmbCeTearDownServerTransport(
|
||
IN OUT PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tears down the server transport instance
|
||
|
||
Arguments:
|
||
|
||
pContext - the server transport construction context
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
{
|
||
SmbCepTearDownServerTransport(pContext->pServerEntry);
|
||
|
||
if (pContext->pCompletionEvent != NULL) {
|
||
ASSERT(pContext->pCallbackContext == NULL);
|
||
ASSERT(pContext->pCompletionRoutine == NULL);
|
||
KeSetEvent(
|
||
pContext->pCompletionEvent,
|
||
0,
|
||
FALSE );
|
||
} else if (pContext->pCallbackContext != NULL) {
|
||
ASSERT(pContext->pCompletionEvent == NULL);
|
||
(pContext->pCompletionRoutine)(pContext->pCallbackContext);
|
||
}
|
||
|
||
RxFreePool(pContext);
|
||
}
|
||
|
||
VOID
|
||
SmbCepUpdateTransportConstructionState(
|
||
IN OUT PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext)
|
||
{
|
||
SMBCE_SERVER_TRANSPORT_CONSTRUCTION_STATE State;
|
||
|
||
if (pContext->Status == STATUS_SUCCESS) {
|
||
if (pContext->TransportsToBeConstructed & SMBCE_STT_MAILSLOT) {
|
||
pContext->TransportsToBeConstructed &= ~SMBCE_STT_MAILSLOT;
|
||
State = SmbCeServerMailSlotTransportConstructionBegin;
|
||
} else if (pContext->TransportsToBeConstructed & SMBCE_STT_VC) {
|
||
pContext->TransportsToBeConstructed &= ~SMBCE_STT_VC;
|
||
State = SmbCeServerVcTransportConstructionBegin;
|
||
} else {
|
||
State = SmbCeServerTransportConstructionEnd;
|
||
}
|
||
} else {
|
||
State = SmbCeServerTransportConstructionEnd;
|
||
}
|
||
|
||
pContext->State = State;
|
||
}
|
||
|
||
VOID
|
||
SmbCeConstructServerTransport(
|
||
IN OUT PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine constructs the server transport instance
|
||
|
||
Arguments:
|
||
|
||
pContext - the server transport construction context
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
SMBCEDB_SERVER_TYPE ServerType;
|
||
|
||
BOOLEAN ContinueConstruction = TRUE;
|
||
BOOLEAN UpdateUnavailableServerlist = TRUE;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
||
|
||
pServerEntry = pContext->pServerEntry;
|
||
ServerType = SmbCeGetServerType(pServerEntry);
|
||
|
||
do {
|
||
switch (pContext->State) {
|
||
case SmbCeServerTransportConstructionBegin :
|
||
{
|
||
if ((pServerEntry->pTransport != NULL) ||
|
||
(pServerEntry->pMailSlotTransport != NULL)) {
|
||
SmbCepTearDownServerTransport(pServerEntry);
|
||
}
|
||
|
||
ASSERT((pServerEntry->pTransport == NULL) &&
|
||
(pServerEntry->pMailSlotTransport == NULL));
|
||
|
||
pContext->Status = STATUS_SUCCESS;
|
||
|
||
// See if we have any reason to believe this server is unavailable
|
||
pContext->Status = SmbCeIsServerAvailable( &pServerEntry->Name );
|
||
|
||
if (pContext->Status != STATUS_SUCCESS) {
|
||
UpdateUnavailableServerlist = FALSE;
|
||
}
|
||
|
||
SmbCepUpdateTransportConstructionState(pContext);
|
||
}
|
||
break;
|
||
|
||
case SmbCeServerMailSlotTransportConstructionBegin:
|
||
{
|
||
Status = MsInstantiateServerTransport(
|
||
pContext);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
ContinueConstruction = FALSE;
|
||
break;
|
||
}
|
||
|
||
ASSERT(pContext->State == SmbCeServerMailSlotTransportConstructionEnd);
|
||
}
|
||
// lack of break intentional
|
||
|
||
case SmbCeServerMailSlotTransportConstructionEnd:
|
||
{
|
||
SmbCepUpdateTransportConstructionState(pContext);
|
||
}
|
||
break;
|
||
|
||
case SmbCeServerVcTransportConstructionBegin:
|
||
{
|
||
Status = VctInstantiateServerTransport(
|
||
pContext);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
ContinueConstruction = FALSE;
|
||
break;
|
||
}
|
||
|
||
ASSERT(pContext->State == SmbCeServerVcTransportConstructionEnd);
|
||
}
|
||
// lack of break intentional
|
||
|
||
case SmbCeServerVcTransportConstructionEnd:
|
||
{
|
||
SmbCepUpdateTransportConstructionState(pContext);
|
||
}
|
||
break;
|
||
|
||
case SmbCeServerTransportConstructionEnd:
|
||
{
|
||
pServerEntry->ServerStatus = pContext->Status;
|
||
|
||
if (pServerEntry->ServerStatus == STATUS_SUCCESS) {
|
||
SmbCeAcquireSpinLock();
|
||
|
||
ASSERT(pContext->pMailSlotTransport != NULL);
|
||
pContext->pMailSlotTransport->SwizzleCount = 1;
|
||
|
||
if (pContext->pTransport != NULL) {
|
||
pContext->pTransport->SwizzleCount = 1;
|
||
}
|
||
|
||
pServerEntry->pTransport = pContext->pTransport;
|
||
pServerEntry->pMailSlotTransport = pContext->pMailSlotTransport;
|
||
|
||
pContext->pTransport = NULL;
|
||
pContext->pMailSlotTransport = NULL;
|
||
|
||
if (pContext->pCallbackContext != NULL) {
|
||
pContext->pCallbackContext->Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
pServerEntry->IsTransportDereferenced = FALSE;
|
||
pServerEntry->SecuritySignaturesActive = FALSE;
|
||
pServerEntry->SecuritySignaturesEnabled = FALSE;
|
||
|
||
SmbCeReleaseSpinLock();
|
||
} else {
|
||
PRX_CONTEXT pRxContext = NULL;
|
||
|
||
if (UpdateUnavailableServerlist &&
|
||
!pServerEntry->Server.IsRemoteBootServer &&
|
||
(pServerEntry->PreferredTransport == NULL)) {
|
||
// In remote boot or specific transport cases, we don't add it to
|
||
// the list so that no negative caching is introduced.
|
||
SmbCeServerIsUnavailable( &pServerEntry->Name, pServerEntry->ServerStatus );
|
||
}
|
||
|
||
if (pContext->pMailSlotTransport != NULL) {
|
||
pContext->pMailSlotTransport->pDispatchVector->TearDown(
|
||
pContext->pMailSlotTransport);
|
||
}
|
||
|
||
if (pContext->pTransport != NULL) {
|
||
pContext->pTransport->pDispatchVector->TearDown(
|
||
pContext->pTransport);
|
||
}
|
||
|
||
pContext->pTransport = NULL;
|
||
pContext->pMailSlotTransport = NULL;
|
||
|
||
pServerEntry->pTransport = NULL;
|
||
pServerEntry->pMailSlotTransport = NULL;
|
||
|
||
if ((pContext->pCallbackContext) &&
|
||
(pContext->pCallbackContext->SrvCalldownStructure)) {
|
||
pRxContext =
|
||
pContext->pCallbackContext->SrvCalldownStructure->RxContext;
|
||
}
|
||
|
||
Status = CscTransitionServerEntryForDisconnectedOperation(
|
||
pServerEntry,
|
||
pRxContext,
|
||
pServerEntry->ServerStatus,
|
||
TRUE // to autodial or not to autodial
|
||
);
|
||
|
||
if (pContext->pCallbackContext != NULL) {
|
||
pContext->pCallbackContext->Status = Status;
|
||
}
|
||
|
||
if (SmbCeIsServerInDisconnectedMode(pServerEntry)) {
|
||
pServerEntry->ServerStatus = STATUS_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
pServerEntry->ServerStatus = Status;
|
||
}
|
||
}
|
||
|
||
if (pContext->pCompletionEvent != NULL) {
|
||
ASSERT(pContext->pCallbackContext == NULL);
|
||
ASSERT(pContext->pCompletionRoutine == NULL);
|
||
KeSetEvent(
|
||
pContext->pCompletionEvent,
|
||
0,
|
||
FALSE );
|
||
} else if (pContext->pCallbackContext != NULL) {
|
||
ASSERT(pContext->pCompletionEvent == NULL);
|
||
|
||
(pContext->pCompletionRoutine)(pContext->pCallbackContext);
|
||
} else {
|
||
ASSERT(!"ill formed transport initialization context");
|
||
}
|
||
|
||
if (pContext->WorkQueueItem.List.Flink != NULL) {
|
||
//DbgBreakPoint();
|
||
}
|
||
|
||
// pServerEntry->ConstructionContext = NULL;
|
||
RxFreePool(pContext);
|
||
|
||
ContinueConstruction = FALSE;
|
||
}
|
||
}
|
||
} while (ContinueConstruction);
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCepInitializeServerTransport(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CALLBACK pCallbackRoutine,
|
||
PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext,
|
||
ULONG TransportsToBeConstructed)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the transport information corresponding to a server
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry instance in the database
|
||
|
||
pCallbackRoutine - the callback routine
|
||
|
||
pCallbackContext - the callback context
|
||
|
||
TransportsToBeConstructed -- the transports to be constructed
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server transport construction has been finalized.
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
Currently, only connection oriented transports are handled.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN CompleteConstruction;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((pServerEntry->ServerStatus == STATUS_SUCCESS) &&
|
||
(pServerEntry->pTransport != NULL) &&
|
||
(pServerEntry->pMailSlotTransport != NULL)) {
|
||
Status = STATUS_SUCCESS;
|
||
CompleteConstruction = TRUE;
|
||
} else {
|
||
PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext;
|
||
|
||
pContext = RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
|
||
CompleteConstruction = (pContext == NULL);
|
||
|
||
if (pContext != NULL) {
|
||
KEVENT CompletionEvent;
|
||
|
||
RtlZeroMemory(
|
||
pContext,
|
||
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT));
|
||
|
||
pContext->Status = STATUS_SUCCESS;
|
||
pContext->pServerEntry = pServerEntry;
|
||
pContext->State = SmbCeServerTransportConstructionBegin;
|
||
pContext->TransportsToBeConstructed = TransportsToBeConstructed;
|
||
|
||
if (pCallbackContext == NULL) {
|
||
KeInitializeEvent(
|
||
&CompletionEvent,
|
||
NotificationEvent,
|
||
FALSE);
|
||
|
||
pContext->pCompletionEvent = &CompletionEvent;
|
||
} else {
|
||
pContext->pCallbackContext = pCallbackContext;
|
||
pContext->pCompletionRoutine = pCallbackRoutine;
|
||
}
|
||
|
||
pServerEntry->ConstructionContext = (PVOID)pContext;
|
||
|
||
Status = STATUS_PENDING;
|
||
|
||
// always post to a worker thread. This is to avaoid the problem of
|
||
// a thread in system process that is impersonating for a non-admin user
|
||
// When this happens, the thread gets access denied while opening a transport
|
||
// handle
|
||
|
||
Status = RxPostToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
&pContext->WorkQueueItem,
|
||
SmbCeConstructServerTransport,
|
||
pContext);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = STATUS_PENDING;
|
||
} else {
|
||
pServerEntry->ConstructionContext = NULL;
|
||
RxFreePool(pContext);
|
||
CompleteConstruction = TRUE;
|
||
}
|
||
|
||
if ((Status == STATUS_PENDING) && (pCallbackContext == NULL)) {
|
||
KeWaitForSingleObject(
|
||
&CompletionEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
Status = pServerEntry->ServerStatus;
|
||
}
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if (CompleteConstruction) {
|
||
pServerEntry->ServerStatus = Status;
|
||
|
||
if (pCallbackRoutine != NULL) {
|
||
pCallbackContext->Status = Status;
|
||
|
||
(pCallbackRoutine)(pCallbackContext);
|
||
|
||
Status = STATUS_PENDING;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeUninitializeServerTransport(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PSMBCE_SERVER_TRANSPORT_DESTRUCTION_CALLBACK pCallbackRoutine,
|
||
PVOID pCallbackContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uninitializes the transport information corresponding to a server
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry instance in the database
|
||
|
||
Returns:
|
||
|
||
STATUS_SUCCESS if successful
|
||
|
||
Notes:
|
||
|
||
Currently, only connection oriented transports are handled.
|
||
|
||
In order to handle async. operations the uninitialization has to be coordinated
|
||
with the referencing mechanism. It is for this reason that this routine sets up
|
||
a rundown event and waits for it to be set.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (pCallbackRoutine == NULL &&
|
||
IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
||
SmbCepTearDownServerTransport(pServerEntry);
|
||
} else {
|
||
PSMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT pContext;
|
||
|
||
pContext = RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
|
||
if (pContext != NULL) {
|
||
KEVENT CompletionEvent;
|
||
|
||
RtlZeroMemory(
|
||
pContext,
|
||
sizeof(SMBCE_SERVER_TRANSPORT_CONSTRUCTION_CONTEXT));
|
||
|
||
pContext->Status = STATUS_SUCCESS;
|
||
pContext->pServerEntry = pServerEntry;
|
||
|
||
if (pCallbackRoutine == NULL) {
|
||
KeInitializeEvent(
|
||
&CompletionEvent,
|
||
NotificationEvent,
|
||
FALSE);
|
||
|
||
pContext->pCompletionEvent = &CompletionEvent;
|
||
} else {
|
||
pContext->pCallbackContext = pCallbackContext;
|
||
pContext->pCompletionRoutine = pCallbackRoutine;
|
||
}
|
||
|
||
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
||
SmbCeTearDownServerTransport(pContext);
|
||
} else {
|
||
Status = RxPostToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
&pContext->WorkQueueItem,
|
||
SmbCeTearDownServerTransport,
|
||
pContext);
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
if (pCallbackRoutine == NULL) {
|
||
KeWaitForSingleObject(
|
||
&CompletionEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
} else {
|
||
Status = STATUS_PENDING;
|
||
}
|
||
} else {
|
||
RxFreePool(pContext);
|
||
}
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
SmbCeCompleteUninitializeServerTransport(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
||
{
|
||
// in case of async uninitialize server transport, an additional reference count of
|
||
// server entry should be taken so that uninitialize server transport will not be
|
||
// called once again from tear down server entry if its reference count comes to 0
|
||
// before uninitialize server transport is done.
|
||
SmbCeDereferenceServerEntry(pServerEntry);
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeInitiateDisconnect(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initiates the TDI disconnect
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry instance in the database
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server transport construction has been finalized.
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMBCE_SERVER_TRANSPORT pTransport;
|
||
PSMBCE_SERVER_TRANSPORT pMailSlotTransport;
|
||
|
||
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
||
|
||
Status = SmbCeReferenceServerTransport(&pServerEntry->pTransport);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = (pServerEntry->pTransport->pDispatchVector->InitiateDisconnect)(
|
||
pServerEntry->pTransport);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
RxDbgTrace(0, Dbg, ("SmbCeInitiateDisconnect : Status %lx\n",Status));
|
||
}
|
||
|
||
SmbCeDereferenceServerTransport(&pServerEntry->pTransport);
|
||
}
|
||
|
||
Status = SmbCeReferenceServerTransport(&pServerEntry->pMailSlotTransport);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = (pServerEntry->pMailSlotTransport->pDispatchVector->InitiateDisconnect)(
|
||
pServerEntry->pMailSlotTransport);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
RxDbgTrace(0, Dbg, ("SmbCeInitiateDisconnect MS : Status %lx\n",Status));
|
||
}
|
||
|
||
SmbCeDereferenceServerTransport(&pServerEntry->pMailSlotTransport);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
LONG Initializes[SENTINEL_EXCHANGE] = {0,0,0,0,0};
|
||
LONG Uninitializes[SENTINEL_EXCHANGE] = {0,0,0,0,0};
|
||
|
||
NTSTATUS
|
||
SmbCeInitializeExchangeTransport(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the transport associated with the exchange
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be initialized
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the exchange transport initialization has been finalized.
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
PAGED_CODE();
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
|
||
Status = pExchange->SmbStatus;
|
||
if (Status == STATUS_SUCCESS) {
|
||
PSMBCE_SERVER_TRANSPORT *pTransportPointer;
|
||
|
||
if (FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
|
||
pTransportPointer = &pServerEntry->pMailSlotTransport;
|
||
} else {
|
||
pTransportPointer = &pServerEntry->pTransport;
|
||
}
|
||
|
||
if (*pTransportPointer != NULL) {
|
||
Status = SmbCeReferenceServerTransport(pTransportPointer);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = ((*pTransportPointer)->pDispatchVector->InitializeExchange)(
|
||
*pTransportPointer,
|
||
pExchange);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
ULONG TransportInitialized;
|
||
|
||
InterlockedIncrement(&Initializes[pExchange->Type]);
|
||
TransportInitialized = InterlockedExchange(&pExchange->ExchangeTransportInitialized,1);
|
||
ASSERT(TransportInitialized == 0);
|
||
} else {
|
||
SmbCeDereferenceServerTransport(pTransportPointer);
|
||
}
|
||
}
|
||
} else {
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeUninitializeExchangeTransport(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uniinitializes the transport associated with the exchange
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be initialized
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the exchange transport initialization has been finalized.
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
PAGED_CODE();
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
|
||
if (InterlockedExchange(&pExchange->ExchangeTransportInitialized,0)==1) {
|
||
PSMBCE_SERVER_TRANSPORT *pTransportPointer;
|
||
|
||
if (FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
|
||
pTransportPointer = &pServerEntry->pMailSlotTransport;
|
||
} else {
|
||
pTransportPointer = &pServerEntry->pTransport;
|
||
}
|
||
|
||
if (*pTransportPointer != NULL) {
|
||
Status = ((*pTransportPointer)->pDispatchVector->UninitializeExchange)(
|
||
*pTransportPointer,
|
||
pExchange);
|
||
|
||
SmbCeDereferenceServerTransport(pTransportPointer);
|
||
InterlockedIncrement(&Uninitializes[pExchange->Type]);
|
||
|
||
return Status;
|
||
} else {
|
||
return STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
} else {
|
||
return pExchange->SmbStatus;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCepReferenceServerTransport(
|
||
PSMBCE_SERVER_TRANSPORT *pServerTransportPointer)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine references the transport associated with a server entry
|
||
|
||
Arguments:
|
||
|
||
pServerEntry - the server entry instance in the database
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server transport was successfully referenced
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
if (*pServerTransportPointer != NULL &&
|
||
(*pServerTransportPointer)->State == SMBCEDB_ACTIVE) {
|
||
InterlockedIncrement(&(*pServerTransportPointer)->SwizzleCount);
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCepDereferenceServerTransport(
|
||
PSMBCE_SERVER_TRANSPORT *pServerTransportPointer)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dereferences the transport associated with a server entry
|
||
|
||
Arguments:
|
||
|
||
pServerTransportPointer - the server entry transport instance pointer
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server transport was successfully dereferenced
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
On finalization this routine sets the event to enable the process awaiting
|
||
tear down to restart. It also tears down the associated server transport
|
||
instance.
|
||
|
||
As a side effect the pointer value is set to NULL under the protection of a
|
||
spin lock.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
if (*pServerTransportPointer != NULL) {
|
||
LONG FinalRefCount;
|
||
PKEVENT pRundownEvent;
|
||
PSMBCE_SERVER_TRANSPORT pServerTransport;
|
||
|
||
pServerTransport = *pServerTransportPointer;
|
||
|
||
FinalRefCount = InterlockedDecrement(&pServerTransport->SwizzleCount);
|
||
|
||
if (FinalRefCount == 0) {
|
||
pServerTransport->State = SMBCEDB_INVALID;
|
||
|
||
// transport is set to NULL before the spinlock is release so that no
|
||
// exchange should reference it after it's been torn down
|
||
*pServerTransportPointer = NULL;
|
||
pRundownEvent = pServerTransport->pRundownEvent;
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (FinalRefCount == 0) {
|
||
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
||
pServerTransport->pDispatchVector->TearDown(pServerTransport);
|
||
} else {
|
||
Status = RxDispatchToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
pServerTransport->pDispatchVector->TearDown,
|
||
pServerTransport);
|
||
}
|
||
}
|
||
} else {
|
||
SmbCeReleaseSpinLock();
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SmbCepReferenceTransport(
|
||
PSMBCE_TRANSPORT pTransport)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine references the transport instance
|
||
|
||
Arguments:
|
||
|
||
pTransport - the transport instance
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server transport was successfully referenced
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
if (pTransport != NULL) {
|
||
SmbCeAcquireSpinLock();
|
||
|
||
if (pTransport->Active) {
|
||
InterlockedIncrement(&pTransport->SwizzleCount);
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
} else {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCepDereferenceTransport(
|
||
PSMBCE_TRANSPORT pTransport)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dereferences the transport
|
||
|
||
Arguments:
|
||
|
||
pTransport - the transport instance
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server transport was successfully dereferenced
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
BOOLEAN AttachToSystemProcess = FALSE;
|
||
KAPC_STATE ApcState;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (pTransport != NULL) {
|
||
LONG FinalRefCount;
|
||
|
||
FinalRefCount = InterlockedDecrement(&pTransport->SwizzleCount);
|
||
|
||
if (FinalRefCount == 0) {
|
||
SmbCeRemoveTransport(pTransport);
|
||
|
||
if (IoGetCurrentProcess() != RxGetRDBSSProcess()) {
|
||
KeStackAttachProcess(RxGetRDBSSProcess(),&ApcState);
|
||
AttachToSystemProcess = TRUE;
|
||
}
|
||
|
||
RxCeTearDownAddress(&pTransport->RxCeAddress);
|
||
|
||
RxCeTearDownTransport(&pTransport->RxCeTransport);
|
||
|
||
if (AttachToSystemProcess) {
|
||
KeUnstackDetachProcess(&ApcState);
|
||
}
|
||
|
||
RxFreePool(pTransport);
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
#ifndef MRXSMB_PNP_POWER5
|
||
|
||
HANDLE MRxSmbTdiNotificationHandle = NULL;
|
||
|
||
VOID
|
||
MRxSmbpBindTransportCallback(
|
||
IN PUNICODE_STRING pTransportName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
TDI calls this routine whenever a transport creates a new device object.
|
||
|
||
Arguments:
|
||
|
||
DeviceName - the name of the newly created device object
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PSMBCE_TRANSPORT pTransport;
|
||
|
||
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
||
|
||
ULONG Priority;
|
||
BOOLEAN fBindToTransport = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
||
|
||
// if this is one of the transports that is of interest to the SMB
|
||
// mini rdr then register the address with it, otherwise skip it.
|
||
|
||
if (SmbCeContext.Transports.Length != 0) {
|
||
PWSTR pSmbMRxTransports = (PWSTR)SmbCeContext.Transports.Buffer;
|
||
UNICODE_STRING SmbMRxTransport;
|
||
|
||
Priority = 1;
|
||
while (*pSmbMRxTransports) {
|
||
SmbMRxTransport.Length = wcslen(pSmbMRxTransports) * sizeof(WCHAR);
|
||
|
||
if (SmbMRxTransport.Length == pTransportName->Length) {
|
||
SmbMRxTransport.MaximumLength = SmbMRxTransport.Length;
|
||
SmbMRxTransport.Buffer = pSmbMRxTransports;
|
||
|
||
if (RtlCompareUnicodeString(
|
||
&SmbMRxTransport,
|
||
pTransportName,
|
||
TRUE) == 0) {
|
||
fBindToTransport = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
pSmbMRxTransports += (SmbMRxTransport.Length / sizeof(WCHAR) + 1);
|
||
Priority++;
|
||
}
|
||
}
|
||
|
||
if (!fBindToTransport) {
|
||
return;
|
||
}
|
||
|
||
pTransport = RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(SMBCE_TRANSPORT),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
|
||
if (pTransport != NULL) {
|
||
Status = RxCeBuildTransport(
|
||
&pTransport->RxCeTransport,
|
||
pTransportName,
|
||
0xffff);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
||
|
||
pProviderInfo = pTransport->RxCeTransport.pProviderInfo;
|
||
|
||
if (!(pProviderInfo->ServiceFlags & TDI_SERVICE_CONNECTION_MODE) ||
|
||
!(pProviderInfo->ServiceFlags & TDI_SERVICE_ERROR_FREE_DELIVERY)) {
|
||
RxCeTearDownTransport(
|
||
&pTransport->RxCeTransport);
|
||
|
||
Status = STATUS_PROTOCOL_UNREACHABLE;
|
||
|
||
RxFreePool(pTransport);
|
||
}
|
||
}
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
// The connection capabilities match the capabilities required by the
|
||
// SMB mini redirector. Attempt to register the local address with the
|
||
// transport and if successful update the local transport list to include
|
||
// this transport for future connection considerations.
|
||
|
||
OEM_STRING OemServerName;
|
||
CHAR TransportAddressBuffer[TDI_TRANSPORT_ADDRESS_LENGTH +
|
||
TDI_ADDRESS_LENGTH_NETBIOS];
|
||
PTRANSPORT_ADDRESS pTransportAddress = (PTRANSPORT_ADDRESS)TransportAddressBuffer;
|
||
PTDI_ADDRESS_NETBIOS pNetbiosAddress = (PTDI_ADDRESS_NETBIOS)pTransportAddress->Address[0].Address;
|
||
|
||
pTransportAddress->TAAddressCount = 1;
|
||
pTransportAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS;
|
||
pTransportAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
||
pNetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
||
|
||
OemServerName.MaximumLength = NETBIOS_NAME_LEN;
|
||
OemServerName.Buffer = pNetbiosAddress->NetbiosName;
|
||
|
||
Status = RtlUpcaseUnicodeStringToOemString(
|
||
&OemServerName,
|
||
&SmbCeContext.ComputerName,
|
||
FALSE);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
// Ensure that the name is always of the desired length by padding
|
||
// white space to the end.
|
||
RtlCopyMemory(
|
||
&OemServerName.Buffer[OemServerName.Length],
|
||
" ",
|
||
NETBIOS_NAME_LEN - OemServerName.Length);
|
||
|
||
OemServerName.Buffer[NETBIOS_NAME_LEN - 1] = '\0';
|
||
|
||
// Register the Transport address for this mini redirector with the connection
|
||
// engine.
|
||
|
||
Status = RxCeBuildAddress(
|
||
&pTransport->RxCeAddress,
|
||
&pTransport->RxCeTransport,
|
||
pTransportAddress,
|
||
&MRxSmbVctAddressEventHandler,
|
||
&SmbCeContext);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Adding new transport\n"));
|
||
|
||
pTransport->Active = TRUE;
|
||
pTransport->Priority = Priority;
|
||
pTransport->SwizzleCount = 0;
|
||
|
||
pTransport->ObjectCategory = SMB_SERVER_TRANSPORT_CATEGORY;
|
||
pTransport->ObjectType = SMBCEDB_OT_TRANSPORT;
|
||
pTransport->State = 0;
|
||
pTransport->Flags = 0;
|
||
|
||
// notify the browser about the transport
|
||
Status = SmbCePnpBindBrowser(pTransportName, TRUE);
|
||
|
||
// Add the transport to the list of transports
|
||
if (Status == STATUS_SUCCESS) {
|
||
SmbCeAddTransport(pTransport);
|
||
} else {
|
||
RxCeTearDownAddress(&pTransport->RxCeAddress);
|
||
|
||
MRxSmbLogTransportError(pTransportName,
|
||
&SmbCeContext.DomainName,
|
||
Status,
|
||
EVENT_RDR_CANT_BIND_TRANSPORT);
|
||
SmbLogError(Status,
|
||
LOG,
|
||
MRxSmbpBindTransportCallback_1,
|
||
LOGULONG(Status)
|
||
LOGUSTR(*pTransportName));
|
||
|
||
}
|
||
} else {
|
||
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Address registration failed %lx\n",Status));
|
||
MRxSmbLogTransportError(pTransportName,
|
||
&SmbCeContext.DomainName,
|
||
Status,
|
||
EVENT_RDR_CANT_REGISTER_ADDRESS);
|
||
SmbLogError(Status,
|
||
LOG,
|
||
MRxSmbpBindTransportCallback_2,
|
||
LOGUSTR(*pTransportName));
|
||
}
|
||
}
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
RxCeTearDownTransport(
|
||
&pTransport->RxCeTransport);
|
||
|
||
Status = STATUS_PROTOCOL_UNREACHABLE;
|
||
RxFreePool(pTransport);
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
MRxSmbpBindTransportWorkerThreadRoutine(
|
||
IN PUNICODE_STRING pTransportName)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
MRxSmbpBindTransportCallback(pTransportName);
|
||
|
||
RxFreePool(pTransportName);
|
||
}
|
||
|
||
VOID
|
||
MRxSmbBindTransportCallback(
|
||
IN PUNICODE_STRING pTransportName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
TDI calls this routine whenever a transport creates a device object
|
||
|
||
Arguments:
|
||
|
||
TransportName = the name of the deleted device object
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
||
MRxSmbpBindTransportCallback(pTransportName);
|
||
} else {
|
||
PUNICODE_STRING pNewTransportName;
|
||
NTSTATUS Status;
|
||
|
||
pNewTransportName = RxAllocatePoolWithTag(
|
||
PagedPool,
|
||
sizeof(UNICODE_STRING) + pTransportName->Length,
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
|
||
if (pNewTransportName != NULL) {
|
||
pNewTransportName->MaximumLength = pTransportName->MaximumLength;
|
||
pNewTransportName->Length = pTransportName->Length;
|
||
pNewTransportName->Buffer = (PWCHAR)((PBYTE)pNewTransportName +
|
||
sizeof(UNICODE_STRING));
|
||
|
||
RtlCopyMemory(
|
||
pNewTransportName->Buffer,
|
||
pTransportName->Buffer,
|
||
pNewTransportName->Length);
|
||
|
||
Status = RxDispatchToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
MRxSmbpBindTransportWorkerThreadRoutine,
|
||
pNewTransportName);
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status != RX_MAP_STATUS(SUCCESS)) {
|
||
RxLog(("SmbCe Tdi Bind .Error %lx\n", Status));
|
||
|
||
MRxSmbLogTransportError(pTransportName,
|
||
&SmbCeContext.DomainName,
|
||
Status,
|
||
EVENT_RDR_CANT_BIND_TRANSPORT);
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
MRxSmbUnbindTransportCallback(
|
||
IN PUNICODE_STRING pTransportName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
TDI calls this routine whenever a transport deletes a device object
|
||
|
||
Arguments:
|
||
|
||
TransportName = the name of the deleted device object
|
||
|
||
--*/
|
||
{
|
||
PSMBCE_TRANSPORT pTransport;
|
||
|
||
PAGED_CODE();
|
||
|
||
pTransport = SmbCeFindTransport(pTransportName);
|
||
|
||
if (pTransport != NULL) {
|
||
// notify the browser about the transport
|
||
SmbCePnpBindBrowser(pTransportName, FALSE);
|
||
|
||
// Remove this transport from the list of transports under consideration
|
||
// in the mini redirector.
|
||
SmbCeRemoveTransport(pTransport);
|
||
|
||
// Enumerate the servers and mark those servers utilizing this transport
|
||
// as having an invalid transport.
|
||
SmbCeHandleTransportInvalidation(pTransport);
|
||
|
||
// dereference the transport
|
||
SmbCeDereferenceTransport(pTransport);
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
MRxSmbRegisterForPnpNotifications()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine registers with TDI for receiving transport notifications
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if(MRxSmbTdiNotificationHandle == NULL ) {
|
||
Status = TdiRegisterNotificationHandler (
|
||
MRxSmbBindTransportCallback,
|
||
MRxSmbUnbindTransportCallback,
|
||
&MRxSmbTdiNotificationHandle );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
MRxSmbDeregisterForPnpNotifications()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deregisters the TDI notification mechanism
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if( MRxSmbTdiNotificationHandle != NULL ) {
|
||
Status = TdiDeregisterNotificationHandler( MRxSmbTdiNotificationHandle );
|
||
|
||
if( NT_SUCCESS( Status ) ) {
|
||
MRxSmbTdiNotificationHandle = NULL;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
#else
|
||
|
||
HANDLE MRxSmbTdiNotificationHandle = NULL;
|
||
|
||
KEVENT TdiNetStartupCompletionEvent;
|
||
|
||
LONG TdiBindRequestsActive = 0;
|
||
|
||
BOOLEAN TdiPnpNetReadyEventReceived = FALSE;
|
||
|
||
// The TRANSPORT_BIND_CONTEXT contains the result of the priority determination
|
||
// as well as the name. The priority is used to order the transports in the order
|
||
// in which connection attempts will be made
|
||
|
||
typedef struct _TRANSPORT_BIND_CONTEXT_ {
|
||
ULONG Priority;
|
||
UNICODE_STRING TransportName;
|
||
} TRANSPORT_BIND_CONTEXT, *PTRANSPORT_BIND_CONTEXT;
|
||
|
||
VOID
|
||
SmbCeSignalNetReadyEvent()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine signals the net ready event if all the bind requests
|
||
have been completed and if the net ready event has been received from TDI
|
||
|
||
Arguments:
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN SignalNetReadyEvent = FALSE;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
if (TdiPnpNetReadyEventReceived &&
|
||
TdiBindRequestsActive == 0) {
|
||
SignalNetReadyEvent = TRUE;
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (SignalNetReadyEvent) {
|
||
KeSetEvent(
|
||
&TdiNetStartupCompletionEvent,
|
||
IO_NETWORK_INCREMENT,
|
||
FALSE);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
MRxSmbpBindTransportCallback(
|
||
IN PTRANSPORT_BIND_CONTEXT pTransportContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
TDI calls this routine whenever a transport creates a new device object.
|
||
|
||
Arguments:
|
||
|
||
TransportName - the name of the newly created device object
|
||
|
||
TransportBindings - the transport bindings ( multi sz)
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PSMBCE_TRANSPORT pTransport;
|
||
|
||
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
||
|
||
PUNICODE_STRING pTransportName;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(IoGetCurrentProcess() == RxGetRDBSSProcess());
|
||
|
||
pTransportName = &pTransportContext->TransportName;
|
||
|
||
RxDbgTrace( 0, Dbg, ("MrxSmbpBindTransportCallback, Transport Name = %wZ\n", pTransportName ));
|
||
|
||
pTransport = RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(SMBCE_TRANSPORT),
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
|
||
if (pTransport != NULL) {
|
||
Status = RxCeBuildTransport(
|
||
&pTransport->RxCeTransport,
|
||
pTransportName,
|
||
0xffff);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;
|
||
|
||
pProviderInfo = pTransport->RxCeTransport.pProviderInfo;
|
||
|
||
if (!(pProviderInfo->ServiceFlags & TDI_SERVICE_CONNECTION_MODE) ||
|
||
!(pProviderInfo->ServiceFlags & TDI_SERVICE_ERROR_FREE_DELIVERY)) {
|
||
RxCeTearDownTransport(
|
||
&pTransport->RxCeTransport);
|
||
|
||
Status = STATUS_PROTOCOL_UNREACHABLE;
|
||
|
||
RxFreePool(pTransport);
|
||
}
|
||
}
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
// The connection capabilities match the capabilities required by the
|
||
// SMB mini redirector. Attempt to register the local address with the
|
||
// transport and if successful update the local transport list to include
|
||
// this transport for future connection considerations.
|
||
|
||
OEM_STRING OemServerName;
|
||
CHAR TransportAddressBuffer[TDI_TRANSPORT_ADDRESS_LENGTH +
|
||
TDI_ADDRESS_LENGTH_NETBIOS];
|
||
PTRANSPORT_ADDRESS pTransportAddress = (PTRANSPORT_ADDRESS)TransportAddressBuffer;
|
||
PTDI_ADDRESS_NETBIOS pNetbiosAddress = (PTDI_ADDRESS_NETBIOS)pTransportAddress->Address[0].Address;
|
||
|
||
pTransportAddress->TAAddressCount = 1;
|
||
pTransportAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS;
|
||
pTransportAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
||
pNetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
||
|
||
OemServerName.MaximumLength = NETBIOS_NAME_LEN;
|
||
OemServerName.Buffer = pNetbiosAddress->NetbiosName;
|
||
|
||
Status = RtlUpcaseUnicodeStringToOemString(
|
||
&OemServerName,
|
||
&SmbCeContext.ComputerName,
|
||
FALSE);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
// Ensure that the name is always of the desired length by padding
|
||
// white space to the end.
|
||
RtlCopyMemory(
|
||
&OemServerName.Buffer[OemServerName.Length],
|
||
" ",
|
||
NETBIOS_NAME_LEN - OemServerName.Length);
|
||
|
||
OemServerName.Buffer[NETBIOS_NAME_LEN - 1] = '\0';
|
||
|
||
// Register the Transport address for this mini redirector with the connection
|
||
// engine.
|
||
|
||
Status = RxCeBuildAddress(
|
||
&pTransport->RxCeAddress,
|
||
&pTransport->RxCeTransport,
|
||
pTransportAddress,
|
||
&MRxSmbVctAddressEventHandler,
|
||
&SmbCeContext);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Adding new transport\n"));
|
||
|
||
pTransport->Active = TRUE;
|
||
pTransport->Priority = pTransportContext->Priority;
|
||
pTransport->SwizzleCount = 0;
|
||
|
||
pTransport->ObjectCategory = SMB_SERVER_TRANSPORT_CATEGORY;
|
||
pTransport->ObjectType = SMBCEDB_OT_TRANSPORT;
|
||
pTransport->State = 0;
|
||
pTransport->Flags = 0;
|
||
|
||
// notify the browser about the transport
|
||
Status = SmbCePnpBindBrowser(pTransportName, TRUE);
|
||
|
||
if (MRxSmbBootedRemotely && (Status == STATUS_REDIRECTOR_NOT_STARTED)) {
|
||
|
||
//
|
||
// Ignore failures here, because when starting during
|
||
// textmode setup in remote boot, the browser is not around.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
// Add the transport to the list of transports
|
||
if (Status == STATUS_SUCCESS) {
|
||
SmbCeAddTransport(pTransport);
|
||
RxDbgTrace( 0, Dbg, ("MrxSmbpBindTransportCallback, Transport %wZ added\n", pTransportName ));
|
||
} else {
|
||
RxCeTearDownAddress(&pTransport->RxCeAddress);
|
||
|
||
MRxSmbLogTransportError(pTransportName,
|
||
&SmbCeContext.DomainName,
|
||
Status,
|
||
EVENT_RDR_CANT_BIND_TRANSPORT);
|
||
SmbLogError(Status,
|
||
LOG,
|
||
MRxSmbpBindTransportCallback_1,
|
||
LOGULONG(Status)
|
||
LOGUSTR(*pTransportName));
|
||
}
|
||
} else {
|
||
RxDbgTrace( 0, Dbg, ("MRxSmbTransportUpdateHandler: Address registration failed %lx\n",Status));
|
||
MRxSmbLogTransportError(pTransportName,
|
||
&SmbCeContext.DomainName,
|
||
Status,
|
||
EVENT_RDR_CANT_REGISTER_ADDRESS);
|
||
SmbLogError(Status,
|
||
LOG,
|
||
MRxSmbpBindTransportCallback_2,
|
||
LOGULONG(Status)
|
||
LOGUSTR(*pTransportName));
|
||
}
|
||
}
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
RxDbgTrace( 0, Dbg, ("MrxSmbpBindTransportCallback, Transport %wZ unreachable 0x%x\n",
|
||
pTransportName, Status ));
|
||
RxCeTearDownTransport(
|
||
&pTransport->RxCeTransport);
|
||
|
||
Status = STATUS_PROTOCOL_UNREACHABLE;
|
||
RxFreePool(pTransport);
|
||
}
|
||
}
|
||
|
||
InterlockedDecrement(&TdiBindRequestsActive);
|
||
SmbCeSignalNetReadyEvent();
|
||
}
|
||
|
||
VOID
|
||
MRxSmbpBindTransportWorkerThreadRoutine(
|
||
IN PTRANSPORT_BIND_CONTEXT pTransportContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The TDI callbacks always do not occur in the context of the FSP process.
|
||
Since there are a few TDi interfaces that accept handles we need to ensure
|
||
that such calls always gets funnelled back to the FSP.
|
||
|
||
Arguments:
|
||
|
||
pTransportContext - the transport binding context
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
MRxSmbpBindTransportCallback(pTransportContext);
|
||
|
||
RxFreePool(pTransportContext);
|
||
}
|
||
|
||
VOID
|
||
MRxSmbpUnbindTransportCallback(
|
||
PSMBCE_TRANSPORT pTransport)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The Unbind callback routine which is always executed in the context of the
|
||
RDR process so that handles can be closed correctly
|
||
|
||
Arguments:
|
||
|
||
pTransport - the transport for which the PNP_OP_DEL was received
|
||
|
||
Notes:
|
||
|
||
On entry to this routine the appropriate transport must have been referenced
|
||
This routine will dereference it and invalidate the existing exchanges using
|
||
this transport.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// notify the browser about the transport
|
||
SmbCePnpBindBrowser(&pTransport->RxCeTransport.Name, FALSE);
|
||
|
||
// Remove this transport from the list of transports under consideration
|
||
// in the mini redirector.
|
||
|
||
SmbCeRemoveTransport(pTransport);
|
||
|
||
// Enumerate the servers and mark those servers utilizing this transport
|
||
// as having an invalid transport.
|
||
SmbCeHandleTransportInvalidation(pTransport);
|
||
|
||
// dereference the transport
|
||
SmbCeDereferenceTransport(pTransport);
|
||
}
|
||
|
||
|
||
VOID
|
||
MRxSmbpOverrideBindingPriority(
|
||
PUNICODE_STRING pTransportName,
|
||
PULONG pPriority
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function obtains a overriding priority value from the registry for a given
|
||
transport.
|
||
|
||
The priority of a transport controls the order in which connections are accepted. It is
|
||
sometimes useful for a customer to control which transport is used first in the redirector.
|
||
|
||
The priority is usually determined by the order of the transports in the binding list. With
|
||
the new Connections UI model for network setup, it will no longer be possible to adjust
|
||
the order of the bindings in the binding list. Thus, another mechanism is needed when the
|
||
user wants to override the priority assigned to a given binding.
|
||
|
||
Arguments:
|
||
|
||
pTransportName - pointer to UNICODE_STRING descriptor for transport string, for example
|
||
"\Device\Netbt_tcpip_{guid}"
|
||
pPriority - pointer to LONG to receive new priority on success, otherwise not touched
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
WCHAR valueBuffer[128];
|
||
UNICODE_STRING path, value, key;
|
||
USHORT length,ulength;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
NTSTATUS status;
|
||
HANDLE parametersHandle;
|
||
ULONG temp;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Validate input
|
||
|
||
if (pTransportName->Length == 0) {
|
||
return;
|
||
}
|
||
|
||
// Open parameters key
|
||
|
||
RtlInitUnicodeString( &path, SMBMRX_MINIRDR_PARAMETERS );
|
||
|
||
InitializeObjectAttributes(
|
||
&objectAttributes,
|
||
&path,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenKey (¶metersHandle, KEY_READ, &objectAttributes);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return;
|
||
}
|
||
|
||
// Construct value name = "BindingPriority" + transportname
|
||
// First, find the last slash. Then form the value from the prefix and
|
||
// the remainder of the transport name.
|
||
ulength = pTransportName->Length / sizeof(WCHAR);
|
||
for( length = ulength - 1; length != 0; length-- ) {
|
||
if (pTransportName->Buffer[length] == L'\\') {
|
||
break;
|
||
}
|
||
}
|
||
|
||
length++;
|
||
key.Buffer = pTransportName->Buffer + length;
|
||
key.Length = (ulength - length) * sizeof(WCHAR);
|
||
|
||
value.Buffer = valueBuffer;
|
||
value.MaximumLength = 128 * sizeof(WCHAR);
|
||
value.Length = 0;
|
||
|
||
RtlAppendUnicodeToString( &value, L"BindingPriority" );
|
||
RtlAppendUnicodeStringToString( &value, &key );
|
||
|
||
// Check if the value is present. If so, replace priority
|
||
// A value of zero is valid and indicates do not bind this one
|
||
|
||
status = MRxSmbGetUlongRegistryParameter(
|
||
parametersHandle,
|
||
value.Buffer,
|
||
(PULONG)&temp,
|
||
FALSE );
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
*pPriority = temp;
|
||
}
|
||
|
||
ZwClose(parametersHandle);
|
||
}
|
||
|
||
VOID
|
||
MRxSmbPnPBindingHandler(
|
||
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
|
||
|
||
--*/
|
||
{
|
||
ULONG Priority;
|
||
|
||
PAGED_CODE();
|
||
|
||
switch (PnPOpcode) {
|
||
case TDI_PNP_OP_ADD:
|
||
{
|
||
BOOLEAN fBindToTransport = FALSE;
|
||
PWSTR pSmbMRxTransports;
|
||
UNICODE_STRING SmbMRxTransport;
|
||
NTSTATUS Status;
|
||
|
||
Status = SmbCeGetConfigurationInformation();
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
return;
|
||
}
|
||
|
||
pSmbMRxTransports = (PWSTR)SmbCeContext.Transports.Buffer;
|
||
Priority = 1;
|
||
while (*pSmbMRxTransports) {
|
||
SmbMRxTransport.Length = wcslen(pSmbMRxTransports) * sizeof(WCHAR);
|
||
|
||
if (SmbMRxTransport.Length == pTransportName->Length) {
|
||
SmbMRxTransport.MaximumLength = SmbMRxTransport.Length;
|
||
SmbMRxTransport.Buffer = pSmbMRxTransports;
|
||
|
||
if (RtlCompareUnicodeString(
|
||
&SmbMRxTransport,
|
||
pTransportName,
|
||
TRUE) == 0) {
|
||
fBindToTransport = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
pSmbMRxTransports += (SmbMRxTransport.Length / sizeof(WCHAR) + 1);
|
||
Priority++;
|
||
}
|
||
|
||
// Provide a local registry means to alter binding priority
|
||
if (fBindToTransport) {
|
||
MRxSmbpOverrideBindingPriority( pTransportName, &Priority );
|
||
fBindToTransport = (Priority != 0);
|
||
}
|
||
|
||
if (fBindToTransport) {
|
||
InterlockedIncrement(&TdiBindRequestsActive);
|
||
|
||
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
||
TRANSPORT_BIND_CONTEXT TransportContext;
|
||
|
||
TransportContext.Priority = Priority;
|
||
TransportContext.TransportName = *pTransportName;
|
||
MRxSmbpBindTransportCallback(&TransportContext);
|
||
} else {
|
||
PTRANSPORT_BIND_CONTEXT pNewTransportContext;
|
||
|
||
NTSTATUS Status;
|
||
|
||
pNewTransportContext = RxAllocatePoolWithTag(
|
||
PagedPool,
|
||
sizeof(TRANSPORT_BIND_CONTEXT) + pTransportName->Length,
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
|
||
if (pNewTransportContext != NULL) {
|
||
pNewTransportContext->Priority = Priority;
|
||
pNewTransportContext->TransportName.MaximumLength = pTransportName->MaximumLength;
|
||
pNewTransportContext->TransportName.Length = pTransportName->Length;
|
||
pNewTransportContext->TransportName.Buffer = (PWCHAR)((PBYTE)pNewTransportContext +
|
||
sizeof(TRANSPORT_BIND_CONTEXT));
|
||
|
||
RtlCopyMemory(
|
||
pNewTransportContext->TransportName.Buffer,
|
||
pTransportName->Buffer,
|
||
pTransportName->Length);
|
||
|
||
Status = RxDispatchToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
MRxSmbpBindTransportWorkerThreadRoutine,
|
||
pNewTransportContext);
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
InterlockedDecrement(&TdiBindRequestsActive);
|
||
SmbCeSignalNetReadyEvent();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case TDI_PNP_OP_DEL:
|
||
{
|
||
PSMBCE_TRANSPORT pTransport;
|
||
|
||
pTransport = SmbCeFindTransport(pTransportName);
|
||
|
||
if (pTransport != NULL) {
|
||
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
|
||
MRxSmbpUnbindTransportCallback(pTransport);
|
||
} else {
|
||
NTSTATUS Status;
|
||
|
||
Status = RxDispatchToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
MRxSmbpUnbindTransportCallback,
|
||
pTransport);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case TDI_PNP_OP_UPDATE:
|
||
{
|
||
}
|
||
break;
|
||
|
||
case TDI_PNP_OP_NETREADY:
|
||
{
|
||
TdiPnpNetReadyEventReceived = TRUE;
|
||
SmbCeSignalNetReadyEvent();
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if( PnPOpcode != TDI_PNP_OP_NETREADY )
|
||
{
|
||
SmbMRxNotifyChangesToNetBt( PnPOpcode, pTransportName, BindingList );
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
MRxSmbPnPPowerHandler(
|
||
IN PUNICODE_STRING DeviceName,
|
||
IN PNET_PNP_EVENT PowerEvent,
|
||
IN PTDI_PNP_CONTEXT Context1,
|
||
IN PTDI_PNP_CONTEXT Context2
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deals with power changes
|
||
|
||
Notes:
|
||
|
||
The implementation needs to be completed
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
LONG NumberOfActiveOpens;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
RxPurgeAllFobxs(MRxSmbDeviceObject);
|
||
|
||
RxScavengeAllFobxs(MRxSmbDeviceObject);
|
||
NumberOfActiveOpens = MRxSmbNumberOfSrvOpens;
|
||
|
||
switch (PowerEvent->NetEvent) {
|
||
case NetEventQueryPower:
|
||
{
|
||
// If the redirector were to return an error on this request there
|
||
// is no underlying support to tell the user about the files that
|
||
// are open. There are two approaches to doing this.. either the RDR
|
||
// rolls its own UI or the PNP manager provides the infra structure.
|
||
// The problem with the former is that hibernation becomes a painstaking
|
||
// process wherein the user has to contend with a variety of UI.
|
||
// Till this is resolved the decision was to use the power mgmt. API
|
||
// to manage system initiated hibernate requests and succeed user
|
||
// initiated requests after appropriate purging/scavenging.
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
case NetEventQueryRemoveDevice:
|
||
{
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
ULONG NumberOfFilesOpen = 0;
|
||
PSMBCE_TRANSPORT pTransport = NULL;
|
||
|
||
pTransport = SmbCeFindTransport(DeviceName);
|
||
|
||
if (pTransport != NULL) {
|
||
SmbCeAcquireSpinLock();
|
||
|
||
pServerEntry = SmbCeGetFirstServerEntry();
|
||
|
||
while (pServerEntry != NULL) {
|
||
if ((pServerEntry->pTransport != NULL) &&
|
||
(pTransport == pServerEntry->pTransport->pTransport)) {
|
||
NumberOfFilesOpen += pServerEntry->Server.NumberOfSrvOpens;
|
||
}
|
||
|
||
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
SmbCeDereferenceTransport(pTransport);
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
MRxSmbRegisterForPnpNotifications()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine registers with TDI for receiving transport notifications
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if(MRxSmbTdiNotificationHandle == NULL ) {
|
||
UNICODE_STRING ClientName;
|
||
|
||
TDI_CLIENT_INTERFACE_INFO ClientInterfaceInfo;
|
||
|
||
RtlInitUnicodeString(&ClientName,L"LanmanWorkStation");
|
||
|
||
ClientInterfaceInfo.MajorTdiVersion = 2;
|
||
ClientInterfaceInfo.MinorTdiVersion = 0;
|
||
|
||
ClientInterfaceInfo.Unused = 0;
|
||
ClientInterfaceInfo.ClientName = &ClientName;
|
||
|
||
ClientInterfaceInfo.BindingHandler = MRxSmbPnPBindingHandler;
|
||
ClientInterfaceInfo.AddAddressHandler = NULL;
|
||
ClientInterfaceInfo.DelAddressHandler = NULL;
|
||
ClientInterfaceInfo.PnPPowerHandler = MRxSmbPnPPowerHandler;
|
||
|
||
KeInitializeEvent(
|
||
&TdiNetStartupCompletionEvent,
|
||
NotificationEvent,
|
||
FALSE);
|
||
|
||
Status = TdiRegisterPnPHandlers (
|
||
&ClientInterfaceInfo,
|
||
sizeof(ClientInterfaceInfo),
|
||
&MRxSmbTdiNotificationHandle );
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
LARGE_INTEGER WaitInterval;
|
||
|
||
WaitInterval.QuadPart = -( 10000 * 2 * 60 * 1000 );
|
||
|
||
Status = KeWaitForSingleObject(
|
||
&TdiNetStartupCompletionEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
&WaitInterval);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
DbgPrint("MRxSmb Finishes waiting on TDI_PNP_OP_NETREADY %lx\n",Status);
|
||
}
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
MRxSmbDeregisterForPnpNotifications()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deregisters the TDI notification mechanism
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if( MRxSmbTdiNotificationHandle != NULL ) {
|
||
Status = TdiDeregisterPnPHandlers( MRxSmbTdiNotificationHandle );
|
||
|
||
if( NT_SUCCESS( Status ) ) {
|
||
MRxSmbTdiNotificationHandle = NULL;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
#endif
|
||
|
||
NTSTATUS
|
||
SmbCePnpBindBrowser( PUNICODE_STRING pTransportName, BOOLEAN IsBind)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine binds the browser with the specified transport
|
||
|
||
Arguments:
|
||
|
||
pTransportName - the name of the transport
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE BrowserHandle;
|
||
PLMDR_REQUEST_PACKET pLmdrRequestPacket;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
UNICODE_STRING BrowserDeviceName;
|
||
ULONG LmdrRequestPacketSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Open up a handle to the browser
|
||
//
|
||
RtlInitUnicodeString( &BrowserDeviceName, DD_BROWSER_DEVICE_NAME_U);
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&BrowserDeviceName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Status = IoCreateFile(
|
||
&BrowserHandle, // FileHandle
|
||
SYNCHRONIZE, // DesiredAccess
|
||
&ObjectAttributes, // ObjectAttributes
|
||
&IoStatusBlock, // IoStatusBlock
|
||
NULL, // AllocationSize
|
||
0L, // FileAttributes
|
||
FILE_SHARE_VALID_FLAGS, // ShareAccess
|
||
FILE_OPEN, // Disposition
|
||
FILE_SYNCHRONOUS_IO_NONALERT, // CreateOptions
|
||
NULL, // EaBuffer
|
||
0, // EaLength
|
||
CreateFileTypeNone, // CreateFileType
|
||
NULL, // ExtraCreateParameters
|
||
0 // Options
|
||
);
|
||
|
||
if( NT_SUCCESS( Status ) ) {
|
||
Status = IoStatusBlock.Status;
|
||
}
|
||
|
||
if( !NT_SUCCESS(Status ) ) {
|
||
return Status;
|
||
}
|
||
|
||
// The browser requires that the computer name and the domain name be
|
||
// concatenated to the transport name and passed on. Since no length
|
||
// fields are provided to supply the length of these two names the
|
||
// NULL delimiter needs to be attached. This accounts for the two
|
||
// additional characters in the calculation
|
||
|
||
LmdrRequestPacketSize = sizeof(*pLmdrRequestPacket) +
|
||
pTransportName->Length +
|
||
SmbCeContext.DomainName.Length + sizeof(WCHAR) +
|
||
SmbCeContext.ComputerName.Length + sizeof(WCHAR);
|
||
|
||
pLmdrRequestPacket = RxAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
LmdrRequestPacketSize,
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
|
||
if (pLmdrRequestPacket != NULL) {
|
||
ULONG BufferOffset = 0;
|
||
PVOID pBuffer;
|
||
WCHAR NullChar = L'\0';
|
||
ULONG BindMode;
|
||
|
||
//
|
||
// Tell the browser to bind to this new transport
|
||
//
|
||
|
||
RtlZeroMemory( pLmdrRequestPacket, sizeof(LMDR_REQUEST_PACKET));
|
||
pLmdrRequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
|
||
pLmdrRequestPacket->Parameters.Bind.TransportNameLength = pTransportName->Length;
|
||
|
||
pBuffer = pLmdrRequestPacket->Parameters.Bind.TransportName;
|
||
|
||
RtlCopyMemory(
|
||
pBuffer,
|
||
pTransportName->Buffer,
|
||
pTransportName->Length);
|
||
BufferOffset = pTransportName->Length;
|
||
|
||
// Tell the browser our computer name.
|
||
pLmdrRequestPacket->Level = TRUE; // Emulated computer name follows transport name.
|
||
RtlCopyMemory(
|
||
((PBYTE)pBuffer + BufferOffset),
|
||
SmbCeContext.ComputerName.Buffer,
|
||
SmbCeContext.ComputerName.Length);
|
||
BufferOffset += SmbCeContext.ComputerName.Length;
|
||
|
||
RtlCopyMemory(
|
||
((PBYTE)pBuffer + BufferOffset),
|
||
&NullChar,
|
||
sizeof(WCHAR));
|
||
BufferOffset += sizeof(WCHAR);
|
||
|
||
|
||
// Tell the browser our domain name.
|
||
pLmdrRequestPacket->EmulatedDomainName.Buffer = (LPWSTR)
|
||
((PBYTE)pBuffer + BufferOffset);
|
||
pLmdrRequestPacket->EmulatedDomainName.MaximumLength =
|
||
pLmdrRequestPacket->EmulatedDomainName.Length =
|
||
SmbCeContext.DomainName.Length;
|
||
RtlCopyMemory(
|
||
((PBYTE)pBuffer + BufferOffset),
|
||
SmbCeContext.DomainName.Buffer,
|
||
SmbCeContext.DomainName.Length);
|
||
BufferOffset += SmbCeContext.DomainName.Length;
|
||
|
||
RtlCopyMemory(
|
||
((PBYTE)pBuffer + BufferOffset),
|
||
&NullChar,
|
||
sizeof(WCHAR));
|
||
BufferOffset += sizeof(WCHAR);
|
||
|
||
BindMode = IsBind?
|
||
IOCTL_LMDR_BIND_TO_TRANSPORT_DOM:
|
||
IOCTL_LMDR_UNBIND_FROM_TRANSPORT_DOM;
|
||
|
||
Status = NtDeviceIoControlFile(
|
||
BrowserHandle, // FileHandle
|
||
NULL, // Event
|
||
NULL, // ApcRoutine
|
||
NULL, // ApcContext
|
||
&IoStatusBlock, // IoStatusBlock
|
||
BindMode, // IoControlCode
|
||
pLmdrRequestPacket, // InputBuffer
|
||
LmdrRequestPacketSize, // InputBufferLength
|
||
NULL, // OutputBuffer
|
||
0 // OutputBufferLength
|
||
);
|
||
|
||
RxFreePool(pLmdrRequestPacket);
|
||
|
||
if( NT_SUCCESS(Status ) ) {
|
||
Status = IoStatusBlock.Status;
|
||
}
|
||
}
|
||
|
||
ZwClose( BrowserHandle );
|
||
|
||
return Status;
|
||
}
|
||
|
||
PSMBCE_TRANSPORT_ARRAY
|
||
SmbCeReferenceTransportArray(VOID)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine references and returns the current transport array instance
|
||
|
||
Return Value:
|
||
|
||
PSMBCE_TRANSPORT_ARRAY - the pointer of the current transport array instance
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
KIRQL SavedIrql;
|
||
PSMBCE_TRANSPORT_ARRAY pTransportArray;
|
||
|
||
KeAcquireSpinLock(&MRxSmbTransports.Lock,&SavedIrql);
|
||
|
||
pTransportArray = MRxSmbTransports.pTransportArray;
|
||
|
||
if (pTransportArray != NULL) {
|
||
InterlockedIncrement(&pTransportArray->ReferenceCount);
|
||
}
|
||
|
||
KeReleaseSpinLock(&MRxSmbTransports.Lock,SavedIrql);
|
||
|
||
return pTransportArray;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeDereferenceTransportArray(
|
||
PSMBCE_TRANSPORT_ARRAY pTransportArray)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dereferences the transport array instance
|
||
|
||
Arguments:
|
||
|
||
pTransportArray - the transport array instance
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - the server transport was successfully dereferenced
|
||
|
||
Other Status codes correspond to error situations.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
KIRQL SavedIrql;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (pTransportArray != NULL) {
|
||
ASSERT( pTransportArray->ReferenceCount > 0 );
|
||
|
||
if(InterlockedDecrement(&pTransportArray->ReferenceCount)==0) {
|
||
ULONG i;
|
||
|
||
for(i=0;i<pTransportArray->Count;i++) {
|
||
SmbCeDereferenceTransport(pTransportArray->SmbCeTransports[i]);
|
||
}
|
||
|
||
RxFreePool(pTransportArray->SmbCeTransports);
|
||
RxFreePool(pTransportArray->LocalAddresses);
|
||
RxFreePool(pTransportArray);
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeIsServerAvailable(
|
||
PUNICODE_STRING Name
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine scans the list of "unreachable" servers and returns the status
|
||
of the last failed connection attempt.
|
||
|
||
Return:
|
||
STATUS_SUCCESS -> we have no reason to believe this server is unreachable
|
||
other -> server is unreachable for this reason
|
||
--*/
|
||
{
|
||
PUNAVAILABLE_SERVER server;
|
||
LARGE_INTEGER now;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
KeQueryTickCount( &now );
|
||
|
||
ExAcquireResourceExclusive( &UnavailableServerListResource, TRUE );
|
||
|
||
for( server = (PUNAVAILABLE_SERVER)UnavailableServerList.Flink;
|
||
server != (PUNAVAILABLE_SERVER)&UnavailableServerList;
|
||
server = (PUNAVAILABLE_SERVER)server->ListEntry.Flink ) {
|
||
|
||
//
|
||
// If this entry has timed out, remove it.
|
||
//
|
||
if( now.QuadPart > server->Time.QuadPart ) {
|
||
PUNAVAILABLE_SERVER tmp;
|
||
//
|
||
// Unlink this entry from the list and discard it
|
||
//
|
||
tmp = (PUNAVAILABLE_SERVER)(server->ListEntry.Blink);
|
||
RemoveEntryList( &server->ListEntry );
|
||
RxFreePool( server );
|
||
server = tmp;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// See if this entry is the one we want
|
||
//
|
||
if( RtlCompareUnicodeString( &server->Name, Name, TRUE ) == 0 ) {
|
||
|
||
status = server->Status;
|
||
|
||
RxDbgTrace(0, Dbg, ("SmbCeIsServerAvailable: Found %wZ %X\n",
|
||
&server->Name, status ));
|
||
}
|
||
}
|
||
|
||
ExReleaseResource( &UnavailableServerListResource );
|
||
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
SmbCeServerIsUnavailable(
|
||
PUNICODE_STRING Name,
|
||
NTSTATUS Status
|
||
)
|
||
{
|
||
PUNAVAILABLE_SERVER server;
|
||
|
||
LARGE_INTEGER CurrentTime;
|
||
LARGE_INTEGER ExpiryTimeInTicks;
|
||
|
||
PAGED_CODE();
|
||
|
||
server = (PUNAVAILABLE_SERVER)RxAllocatePoolWithTag(
|
||
PagedPool,
|
||
sizeof( *server ) + Name->Length,
|
||
MRXSMB_TRANSPORT_POOLTAG
|
||
);
|
||
|
||
if( server == NULL ) {
|
||
return;
|
||
}
|
||
|
||
RxDbgTrace(0, Dbg, ("SmbCeServerIsUnavailable: Add %wZ %X\n", Name, Status ));
|
||
|
||
server->Name.Buffer = (PUSHORT)(server + 1);
|
||
server->Name.MaximumLength = Name->Length;
|
||
RtlCopyUnicodeString( &server->Name, Name );
|
||
|
||
KeQueryTickCount( &CurrentTime );
|
||
|
||
ExpiryTimeInTicks.QuadPart = (1000 * 1000 * 10) / KeQueryTimeIncrement();
|
||
|
||
ExpiryTimeInTicks.QuadPart = UNAVAILABLE_SERVER_TIME * ExpiryTimeInTicks.QuadPart;
|
||
|
||
server->Time.QuadPart = CurrentTime.QuadPart + ExpiryTimeInTicks.QuadPart;
|
||
|
||
server->Status = Status;
|
||
|
||
ExAcquireResourceExclusive( &UnavailableServerListResource, TRUE );
|
||
InsertHeadList( &UnavailableServerList, &server->ListEntry );
|
||
ExReleaseResource( &UnavailableServerListResource );
|
||
}
|
||
|
||
VOID
|
||
SmbCeDiscardUnavailableServerList(
|
||
)
|
||
{
|
||
PUNAVAILABLE_SERVER server;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace(0, Dbg, ("SmbCeDiscardUnavailableServerList\n" ));
|
||
|
||
ExAcquireResourceExclusive( &UnavailableServerListResource, TRUE );
|
||
|
||
while( UnavailableServerList.Flink != &UnavailableServerList ) {
|
||
server = (PUNAVAILABLE_SERVER)UnavailableServerList.Flink;
|
||
RemoveEntryList( &server->ListEntry );
|
||
RxFreePool( server );
|
||
}
|
||
|
||
ExReleaseResource( &UnavailableServerListResource );
|
||
}
|
||
|
||
extern BOOLEAN SetupInProgress;
|
||
|
||
VOID
|
||
MRxSmbLogTransportError(
|
||
PUNICODE_STRING pTransportName,
|
||
PUNICODE_STRING pDomainName,
|
||
NTSTATUS ErrorStatus,
|
||
IN ULONG Id)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reports the error that occurs at binding the browser with the specified transport
|
||
|
||
Arguments:
|
||
|
||
pTransportName - the name of the transport
|
||
|
||
Status - the NT status of the error occured
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
USHORT RemainingLength = ERROR_LOG_MAXIMUM_SIZE - sizeof(IO_ERROR_LOG_PACKET) - 3*sizeof(UNICODE_NULL);
|
||
UNICODE_STRING ErrorLog[3];
|
||
UNICODE_STRING UnicodeStatus;
|
||
UNICODE_STRING TempUnicode;
|
||
ULONG DosError;
|
||
|
||
// strip of the "\device\" at the beginning of transport name to reduce the message length
|
||
pTransportName->Length -= 8*sizeof(UNICODE_NULL);
|
||
|
||
// assume the dos error code won't be larger than 10 digits
|
||
UnicodeStatus.Length = 12 * sizeof(UNICODE_NULL);
|
||
UnicodeStatus.MaximumLength = UnicodeStatus.Length;
|
||
UnicodeStatus.Buffer = RxAllocatePoolWithTag(NonPagedPool,
|
||
UnicodeStatus.Length,
|
||
MRXSMB_TRANSPORT_POOLTAG);
|
||
if (UnicodeStatus.Buffer == NULL) {
|
||
goto FINALY;
|
||
}
|
||
|
||
// use the dos error code to display the status on the event message
|
||
UnicodeStatus.Buffer[0] = L'%';
|
||
UnicodeStatus.Buffer[1] = L'%';
|
||
|
||
DosError = RtlNtStatusToDosError(ErrorStatus);
|
||
|
||
TempUnicode.Length = UnicodeStatus.Length - 2*sizeof(UNICODE_NULL);
|
||
TempUnicode.MaximumLength = UnicodeStatus.MaximumLength - 2*sizeof(UNICODE_NULL);
|
||
TempUnicode.Buffer = &UnicodeStatus.Buffer[2];
|
||
|
||
Status = RtlIntegerToUnicodeString(
|
||
DosError,
|
||
0,
|
||
&TempUnicode);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
goto FINALY;
|
||
}
|
||
|
||
ErrorLog[2].Length = TempUnicode.Length + 2*sizeof(UNICODE_NULL);
|
||
ErrorLog[2].MaximumLength = ErrorLog[2].Length;
|
||
ErrorLog[2].Buffer = UnicodeStatus.Buffer;
|
||
|
||
RemainingLength -= ErrorLog[2].Length;
|
||
|
||
if (pDomainName->Length + pTransportName->Length > RemainingLength) {
|
||
// the length error log message is limited by the ERROR_LOG_MAXIMUM_SIZE. This restriction can be
|
||
// enfored by truncating the doamin and transport names so that both of them can get chance to be
|
||
// displayed on the EvenLog.
|
||
|
||
ErrorLog[0].Length = pDomainName->Length < RemainingLength / 2 ?
|
||
pDomainName->Length :
|
||
RemainingLength / 2;
|
||
|
||
RemainingLength -= ErrorLog[0].Length;
|
||
|
||
ErrorLog[1].Length = pTransportName->Length < RemainingLength ?
|
||
pTransportName->Length :
|
||
RemainingLength;
|
||
} else {
|
||
ErrorLog[0].Length = pDomainName->Length;
|
||
ErrorLog[1].Length = pTransportName->Length;
|
||
}
|
||
|
||
ErrorLog[0].MaximumLength = ErrorLog[0].Length;
|
||
ErrorLog[1].MaximumLength = ErrorLog[1].Length;
|
||
|
||
ErrorLog[0].Buffer = pDomainName->Buffer;
|
||
|
||
// strip of the "\device\" at the beginning of transport name
|
||
ErrorLog[1].Buffer = &pTransportName->Buffer[8];
|
||
|
||
RxLogEventWithAnnotation (
|
||
MRxSmbDeviceObject,
|
||
Id,
|
||
ErrorStatus,
|
||
NULL,
|
||
0,
|
||
ErrorLog,
|
||
3
|
||
);
|
||
|
||
FINALY:
|
||
|
||
// restore the length with "\device\" at the beginning of transport name
|
||
pTransportName->Length += 8*sizeof(UNICODE_NULL);
|
||
|
||
if (UnicodeStatus.Buffer != NULL) {
|
||
RxFreePool(UnicodeStatus.Buffer);
|
||
}
|
||
|
||
if (!SetupInProgress && ErrorStatus == STATUS_DUPLICATE_NAME) {
|
||
IoRaiseInformationalHardError(ErrorStatus, NULL, NULL);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
SmbMRxNotifyChangesToNetBt(
|
||
IN TDI_PNP_OPCODE PnPOpcode,
|
||
IN PUNICODE_STRING DeviceName,
|
||
IN PWSTR MultiSZBindList)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine should not be part of rdr. It has been introduced into this
|
||
component to overcome current limitations in NetBt. The NetBt transport
|
||
exposes two kinds of devices -- the traditional NetBt device and the
|
||
new non Netbios device which make use of the NetBt framing code without the
|
||
name resolution aspects of it. The current implementation in NetBt exposes
|
||
the former devices on a per adapter basis while the second category of device
|
||
is exposed on a global basis ( one for all the adapters ). This poses
|
||
problems in disabling/enabling srv on a given adapter.
|
||
|
||
The correct solution is to expose the second category of devices on a per
|
||
adapter basis. Till it is done this workaround is reqd. With this workaround
|
||
whenever the server is notified of any changes to the binding string it turns
|
||
around and notifies the NetBt transport about these changes.
|
||
|
||
This routine is based upon the following assumptions ...
|
||
|
||
1) The notification from TDI is not done at raised IRQL.
|
||
|
||
2) The thread on which this notification occurs has enough access rights.
|
||
|
||
3) The notification to NetBt is done asynchronously with srv's reaction
|
||
to the change. The srv handles the PNP notification by passing it off to
|
||
user mode and have it come through the server service.
|
||
|
||
Arguments:
|
||
|
||
PNPOpcode - the PNP opcode
|
||
|
||
DeviceName - the transport for which this opcode is intended
|
||
|
||
MultiSZBindList - the binding list
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE NetbioslessSmbHandle;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
UNICODE_STRING NetbioslessSmbName = {36,36, L"\\device\\NetbiosSmb"};
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&NetbioslessSmbName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL );
|
||
|
||
Status = ZwCreateFile (
|
||
&NetbioslessSmbHandle,
|
||
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access
|
||
&ObjectAttributes, // object attributes
|
||
&IoStatusBlock, // returned status information
|
||
NULL, // block size (unused)
|
||
0, // file attributes
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
|
||
FILE_CREATE, // create disposition
|
||
0, // create options
|
||
NULL, // EA buffer
|
||
0 // EA length
|
||
);
|
||
|
||
if ( NT_SUCCESS(Status) ) {
|
||
NETBT_SMB_BIND_REQUEST NetBtNotificationParameters;
|
||
|
||
NetBtNotificationParameters.RequestType = SMB_CLIENT;
|
||
NetBtNotificationParameters.PnPOpCode = PnPOpcode;
|
||
NetBtNotificationParameters.pDeviceName = DeviceName;
|
||
NetBtNotificationParameters.MultiSZBindList = MultiSZBindList;
|
||
|
||
Status = ZwDeviceIoControlFile(
|
||
NetbioslessSmbHandle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
IOCTL_NETBT_SET_SMBDEVICE_BIND_INFO,
|
||
&NetBtNotificationParameters,
|
||
sizeof(NetBtNotificationParameters),
|
||
NULL,
|
||
0);
|
||
|
||
Status = ZwClose(NetbioslessSmbHandle);
|
||
}
|
||
}
|
||
|
||
|