windows-nt/Source/XPSP1/NT/base/fs/srv/smbtree.c
2020-09-26 16:20:57 +08:00

1958 lines
56 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:
smbtree.c
Abstract:
This module contains routines for dealing with tree connects and
disconnects:
Tree Connect
Tree Connect And X
Tree Disconnect
Author:
David Treadwell (davidtr) 15-Nov-1989
Revision History:
--*/
#include "precomp.h"
#include "smbtree.tmh"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvSmbTreeConnect )
#pragma alloc_text( PAGE, SrvSmbTreeConnectAndX )
#pragma alloc_text( PAGE, SrvSmbTreeDisconnect )
#endif
SMB_PROCESSOR_RETURN_TYPE
SrvSmbTreeConnect (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
Processes a tree connect SMB.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{
PREQ_TREE_CONNECT request;
PRESP_TREE_CONNECT response;
PSESSION session;
PCONNECTION connection;
PPAGED_CONNECTION pagedConnection;
PTABLE_HEADER tableHeader;
PTABLE_ENTRY entry;
SHORT tidIndex;
PSHARE share;
PTREE_CONNECT treeConnect;
PSZ password, service;
USHORT len;
NTSTATUS status = STATUS_SUCCESS;
NTSTATUS TableStatus;
SMB_STATUS SmbStatus = SmbStatusInProgress;
BOOLEAN didLogon = FALSE;
SHORT uidIndex;
SMB_DIALECT smbDialect;
PUNICODE_STRING clientMachineNameString;
ACCESS_MASK desiredAccess;
ACCESS_MASK grantedAccess;
SECURITY_SUBJECT_CONTEXT subjectContext;
UNICODE_STRING domain = { 0, 0, StrNull };
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_TREE_CONNECT;
SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(TREE1) {
KdPrint(( "Tree connect request header at 0x%p, response header at 0x%p\n",
WorkContext->RequestHeader, WorkContext->ResponseHeader ));
KdPrint(( "Tree connect request parameters at 0x%p, response parameters at 0x%p\n",
WorkContext->RequestParameters,
WorkContext->ResponseParameters ));
}
//
// Set up parameters.
//
request = (PREQ_TREE_CONNECT)(WorkContext->RequestParameters);
response = (PRESP_TREE_CONNECT)(WorkContext->ResponseParameters);
connection = WorkContext->Connection;
pagedConnection = connection->PagedConnection;
smbDialect = connection->SmbDialect;
//
// If this client has not yet done a session setup and this his first
// tree connection then we must first do a logon. (i.e. SessionSetup)
//
len = SrvGetStringLength(
(PSZ)request->Buffer,
END_OF_REQUEST_SMB( WorkContext ),
FALSE, // not unicode
FALSE // do not include null terminator
);
if( len == (USHORT)-1 ) {
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
status = STATUS_INVALID_SMB;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
password = (PSZ)request->Buffer + 2 + len;
len = SrvGetStringLength(
password,
END_OF_REQUEST_SMB( WorkContext ),
FALSE, // not unicode
FALSE // do not include null terminator
);
if( len == (USHORT)-1 ) {
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
status = STATUS_INVALID_SMB;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
service = password + (len + 1) + 1;
//
// Allocate a tree connect block. We do this early on the
// assumption that the request will usually succeed. This also
// reduces the amount of time that we hold the lock.
//
SrvAllocateTreeConnect( &treeConnect, NULL );
if ( treeConnect == NULL ) {
//
// Unable to allocate tree connect. Return an error to the
// client.
//
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
status = STATUS_INSUFF_SERVER_RESOURCES;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
ASSERT( SrvSessionList.Lock == &SrvOrderedListLock );
ACQUIRE_LOCK( &connection->Lock );
if ( pagedConnection->CurrentNumberOfSessions != 0 ) {
RELEASE_LOCK( &connection->Lock );
session = SrvVerifyUid (
WorkContext,
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
);
if ( session == NULL ) {
//
// This should only happen if the client has already
// established a session, as in tree connecting with a bad
// UID.
//
SrvFreeTreeConnect( treeConnect );
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
status = STATUS_SMB_BAD_UID;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
else if( session->IsSessionExpired )
{
SrvFreeTreeConnect( treeConnect );
status = SESSION_EXPIRED_STATUS_CODE;
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
} else if ( (smbDialect <= SmbDialectLanMan10) ||
(smbDialect == SmbDialectIllegal) ) {
//
// An LM 1.0 or newer client has tried to do a tree connect
// without first doing session setup. We call this a protocol
// violation.
//
// Also catch clients that are trying to connect without
// negotiating a valid protocol.
//
RELEASE_LOCK( &connection->Lock );
IF_DEBUG(SMB_ERRORS) {
if ( smbDialect == SmbDialectIllegal ) {
KdPrint(("SrvSmbTreeConnect: Client %z is using an illegal "
"dialect.\n", (PCSTRING)&connection->OemClientMachineNameString ));;
} else {
KdPrint(( "Client speaking dialect %ld sent tree connect without session setup.\n", connection->SmbDialect ));
}
}
SrvFreeTreeConnect( treeConnect );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
status = STATUS_INVALID_SMB;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
} else {
UNICODE_STRING machineName;
PENDPOINT endpoint;
BOOLEAN seqNumbers;
RELEASE_LOCK( &connection->Lock );
//
// Convert the client name to unicode
//
clientMachineNameString = &pagedConnection->ClientMachineNameString;
if ( clientMachineNameString->Length == 0 ) {
UNICODE_STRING clientMachineName;
clientMachineName.Buffer = pagedConnection->ClientMachineName;
clientMachineName.MaximumLength =
(USHORT)(COMPUTER_NAME_LENGTH+1)*sizeof(WCHAR);
(VOID)RtlOemStringToUnicodeString(
&clientMachineName,
&connection->OemClientMachineNameString,
FALSE
);
//
// Add the double backslashes to the length
//
clientMachineNameString->Length =
(USHORT)(clientMachineName.Length + 2*sizeof(WCHAR));
}
//
// Form a string describing the computer name without the
// leading backslashes.
//
machineName.Buffer = clientMachineNameString->Buffer + 2;
machineName.Length = clientMachineNameString->Length - 2 * sizeof(WCHAR);
machineName.MaximumLength =
clientMachineNameString->MaximumLength - 2 * sizeof(WCHAR);
//
// Allocate a session block.
//
SrvAllocateSession(
&session,
&machineName,
&domain );
if ( session == NULL ) {
//
// Unable to allocate a Session block. Return an error
// status.
//
SrvFreeTreeConnect( treeConnect );
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
status = STATUS_INSUFF_SERVER_RESOURCES;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Assume that down-level clients that are getting logged on
// here will always use canonicalized (uppercase) paths. This
// will result in case insensitivity for all operations.
//
session->UsingUppercasePaths = TRUE;
//
// The only way for a client to tell us the buffer size or the
// max count of pending requests he wants to use is the Session
// Setup SMB. If he didn't send one, then we get to
// unilaterally determine the buffer size and multiplex count
// used by both of us.
//
endpoint = connection->Endpoint;
if ( endpoint->IsConnectionless ) {
ULONG adapterNumber;
//
// Our session max buffer size is the smaller of the
// server receive buffer size and the ipx transport
// indicated max packet size.
//
adapterNumber =
WorkContext->ClientAddress->DatagramOptions.LocalTarget.NicId;
session->MaxBufferSize =
(USHORT) GetIpxMaxBufferSize(
endpoint,
adapterNumber,
SrvReceiveBufferLength
);
} else {
session->MaxBufferSize = (USHORT)SrvReceiveBufferLength;
}
session->MaxMpxCount = SrvMaxMpxCount;
if ( session->MaxMpxCount < 2 ) {
connection->OplocksAlwaysDisabled = TRUE;
}
if( SrvSmbSecuritySignaturesRequired == TRUE &&
WorkContext->Connection->Endpoint->IsConnectionless == FALSE ) {
seqNumbers = TRUE;
} else {
seqNumbers = FALSE;
}
//
// Try to find legitimate name/password combination.
//
status = SrvValidateUser(
&session->UserHandle,
session,
connection,
&machineName,
password,
strlen( password ) + 1,
NULL, // CaseSensitivePassword
0, // CaseSensitivePasswordLength
seqNumbers,
NULL // action
);
//
// If a bad name/password combination was sent, return an error.
//
if ( !NT_SUCCESS(status) ) {
SrvFreeSession( session );
SrvFreeTreeConnect ( treeConnect );
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: Bad user/password combination.\n" ));
}
SrvStatistics.LogonErrors++;
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
IF_SMB_DEBUG(ADMIN1) {
KdPrint(( "Validated user: %ws\n",
connection->PagedConnection->ClientMachineName ));
}
//
// Making a new session visible is a multiple-step operation. It
// must be inserted in the global ordered tree connect list and the
// containing connection's session table, and the connection must be
// referenced. We need to make these operations appear atomic, so
// that the session cannot be accessed elsewhere before we're done
// setting it up. In order to do this, we hold all necessary locks
// the entire time we're doing the operations. The first operation
// is protected by the global ordered list lock
// (SrvOrderedListLock), while the other operations are protected by
// the per-connection lock. We take out the ordered list lock
// first, then the connection lock. This ordering is required by
// lock levels (see lock.h).
//
//
// Ready to try to find a UID for the session. Check to see if
// the connection is being closed, and if so, terminate this
// operation.
//
ASSERT( SrvSessionList.Lock == &SrvOrderedListLock );
ACQUIRE_LOCK( SrvSessionList.Lock );
ACQUIRE_LOCK( &connection->Lock );
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
RELEASE_LOCK( &connection->Lock );
RELEASE_LOCK( SrvSessionList.Lock );
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: Connection closing\n" ));
}
SrvFreeSession( session );
SrvFreeTreeConnect( treeConnect );
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
status = STATUS_INVALID_PARAMETER;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Because the client is speaking the "core" dialect, it will
// not send a valid UID in future SMBs, so it can only have one
// session. We define that session to live in UID slot 0. We
// know that the client has no sessions yet, so slot 0 must be
// free.
//
tableHeader = &pagedConnection->SessionTable;
ASSERT( tableHeader->Table[0].Owner == NULL );
uidIndex = 0;
//
// Remove the UID slot from the free list and set its owner and
// sequence number. Create a UID for the session. Increment
// count of sessions.
//
entry = &tableHeader->Table[uidIndex];
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
DEBUG entry->NextFreeEntry = -2;
if ( tableHeader->LastFreeEntry == uidIndex ) {
tableHeader->LastFreeEntry = -1;
}
entry->Owner = session;
INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
if ( uidIndex == 0 && entry->SequenceNumber == 0 ) {
INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
}
session->Uid = MAKE_UID( uidIndex, entry->SequenceNumber );
pagedConnection->CurrentNumberOfSessions++;
IF_SMB_DEBUG(ADMIN1) {
KdPrint(( "Found UID. Index = 0x%lx, sequence = 0x%lx\n",
(ULONG)UID_INDEX( session->Uid ),
(ULONG)UID_SEQUENCE( session->Uid ) ));
}
//
// Insert the session on the global session list.
//
SrvInsertEntryOrderedList( &SrvSessionList, session );
//
// Reference the connection block to account for the new
// session.
//
SrvReferenceConnection( connection );
session->Connection = connection;
RELEASE_LOCK( &connection->Lock );
RELEASE_LOCK( SrvSessionList.Lock );
//
// Session successfully created. Remember its address in the
// work context block.
//
// *** Note that the reference count on the session block is
// initially set to 2, to allow for the active status on the
// block and the pointer that we're maintaining. In other
// words, this is a referenced pointer, and the pointer must
// be dereferenced when processing of this SMB is complete.
//
WorkContext->Session = session;
didLogon = TRUE;
}
//
// Try to match pathname against available shared resources. Note
// that if SrvVerifyShare finds a matching share, it references it
// and stores its address in WorkContext->Share.
//
share = SrvVerifyShare(
WorkContext,
(PSZ)request->Buffer + 1,
service,
SMB_IS_UNICODE( WorkContext ),
session->IsNullSession,
&status,
NULL
);
//
// If no match was found, return an error.
//
if ( share == NULL ) {
if ( didLogon ) {
SrvCloseSession( session );
}
SrvFreeTreeConnect( treeConnect );
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: SrvVerifyShare failed for %s. Status = %x\n", request->Buffer+1, status ));
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Impersonate the user so that we can capture his security context.
// This is necessary in order to determine whether the user can
// connect to the share.
//
status = IMPERSONATE( WorkContext );
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
if ( didLogon ) {
SrvCloseSession( session );
}
SrvFreeTreeConnect( treeConnect );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
SeCaptureSubjectContext( &subjectContext );
//
// Set up the desired access on the share, based on whether the
// server is paused. If the server is paused, admin privilege is
// required to connect to any share; if the server is not paused,
// admin privilege is required only for admin shares (C$, etc.).
//
if ( SrvPaused ) {
desiredAccess = SRVSVC_PAUSED_SHARE_CONNECT;
} else {
desiredAccess = SRVSVC_SHARE_CONNECT;
}
//
// Check whether the user has access to this share.
//
if ( !SeAccessCheck(
share->SecurityDescriptor,
&subjectContext,
FALSE,
desiredAccess,
0L,
NULL,
&SrvShareConnectMapping,
UserMode,
&grantedAccess,
&status
) ) {
IF_SMB_DEBUG(TREE2) {
KdPrint(( "SrvSmbTreeConnect: SeAccessCheck failed: %X\n",
status ));
}
//
// Release the subject context and revert to the server's security
// context.
//
SeReleaseSubjectContext( &subjectContext );
REVERT( );
if ( SrvPaused ) {
SrvSetSmbError( WorkContext, STATUS_SHARING_PAUSED );
status = STATUS_SHARING_PAUSED;
} else {
SrvSetSmbError( WorkContext, status );
}
if ( didLogon ) {
SrvCloseSession( session );
}
SrvFreeTreeConnect( treeConnect );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
ASSERT( grantedAccess == desiredAccess );
//
// Release the subject context and revert to the server's security
// context.
//
SeReleaseSubjectContext( &subjectContext );
REVERT( );
//
// Let the license server know
//
if( share->ShareType != ShareTypePipe ) {
status = SrvXsLSOperation( session, XACTSRV_MESSAGE_LSREQUEST );
if( !NT_SUCCESS( status ) ) {
if ( didLogon ) {
SrvCloseSession( session );
}
SrvFreeTreeConnect( treeConnect );
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: License server returned %X\n",
status ));
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
}
//
// Making a new tree connect visible is a three-step operation. It
// must be inserted in the containing share's tree connect list, the
// global ordered tree connect list, and the containing connection's
// tree connect table. We need to make these operations appear
// atomic, so that the tree connect cannot be accessed elsewhere
// before we're done setting it up. In order to do this, we hold
// all necessary locks the entire time we're doing the three
// operations. The first and second operations are protected by the
// global share lock (SrvShareLock), while the third operation is
// protected by the per-connection lock. We take out the share lock
// first, then the connection lock. This ordering is required by
// lock levels (see lock.h).
//
// Another problem here is that the checking of the share state, the
// inserting of the tree connect on the share's list, and the
// referencing of the share all need to be atomic. (The same holds
// for the connection actions.) Normally this would not be a
// problem, because we could just hold the share lock while doing
// all three actions. However, in this case we also need to hold
// the connection lock, and we can't call SrvReferenceShare while
// doing that. To get around this problem, we reference the share
// _before_ taking out the locks, and dereference after releasing
// the locks if we decide not to insert the tree connect.
//
status = SrvReferenceShareForTreeConnect( share );
//
// SrvReferenceShareForTreeConnect will fail if it cannot open the
// share root directory for some reason. If this happens,
// fail the tree connect attempt.
//
if ( !NT_SUCCESS(status) ) {
if ( didLogon ) {
SrvCloseSession( session );
}
SrvFreeTreeConnect( treeConnect );
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: open of share root failed:%X\n",
status ));
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
ACQUIRE_LOCK( &SrvShareLock );
ASSERT( SrvTreeConnectList.Lock == &SrvShareLock );
ACQUIRE_LOCK( &connection->Lock );
//
// We first check all conditions to make sure that we can actually
// insert this tree connect block.
//
// Make sure that the share isn't closing, and that there aren't
// already too many uses on this share.
//
if ( GET_BLOCK_STATE(share) != BlockStateActive ) {
//
// The share is closing. Reject the request.
//
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: Share %wZ (0x%p) is closing\n",
&share->ShareName, share ));
}
status = STATUS_INVALID_PARAMETER;
goto cant_insert;
}
if ( share->CurrentUses > share->MaxUses ) {
//
// The share is full. Reject the request.
//
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: No more uses available for share %wZ (0x%p), max = %ld\n",
&share->ShareName, share, share->MaxUses ));
}
status = STATUS_REQUEST_NOT_ACCEPTED;
goto cant_insert;
}
//
// Make sure that the connection isn't closing.
//
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
IF_DEBUG(SMB_ERRORS) {
KdPrint(( "SrvSmbTreeConnect: Connection closing\n" ));
}
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
status = STATUS_INVALID_PARAMETER;
goto cant_insert;
}
//
// Find a TID that can be used for this tree connect.
//
tableHeader = &pagedConnection->TreeConnectTable;
if ( tableHeader->FirstFreeEntry == -1
&&
SrvGrowTable(
tableHeader,
SrvInitialTreeTableSize,
SrvMaxTreeTableSize,
&TableStatus ) == FALSE
) {
//
// No free entries in the tree table. Reject the request.
//
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: No more TIDs available.\n" ));
}
status = TableStatus;
if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
{
SrvLogTableFullError( SRV_TABLE_TREE_CONNECT );
}
goto cant_insert;
}
tidIndex = tableHeader->FirstFreeEntry;
//
// All conditions have been satisfied. We can now do the things
// necessary to make the tree connect visible.
//
// Increment the count of uses for the share. Link the tree connect
// into the list of active tree connects for the share. Save the
// share address in the tree connect. Note that we referenced the
// share earlier, before taking out the connection lock.
//
SrvInsertTailList(
&share->TreeConnectList,
&treeConnect->ShareListEntry
);
treeConnect->Share = share;
//
// Remove the TID slot from the free list and set its owner and
// sequence number. Create a TID for the tree connect.
//
entry = &tableHeader->Table[tidIndex];
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
DEBUG entry->NextFreeEntry = -2;
if ( tableHeader->LastFreeEntry == tidIndex ) {
tableHeader->LastFreeEntry = -1;
}
entry->Owner = treeConnect;
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
if ( tidIndex == 0 && entry->SequenceNumber == 0 ) {
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
}
treeConnect->Tid = MAKE_TID( tidIndex, entry->SequenceNumber );
IF_SMB_DEBUG(TREE1) {
KdPrint(( "Found TID. Index = 0x%lx, sequence = 0x%lx\n",
TID_INDEX( treeConnect->Tid ),
TID_SEQUENCE( treeConnect->Tid ) ));
}
//
// Reference the connection to account for the active tree connect.
//
SrvReferenceConnection( connection );
treeConnect->Connection = connection;
if( session )
{
SrvReferenceSession( session );
treeConnect->Session = session;
}
//
// Link the tree connect into the global list of tree connects.
//
SrvInsertEntryOrderedList( &SrvTreeConnectList, treeConnect );
//
// Release the locks used to make this operation appear atomic.
//
RELEASE_LOCK( &connection->Lock );
RELEASE_LOCK( &SrvShareLock );
//
// Get the qos information for this connection
//
SrvUpdateVcQualityOfService ( connection, NULL );
//
// Tree connect successfully created. Because the tree connect was
// created with an initial reference count of 2, dereference it now.
//
// *** Don't bother to save the tree connect address in the work
// context block, because we're going to forget our pointers
// soon anyway (we're done with the request). TreeConnectAndX
// has to remember these things, though.
//
SrvDereferenceTreeConnect( treeConnect );
//
// Set up response SMB.
//
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Tid, treeConnect->Tid );
response->WordCount = 2;
SmbPutUshort( &response->MaxBufferSize, (USHORT)session->MaxBufferSize );
SmbPutUshort( &response->Tid, treeConnect->Tid );
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters = NEXT_LOCATION(
response,
RESP_TREE_CONNECT,
0
);
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbTreeConnect complete.\n" ));
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
cant_insert:
//
// We get here if for some reason we decide that we can't insert
// the tree connect. On entry, status contains the reason code.
// The connection lock and the share lock are held.
//
RELEASE_LOCK( &connection->Lock );
RELEASE_LOCK( &SrvShareLock );
if ( didLogon ) {
SrvCloseSession( session );
}
SrvDereferenceShareForTreeConnect( share );
SrvFreeTreeConnect( treeConnect );
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbTreeConnect
SMB_PROCESSOR_RETURN_TYPE
SrvSmbTreeConnectAndX (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
Processes a tree connect and X SMB.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{
PREQ_TREE_CONNECT_ANDX request;
PRESP_TREE_CONNECT_ANDX response;
PRESP_EXTENDED_TREE_CONNECT_ANDX responseExtended;
PRESP_21_TREE_CONNECT_ANDX response21;
NTSTATUS status = STATUS_SUCCESS;
NTSTATUS TableStatus;
SMB_STATUS SmbStatus = SmbStatusInProgress;
PCONNECTION connection;
PPAGED_CONNECTION pagedConnection;
PTABLE_HEADER tableHeader;
PTABLE_ENTRY entry;
SHORT tidIndex;
PSHARE share;
PTREE_CONNECT treeConnect;
PVOID shareName;
PUCHAR shareType;
USHORT shareNameLength;
USHORT reqAndXOffset;
UCHAR nextCommand;
PSZ shareString;
USHORT shareStringLength;
USHORT RequestFlags;
USHORT byteCount;
PUCHAR smbBuffer;
PSESSION session;
SECURITY_SUBJECT_CONTEXT subjectContext;
ACCESS_MASK desiredAccess;
ACCESS_MASK grantedAccess;
BOOLEAN isUnicode;
UNICODE_STRING serverName;
BOOLEAN remapPipeNames = FALSE;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_TREE_CONNECT_AND_X;
SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(TREE1) {
KdPrint(( "Tree connect and X request header at 0x%p, response header at 0x%p\n",
WorkContext->RequestHeader, WorkContext->ResponseHeader ));
KdPrint(( "Tree connect and X request parameters at 0x%p, response parameters at 0x%p\n",
WorkContext->RequestParameters,
WorkContext->ResponseParameters ));
}
//
// Set up parameters.
//
request = (PREQ_TREE_CONNECT_ANDX)(WorkContext->RequestParameters);
response = (PRESP_TREE_CONNECT_ANDX)(WorkContext->ResponseParameters);
responseExtended = (PRESP_EXTENDED_TREE_CONNECT_ANDX)(WorkContext->ResponseParameters);
response21 = (PRESP_21_TREE_CONNECT_ANDX)(WorkContext->ResponseParameters);
//
// If bit 0 of Flags is set, disconnect tree in header TID. We must
// get the appropriate tree connect pointer. SrvVerifyTid does this
// for us, referencing the tree connect and storing the pointer in
// the work context block. We have to dereference the block and
// erase the pointer after calling SrvCloseTreeConnect.
//
if ( (SmbGetUshort( &request->Flags ) & 1) != 0 ) {
if ( SrvVerifyTid(
WorkContext,
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid )
) == NULL ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: Invalid TID to disconnect: 0x%lx\n",
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ) ));
}
//
// Just ignore an invalid TID--this is what the LM 2.0
// server does.
//
} else {
SrvCloseTreeConnect( WorkContext->TreeConnect );
SrvDereferenceTreeConnect( WorkContext->TreeConnect );
WorkContext->TreeConnect = NULL;
}
}
//
// Validate the UID in the header and get a session pointer. We need
// the user's token to check whether they can access this share.
//
session = SrvVerifyUid(
WorkContext,
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
);
//
// If we couldn't find a valid session fail the tree connect.
//
if ( session == NULL ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: rejecting tree connect for "
"session %p due to server paused.\n", session ));
}
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
status = STATUS_SMB_BAD_UID;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
else if( session->IsSessionExpired )
{
status = SESSION_EXPIRED_STATUS_CODE;
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Try to match pathname against available shared resources. Note
// that if SrvVerifyShare finds a matching share, it references it
// and stores its address in WorkContext->Share.
//
shareName = (PSZ)request->Buffer +
SmbGetUshort( &request->PasswordLength );
connection = WorkContext->Connection;
pagedConnection = connection->PagedConnection;
isUnicode = SMB_IS_UNICODE( WorkContext );
if ( isUnicode ) {
shareName = ALIGN_SMB_WSTR( shareName );
}
shareNameLength = SrvGetStringLength(
shareName,
END_OF_REQUEST_SMB( WorkContext ),
SMB_IS_UNICODE( WorkContext ),
TRUE // include null terminator
);
//
// if share name is bogus, return an error.
//
if ( shareNameLength == (USHORT)-1 ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: pathname is bogus.\n"));
}
SrvSetSmbError( WorkContext, STATUS_BAD_NETWORK_NAME );
status = STATUS_BAD_NETWORK_NAME;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
shareType = (PCHAR)shareName + shareNameLength;
share = SrvVerifyShare(
WorkContext,
shareName,
shareType,
isUnicode,
session->IsNullSession,
&status,
&serverName
);
//
// If no match was found, return an error.
//
if ( share == NULL ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: pathname does not match "
"any shares: %s\n", shareName ));
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// If the the client is connecting with a netbiosless transport and the name of the
// server which the client was requesting doesn't match any of our servernames, then
// the client has accidentally connected to the wrong server. Let the client know.
//
if( !SrvDisableStrictNameChecking &&
serverName.Buffer != NULL &&
connection->Endpoint->IsNoNetBios &&
SrvIsDottedQuadAddress( &serverName ) == FALSE &&
SrvFindNamedEndpoint( &serverName, NULL ) == FALSE ) {
BOOL bBadName = TRUE;
// Last check, make sure its not the domain DNS name (which may differ from the NETBIOS DNS name)
ACQUIRE_LOCK_SHARED( &SrvEndpointLock );
// We only check up to the first ., so ntdev.microsoft.com would match SrvDnsDomainName "NTDEV"
// Strip off the excess info for the check, then put it back
if( SrvDnsDomainName ) {
if( SrvDnsDomainName->Length <= serverName.Length )
{
USHORT oldLength = serverName.Length;
serverName.Length = SrvDnsDomainName->Length;
if( RtlEqualUnicodeString( &serverName, SrvDnsDomainName, TRUE ) )
{
bBadName = FALSE;
}
serverName.Length = oldLength;
}
}
RELEASE_LOCK( &SrvEndpointLock );
//
// The client has connected to this server in error--turn the client back!
//
if( bBadName )
{
SrvSetSmbError( WorkContext, STATUS_DUPLICATE_NAME );
status = STATUS_DUPLICATE_NAME;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
}
//
// Impersonate the user so that we can capture his security context.
// This is necessary in order to determine whether the user can
// connect to the share.
//
status = IMPERSONATE( WorkContext );
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
SeCaptureSubjectContext( &subjectContext );
//
// Set up the desired access on the share, based on whether the
// server is paused. If the server is paused, admin privilege is
// required to connect to any share; if the server is not paused,
// admin privilege is required only for admin shares (C$, etc.).
//
if ( SrvPaused ) {
desiredAccess = SRVSVC_PAUSED_SHARE_CONNECT;
} else {
desiredAccess = SRVSVC_SHARE_CONNECT;
}
//
// Check whether the user has access to this share.
//
if ( !SeAccessCheck(
share->SecurityDescriptor,
&subjectContext,
FALSE,
desiredAccess,
0L,
NULL,
&SrvShareConnectMapping,
UserMode,
&grantedAccess,
&status
) ) {
IF_SMB_DEBUG(TREE2) {
KdPrint(( "SrvSmbTreeConnectAndX: SeAccessCheck failed: %X\n",
status ));
}
//
// Release the subject context and revert to the server's security
// context.
//
SeReleaseSubjectContext( &subjectContext );
REVERT( );
if ( SrvPaused ) {
SrvSetSmbError( WorkContext, STATUS_SHARING_PAUSED );
status = STATUS_SHARING_PAUSED;
} else {
SrvSetSmbError( WorkContext, status );
}
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
ASSERT( grantedAccess == desiredAccess );
//
// Release the subject context and revert to the server's security
// context.
//
SeReleaseSubjectContext( &subjectContext );
REVERT( );
//
// See if the license server wants to let this person in on the NTAS
//
if( share->ShareType != ShareTypePipe ) {
status = SrvXsLSOperation( session, XACTSRV_MESSAGE_LSREQUEST );
if( !NT_SUCCESS( status ) ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: License server returned %X\n",
status ));
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
} else if( serverName.Buffer != NULL ) {
//
// This is the IPC$ share. See if we're supposed to remap pipe names
//
SrvFindNamedEndpoint( &serverName, &remapPipeNames );
}
//
// Allocate a tree connect block.
//
SrvAllocateTreeConnect( &treeConnect, serverName.Buffer ? &serverName : NULL );
if ( treeConnect == NULL ) {
//
// Unable to allocate tree connect. Return an error to the
// client.
//
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
status = STATUS_INSUFF_SERVER_RESOURCES;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
treeConnect->RemapPipeNames = remapPipeNames;
//
// Making a new tree connect visible is a three-step operation. It
// must be inserted in the containing share's tree connect list, the
// global ordered tree connect list, and the containing connection's
// tree connect table. We need to make these operations appear
// atomic, so that the tree connect cannot be accessed elsewhere
// before we're done setting it up. In order to do this, we hold
// all necessary locks the entire time we're doing the three
// operations. The first and second operations are protected by the
// global share lock (SrvShareLock), while the third operation is
// protected by the per-connection lock. We take out the share lock
// first, then the connection lock. This ordering is required by
// lock levels (see lock.h).
//
// Another problem here is that the checking of the share state, the
// inserting of the tree connect on the share's list, and the
// referencing of the share all need to be atomic. (The same holds
// for the connection actions.) Normally this would not be a
// problem, because we could just hold the share lock while doing
// all three actions. However, in this case we also need to hold
// the connection lock, and we can't call SrvReferenceShare while
// doing that. To get around this problem, we reference the share
// _before_ taking out the locks, and dereference after releasing
// the locks if we decide not to insert the tree connect.
//
status = SrvReferenceShareForTreeConnect( share );
//
// SrvReferenceShareForTreeConnect will fail if it cannot open the
// share root directory for some reason. If this happens,
// fail the tree connect attempt.
//
if ( !NT_SUCCESS(status) ) {
SrvFreeTreeConnect( treeConnect );
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: open of share root failed:%X\n",
status ));
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
ACQUIRE_LOCK( &SrvShareLock );
ASSERT( SrvTreeConnectList.Lock == &SrvShareLock );
ACQUIRE_LOCK( &connection->Lock );
//
// We first check all conditions to make sure that we can actually
// insert this tree connect block.
//
// Make sure that the share isn't closing, and that there aren't
// already too many uses on this share.
//
if ( GET_BLOCK_STATE(share) != BlockStateActive ) {
//
// The share is closing. Reject the request.
//
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: Share %wZ (0x%p) is closing\n",
&share->ShareName, share ));
}
status = STATUS_INVALID_PARAMETER;
goto cant_insert;
}
if ( share->CurrentUses > share->MaxUses ) {
//
// The share is full. Reject the request.
//
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: No more uses available for share %wZ (0x%p), max = %ld\n",
&share->ShareName, share, share->MaxUses ));
}
status = STATUS_REQUEST_NOT_ACCEPTED;
goto cant_insert;
}
//
// Make sure that the connection isn't closing, and that there's
// room in its tree connect table.
//
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
IF_DEBUG(SMB_ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: Connection closing\n" ));
}
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
status = STATUS_INVALID_PARAMETER;
goto cant_insert;
}
//
// Find a TID that can be used for this tree connect.
//
tableHeader = &pagedConnection->TreeConnectTable;
if ( tableHeader->FirstFreeEntry == -1
&&
SrvGrowTable(
tableHeader,
SrvInitialTreeTableSize,
SrvMaxTreeTableSize,
&TableStatus ) == FALSE
) {
//
// No free entries in the tree table. Reject the request.
//
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbTreeConnect: No more TIDs available.\n" ));
}
if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
{
SrvLogTableFullError( SRV_TABLE_TREE_CONNECT );
}
status = TableStatus;
goto cant_insert;
}
tidIndex = tableHeader->FirstFreeEntry;
//
// All conditions have been satisfied. We can now do the things
// necessary to make the tree connect visible.
//
// Link the tree connect into the list of active tree connects for
// the share. Save the share address in the tree connect. Note
// that we referenced the share earlier, before taking out the
// connection lock.
//
SrvInsertTailList(
&share->TreeConnectList,
&treeConnect->ShareListEntry
);
treeConnect->Share = share;
//
// Remove the TID slot from the free list and set its owner and
// sequence number. Create a TID for the tree connect.
//
entry = &tableHeader->Table[tidIndex];
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
DEBUG entry->NextFreeEntry = -2;
if ( tableHeader->LastFreeEntry == tidIndex ) {
tableHeader->LastFreeEntry = -1;
}
entry->Owner = treeConnect;
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
if ( tidIndex == 0 && entry->SequenceNumber == 0 ) {
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
}
treeConnect->Tid = MAKE_TID( tidIndex, entry->SequenceNumber );
IF_SMB_DEBUG(TREE1) {
KdPrint(( "Found TID. Index = 0x%lx, sequence = 0x%lx\n",
TID_INDEX( treeConnect->Tid ),
TID_SEQUENCE( treeConnect->Tid ) ));
}
//
// Reference the connection to account for the active tree connect.
//
SrvReferenceConnection( connection );
treeConnect->Connection = connection;
if( session )
{
SrvReferenceSession( session );
treeConnect->Session = session;
}
//
// Link the tree connect into the global list of tree connects.
//
SrvInsertEntryOrderedList( &SrvTreeConnectList, treeConnect );
//
// Release the locks used to make this operation appear atomic.
//
RELEASE_LOCK( &connection->Lock );
RELEASE_LOCK( &SrvShareLock );
//
// Get the qos information for this connection
//
SrvUpdateVcQualityOfService ( connection, NULL );
//
// Tree connect successfully created. Save the tree connect block
// address in the work context block. Note that the reference count
// on the new block was incremented on creation to account for our
// reference to the block.
//
WorkContext->TreeConnect = treeConnect;
//
// Set up response SMB, making sure to save request fields first in
// case the response overwrites the request.
//
reqAndXOffset = SmbGetUshort( &request->AndXOffset );
nextCommand = request->AndXCommand;
RequestFlags = SmbGetUshort(&request->Flags);
SmbPutAlignedUshort( &WorkContext->RequestHeader->Tid, treeConnect->Tid );
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Tid, treeConnect->Tid );
response->AndXCommand = nextCommand;
response->AndXReserved = 0;
if ( connection->SmbDialect > SmbDialectDosLanMan21) {
response->WordCount = 2;
smbBuffer = (PUCHAR)response->Buffer;
} else {
if (RequestFlags & TREE_CONNECT_ANDX_EXTENDED_RESPONSE) {
responseExtended->WordCount = 7;
smbBuffer = (PUCHAR)responseExtended->Buffer;
} else {
response21->WordCount = 3;
smbBuffer = (PUCHAR)response21->Buffer;
}
// Fields common to 21 and extended response.
response21->OptionalSupport = SMB_SUPPORT_SEARCH_BITS;
SrvIsShareInDfs( share, &share->IsDfs, &share->IsDfsRoot );
if (share->IsDfs) {
response21->OptionalSupport |= SMB_SHARE_IS_IN_DFS;
}
switch( share->CSCState ) {
case CSC_CACHE_MANUAL_REINT:
response21->OptionalSupport |= SMB_CSC_CACHE_MANUAL_REINT;
break;
case CSC_CACHE_AUTO_REINT:
response21->OptionalSupport |= SMB_CSC_CACHE_AUTO_REINT;
break;
case CSC_CACHE_VDO:
response21->OptionalSupport |= SMB_CSC_CACHE_VDO;
break;
case CSC_CACHE_NONE:
response21->OptionalSupport |= SMB_CSC_NO_CACHING;
break;
}
if( share->UniqueNames )
{
response21->OptionalSupport |= SMB_UNIQUE_FILE_NAME;
}
}
//
// Append the service name string to the SMB. The service name
// is always sent in ANSI.
//
shareString = StrShareTypeNames[share->ShareType];
shareStringLength = (USHORT)( strlen( shareString ) + 1 );
RtlCopyMemory ( smbBuffer, shareString, shareStringLength );
byteCount = shareStringLength;
smbBuffer += shareStringLength;
if ( connection->SmbDialect <= SmbDialectDosLanMan21 ) {
//
// Append the file system name to the response.
// If the file system name is unavailable, supply the nul string
// as the name.
//
if ( isUnicode ) {
if ( ((ULONG_PTR)smbBuffer & 1) != 0 ) {
smbBuffer++;
byteCount++;
}
if ( share->Type.FileSystem.Name.Buffer != NULL ) {
RtlCopyMemory(
smbBuffer,
share->Type.FileSystem.Name.Buffer,
share->Type.FileSystem.Name.Length
);
byteCount += share->Type.FileSystem.Name.Length;
} else {
*(PWCH)smbBuffer = UNICODE_NULL;
byteCount += sizeof( UNICODE_NULL );
}
} else {
if ( share->Type.FileSystem.Name.Buffer != NULL ) {
RtlCopyMemory(
smbBuffer,
share->Type.FileSystem.OemName.Buffer,
share->Type.FileSystem.OemName.Length
);
byteCount += share->Type.FileSystem.OemName.Length;
} else {
*(PUCHAR)smbBuffer = '\0';
byteCount += 1;
}
}
if (RequestFlags & TREE_CONNECT_ANDX_EXTENDED_RESPONSE) {
PRESP_EXTENDED_TREE_CONNECT_ANDX ExtendedResponse;
ExtendedResponse = (PRESP_EXTENDED_TREE_CONNECT_ANDX)response;
SmbPutUshort( &ExtendedResponse->ByteCount, byteCount );
SrvUpdateMaximalShareAccessRightsInResponse(
WorkContext,
&ExtendedResponse->MaximalShareAccessRights,
&ExtendedResponse->GuestMaximalShareAccessRights);
SmbPutUshort(
&ExtendedResponse->AndXOffset,
GET_ANDX_OFFSET(
WorkContext->ResponseHeader,
WorkContext->ResponseParameters,
RESP_EXTENDED_TREE_CONNECT_ANDX,
byteCount
)
);
} else {
SmbPutUshort( &response21->ByteCount, byteCount );
SmbPutUshort(
&response->AndXOffset,
GET_ANDX_OFFSET(
WorkContext->ResponseHeader,
WorkContext->ResponseParameters,
RESP_21_TREE_CONNECT_ANDX,
byteCount
)
);
}
} else { // if Smb dialect == LAN Man 2.1
SmbPutUshort( &response->ByteCount, byteCount );
SmbPutUshort(
&response->AndXOffset,
GET_ANDX_OFFSET(
WorkContext->ResponseHeader,
WorkContext->ResponseParameters,
RESP_TREE_CONNECT_ANDX,
byteCount
)
);
}
WorkContext->ResponseParameters = (PUCHAR)WorkContext->ResponseHeader +
SmbGetUshort( &response->AndXOffset );
//
// Test for legal followon command.
//
switch ( nextCommand ) {
case SMB_COM_NO_ANDX_COMMAND:
break;
case SMB_COM_OPEN:
case SMB_COM_OPEN_ANDX:
case SMB_COM_CREATE:
case SMB_COM_CREATE_NEW:
case SMB_COM_CREATE_DIRECTORY:
case SMB_COM_DELETE:
case SMB_COM_DELETE_DIRECTORY:
case SMB_COM_SEARCH:
case SMB_COM_FIND:
case SMB_COM_FIND_UNIQUE:
case SMB_COM_COPY:
case SMB_COM_RENAME:
case SMB_COM_NT_RENAME:
case SMB_COM_CHECK_DIRECTORY:
case SMB_COM_QUERY_INFORMATION:
case SMB_COM_SET_INFORMATION:
case SMB_COM_QUERY_INFORMATION_SRV:
case SMB_COM_OPEN_PRINT_FILE:
case SMB_COM_GET_PRINT_QUEUE:
case SMB_COM_TRANSACTION:
//
// Make sure the AndX command is still within the received SMB
//
if( (PCHAR)WorkContext->RequestHeader + reqAndXOffset <=
END_OF_REQUEST_SMB( WorkContext ) ) {
break;
}
/* Falls Through */
default: // Illegal followon command
IF_DEBUG(SMB_ERRORS) {
KdPrint(( "SrvSmbTreeConnectAndX: Illegal followon command: 0x%c\n", nextCommand ));
}
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
status = STATUS_INVALID_SMB;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// If there is an AndX command, set up to process it. Otherwise,
// indicate completion to the caller.
//
if ( nextCommand != SMB_COM_NO_ANDX_COMMAND ) {
// *** Watch out for overwriting request with response.
WorkContext->NextCommand = nextCommand;
WorkContext->RequestParameters = (PUCHAR)WorkContext->RequestHeader +
reqAndXOffset;
SmbStatus = SmbStatusMoreCommands;
goto Cleanup;
}
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbTreeConnectAndX complete.\n" ));
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
cant_insert:
//
// We get here if for some reason we decide that we can't insert
// the tree connect. On entry, status contains the reason code.
// The connection lock and the share lock are held.
//
RELEASE_LOCK( &connection->Lock );
RELEASE_LOCK( &SrvShareLock );
SrvDereferenceShareForTreeConnect( share );
SrvFreeTreeConnect( treeConnect );
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbTreeConnectAndX
SMB_PROCESSOR_RETURN_TYPE
SrvSmbTreeDisconnect (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
Processes a tree disconnect SMB.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{
PREQ_TREE_DISCONNECT request;
PRESP_TREE_DISCONNECT response;
PTREE_CONNECT treeConnect;
NTSTATUS status = STATUS_SUCCESS;
SMB_STATUS SmbStatus = SmbStatusInProgress;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_TREE_DISCONNECT;
SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(TREE1) {
KdPrint(( "Tree disconnect request header at 0x%p, response header at 0x%p\n",
WorkContext->RequestHeader, WorkContext->ResponseHeader ));
KdPrint(( "Tree disconnect request parameters at 0x%p, response parameters at 0x%p\n",
WorkContext->RequestParameters,
WorkContext->ResponseParameters ));
}
//
// Set up parameters.
//
request = (PREQ_TREE_DISCONNECT)(WorkContext->RequestParameters);
response = (PRESP_TREE_DISCONNECT)(WorkContext->ResponseParameters);
//
// Find tree connect corresponding to given TID if a tree connect
// pointer has not already been put in the WorkContext block by an
// AndX command.
//
treeConnect = SrvVerifyTid(
WorkContext,
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid )
);
if ( treeConnect == NULL ) {
IF_DEBUG(SMB_ERRORS) {
KdPrint(( "SrvSmbTreeDisconnect: Invalid TID: 0x%lx\n",
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ) ));
}
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_TID );
status = STATUS_SMB_BAD_UID;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Do the actual tree disconnect.
//
SrvCloseTreeConnect( WorkContext->TreeConnect );
//
// Build the response SMB.
//
response->WordCount = 0;
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters = NEXT_LOCATION(
response,
RESP_TREE_DISCONNECT,
0
);
SmbStatus = SmbStatusSendResponse;
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbTreeDisconnect complete.\n" ));
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbTreeDisconnect