Leaked source code of windows server 2003
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.

746 lines
21 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. mprbase.cxx
  5. Abstract:
  6. Contains implementations of base classes that supply common code for
  7. Multi-Provider Router operations.
  8. Contains:
  9. CMprOperation::Perform
  10. CRoutedOperation::ValidateParameters
  11. CRoutedOperation::GetResult
  12. Author:
  13. Anirudh Sahni (anirudhs) 11-Oct-1995
  14. Environment:
  15. User Mode -Win32
  16. Notes:
  17. Revision History:
  18. 11-Oct-1995 AnirudhS
  19. Created.
  20. 05-May-1999 jschwart
  21. Make provider addition/removal dynamic
  22. --*/
  23. //
  24. // Includes
  25. //
  26. #include "precomp.hxx"
  27. #include <malloc.h> // _alloca
  28. //
  29. // External Globals and Statics
  30. //
  31. extern DWORD GlobalNumActiveProviders;
  32. CRoutedOperation::CPathCache CRoutedOperation::_PathCache;
  33. //
  34. // Defines
  35. //
  36. //
  37. // Local Function Prototypes
  38. //
  39. //+-------------------------------------------------------------------------
  40. //
  41. // Function: CMprOperation::Perform
  42. //
  43. // Purpose: See header file
  44. //
  45. // History: 11-Oct-95 AnirudhS Created.
  46. //
  47. // Notes:
  48. //
  49. //--------------------------------------------------------------------------
  50. DWORD CMprOperation::Perform()
  51. {
  52. DWORD status = WN_SUCCESS;
  53. __try
  54. {
  55. //
  56. // Ask the derived class to validate the API parameters
  57. //
  58. status = ValidateParameters();
  59. }
  60. __except (MPR_EXCEPTION_FILTER)
  61. {
  62. status = GetExceptionCode();
  63. if (status != EXCEPTION_ACCESS_VIOLATION)
  64. {
  65. MPR_LOG2(ERROR,"CMprOperation(%s): Unexpected exception %#lx\n",_OpName,status);
  66. }
  67. status = WN_BAD_POINTER;
  68. }
  69. if (status == WN_SUCCESS)
  70. {
  71. //
  72. // Ask the derived class to perform the operation
  73. //
  74. status = GetResult();
  75. }
  76. if (status != WN_SUCCESS)
  77. {
  78. SetLastError(status);
  79. }
  80. return status;
  81. }
  82. //+-------------------------------------------------------------------------
  83. //
  84. // Function: CRoutedOperation::Perform
  85. //
  86. // Purpose: See header file
  87. //
  88. // History: 27-May-99 jschwart Created.
  89. //
  90. // Notes: Since the CMprOperation should have no knowledge of
  91. // providers, deal with provider-related locking/checking
  92. // in the CRoutedOperation class, which is meant for
  93. // APIs that use the providers.
  94. //
  95. //--------------------------------------------------------------------------
  96. DWORD CRoutedOperation::Perform(BOOL fCheckProviders)
  97. {
  98. //
  99. // If an API that uses this class creates another instance of the class
  100. // (e.g., CGetConnectionPerformance uses CGetConnection), there needs
  101. // to be a way to prevent the second call from trying to acquire the
  102. // exclusive lock while the original call holds the shared lock.
  103. //
  104. if (fCheckProviders)
  105. {
  106. MprCheckProviders();
  107. }
  108. CProviderSharedLock PLock;
  109. return CMprOperation::Perform();
  110. }
  111. //+-------------------------------------------------------------------------
  112. //
  113. // Function: CRoutedOperation::ValidateParameters
  114. //
  115. // Purpose: See header file
  116. //
  117. // History: 11-Oct-95 AnirudhS Created.
  118. //
  119. // Notes:
  120. //
  121. //--------------------------------------------------------------------------
  122. DWORD CRoutedOperation::ValidateParameters()
  123. {
  124. //
  125. // Ask the derived class to validate the API parameters.
  126. // Also, if the API caller passed in a specific NP name, the derived
  127. // class should pass it back here, to be validated here. The provider
  128. // is looked up and stored in _pSpecifiedProvider.
  129. // If the API caller passed in a remote name that can be used as a
  130. // hint for routing, the derived class should pass it back here. A
  131. // pointer to it is stored in _RemoteName and used later, in GetResult(),
  132. // to help pick an efficient provider routing order.
  133. //
  134. LPCWSTR pwszProviderName = NULL;
  135. LPCWSTR pwszRemoteName = NULL;
  136. LPCWSTR pwszLocalName = NULL;
  137. ASSERT(MPRProviderLock.Have());
  138. DWORD status = ValidateRoutedParameters(&pwszProviderName,
  139. &pwszRemoteName,
  140. &pwszLocalName);
  141. if (status == WN_SUCCESS)
  142. {
  143. //
  144. // Optimization: Store away the drive type. In GetResult(),
  145. // we need only call the providers if the local name is
  146. // a remote drive. _uDriveType is initialized to DRIVE_REMOTE
  147. //
  148. if (! IS_EMPTY_STRING(pwszLocalName) && pwszLocalName[1] == L':')
  149. {
  150. WCHAR wszRootPath[] = L" :\\";
  151. wszRootPath[0] = pwszLocalName[0];
  152. _uDriveType = GetDriveType(wszRootPath);
  153. }
  154. // This probes pwszRemoteName as well as saving its length
  155. RtlInitUnicodeString(&_RemoteName, pwszRemoteName);
  156. if (! IS_EMPTY_STRING(pwszProviderName))
  157. {
  158. //
  159. // Level 1 init for MprFindProviderByName
  160. //
  161. if (!(GlobalInitLevel & FIRST_LEVEL)) {
  162. status = MprLevel1Init();
  163. if (status != WN_SUCCESS) {
  164. return status;
  165. }
  166. }
  167. _pSpecifiedProvider = MprFindProviderByName(pwszProviderName);
  168. if (_pSpecifiedProvider == NULL)
  169. {
  170. return WN_BAD_PROVIDER;
  171. }
  172. }
  173. }
  174. return status;
  175. }
  176. //+-------------------------------------------------------------------------
  177. //
  178. // Function: CRoutedOperation::GetResult
  179. //
  180. // Purpose: See header file
  181. //
  182. // History: 11-Oct-95 AnirudhS Created.
  183. //
  184. // Notes:
  185. //
  186. //--------------------------------------------------------------------------
  187. DWORD CRoutedOperation::GetResult()
  188. {
  189. DWORD status = WN_SUCCESS;
  190. LPPROVIDER *ProviderArray;
  191. //
  192. // Only call the providers if it's a remote drive
  193. //
  194. if (_uDriveType != DRIVE_REMOTE)
  195. {
  196. return WN_NOT_CONNECTED;
  197. }
  198. INIT_IF_NECESSARY(NETWORK_LEVEL, status);
  199. //
  200. // If there are no providers, return NO_NETWORK
  201. //
  202. if (GlobalNumActiveProviders == 0)
  203. {
  204. return WN_NO_NETWORK;
  205. }
  206. // Array of pointers into the GlobalProviderInfo array.
  207. DWORD numProviders;
  208. __try
  209. {
  210. ProviderArray = (LPPROVIDER *) _alloca(GlobalNumProviders * sizeof(LPPROVIDER));
  211. }
  212. __except (EXCEPTION_EXECUTE_HANDLER)
  213. {
  214. status = WN_OUT_OF_MEMORY;
  215. }
  216. if (status != WN_SUCCESS)
  217. {
  218. return status;
  219. }
  220. //
  221. // Find the list of providers to call for this request.
  222. //
  223. if (_pSpecifiedProvider != NULL)
  224. {
  225. //
  226. // The caller requested a particular Provider
  227. //
  228. ProviderArray[0] = _pSpecifiedProvider;
  229. numProviders = 1;
  230. }
  231. else
  232. {
  233. //
  234. // A Provider name was not specified. Therefore, we must
  235. // create an ordered list and pick the best one.
  236. //
  237. status = FindCallOrder(
  238. &_RemoteName,
  239. ProviderArray,
  240. &numProviders,
  241. NETWORK_TYPE
  242. );
  243. if (status != WN_SUCCESS)
  244. {
  245. return status;
  246. }
  247. }
  248. //
  249. // Loop through the list of providers until one answers the request,
  250. // or the list is exhausted.
  251. //
  252. DWORD statusFlag = 0; // Mask of combined error returns
  253. DWORD FirstNetPathError = WN_SUCCESS; // First NO_NET or BAD_NAME error
  254. DWORD FirstSignificantError = WN_SUCCESS; // First "other" error, used in
  255. // aggressive routing only
  256. status = WN_NOT_SUPPORTED; // Returned if no providers respond
  257. for (DWORD i=0; i<numProviders; i++)
  258. {
  259. _LastProvider = ProviderArray[i];
  260. if (_pProviderFunction != NULL &&
  261. _LastProvider->*_pProviderFunction == NULL)
  262. {
  263. //
  264. // The provider doesn't supply the required entry point.
  265. //
  266. status = WN_NOT_SUPPORTED;
  267. }
  268. else
  269. {
  270. //
  271. // Ask the derived class to try the provider.
  272. //
  273. __try
  274. {
  275. MPR_LOG2(ROUTE, "%s: trying %ws ...\n",
  276. OpName(), _LastProvider->Resource.lpProvider);
  277. status = TestProvider(_LastProvider);
  278. MPR_LOG(ROUTE, "... provider returned %lu\n", status);
  279. }
  280. __except (MPR_EXCEPTION_FILTER)
  281. {
  282. status = GetExceptionCode();
  283. MPR_LOG(ROUTE, "... provider threw EXCEPTION %#lx\n", status);
  284. if (status != EXCEPTION_ACCESS_VIOLATION)
  285. {
  286. MPR_LOG3(ERROR,
  287. "%s: Unexpected Exception %#lx "
  288. "calling %ws provider\n",
  289. OpName(), status,
  290. _LastProvider->Resource.lpProvider);
  291. }
  292. status = WN_BAD_POINTER;
  293. }
  294. }
  295. //
  296. // Decide whether to stop trying other providers and return the
  297. // error immediately, or continue trying other providers.
  298. // There are two algorithms for routing to providers, called
  299. // "lazy routing" and "aggressive routing". In lazy routing,
  300. // we always stop routing, unless the error was an insignificant
  301. // one (such as WN_BAD_NETNAME) indicating that the call may be
  302. // meant for some other provider. In aggressive routing, we
  303. // always continue routing, except on a few special errors (such
  304. // as WN_SUCCESS).
  305. //
  306. switch (status)
  307. {
  308. //////////////////////////////////////////////////////////////
  309. // Always stop routing on these errors, even if routing //
  310. // aggressively //
  311. //////////////////////////////////////////////////////////////
  312. case WN_SUCCESS:
  313. case WN_MORE_DATA:
  314. //
  315. // The provider successfully operated on this path, so add it
  316. // to the cache...
  317. //
  318. _PathCache.AddEntry(&_RemoteName, _LastProvider);
  319. //
  320. // ... and fall through
  321. //
  322. case WN_BAD_POINTER:
  323. case WN_ALREADY_CONNECTED:
  324. case WN_CANCEL:
  325. goto CleanExit;
  326. //////////////////////////////////////////////////////////////
  327. // Always continue routing on these errors //
  328. // Classify them so that if we later decide to return one //
  329. // of them to the caller, we pick the most sensible one //
  330. //////////////////////////////////////////////////////////////
  331. case WN_NOT_SUPPORTED:
  332. //
  333. // Ignore the error
  334. //
  335. break;
  336. case WN_NO_MORE_DEVICES:
  337. statusFlag |= NO_DEVICES;
  338. break;
  339. case WN_NOT_CONNECTED:
  340. statusFlag |= NOT_CONNECTED;
  341. break;
  342. case WN_NOT_CONTAINER:
  343. statusFlag |= NOT_CONTAINER;
  344. break;
  345. case WN_NO_NETWORK:
  346. case WN_FUNCTION_BUSY:
  347. case WN_NO_NET_OR_BAD_PATH:
  348. case WN_NOT_LOGGED_ON:
  349. statusFlag |= NO_NET;
  350. if (FirstNetPathError == WN_SUCCESS)
  351. {
  352. FirstNetPathError = status;
  353. }
  354. break;
  355. case WN_BAD_NETNAME:
  356. case ERROR_BAD_NETPATH:
  357. case WN_BAD_LOCALNAME:
  358. case WN_BAD_VALUE:
  359. case WN_BAD_LEVEL:
  360. case ERROR_REM_NOT_LIST:
  361. statusFlag |= BAD_NAME;
  362. if (FirstNetPathError == WN_SUCCESS)
  363. {
  364. FirstNetPathError = status;
  365. }
  366. break;
  367. //////////////////////////////////////////////////////////////
  368. // On other errors, stop routing if lazy, continue if //
  369. // aggressive //
  370. //////////////////////////////////////////////////////////////
  371. default:
  372. if (_AggressiveRouting)
  373. {
  374. // Remember the first one of these errors. It will take
  375. // precedence over other errors.
  376. if (FirstSignificantError == WN_SUCCESS)
  377. {
  378. FirstSignificantError = status;
  379. }
  380. break;
  381. // Note that if multiple providers return WN_EXTENDED_ERROR,
  382. // we'll return the error reported by the last one rather
  383. // than the first.
  384. }
  385. else
  386. {
  387. // Return this error immediately
  388. goto CleanExit;
  389. }
  390. } // switch
  391. } // for all providers
  392. //
  393. // If a specific provider was tried then return the error from that provider.
  394. // Otherwise, concoct the best return code from the errors returned.
  395. //
  396. if (numProviders > 1)
  397. {
  398. if (FirstSignificantError != WN_SUCCESS)
  399. {
  400. status = FirstSignificantError;
  401. }
  402. else if (statusFlag & NO_DEVICES)
  403. {
  404. status = WN_NO_MORE_DEVICES;
  405. }
  406. else if (statusFlag & NOT_CONNECTED)
  407. {
  408. status = WN_NOT_CONNECTED;
  409. }
  410. else if (statusFlag & NOT_CONTAINER)
  411. {
  412. status = WN_NOT_CONTAINER;
  413. }
  414. else if (statusFlag & (NO_NET | BAD_NAME))
  415. {
  416. if ((statusFlag & (NO_NET | BAD_NAME)) == (NO_NET | BAD_NAME))
  417. {
  418. //
  419. // Mix of special errors occured.
  420. // Pass back the combined error message.
  421. //
  422. status = WN_NO_NET_OR_BAD_PATH;
  423. }
  424. else
  425. {
  426. status = FirstNetPathError;
  427. }
  428. }
  429. else
  430. {
  431. ASSERT(status == WN_NOT_SUPPORTED);
  432. }
  433. }
  434. CleanExit:
  435. MPR_LOG2(ROUTE, "CRoutedOperation(%s): returning %lu\n\n", OpName(), status);
  436. return status;
  437. }
  438. //+-------------------------------------------------------------------------
  439. //
  440. // Function: CRoutedOperation::CPathCache::Construct
  441. //
  442. // Purpose: Constructor, called explicitly to avoid dependence on CRT
  443. //
  444. // History: 09-Apr-96 AnirudhS Created.
  445. //
  446. //--------------------------------------------------------------------------
  447. void CRoutedOperation::CPathCache::Construct()
  448. {
  449. InitializeCriticalSection(&_Lock);
  450. RtlZeroMemory(_RecentPaths, sizeof(_RecentPaths));
  451. InitializeListHead(&_ListHead);
  452. _NumFree = PATH_CACHE_SIZE;
  453. }
  454. //+-------------------------------------------------------------------------
  455. //
  456. // Function: CRoutedOperation::CPathCache::Destroy
  457. //
  458. // Purpose: Destructor, called explicitly to avoid dependence on CRT
  459. //
  460. // History: 09-Apr-96 AnirudhS Created.
  461. //
  462. //--------------------------------------------------------------------------
  463. void CRoutedOperation::CPathCache::Destroy()
  464. {
  465. //
  466. // This is really needed only if the DLL is being unloaded because of
  467. // a FreeLibrary call, not if the process is exiting
  468. //
  469. for (DWORD i = _NumFree; i < PATH_CACHE_SIZE; i++)
  470. {
  471. LocalFree(_RecentPaths[i].Path.Buffer);
  472. }
  473. DeleteCriticalSection(&_Lock);
  474. }
  475. //+-------------------------------------------------------------------------
  476. //
  477. // Function: CRoutedOperation::CPathCache::AddEntry
  478. //
  479. // Purpose: Add an entry to the cache
  480. //
  481. // History: 09-Apr-96 AnirudhS Created.
  482. //
  483. //--------------------------------------------------------------------------
  484. void CRoutedOperation::CPathCache::AddEntry(
  485. const UNICODE_STRING * Path,
  486. LPPROVIDER Provider
  487. )
  488. {
  489. if (Path->Length == 0 || Path->Length >= (MAX_PATH*sizeof(WCHAR)))
  490. {
  491. //
  492. // Don't add empty or too-long paths to the cache
  493. //
  494. return;
  495. }
  496. ASSERT(Path->MaximumLength == Path->Length + sizeof(UNICODE_NULL));
  497. EnterCriticalSection(&_Lock);
  498. CacheEntry *pEntry = NULL; // Entry to write
  499. //
  500. // See if there's a matching path string in the cache already
  501. //
  502. for (PLIST_ENTRY pLinks = _ListHead.Flink;
  503. pLinks != &_ListHead;
  504. pLinks = pLinks->Flink)
  505. {
  506. pEntry = CONTAINING_RECORD(pLinks, CacheEntry, Links);
  507. if (RtlEqualUnicodeString(&pEntry->Path, (PUNICODE_STRING) Path, TRUE))
  508. {
  509. break;
  510. }
  511. pEntry = NULL;
  512. }
  513. if (pEntry == NULL)
  514. {
  515. //
  516. // No matching entry.
  517. // If there's a free entry in the array, use it.
  518. // Otherwise overwrite the last entry in the list.
  519. //
  520. if (_NumFree > 0)
  521. {
  522. _NumFree--;
  523. pEntry = &_RecentPaths[_NumFree];
  524. //
  525. // Add this new entry to the list.
  526. //
  527. InsertHeadList(&_ListHead, &pEntry->Links);
  528. }
  529. else
  530. {
  531. ASSERT(!IsListEmpty(&_ListHead));
  532. pEntry = CONTAINING_RECORD(_ListHead.Blink, CacheEntry, Links);
  533. }
  534. //
  535. // Copy the path string into the cache. Re-use the string buffer,
  536. // unless it's too small.
  537. //
  538. if (pEntry->Path.MaximumLength < Path->MaximumLength)
  539. {
  540. //
  541. // Re-allocate the string buffer. Allocate twice as much space
  542. // as needed, but never more than MAX_PATH Unicode characters.
  543. // Note, here we know that MaximumLength <= MAX_PATH characters.
  544. //
  545. HLOCAL NewBuffer = LocalAlloc(
  546. 0,
  547. min(Path->MaximumLength * 2, MAX_PATH * sizeof(WCHAR))
  548. );
  549. if (NewBuffer == NULL)
  550. {
  551. //
  552. // Couldn't allocate. Don't add to the cache.
  553. // (If it was unused, this cache entry is lost forever.
  554. // CODEWORK try to recover it in this case?)
  555. //
  556. goto CleanExit;
  557. }
  558. LocalFree(pEntry->Path.Buffer);
  559. pEntry->Path.Buffer = (PWSTR) NewBuffer;
  560. pEntry->Path.MaximumLength = (USHORT)LocalSize(NewBuffer);
  561. }
  562. RtlCopyUnicodeString(&pEntry->Path, (PUNICODE_STRING) Path);
  563. }
  564. //
  565. // Remember the provider in the cache. (This overwrites any previously
  566. // remembered provider for the path.)
  567. //
  568. pEntry->Provider = Provider;
  569. MPR_LOG2(ROUTE, "cache: cached %ws for %ws\n", Provider->Resource.lpProvider, Path->Buffer);
  570. //
  571. // Move this entry to the front of the list, if it isn't there already.
  572. //
  573. if (_ListHead.Flink != &pEntry->Links)
  574. {
  575. RemoveEntryList(&pEntry->Links);
  576. InsertHeadList(&_ListHead, &pEntry->Links);
  577. }
  578. CleanExit:
  579. LeaveCriticalSection(&_Lock);
  580. }
  581. //+-------------------------------------------------------------------------
  582. //
  583. // Function: CRoutedOperation::CPathCache::FindEntry
  584. //
  585. // Purpose: Search for an entry in the cache
  586. //
  587. // History: 09-Apr-96 AnirudhS Created.
  588. //
  589. //--------------------------------------------------------------------------
  590. LPPROVIDER CRoutedOperation::CPathCache::FindEntry(
  591. const UNICODE_STRING * Path
  592. )
  593. {
  594. if (Path->Length == 0)
  595. {
  596. return NULL;
  597. }
  598. ASSERT(Path->MaximumLength == Path->Length + sizeof(UNICODE_NULL));
  599. EnterCriticalSection(&_Lock);
  600. //
  601. // Search forward in the list for a matching path string
  602. //
  603. LPPROVIDER Provider = NULL;
  604. for (PLIST_ENTRY pLinks = _ListHead.Flink;
  605. pLinks != &_ListHead;
  606. pLinks = pLinks->Flink)
  607. {
  608. CacheEntry *pEntry = CONTAINING_RECORD(pLinks, CacheEntry, Links);
  609. if (RtlEqualUnicodeString(&pEntry->Path, (PUNICODE_STRING) Path, TRUE))
  610. {
  611. Provider = pEntry->Provider;
  612. //
  613. // Move this entry to the front of the list, if it isn't there already.
  614. //
  615. if (_ListHead.Flink != &pEntry->Links)
  616. {
  617. RemoveEntryList(&pEntry->Links);
  618. InsertHeadList(&_ListHead, &pEntry->Links);
  619. }
  620. break;
  621. }
  622. }
  623. LeaveCriticalSection(&_Lock);
  624. MPR_LOG2(ROUTE, "cache: found %ws for %ws\n",
  625. (Provider ? Provider->Resource.lpProvider : L"no cached provider"),
  626. Path->Buffer);
  627. return Provider;
  628. }