|
|
/*++
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
|