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.

715 lines
21 KiB

  1. /*++
  2. Copyright (c) 1987-1999 Microsoft Corporation
  3. Module Name:
  4. smbcemid.c
  5. Abstract:
  6. This module defines the routines for manipulating MIDs associated with SMB's
  7. --*/
  8. #include "precomp.h"
  9. #pragma hdrstop
  10. #ifdef ALLOC_PRAGMA
  11. #endif
  12. RXDT_DefineCategory(SMBCEMID);
  13. #define Dbg (DEBUG_TRACE_SMBCEMID)
  14. INLINE
  15. BOOLEAN
  16. SmbCeVerifyMid(
  17. PSMBCEDB_SERVER_ENTRY pServerEntry,
  18. PSMB_EXCHANGE pExchange,
  19. SMB_MPX_ID Mid)
  20. {
  21. BOOLEAN MidIsValid = TRUE;
  22. USHORT ServerVersion;
  23. ASSERT(pServerEntry != NULL);
  24. ASSERT(pServerEntry->pMidAtlas != NULL);
  25. if (pServerEntry->pMidAtlas->MaximumMidFieldWidth < 16) {
  26. USHORT MidMask;
  27. MidMask = 0x1 << pServerEntry->pMidAtlas->MaximumMidFieldWidth;
  28. MidMask = MidMask -1;
  29. MidIsValid = ((Mid & ~MidMask) == pExchange->MidCookie);
  30. }
  31. return MidIsValid;
  32. }
  33. INLINE
  34. SMB_MPX_ID
  35. SmbCeEncodeMid(
  36. PSMBCEDB_SERVER_ENTRY pServerEntry,
  37. PSMB_EXCHANGE pExchange,
  38. SMB_MPX_ID Mid)
  39. {
  40. USHORT VersionNumber;
  41. SMB_MPX_ID EncodedMid;
  42. EncodedMid = Mid;
  43. if (pServerEntry->pMidAtlas->MaximumMidFieldWidth < 16) {
  44. LONG MidCookie = InterlockedIncrement(&pServerEntry->Server.MidCounter);
  45. pExchange->MidCookie= ((USHORT)MidCookie <<
  46. pServerEntry->pMidAtlas->MaximumMidFieldWidth);
  47. EncodedMid |= pExchange->MidCookie;
  48. }
  49. return EncodedMid;
  50. }
  51. INLINE
  52. SMB_MPX_ID
  53. SmbCeExtractMid(
  54. PSMBCEDB_SERVER_ENTRY pServerEntry,
  55. SMB_MPX_ID EncodedMid)
  56. {
  57. SMB_MPX_ID Mid = EncodedMid;
  58. if (pServerEntry->pMidAtlas->MaximumMidFieldWidth < 16) {
  59. USHORT MidMask;
  60. MidMask = 0x1 << pServerEntry->pMidAtlas->MaximumMidFieldWidth;
  61. MidMask = MidMask -1;
  62. Mid &= MidMask;
  63. }
  64. return Mid;
  65. }
  66. NTSTATUS
  67. SmbCeAssociateExchangeWithMid(
  68. PSMBCEDB_SERVER_ENTRY pServerEntry,
  69. struct _SMB_EXCHANGE *pExchange)
  70. /*++
  71. Routine Description:
  72. This routine associates an exchange with a MID
  73. Arguments:
  74. pServerEntry - the servere entry
  75. pExchange - the Exchange instance.
  76. Return Value:
  77. STATUS_SUCCESS if successful, otherwise one of the following errors
  78. Notes:
  79. If an asynchronous mechanism to acquire MID's is to be introduced this routine
  80. needs to be modified. Currently this routine does not return control till a
  81. MID is acquired or the exchange is aborted/terminated.
  82. --*/
  83. {
  84. NTSTATUS Status = STATUS_SUCCESS;
  85. PSMBCEDB_REQUEST_ENTRY pRequestEntry;
  86. SMBCE_RESUMPTION_CONTEXT ResumptionContext;
  87. SMBCEDB_SERVER_TYPE ServerType;
  88. BOOLEAN ResetServerEntry = FALSE;
  89. ServerType = SmbCeGetServerType(pServerEntry);
  90. // Acquire the resource
  91. SmbCeAcquireSpinLock();
  92. // Attempt to allocate a MID only for FILE Servers.
  93. if (pServerEntry->pMidAtlas != NULL) {
  94. if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_INDEFINITE_DELAY_IN_RESPONSE) {
  95. // This exchange response can be arbitrarily delayed. Ensure that
  96. // all the available MIDS are not tied up in such exchanges.
  97. if ((pServerEntry->pMidAtlas->NumberOfMidsInUse + 1) >=
  98. pServerEntry->pMidAtlas->MaximumNumberOfMids) {
  99. Status = STATUS_TOO_MANY_COMMANDS;
  100. }
  101. }
  102. if (Status == STATUS_SUCCESS) {
  103. if (pServerEntry->pMidAtlas->NumberOfMidsDiscarded ==
  104. pServerEntry->pMidAtlas->MaximumNumberOfMids) {
  105. Status = STATUS_TOO_MANY_COMMANDS;
  106. ResetServerEntry = TRUE;
  107. }
  108. }
  109. if (Status == STATUS_SUCCESS) {
  110. SMB_MPX_ID Mid;
  111. Status = FsRtlAssociateContextWithMid(
  112. pServerEntry->pMidAtlas,
  113. pExchange,
  114. &Mid);
  115. if (Status == STATUS_SUCCESS) {
  116. pExchange->Mid = SmbCeEncodeMid(pServerEntry,pExchange,Mid);
  117. }
  118. }
  119. } else {
  120. if (pServerEntry->Header.State == SMBCEDB_ACTIVE) {
  121. Status = STATUS_INVALID_PARAMETER;
  122. } else {
  123. Status = STATUS_CONNECTION_DISCONNECTED;
  124. }
  125. }
  126. if (Status == STATUS_UNSUCCESSFUL) {
  127. // Allocate a new entry and add it to the list.
  128. pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
  129. if (pRequestEntry != NULL) {
  130. // Enqueue the request entry.
  131. SmbCeInitializeResumptionContext(&ResumptionContext);
  132. pRequestEntry->MidRequest.Type = ACQUIRE_MID_REQUEST;
  133. pRequestEntry->MidRequest.pExchange = pExchange;
  134. pRequestEntry->MidRequest.pResumptionContext = &ResumptionContext;
  135. SmbCeAddRequestEntryLite(&pServerEntry->MidAssignmentRequests,pRequestEntry);
  136. } else {
  137. Status = STATUS_INSUFFICIENT_RESOURCES;
  138. }
  139. } else if (Status == STATUS_SUCCESS) {
  140. pExchange->SmbCeFlags |= SMBCE_EXCHANGE_MID_VALID;
  141. }
  142. // Release the resource
  143. SmbCeReleaseSpinLock();
  144. if (Status == STATUS_UNSUCCESSFUL) {
  145. //DbgPrint("***** Thread %lx Waiting for MID Resumption Context %lx*****\n",PsGetCurrentThread(),&ResumptionContext);
  146. SmbCeSuspend(&ResumptionContext);
  147. Status = ResumptionContext.Status;
  148. //DbgPrint("***** Thread %lx MID Wait Satisfied %lx *****\n",PsGetCurrentThread(),&ResumptionContext);
  149. }
  150. if (ResetServerEntry) {
  151. // If all the mids have been discarded we rest the transport connection
  152. // to start afresh.
  153. SmbCeTransportDisconnectIndicated(pServerEntry);
  154. }
  155. return Status;
  156. }
  157. struct _SMB_EXCHANGE *
  158. SmbCeMapMidToExchange(
  159. PSMBCEDB_SERVER_ENTRY pServerEntry,
  160. SMB_MPX_ID Mid)
  161. /*++
  162. Routine Description:
  163. This routine maps a given MID to the exchange associated with it
  164. Arguments:
  165. pServerEntry - the servere entry
  166. Mid - the mid to be mapped to an Exchange.
  167. Return Value:
  168. a valid SMB_EXCHANGE instance if successful, otheriwse NULL.
  169. --*/
  170. {
  171. PSMB_EXCHANGE pExchange;
  172. // Acquire the resource
  173. SmbCeAcquireSpinLock();
  174. if (pServerEntry->pMidAtlas != NULL) {
  175. pExchange = FsRtlMapMidToContext(
  176. pServerEntry->pMidAtlas,
  177. Mid);
  178. if (pExchange != NULL) {
  179. if (!SmbCeVerifyMid(pServerEntry,pExchange,Mid)) {
  180. pExchange = NULL;
  181. }
  182. }
  183. } else {
  184. pExchange = NULL;
  185. }
  186. // Release the resource
  187. SmbCeReleaseSpinLock();
  188. return pExchange;
  189. }
  190. NTSTATUS
  191. SmbCeDissociateMidFromExchange(
  192. PSMBCEDB_SERVER_ENTRY pServerEntry,
  193. struct _SMB_EXCHANGE *pExchange)
  194. /*++
  195. Routine Description:
  196. This routine disassociates an exchange from the MID
  197. Arguments:
  198. pServerEntry - the servere entry
  199. pExchange - the exchange instance.
  200. Return Value:
  201. a valid SMB_EXCHANGE instance if successful, otheriwse NULL.
  202. Notes:
  203. If an asynchronous mechanism to acquire MID's is to be introduced this routine
  204. needs to be modified. This modification will also include posting requests
  205. for resumption of exchanges when invoked at DPC level.
  206. --*/
  207. {
  208. NTSTATUS Status = STATUS_SUCCESS;
  209. SMBCEDB_SERVER_TYPE ServerType;
  210. ServerType = SmbCeGetServerType(pServerEntry);
  211. if (pExchange->Mid != SMBCE_OPLOCK_RESPONSE_MID) {
  212. PVOID pContext;
  213. PSMBCEDB_REQUEST_ENTRY pRequestEntry = NULL;
  214. // Acquire the resource
  215. SmbCeAcquireSpinLock();
  216. if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
  217. // Check if there are any pending MID assignment requests and transfer the MID
  218. // if one exists.
  219. pRequestEntry = SmbCeGetFirstRequestEntry(&pServerEntry->MidAssignmentRequests);
  220. if (pRequestEntry != NULL) {
  221. SmbCeRemoveRequestEntryLite(&pServerEntry->MidAssignmentRequests,pRequestEntry);
  222. }
  223. if (pServerEntry->pMidAtlas != NULL) {
  224. SMB_MPX_ID Mid;
  225. Mid = SmbCeExtractMid(pServerEntry,pExchange->Mid);
  226. if (pRequestEntry != NULL) {
  227. Status = FsRtlReassociateMid(
  228. pServerEntry->pMidAtlas,
  229. Mid,
  230. pRequestEntry->MidRequest.pExchange);
  231. ASSERT(Status == STATUS_SUCCESS);
  232. pRequestEntry->MidRequest.pExchange->SmbCeFlags |= SMBCE_EXCHANGE_MID_VALID;
  233. pRequestEntry->MidRequest.pExchange->Mid = SmbCeEncodeMid(
  234. pServerEntry,
  235. pRequestEntry->MidRequest.pExchange,
  236. Mid);
  237. pRequestEntry->MidRequest.pResumptionContext->Status = STATUS_SUCCESS;
  238. } else {
  239. Status = FsRtlMapAndDissociateMidFromContext(
  240. pServerEntry->pMidAtlas,
  241. Mid,
  242. &pContext);
  243. ASSERT(pContext == pExchange);
  244. }
  245. } else {
  246. Status = STATUS_INVALID_PARAMETER;
  247. }
  248. }
  249. // Release the resource
  250. SmbCeReleaseSpinLock();
  251. if (pRequestEntry != NULL) {
  252. // Signal the waiter for resumption
  253. SmbCeResume(pRequestEntry->MidRequest.pResumptionContext);
  254. SmbCeTearDownRequestEntry(pRequestEntry);
  255. }
  256. }
  257. pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_MID_VALID;
  258. return Status;
  259. }
  260. VOID
  261. SmbCeDiscardMidAssignmentRequests(
  262. PSMBCEDB_SERVER_ENTRY pServerEntry)
  263. /*++
  264. Routine Description:
  265. This routine discards all mid assignment requests for a given server entry
  266. Arguments:
  267. pServerEntry - the servere entry
  268. Notes:
  269. This typically happens when the mids in use are being cancelled against a
  270. down level server. In such cases there is no cancel command that can be
  271. sent to the server. Typically we throw away the MID and not use it any
  272. further. this will lead to a graceful degradation in performance when
  273. the connection is reestablished
  274. --*/
  275. {
  276. SMBCEDB_REQUESTS MidRequests;
  277. InitializeListHead(&MidRequests.ListHead);
  278. SmbCeAcquireSpinLock();
  279. if (pServerEntry->pMidAtlas != NULL) {
  280. if (pServerEntry->pMidAtlas->NumberOfMidsDiscarded ==
  281. pServerEntry->pMidAtlas->MaximumNumberOfMids) {
  282. SmbCeTransferRequests(
  283. &MidRequests,
  284. &pServerEntry->MidAssignmentRequests);
  285. }
  286. }
  287. SmbCeReleaseSpinLock();
  288. SmbCeResumeDiscardedMidAssignmentRequests(
  289. &MidRequests,
  290. STATUS_TOO_MANY_COMMANDS);
  291. SmbCeDereferenceServerEntry(pServerEntry);
  292. }
  293. NTSTATUS
  294. SmbCepDiscardMidAssociatedWithExchange(
  295. PSMB_EXCHANGE pExchange)
  296. /*++
  297. Routine Description:
  298. This routine discards the mid associated with an exchange
  299. Arguments:
  300. pExchange - the exchange
  301. Notes:
  302. We use the hypercritical thread to ensure that this request does not block
  303. behind other requests.
  304. This routine also assumes that it is invoked with the SmbCeSpinLock held
  305. --*/
  306. {
  307. NTSTATUS Status = STATUS_SUCCESS;
  308. if ((pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) &&
  309. (pExchange->Mid != SMBCE_OPLOCK_RESPONSE_MID) &&
  310. (pExchange->Mid != SMBCE_ECHO_PROBE_MID)) {
  311. PSMBCEDB_SERVER_ENTRY pServerEntry;
  312. pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
  313. if ((pServerEntry != NULL) &&
  314. (pServerEntry->pMidAtlas != NULL)) {
  315. SMB_MPX_ID Mid;
  316. Mid = SmbCeExtractMid(pServerEntry,pExchange->Mid);
  317. Status = FsRtlReassociateMid(
  318. pServerEntry->pMidAtlas,
  319. Mid,
  320. NULL);
  321. if (Status == STATUS_SUCCESS) {
  322. pServerEntry->pMidAtlas->NumberOfMidsDiscarded++;
  323. if (pServerEntry->pMidAtlas->NumberOfMidsDiscarded ==
  324. pServerEntry->pMidAtlas->MaximumNumberOfMids) {
  325. // All the mids have been discarded. Any pending
  326. // mid assignment requests needs to be completed
  327. // with the appropriate error code.
  328. SmbCeReferenceServerEntry(pServerEntry);
  329. Status = RxDispatchToWorkerThread(
  330. MRxSmbDeviceObject,
  331. HyperCriticalWorkQueue,
  332. SmbCeDiscardMidAssignmentRequests,
  333. pServerEntry);
  334. }
  335. }
  336. pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_MID_VALID;
  337. } else {
  338. Status = STATUS_INVALID_PARAMETER;
  339. }
  340. }
  341. return Status;
  342. }
  343. VOID
  344. SmbCeResumeDiscardedMidAssignmentRequests(
  345. PSMBCEDB_REQUESTS pMidRequests,
  346. NTSTATUS ResumptionStatus)
  347. /*++
  348. Routine Description:
  349. This routine resumes discarded mid assignment requests with the appropriate error
  350. Arguments:
  351. pMidRequests - the discarded requests
  352. ResumptionStatus - the resumption status
  353. Return Value:
  354. a valid SMB_EXCHANGE instance if successful, otheriwse NULL.
  355. Notes:
  356. This routine and the routines that follow enable a pipelined reuse of MID's
  357. If a large buffer is to be copied then this can be done without hodling onto
  358. a MID. This improves the throughput between the client and the server. At the
  359. very least this mechanism ensures that the connection engine will not be the
  360. constraining factor in MID reuse.
  361. --*/
  362. {
  363. PSMBCEDB_REQUEST_ENTRY pRequestEntry;
  364. pRequestEntry = SmbCeGetFirstRequestEntry(pMidRequests);
  365. while (pRequestEntry != NULL) {
  366. // Remove the request entry from the list
  367. SmbCeRemoveRequestEntryLite(pMidRequests,pRequestEntry);
  368. ASSERT(pRequestEntry->GenericRequest.Type == ACQUIRE_MID_REQUEST);
  369. // Signal the waiter for resumption
  370. pRequestEntry->MidRequest.pResumptionContext->Status = ResumptionStatus;
  371. SmbCeResume(pRequestEntry->MidRequest.pResumptionContext);
  372. SmbCeTearDownRequestEntry(pRequestEntry);
  373. pRequestEntry = SmbCeGetFirstRequestEntry(pMidRequests);
  374. }
  375. }
  376. struct _SMB_EXCHANGE *
  377. SmbCeGetExchangeAssociatedWithBuffer(
  378. PSMBCEDB_SERVER_ENTRY pServerEntry,
  379. PVOID pBuffer)
  380. /*++
  381. Routine Description:
  382. This routine gets the exchange associated with a buffer
  383. Arguments:
  384. pServerEntry - the servere entry
  385. pBuffer - the buffer instance.
  386. Return Value:
  387. a valid SMB_EXCHANGE instance if successful, otheriwse NULL.
  388. Notes:
  389. This routine and the routines that follow enable a pipelined reuse of MID's
  390. If a large buffer is to be copied then this can be done without hodling onto
  391. a MID. This improves the throughput between the client and the server. At the
  392. very least this mechanism ensures that the connection engine will not be the
  393. constraining factor in MID reuse.
  394. --*/
  395. {
  396. PSMBCEDB_REQUEST_ENTRY pRequestEntry;
  397. PSMB_EXCHANGE pExchange = NULL;
  398. // Acquire the resource
  399. SmbCeAcquireSpinLock();
  400. // Walk through the list of requests maintained on this and remove the one
  401. // matching the cached buffer ptr with the ptr indicated
  402. pRequestEntry = SmbCeGetFirstRequestEntry(&pServerEntry->OutstandingRequests);
  403. while (pRequestEntry != NULL) {
  404. if ((pRequestEntry->GenericRequest.Type == COPY_DATA_REQUEST) &&
  405. (pRequestEntry->CopyDataRequest.pBuffer == pBuffer)) {
  406. pExchange = pRequestEntry->CopyDataRequest.pExchange;
  407. pRequestEntry->CopyDataRequest.pBuffer = NULL;
  408. break;
  409. }
  410. pRequestEntry = SmbCeGetNextRequestEntry(
  411. &pServerEntry->OutstandingRequests,
  412. pRequestEntry);
  413. }
  414. // Release the resource
  415. SmbCeReleaseSpinLock();
  416. return pExchange;
  417. }
  418. NTSTATUS
  419. SmbCeAssociateBufferWithExchange(
  420. PSMBCEDB_SERVER_ENTRY pServerEntry,
  421. struct _SMB_EXCHANGE * pExchange,
  422. PVOID pBuffer)
  423. /*++
  424. Routine Description:
  425. This routine establishes an association between an exchange and a copy data request
  426. buffer
  427. Arguments:
  428. pServerEntry - the servere entry
  429. pBuffer - the buffer instance.
  430. Return Value:
  431. STATUS_SUCCESS if succesful
  432. --*/
  433. {
  434. NTSTATUS Status = STATUS_SUCCESS;
  435. PSMBCEDB_REQUEST_ENTRY pRequestEntry;
  436. // Acquire the resource
  437. SmbCeAcquireSpinLock();
  438. Status = pServerEntry->ServerStatus;
  439. if (Status == STATUS_SUCCESS) {
  440. // Walk through the list of requests maintained on this and remove the one
  441. // matching the cached buffer ptr with the ptr indicated
  442. pRequestEntry = SmbCeGetFirstRequestEntry(&pServerEntry->OutstandingRequests);
  443. while (pRequestEntry != NULL) {
  444. if ((pRequestEntry->GenericRequest.Type == COPY_DATA_REQUEST) &&
  445. (pRequestEntry->CopyDataRequest.pBuffer == NULL)) {
  446. pRequestEntry->CopyDataRequest.pExchange = pExchange;
  447. pRequestEntry->CopyDataRequest.pBuffer = pBuffer;
  448. break;
  449. }
  450. pRequestEntry = SmbCeGetNextRequestEntry(&pServerEntry->OutstandingRequests,pRequestEntry);
  451. }
  452. }
  453. // Release the resource
  454. SmbCeReleaseSpinLock();
  455. if ((Status == STATUS_SUCCESS) &&
  456. (pRequestEntry == NULL)) {
  457. // Allocate a new entry and add it to the list.
  458. pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
  459. if (pRequestEntry != NULL) {
  460. // Enqueue the request entry.
  461. pRequestEntry->CopyDataRequest.Type = COPY_DATA_REQUEST;
  462. pRequestEntry->CopyDataRequest.pExchange = pExchange;
  463. pRequestEntry->CopyDataRequest.pBuffer = pBuffer;
  464. // Acquire the resource
  465. SmbCeAcquireSpinLock();
  466. if ((Status = pServerEntry->ServerStatus) == STATUS_SUCCESS) {
  467. SmbCeAddRequestEntryLite(&pServerEntry->OutstandingRequests,pRequestEntry);
  468. }
  469. // Release the resource
  470. SmbCeReleaseSpinLock();
  471. if (Status != STATUS_SUCCESS) {
  472. SmbCeTearDownRequestEntry(pRequestEntry);
  473. }
  474. } else {
  475. Status = STATUS_INSUFFICIENT_RESOURCES;
  476. }
  477. }
  478. return Status;
  479. }
  480. VOID
  481. SmbCePurgeBuffersAssociatedWithExchange(
  482. PSMBCEDB_SERVER_ENTRY pServerEntry,
  483. struct _SMB_EXCHANGE * pExchange)
  484. /*++
  485. Routine Description:
  486. This routine purges all the copy data requests associated with an exchange.
  487. Arguments:
  488. pServerEntry - the servere entry
  489. pExchange - the exchange instance.
  490. Notes:
  491. This mechanism of delaying the purging of requests associated with an exchange
  492. till it is discared is intended to solve the problem of repeated allocation/freeing
  493. of request entries. This rests on the assumption that there will not be too many
  494. copy data requests outstanding for any exchange. If evidence to the contrary is
  495. noticed this technique has to be modified.
  496. --*/
  497. {
  498. SMBCEDB_REQUESTS ExchangeRequests;
  499. PSMBCEDB_REQUEST_ENTRY pRequestEntry;
  500. PSMBCEDB_REQUEST_ENTRY pNextRequestEntry;
  501. SmbCeInitializeRequests(&ExchangeRequests);
  502. // Acquire the resource
  503. SmbCeAcquireSpinLock();
  504. // Walk through the list of requests maintained on this and remove the one
  505. // matching the given exchange
  506. pRequestEntry = SmbCeGetFirstRequestEntry(&pServerEntry->OutstandingRequests);
  507. while (pRequestEntry != NULL) {
  508. pNextRequestEntry = SmbCeGetNextRequestEntry(&pServerEntry->OutstandingRequests,pRequestEntry);
  509. if (pRequestEntry->GenericRequest.pExchange == pExchange) {
  510. SmbCeRemoveRequestEntryLite(&pServerEntry->OutStandingRequests,pRequestEntry);
  511. SmbCeAddRequestEntryLite(&ExchangeRequests,pRequestEntry);
  512. }
  513. pRequestEntry = pNextRequestEntry;
  514. }
  515. // Release the resource
  516. SmbCeReleaseSpinLock();
  517. pRequestEntry = SmbCeGetFirstRequestEntry(&ExchangeRequests);
  518. while (pRequestEntry != NULL) {
  519. SmbCeRemoveRequestEntryLite(&ExchangeRequests,pRequestEntry);
  520. SmbCeTearDownRequestEntry(pRequestEntry);
  521. pRequestEntry = SmbCeGetFirstRequestEntry(&ExchangeRequests);
  522. }
  523. }
  524.