summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/create4.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/create4.c')
-rw-r--r--private/nw/rdr/create4.c2075
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;
+}