diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/nw/rdr/create4.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/nw/rdr/create4.c')
-rw-r--r-- | private/nw/rdr/create4.c | 2075 |
1 files changed, 2075 insertions, 0 deletions
diff --git a/private/nw/rdr/create4.c b/private/nw/rdr/create4.c new file mode 100644 index 000000000..c0e044be0 --- /dev/null +++ b/private/nw/rdr/create4.c @@ -0,0 +1,2075 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + create4.c + +Abstract: + + This implements the NDS create routines. + +Author: + + Cory West [CoryWest] 23-Feb-1995 + +--*/ + +#include "Procs.h" + +#define Dbg (DEBUG_TRACE_NDS) + +// +// Pageable. +// + +#pragma alloc_text( PAGE, NdsCreateTreeScb ) +#pragma alloc_text( PAGE, ConnectBinderyVolume ) +#pragma alloc_text( PAGE, HandleVolumeAttach ) +#pragma alloc_text( PAGE, NdsGetDsObjectFromPath ) +#pragma alloc_text( PAGE, NdsVerifyObject ) +#pragma alloc_text( PAGE, NdsVerifyContext ) +#pragma alloc_text( PAGE, NdsMapObjectToServerShare ) + +// +// Not page-able: +// +// NdsSelectConnection (holds a spin lock) +// + +NTSTATUS +NdsSelectConnection( + PIRP_CONTEXT pIrpContext, + PUNICODE_STRING puTreeName, + PUNICODE_STRING puUserName, + PUNICODE_STRING puPassword, + BOOL DeferredLogon, + BOOL UseBinderyConnections, + PNONPAGED_SCB *ppNpScb +) +/*++ + +Routine Description: + + Find a nearby tree connection point for the given tree. + + DeferredLogon tells us whether or not we need to + initiate a login/authenticate exchange yet. If we have + credentials to a tree, we are NOT allowed to hand off + a connection that has not been logged in because the view + of the tree may be different from what it is supposed to + be. + + UseBinderyConnections tells us whether or not we want + to return bindery authenticated connections as valid + nds browse points. + +Return Value: + + Scb to a server that belongs to the tree we want. + +--*/ +{ + + NTSTATUS Status = STATUS_BAD_NETWORK_PATH; + + PLOGON pLogon; + PLIST_ENTRY ScbQueueEntry; + KIRQL OldIrql; + + PNONPAGED_SCB pFirstNpScb, pNextNpScb; + PNONPAGED_SCB pFoundNpScb = NULL; + PSCB pScb; + LARGE_INTEGER Uid; + + PNONPAGED_SCB pOriginalNpScb; + PSCB pOriginalScb; + + PNDS_SECURITY_CONTEXT pNdsContext; + SECURITY_SUBJECT_CONTEXT SubjectContext; + BOOL PasswordExpired = FALSE; + + // + // Save the original server pointers. + // + + pOriginalNpScb = pIrpContext->pNpScb; + pOriginalScb = pIrpContext->pScb; + + Uid = pIrpContext->Specific.Create.UserUid; + + // + // Determine if we need a guest browse connection. + // + + if ( DeferredLogon ) { + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + pLogon = FindUser( &Uid, FALSE ); + NwReleaseRcb( &NwRcb ); + + if ( pLogon ) { + + Status = NdsLookupCredentials( puTreeName, + pLogon, + &pNdsContext, + CREDENTIAL_READ, + FALSE ); + + if ( NT_SUCCESS( Status ) ) { + + if ( ( pNdsContext->Credential != NULL ) && + ( pNdsContext->CredentialLocked == FALSE ) ) { + + DebugTrace( 0, Dbg, "Forcing authenticated browse to %wZ.\n", puTreeName ); + DeferredLogon = FALSE; + } + + NwReleaseCredList( pLogon ); + } + } + } + + // + // Start at the head of the SCB list. + // + + KeAcquireSpinLock(&ScbSpinLock, &OldIrql); + + if ( ScbQueue.Flink == &ScbQueue ) { + KeReleaseSpinLock( &ScbSpinLock, OldIrql); + return STATUS_BAD_NETWORK_PATH; + } + + ScbQueueEntry = ScbQueue.Flink; + pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry, + NONPAGED_SCB, + ScbLinks ); + pNextNpScb = pFirstNpScb; + + // + // Leave the first SCB referenced since we need it to + // be there for when we walk all the way around the list. + // + + NwReferenceScb( pFirstNpScb ); + NwReferenceScb( pNextNpScb ); + + KeReleaseSpinLock( &ScbSpinLock, OldIrql); + + while ( TRUE ) { + + // + // Check to see if the SCB we have is in the correct tree + // and is usable. Make sure we skip over the permanent + // npscb since it isn't a tree connection. The current + // SCB is always referenced while we're in here. + // + + if ( pNextNpScb->pScb ) { + + pScb = pNextNpScb->pScb; + + if ( RtlEqualUnicodeString( puTreeName, &pScb->NdsTreeName, TRUE ) && + Uid.QuadPart == pScb->UserUid.QuadPart ) { + + pIrpContext->pNpScb = pNextNpScb; + pIrpContext->pScb = pNextNpScb->pScb; + NwAppendToQueueAndWait( pIrpContext ); + + switch ( pNextNpScb->State ) { + + case SCB_STATE_RECONNECT_REQUIRED: + + // + // Reconnect to the server. This is not + // a valid path for an anonymous create, + // so there's no chance that we'll get + // a name collision. + // + + Status = ConnectToServer( pIrpContext, NULL ); + + if (!NT_SUCCESS(Status)) { + break; + } + + pNextNpScb->State = SCB_STATE_LOGIN_REQUIRED; + + case SCB_STATE_LOGIN_REQUIRED: + + // + // See if we can login if requested. + // + + if ( !DeferredLogon ) { + + Status = DoNdsLogon( pIrpContext, puUserName, puPassword ); + + if ( !NT_SUCCESS( Status ) ) { + break; + } + + // + // If we get a warning from this, we need to return it! + // + + if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) { + PasswordExpired = TRUE; + } + + // + // Do we have to re-license the connection? + // + + if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) { + + Status = NdsLicenseConnection( pIrpContext ); + + if ( !NT_SUCCESS( Status ) ) { + Status = STATUS_REMOTE_SESSION_LIMIT; + break; + } + } + + pNextNpScb->State = SCB_STATE_IN_USE; + } + + case SCB_STATE_IN_USE: + + if ( pNextNpScb->State == SCB_STATE_IN_USE ) { + + if ( ( !UseBinderyConnections ) && + ( pNextNpScb->pScb->UserName.Length != 0 ) ) { + + // + // We may not want to use a connection that has been + // bindery authenticated to read the NDS tree because + // we don't have a way to validate that the NDS and + // bindery users are the same. + // + + Status = STATUS_ACCESS_DENIED; + break; + } + + // + // Verify that we have security rights to this server. + // + + Status = CheckScbSecurity( pIrpContext, + pNextNpScb->pScb, + puUserName, + puPassword, + ( BOOLEAN )DeferredLogon ); + + if ( !NT_SUCCESS( Status ) ) { + break; + } + + // + // Check SCB security might return with state login required. + // + + if ( ( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) && + ( !DeferredLogon ) ) { + + Status = DoNdsLogon( pIrpContext, puUserName, puPassword ); + + if ( !NT_SUCCESS( Status ) ) { + break; + } + + pNextNpScb->State = SCB_STATE_IN_USE; + } + + } else { + + // + // If we picked up an already good SCB and the + // login was deferred, set success and continue. + // + + ASSERT( DeferredLogon == TRUE ); + Status = STATUS_SUCCESS; + } + + pFoundNpScb = pNextNpScb; + DebugTrace( 0, Dbg, "NdsSelectConnection: NpScb = %lx\n", pFoundNpScb ); + break; + + default: + + break; + + } + + NwDequeueIrpContext( pIrpContext, FALSE ); + + if ( pFoundNpScb ) { + ASSERT( NT_SUCCESS( Status ) ); + break; + } + + if ( Status == STATUS_WRONG_PASSWORD || + Status == STATUS_NO_SUCH_USER ) { + NwDereferenceScb( pNextNpScb ); + break; + } + + // + // Restore the server pointers. + // + + pIrpContext->pNpScb = pOriginalNpScb; + pIrpContext->pScb = pOriginalScb; + + } + } + + // + // Otherwise, get the next one in the list. Don't + // forget to skip the list head. + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + + ScbQueueEntry = pNextNpScb->ScbLinks.Flink; + + if ( ScbQueueEntry == &ScbQueue ) { + ScbQueueEntry = ScbQueue.Flink; + } + + NwDereferenceScb( pNextNpScb ); + pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks ); + + if ( pNextNpScb == pFirstNpScb ) { + + KeReleaseSpinLock( &ScbSpinLock, OldIrql ); + Status = STATUS_BAD_NETWORK_PATH; + break; + } + + // + // Otherwise, reference this SCB and continue. + // + + NwReferenceScb( pNextNpScb ); + KeReleaseSpinLock( &ScbSpinLock, OldIrql ); + + } + + NwDereferenceScb( pFirstNpScb ); + *ppNpScb = pFoundNpScb; + + if ( ( NT_SUCCESS( Status ) ) && + ( PasswordExpired ) ) { + Status = NWRDR_PASSWORD_HAS_EXPIRED; + } + + return Status; +} + +NTSTATUS +NdsCreateTreeScb( + IN PIRP_CONTEXT pIrpContext, + IN OUT PSCB *ppScb, + IN PUNICODE_STRING puTree, + IN PUNICODE_STRING puUserName, + IN PUNICODE_STRING puPassword, + IN BOOLEAN DeferredLogon, + IN BOOLEAN DeleteOnClose +) +/*++ + +Description: + + Given a tree name, find us a connection point to the tree. This is + done by getting the server addresses out of the bindery and looking + up the names of the servers for those addresses. + + When we are all done we need to return the preferred connection + point in ppScb. + +Arguments: + + pIrpContext - irp context for this request + ppScb - pointer to a pointer to the scb that we want + puTree - tree we want to talk to + +--*/ +{ + + NTSTATUS Status; + + PLARGE_INTEGER pUid; + PNONPAGED_SCB pNpExistingScb; + + UNICODE_STRING UidServerName; + PSCB pTreeScb = NULL; + + PSCB pNearestTreeScb = NULL; + PNONPAGED_SCB pNpNearestTreeScb = NULL; + + PSCB pNearbyScb = NULL; + BOOLEAN fOnNearbyQueue = FALSE; + PIRP_CONTEXT pExtraIrpContext = NULL; + + UNICODE_STRING ScanTreeName; + WCHAR ScanBuffer[NDS_TREE_NAME_LEN + 2]; + int i; + + IPXaddress DirServerAddress; + CHAR DirServerName[MAX_SERVER_NAME_LENGTH]; + ULONG dwLastOid = (ULONG)-1; + + UNICODE_STRING CredentialName; + PUNICODE_STRING puConnectName; + + PAGED_CODE(); + + UidServerName.Buffer = NULL; + + // + // Make sure the tree name is reasonable, first. + // + + if ( ( !puTree ) || + ( !puTree->Length ) || + ( puTree->Length / sizeof( WCHAR ) ) > NDS_TREE_NAME_LEN ) { + + return STATUS_INVALID_PARAMETER; + } + + // + // If this is an extended credential create, munge the name. + // + + RtlInitUnicodeString( &CredentialName, NULL ); + + if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) && + ( !IsCredentialName( puTree ) ) ) { + + Status = BuildExCredentialServerName( puTree, + puUserName, + &CredentialName ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + puConnectName = &CredentialName; + + } else { + + puConnectName = puTree; + } + + // + // First check to see if we already have a connection + // to this tree that we can use... If so, this will + // leave the irp context pointed at that server for us. + // + // This time around, don't use bindery authenticated + // connections to browse the tree. + // + + Status = NdsSelectConnection( pIrpContext, + puConnectName, + puUserName, + puPassword, + DeferredLogon, + FALSE, + &pNpExistingScb ); + + if ( NT_SUCCESS( Status ) && pNpExistingScb ) { + *ppScb = pNpExistingScb->pScb; + ASSERT( *ppScb != NULL ); + ASSERT( NT_SUCCESS( Status ) ); + goto ExitWithCleanup; + } + + // + // If there was an authentication failure, bail out. + // + + if ( Status == STATUS_NO_SUCH_USER || + Status == STATUS_WRONG_PASSWORD ) { + goto ExitWithCleanup; + } + + // + // Otherwise, we need to select a dir server. To do this, + // we have to look up dir server names by address. To do + // this we create an SCB for synchronization with the name + // *tree*, which isn't a valid server name. + // + + ScanTreeName.Length = sizeof( WCHAR ); + ScanTreeName.MaximumLength = sizeof( ScanBuffer ); + ScanTreeName.Buffer = ScanBuffer; + + ScanBuffer[0] = L'*'; + RtlAppendUnicodeStringToString( &ScanTreeName, puTree ); + ScanBuffer[( ScanTreeName.Length / sizeof( WCHAR ) )] = L'*'; + ScanTreeName.Length += sizeof( WCHAR ); + + // + // Now make it a uid server name. + // + + Status = MakeUidServer( &UidServerName, + &pIrpContext->Specific.Create.UserUid, + &ScanTreeName ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + NwFindScb( &pTreeScb, + pIrpContext, + &UidServerName, + &ScanTreeName ); + + if ( !pTreeScb ) { + DebugTrace( 0, Dbg, "Failed to get a tree scb for synchronization.\n", 0 ); + goto ExitWithCleanup; + } + + // + // Get a nearby server connection and prepare to + // do the bindery scan for tree connection points. + // Don't forget to copy the user uid for security. + // + + if ( !NwAllocateExtraIrpContext( &pExtraIrpContext, + pTreeScb->pNpScb ) ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitWithCleanup; + + } + + pExtraIrpContext->Specific.Create.UserUid.QuadPart = + pIrpContext->Specific.Create.UserUid.QuadPart; + + // + // Append a wildcard to the tree name for the bindery scan. + // + + ScanTreeName.Length = 0; + ScanTreeName.MaximumLength = sizeof( ScanBuffer ); + ScanTreeName.Buffer = ScanBuffer; + + RtlCopyUnicodeString( &ScanTreeName, puTree ); + + i = ScanTreeName.Length / sizeof( WCHAR ); + + while( i <= NDS_TREE_NAME_LEN ) { + ScanBuffer[i++] = L'_'; + } + + ScanBuffer[NDS_TREE_NAME_LEN] = L'*'; + ScanTreeName.Length = (NDS_TREE_NAME_LEN + 1) * sizeof( WCHAR ); + + DebugTrace( 0, Dbg, "Scanning for NDS tree %wZ.\n", puTree ); + + // + // Now we lookup the dir server addresses in the bindery and + // try to make dir server connections. + // + + while ( TRUE ) { + + if ( ( pNearbyScb ) && ( !fOnNearbyQueue ) ) { + + // + // Get back to the head of the nearby server so we can continue + // looking for dir servers. If the nearby server is no good anymore, + // dereference the connection and set the nearby scb pointer to + // NULL. This will cause us to get a new nearby server when we + // continue. + // + + NwAppendToQueueAndWait( pExtraIrpContext ); + + if ( !( ( pNearbyScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) || + ( pNearbyScb->pNpScb->State == SCB_STATE_IN_USE ) ) ) { + + NwDequeueIrpContext( pExtraIrpContext, FALSE ); + NwDereferenceScb( pNearbyScb->pNpScb ); + pNearbyScb = NULL; + + // + // Don't restart the search. If our bindery server went down in + // the middle of a connect, the connect will fail and that's ok. + // If we restart the search we can end up in this loop forever. + // + + } else { + + fOnNearbyQueue = TRUE; + } + + } + + // + // Get a bindery server to talk to if we don't have one. This may + // be our first time through this loop, or our server may have + // gone bad (see above). + // + // Optimization: What if this CreateScb returns a valid dir server + // for the tree we are looking for? We should use it!! + // + + if ( !pNearbyScb ) { + + Status = CreateScb( &pNearbyScb, + pExtraIrpContext, + NULL, + NULL, + NULL, + NULL, + TRUE, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + ASSERT( pExtraIrpContext->pNpScb == pNearbyScb->pNpScb ); + ASSERT( pExtraIrpContext->pScb == pNearbyScb ); + + NwAppendToQueueAndWait( pExtraIrpContext ); + fOnNearbyQueue = TRUE; + + } + + // + // Look up the dir server address from our nearby server. + // + + Status = ExchangeWithWait( pExtraIrpContext, + SynchronousResponseCallback, + "SdwU", + NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT, + dwLastOid, + OT_DIRSERVER, + &ScanTreeName ); + + if ( !NT_SUCCESS( Status ) ) { + + // + // We're out of options for dir servers. + // + + Status = STATUS_BAD_NETWORK_PATH; + break; + } + + Status = ParseResponse( pExtraIrpContext, + pExtraIrpContext->rsp, + pExtraIrpContext->ResponseLength, + "Nd_r", + &dwLastOid, + sizeof( WORD ), + DirServerName, + MAX_SERVER_NAME_LENGTH ); + + if ( !NT_SUCCESS( Status ) ) { + break; + } + + Status = ExchangeWithWait ( pExtraIrpContext, + SynchronousResponseCallback, + "Swbrbp", + NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE, + OT_DIRSERVER, + 0x30, + DirServerName, + MAX_SERVER_NAME_LENGTH, + 1, // Segment number + NET_ADDRESS_PROPERTY ); + + if ( !NT_SUCCESS( Status ) ) { + + DebugTrace( 0, Dbg, "No net address property for this dir server.\n", 0 ); + continue; + } + + Status = ParseResponse( pExtraIrpContext, + pExtraIrpContext->rsp, + pExtraIrpContext->ResponseLength, + "Nr", + &DirServerAddress, + sizeof(TDI_ADDRESS_IPX) ); + + if ( !NT_SUCCESS( Status ) ) { + + DebugTrace( 0, Dbg, "Couldn't parse net address property for this dir server.\n", 0 ); + continue; + } + + // + // We know the address of the dir server, so do an anonymous + // create to it. Use the original irp context so the uid is + // correct. Note that we have to dequeue from the nearby scb + // in case we are referred to that server! + // + + NwDequeueIrpContext( pExtraIrpContext, FALSE ); + fOnNearbyQueue = FALSE; + + NwDequeueIrpContext( pIrpContext, FALSE ); + + Status = CreateScb( &pNearestTreeScb, + pIrpContext, + NULL, + &DirServerAddress, + puUserName, + puPassword, + DeferredLogon, + DeleteOnClose ); + + if ( !NT_SUCCESS( Status ) ) { + + if ( Status == STATUS_NO_SUCH_USER || + Status == STATUS_WRONG_PASSWORD || + Status == STATUS_ACCESS_DENIED || + Status == STATUS_ACCOUNT_DISABLED || + Status == STATUS_LOGIN_TIME_RESTRICTION || + Status == STATUS_REMOTE_SESSION_LIMIT || + Status == STATUS_CONNECTION_COUNT_LIMIT || + Status == STATUS_NETWORK_CREDENTIAL_CONFLICT || + Status == STATUS_PASSWORD_EXPIRED ) { + break; + } + + continue; + } + + // + // If the server we got back was bindery authenticated, + // it is NOT a valid dir server for us to use (yet)!! + // + + if ( pNearestTreeScb->UserName.Length != 0 ) { + + Status = STATUS_ACCESS_DENIED; + NwDequeueIrpContext( pIrpContext, FALSE ); + NwDereferenceScb( pNearestTreeScb->pNpScb ); + + continue; + } + + // + // Otherwise, we're golden. Break out of here! + // + + DebugTrace( 0, Dbg, "Dir server: %wZ\n", &pNearestTreeScb->UidServerName ); + *ppScb = pNearestTreeScb; + ASSERT( NT_SUCCESS( Status ) ); + break; + + } + + // + // We have been wholly unable to get a browse connection + // to this tree. Try again but this time allow the use + // of connections that are bindery authenticated. We don't + // need the nearby server anymore. + // + + if ( pNearbyScb ) { + + NwDequeueIrpContext( pExtraIrpContext, FALSE ); + NwDereferenceScb( pNearbyScb->pNpScb ); + } + + if ( ( Status != STATUS_SUCCESS ) && + ( Status != STATUS_NO_SUCH_USER ) && + ( Status != STATUS_WRONG_PASSWORD ) && + ( Status != STATUS_ACCESS_DENIED ) && + ( Status != STATUS_ACCOUNT_DISABLED ) && + ( Status != STATUS_LOGIN_TIME_RESTRICTION ) && + ( Status != STATUS_REMOTE_SESSION_LIMIT ) && + ( Status != STATUS_CONNECTION_COUNT_LIMIT ) && + ( Status != STATUS_NETWORK_CREDENTIAL_CONFLICT ) && + ( Status != STATUS_PASSWORD_EXPIRED ) ) { + + Status = NdsSelectConnection( pIrpContext, + puConnectName, + puUserName, + puPassword, + DeferredLogon, + TRUE, + &pNpExistingScb ); + + if ( NT_SUCCESS( Status ) && pNpExistingScb ) { + *ppScb = pNpExistingScb->pScb; + ASSERT( *ppScb != NULL ); + } + } + +ExitWithCleanup: + + // + // Clean up and bail. + // + + if ( pExtraIrpContext ) { + NwFreeExtraIrpContext( pExtraIrpContext ); + } + + if ( UidServerName.Buffer != NULL ) { + FREE_POOL( UidServerName.Buffer ); + } + + if ( pTreeScb ) { + NwDereferenceScb( pTreeScb->pNpScb ); + } + + if ( CredentialName.Buffer ) { + FREE_POOL( CredentialName.Buffer ); + } + + return Status; + +} + +NTSTATUS +ConnectBinderyVolume( + PIRP_CONTEXT pIrpContext, + PUNICODE_STRING puServerName, + PUNICODE_STRING puVolumeName +) +/*++ + +Description: + + Given a server name and a volume, try to connect the volume. + This is used in QueryPath to pre-connect a volume. + +--*/ +{ + + NTSTATUS Status; + PSCB pScb; + PVCB pVcb; + + PAGED_CODE(); + + // + // Try making a server connection with this name. + // + + Status = CreateScb( &pScb, + pIrpContext, + puServerName, + NULL, + NULL, + NULL, + FALSE, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + return Status; + } + + DebugTrace( 0, Dbg, "Bindery volume connect got server %wZ\n", puServerName ); + + // + // If we succeeded, do a standard bindery volume attach. + // + + pVcb = NwFindVcb( pIrpContext, + puVolumeName, + RESOURCETYPE_ANY, + 0, + FALSE, + FALSE ); + + if ( pVcb == NULL ) { + + Status = STATUS_BAD_NETWORK_PATH; + + } else { + + // + // We should not have jumped servers since this was explicit. + // + + ASSERT( pScb == pIrpContext->pScb ); + + // + // Remove NwFindVcb reference. Don't supply an IrpContext + // so the Vcb doesn't get destroyed immediately after we just + // created it because no-one else has it referenced. + // + + NwDereferenceVcb( pVcb, NULL, FALSE ); + DebugTrace( 0, Dbg, "Bindery volume connect got volume %wZ\n", puVolumeName ); + } + + NwDereferenceScb( pScb->pNpScb ); + return Status; + +} + +NTSTATUS +HandleVolumeAttach( + PIRP_CONTEXT pIrpContext, + PUNICODE_STRING puServerName, + PUNICODE_STRING puVolumeName +) +/*++ + +Description: + + This function is only callable from the QUERY_PATH code path! + + This functions takes a server name and volume name from + QueryPath() and resolves it into a server/volume connection. + The server/volume name can be plain or can refer to an + nds tree and the nds path to a volume object. + + In the nds case, we only verify that the volume object exists. + +Arguments: + + pIrpContext - irp context for this request + puServerName - server name or nds tree name + puVolumeName - volume name or nds path to volume object + +--*/ +{ + + NTSTATUS Status; + PSCB pScb; + + UNICODE_STRING uDsObject; + DWORD dwVolumeOid, dwObjectType; + + PAGED_CODE(); + + // + // Try the bindery server/volume case first. + // + + Status = ConnectBinderyVolume( pIrpContext, + puServerName, + puVolumeName ); + if ( NT_SUCCESS( Status ) ) { + return Status; + } + + if ( Status == STATUS_NETWORK_UNREACHABLE ) { + + // IPX is not bound to anything that is currently + // up (which means it's probably bound only to the + // RAS WAN wrapper). Don't waste time looking for + // a ds tree. + // + + return STATUS_BAD_NETWORK_PATH; + } + + // + // See if this is a tree name and get a ds connection. + // + + pIrpContext->Specific.Create.NdsCreate = TRUE; + + Status = NdsCreateTreeScb( pIrpContext, + &pScb, + puServerName, + NULL, + NULL, + TRUE, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + return Status; + } + + // + // If we have a tree, resolve the volume object. + // BUGBUG: We should actually check to see if we + // already have a connection to this object before + // we hit the ds. + // + + Status = NdsGetDsObjectFromPath( pIrpContext, + &uDsObject ); + + if ( !NT_SUCCESS( Status ) ) { + NwDereferenceScb( pIrpContext->pNpScb ); + return Status; + } + + Status = NdsVerifyObject( pIrpContext, // irp context for the request + &uDsObject, // path to volume object + TRUE, // allow a server jump + DEFAULT_RESOLVE_FLAGS, // resolver flags + &dwVolumeOid, // volume oid from the ds + &dwObjectType ); // volume or print queue + + // + // We may have jumped servers in the VerifyObject code, + // so just make sure we dereference the correct server. + // + + NwDereferenceScb( pIrpContext->pNpScb ); + return Status; + +} + +NTSTATUS +NdsGetDsObjectFromPath( + IN PIRP_CONTEXT pIrpContext, + OUT PUNICODE_STRING puDsObject +) +/*++ + +Description: + + Take the full path from the create irp context and + extract out the ds path of the desired object. + + The supplied unicode string shouldn't have a buffer; + it will be set up to point into the user's buffer + referred to by the irp context. + +Arguments: + + pIrpContext - an irp context from a create path request + puDsObject - unicode string that will refer to the correct ds path + +--*/ +{ + + DWORD dwPathSeparators; + USHORT NewHead; + + PAGED_CODE(); + + // + // The VolumeName is one of the following: + // + // \X:\Server\Volume.Object.Path + // \Server\Volume.Object.Path + // + + *puDsObject = pIrpContext->Specific.Create.VolumeName; + + // + // Skip the leading slash. + // + + puDsObject->Length -= sizeof( WCHAR ); + puDsObject->Buffer += 1; + + // + // How many more are there to overcome? + // + + NewHead = 0; + dwPathSeparators = pIrpContext->Specific.Create.DriveLetter ? 2 : 1; + + while ( NewHead < puDsObject->Length && + dwPathSeparators ) { + + if ( puDsObject->Buffer[NewHead/sizeof(WCHAR)] == OBJ_NAME_PATH_SEPARATOR ) { + dwPathSeparators--; + } + + NewHead += sizeof( WCHAR ); + } + + if ( dwPathSeparators || + NewHead == puDsObject->Length) { + + // + // Something wasn't formed right in the volume name. + // + + return STATUS_BAD_NETWORK_PATH; + } + + puDsObject->Length -= NewHead; + puDsObject->Buffer += NewHead/sizeof(WCHAR); + + // + // If there is a leading dot, skip it. + // + + if ( puDsObject->Buffer[0] == L'.' ) { + + puDsObject->Length -= sizeof( WCHAR ); + puDsObject->Buffer += 1; + } + + puDsObject->MaximumLength = puDsObject->Length; + + DebugTrace( 0, Dbg, "DS object: %wZ\n", puDsObject ); + + return STATUS_SUCCESS; +} + +NTSTATUS +NdsVerifyObject( + IN PIRP_CONTEXT pIrpContext, + IN PUNICODE_STRING puDsObject, + IN BOOLEAN fAllowServerJump, + IN DWORD dwResolverFlags, + OUT PDWORD pdwDsOid, + OUT PDWORD pdwObjectType +) +/*++ + +Description: + + This function verifies that a ds path refers to a volume + object, print queue, or a dir map. It returns the oid + of the object. + + If fAllowServerJump is set to false, this simply looks up + the oid on the current server but doesn't verify the object + type. This routine checks all appropriate contexts for the + object, unlike ResolveNameKm. + +Parameters: + + pIrpContext - irp context for this request, pointed to the ds server + puDsObject - path to the object in the ds + fAllowServerJump - allow a server jump to take place + pdwDsOid - destination of the ds oid of the object + pdwObjectType - NDS_OBJECTTYPE_VOLUME, NDS_OBJECTTYPE_QUEUE, or NDS_OBJECTTYPE_DIRMAP + +--*/ +{ + + NTSTATUS Status; + + PNDS_SECURITY_CONTEXT pCredentials = NULL; + PUNICODE_STRING puAppendableContext; + + UNICODE_STRING uFdnObject; + WCHAR FdnObject[MAX_NDS_NAME_CHARS]; + + PLOGON pLogon; + PSCB pScb; + USHORT i; + + LOCKED_BUFFER NdsRequest; + DWORD dwObjectOid, dwObjectType; + + UNICODE_STRING uVolume; + UNICODE_STRING uQueue; + UNICODE_STRING uDirMap; + + UNICODE_STRING uReplyString; + WCHAR ReplyBuffer[32]; + BOOLEAN fHoldingCredentialList = FALSE; + BOOLEAN fPartiallyDistinguished = FALSE; + + PAGED_CODE(); + + NdsRequest.pRecvBufferVa = NULL; + + // + // Get the user credentials. + // + + pScb = pIrpContext->pNpScb->pScb; + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + pLogon = FindUser( &pScb->UserUid, FALSE ); + NwReleaseRcb( &NwRcb ); + + // + // Get the credential. We don't care if it's locked or + // not since we're just querying the ds. + // + + if ( pLogon ) { + + Status = NdsLookupCredentials( &pScb->NdsTreeName, + pLogon, + &pCredentials, + CREDENTIAL_READ, + FALSE ); + + if ( NT_SUCCESS( Status ) ) { + ASSERT( pCredentials != NULL ); + fHoldingCredentialList = TRUE; + } + + } + + // + // Check to see if it's at least partially distinguished already. + // + + i = 0; + while (i < puDsObject->Length / sizeof( WCHAR ) ) { + + if ( puDsObject->Buffer[i++] == L'.' ) { + fPartiallyDistinguished = TRUE; + } + } + + // + // If it's partially distinguished, try it without the context first. + // + + if ( fPartiallyDistinguished ) { + + Status = NdsResolveNameKm ( pIrpContext, + puDsObject, + &dwObjectOid, + fAllowServerJump, + dwResolverFlags ); + + if ( NT_SUCCESS( Status ) ) { + + DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", puDsObject ); + goto GetObjectType; + } + } + + // + // If that failed, or if it wasn't partially distinguished, + // see if there's a current context we can append. + // + + if ( ( pCredentials ) && + ( pCredentials->CurrentContext.Length ) ) { + + if ( ( puDsObject->Length + pCredentials->CurrentContext.Length ) < sizeof( FdnObject ) ) { + + // + // Append the context. + // + + uFdnObject.MaximumLength = sizeof( FdnObject ); + uFdnObject.Buffer = FdnObject; + + RtlCopyMemory( FdnObject, puDsObject->Buffer, puDsObject->Length ); + uFdnObject.Length = puDsObject->Length; + + if ( uFdnObject.Buffer[( uFdnObject.Length / sizeof( WCHAR ) ) - 1] == L'.' ) { + uFdnObject.Length -= sizeof( WCHAR ); + } + + if ( pCredentials->CurrentContext.Buffer[0] != L'.' ) { + uFdnObject.Buffer[uFdnObject.Length / sizeof( WCHAR )] = L'.'; + uFdnObject.Length += sizeof( WCHAR ); + } + + RtlCopyMemory( ((BYTE *)FdnObject) + uFdnObject.Length, + pCredentials->CurrentContext.Buffer, + pCredentials->CurrentContext.Length ); + + uFdnObject.Length += pCredentials->CurrentContext.Length; + + // + // Resolve this name. + // + + Status = NdsResolveNameKm ( pIrpContext, + &uFdnObject, + &dwObjectOid, + fAllowServerJump, + dwResolverFlags ); + + if ( NT_SUCCESS( Status ) ) { + + DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", &uFdnObject ); + goto GetObjectType; + } + + } + + } + + // + // This is not a valid name. + // + + DebugTrace( 0, Dbg, "VerifyObject: No ds object to resolve.\n", 0 ); + + if ( fHoldingCredentialList ) { + NwReleaseCredList( pLogon ); + fHoldingCredentialList = FALSE; + } + + return STATUS_BAD_NETWORK_PATH; + + +GetObjectType: + + if ( fHoldingCredentialList ) { + NwReleaseCredList( pLogon ); + fHoldingCredentialList = FALSE; + } + + // + // If a server jump is not allowed, we don't need to worry + // about getting the object type. + // + + if ( !fAllowServerJump ) { + dwObjectType = 0; + goto CompletedObject; + } + + // + // Resolve the object and get its information. + // + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = FragExWithWait( pIrpContext, + NDSV_READ_ENTRY_INFO, + &NdsRequest, + "DD", + 0, + dwObjectOid ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + Status = NdsCompletionCodetoNtStatus( &NdsRequest ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Verify that it's a volume object. + // + + RtlInitUnicodeString( &uVolume, VOLUME_ATTRIBUTE ); + RtlInitUnicodeString( &uQueue, QUEUE_ATTRIBUTE ); + RtlInitUnicodeString( &uDirMap, DIR_MAP_ATTRIBUTE ); + + uReplyString.Length = 0; + uReplyString.MaximumLength = sizeof( ReplyBuffer ); + uReplyString.Buffer = ReplyBuffer; + + Status = ParseResponse( NULL, + NdsRequest.pRecvBufferVa, + NdsRequest.dwBytesWritten, + "G_T", + sizeof( NDS_RESPONSE_GET_OBJECT_INFO ), + &uReplyString ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + dwObjectType = 0; + + if ( !RtlCompareUnicodeString( &uVolume, &uReplyString, FALSE ) ) { + dwObjectType = NDS_OBJECTTYPE_VOLUME; + } else if ( !RtlCompareUnicodeString( &uQueue, &uReplyString, FALSE ) ) { + dwObjectType = NDS_OBJECTTYPE_QUEUE; + } else if ( !RtlCompareUnicodeString( &uDirMap, &uReplyString, FALSE ) ) { + dwObjectType = NDS_OBJECTTYPE_DIRMAP; + } + + if ( !dwObjectType ) { + + DebugTrace( 0, Dbg, "DS object is not a connectable type.\n", 0 ); + Status = STATUS_OBJECT_PATH_SYNTAX_BAD; + goto ExitWithCleanup; + } + +CompletedObject: + + if ( pdwDsOid ) { + *pdwDsOid = dwObjectOid; + } + + if ( pdwObjectType ) { + *pdwObjectType = dwObjectType; + } + + Status = STATUS_SUCCESS; + +ExitWithCleanup: + + if ( fHoldingCredentialList ) { + NwReleaseCredList( pLogon ); + } + + if ( NdsRequest.pRecvBufferVa ) { + NdsFreeLockedBuffer( &NdsRequest ); + } + + return Status; +} + +NTSTATUS +NdsVerifyContext( + PIRP_CONTEXT pIrpContext, + PUNICODE_STRING puTree, + PUNICODE_STRING puContext +) +/*++ + + Given a context and a tree, verify that the context is a + valid container in the tree. + + This call may cause the irpcontex to jump servers to an + referred dir server. If so, the scb pointers in the irp + context will be updated, the old server will be dereferenced, + and the new server will hold the reference for this request. + +--*/ +{ + + NTSTATUS Status; + DWORD dwOid, dwSubordinates; + LOCKED_BUFFER NdsRequest; + PSCB pScb, pTreeScb; + PNONPAGED_SCB pNpScb; + + PAGED_CODE(); + + // + // Establish a browse connection to the tree we want to query. + // + + NdsRequest.pRecvBufferVa = NULL; + + pScb = pIrpContext->pScb; + pNpScb = pIrpContext->pNpScb; + + Status = NdsCreateTreeScb( pIrpContext, + &pTreeScb, + puTree, + NULL, + NULL, + TRUE, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + pTreeScb = NULL; + goto ExitWithCleanup; + } + + Status = NdsResolveNameKm ( pIrpContext, + puContext, + &dwOid, + TRUE, + DEFAULT_RESOLVE_FLAGS ); + + if ( !NT_SUCCESS( Status ) ) { + DebugTrace( 0, Dbg, "NdsVerifyContext: resolve failed.\n", 0 ); + goto ExitWithCleanup; + } + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + Status = STATUS_INSUFFICIENT_RESOURCES; + NdsRequest.pRecvBufferVa = NULL; + goto ExitWithCleanup; + } + + Status = FragExWithWait( pIrpContext, + NDSV_READ_ENTRY_INFO, + &NdsRequest, + "DD", + 0, + dwOid ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + Status = NdsCompletionCodetoNtStatus( &NdsRequest ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Verify that it's a volume object by checking the + // third DWORD, which is the subordinate count. + // + + Status = ParseResponse( NULL, + NdsRequest.pRecvBufferVa, + NdsRequest.dwBytesWritten, + "G_D", + 2 * sizeof( DWORD ), + &dwSubordinates ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + if ( !dwSubordinates ) { + + DebugTrace( 0, Dbg, "No subordinates in VerifyContext.\n", 0 ); + Status = STATUS_INVALID_PARAMETER; + goto ExitWithCleanup; + } + + // + // Success! + // + +ExitWithCleanup: + + // + // We may have jumped servers in the resolve name call, + // so make sure we dereference the correct SCB! + // + + if ( pTreeScb ) { + NwDereferenceScb( pIrpContext->pNpScb ); + } + + // + // Restore the connection to the original server. + // + + NwDequeueIrpContext( pIrpContext, FALSE ); + pIrpContext->pScb = pScb; + pIrpContext->pNpScb = pNpScb; + + if ( NdsRequest.pRecvBufferVa ) { + NdsFreeLockedBuffer( &NdsRequest ); + } + + return Status; +} + + +NTSTATUS +NdsMapObjectToServerShare( + PIRP_CONTEXT pIrpContext, + PSCB *ppScb, + PUNICODE_STRING puServerSharePath, + BOOLEAN CreateTreeConnection, + PDWORD pdwObjectId +) +/*++ + +Description: + + This function takes a pointer to a tree scb and an irp + context for a create request. It looks up the ds object + from the create request in the ds and maps it to + the appropriate server/share duple. + + The FullPathName and VolumeName strings in the create + section of the irp context are updated and a connection + to the real host server is established so that the + create request can continue as desired. + +--*/ +{ + + NTSTATUS Status; + LOCKED_BUFFER NdsRequest; + + UNICODE_STRING uServerAttribute; + UNICODE_STRING uVolumeAttribute; + UNICODE_STRING uQueueAttribute; + UNICODE_STRING uPathAttribute; + + UNICODE_STRING uHostServer; + UNICODE_STRING uRealServerName; + UNICODE_STRING uHostVolume; + UNICODE_STRING uHostPath; + UNICODE_STRING uIntermediateVolume; + + UNICODE_STRING uDsObjectPath; + DWORD dwObjectOid, dwObjectType, dwDirMapType; + + DWORD dwTotalPathLen; + USHORT usSrv; + PSCB pOldScb, pNewServerScb; + + UNICODE_STRING UserName, Password; + ULONG ShareType; + + PAGED_CODE(); + + // + // Set up strings and buffers. + // + + RtlInitUnicodeString( &uServerAttribute, HOST_SERVER_ATTRIBUTE ); + RtlInitUnicodeString( &uVolumeAttribute, HOST_VOLUME_ATTRIBUTE ); + RtlInitUnicodeString( &uQueueAttribute, HOST_QUEUE_ATTRIBUTE ); + RtlInitUnicodeString( &uPathAttribute, HOST_PATH_ATTRIBUTE ); + + RtlInitUnicodeString( &uHostServer, NULL ); + RtlInitUnicodeString( &uRealServerName, NULL ); + RtlInitUnicodeString( &uHostVolume, NULL ); + RtlInitUnicodeString( &uHostPath, NULL ); + RtlInitUnicodeString( &uIntermediateVolume, NULL ); + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + return Status; + } + + uHostServer.Buffer = ALLOCATE_POOL( PagedPool, 4 * MAX_NDS_NAME_SIZE ); + + if ( !uHostServer.Buffer ) { + + NdsFreeLockedBuffer( &NdsRequest ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + uHostServer.MaximumLength = MAX_NDS_NAME_SIZE; + + uHostVolume.Buffer = ( PWCHAR )(((BYTE *)uHostServer.Buffer) + MAX_NDS_NAME_SIZE); + uHostVolume.MaximumLength = MAX_NDS_NAME_SIZE; + + uHostPath.Buffer = ( PWCHAR )(((BYTE *)uHostVolume.Buffer) + MAX_NDS_NAME_SIZE); + uHostPath.MaximumLength = MAX_NDS_NAME_SIZE; + + uIntermediateVolume.Buffer = ( PWCHAR )(((BYTE *)uHostPath.Buffer) + MAX_NDS_NAME_SIZE); + uIntermediateVolume.MaximumLength = MAX_NDS_NAME_SIZE; + + // + // First get the object id from the ds. + // + + Status = NdsGetDsObjectFromPath( pIrpContext, &uDsObjectPath ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + pOldScb = pIrpContext->pScb; + + Status = NdsVerifyObject( pIrpContext, + &uDsObjectPath, + TRUE, // allow server jumping + DEFAULT_RESOLVE_FLAGS, + &dwObjectOid, + &dwObjectType ); + + // + // We may have jumped servers. + // + + *ppScb = pIrpContext->pScb; + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // If this is a dir map, grab the target volume and re-verify + // the object for connectability. + // + + if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) { + + // + // First get the volume object and path. + // + + Status = NdsReadAttributesKm( pIrpContext, + dwObjectOid, + &uPathAttribute, + &NdsRequest ); + + if ( !NT_SUCCESS( Status )) { + goto ExitWithCleanup; + } + + // + // Dig out the volume path and the directory path. + // + + Status = ParseResponse( NULL, + NdsRequest.pRecvBufferVa, + NdsRequest.dwBytesWritten, + "G_____S_ST", + sizeof( DWORD ), // completion code + sizeof( DWORD ), // iter handle + sizeof( DWORD ), // info type + sizeof( DWORD ), // attribute count + sizeof( DWORD ), // syntax id + NULL, // attribute name + 3 * sizeof( DWORD ), // unknown + &uIntermediateVolume, // ds volume + &uHostPath ); // dir map path + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Verify the target volume object. + // + + Status = NdsVerifyObject( pIrpContext, + &uIntermediateVolume, + TRUE, + DEFAULT_RESOLVE_FLAGS, + &dwObjectOid, + &dwDirMapType ); + + // + // We may have jumped servers. + // + + *ppScb = pIrpContext->pScb; + + if ( !NT_SUCCESS( Status )) { + goto ExitWithCleanup; + } + + ASSERT( dwDirMapType == NDS_OBJECTTYPE_VOLUME ); + + } + + // + // Get the server (for any connectable object). + // + + Status = NdsReadStringAttribute( pIrpContext, + dwObjectOid, + &uServerAttribute, + &uHostServer ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Get the host volume or queue. + // + + if ( dwObjectType == NDS_OBJECTTYPE_VOLUME || + dwObjectType == NDS_OBJECTTYPE_DIRMAP ) { + + Status = NdsReadStringAttribute( pIrpContext, + dwObjectOid, + &uVolumeAttribute, + &uHostVolume ); + + } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) { + + Status = NdsReadStringAttribute( pIrpContext, + dwObjectOid, + &uQueueAttribute, + &uHostVolume ); + + } else { + + Status = STATUS_BAD_NETWORK_PATH; + + } + + if ( !NT_SUCCESS( Status )) { + goto ExitWithCleanup; + } + + // + // Dig out the actual server name from the X.500 name. + // + + Status = NdsGetServerBasicName( &uHostServer, + &uRealServerName ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Make sure we have enough space in the new buffer to format + // the new connect string of \X:\Server\Share\Path, + // \LPTX\Server\Share\Path, or \Server\Share\Path. + // + + dwTotalPathLen = uRealServerName.Length + uHostVolume.Length; + dwTotalPathLen += ( sizeof( L"\\\\" ) - sizeof( L"" ) ); + + // + // Account for the correct prefix. We count on single character + // drive and printer letters here. Again, maybe unwise later on. + // + + if ( pIrpContext->Specific.Create.DriveLetter ) { + + if ( dwObjectType == NDS_OBJECTTYPE_VOLUME || + dwObjectType == NDS_OBJECTTYPE_DIRMAP ) { + + dwTotalPathLen += ( sizeof( L"X:\\" ) - sizeof( L"" ) ); + + } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) { + + dwTotalPathLen += ( sizeof( L"LPT1\\" ) - sizeof( L"" ) ); + + } else { + + Status = STATUS_BAD_NETWORK_PATH; + goto ExitWithCleanup; + } + } + + // + // Count space for the path and filename if present. + // + + if ( pIrpContext->Specific.Create.PathName.Length ) { + dwTotalPathLen += pIrpContext->Specific.Create.PathName.Length; + } + + if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) { + dwTotalPathLen += uHostPath.Length; + dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) ); + } + + if ( pIrpContext->Specific.Create.FileName.Length ) { + dwTotalPathLen += pIrpContext->Specific.Create.FileName.Length; + dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) ); + } + + if ( dwTotalPathLen > puServerSharePath->MaximumLength ) { + + DebugTrace( 0 , Dbg, "NdsMapObjectToServerShare: Buffer too small.\n", 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + goto ExitWithCleanup; + } + + // + // First dequeue the irp context from the dir server we've been + // talking to, then make the connect to the new server. We logged + // in earlier so this will get us an authenticated connection. + // + + NwDequeueIrpContext( pIrpContext, FALSE ); + + // + // Since it's possible for us to get attaching to a bindery + // authenticated resource, we have to dig out the user name + // and password for the create call!! + // + + ReadAttachEas( pIrpContext->pOriginalIrp, + &UserName, + &Password, + &ShareType, + NULL ); + + Status = CreateScb( &pNewServerScb, + pIrpContext, + &uRealServerName, + NULL, + &UserName, + &Password, + FALSE, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + ASSERT( pNewServerScb->pNpScb->State == SCB_STATE_IN_USE ); + + NwDereferenceScb( (*ppScb)->pNpScb ); + *ppScb = pNewServerScb; + + // + // Re-query the OID of the print queue object on this server + // or it could be wrong. Do not permit any sort of a server + // jump this time. + // + + if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) { + + Status = NdsVerifyObject( pIrpContext, + &uDsObjectPath, + FALSE, + RSLV_CREATE_ID, + &dwObjectOid, + NULL ); + + if ( !NT_SUCCESS( Status )) { + goto ExitWithCleanup; + } + + } + + if ( pdwObjectId ) { + *pdwObjectId = dwObjectOid; + } + + // + // Re-format the path strings in the irp context. The nds share + // length tells us how much of the NDS share name is interesting + // for getting the directory handle. + // + + usSrv = 0; + pIrpContext->Specific.Create.dwNdsShareLength = 0; + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; + puServerSharePath->Length = sizeof( WCHAR ); + usSrv += sizeof( WCHAR ); + + // + // Set the proper prefix for this connect type. + // + + if ( pIrpContext->Specific.Create.DriveLetter ) { + + if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) { + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'L'; + usSrv += sizeof( WCHAR ); + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'P'; + usSrv += sizeof( WCHAR ); + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'T'; + usSrv += sizeof( WCHAR ); + } + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = + pIrpContext->Specific.Create.DriveLetter; + usSrv += sizeof( WCHAR ); + + if ( dwObjectType != NDS_OBJECTTYPE_QUEUE ) { + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L':'; + usSrv += sizeof( WCHAR ); + } + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; + usSrv += sizeof( WCHAR ); + + puServerSharePath->Length = usSrv; + } + + // + // Append the server name. + // + + RtlAppendUnicodeStringToString( puServerSharePath, &uRealServerName ); + usSrv += uRealServerName.Length; + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; + puServerSharePath->Length += sizeof( WCHAR ); + usSrv += sizeof( WCHAR ); + + // + // Append the volume for volumes or the full ds path to + // the print queue for queues. + // + + if ( dwObjectType == NDS_OBJECTTYPE_VOLUME || + dwObjectType == NDS_OBJECTTYPE_DIRMAP ) { + + RtlAppendUnicodeStringToString( puServerSharePath, &uHostVolume ); + usSrv += uHostVolume.Length; + pIrpContext->Specific.Create.dwNdsShareLength += uHostVolume.Length; + + } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) { + + RtlAppendUnicodeStringToString( puServerSharePath, &uDsObjectPath ); + usSrv += uDsObjectPath.Length; + pIrpContext->Specific.Create.dwNdsShareLength += uDsObjectPath.Length; + + } + + // + // Append the dir map path. + // + + if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) { + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; + puServerSharePath->Length += sizeof( WCHAR ); + usSrv += sizeof( WCHAR ); + pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR ); + + RtlAppendUnicodeStringToString( puServerSharePath, &uHostPath ); + usSrv += uHostPath.Length; + pIrpContext->Specific.Create.dwNdsShareLength += uHostPath.Length; + + } + + // + // Handle the path and file if they exist. + // + + if ( pIrpContext->Specific.Create.PathName.Length ) { + + ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE ); + RtlAppendUnicodeStringToString( puServerSharePath, + &pIrpContext->Specific.Create.PathName ); + usSrv += pIrpContext->Specific.Create.PathName.Length; + + // + // If this is a tree connection, then include the path in + // the share name so that the map point is correct. + // + + if ( CreateTreeConnection ) { + pIrpContext->Specific.Create.dwNdsShareLength += + pIrpContext->Specific.Create.PathName.Length; + } + } + + if ( pIrpContext->Specific.Create.FileName.Length ) { + + ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE ); + + puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; + puServerSharePath->Length += sizeof( WCHAR ); + usSrv += sizeof( WCHAR ); + + RtlAppendUnicodeStringToString( puServerSharePath, + &pIrpContext->Specific.Create.FileName ); + usSrv += pIrpContext->Specific.Create.FileName.Length; + + // + // If this is a tree connection, then include the file in + // the share name so that the map point is correct. + // + + if ( CreateTreeConnection ) { + pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR ); + pIrpContext->Specific.Create.dwNdsShareLength += + pIrpContext->Specific.Create.FileName.Length; + } + } + + // + // Record the object type in the irp context. + // + + pIrpContext->Specific.Create.dwNdsObjectType = dwObjectType; + + DebugTrace( 0, Dbg, "DS Object path is %wZ\n", &pIrpContext->Specific.Create.FullPathName ); + DebugTrace( 0, Dbg, "Resolved path is %wZ\n", puServerSharePath ); + +ExitWithCleanup: + + + NdsFreeLockedBuffer( &NdsRequest ); + FREE_POOL( uHostServer.Buffer ); + return Status; +} |