2025-04-27 07:49:33 -04:00

1757 lines
59 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
smbcedb.c
Abstract:
This module implements all functions related to accessing the SMB connection engine
database
Notes:
The construction of server, net root and session entries involve a certain amount of
network traffic. Therefore, all these entities are constructed using a two phase protocol
This continuation context is that of the RDBSS during construction of srv call and
net root entries. For the session entries it is an SMB exchange that needs to be resumed.
The three primary data structures in the SMB mini redirector, i.e., SMBCEDB_SERVER_ENTRY,
SMBCEDB_SESSION_ENTRY and SMBCEDB_NET_ROOT_ENTRY and their counterparts in the RDBSS
(MRX_SRV_CALL, MRX_V_NET_ROOT and MRX_NET_ROOT) constitute the core of the SMB mini
redirector connection engine. There exists a one to one mapping between the SERVER_ENTRY
and the MRX_SRV_CALL, as well as NET_ROOT_ENTRY and MRX_NET_ROOT.
On the other hand the mapping between MRX_V_NET_ROOT and SMBCEDB_SESSION_ENTRY is a many to
one relationship, i.e., more than one MRX_V_NET_ROOT instance can be associated with the
same SMBCEDB_SESSION_ENTRY. More than one tree connect to a server can use the same session
on a USER level security share. Consequently mapping rules need to be established to
manage this relationship. The SMB mini redirector implements the following rules ...
1) The first session with explicitly specified credentials will be treated as the
default session for all subsequent requests to any given server unless credentials
are explicitly specified for the new session.
2) If no session with explicitly specified credentials exist then a session with
the same logon id. is choosen.
3) If no session with the same logon id. exists a new session is created.
These rules are liable to change as we experiment with rules for establishing sessions
with differing credentials to a given server. The problem is not with creating/manipulating
these sessions but providing an adequate set of fallback rules for emulating the behaviour
of the old redirector.
These rules are implemented in SmbCeInitializeSessionEntry.
--*/
#include "precomp.h"
#pragma hdrstop
#include "secext.h"
RXDT_DefineCategory(SMBCEDB);
#define Dbg (DEBUG_TRACE_SMBCEDB)
// The flag mask to control reference count tracing.
ULONG MRxSmbReferenceTracingValue = 0;
NTSTATUS
SmbCeInitializeServerEntry(
PMRX_SRV_CALL pSrvCall,
PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
/*++
Routine Description:
This routine opens/creates a server entry in the connection engine database
Arguments:
pSrvCall - the SrvCall instance
pCallbackContext - the RDBSS context
Return Value:
STATUS_SUCCESS - the server call construction has been finalized.
Other Status codes correspond to error situations.
Notes:
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMBCEDB_SERVER_ENTRY pServerEntry = NULL;
SMBCEDB_SERVER_TYPE ServerType;
ASSERT(pSrvCall->Context == NULL);
ServerType = (pSrvCall->Flags & SRVCALL_FLAG_MAILSLOT_SERVER)
? SMBCEDB_MAILSLOT_SERVER
: SMBCEDB_FILE_SERVER;
// Create a server instance, initialize its state, add it to the list
// release the resources and rsume with the process of negotiating with
// the server.
pServerEntry = (PSMBCEDB_SERVER_ENTRY)SmbMmAllocateObject(SMBCEDB_OT_SERVER);
if (pServerEntry != NULL) {
//
// If the allocation succeeded, bump the reference count, and add the
// new server to the list of servers.
//
SmbCeReferenceServerEntry(pServerEntry);
SmbCeAddServerEntry(pServerEntry);
pServerEntry->pRdbssSrvCall = pSrvCall;
if (ServerType != SMBCEDB_MAILSLOT_SERVER)
{
pServerEntry->Header.State = SMBCEDB_CONSTRUCTION_IN_PROGRESS;
SmbCeSetServerType(pServerEntry,SMBCEDB_FILE_SERVER);
}
else
{
pServerEntry->Header.State = SMBCEDB_ACTIVE;
SmbCeSetServerType(pServerEntry,SMBCEDB_MAILSLOT_SERVER);
}
Status = SmbCeInitializeServerTransport(pServerEntry);
//
// Send the negotiate SMB.
//
if (Status == STATUS_SUCCESS) {
if (ServerType != SMBCEDB_MAILSLOT_SERVER) {
Status = SmbCeNegotiate(pServerEntry,pSrvCall);
if (Status == STATUS_SUCCESS) {
Status = pServerEntry->ServerStatus;
}
} else {
// Initialize the mailslot server parameters.
pServerEntry->Server.Dialect = LANMAN21_DIALECT;
pServerEntry->Server.MaximumBufferSize = 0xffff;
}
} else {
RxDbgTrace(0, Dbg, ("SmbCeOpenServer : SmbCeInitializeTransport returned %lx\n",Status));
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
RxDbgTrace(0, Dbg, ("SmbCeOpenServer : Server Entry Allocation failed\n"));
}
if ((Status != STATUS_SUCCESS) &&
(pServerEntry != NULL)) {
SmbCeDereferenceServerEntry(pServerEntry);
} else {
// Initialize the SrvCall flags based upon the capabilities of the remote
// server. The only flag that the SMB mini redirector updates is the
// SRVCALL_FLAG_DFS_AWARE
if ((pServerEntry != NULL) &&
(pServerEntry->Server.Capabilities & CAP_DFS)) {
SetFlag(pSrvCall->Flags,SRVCALL_FLAG_DFS_AWARE_SERVER);
}
pCallbackContext->RecommunicateContext = pServerEntry;
}
ASSERT(Status != STATUS_PENDING);
return Status;
}
VOID
SmbCeCompleteServerEntryInitialization(
PVOID pContext)
/*++
Routine Description:
This routine is invoked in the context of a worker thread to finalize the
construction of a server entry
Arguments:
pContext - the server entry to be finalized
--*/
{
NTSTATUS ServerStatus;
ULONG ServerState;
PSMBCEDB_SERVER_ENTRY pServerEntry = (PSMBCEDB_SERVER_ENTRY)pContext;
SMBCEDB_REQUESTS ReconnectRequests;
PSMBCEDB_REQUEST_ENTRY pRequestEntry;
KIRQL SavedIrql;
RxDbgTrace( 0, Dbg, ("Server Entry Finalization\n"));
ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
InitializeListHead(&ReconnectRequests.ListHead);
// Acquire the SMBCE resource
SmbCeAcquireResource();
SmbCeAcquireSpinLock();
ServerStatus = pServerEntry->ServerStatus;
ServerState = pServerEntry->Header.State;
// Weed out all the reconnect requests so that they can be resumed
pRequestEntry = SmbCeGetFirstRequestEntry(&pServerEntry->OutstandingRequests);
while (pRequestEntry != NULL) {
if (pRequestEntry->GenericRequest.Type == RECONNECT_REQUEST) {
PSMBCEDB_REQUEST_ENTRY pTempRequestEntry;
pTempRequestEntry = pRequestEntry;
pRequestEntry = SmbCeGetNextRequestEntry(
&pServerEntry->OutstandingRequests,
pRequestEntry);
SmbCeRemoveRequestEntryLite(
&pServerEntry->OutstandingRequests,
pTempRequestEntry);
SmbCeAddRequestEntryLite(
&ReconnectRequests,
pTempRequestEntry);
} else {
pRequestEntry = SmbCeGetNextRequestEntry(
&pServerEntry->OutstandingRequests,
pRequestEntry);
}
}
SmbCeReleaseSpinLock();
if ((ServerState == SMBCEDB_ACTIVE) && (ServerStatus == STATUS_SUCCESS)) {
InterlockedIncrement(&pServerEntry->Server.Version);
ASSERT(pServerEntry->pMidAtlas == NULL);
// Initialize the MID Atlas
pServerEntry->pMidAtlas = IfsMrxCreateMidAtlas(
pServerEntry->Server.MaximumRequests,
pServerEntry->Server.MaximumRequests);
if (pServerEntry->pMidAtlas == NULL) {
pServerEntry->ServerStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
// Release the resource for the server entry
SmbCeReleaseResource();
// Resume all the outstanding reconnect requests that were held up because an earlier
// reconnect request was under way.
// Iterate over the list of pending requests and resume all of them
pRequestEntry = SmbCeGetFirstRequestEntry(&ReconnectRequests);
while (pRequestEntry != NULL) {
NTSTATUS Status;
PSMB_EXCHANGE pExchange = pRequestEntry->ReconnectRequest.pExchange;
DbgPrint("Resuming outstanding reconnect request exchange %lx \n",pExchange);
// Resume the exchange after completing the server initialization for it.
// This can be done concurrently for all the outstanding requests since
// each exchange completion can take a while.
if (ServerStatus == STATUS_SUCCESS) {
pExchange->SmbCeContext.pServerEntry = pServerEntry;
pExchange->SmbCeState = SMBCE_EXCHANGE_SERVER_INITIALIZED;
} else {
RxDbgTrace( 0, Dbg, ("Resuming exchange%lx with error\n",pExchange));
pExchange->Status = ServerStatus;
}
// Resume the exchange.
if (pRequestEntry->Request.pExchange->pSmbCeSynchronizationEvent == NULL) {
if (ServerState == SMBCEDB_ACTIVE) {
Status = SmbCeInitiateExchange(pExchange);
} else {
// Invoke the error handler
RxDbgTrace( 0, Dbg, ("Resuming exchange%lx with error\n",pRequestEntry->Request.pExchange));
SmbCeFinalizeExchange(pExchange);
}
} else {
KeSetEvent(
pRequestEntry->Request.pExchange->pSmbCeSynchronizationEvent,
0,
FALSE);
}
// Delete the request entry
SmbCeRemoveRequestEntryLite(&ReconnectRequests,pRequestEntry);
// Tear down the continuation entry
SmbCeTearDownRequestEntry(pRequestEntry);
// Skip to the next one.
pRequestEntry = SmbCeGetFirstRequestEntry(&ReconnectRequests);
}
}
VOID
SmbCepDereferenceServerEntry(
PSMBCEDB_SERVER_ENTRY pServerEntry)
/*++
Routine Description:
This routine dereferences a server entry instance
Arguments:
pServerEntry - the server entry to be dereferenced
--*/
{
if (pServerEntry != NULL) {
BOOLEAN fTearDownEntry = FALSE;
LONG FinalRefCount;
ASSERT((pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER) &&
(pServerEntry->Header.SwizzleCount > 0));
MRXSMB_PRINT_REF_COUNT(SERVER_ENTRY,pServerEntry->Header.SwizzleCount);
SmbCeAcquireSpinLock();
FinalRefCount = InterlockedDecrement(&pServerEntry->Header.SwizzleCount);
// The transport has an outstanding reference. Therefore initiate the
// teardown on a reference count of 1. If no transport is associated
// with the server entry initiate it on a ref count of zero.
if (pServerEntry->Header.State != SMBCEDB_MARKED_FOR_DELETION) {
fTearDownEntry = (FinalRefCount == 0);
}
if (fTearDownEntry) {
pServerEntry->Header.State = SMBCEDB_MARKED_FOR_DELETION;
SmbCeRemoveServerEntryLite(pServerEntry);
}
SmbCeReleaseSpinLock();
if (fTearDownEntry) {
SmbCeTearDownServerEntry(pServerEntry);
}
}
}
VOID
SmbCeTearDownServerEntry(
PSMBCEDB_SERVER_ENTRY pServerEntry)
/*++
Routine Description:
This routine tears down a server entry instance
Arguments:
pServerEntry - the server entry to be dereferenced
--*/
{
ASSERT(pServerEntry->Header.State == SMBCEDB_MARKED_FOR_DELETION);
if (pServerEntry->pMidAtlas != NULL) {
IfsMrxDestroyMidAtlas(pServerEntry->pMidAtlas,NULL);
pServerEntry->pMidAtlas = NULL;
}
if (pServerEntry->pTransport != NULL) {
SmbCeUninitializeServerTransport(pServerEntry);
}
SmbMmFreeObject(pServerEntry);
}
NTSTATUS
SmbCeInitializeSessionEntry(
PMRX_V_NET_ROOT pVNetRoot)
/*++
Routine Description:
This routine opens/creates a session for a given user in the connection engine database
Arguments:
pVNetRoot - the RDBSS Virtual net root instance
Return Value:
STATUS_SUCCESS - if successful
Other Status codes correspond to error situations.
Notes:
Please refer to the header of this module for a detailed description of the rules
for associating session entries with V_NET_ROOT's.
This routine assumes that the necesary concurreny control mechanism has already
been taken.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMBCEDB_SERVER_ENTRY pServerEntry = NULL;
PSMBCEDB_SESSION_ENTRY pSessionEntry = NULL;
BOOLEAN fSessionEntryFound = FALSE;
BOOLEAN fUserCredentialsSpecified;
fUserCredentialsSpecified = ((pVNetRoot->pUserName != NULL) || (pVNetRoot->pPassword != NULL));
SmbCeAcquireResource();
// Reference the server handle
pServerEntry = SmbCeReferenceAssociatedServerEntry(pVNetRoot->pNetRoot->pSrvCall);
if (pServerEntry != NULL) {
if (!fUserCredentialsSpecified) {
SmbCeAcquireSpinLock();
// Rule No. 1
// 1) The first session with explicitly specified credentials will be treated as the
// default session for all subsequent requests to any given server.
pSessionEntry = SmbCeGetDefaultSessionEntry(pServerEntry);
if (pSessionEntry == NULL) {
// Rule No. 2
// 2) If no session with explicitly specified credentials exist then a session with
// the same logon id. is choosen.
//
// Enumerate the sessions to detect if a session satisfying rule 2 exists
pSessionEntry = SmbCeGetFirstSessionEntry(pServerEntry);
while (pSessionEntry != NULL) {
if (RtlCompareMemory(
&pSessionEntry->Session.LogonId,
&pVNetRoot->LogonId,
sizeof(pSessionEntry->Session.LogonId))) {
break;
}
pSessionEntry = SmbCeGetNextSessionEntry(pServerEntry,pSessionEntry);
}
}
if (pSessionEntry != NULL) {
SmbCeReferenceSessionEntry(pSessionEntry);
}
SmbCeReleaseSpinLock();
} else {
BOOLEAN SessionEntryFound = FALSE;
SmbCeAcquireSpinLock();
pSessionEntry = SmbCeGetFirstSessionEntry(pServerEntry);
if (pSessionEntry != NULL) {
SmbCeReferenceSessionEntry(pSessionEntry);
}
SmbCeReleaseSpinLock();
while ((pSessionEntry != NULL) && !SessionEntryFound) {
for (;;) {
PSMBCE_SESSION pSession = &pSessionEntry->Session;
// For each existing session check to determine if the credentials
// supplied match the credentials used to construct the session.
if ((pVNetRoot->pUserName != NULL) &&
(pSession->pUserName != NULL)) {
if (!RtlEqualUnicodeString(
pVNetRoot->pUserName,
pSession->pUserName,
TRUE)) {
Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
break;
}
} else {
if (pSession->pUserName != pVNetRoot->pUserName) {
break;
}
}
if ((pVNetRoot->pUserDomainName != NULL) &&
(pSession->pUserDomainName != NULL)) {
if (!RtlEqualUnicodeString(
pVNetRoot->pUserDomainName,
pSession->pUserDomainName,
TRUE)) {
Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
break;
}
} else {
if (pSession->pUserDomainName != pVNetRoot->pUserDomainName) {
break;
}
}
if ((pVNetRoot->pPassword != NULL) &&
(pSession->pPassword != NULL)) {
if (!RtlEqualUnicodeString(
pVNetRoot->pPassword,
pSession->pPassword,
FALSE)) {
Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
break;
}
} else {
if (pSession->pPassword != pVNetRoot->pPassword) {
break;
}
}
// An entry that matches the credentials supplied has been found. use it.
SessionEntryFound = TRUE;
break;
}
if (!SessionEntryFound) {
if (Status == STATUS_SUCCESS) {
PSMBCEDB_SESSION_ENTRY pNextSessionEntry;
SmbCeAcquireSpinLock();
pNextSessionEntry = SmbCeGetNextSessionEntry(
pServerEntry,
pSessionEntry);
if (pNextSessionEntry != NULL) {
SmbCeReferenceSessionEntry(pNextSessionEntry);
}
SmbCeReleaseSpinLock();
SmbCeDereferenceSessionEntry(pSessionEntry);
pSessionEntry = pNextSessionEntry;
} else {
// An error situation was encountered. Terminate the iteration.
// Typically a set of conflicting credentials have been presented
SmbCeDereferenceSessionEntry(pSessionEntry);
pSessionEntry = NULL;
}
}
}
}
if ((pSessionEntry != NULL) &&
!(pSessionEntry->Session.Flags & SMBCE_SESSION_FLAGS_PARAMETERS_ALLOCATED)) {
// This is the point at which a many to mapping between session entries and
// V_NET_ROOT's in the RDBSS is being established. From this point it is
// true that the session entry can outlive the associated V_NET_ROOT entry.
// Therefore copies of the parameters used in the session setup need be made.
PSMBCE_SESSION pSession = &pSessionEntry->Session;
PUNICODE_STRING pPassword,pUserName,pUserDomainName;
if (pSession->pPassword != NULL) {
pPassword = (PUNICODE_STRING)
RxAllocatePoolWithTag(
NonPagedPool,
sizeof(UNICODE_STRING) + pSession->pPassword->Length,
MRXSMB_SESSION_POOLTAG);
if (pPassword != NULL) {
pPassword->Buffer = (PWCHAR)((PCHAR)pPassword + sizeof(UNICODE_STRING));
pPassword->Length = pSession->pPassword->Length;
pPassword->MaximumLength = pPassword->Length;
RtlCopyMemory(
pPassword->Buffer,
pSession->pPassword->Buffer,
pPassword->Length);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
pPassword = NULL;
}
if ((pSession->pUserName != NULL) &&
(Status == STATUS_SUCCESS)) {
pUserName = (PUNICODE_STRING)
RxAllocatePoolWithTag(
NonPagedPool,
sizeof(UNICODE_STRING) + pSession->pUserName->Length,
MRXSMB_SESSION_POOLTAG);
if (pUserName != NULL) {
pUserName->Buffer = (PWCHAR)((PCHAR)pUserName + sizeof(UNICODE_STRING));
pUserName->Length = pSession->pUserName->Length;
pUserName->MaximumLength = pUserName->Length;
RtlCopyMemory(
pUserName->Buffer,
pSession->pUserName->Buffer,
pUserName->Length);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
pUserName = NULL;
}
if ((pSession->pUserDomainName != NULL) &&
(Status == STATUS_SUCCESS)) {
pUserDomainName = (PUNICODE_STRING)
RxAllocatePoolWithTag(
NonPagedPool,
sizeof(UNICODE_STRING) + pSession->pUserDomainName->Length,
MRXSMB_SESSION_POOLTAG);
if (pUserDomainName != NULL) {
pUserDomainName->Buffer = (PWCHAR)((PCHAR)pUserDomainName + sizeof(UNICODE_STRING));
pUserDomainName->Length = pSession->pUserDomainName->Length;
pUserDomainName->MaximumLength = pUserDomainName->Length;
RtlCopyMemory(pUserDomainName->Buffer,pSession->pUserDomainName->Buffer,pUserDomainName->Length);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
pUserDomainName = NULL;
}
if (Status == STATUS_SUCCESS) {
pSession->pUserName = pUserName;
pSession->pUserDomainName = pUserDomainName;
pSession->pPassword = pPassword;
pSession->Flags |= SMBCE_SESSION_FLAGS_PARAMETERS_ALLOCATED;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
SmbCeDereferenceSessionEntry(pSessionEntry);
}
}
if ((pSessionEntry == NULL) && (Status == STATUS_SUCCESS)) {
// Rule No. 3
// 3) If no session with the same logon id. exists a new session is created.
//
// Allocate a new session entry
pSessionEntry = SmbMmAllocateSessionEntry(pServerEntry);
if (pSessionEntry != NULL) {
PSMBCE_SESSION pSession = & pSessionEntry->Session;
pSessionEntry->Header.State = SMBCEDB_START_CONSTRUCTION;
pSessionEntry->pRdbssVNetRoot = pVNetRoot;
pSessionEntry->pServerEntry = pServerEntry;
if (pServerEntry->Server.SecurityMode == SECURITY_MODE_SHARE_LEVEL) {
pSessionEntry->Session.UserId = (SMB_USER_ID)SMBCE_SHARE_LEVEL_SERVER_USERID;
}
pSession->Flags = 0;
pSession->LogonId = pVNetRoot->LogonId;
pSession->pUserName = pVNetRoot->pUserName;
pSession->pPassword = pVNetRoot->pPassword;
pSession->pUserDomainName = pVNetRoot->pUserDomainName;
SmbCeReferenceSessionEntry(pSessionEntry);
SmbCeAddSessionEntry(pServerEntry,pSessionEntry);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (Status == STATUS_SUCCESS) {
ASSERT(pVNetRoot->Context == NULL);
pVNetRoot->Context = pSessionEntry;
}
SmbCeDereferenceServerEntry(pServerEntry);
} else {
Status = STATUS_INVALID_HANDLE;
}
SmbCeReleaseResource();
return Status;
}
VOID
SmbCeCompleteSessionEntryInitialization(
PVOID pContext)
/*++
Routine Description:
This routine is invoked in the context of a worker thread to finalize the
construction of a session entry
Arguments:
pContext - the session entry to be activated
Notes:
PRE_CONDITION: The session entry must have been referenced to ensure that
even it has been finalized it will not be deleted.
--*/
{
NTSTATUS Status;
PSMBCEDB_SESSION_ENTRY pSessionEntry = (PSMBCEDB_SESSION_ENTRY)pContext;
PSMBCEDB_REQUEST_ENTRY pRequestEntry;
SMBCEDB_REQUESTS Requests;
PAGED_CODE();
RxDbgTrace( 0, Dbg, ("Session Entry Finalization\n"));
ASSERT(pSessionEntry->Header.ObjectType == SMBCEDB_OT_SESSION);
// Acquire the SMBCE resource
SmbCeAcquireResource();
// Create a temporary copy of the list that can be traversed after releasing the
// resource.
SmbCeTransferRequests(&Requests,&pSessionEntry->Requests);
// Release the resource for the session entry
SmbCeReleaseResource();
// Iterate over the list of pending requests and resume all of them
pRequestEntry = SmbCeGetFirstRequestEntry(&Requests);
while (pRequestEntry != NULL) {
// Resume the exchange.
if (pRequestEntry->Request.pExchange->pSmbCeSynchronizationEvent == NULL) {
if (pSessionEntry->Header.State == SMBCEDB_ACTIVE) {
Status = SmbCeInitiateExchange(pRequestEntry->Request.pExchange);
} else {
// Invoke the error handler
RxDbgTrace( 0, Dbg, ("Resuming exchange%lx with error\n",pRequestEntry->Request.pExchange));
pRequestEntry->Request.pExchange->Status = STATUS_BAD_LOGON_SESSION_STATE;
SmbCeFinalizeExchange(pRequestEntry->Request.pExchange);
}
} else {
if (pSessionEntry->Header.State == SMBCEDB_ACTIVE) {
pRequestEntry->Request.pExchange->Status = STATUS_SUCCESS;
} else {
pRequestEntry->Request.pExchange->Status = STATUS_BAD_LOGON_SESSION_STATE;
}
KeSetEvent(
pRequestEntry->Request.pExchange->pSmbCeSynchronizationEvent,
0,
FALSE);
}
// Delete the request entry
SmbCeRemoveRequestEntryLite(&Requests,pRequestEntry);
// Tear down the continuation entry
SmbCeTearDownRequestEntry(pRequestEntry);
// Skip to the next one.
pRequestEntry = SmbCeGetFirstRequestEntry(&Requests);
}
SmbCeDereferenceSessionEntry(pSessionEntry);
}
NTSTATUS
SmbCeGetUserNameAndDomainName(
PSMBCEDB_SESSION_ENTRY pSessionEntry,
PUNICODE_STRING pUserName,
PUNICODE_STRING pUserDomainName)
/*++
Routine Description:
This routine returns the user name and domain name associated with a session
in a caller allocated buffer.
Arguments:
pSessionEntry - the session entry to be dereferenced
pUserName - the User name
pUserDomainName - the user domain name
Return Value:
STATUS_SUCCESS if successful
--*/
{
NTSTATUS Status;
PSMBCE_SESSION pSession;
PUNICODE_STRING pSessionUserName,pSessionDomainName;
PSecurityUserData pSecurityData;
BOOLEAN ProcessAttached;
ASSERT(pSessionEntry != NULL);
pSession = &pSessionEntry->Session;
if ((pUserName == NULL) ||
(pUserDomainName == NULL) ||
(pUserName->MaximumLength < (UNLEN * sizeof(WCHAR))) ||
(pUserDomainName->MaximumLength < (DNLEN * sizeof(WCHAR)))) {
return STATUS_INVALID_PARAMETER;
}
Status = STATUS_SUCCESS;
pSecurityData = NULL;
ProcessAttached = FALSE;
pSessionUserName = pSession->pUserName;
pSessionDomainName = pSession->pUserDomainName;
try {
if ((pSessionUserName == NULL) ||
(pSessionDomainName == NULL)) {
// Attach to the redirector's FSP to allow us to call into the LSA.
if (PsGetCurrentProcess() != RxGetRDBSSProcess()) {
KeAttachProcess(RxGetRDBSSProcess());
ProcessAttached = TRUE;
}
Status = GetSecurityUserInfo(
&pSession->LogonId,
UNDERSTANDS_LONG_NAMES,
&pSecurityData);
if (NT_SUCCESS(Status)) {
pSessionUserName = &(pSecurityData->UserName);
pSessionDomainName = &(pSecurityData->LogonDomainName);
}
}
if (NT_SUCCESS(Status)) {
ASSERT(pSessionUserName->Length <= pUserName->MaximumLength);
ASSERT(pSessionDomainName->Length <= pUserDomainName->MaximumLength);
pUserName->Length = pSessionUserName->Length;
RtlCopyMemory(
pUserName->Buffer,
pSessionUserName->Buffer,
pUserName->Length);
pUserDomainName->Length = pSessionDomainName->Length;
RtlCopyMemory(
pUserDomainName->Buffer,
pSessionDomainName->Buffer,
pUserDomainName->Length);
}
} finally {
if (pSecurityData != NULL) {
LsaFreeReturnBuffer(pSecurityData);
}
if (ProcessAttached) {
KeDetachProcess();
}
}
return Status;
}
VOID
SmbCepDereferenceSessionEntry(
PSMBCEDB_SESSION_ENTRY pSessionEntry)
/*++
Routine Description:
This routine dereferences a session entry instance
Arguments:
pSessionEntry - the session entry to be dereferenced
--*/
{
if (pSessionEntry != NULL) {
BOOLEAN fTearDownEntry;
BOOLEAN fLogOffRequired;
ASSERT((pSessionEntry->Header.ObjectType == SMBCEDB_OT_SESSION) &&
(pSessionEntry->Header.SwizzleCount > 0));
MRXSMB_PRINT_REF_COUNT(SESSION_ENTRY,pSessionEntry->Header.SwizzleCount);
SmbCeAcquireSpinLock();
if (InterlockedDecrement(&pSessionEntry->Header.SwizzleCount) == 0) {
if ((pSessionEntry->Header.State == SMBCEDB_ACTIVE) &&
(pSessionEntry->Session.UserId != SMBCE_SHARE_LEVEL_SERVER_USERID)) {
SmbCeReferenceServerEntry(pSessionEntry->pServerEntry);
fLogOffRequired = TRUE;
} else {
fLogOffRequired = FALSE;
pSessionEntry->Header.State = SMBCEDB_MARKED_FOR_DELETION;
}
SmbCeRemoveSessionEntryLite(pSessionEntry->pServerEntry,pSessionEntry);
fTearDownEntry = TRUE;
} else {
fTearDownEntry = FALSE;
}
SmbCeReleaseSpinLock();
if (fTearDownEntry) {
if (fLogOffRequired) {
SmbCeLogOff(pSessionEntry->pServerEntry,pSessionEntry);
} else {
SmbCeTearDownSessionEntry(pSessionEntry);
}
}
}
}
VOID
SmbCeTearDownSessionEntry(
PSMBCEDB_SESSION_ENTRY pSessionEntry)
/*++
Routine Description:
This routine tears down a session entry instance
Arguments:
pSessionEntry - the session entry to be dereferenced
--*/
{
ASSERT((pSessionEntry->Header.SwizzleCount == 0) &&
(pSessionEntry->Header.State == SMBCEDB_MARKED_FOR_DELETION));
if (pSessionEntry->Session.Flags & SMBCE_SESSION_FLAGS_PARAMETERS_ALLOCATED) {
if (pSessionEntry->Session.pUserName != NULL) {
RxFreePool(pSessionEntry->Session.pUserName);
}
if (pSessionEntry->Session.pPassword != NULL) {
RxFreePool(pSessionEntry->Session.pPassword);
}
if (pSessionEntry->Session.pUserDomainName != NULL) {
RxFreePool(pSessionEntry->Session.pUserDomainName);
}
}
UninitializeSecurityContextsForSession(&pSessionEntry->Session);
SmbMmFreeSessionEntry(pSessionEntry);
}
NTSTATUS
SmbCeInitializeNetRootEntry(
PMRX_NET_ROOT pNetRoot)
/*++
Routine Description:
This routine opens/creates a net root entry in the connection engine database
Arguments:
pNetRoot -- the RDBSS net root instance
Return Value:
STATUS_SUCCESS - the construction of the net root instance has been finalized
Other Status codes correspond to error situations.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMBCEDB_SERVER_ENTRY pServerEntry = NULL;
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = NULL;
PSMBCEDB_SESSION_ENTRY pSessionEntry = NULL;
SMB_USER_ID UserId = 0;
pServerEntry = SmbCeReferenceAssociatedServerEntry(pNetRoot->pSrvCall);
if (pServerEntry != NULL) {
pNetRootEntry = (PSMBCEDB_NET_ROOT_ENTRY)SmbMmAllocateObject(SMBCEDB_OT_NETROOT);
if (pNetRootEntry != NULL) {
pNetRootEntry->pServerEntry = pServerEntry;
pNetRootEntry->pRdbssNetRoot = pNetRoot;
pNetRootEntry->NetRoot.UserId = UserId;
pNetRootEntry->NetRoot.NetRootType = pNetRoot->Type;
InitializeListHead(&pNetRootEntry->NetRoot.ClusterSizeSerializationQueue);
if (pNetRoot->Type == NET_ROOT_MAILSLOT) {
pNetRootEntry->Header.State = SMBCEDB_ACTIVE;
}
SmbCeReferenceNetRootEntry(pNetRootEntry);
SmbCeAddNetRootEntry(pServerEntry,pNetRootEntry);
pNetRoot->Context = pNetRootEntry;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
SmbCeDereferenceServerEntry(pServerEntry);
} else {
Status = STATUS_INVALID_HANDLE;
}
return Status;
}
VOID
SmbCeCompleteNetRootEntryInitialization(
PVOID pContext)
/*++
Routine Description:
This routine is invoked in the context of a worker thread to finalize the
construction of a net root entry
Arguments:
pContext - the net root entry to be finalized
Notes:
PRE_CONDITION: The session entry must have been referenced to ensure that
even it has been finalized it will not be deleted.
--*/
{
NTSTATUS Status;
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = (PSMBCEDB_NET_ROOT_ENTRY)pContext;
PSMBCEDB_REQUEST_ENTRY pRequestEntry;
SMBCEDB_REQUESTS Requests;
RxDbgTrace( 0, Dbg, ("Net Root Entry Finalization\n"));
ASSERT(pNetRootEntry->Header.ObjectType == SMBCEDB_OT_NETROOT);
SmbCeAcquireResource();
SmbCeTransferRequests(&Requests,&pNetRootEntry->Requests);
SmbCeReleaseResource();
// Iterate over the list of pending requests and resume all of them
pRequestEntry = SmbCeGetFirstRequestEntry(&Requests);
while (pRequestEntry != NULL) {
// Resume the exchange.
if (pRequestEntry->Request.pExchange->pSmbCeSynchronizationEvent == NULL) {
if (pNetRootEntry->Header.State == SMBCEDB_ACTIVE) {
Status = SmbCeInitiateExchange(pRequestEntry->Request.pExchange);
} else {
// Invoke the error handler
RxDbgTrace( 0, Dbg, ("Resuming exchange%lx with error\n",pRequestEntry->Request.pExchange));
pRequestEntry->Request.pExchange->Status = STATUS_INVALID_CONNECTION;
SmbCeFinalizeExchange(pRequestEntry->Request.pExchange);
}
} else {
if (pNetRootEntry->Header.State == SMBCEDB_ACTIVE) {
pRequestEntry->Request.pExchange->Status = STATUS_SUCCESS;
} else {
pRequestEntry->Request.pExchange->Status = STATUS_INVALID_CONNECTION;
}
KeSetEvent(
pRequestEntry->Request.pExchange->pSmbCeSynchronizationEvent,
0,
FALSE);
}
// Delete the request entry
SmbCeRemoveRequestEntry(&Requests,pRequestEntry);
// Tear down the continuation entry
SmbCeTearDownRequestEntry(pRequestEntry);
// Skip to the next one.
pRequestEntry = SmbCeGetFirstRequestEntry(&Requests);
}
SmbCeDereferenceNetRootEntry(pNetRootEntry);
}
VOID
SmbCepDereferenceNetRootEntry(
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry)
/*++
Routine Description:
This routine dereferences a net root entry instance
Arguments:
pNetRootEntry - the NEt Root entry to be dereferenced
Notes:
Disconnects are not required for mailslot servers. They need to be
sent to File servers only.
--*/
{
if (pNetRootEntry != NULL) {
BOOLEAN fTearDownEntry;
BOOLEAN fDisconnectRequired;
ASSERT((pNetRootEntry->Header.ObjectType == SMBCEDB_OT_NETROOT) &&
(pNetRootEntry->Header.SwizzleCount > 0));
MRXSMB_PRINT_REF_COUNT(NETROOT_ENTRY,pNetRootEntry->Header.SwizzleCount);
SmbCeAcquireSpinLock();
if (InterlockedDecrement(&pNetRootEntry->Header.SwizzleCount) == 0) {
if ((pNetRootEntry->Header.State == SMBCEDB_ACTIVE) &&
(SmbCeGetServerType(pNetRootEntry->pServerEntry) == SMBCEDB_FILE_SERVER)) {
SmbCeReferenceServerEntry(pNetRootEntry->pServerEntry);
fDisconnectRequired = TRUE;
} else {
fDisconnectRequired = FALSE;
pNetRootEntry->Header.State = SMBCEDB_MARKED_FOR_DELETION;
}
SmbCeRemoveNetRootEntryLite(pNetRootEntry->pServerEntry,pNetRootEntry);
fTearDownEntry = TRUE;
} else {
fTearDownEntry = FALSE;
}
SmbCeReleaseSpinLock();
if (fTearDownEntry) {
if (fDisconnectRequired) {
SmbCeDisconnect(pNetRootEntry->pServerEntry,pNetRootEntry);
} else {
SmbCeTearDownNetRootEntry(pNetRootEntry);
}
}
}
}
VOID
SmbCeTearDownNetRootEntry(
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry)
/*++
Routine Description:
This routine tears down a net root entry instance
Arguments:
pNetRootEntry - the NEt Root entry to be dereferenced
--*/
{
ASSERT((pNetRootEntry->Header.SwizzleCount == 0) &&
(pNetRootEntry->Header.State == SMBCEDB_MARKED_FOR_DELETION));
SmbMmFreeObject(pNetRootEntry);
}
VOID
SmbCeProbeServers(
PVOID pContext)
/*++
Routine Description:
This routine probes all the remote servers on which no activity has been
detected in the recent past.
Notes:
The current implementation of walking through the list of all servers to
initiate echo processing will not scale very well for gateway servers. A
different mechanism needs to be implemented.
--*/
{
ULONG EchoProbeContextFlags;
LIST_ENTRY DiscardedServersList;
PSMBCEDB_SERVER_ENTRY pServerEntry;
PSMBCEDB_SERVER_ENTRY pPreviousServerEntry = NULL;
InitializeListHead(&DiscardedServersList);
SmbCeAcquireSpinLock();
pServerEntry = SmbCeGetFirstServerEntry();
while (pServerEntry != NULL) {
if (pServerEntry->Header.State == SMBCEDB_ACTIVE) {
if ((pServerEntry->Server.SmbsReceivedSinceLastStrobe == 0) &&
(pServerEntry->pMidAtlas != NULL) &&
(pServerEntry->pMidAtlas->NumberOfMidsInUse > 0)) {
switch (pServerEntry->Server.EchoProbeState) {
case ECHO_PROBE_AWAITING_RESPONSE:
case ECHO_PROBE_SEND:
{
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN SendEchoSmb;
// Classify the connection as being disconnected.
// The additional reference is required to keep this server entry
// as a place marker in the list of server entries.
// This will be released on resumption of the processinf further
// down in this routine
InterlockedIncrement(&pServerEntry->Header.SwizzleCount);
SmbCeReleaseSpinLock();
SendEchoSmb = ((pServerEntry->Server.Dialect >= NTLANMAN_DIALECT) ||
(pServerEntry->Server.EchoProbesSent < ECHO_PROBE_LIMIT));
if (SendEchoSmb &&
(pServerEntry->Server.EchoProbeState == ECHO_PROBE_SEND)) {
pServerEntry->Server.EchoProbesSent++;
pServerEntry->Server.EchoProbeState = ECHO_PROBE_AWAITING_RESPONSE;
Status = SmbCeSendToServer(
pServerEntry,
RXCE_SEND_SYNCHRONOUS,
EchoProbeContext.pEchoSmbMdl,
EchoProbeContext.EchoSmbLength);
RxDbgTrace(0,Dbg,("Sending ECHO SMB %lx Status %lx\n",pServerEntry,Status));
} else {
if (pServerEntry->Server.EchoProbesSent >= ECHO_PROBE_LIMIT) {
DbgPrint("****** ServerEntry(%lx) ECHO probe Limit exceeded\n",pServerEntry);
}
Status = STATUS_MORE_PROCESSING_REQUIRED;
}
if ((Status != STATUS_SUCCESS) &&
(Status != STATUS_PENDING)) {
RxDbgTrace(0,Dbg,("Disconnecting Connection %lx\n",pServerEntry));
InterlockedIncrement(&MRxIfsStatistics.HungSessions);
SmbCeTransportDisconnectIndicated(pServerEntry);
}
SmbCeAcquireSpinLock();
pPreviousServerEntry = pServerEntry;
}
default:
case ECHO_PROBE_IDLE:
{
// Prepare to send an ECHO SMB.
pServerEntry->Server.EchoProbeState = ECHO_PROBE_SEND;
}
}
} else {
pServerEntry->Server.EchoProbesSent = 0;
InterlockedExchange(&pServerEntry->Server.SmbsReceivedSinceLastStrobe,0);
pServerEntry->Server.EchoProbeState = ECHO_PROBE_IDLE;
}
}
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
if (pPreviousServerEntry != NULL) {
LONG FinalRefCount;
FinalRefCount = InterlockedDecrement(&pPreviousServerEntry->Header.SwizzleCount);
if (FinalRefCount == 0) {
pPreviousServerEntry->Header.State = SMBCEDB_MARKED_FOR_DELETION;
SmbCeRemoveServerEntryLite(pPreviousServerEntry);
InsertTailList(&DiscardedServersList,&pPreviousServerEntry->ServersList);
}
pPreviousServerEntry = NULL;
}
}
EchoProbeContextFlags = EchoProbeContext.Flags;
if (EchoProbeContextFlags & ECHO_PROBE_CANCELLED_FLAG) {
KeSetEvent(
&EchoProbeContext.CancelCompletionEvent,
0,
FALSE );
}
SmbCeReleaseSpinLock();
while (!IsListEmpty(&DiscardedServersList)) {
PLIST_ENTRY pListEntry = DiscardedServersList.Flink;
RemoveEntryList(pListEntry);
pServerEntry = (PSMBCEDB_SERVER_ENTRY)
(CONTAINING_RECORD(
pListEntry,
SMBCEDB_SERVER_ENTRY,
ServersList));
SmbCeTearDownServerEntry(pServerEntry);
}
if (!(EchoProbeContextFlags & ECHO_PROBE_CANCELLED_FLAG)) {
EchoProbeContext.Status = RxPostOneShotTimerRequest(
MRxIfsDeviceObject,
&EchoProbeContext.WorkItem,
SmbCeProbeServers,
&EchoProbeContext,
EchoProbeContext.Interval);
}
RxDbgTraceLV(0, Dbg, 2000, ("SmbCeProbeServers: Reposting Request\n"));
if (EchoProbeContext.Status != STATUS_SUCCESS) {
RxLog(("SmbCe Echo Probe Reposting Error %lx\n", EchoProbeContext.Status));
}
}
VOID
SmbCeTransportDisconnectIndicated(
PSMBCEDB_SERVER_ENTRY pServerEntry)
/*++
Routine Description:
This routine invalidates a server entry on notification from the underlying transport
Arguments:
pServerEntry - the server entry to be dereferenced
Notes:
The server entry and the associated net roots and sessions are marked as invalid. A
reconnect is facilitated on other requests as and when required. In addition all
pending requests are resumed with the appropriate error indication.
--*/
{
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
PSMBCEDB_SESSION_ENTRY pSessionEntry;
RxDbgTrace(0,
Dbg,
("SmbCeDbTransportDisconnectIndicated for %lx -- Entry\n",pServerEntry));
// Acquire the database resource (DPC Level)
SmbCeAcquireSpinLock();
// Increment the associated version count so as to invalidate all existing Fids
InterlockedIncrement(&pServerEntry->Server.Version);
pServerEntry->ServerStatus = STATUS_CONNECTION_DISCONNECTED;
pServerEntry->Header.State = SMBCEDB_DESTRUCTION_IN_PROGRESS;
// Mark all the associated sessions as being invalid.
pSessionEntry = SmbCeGetFirstSessionEntry(pServerEntry);
while (pSessionEntry != NULL) {
pSessionEntry->Header.State = SMBCEDB_INVALID;
pSessionEntry = SmbCeGetNextSessionEntry(pServerEntry,pSessionEntry);
}
// Mark all the associated net roots as being invalid
pNetRootEntry = SmbCeGetFirstNetRootEntry(pServerEntry);
while (pNetRootEntry != NULL) {
pNetRootEntry->Header.State = SMBCEDB_INVALID;
pNetRootEntry = SmbCeGetNextNetRootEntry(pServerEntry,pNetRootEntry);
}
SmbCeReferenceServerEntry(pServerEntry);
// release the database resource (DPC Level)
SmbCeReleaseSpinLock();
if (!RxShouldPostCompletion()) {
SmbCeResumeAllOutstandingRequestsOnError(pServerEntry);
} else {
NTSTATUS Status;
Status = RxDispatchToWorkerThread(
MRxIfsDeviceObject,
CriticalWorkQueue,
SmbCeResumeAllOutstandingRequestsOnError,
pServerEntry);
if (Status != STATUS_SUCCESS) {
RxLog(("SmbCe Xport Disc.Error %lx\n", pServerEntry));
}
}
RxDbgTrace(0,
Dbg,
("SmbCeTransportDisconnectIndicated -- Exit\n"));
}
VOID
SmbCeHandleTransportInvalidation(
IN PSMBCE_TRANSPORT pTransport)
/*++
Routine Description:
This routine invalidates all servers using a particular transport. This is different from
a disconnect indication in which one server is invalidated. In this case a transport is being
removed/invalidated locally and all servers using that transport must be invalidated
Arguments:
pTransport - the transport being invalidated
--*/
{
PSMBCEDB_SERVER_ENTRY pServerEntry;
SmbCeAcquireSpinLock();
pServerEntry = SmbCeGetFirstServerEntry();
while (pServerEntry != NULL) {
if ((pServerEntry->pTransport != NULL) &&
(pServerEntry->pTransport->pTransport == pTransport)) {
pServerEntry->Header.State = SMBCEDB_DESTRUCTION_IN_PROGRESS;
// The invalidation needs to hold onto an extra reference to avoid
// race conditions which could lead to premature destruction of
// this server entry.
SmbCeReferenceServerEntry(pServerEntry);
}
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
}
SmbCeReleaseSpinLock();
SmbCeAcquireResource();
pServerEntry = SmbCeGetFirstServerEntry();
while (pServerEntry != NULL) {
PSMBCEDB_SERVER_ENTRY pPrevServerEntry;
BOOLEAN fDereferencePrevServerEntry = FALSE;
if ((pServerEntry->pTransport != NULL) &&
(pServerEntry->pTransport->pTransport == pTransport)) {
SmbCeReleaseResource();
SmbCeTransportDisconnectIndicated(pServerEntry);
SmbCeAcquireResource();
fDereferencePrevServerEntry = TRUE;
}
pPrevServerEntry = pServerEntry;
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
if (fDereferencePrevServerEntry) {
SmbCeDereferenceServerEntry(pPrevServerEntry);
}
}
SmbCeReleaseResource();
}
VOID
SmbCeFinalizeExchangeOnTransportDisconnect(
PSMB_EXCHANGE pExchange)
/*++
Routine Description:
This routine handles the finalization of an exchange instance during transport disconnects
Arguments:
pExchange - the exchange instance
--*/
{
ASSERT(pExchange->ReceivePendingOperations > 0);
pExchange->Status = STATUS_CONNECTION_DISCONNECTED;
pExchange->SmbStatus = STATUS_CONNECTION_DISCONNECTED;
pExchange->ReceivePendingOperations = 0;
SmbCeFinalizeExchange(pExchange);
}
VOID
SmbCeResumeAllOutstandingRequestsOnError(
PSMBCEDB_SERVER_ENTRY pServerEntry)
/*++
Routine Description:
This routine handles the resumption of all outstanding requests on an error
Arguments:
pServerEntry - the Server entry which is being classified as disconnected
Notes:
This routine requires the caller to have obtained a reference on the corresponding
server entry. This is required because invocation of this routine can be posted
which implies that a reference is required to avoid premature destruction of
the associated server entry.
--*/
{
SMBCEDB_REQUESTS Requests;
SMBCEDB_REQUESTS MidRequests;
PSMBCEDB_REQUEST_ENTRY pRequestEntry;
PMID_ATLAS pMidAtlas;
PSMB_EXCHANGE pNegotiateExchange = NULL;
//DbgPrint("SmbCeResumeAllOutstandingRequestsOnError: Invoked \n");
InitializeListHead(&Requests.ListHead);
SmbCeAcquireResource();
SmbCeAcquireSpinLock();
// Create a temporary copy of the list that can be traversed after releasing the
// resource.
// Copy all the MID assignment requests pending.
SmbCeTransferRequests(&MidRequests,&pServerEntry->MidAssignmentRequests);
// Weed out all the reconnect requests so that they can be resumed
pRequestEntry = SmbCeGetFirstRequestEntry(&pServerEntry->OutstandingRequests);
while (pRequestEntry != NULL) {
if (pRequestEntry->GenericRequest.Type == RECONNECT_REQUEST) {
PSMBCEDB_REQUEST_ENTRY pTempRequestEntry;
pTempRequestEntry = pRequestEntry;
pRequestEntry = SmbCeGetNextRequestEntry(&pServerEntry->OutstandingRequests,pRequestEntry);
SmbCeRemoveRequestEntryLite(&pServerEntry->OutstandingRequests,pTempRequestEntry);
SmbCeAddRequestEntryLite(&Requests,pTempRequestEntry);
} else {
pRequestEntry = SmbCeGetNextRequestEntry(&pServerEntry->OutstandingRequests,pRequestEntry);
}
}
if (pServerEntry->pNegotiateExchange != NULL) {
if (pServerEntry->pNegotiateExchange->ReceivePendingOperations > 0) {
pNegotiateExchange = pServerEntry->pNegotiateExchange;
}
}
// The exchanges that have valid MID's assigned to them fall into two categories
// Those that have a ReceivePendingOperation count of > 0 and those that have
// a ReceievePendingOperation count of zero. For all the exchanges that belong
// to the first category the finalize ( quiescent state ) routine must be invoked
// since no receives will be forthcoming. For those exchanges that are in the
// second category it is sufficient to mark the MID as being invalid. The
// finalization( quiescent state ) routine is going to be called on completion
// of other opertaions in this case.
pMidAtlas = pServerEntry->pMidAtlas;
if (pMidAtlas != NULL) {
PVOID pContext;
USHORT MidsProcessed = 0;
USHORT NumberOfMidsInUse;
USHORT NextMid = 0;
NumberOfMidsInUse = IfsMrxGetNumberOfMidsInUse(pMidAtlas);
while (NumberOfMidsInUse > MidsProcessed) {
pContext = IfsMrxMapMidToContext(pMidAtlas,NextMid);
if (pContext != NULL) {
PSMB_EXCHANGE pExchange = (PSMB_EXCHANGE)pContext;
pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_MID_VALID;
if ((pExchange->ReceivePendingOperations > 0) &&
((pExchange->LocalPendingOperations > 0) ||
(pExchange->CopyDataPendingOperations > 0) ||
(pExchange->SendCompletePendingOperations > 0))) {
// There are other pending operations. By merely setting the
// pending receive operations to zero, the finalization of
// the exchange is ensured.
pExchange->ReceivePendingOperations = 0;
}
if (pExchange->ReceivePendingOperations == 0) {
IfsMrxMapAndDissociateMidFromContext(pMidAtlas,NextMid,&pContext);
}
MidsProcessed++;
}
NextMid++;
}
}
pServerEntry->pNegotiateExchange = NULL;
pServerEntry->pMidAtlas = NULL;
pServerEntry->Header.State = SMBCEDB_INVALID;
SmbCeReleaseSpinLock();
SmbCeReleaseResource();
//DbgPrint("SmbCeResumeAllOutstandingRequestsOnError: Processing outsanding request \n");
// Ensure that all the reconnect requests are resumed with the appropriate error.
pRequestEntry = SmbCeGetFirstRequestEntry(&Requests);
while (pRequestEntry != NULL) {
// Remove the request entry from the list
SmbCeRemoveRequestEntryLite(&Requests,pRequestEntry);
pRequestEntry->GenericRequest.pExchange->SmbStatus = pServerEntry->ServerStatus;
SmbCeFinalizeExchange(pRequestEntry->GenericRequest.pExchange);
SmbCeTearDownRequestEntry(pRequestEntry);
pRequestEntry = SmbCeGetFirstRequestEntry(&Requests);
}
//DbgPrint("SmbCeResumeAllOutstandingRequestsOnError: Processing MID request \n");
pRequestEntry = SmbCeGetFirstRequestEntry(&MidRequests);
while (pRequestEntry != NULL) {
// Remove the request entry from the list
SmbCeRemoveRequestEntryLite(&MidRequests,pRequestEntry);
ASSERT(pRequestEntry->GenericRequest.Type == ACQUIRE_MID_REQUEST);
// Signal the waiter for resumption
pRequestEntry->MidRequest.pResumptionContext->Status = STATUS_CONNECTION_DISCONNECTED;
SmbCeResume(pRequestEntry->MidRequest.pResumptionContext);
SmbCeTearDownRequestEntry(pRequestEntry);
pRequestEntry = SmbCeGetFirstRequestEntry(&MidRequests);
}
// Resume all the outstanding requests with the error indication
// The IfsMrxDestroyMidAtlas destroys the Mid atlas and at the same
// time invokes the specified routine on each valid context.
if (pMidAtlas != NULL) {
IfsMrxDestroyMidAtlas(pMidAtlas,SmbCeFinalizeExchangeOnTransportDisconnect);
}
if (pNegotiateExchange != NULL) {
SmbCeFinalizeExchangeOnTransportDisconnect(pNegotiateExchange);
}
SmbCeDereferenceServerEntry(pServerEntry);
}
VOID
SmbCeTearDownRequestEntry(
PSMBCEDB_REQUEST_ENTRY pRequestEntry)
{
SmbMmFreeObject(pRequestEntry);
}
//
// The connection engine database initializtion/tear down routines
//
extern NTSTATUS
SmbMmInit();
extern VOID
SmbMmTearDown();
KIRQL s_SmbCeDbSpinLockSavedIrql;
KSPIN_LOCK s_SmbCeDbSpinLock;
ERESOURCE s_SmbCeDbResource;
SMBCEDB_SERVERS s_DbServers;
BOOLEAN s_SmbCeDbSpinLockAcquired;
NTSTATUS
SmbCeDbInit()
{
NTSTATUS Status;
// Initialize the lists associated with various database entities
InitializeListHead(&s_DbServers.ListHead);
// Initialize the resource associated with the database.
KeInitializeSpinLock(&s_SmbCeDbSpinLock );
ExInitializeResource(&s_SmbCeDbResource);
s_SmbCeDbSpinLockAcquired = FALSE;
MRxSmbInitializeSmbCe();
// Initialize the memory management data structures.
Status = SmbMmInit();
return Status;
}
VOID
SmbCeDbTearDown()
{
// Walk through the list of servers and tear them down.
PSMBCEDB_SERVER_ENTRY pServerEntry = NULL;
SmbCeAcquireResource();
SmbCeAcquireSpinLock();
pServerEntry = SmbCeGetFirstServerEntry();
while (pServerEntry != NULL) {
PSMBCEDB_SERVER_ENTRY pTempServerEntry;
pTempServerEntry = pServerEntry;
SmbCeReferenceServerEntry(pServerEntry);
SmbCeReferenceServerEntry(pServerEntry);
SmbCeReleaseSpinLock();
SmbCeReleaseResource();
pServerEntry->ServerStatus = STATUS_REDIRECTOR_PAUSED;
SmbCeResumeAllOutstandingRequestsOnError(pServerEntry);
SmbCeAcquireSpinLock();
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
SmbCeReleaseSpinLock();
SmbCeDereferenceServerEntry(pTempServerEntry);
SmbCeAcquireResource();
SmbCeAcquireSpinLock();
}
SmbCeReleaseSpinLock();
SmbCeReleaseResource();
MRxSmbTearDownSmbCe();
SmbCeAcquireResource();
SmbCeAcquireSpinLock();
pServerEntry = SmbCeGetFirstServerEntry();
while (pServerEntry != NULL) {
ASSERT(pServerEntry->Header.SwizzleCount == 1);
pServerEntry->Header.State = SMBCEDB_MARKED_FOR_DELETION;
SmbCeRemoveServerEntryLite(pServerEntry);
SmbCeReleaseSpinLock();
SmbCeReleaseResource();
SmbCeTearDownServerEntry(pServerEntry);
SmbCeAcquireResource();
SmbCeAcquireSpinLock();
pServerEntry = SmbCeGetFirstServerEntry();
}
SmbCeReleaseSpinLock();
SmbCeReleaseResource();
// free the pool associated with the resource
ExDeleteResource(&s_SmbCeDbResource);
// Tear down the connection engine memory management data structures.
SmbMmTearDown();
}