Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2325 lines
64 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. create4.c
  5. Abstract:
  6. This implements the NDS create routines.
  7. Author:
  8. Cory West [CoryWest] 23-Feb-1995
  9. --*/
  10. #include "Procs.h"
  11. #define Dbg (DEBUG_TRACE_NDS)
  12. //
  13. // Pageable.
  14. //
  15. #pragma alloc_text( PAGE, NdsCreateTreeScb )
  16. #pragma alloc_text( PAGE, ConnectBinderyVolume )
  17. #pragma alloc_text( PAGE, HandleVolumeAttach )
  18. #pragma alloc_text( PAGE, NdsGetDsObjectFromPath )
  19. #pragma alloc_text( PAGE, NdsVerifyObject )
  20. #pragma alloc_text( PAGE, NdsVerifyContext )
  21. #pragma alloc_text( PAGE, NdsMapObjectToServerShare )
  22. //
  23. // Not page-able:
  24. //
  25. // NdsSelectConnection (holds a spin lock)
  26. //
  27. NTSTATUS
  28. NdsSelectConnection(
  29. PIRP_CONTEXT pIrpContext,
  30. PUNICODE_STRING puTreeName,
  31. PUNICODE_STRING puUserName,
  32. PUNICODE_STRING puPassword,
  33. BOOL DeferredLogon,
  34. BOOL UseBinderyConnections,
  35. PNONPAGED_SCB *ppNpScb
  36. )
  37. /*++
  38. Routine Description:
  39. Find a nearby tree connection point for the given tree.
  40. DeferredLogon tells us whether or not we need to
  41. initiate a login/authenticate exchange yet. If we have
  42. credentials to a tree, we are NOT allowed to hand off
  43. a connection that has not been logged in because the view
  44. of the tree may be different from what it is supposed to
  45. be.
  46. UseBinderyConnections tells us whether or not we want
  47. to return bindery authenticated connections as valid
  48. nds browse points.
  49. Return Value:
  50. Scb to a server that belongs to the tree we want.
  51. --*/
  52. {
  53. NTSTATUS Status = STATUS_BAD_NETWORK_PATH;
  54. PLOGON pLogon;
  55. PLIST_ENTRY ScbQueueEntry;
  56. KIRQL OldIrql;
  57. PNONPAGED_SCB pFirstNpScb, pNextNpScb;
  58. PNONPAGED_SCB pFoundNpScb = NULL;
  59. PSCB pScb;
  60. LARGE_INTEGER Uid;
  61. PNONPAGED_SCB pOriginalNpScb;
  62. PSCB pOriginalScb;
  63. PNDS_SECURITY_CONTEXT pNdsContext;
  64. SECURITY_SUBJECT_CONTEXT SubjectContext;
  65. BOOL PasswordExpired = FALSE;
  66. //
  67. // Save the original server pointers.
  68. //
  69. pOriginalNpScb = pIrpContext->pNpScb;
  70. pOriginalScb = pIrpContext->pScb;
  71. Uid = pIrpContext->Specific.Create.UserUid;
  72. //
  73. // Determine if we need a guest browse connection.
  74. //
  75. if ( DeferredLogon ) {
  76. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  77. pLogon = FindUser( &Uid, FALSE );
  78. NwReleaseRcb( &NwRcb );
  79. if ( pLogon ) {
  80. Status = NdsLookupCredentials( pIrpContext,
  81. puTreeName,
  82. pLogon,
  83. &pNdsContext,
  84. CREDENTIAL_READ,
  85. FALSE );
  86. if ( NT_SUCCESS( Status ) ) {
  87. if ( ( pNdsContext->Credential != NULL ) &&
  88. ( pNdsContext->CredentialLocked == FALSE ) ) {
  89. DebugTrace( 0, Dbg, "Forcing authenticated browse to %wZ.\n", puTreeName );
  90. DeferredLogon = FALSE;
  91. }
  92. NwReleaseCredList( pLogon, pIrpContext );
  93. }
  94. }
  95. }
  96. //
  97. // Start at the head of the SCB list.
  98. //
  99. KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
  100. if ( ScbQueue.Flink == &ScbQueue ) {
  101. KeReleaseSpinLock( &ScbSpinLock, OldIrql);
  102. return STATUS_BAD_NETWORK_PATH;
  103. }
  104. ScbQueueEntry = ScbQueue.Flink;
  105. pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry,
  106. NONPAGED_SCB,
  107. ScbLinks );
  108. pNextNpScb = pFirstNpScb;
  109. //
  110. // Leave the first SCB referenced since we need it to
  111. // be there for when we walk all the way around the list.
  112. //
  113. NwReferenceScb( pFirstNpScb );
  114. NwReferenceScb( pNextNpScb );
  115. KeReleaseSpinLock( &ScbSpinLock, OldIrql);
  116. while ( TRUE ) {
  117. //
  118. // Check to see if the SCB we have is in the correct tree
  119. // and is usable. Make sure we skip over the permanent
  120. // npscb since it isn't a tree connection. The current
  121. // SCB is always referenced while we're in here.
  122. //
  123. if ( pNextNpScb->pScb ) {
  124. pScb = pNextNpScb->pScb;
  125. if ( RtlEqualUnicodeString( puTreeName, &pScb->NdsTreeName, TRUE ) &&
  126. Uid.QuadPart == pScb->UserUid.QuadPart ) {
  127. pIrpContext->pNpScb = pNextNpScb;
  128. pIrpContext->pScb = pNextNpScb->pScb;
  129. NwAppendToQueueAndWait( pIrpContext );
  130. switch ( pNextNpScb->State ) {
  131. case SCB_STATE_RECONNECT_REQUIRED:
  132. //
  133. // Reconnect to the server. This is not
  134. // a valid path for an anonymous create,
  135. // so there's no chance that we'll get
  136. // a name collision.
  137. //
  138. Status = ConnectToServer( pIrpContext, NULL );
  139. if (!NT_SUCCESS(Status)) {
  140. break;
  141. }
  142. pNextNpScb->State = SCB_STATE_LOGIN_REQUIRED;
  143. case SCB_STATE_LOGIN_REQUIRED:
  144. //
  145. // See if we can login if requested.
  146. //
  147. if ( !DeferredLogon ) {
  148. Status = DoNdsLogon( pIrpContext, puUserName, puPassword );
  149. if ( !NT_SUCCESS( Status ) ) {
  150. break;
  151. }
  152. //
  153. // If we get a warning from this, we need to return it!
  154. //
  155. if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
  156. PasswordExpired = TRUE;
  157. }
  158. //
  159. // Do we have to re-license the connection?
  160. //
  161. if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) {
  162. Status = NdsLicenseConnection( pIrpContext );
  163. if ( !NT_SUCCESS( Status ) ) {
  164. Status = STATUS_REMOTE_SESSION_LIMIT;
  165. break;
  166. }
  167. }
  168. pNextNpScb->State = SCB_STATE_IN_USE;
  169. }
  170. case SCB_STATE_IN_USE:
  171. if ( pNextNpScb->State == SCB_STATE_IN_USE ) {
  172. if ( ( !UseBinderyConnections ) &&
  173. ( pNextNpScb->pScb->UserName.Length != 0 ) ) {
  174. //
  175. // We may not want to use a connection that has been
  176. // bindery authenticated to read the NDS tree because
  177. // we don't have a way to validate that the NDS and
  178. // bindery users are the same.
  179. //
  180. Status = STATUS_ACCESS_DENIED;
  181. break;
  182. }
  183. //
  184. // Verify that we have security rights to this server.
  185. //
  186. Status = CheckScbSecurity( pIrpContext,
  187. pNextNpScb->pScb,
  188. puUserName,
  189. puPassword,
  190. ( BOOLEAN )DeferredLogon );
  191. if ( !NT_SUCCESS( Status ) ) {
  192. break;
  193. }
  194. //
  195. // Check SCB security might return with state login required.
  196. //
  197. if ( ( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) &&
  198. ( !DeferredLogon ) ) {
  199. Status = DoNdsLogon( pIrpContext, puUserName, puPassword );
  200. if ( !NT_SUCCESS( Status ) ) {
  201. break;
  202. }
  203. pNextNpScb->State = SCB_STATE_IN_USE;
  204. }
  205. } else {
  206. //
  207. // If we picked up an already good SCB and the
  208. // login was deferred, set success and continue.
  209. //
  210. ASSERT( DeferredLogon == TRUE );
  211. Status = STATUS_SUCCESS;
  212. }
  213. pFoundNpScb = pNextNpScb;
  214. DebugTrace( 0, Dbg, "NdsSelectConnection: NpScb = %lx\n", pFoundNpScb );
  215. break;
  216. default:
  217. break;
  218. }
  219. NwDequeueIrpContext( pIrpContext, FALSE );
  220. if ( pFoundNpScb ) {
  221. ASSERT( NT_SUCCESS( Status ) );
  222. break;
  223. }
  224. if ( Status == STATUS_WRONG_PASSWORD ||
  225. Status == STATUS_NO_SUCH_USER ) {
  226. NwDereferenceScb( pNextNpScb );
  227. break;
  228. }
  229. //
  230. // Restore the server pointers.
  231. //
  232. pIrpContext->pNpScb = pOriginalNpScb;
  233. pIrpContext->pScb = pOriginalScb;
  234. }
  235. }
  236. //
  237. // Otherwise, get the next one in the list. Don't
  238. // forget to skip the list head.
  239. //
  240. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  241. ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
  242. if ( ScbQueueEntry == &ScbQueue ) {
  243. ScbQueueEntry = ScbQueue.Flink;
  244. }
  245. NwDereferenceScb( pNextNpScb );
  246. pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
  247. if ( pNextNpScb == pFirstNpScb ) {
  248. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  249. Status = STATUS_BAD_NETWORK_PATH;
  250. break;
  251. }
  252. //
  253. // Otherwise, reference this SCB and continue.
  254. //
  255. NwReferenceScb( pNextNpScb );
  256. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  257. }
  258. NwDereferenceScb( pFirstNpScb );
  259. *ppNpScb = pFoundNpScb;
  260. if ( ( NT_SUCCESS( Status ) ) &&
  261. ( PasswordExpired ) ) {
  262. Status = NWRDR_PASSWORD_HAS_EXPIRED;
  263. }
  264. return Status;
  265. }
  266. NTSTATUS
  267. NdsCreateTreeScb(
  268. IN PIRP_CONTEXT pIrpContext,
  269. IN OUT PSCB *ppScb,
  270. IN PUNICODE_STRING puTree,
  271. IN PUNICODE_STRING puUserName,
  272. IN PUNICODE_STRING puPassword,
  273. IN BOOLEAN DeferredLogon,
  274. IN BOOLEAN DeleteOnClose
  275. )
  276. /*++
  277. Description:
  278. Given a tree name, find us a connection point to the tree. This is
  279. done by getting the server addresses out of the bindery and looking
  280. up the names of the servers for those addresses.
  281. When we are all done we need to return the preferred connection
  282. point in ppScb.
  283. Arguments:
  284. pIrpContext - irp context for this request
  285. ppScb - pointer to a pointer to the scb that we want
  286. puTree - tree we want to talk to
  287. --*/
  288. {
  289. NTSTATUS Status;
  290. PLARGE_INTEGER pUid;
  291. PNONPAGED_SCB pNpExistingScb;
  292. UNICODE_STRING UidServerName;
  293. PSCB pTreeScb = NULL;
  294. PSCB pNearestTreeScb = NULL;
  295. PNONPAGED_SCB pNpNearestTreeScb = NULL;
  296. PSCB pNearbyScb = NULL;
  297. BOOLEAN fOnNearbyQueue = FALSE;
  298. PIRP_CONTEXT pExtraIrpContext = NULL;
  299. UNICODE_STRING ScanTreeName;
  300. WCHAR ScanBuffer[NDS_TREE_NAME_LEN + 2];
  301. int i;
  302. IPXaddress DirServerAddress;
  303. CHAR DirServerName[MAX_SERVER_NAME_LENGTH];
  304. ULONG dwLastOid = (ULONG)-1;
  305. UNICODE_STRING CredentialName;
  306. PUNICODE_STRING puConnectName;
  307. PAGED_CODE();
  308. UidServerName.Buffer = NULL;
  309. //
  310. // Make sure the tree name is reasonable, first.
  311. //
  312. if ( ( !puTree ) ||
  313. ( !puTree->Length ) ||
  314. ( puTree->Length / sizeof( WCHAR ) ) > NDS_TREE_NAME_LEN ) {
  315. *ppScb = NULL; //***Terminal Server Merge
  316. return STATUS_INVALID_PARAMETER;
  317. }
  318. //
  319. // If this is an extended credential create, munge the name.
  320. //
  321. RtlInitUnicodeString( &CredentialName, NULL );
  322. if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) &&
  323. ( !IsCredentialName( puTree ) ) ) {
  324. Status = BuildExCredentialServerName( puTree,
  325. puUserName,
  326. &CredentialName );
  327. if ( !NT_SUCCESS( Status ) ) {
  328. goto ExitWithCleanup;
  329. }
  330. puConnectName = &CredentialName;
  331. } else {
  332. puConnectName = puTree;
  333. }
  334. //
  335. // First check to see if we already have a connection
  336. // to this tree that we can use... If so, this will
  337. // leave the irp context pointed at that server for us.
  338. //
  339. // This time around, don't use bindery authenticated
  340. // connections to browse the tree.
  341. //
  342. Status = NdsSelectConnection( pIrpContext,
  343. puConnectName,
  344. puUserName,
  345. puPassword,
  346. DeferredLogon,
  347. FALSE,
  348. &pNpExistingScb );
  349. if ( NT_SUCCESS( Status ) && pNpExistingScb ) {
  350. *ppScb = pNpExistingScb->pScb;
  351. ASSERT( *ppScb != NULL );
  352. ASSERT( NT_SUCCESS( Status ) );
  353. goto ExitWithCleanup;
  354. }
  355. //
  356. // If there was an authentication failure, bail out.
  357. //
  358. if ( Status == STATUS_NO_SUCH_USER ||
  359. Status == STATUS_WRONG_PASSWORD ) {
  360. goto ExitWithCleanup;
  361. *ppScb = NULL; //Terminal Server code merge
  362. }
  363. //
  364. // Otherwise, we need to select a dir server. To do this,
  365. // we have to look up dir server names by address. To do
  366. // this we create an SCB for synchronization with the name
  367. // *tree*, which isn't a valid server name.
  368. //
  369. ScanTreeName.Length = sizeof( WCHAR );
  370. ScanTreeName.MaximumLength = sizeof( ScanBuffer );
  371. ScanTreeName.Buffer = ScanBuffer;
  372. ScanBuffer[0] = L'*';
  373. RtlAppendUnicodeStringToString( &ScanTreeName, puTree );
  374. ScanBuffer[( ScanTreeName.Length / sizeof( WCHAR ) )] = L'*';
  375. ScanTreeName.Length += sizeof( WCHAR );
  376. //
  377. // Now make it a uid server name.
  378. //
  379. Status = MakeUidServer( &UidServerName,
  380. &pIrpContext->Specific.Create.UserUid,
  381. &ScanTreeName );
  382. if ( !NT_SUCCESS( Status ) ) {
  383. goto ExitWithCleanup;
  384. }
  385. NwFindScb( &pTreeScb,
  386. pIrpContext,
  387. &UidServerName,
  388. &ScanTreeName );
  389. if ( !pTreeScb ) {
  390. DebugTrace( 0, Dbg, "Failed to get a tree scb for synchronization.\n", 0 );
  391. goto ExitWithCleanup;
  392. }
  393. //
  394. // Get a nearby server connection and prepare to
  395. // do the bindery scan for tree connection points.
  396. // Don't forget to copy the user uid for security.
  397. //
  398. if ( !NwAllocateExtraIrpContext( &pExtraIrpContext,
  399. pTreeScb->pNpScb ) ) {
  400. Status = STATUS_INSUFFICIENT_RESOURCES;
  401. goto ExitWithCleanup;
  402. }
  403. pExtraIrpContext->Specific.Create.UserUid.QuadPart =
  404. pIrpContext->Specific.Create.UserUid.QuadPart;
  405. //
  406. // Append a wildcard to the tree name for the bindery scan.
  407. //
  408. ScanTreeName.Length = 0;
  409. ScanTreeName.MaximumLength = sizeof( ScanBuffer );
  410. ScanTreeName.Buffer = ScanBuffer;
  411. RtlCopyUnicodeString( &ScanTreeName, puTree );
  412. i = ScanTreeName.Length / sizeof( WCHAR );
  413. while( i <= NDS_TREE_NAME_LEN ) {
  414. ScanBuffer[i++] = L'_';
  415. }
  416. ScanBuffer[NDS_TREE_NAME_LEN] = L'*';
  417. ScanTreeName.Length = (NDS_TREE_NAME_LEN + 1) * sizeof( WCHAR );
  418. DebugTrace( 0, Dbg, "Scanning for NDS tree %wZ.\n", puTree );
  419. //
  420. // Now we lookup the dir server addresses in the bindery and
  421. // try to make dir server connections.
  422. //
  423. while ( TRUE ) {
  424. if ( ( pNearbyScb ) && ( !fOnNearbyQueue ) ) {
  425. //
  426. // Get back to the head of the nearby server so we can continue
  427. // looking for dir servers. If the nearby server is no good anymore,
  428. // dereference the connection and set the nearby scb pointer to
  429. // NULL. This will cause us to get a new nearby server when we
  430. // continue.
  431. //
  432. NwAppendToQueueAndWait( pExtraIrpContext );
  433. if ( !( ( pNearbyScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
  434. ( pNearbyScb->pNpScb->State == SCB_STATE_IN_USE ) ) ) {
  435. NwDequeueIrpContext( pExtraIrpContext, FALSE );
  436. NwDereferenceScb( pNearbyScb->pNpScb );
  437. pNearbyScb = NULL;
  438. //
  439. // Don't restart the search. If our bindery server went down in
  440. // the middle of a connect, the connect will fail and that's ok.
  441. // If we restart the search we can end up in this loop forever.
  442. //
  443. } else {
  444. fOnNearbyQueue = TRUE;
  445. }
  446. }
  447. //
  448. // Get a bindery server to talk to if we don't have one. This may
  449. // be our first time through this loop, or our server may have
  450. // gone bad (see above).
  451. //
  452. // Optimization: What if this CreateScb returns a valid dir server
  453. // for the tree we are looking for? We should use it!!
  454. //
  455. if ( !pNearbyScb ) {
  456. Status = CreateScb( &pNearbyScb,
  457. pExtraIrpContext,
  458. NULL,
  459. NULL,
  460. NULL,
  461. NULL,
  462. TRUE,
  463. FALSE );
  464. if ( !NT_SUCCESS( Status ) ) {
  465. goto ExitWithCleanup;
  466. }
  467. ASSERT( pExtraIrpContext->pNpScb == pNearbyScb->pNpScb );
  468. ASSERT( pExtraIrpContext->pScb == pNearbyScb );
  469. NwAppendToQueueAndWait( pExtraIrpContext );
  470. fOnNearbyQueue = TRUE;
  471. }
  472. //
  473. // Look up the dir server address from our nearby server.
  474. //
  475. Status = ExchangeWithWait( pExtraIrpContext,
  476. SynchronousResponseCallback,
  477. "SdwU",
  478. NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
  479. dwLastOid,
  480. OT_DIRSERVER,
  481. &ScanTreeName );
  482. if ( !NT_SUCCESS( Status ) ) {
  483. //
  484. // We're out of options for dir servers.
  485. //
  486. Status = STATUS_BAD_NETWORK_PATH;
  487. break;
  488. }
  489. Status = ParseResponse( pExtraIrpContext,
  490. pExtraIrpContext->rsp,
  491. pExtraIrpContext->ResponseLength,
  492. "Nd_r",
  493. &dwLastOid,
  494. sizeof( WORD ),
  495. DirServerName,
  496. MAX_SERVER_NAME_LENGTH );
  497. if ( !NT_SUCCESS( Status ) ) {
  498. break;
  499. }
  500. Status = ExchangeWithWait ( pExtraIrpContext,
  501. SynchronousResponseCallback,
  502. "Swbrbp",
  503. NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE,
  504. OT_DIRSERVER,
  505. 0x30,
  506. DirServerName,
  507. MAX_SERVER_NAME_LENGTH,
  508. 1, // Segment number
  509. NET_ADDRESS_PROPERTY );
  510. if ( !NT_SUCCESS( Status ) ) {
  511. DebugTrace( 0, Dbg, "No net address property for this dir server.\n", 0 );
  512. continue;
  513. }
  514. Status = ParseResponse( pExtraIrpContext,
  515. pExtraIrpContext->rsp,
  516. pExtraIrpContext->ResponseLength,
  517. "Nr",
  518. &DirServerAddress,
  519. sizeof(TDI_ADDRESS_IPX) );
  520. if ( !NT_SUCCESS( Status ) ) {
  521. DebugTrace( 0, Dbg, "Couldn't parse net address property for this dir server.\n", 0 );
  522. continue;
  523. }
  524. //
  525. // We get back some odd socket number here, but we really want to
  526. // connect to the NCP socket.
  527. //
  528. DirServerAddress.Socket = NCP_SOCKET;
  529. //
  530. // We know the address of the dir server, so do an anonymous
  531. // create to it. Use the original irp context so the uid is
  532. // correct. Note that we have to dequeue from the nearby scb
  533. // in case we are referred to that server!
  534. //
  535. NwDequeueIrpContext( pExtraIrpContext, FALSE );
  536. fOnNearbyQueue = FALSE;
  537. NwDequeueIrpContext( pIrpContext, FALSE );
  538. Status = CreateScb( &pNearestTreeScb,
  539. pIrpContext,
  540. NULL,
  541. &DirServerAddress,
  542. puUserName,
  543. puPassword,
  544. DeferredLogon,
  545. DeleteOnClose );
  546. if ( !NT_SUCCESS( Status ) ) {
  547. if ( Status == STATUS_NO_SUCH_USER ||
  548. Status == STATUS_WRONG_PASSWORD ||
  549. Status == STATUS_ACCESS_DENIED ||
  550. Status == STATUS_ACCOUNT_DISABLED ||
  551. Status == STATUS_LOGIN_TIME_RESTRICTION ||
  552. Status == STATUS_REMOTE_SESSION_LIMIT ||
  553. Status == STATUS_CONNECTION_COUNT_LIMIT ||
  554. Status == STATUS_NETWORK_CREDENTIAL_CONFLICT ||
  555. Status == STATUS_PASSWORD_EXPIRED ) {
  556. break;
  557. }
  558. continue;
  559. }
  560. //
  561. // If the server we got back was bindery authenticated,
  562. // it is NOT a valid dir server for us to use (yet)!!
  563. //
  564. if ( pNearestTreeScb->UserName.Length != 0 ) {
  565. Status = STATUS_ACCESS_DENIED;
  566. NwDequeueIrpContext( pIrpContext, FALSE );
  567. NwDereferenceScb( pNearestTreeScb->pNpScb );
  568. continue;
  569. }
  570. //
  571. // Otherwise, we're golden. Break out of here!
  572. //
  573. DebugTrace( 0, Dbg, "Dir server: %wZ\n", &pNearestTreeScb->UidServerName );
  574. *ppScb = pNearestTreeScb;
  575. ASSERT( NT_SUCCESS( Status ) );
  576. break;
  577. }
  578. //
  579. // We have been wholly unable to get a browse connection
  580. // to this tree. Try again but this time allow the use
  581. // of connections that are bindery authenticated. We don't
  582. // need the nearby server anymore.
  583. //
  584. if ( pNearbyScb ) {
  585. NwDequeueIrpContext( pExtraIrpContext, FALSE );
  586. NwDereferenceScb( pNearbyScb->pNpScb );
  587. }
  588. if ( ( Status != STATUS_SUCCESS ) &&
  589. ( Status != STATUS_NO_SUCH_USER ) &&
  590. ( Status != STATUS_WRONG_PASSWORD ) &&
  591. ( Status != STATUS_ACCESS_DENIED ) &&
  592. ( Status != STATUS_ACCOUNT_DISABLED ) &&
  593. ( Status != STATUS_LOGIN_TIME_RESTRICTION ) &&
  594. ( Status != STATUS_REMOTE_SESSION_LIMIT ) &&
  595. ( Status != STATUS_CONNECTION_COUNT_LIMIT ) &&
  596. ( Status != STATUS_NETWORK_CREDENTIAL_CONFLICT ) &&
  597. ( Status != STATUS_PASSWORD_EXPIRED ) ) {
  598. Status = NdsSelectConnection( pIrpContext,
  599. puConnectName,
  600. puUserName,
  601. puPassword,
  602. DeferredLogon,
  603. TRUE,
  604. &pNpExistingScb );
  605. if ( NT_SUCCESS( Status ) && pNpExistingScb ) {
  606. *ppScb = pNpExistingScb->pScb;
  607. ASSERT( *ppScb != NULL );
  608. }
  609. }
  610. ExitWithCleanup:
  611. //
  612. // Clean up and bail.
  613. //
  614. if ( pExtraIrpContext ) {
  615. NwFreeExtraIrpContext( pExtraIrpContext );
  616. }
  617. if ( UidServerName.Buffer != NULL ) {
  618. FREE_POOL( UidServerName.Buffer );
  619. }
  620. if ( pTreeScb ) {
  621. NwDereferenceScb( pTreeScb->pNpScb );
  622. }
  623. if ( CredentialName.Buffer ) {
  624. FREE_POOL( CredentialName.Buffer );
  625. }
  626. if (!NT_SUCCESS(Status)) {
  627. *ppScb = NULL;
  628. }
  629. return Status;
  630. }
  631. NTSTATUS
  632. ConnectBinderyVolume(
  633. PIRP_CONTEXT pIrpContext,
  634. PUNICODE_STRING puServerName,
  635. PUNICODE_STRING puVolumeName
  636. )
  637. /*++
  638. Description:
  639. Given a server name and a volume, try to connect the volume.
  640. This is used in QueryPath to pre-connect a volume.
  641. --*/
  642. {
  643. NTSTATUS Status;
  644. PSCB pScb;
  645. PVCB pVcb;
  646. PAGED_CODE();
  647. //
  648. // Try making a server connection with this name.
  649. //
  650. Status = CreateScb( &pScb,
  651. pIrpContext,
  652. puServerName,
  653. NULL,
  654. NULL,
  655. NULL,
  656. FALSE,
  657. FALSE );
  658. if ( !NT_SUCCESS( Status ) ) {
  659. return Status;
  660. }
  661. DebugTrace( 0, Dbg, "Bindery volume connect got server %wZ\n", puServerName );
  662. //
  663. // If we succeeded, do a standard bindery volume attach.
  664. //
  665. NwAppendToQueueAndWait( pIrpContext );
  666. NwAcquireOpenLock( );
  667. try {
  668. pVcb = NwFindVcb( pIrpContext,
  669. puVolumeName,
  670. RESOURCETYPE_ANY,
  671. 0,
  672. FALSE,
  673. FALSE );
  674. } finally {
  675. NwReleaseOpenLock( );
  676. }
  677. if ( pVcb == NULL ) {
  678. Status = STATUS_BAD_NETWORK_PATH;
  679. } else {
  680. //
  681. // We should not have jumped servers since this was explicit.
  682. //
  683. ASSERT( pScb == pIrpContext->pScb );
  684. //
  685. // Remove NwFindVcb reference. Don't supply an IrpContext
  686. // so the Vcb doesn't get destroyed immediately after we just
  687. // created it because no-one else has it referenced.
  688. //
  689. NwDereferenceVcb( pVcb, NULL, FALSE );
  690. DebugTrace( 0, Dbg, "Bindery volume connect got volume %wZ\n", puVolumeName );
  691. }
  692. NwDequeueIrpContext( pIrpContext, FALSE );
  693. NwDereferenceScb( pIrpContext->pNpScb );
  694. return Status;
  695. }
  696. NTSTATUS
  697. HandleVolumeAttach(
  698. PIRP_CONTEXT pIrpContext,
  699. PUNICODE_STRING puServerName,
  700. PUNICODE_STRING puVolumeName
  701. )
  702. /*++
  703. Description:
  704. This function is only callable from the QUERY_PATH code path!
  705. This functions takes a server name and volume name from
  706. QueryPath() and resolves it into a server/volume connection.
  707. The server/volume name can be plain or can refer to an
  708. nds tree and the nds path to a volume object.
  709. In the nds case, we only verify that the volume object exists.
  710. Arguments:
  711. pIrpContext - irp context for this request
  712. puServerName - server name or nds tree name
  713. puVolumeName - volume name or nds path to volume object
  714. --*/
  715. {
  716. NTSTATUS Status;
  717. PSCB pScb;
  718. UNICODE_STRING uDsObject;
  719. DWORD dwVolumeOid, dwObjectType;
  720. PAGED_CODE();
  721. //
  722. // Try the bindery server/volume case first.
  723. //
  724. Status = ConnectBinderyVolume( pIrpContext,
  725. puServerName,
  726. puVolumeName );
  727. if ( NT_SUCCESS( Status ) ) {
  728. return Status;
  729. }
  730. if ( Status == STATUS_NETWORK_UNREACHABLE ) {
  731. // IPX is not bound to anything that is currently
  732. // up (which means it's probably bound only to the
  733. // RAS WAN wrapper). Don't waste time looking for
  734. // a ds tree.
  735. //
  736. return STATUS_BAD_NETWORK_PATH;
  737. }
  738. //
  739. // See if this is a tree name and get a ds connection.
  740. //
  741. pIrpContext->Specific.Create.NdsCreate = TRUE;
  742. Status = NdsCreateTreeScb( pIrpContext,
  743. &pScb,
  744. puServerName,
  745. NULL,
  746. NULL,
  747. TRUE,
  748. FALSE );
  749. if ( !NT_SUCCESS( Status ) ) {
  750. return Status;
  751. }
  752. //
  753. // If we have a tree, resolve the volume object.
  754. // TRACKING: We should actually check to see if we
  755. // already have a connection to this object before
  756. // we hit the ds.
  757. //
  758. Status = NdsGetDsObjectFromPath( pIrpContext,
  759. &uDsObject );
  760. if ( !NT_SUCCESS( Status ) ) {
  761. NwDereferenceScb( pIrpContext->pNpScb );
  762. return Status;
  763. }
  764. Status = NdsVerifyObject( pIrpContext, // irp context for the request
  765. &uDsObject, // path to volume object
  766. TRUE, // allow a server jump
  767. DEFAULT_RESOLVE_FLAGS, // resolver flags
  768. &dwVolumeOid, // volume oid from the ds
  769. &dwObjectType ); // volume or print queue
  770. //
  771. // We may have jumped servers in the VerifyObject code,
  772. // so just make sure we dereference the correct server.
  773. //
  774. NwDereferenceScb( pIrpContext->pNpScb );
  775. return Status;
  776. }
  777. NTSTATUS
  778. NdsGetDsObjectFromPath(
  779. IN PIRP_CONTEXT pIrpContext,
  780. OUT PUNICODE_STRING puDsObject
  781. )
  782. /*++
  783. Description:
  784. Take the full path from the create irp context and
  785. extract out the ds path of the desired object.
  786. The supplied unicode string shouldn't have a buffer;
  787. it will be set up to point into the user's buffer
  788. referred to by the irp context.
  789. Arguments:
  790. pIrpContext - an irp context from a create path request
  791. puDsObject - unicode string that will refer to the correct ds path
  792. --*/
  793. {
  794. DWORD dwPathSeparators;
  795. USHORT NewHead;
  796. PAGED_CODE();
  797. //
  798. // The VolumeName is one of the following:
  799. //
  800. // \X:\Server\Volume.Object.Path
  801. // \Server\Volume.Object.Path
  802. //
  803. *puDsObject = pIrpContext->Specific.Create.VolumeName;
  804. //
  805. // Skip the leading slash.
  806. //
  807. puDsObject->Length -= sizeof( WCHAR );
  808. puDsObject->Buffer += 1;
  809. //
  810. // How many more are there to overcome?
  811. //
  812. NewHead = 0;
  813. dwPathSeparators = pIrpContext->Specific.Create.DriveLetter ? 2 : 1;
  814. while ( NewHead < puDsObject->Length &&
  815. dwPathSeparators ) {
  816. if ( puDsObject->Buffer[NewHead/sizeof(WCHAR)] == OBJ_NAME_PATH_SEPARATOR ) {
  817. dwPathSeparators--;
  818. }
  819. NewHead += sizeof( WCHAR );
  820. }
  821. if ( dwPathSeparators ||
  822. NewHead == puDsObject->Length) {
  823. //
  824. // Something wasn't formed right in the volume name.
  825. //
  826. return STATUS_BAD_NETWORK_PATH;
  827. }
  828. puDsObject->Length -= NewHead;
  829. puDsObject->Buffer += NewHead/sizeof(WCHAR);
  830. //
  831. // If there is a leading dot, skip it.
  832. //
  833. if ( puDsObject->Buffer[0] == L'.' ) {
  834. puDsObject->Length -= sizeof( WCHAR );
  835. puDsObject->Buffer += 1;
  836. }
  837. puDsObject->MaximumLength = puDsObject->Length;
  838. DebugTrace( 0, Dbg, "DS object: %wZ\n", puDsObject );
  839. return STATUS_SUCCESS;
  840. }
  841. NTSTATUS
  842. NdsVerifyObject(
  843. IN PIRP_CONTEXT pIrpContext,
  844. IN PUNICODE_STRING puDsObject,
  845. IN BOOLEAN fAllowServerJump,
  846. IN DWORD dwResolverFlags,
  847. OUT PDWORD pdwDsOid,
  848. OUT PDWORD pdwObjectType
  849. )
  850. /*++
  851. Description:
  852. This function verifies that a ds path refers to a volume
  853. object, print queue, or a dir map. It returns the oid
  854. of the object.
  855. If fAllowServerJump is set to false, this simply looks up
  856. the oid on the current server but doesn't verify the object
  857. type. This routine checks all appropriate contexts for the
  858. object, unlike ResolveNameKm.
  859. Parameters:
  860. pIrpContext - irp context for this request, pointed to the ds server
  861. puDsObject - path to the object in the ds
  862. fAllowServerJump - allow a server jump to take place
  863. pdwDsOid - destination of the ds oid of the object
  864. pdwObjectType - NDS_OBJECTTYPE_VOLUME, NDS_OBJECTTYPE_QUEUE, or NDS_OBJECTTYPE_DIRMAP
  865. --*/
  866. {
  867. NTSTATUS Status;
  868. PNDS_SECURITY_CONTEXT pCredentials = NULL;
  869. PUNICODE_STRING puAppendableContext;
  870. UNICODE_STRING uFdnObject;
  871. WCHAR FdnObject[MAX_NDS_NAME_CHARS];
  872. PLOGON pLogon;
  873. PSCB pScb;
  874. USHORT i;
  875. LOCKED_BUFFER NdsRequest;
  876. DWORD dwObjectOid, dwObjectType;
  877. UNICODE_STRING uVolume;
  878. UNICODE_STRING uQueue;
  879. UNICODE_STRING uDirMap;
  880. UNICODE_STRING uReplyString;
  881. WCHAR ReplyBuffer[32];
  882. BOOLEAN fHoldingCredentialList = FALSE;
  883. BOOLEAN fPartiallyDistinguished = FALSE;
  884. PLIST_ENTRY ListHead;
  885. PLIST_ENTRY Entry;
  886. PNDS_OBJECT_CACHE_ENTRY ObjectEntry = NULL;
  887. LARGE_INTEGER CurrentTick;
  888. BOOLEAN UseEntry = FALSE;
  889. BOOLEAN ObjectCacheLocked = FALSE;
  890. PAGED_CODE();
  891. NdsRequest.pRecvBufferVa = NULL;
  892. //
  893. // Get the user credentials.
  894. //
  895. pScb = pIrpContext->pNpScb->pScb;
  896. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  897. pLogon = FindUser( &pScb->UserUid, FALSE );
  898. NwReleaseRcb( &NwRcb );
  899. //
  900. // Get the credential. We don't care if it's locked or
  901. // not since we're just querying the ds.
  902. //
  903. // Also, get to the head of the queue before you grab
  904. // the credentials and call NdsResolveNameKm
  905. //
  906. NwAppendToQueueAndWait ( pIrpContext );
  907. if ( pLogon ) {
  908. Status = NdsLookupCredentials( pIrpContext,
  909. &pScb->NdsTreeName,
  910. pLogon,
  911. &pCredentials,
  912. CREDENTIAL_READ,
  913. FALSE );
  914. if ( NT_SUCCESS( Status ) ) {
  915. ASSERT( pCredentials != NULL );
  916. fHoldingCredentialList = TRUE;
  917. }
  918. }
  919. //
  920. // Check to see if we have already seen this request.
  921. // If the ObjectCacheBuffer is NULL, then there is no cache
  922. // for this SCB.
  923. //
  924. if( pScb->ObjectCacheBuffer != NULL ) {
  925. //
  926. // Acquire the cache lock so that the cache can be messed with.
  927. // This wait should never fail, but if it does, act as if there
  928. // is no cache for this SCB. The lock is released before returning
  929. // from this function.
  930. //
  931. Status = KeWaitForSingleObject( &(pScb->ObjectCacheLock),
  932. Executive,
  933. KernelMode,
  934. FALSE,
  935. NULL );
  936. if( NT_SUCCESS(Status) ) {
  937. //
  938. // Reference this SCB so it cannot go away, and
  939. // remember it is locked and referenced.
  940. //
  941. NwReferenceScb( pScb->pNpScb );
  942. ObjectCacheLocked = TRUE;
  943. //
  944. // Walk the cache looking for a match.
  945. //
  946. ListHead = &(pScb->ObjectCacheList);
  947. Entry = ListHead->Flink;
  948. while( Entry != ListHead ) {
  949. ObjectEntry = CONTAINING_RECORD( Entry, NDS_OBJECT_CACHE_ENTRY, Links );
  950. //
  951. // Three things are checked; the object name, the AllowServerJump flag,
  952. // and the Resolver flags. If these all match, then this exact request has
  953. // been seen before and the results are already known. If any one of these
  954. // does not match, then the request is different.
  955. //
  956. if( RtlEqualUnicodeString( puDsObject,
  957. &(ObjectEntry->ObjectName),
  958. TRUE ) &&
  959. fAllowServerJump == ObjectEntry->AllowServerJump &&
  960. dwResolverFlags == ObjectEntry->ResolverFlags ) {
  961. //
  962. // A match was found, but the timeout and SCB must be looked at to
  963. // see if this entry needs to be refreshed.
  964. //
  965. KeQueryTickCount( &CurrentTick );
  966. if( ObjectEntry->Scb != NULL && CurrentTick.QuadPart < ObjectEntry->Timeout.QuadPart ) {
  967. UseEntry = TRUE;
  968. }
  969. //
  970. // If an entry was found, exit the loop. This needs to
  971. // happen regardless of whether the data in the entry is
  972. // valid. If the data is not valid, then it will be update
  973. // in the code below.
  974. //
  975. break;
  976. }
  977. Entry = Entry->Flink;
  978. }
  979. if( Entry == ListHead ) {
  980. //
  981. // No entry was found. Reuse the oldest entry in the cache.
  982. //
  983. Entry = ListHead->Blink;
  984. ObjectEntry = CONTAINING_RECORD( Entry, NDS_OBJECT_CACHE_ENTRY, Links );
  985. } else if( UseEntry == TRUE ) {
  986. //
  987. // An entry was found and its data is up to date.
  988. // Just return the data in the cache and save network bandwidth.
  989. //
  990. dwObjectOid = ObjectEntry->DsOid;
  991. dwObjectType = ObjectEntry->ObjectType;
  992. //
  993. // If needed, simulate a server jump by changing the SCB in the IRP_CONTEXT.
  994. //
  995. if( ObjectEntry->Scb != pScb ) {
  996. NwDequeueIrpContext( pIrpContext, FALSE );
  997. NwReferenceScb( ObjectEntry->Scb->pNpScb );
  998. pIrpContext->pScb = ObjectEntry->Scb;
  999. pIrpContext->pNpScb = ObjectEntry->Scb->pNpScb;
  1000. NwDereferenceScb( pScb->pNpScb );
  1001. NwAppendToQueueAndWait( pIrpContext );
  1002. }
  1003. goto CompletedObject;
  1004. }
  1005. //
  1006. // At this point we are going to reuse an exisiting entry. If there is an
  1007. // SCB pointed to by it, dereference it.
  1008. //
  1009. if( ObjectEntry->Scb != NULL ) {
  1010. NwDereferenceScb( ObjectEntry->Scb->pNpScb );
  1011. ObjectEntry->Scb = NULL;
  1012. }
  1013. }
  1014. }
  1015. //
  1016. // Check to see if it's at least partially distinguished already.
  1017. //
  1018. i = 0;
  1019. while (i < puDsObject->Length / sizeof( WCHAR ) ) {
  1020. if ( puDsObject->Buffer[i++] == L'.' ) {
  1021. fPartiallyDistinguished = TRUE;
  1022. }
  1023. }
  1024. //
  1025. // If it's partially distinguished, try it without the context first.
  1026. //
  1027. if ( fPartiallyDistinguished ) {
  1028. Status = NdsResolveNameKm ( pIrpContext,
  1029. puDsObject,
  1030. &dwObjectOid,
  1031. fAllowServerJump,
  1032. dwResolverFlags );
  1033. if ( NT_SUCCESS( Status ) ) {
  1034. DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", puDsObject );
  1035. goto GetObjectType;
  1036. }
  1037. }
  1038. //
  1039. // If that failed, or if it wasn't partially distinguished,
  1040. // see if there's a current context we can append.
  1041. //
  1042. if ( ( pCredentials ) &&
  1043. ( pCredentials->CurrentContext.Length ) ) {
  1044. if ( ( puDsObject->Length + pCredentials->CurrentContext.Length ) < sizeof( FdnObject ) ) {
  1045. //
  1046. // Append the context.
  1047. //
  1048. uFdnObject.MaximumLength = sizeof( FdnObject );
  1049. uFdnObject.Buffer = FdnObject;
  1050. RtlCopyMemory( FdnObject, puDsObject->Buffer, puDsObject->Length );
  1051. uFdnObject.Length = puDsObject->Length;
  1052. if ( uFdnObject.Buffer[( uFdnObject.Length / sizeof( WCHAR ) ) - 1] == L'.' ) {
  1053. uFdnObject.Length -= sizeof( WCHAR );
  1054. }
  1055. if ( pCredentials->CurrentContext.Buffer[0] != L'.' ) {
  1056. uFdnObject.Buffer[uFdnObject.Length / sizeof( WCHAR )] = L'.';
  1057. uFdnObject.Length += sizeof( WCHAR );
  1058. }
  1059. RtlCopyMemory( ((BYTE *)FdnObject) + uFdnObject.Length,
  1060. pCredentials->CurrentContext.Buffer,
  1061. pCredentials->CurrentContext.Length );
  1062. uFdnObject.Length += pCredentials->CurrentContext.Length;
  1063. //
  1064. // Resolve this name.
  1065. //
  1066. Status = NdsResolveNameKm ( pIrpContext,
  1067. &uFdnObject,
  1068. &dwObjectOid,
  1069. fAllowServerJump,
  1070. dwResolverFlags );
  1071. if ( NT_SUCCESS( Status ) ) {
  1072. DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", &uFdnObject );
  1073. goto GetObjectType;
  1074. }
  1075. }
  1076. }
  1077. //
  1078. // This is not a valid name.
  1079. //
  1080. DebugTrace( 0, Dbg, "VerifyObject: No ds object to resolve.\n", 0 );
  1081. if( ObjectCacheLocked == TRUE ) {
  1082. NwDereferenceScb( pScb->pNpScb );
  1083. KeReleaseSemaphore( &(pScb->ObjectCacheLock),
  1084. 0,
  1085. 1,
  1086. FALSE );
  1087. ObjectCacheLocked = FALSE;
  1088. }
  1089. if ( fHoldingCredentialList ) {
  1090. NwReleaseCredList( pLogon, pIrpContext );
  1091. fHoldingCredentialList = FALSE;
  1092. }
  1093. return STATUS_BAD_NETWORK_PATH;
  1094. GetObjectType:
  1095. if ( fHoldingCredentialList ) {
  1096. NwReleaseCredList( pLogon, pIrpContext );
  1097. fHoldingCredentialList = FALSE;
  1098. }
  1099. //
  1100. // If a server jump is not allowed, we don't need to worry
  1101. // about getting the object type.
  1102. //
  1103. if ( !fAllowServerJump ) {
  1104. dwObjectType = 0;
  1105. goto CompletedObject;
  1106. }
  1107. //
  1108. // Resolve the object and get its information.
  1109. //
  1110. Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
  1111. if ( !NT_SUCCESS( Status ) ) {
  1112. if( ObjectCacheLocked == TRUE ) {
  1113. NwDereferenceScb( pScb->pNpScb );
  1114. KeReleaseSemaphore( &(pScb->ObjectCacheLock),
  1115. 0,
  1116. 1,
  1117. FALSE );
  1118. ObjectCacheLocked = FALSE;
  1119. }
  1120. return STATUS_INSUFFICIENT_RESOURCES;
  1121. }
  1122. Status = FragExWithWait( pIrpContext,
  1123. NDSV_READ_ENTRY_INFO,
  1124. &NdsRequest,
  1125. "DD",
  1126. 0,
  1127. dwObjectOid );
  1128. if ( !NT_SUCCESS( Status ) ) {
  1129. goto ExitWithCleanup;
  1130. }
  1131. Status = NdsCompletionCodetoNtStatus( &NdsRequest );
  1132. if ( !NT_SUCCESS( Status ) ) {
  1133. goto ExitWithCleanup;
  1134. }
  1135. //
  1136. // Verify that it's a volume object.
  1137. //
  1138. RtlInitUnicodeString( &uVolume, VOLUME_ATTRIBUTE );
  1139. RtlInitUnicodeString( &uQueue, QUEUE_ATTRIBUTE );
  1140. RtlInitUnicodeString( &uDirMap, DIR_MAP_ATTRIBUTE );
  1141. uReplyString.Length = 0;
  1142. uReplyString.MaximumLength = sizeof( ReplyBuffer );
  1143. uReplyString.Buffer = ReplyBuffer;
  1144. Status = ParseResponse( NULL,
  1145. NdsRequest.pRecvBufferVa,
  1146. NdsRequest.dwBytesWritten,
  1147. "G_T",
  1148. sizeof( NDS_RESPONSE_GET_OBJECT_INFO ),
  1149. &uReplyString );
  1150. if ( !NT_SUCCESS( Status ) ) {
  1151. goto ExitWithCleanup;
  1152. }
  1153. dwObjectType = 0;
  1154. if ( !RtlCompareUnicodeString( &uVolume, &uReplyString, FALSE ) ) {
  1155. dwObjectType = NDS_OBJECTTYPE_VOLUME;
  1156. } else if ( !RtlCompareUnicodeString( &uQueue, &uReplyString, FALSE ) ) {
  1157. dwObjectType = NDS_OBJECTTYPE_QUEUE;
  1158. } else if ( !RtlCompareUnicodeString( &uDirMap, &uReplyString, FALSE ) ) {
  1159. dwObjectType = NDS_OBJECTTYPE_DIRMAP;
  1160. }
  1161. if ( !dwObjectType ) {
  1162. DebugTrace( 0, Dbg, "DS object is not a connectable type.\n", 0 );
  1163. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  1164. goto ExitWithCleanup;
  1165. }
  1166. CompletedObject:
  1167. //
  1168. // See if the cache needs to be updated. If an entry was
  1169. // found in the cache or the oldest is being replace, then
  1170. // ObjectEntry will point to that entry, but UseEntry will
  1171. // be FALSE. If the data from the cache was used, then
  1172. // UseEntry will be TRUE. If the cache is disabled or there
  1173. // was some other problem, then ObjectEntry will be NULL.
  1174. //
  1175. if( ObjectEntry != NULL && UseEntry == FALSE ) {
  1176. //
  1177. // Store the results in the cache entry.
  1178. //
  1179. ObjectEntry->DsOid = dwObjectOid;
  1180. ObjectEntry->ObjectType = dwObjectType;
  1181. ObjectEntry->Scb = pIrpContext->pScb;
  1182. NwReferenceScb( ObjectEntry->Scb->pNpScb );
  1183. //
  1184. // Store the information describing the request.
  1185. //
  1186. ObjectEntry->ResolverFlags = dwResolverFlags;
  1187. ObjectEntry->AllowServerJump = fAllowServerJump;
  1188. RtlCopyUnicodeString( &(ObjectEntry->ObjectName),
  1189. puDsObject );
  1190. //
  1191. // Set the timeout.
  1192. //
  1193. KeQueryTickCount( &CurrentTick );
  1194. ObjectEntry->Timeout.QuadPart = CurrentTick.QuadPart + (NdsObjectCacheTimeout * 100);
  1195. //
  1196. // Remove this entry from wherever it is in the list, and
  1197. // insert it on the front.
  1198. //
  1199. RemoveEntryList( Entry );
  1200. InsertHeadList( ListHead, Entry );
  1201. }
  1202. if ( pdwDsOid ) {
  1203. *pdwDsOid = dwObjectOid;
  1204. }
  1205. if ( pdwObjectType ) {
  1206. *pdwObjectType = dwObjectType;
  1207. }
  1208. Status = STATUS_SUCCESS;
  1209. ExitWithCleanup:
  1210. if( ObjectCacheLocked == TRUE ) {
  1211. NwDereferenceScb( pScb->pNpScb );
  1212. KeReleaseSemaphore( &(pScb->ObjectCacheLock),
  1213. 0,
  1214. 1,
  1215. FALSE );
  1216. }
  1217. if ( fHoldingCredentialList ) {
  1218. NwReleaseCredList( pLogon, pIrpContext );
  1219. }
  1220. if ( NdsRequest.pRecvBufferVa ) {
  1221. NdsFreeLockedBuffer( &NdsRequest );
  1222. }
  1223. return Status;
  1224. }
  1225. NTSTATUS
  1226. NdsVerifyContext(
  1227. PIRP_CONTEXT pIrpContext,
  1228. PUNICODE_STRING puTree,
  1229. PUNICODE_STRING puContext
  1230. )
  1231. /*++
  1232. Given a context and a tree, verify that the context is a
  1233. valid container in the tree.
  1234. This call may cause the irpcontex to jump servers to an
  1235. referred dir server. If so, the scb pointers in the irp
  1236. context will be updated, the old server will be dereferenced,
  1237. and the new server will hold the reference for this request.
  1238. --*/
  1239. {
  1240. NTSTATUS Status;
  1241. DWORD dwOid, dwSubordinates;
  1242. LOCKED_BUFFER NdsRequest;
  1243. PSCB pScb, pTreeScb;
  1244. PNONPAGED_SCB pNpScb;
  1245. PAGED_CODE();
  1246. //
  1247. // Establish a browse connection to the tree we want to query.
  1248. //
  1249. NdsRequest.pRecvBufferVa = NULL;
  1250. pScb = pIrpContext->pScb;
  1251. pNpScb = pIrpContext->pNpScb;
  1252. Status = NdsCreateTreeScb( pIrpContext,
  1253. &pTreeScb,
  1254. puTree,
  1255. NULL,
  1256. NULL,
  1257. TRUE,
  1258. FALSE );
  1259. if ( !NT_SUCCESS( Status ) ) {
  1260. pTreeScb = NULL;
  1261. goto ExitWithCleanup;
  1262. }
  1263. Status = NdsResolveNameKm ( pIrpContext,
  1264. puContext,
  1265. &dwOid,
  1266. TRUE,
  1267. DEFAULT_RESOLVE_FLAGS );
  1268. if ( !NT_SUCCESS( Status ) ) {
  1269. DebugTrace( 0, Dbg, "NdsVerifyContext: resolve failed.\n", 0 );
  1270. goto ExitWithCleanup;
  1271. }
  1272. Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
  1273. if ( !NT_SUCCESS( Status ) ) {
  1274. Status = STATUS_INSUFFICIENT_RESOURCES;
  1275. NdsRequest.pRecvBufferVa = NULL;
  1276. goto ExitWithCleanup;
  1277. }
  1278. Status = FragExWithWait( pIrpContext,
  1279. NDSV_READ_ENTRY_INFO,
  1280. &NdsRequest,
  1281. "DD",
  1282. 0,
  1283. dwOid );
  1284. if ( !NT_SUCCESS( Status ) ) {
  1285. goto ExitWithCleanup;
  1286. }
  1287. Status = NdsCompletionCodetoNtStatus( &NdsRequest );
  1288. if ( !NT_SUCCESS( Status ) ) {
  1289. goto ExitWithCleanup;
  1290. }
  1291. //
  1292. // Verify that it's a volume object by checking the
  1293. // third DWORD, which is the subordinate count.
  1294. //
  1295. Status = ParseResponse( NULL,
  1296. NdsRequest.pRecvBufferVa,
  1297. NdsRequest.dwBytesWritten,
  1298. "G_D",
  1299. 2 * sizeof( DWORD ),
  1300. &dwSubordinates );
  1301. if ( !NT_SUCCESS( Status ) ) {
  1302. goto ExitWithCleanup;
  1303. }
  1304. if ( !dwSubordinates ) {
  1305. DebugTrace( 0, Dbg, "No subordinates in VerifyContext.\n", 0 );
  1306. Status = STATUS_INVALID_PARAMETER;
  1307. goto ExitWithCleanup;
  1308. }
  1309. //
  1310. // Success!
  1311. //
  1312. ExitWithCleanup:
  1313. //
  1314. // We may have jumped servers in the resolve name call,
  1315. // so make sure we dereference the correct SCB!
  1316. //
  1317. if ( pTreeScb ) {
  1318. NwDereferenceScb( pIrpContext->pNpScb );
  1319. }
  1320. //
  1321. // Restore the connection to the original server.
  1322. //
  1323. NwDequeueIrpContext( pIrpContext, FALSE );
  1324. pIrpContext->pScb = pScb;
  1325. pIrpContext->pNpScb = pNpScb;
  1326. if ( NdsRequest.pRecvBufferVa ) {
  1327. NdsFreeLockedBuffer( &NdsRequest );
  1328. }
  1329. return Status;
  1330. }
  1331. NTSTATUS
  1332. NdsMapObjectToServerShare(
  1333. PIRP_CONTEXT pIrpContext,
  1334. PSCB *ppScb,
  1335. PUNICODE_STRING puServerSharePath,
  1336. BOOLEAN CreateTreeConnection,
  1337. PDWORD pdwObjectId
  1338. )
  1339. /*++
  1340. Description:
  1341. This function takes a pointer to a tree scb and an irp
  1342. context for a create request. It looks up the ds object
  1343. from the create request in the ds and maps it to
  1344. the appropriate server/share duple.
  1345. The FullPathName and VolumeName strings in the create
  1346. section of the irp context are updated and a connection
  1347. to the real host server is established so that the
  1348. create request can continue as desired.
  1349. --*/
  1350. {
  1351. NTSTATUS Status;
  1352. LOCKED_BUFFER NdsRequest;
  1353. UNICODE_STRING uServerAttribute;
  1354. UNICODE_STRING uVolumeAttribute;
  1355. UNICODE_STRING uQueueAttribute;
  1356. UNICODE_STRING uPathAttribute;
  1357. UNICODE_STRING uHostServer;
  1358. UNICODE_STRING uRealServerName;
  1359. UNICODE_STRING uHostVolume;
  1360. UNICODE_STRING uHostPath;
  1361. UNICODE_STRING uIntermediateVolume;
  1362. UNICODE_STRING uDsObjectPath;
  1363. DWORD dwObjectOid, dwObjectType, dwDirMapType;
  1364. DWORD dwTotalPathLen;
  1365. USHORT usSrv;
  1366. PSCB pOldScb, pNewServerScb;
  1367. UNICODE_STRING UserName, Password;
  1368. ULONG ShareType;
  1369. PAGED_CODE();
  1370. //
  1371. // Set up strings and buffers.
  1372. //
  1373. RtlInitUnicodeString( &uServerAttribute, HOST_SERVER_ATTRIBUTE );
  1374. RtlInitUnicodeString( &uVolumeAttribute, HOST_VOLUME_ATTRIBUTE );
  1375. RtlInitUnicodeString( &uQueueAttribute, HOST_QUEUE_ATTRIBUTE );
  1376. RtlInitUnicodeString( &uPathAttribute, HOST_PATH_ATTRIBUTE );
  1377. RtlInitUnicodeString( &uHostServer, NULL );
  1378. RtlInitUnicodeString( &uRealServerName, NULL );
  1379. RtlInitUnicodeString( &uHostVolume, NULL );
  1380. RtlInitUnicodeString( &uHostPath, NULL );
  1381. RtlInitUnicodeString( &uIntermediateVolume, NULL );
  1382. Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
  1383. if ( !NT_SUCCESS( Status ) ) {
  1384. return Status;
  1385. }
  1386. uHostServer.Buffer = ALLOCATE_POOL( PagedPool, 4 * MAX_NDS_NAME_SIZE );
  1387. if ( !uHostServer.Buffer ) {
  1388. NdsFreeLockedBuffer( &NdsRequest );
  1389. return STATUS_INSUFFICIENT_RESOURCES;
  1390. }
  1391. uHostServer.MaximumLength = MAX_NDS_NAME_SIZE;
  1392. uHostVolume.Buffer = ( PWCHAR )(((BYTE *)uHostServer.Buffer) + MAX_NDS_NAME_SIZE);
  1393. uHostVolume.MaximumLength = MAX_NDS_NAME_SIZE;
  1394. uHostPath.Buffer = ( PWCHAR )(((BYTE *)uHostVolume.Buffer) + MAX_NDS_NAME_SIZE);
  1395. uHostPath.MaximumLength = MAX_NDS_NAME_SIZE;
  1396. uIntermediateVolume.Buffer = ( PWCHAR )(((BYTE *)uHostPath.Buffer) + MAX_NDS_NAME_SIZE);
  1397. uIntermediateVolume.MaximumLength = MAX_NDS_NAME_SIZE;
  1398. //
  1399. // First get the object id from the ds.
  1400. //
  1401. Status = NdsGetDsObjectFromPath( pIrpContext, &uDsObjectPath );
  1402. if ( !NT_SUCCESS( Status ) ) {
  1403. goto ExitWithCleanup;
  1404. }
  1405. pOldScb = pIrpContext->pScb;
  1406. Status = NdsVerifyObject( pIrpContext,
  1407. &uDsObjectPath,
  1408. TRUE, // allow server jumping
  1409. DEFAULT_RESOLVE_FLAGS,
  1410. &dwObjectOid,
  1411. &dwObjectType );
  1412. //
  1413. // We may have jumped servers.
  1414. //
  1415. *ppScb = pIrpContext->pScb;
  1416. if ( !NT_SUCCESS( Status ) ) {
  1417. goto ExitWithCleanup;
  1418. }
  1419. //
  1420. // If this is a dir map, grab the target volume and re-verify
  1421. // the object for connectability.
  1422. //
  1423. if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
  1424. //
  1425. // First get the volume object and path.
  1426. //
  1427. Status = NdsReadAttributesKm( pIrpContext,
  1428. dwObjectOid,
  1429. &uPathAttribute,
  1430. &NdsRequest );
  1431. if ( !NT_SUCCESS( Status )) {
  1432. goto ExitWithCleanup;
  1433. }
  1434. //
  1435. // Dig out the volume path and the directory path.
  1436. //
  1437. Status = ParseResponse( NULL,
  1438. NdsRequest.pRecvBufferVa,
  1439. NdsRequest.dwBytesWritten,
  1440. "G_____S_ST",
  1441. sizeof( DWORD ), // completion code
  1442. sizeof( DWORD ), // iter handle
  1443. sizeof( DWORD ), // info type
  1444. sizeof( DWORD ), // attribute count
  1445. sizeof( DWORD ), // syntax id
  1446. NULL, // attribute name
  1447. 3 * sizeof( DWORD ), // unknown
  1448. &uIntermediateVolume, // ds volume
  1449. &uHostPath ); // dir map path
  1450. if ( !NT_SUCCESS( Status ) ) {
  1451. goto ExitWithCleanup;
  1452. }
  1453. //
  1454. // Verify the target volume object.
  1455. //
  1456. Status = NdsVerifyObject( pIrpContext,
  1457. &uIntermediateVolume,
  1458. TRUE,
  1459. DEFAULT_RESOLVE_FLAGS,
  1460. &dwObjectOid,
  1461. &dwDirMapType );
  1462. //
  1463. // We may have jumped servers.
  1464. //
  1465. *ppScb = pIrpContext->pScb;
  1466. if ( !NT_SUCCESS( Status )) {
  1467. goto ExitWithCleanup;
  1468. }
  1469. ASSERT( dwDirMapType == NDS_OBJECTTYPE_VOLUME );
  1470. }
  1471. //
  1472. // Get the server (for any connectable object).
  1473. //
  1474. Status = NdsReadStringAttribute( pIrpContext,
  1475. dwObjectOid,
  1476. &uServerAttribute,
  1477. &uHostServer );
  1478. if ( !NT_SUCCESS( Status ) ) {
  1479. goto ExitWithCleanup;
  1480. }
  1481. //
  1482. // Get the host volume or queue.
  1483. //
  1484. if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
  1485. dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
  1486. Status = NdsReadStringAttribute( pIrpContext,
  1487. dwObjectOid,
  1488. &uVolumeAttribute,
  1489. &uHostVolume );
  1490. } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
  1491. Status = NdsReadStringAttribute( pIrpContext,
  1492. dwObjectOid,
  1493. &uQueueAttribute,
  1494. &uHostVolume );
  1495. } else {
  1496. Status = STATUS_BAD_NETWORK_PATH;
  1497. }
  1498. if ( !NT_SUCCESS( Status )) {
  1499. goto ExitWithCleanup;
  1500. }
  1501. //
  1502. // Dig out the actual server name from the X.500 name.
  1503. //
  1504. Status = NdsGetServerBasicName( &uHostServer,
  1505. &uRealServerName );
  1506. if ( !NT_SUCCESS( Status ) ) {
  1507. goto ExitWithCleanup;
  1508. }
  1509. //
  1510. // Make sure we have enough space in the new buffer to format
  1511. // the new connect string of \X:\Server\Share\Path,
  1512. // \LPTX\Server\Share\Path, or \Server\Share\Path.
  1513. //
  1514. dwTotalPathLen = uRealServerName.Length + uHostVolume.Length;
  1515. dwTotalPathLen += ( sizeof( L"\\\\" ) - sizeof( L"" ) );
  1516. //
  1517. // Account for the correct prefix. We count on single character
  1518. // drive and printer letters here. Again, maybe unwise later on.
  1519. //
  1520. if ( pIrpContext->Specific.Create.DriveLetter ) {
  1521. if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
  1522. dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
  1523. dwTotalPathLen += ( sizeof( L"X:\\" ) - sizeof( L"" ) );
  1524. } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
  1525. dwTotalPathLen += ( sizeof( L"LPT1\\" ) - sizeof( L"" ) );
  1526. } else {
  1527. Status = STATUS_BAD_NETWORK_PATH;
  1528. goto ExitWithCleanup;
  1529. }
  1530. }
  1531. //
  1532. // Count space for the path and filename if present.
  1533. //
  1534. if ( pIrpContext->Specific.Create.PathName.Length ) {
  1535. dwTotalPathLen += pIrpContext->Specific.Create.PathName.Length;
  1536. }
  1537. if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
  1538. dwTotalPathLen += uHostPath.Length;
  1539. dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) );
  1540. }
  1541. if ( pIrpContext->Specific.Create.FileName.Length ) {
  1542. dwTotalPathLen += pIrpContext->Specific.Create.FileName.Length;
  1543. dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) );
  1544. }
  1545. if ( dwTotalPathLen > puServerSharePath->MaximumLength ) {
  1546. DebugTrace( 0 , Dbg, "NdsMapObjectToServerShare: Buffer too small.\n", 0 );
  1547. Status = STATUS_BUFFER_TOO_SMALL;
  1548. goto ExitWithCleanup;
  1549. }
  1550. //
  1551. // First dequeue the irp context from the dir server we've been
  1552. // talking to, then make the connect to the new server. We logged
  1553. // in earlier so this will get us an authenticated connection.
  1554. //
  1555. NwDequeueIrpContext( pIrpContext, FALSE );
  1556. //
  1557. // Since it's possible for us to get attaching to a bindery
  1558. // authenticated resource, we have to dig out the user name
  1559. // and password for the create call!!
  1560. //
  1561. ReadAttachEas( pIrpContext->pOriginalIrp,
  1562. &UserName,
  1563. &Password,
  1564. &ShareType,
  1565. NULL );
  1566. Status = CreateScb( &pNewServerScb,
  1567. pIrpContext,
  1568. &uRealServerName,
  1569. NULL,
  1570. &UserName,
  1571. &Password,
  1572. FALSE,
  1573. FALSE );
  1574. if ( !NT_SUCCESS( Status ) ) {
  1575. goto ExitWithCleanup;
  1576. }
  1577. ASSERT( pNewServerScb->pNpScb->State == SCB_STATE_IN_USE );
  1578. NwDereferenceScb( (*ppScb)->pNpScb );
  1579. *ppScb = pNewServerScb;
  1580. //
  1581. // Re-query the OID of the print queue object on this server
  1582. // or it could be wrong. Do not permit any sort of a server
  1583. // jump this time.
  1584. //
  1585. if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
  1586. Status = NdsVerifyObject( pIrpContext,
  1587. &uDsObjectPath,
  1588. FALSE,
  1589. RSLV_CREATE_ID,
  1590. &dwObjectOid,
  1591. NULL );
  1592. if ( !NT_SUCCESS( Status )) {
  1593. goto ExitWithCleanup;
  1594. }
  1595. }
  1596. if ( pdwObjectId ) {
  1597. *pdwObjectId = dwObjectOid;
  1598. }
  1599. //
  1600. // Re-format the path strings in the irp context. The nds share
  1601. // length tells us how much of the NDS share name is interesting
  1602. // for getting the directory handle.
  1603. //
  1604. usSrv = 0;
  1605. pIrpContext->Specific.Create.dwNdsShareLength = 0;
  1606. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
  1607. puServerSharePath->Length = sizeof( WCHAR );
  1608. usSrv += sizeof( WCHAR );
  1609. //
  1610. // Set the proper prefix for this connect type.
  1611. //
  1612. if ( pIrpContext->Specific.Create.DriveLetter ) {
  1613. if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
  1614. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'L';
  1615. usSrv += sizeof( WCHAR );
  1616. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'P';
  1617. usSrv += sizeof( WCHAR );
  1618. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'T';
  1619. usSrv += sizeof( WCHAR );
  1620. }
  1621. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] =
  1622. pIrpContext->Specific.Create.DriveLetter;
  1623. usSrv += sizeof( WCHAR );
  1624. if ( dwObjectType != NDS_OBJECTTYPE_QUEUE ) {
  1625. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L':';
  1626. usSrv += sizeof( WCHAR );
  1627. }
  1628. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
  1629. usSrv += sizeof( WCHAR );
  1630. puServerSharePath->Length = usSrv;
  1631. }
  1632. //
  1633. // Append the server name.
  1634. //
  1635. RtlAppendUnicodeStringToString( puServerSharePath, &uRealServerName );
  1636. usSrv += uRealServerName.Length;
  1637. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
  1638. puServerSharePath->Length += sizeof( WCHAR );
  1639. usSrv += sizeof( WCHAR );
  1640. //
  1641. // Append the volume for volumes or the full ds path to
  1642. // the print queue for queues.
  1643. //
  1644. if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
  1645. dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
  1646. RtlAppendUnicodeStringToString( puServerSharePath, &uHostVolume );
  1647. usSrv += uHostVolume.Length;
  1648. pIrpContext->Specific.Create.dwNdsShareLength += uHostVolume.Length;
  1649. } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
  1650. RtlAppendUnicodeStringToString( puServerSharePath, &uDsObjectPath );
  1651. usSrv += uDsObjectPath.Length;
  1652. pIrpContext->Specific.Create.dwNdsShareLength += uDsObjectPath.Length;
  1653. }
  1654. //
  1655. // Append the dir map path.
  1656. //
  1657. if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
  1658. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
  1659. puServerSharePath->Length += sizeof( WCHAR );
  1660. usSrv += sizeof( WCHAR );
  1661. pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR );
  1662. RtlAppendUnicodeStringToString( puServerSharePath, &uHostPath );
  1663. usSrv += uHostPath.Length;
  1664. pIrpContext->Specific.Create.dwNdsShareLength += uHostPath.Length;
  1665. }
  1666. //
  1667. // Handle the path and file if they exist.
  1668. //
  1669. if ( pIrpContext->Specific.Create.PathName.Length ) {
  1670. ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE );
  1671. RtlAppendUnicodeStringToString( puServerSharePath,
  1672. &pIrpContext->Specific.Create.PathName );
  1673. usSrv += pIrpContext->Specific.Create.PathName.Length;
  1674. //
  1675. // If this is a tree connection, then include the path in
  1676. // the share name so that the map point is correct.
  1677. //
  1678. if ( CreateTreeConnection ) {
  1679. pIrpContext->Specific.Create.dwNdsShareLength +=
  1680. pIrpContext->Specific.Create.PathName.Length;
  1681. }
  1682. }
  1683. if ( pIrpContext->Specific.Create.FileName.Length ) {
  1684. ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE );
  1685. puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
  1686. puServerSharePath->Length += sizeof( WCHAR );
  1687. usSrv += sizeof( WCHAR );
  1688. RtlAppendUnicodeStringToString( puServerSharePath,
  1689. &pIrpContext->Specific.Create.FileName );
  1690. usSrv += pIrpContext->Specific.Create.FileName.Length;
  1691. //
  1692. // If this is a tree connection, then include the file in
  1693. // the share name so that the map point is correct.
  1694. //
  1695. if ( CreateTreeConnection ) {
  1696. pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR );
  1697. pIrpContext->Specific.Create.dwNdsShareLength +=
  1698. pIrpContext->Specific.Create.FileName.Length;
  1699. }
  1700. }
  1701. //
  1702. // Record the object type in the irp context.
  1703. //
  1704. pIrpContext->Specific.Create.dwNdsObjectType = dwObjectType;
  1705. DebugTrace( 0, Dbg, "DS Object path is %wZ\n", &pIrpContext->Specific.Create.FullPathName );
  1706. DebugTrace( 0, Dbg, "Resolved path is %wZ\n", puServerSharePath );
  1707. ExitWithCleanup:
  1708. NdsFreeLockedBuffer( &NdsRequest );
  1709. FREE_POOL( uHostServer.Buffer );
  1710. return Status;
  1711. }