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.

1299 lines
39 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. fetch.c
  5. Abstract:
  6. Staging File Fetcher Command Server.
  7. Author:
  8. Billy J. Fuller 05-Jun-1997
  9. Environment
  10. User mode winnt
  11. --*/
  12. #include <ntreppch.h>
  13. #pragma hdrstop
  14. #undef DEBSUB
  15. #undef DEBSUB
  16. #define DEBSUB "FETCH:"
  17. #include <frs.h>
  18. #include <tablefcn.h>
  19. #include <perrepsr.h>
  20. // #include <md5.h>
  21. //
  22. // Retry times
  23. //
  24. // NOT TOO LONG; we wouldn't want the comm timeout to hit on our
  25. // downstream partner waiting for the fetch to succeed.
  26. //
  27. // Our downstream partner waits FETCHCS_RETRY_WAIT before retrying.
  28. //
  29. #define FETCHCS_RETRY_MIN ( 1 * 1000) // 1 second
  30. #define FETCHCS_RETRY_MAX (10 * 1000) // 10 seconds
  31. #define FETCHCS_RETRY_WAIT ( 5 * 1000) // 5 seconds
  32. //
  33. // Maximume transfer block size in bytes
  34. //
  35. #define FETCHCS_MAX_BLOCK_SIZE (64 * 1024)
  36. //
  37. // Struct for the Staging File Fetcher Command Server
  38. // Contains info about the queues and the threads
  39. //
  40. COMMAND_SERVER FetchCs;
  41. ULONG MaxFetchCsThreads;
  42. //
  43. // Retry fetch after N fetches and reset N to N + 1
  44. //
  45. #if DBG
  46. #define PULL_FETCH_RETRY_TRIGGER(_Coc_, _WStatus_, _Flags_) \
  47. { \
  48. if (DebugInfo.FetchRetryTrigger && --DebugInfo.FetchRetryTrigger <= 0) { \
  49. if (WIN_SUCCESS(_WStatus_)) { \
  50. StageRelease(&_Coc_->ChangeOrderGuid, _Coc_->FileName, _Flags_, NULL, NULL, NULL); \
  51. _WStatus_ = ERROR_RETRY; \
  52. } \
  53. DebugInfo.FetchRetryReset += DebugInfo.FetchRetryInc; \
  54. DebugInfo.FetchRetryTrigger = DebugInfo.FetchRetryReset; \
  55. DPRINT2(0, "++ FETCH RETRY TRIGGER FIRED on %ws; reset to %d\n", \
  56. _Coc_->FileName, DebugInfo.FetchRetryTrigger); \
  57. } \
  58. }
  59. #define CHECK_FETCH_RETRY_TRIGGER(_Always_) \
  60. { \
  61. if (DebugInfo.FetchRetryReset && !_Always_) { \
  62. return FALSE; \
  63. } \
  64. }
  65. #else DBG
  66. #define PULL_FETCH_RETRY_TRIGGER(_WStatus_)
  67. #define CHECK_FETCH_RETRY_TRIGGER()
  68. #endif DBG
  69. DWORD
  70. StuGenerateStage(
  71. IN PCHANGE_ORDER_COMMAND Coc,
  72. IN PCHANGE_ORDER_ENTRY Coe,
  73. IN BOOL FromPreExisting,
  74. IN MD5_CTX *Md5,
  75. PULONGLONG GeneratedSize,
  76. OUT GUID *CompressionFormatUsed
  77. );
  78. DWORD
  79. StuGenerateDecompressedStage(
  80. IN PWCHAR StageDir,
  81. IN GUID *CoGuid,
  82. IN GUID *CompressionFormatUsed
  83. );
  84. BOOL
  85. FetchCsDelCsSubmit(
  86. IN PCOMMAND_PACKET Cmd,
  87. IN BOOL Always
  88. )
  89. /*++
  90. Routine Description:
  91. Set the timer and kick off a delayed staging file command
  92. Arguments:
  93. Cmd
  94. Return Value:
  95. None.
  96. --*/
  97. {
  98. #undef DEBSUB
  99. #define DEBSUB "FetchCsDelCsSubmit:"
  100. //
  101. // Don't bother if the fetch retry trigger is set (error injection)
  102. // MAY RETURN!!!
  103. //
  104. CHECK_FETCH_RETRY_TRIGGER(Always);
  105. //
  106. // Extend the retry time (but not too long)
  107. //
  108. RsTimeout(Cmd) <<= 1;
  109. if (RsTimeout(Cmd) > FETCHCS_RETRY_MAX) {
  110. if (Always) {
  111. RsTimeout(Cmd) = FETCHCS_RETRY_MAX;
  112. }
  113. else {
  114. return (FALSE);
  115. }
  116. }
  117. //
  118. // or too short
  119. //
  120. if (RsTimeout(Cmd) < FETCHCS_RETRY_MIN) {
  121. RsTimeout(Cmd) = FETCHCS_RETRY_MIN;
  122. }
  123. //
  124. // This command will come back to us in a bit
  125. //
  126. FrsDelCsSubmitSubmit(&FetchCs, Cmd, RsTimeout(Cmd));
  127. return (TRUE);
  128. }
  129. VOID
  130. FetchCsRetryFetch(
  131. IN PCOMMAND_PACKET Cmd
  132. )
  133. /*++
  134. Routine Description:
  135. Our upstream partner has requested that we retry the
  136. fetch at a later time because the staging file wasn't present
  137. and couldn't be regenerated because of sharing problems or
  138. lack of disk space.
  139. Arguments:
  140. Cmd
  141. Return Value:
  142. None.
  143. --*/
  144. {
  145. #undef DEBSUB
  146. #define DEBSUB "FetchCsRetryFetch:"
  147. DWORD WStatus;
  148. DWORD Flags;
  149. GUID *CoGuid;
  150. PWCHAR FileName;
  151. PCHANGE_ORDER_COMMAND Coc = RsCoc(Cmd);
  152. //
  153. // Already waited for a bit; retry
  154. //
  155. if (RsTimeout(Cmd)) {
  156. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Retry Initiated");
  157. RcsSubmitTransferToRcs(Cmd, CMD_RECEIVED_STAGE);
  158. return;
  159. }
  160. CoGuid = &Coc->ChangeOrderGuid;
  161. FileName = Coc->FileName;
  162. //
  163. // Free the data block
  164. //
  165. RsBlock(Cmd) = FrsFree(RsBlock(Cmd));
  166. RsBlockSize(Cmd) = QUADZERO;
  167. //
  168. // Delete the current staging file if we are starting over
  169. //
  170. if (RsFileOffset(Cmd).QuadPart == QUADZERO) {
  171. //
  172. // Acquire access to the staging file
  173. //
  174. Flags = STAGE_FLAG_RESERVE | STAGE_FLAG_EXCLUSIVE;
  175. if (CoCmdIsDirectory(Coc)) {
  176. SetFlag(Flags, STAGE_FLAG_FORCERESERVE);
  177. }
  178. WStatus = StageAcquire(CoGuid, FileName, Coc->FileSize, &Flags, 0, NULL);
  179. if (WIN_SUCCESS(WStatus)) {
  180. StageDeleteFile(Coc, NULL, FALSE);
  181. StageRelease(CoGuid, FileName, STAGE_FLAG_UNRESERVE, NULL, NULL, NULL);
  182. }
  183. }
  184. RsTimeout(Cmd) = FETCHCS_RETRY_WAIT;
  185. FrsDelCsSubmitSubmit(&FetchCs, Cmd, RsTimeout(Cmd));
  186. }
  187. VOID
  188. FetchCsAbortFetch(
  189. IN PCOMMAND_PACKET Cmd,
  190. IN DWORD WStatus
  191. )
  192. /*++
  193. Routine Description:
  194. Out inbound partner has requested that we abort the fetch.
  195. The inbound partner sends this response when it is unable to generate
  196. or deliver the staging file due to a non-recoverable error. Currently
  197. this means any error NOT in the following list: (WIN_RETRY_FETCH() Macro)
  198. ERROR_SHARING_VIOLATION
  199. ERROR_DISK_FULL
  200. ERROR_HANDLE_DISK_FULL
  201. ERROR_DIR_NOT_EMPTY
  202. ERROR_OPLOCK_NOT_GRANTED
  203. ERROR_RETRY
  204. Typically we get an abort if the upstream partner has deleted the underlying
  205. file and the staging file associated with this change order has been
  206. cleaned up (e.g. the upstream partner has been stopped and restarted).
  207. Arguments:
  208. Cmd
  209. WStatus - Win32 status code.
  210. Return Value:
  211. None.
  212. --*/
  213. {
  214. #undef DEBSUB
  215. #define DEBSUB "FetchCsAbortFetch:"
  216. SET_COE_FLAG(RsCoe(Cmd), COE_FLAG_STAGE_ABORTED | COE_FLAG_STAGE_DELETED);
  217. ChgOrdInboundRetired(RsCoe(Cmd));
  218. RsCoe(Cmd) = NULL;
  219. FrsCompleteCommand(Cmd, WStatus);
  220. }
  221. VOID
  222. FetchCsReceivingStage(
  223. IN PCOMMAND_PACKET Cmd
  224. )
  225. /*++
  226. Routine Description:
  227. Put this data into the staging file
  228. TODO -- If the MD5 checksum was updated by the upstream member as a part of
  229. demand fetch stage file generation (see FetchCsSendStage()) then we need to
  230. propagate RsMd5Digest(Cmd) into the change order command so it can be
  231. updated in the IDTable when this Co retires. Need to decide the correct
  232. conditions under which this should happen.
  233. Arguments:
  234. Cmd
  235. Return Value:
  236. None.
  237. --*/
  238. {
  239. #undef DEBSUB
  240. #define DEBSUB "FetchCsReceivingStage:"
  241. DWORD WStatus;
  242. ULONG Flags;
  243. PWCHAR StagePath = NULL;
  244. PWCHAR FinalPath = NULL;
  245. HANDLE Handle = INVALID_HANDLE_VALUE;
  246. WIN32_FILE_ATTRIBUTE_DATA Attrs;
  247. STAGE_HEADER Header;
  248. PREPLICA Replica = NULL;
  249. //
  250. // If this Cmd was not in response to a request that we made or
  251. // it was not for the correct offset that we were expecting then
  252. // just ignore it.
  253. //
  254. if (RsCoe(Cmd) == NULL) {
  255. FrsCompleteCommand(Cmd, ERROR_SUCCESS);
  256. return;
  257. }
  258. CHANGE_ORDER_TRACE(3, RsCoe(Cmd), "Fetch Receiving");
  259. DPRINT1(4, "++ RsFileSize(Cmd).QuadPart: %08x %08x\n",
  260. PRINTQUAD(RsFileSize(Cmd).QuadPart));
  261. DPRINT1(4, "++ RsFileOffset(Cmd).QuadPart: %08x %08x\n",
  262. PRINTQUAD(RsFileOffset(Cmd).QuadPart));
  263. DPRINT1(4, "++ RsBlockSize(Cmd) : %08x %08x\n",
  264. PRINTQUAD(RsBlockSize(Cmd)));
  265. //
  266. // Acquire access to the staging file
  267. //
  268. Flags = STAGE_FLAG_RESERVE | STAGE_FLAG_EXCLUSIVE;
  269. if (CoCmdIsDirectory(RsCoc(Cmd))) {
  270. SetFlag(Flags, STAGE_FLAG_FORCERESERVE);
  271. }
  272. WStatus = StageAcquire(&RsCoc(Cmd)->ChangeOrderGuid,
  273. RsCoc(Cmd)->FileName,
  274. RsCoc(Cmd)->FileSize,
  275. &Flags,
  276. RsReplica(Cmd)->ReplicaNumber,
  277. NULL);
  278. //
  279. // Retriable problem; Send the CO through retry.
  280. //
  281. if (WIN_RETRY_FETCH(WStatus)) {
  282. CHANGE_ORDER_TRACE(3, RsCoe(Cmd), "Can't Acquire Stage Retry Co");
  283. ChgOrdInboundRetry(RsCoe(Cmd), IBCO_FETCH_RETRY);
  284. RsCoe(Cmd) = NULL;
  285. FrsCompleteCommand(Cmd, WStatus);
  286. return;
  287. /*
  288. CHANGE_ORDER_TRACEW(3, RsCoe(Cmd), "Fetch Receiving Retry", WStatus);
  289. FrsFetchCsSubmitTransfer(Cmd, CMD_RETRY_FETCH);
  290. return;
  291. */
  292. }
  293. //
  294. // Unrecoverable error; abort (see FetchCsAbortFetch() for description.)
  295. //
  296. if (!WIN_SUCCESS(WStatus)) {
  297. CHANGE_ORDER_TRACEW(0, RsCoe(Cmd), "fetch Receiving Abort", WStatus);
  298. FetchCsAbortFetch(Cmd, WStatus);
  299. return;
  300. }
  301. if (RsFileOffset(Cmd).QuadPart == QUADZERO) {
  302. //
  303. // This is the first block of file data. It will have the stage header.
  304. // Read the header and get the compression guid for this stage file from
  305. // it. Block size is 64K max. 1st block will atleast have the complete header.
  306. // Check it just to make sure.
  307. //
  308. if (RsBlockSize(Cmd) >= sizeof(STAGE_HEADER)) {
  309. ZeroMemory(&Header, sizeof(STAGE_HEADER));
  310. CopyMemory(&Header, RsBlock(Cmd), sizeof(STAGE_HEADER));
  311. }
  312. if (!IS_GUID_ZERO(&Header.CompressionGuid)) {
  313. SET_COC_FLAG(RsCoc(Cmd), CO_FLAG_COMPRESSED_STAGE);
  314. } else {
  315. CLEAR_COC_FLAG(RsCoc(Cmd), CO_FLAG_COMPRESSED_STAGE);
  316. }
  317. }
  318. //
  319. // Get a handle to the staging file. Use a different prefix depending
  320. // on whether the stage file being sent is compressed or uncompressed.
  321. //
  322. if (COC_FLAG_ON(RsCoc(Cmd), CO_FLAG_COMPRESSED_STAGE)) {
  323. StagePath = StuCreStgPath(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), STAGE_GENERATE_COMPRESSED_PREFIX);
  324. SetFlag(Flags, STAGE_FLAG_COMPRESSED);
  325. } else {
  326. StagePath = StuCreStgPath(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), STAGE_GENERATE_PREFIX);
  327. }
  328. if ((Flags & STAGE_FLAG_DATA_PRESENT) ||
  329. (RsFileOffset(Cmd).QuadPart >= RsFileSize(Cmd).QuadPart)) {
  330. //
  331. // Data has arrived. Go complete the stage file final rename.
  332. //
  333. goto RESTART;
  334. }
  335. if (Flags & STAGE_FLAG_CREATING) {
  336. //
  337. // Make sure to truncate the staging file when our upstream
  338. // partner is sending (or resending) the first block of the
  339. // staging file.
  340. //
  341. // Without the truncation, BackupWrite() can AV if NtFrs
  342. // passes in garbage at the end of a too-large
  343. // staging file. A staging file may be too-large if the
  344. // preexisting file used to generate the local staging
  345. // file is smaller than the version of the same file our
  346. // partner wants to send.
  347. //
  348. // Alternatively, I could have truncated the staging file
  349. // after receiving the last block but this code change is less
  350. // risk and is just as effective.
  351. //
  352. if (RsFileOffset(Cmd).QuadPart == QUADZERO) {
  353. ClearFlag(Flags, STAGE_FLAG_CREATING | STAGE_FLAG_CREATED | STAGE_FLAG_DATA_PRESENT);
  354. } else {
  355. //
  356. // See if the staging file exists. If not, set the flags
  357. // to create it.
  358. //
  359. StuOpenFile(StagePath, GENERIC_READ | GENERIC_WRITE, &Handle);
  360. if (!HANDLE_IS_VALID(Handle)) {
  361. ClearFlag(Flags, STAGE_FLAG_CREATING | STAGE_FLAG_CREATED | STAGE_FLAG_DATA_PRESENT);
  362. }
  363. }
  364. }
  365. if (!(Flags & STAGE_FLAG_CREATING)) {
  366. CHANGE_ORDER_TRACE(3, RsCoe(Cmd), "Fetch Receiving Generate Stage");
  367. //
  368. // No longer have a staging file; digest invalid
  369. //
  370. RsMd5Digest(Cmd) = FrsFree(RsMd5Digest(Cmd));
  371. //
  372. // Create and allocate disk space
  373. //
  374. WStatus = StuCreateFile(StagePath, &Handle);
  375. if (!HANDLE_IS_VALID(Handle) || !WIN_SUCCESS(WStatus)) {
  376. goto ERROUT;
  377. }
  378. WStatus = FrsSetFilePointer(StagePath, Handle, RsFileSize(Cmd).HighPart,
  379. RsFileSize(Cmd).LowPart);
  380. CLEANUP1_WS(0, "++ SetFilePointer failed on %ws;", StagePath, WStatus, ERROUT);
  381. WStatus = FrsSetEndOfFile(StagePath, Handle);
  382. CLEANUP1_WS(0, "++ SetEndOfFile failed on %ws;", StagePath, WStatus, ERROUT);
  383. //
  384. // File was deleted during the fetch; start over
  385. //
  386. if (RsFileOffset(Cmd).QuadPart != QUADZERO) {
  387. CHANGE_ORDER_TRACE(3, RsCoe(Cmd), "Fetch Receiving Restart");
  388. RsFileOffset(Cmd).QuadPart = QUADZERO;
  389. RsBlock(Cmd) = FrsFree(RsBlock(Cmd));
  390. RsBlockSize(Cmd) = QUADZERO;
  391. goto RESTART;
  392. }
  393. }
  394. //
  395. // Seek to the offset for this block
  396. //
  397. WStatus = FrsSetFilePointer(StagePath, Handle, RsFileOffset(Cmd).HighPart,
  398. RsFileOffset(Cmd).LowPart);
  399. CLEANUP1_WS(0, "++ SetFilePointer failed on %ws;", StagePath, WStatus, ERROUT);
  400. //
  401. // write the file and update the offset for the next block
  402. //
  403. WStatus = StuWriteFile(StagePath, Handle, RsBlock(Cmd), (ULONG)RsBlockSize(Cmd));
  404. CLEANUP1_WS(0, "++ WriteFile failed on %ws;", StagePath, WStatus, ERROUT);
  405. //
  406. // Increment the counter Bytes of staging Fetched
  407. //
  408. Replica = RsCoe(Cmd)->NewReplica;
  409. PM_INC_CTR_REPSET(Replica, SFFetchedB, RsBlockSize(Cmd));
  410. RESTART:
  411. FrsFlushFile(StagePath, Handle);
  412. FRS_CLOSE(Handle);
  413. if ((RsFileOffset(Cmd).QuadPart + RsBlockSize(Cmd)) >= RsFileSize(Cmd).QuadPart) {
  414. //
  415. // All the stage file data is here. Do the final rename.
  416. //
  417. SetFlag(Flags, STAGE_FLAG_DATA_PRESENT | STAGE_FLAG_RERESERVE);
  418. if (COC_FLAG_ON(RsCoc(Cmd), CO_FLAG_COMPRESSED_STAGE)) {
  419. FinalPath = StuCreStgPath(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), STAGE_FINAL_COMPRESSED_PREFIX);
  420. } else {
  421. FinalPath = StuCreStgPath(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), STAGE_FINAL_PREFIX);
  422. }
  423. if (!MoveFileEx(StagePath,
  424. FinalPath,
  425. MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING)) {
  426. WStatus = GetLastError();
  427. } else {
  428. WStatus = ERROR_SUCCESS;
  429. }
  430. if (!WIN_SUCCESS(WStatus)) {
  431. CHANGE_ORDER_TRACEW(3, RsCoe(Cmd), "Fetch Receiving Rename fail", WStatus);
  432. DPRINT2_WS(0, "++ Can't move fetched %ws to %ws;",
  433. StagePath, FinalPath, WStatus);
  434. FinalPath = FrsFree(FinalPath);
  435. goto ERROUT;
  436. }
  437. //
  438. // Stage file with final name is in place and ready to install
  439. // and/or deliver to our downstream partners.
  440. //
  441. SetFlag(Flags, STAGE_FLAG_CREATED | STAGE_FLAG_INSTALLING);
  442. }
  443. //
  444. // The last block isn't officially "written" into the staging file
  445. // until the above rename finishes. That is because the write of
  446. // the last byte of the staging file signifies "all done" to the
  447. // replica command server (replica.c).
  448. //
  449. RsFileOffset(Cmd).QuadPart += RsBlockSize(Cmd);
  450. //
  451. // This block has been successfully transferred; free the buffer now
  452. //
  453. FrsFree(StagePath);
  454. FrsFree(FinalPath);
  455. RsBlock(Cmd) = FrsFree(RsBlock(Cmd));
  456. RsBlockSize(Cmd) = QUADZERO;
  457. //
  458. // Release staging resources
  459. //
  460. SetFlag(Flags, STAGE_FLAG_CREATING);
  461. if (!IS_GUID_ZERO(&Header.CompressionGuid)) {
  462. StageRelease(&RsCoc(Cmd)->ChangeOrderGuid,
  463. RsCoc(Cmd)->FileName,
  464. Flags | STAGE_FLAG_COMPRESSED |
  465. STAGE_FLAG_COMPRESSION_FORMAT_KNOWN,
  466. &(RsFileOffset(Cmd).QuadPart),
  467. NULL,
  468. &Header.CompressionGuid);
  469. } else {
  470. StageRelease(&RsCoc(Cmd)->ChangeOrderGuid,
  471. RsCoc(Cmd)->FileName,
  472. Flags,
  473. &(RsFileOffset(Cmd).QuadPart),
  474. NULL,
  475. NULL);
  476. }
  477. RcsSubmitTransferToRcs(Cmd, CMD_RECEIVED_STAGE);
  478. return;
  479. ERROUT:
  480. //
  481. // Discard local state
  482. //
  483. FRS_CLOSE(Handle);
  484. FrsFree(StagePath);
  485. if (!IS_GUID_ZERO(&Header.CompressionGuid)) {
  486. StageRelease(&RsCoc(Cmd)->ChangeOrderGuid,
  487. RsCoc(Cmd)->FileName,
  488. Flags | STAGE_FLAG_COMPRESSED |
  489. STAGE_FLAG_COMPRESSION_FORMAT_KNOWN,
  490. NULL,
  491. NULL,
  492. &Header.CompressionGuid);
  493. } else {
  494. StageRelease(&RsCoc(Cmd)->ChangeOrderGuid, RsCoc(Cmd)->FileName, Flags, NULL, NULL, NULL);
  495. }
  496. //
  497. // Pretend it is retriable
  498. //
  499. CHANGE_ORDER_TRACE(3, RsCoe(Cmd), "Fetch Receiving Retry on Error");
  500. FrsFetchCsSubmitTransfer(Cmd, CMD_RETRY_FETCH);
  501. }
  502. VOID
  503. FetchCsSendStage(
  504. IN PCOMMAND_PACKET Cmd
  505. )
  506. /*++
  507. Routine Description:
  508. Send the local staging file to the requesting outbound partner.
  509. Arguments:
  510. Cmd
  511. Return Value:
  512. None.
  513. --*/
  514. {
  515. #undef DEBSUB
  516. #define DEBSUB "FetchCsSendStage:"
  517. ULONGLONG GeneratedSize = 0;
  518. FILE_NETWORK_OPEN_INFORMATION Attrs;
  519. PCHANGE_ORDER_COMMAND Coc = RsPartnerCoc(Cmd);
  520. GUID *CoGuid;
  521. PWCHAR FileName;
  522. ULONG Flags;
  523. DWORD WStatus;
  524. DWORD BytesRead;
  525. USN Usn = 0;
  526. PWCHAR StagePath = NULL;
  527. HANDLE Handle = INVALID_HANDLE_VALUE;
  528. BOOL Md5Valid = FALSE;
  529. MD5_CTX Md5;
  530. GUID CompressionFormatUsed;
  531. PREPLICA Replica = RsReplica(Cmd);
  532. PCXTION OutCxtion;
  533. STAGE_HEADER Header;
  534. BOOL DeleteStagingFile = FALSE;
  535. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Send");
  536. //
  537. // Check Connection before anything else. This is checked by the replica
  538. // command server but this command packet can hang around on the
  539. // FetchCs queue for a while. During this time the connection can
  540. // be unjoined, moved to the deleted connection table, and recreated.
  541. // The recreated connection may not be fully initialized and this
  542. // can lead to an access violation. And yes this did happen.
  543. // StuGenerateStage is an expensive call. We don't want to generate
  544. // the stage file and later discard it because the cxtion has unjoined.
  545. //
  546. OutCxtion = RcsCheckCxtion(Cmd, DEBSUB, CHECK_CXTION_FOR_FETCHCS |
  547. CHECK_CXTION_JOIN_OK |
  548. CHECK_CXTION_OUTBOUND);
  549. //
  550. // This connection does not exist any more.
  551. //
  552. if (OutCxtion == NULL) {
  553. goto ERROUT_NOACQUIRE;
  554. }
  555. ZeroMemory(&CompressionFormatUsed, sizeof(GUID));
  556. //
  557. // Even if the file is 0 bytes in length, the staging file will
  558. // always have at least the header. There are some retry paths
  559. // that will incorrectly think the staging file has been fetched
  560. // if RsFileSize(Cmd) is 0. So make sure it isn't.
  561. //
  562. if (RsFileSize(Cmd).QuadPart == QUADZERO) {
  563. RsFileSize(Cmd).QuadPart = Coc->FileSize;
  564. if (RsFileSize(Cmd).QuadPart == QUADZERO) {
  565. RsFileSize(Cmd).QuadPart = sizeof(STAGE_HEADER);
  566. }
  567. }
  568. CoGuid = &Coc->ChangeOrderGuid;
  569. FileName = Coc->FileName;
  570. //
  571. // Acquire shared access to the staging file
  572. //
  573. Flags = 0;
  574. WStatus = StageAcquire(CoGuid, FileName, RsFileSize(Cmd).QuadPart,
  575. &Flags, Replica->ReplicaNumber, &CompressionFormatUsed);
  576. if (!WIN_SUCCESS(WStatus) || !(Flags & STAGE_FLAG_CREATED)) {
  577. //
  578. // Acquire exclusive access to the file
  579. //
  580. if (WIN_SUCCESS(WStatus)) {
  581. StageRelease(CoGuid, FileName, Flags, NULL, NULL, NULL);
  582. }
  583. Flags = STAGE_FLAG_RESERVE | STAGE_FLAG_EXCLUSIVE;
  584. if (CoCmdIsDirectory(Coc)) {
  585. SetFlag(Flags, STAGE_FLAG_FORCERESERVE);
  586. }
  587. WStatus = StageAcquire(CoGuid, FileName, RsFileSize(Cmd).QuadPart,
  588. &Flags, Replica->ReplicaNumber, &CompressionFormatUsed);
  589. }
  590. //
  591. // Retry fetch when fetch retry trigger hits
  592. //
  593. PULL_FETCH_RETRY_TRIGGER(Coc, WStatus, Flags);
  594. //
  595. // Retriable problem; do so
  596. //
  597. if (WIN_RETRY_FETCH(WStatus)) {
  598. CHANGE_ORDER_COMMAND_TRACEW(3, Coc, "Fetch Send Retry Cmd", WStatus);
  599. if (FetchCsDelCsSubmit(Cmd, FALSE)) {
  600. return;
  601. }
  602. CHANGE_ORDER_COMMAND_TRACEW(3, Coc, "Fetch Send Retry Co", WStatus);
  603. RcsSubmitTransferToRcs(Cmd, CMD_SEND_RETRY_FETCH);
  604. return;
  605. }
  606. //
  607. // Unretriable problem; abort
  608. //
  609. if (!WIN_SUCCESS(WStatus)) {
  610. CHANGE_ORDER_COMMAND_TRACEW(3, Coc, "Fetch Send Abort", WStatus);
  611. RcsSubmitTransferToRcs(Cmd, CMD_SEND_ABORT_FETCH);
  612. return;
  613. }
  614. //
  615. // Create the staging file, if needed
  616. //
  617. if (!(Flags & STAGE_FLAG_CREATED)) {
  618. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Send Gen Stage");
  619. //
  620. // Make sure we start at the beginning of the staging file
  621. //
  622. RsFileOffset(Cmd).QuadPart = QUADZERO;
  623. //
  624. // Create the staging file.
  625. //
  626. if (RsMd5Digest(Cmd)) {
  627. //
  628. // The requesting downstream partner had a pre-exisitng file
  629. // and included an Md5 digest in the fetch request. So calc
  630. // the MD5 digest as we generate the staging file.
  631. //
  632. WStatus = StuGenerateStage(Coc, NULL, FALSE, &Md5, &GeneratedSize,
  633. &CompressionFormatUsed);
  634. Md5Valid = TRUE;
  635. } else {
  636. WStatus = StuGenerateStage(Coc, NULL, FALSE, NULL, &GeneratedSize,
  637. &CompressionFormatUsed);
  638. }
  639. //
  640. // Release staging resources if error
  641. //
  642. if (!WIN_SUCCESS(WStatus)) {
  643. StageDeleteFile(Coc, NULL, FALSE);
  644. StageRelease(CoGuid, FileName, STAGE_FLAG_UNRESERVE, NULL, NULL, NULL);
  645. } else {
  646. //
  647. // Increment the staging files regenerated counter
  648. //
  649. PM_INC_CTR_REPSET(Replica, SFReGenerated, 1);
  650. }
  651. //
  652. // Retriable problem; do so
  653. //
  654. if (WIN_RETRY_FETCH(WStatus)) {
  655. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Send Gen Stage Retry Cmd");
  656. if (FetchCsDelCsSubmit(Cmd, FALSE)) {
  657. return;
  658. }
  659. CHANGE_ORDER_COMMAND_TRACEW(3, Coc, "Fetch Send Gen Stage Retry Co", WStatus);
  660. RcsSubmitTransferToRcs(Cmd, CMD_SEND_RETRY_FETCH);
  661. return;
  662. }
  663. //
  664. // Unretriable problem; abort
  665. //
  666. if (!WIN_SUCCESS(WStatus)) {
  667. CHANGE_ORDER_COMMAND_TRACEW(3, Coc, "Fetch Send Gen Stage Abort", WStatus);
  668. RcsSubmitTransferToRcs(Cmd, CMD_SEND_ABORT_FETCH);
  669. return;
  670. }
  671. if (!IS_GUID_ZERO(&CompressionFormatUsed)) {
  672. SetFlag(Flags, (STAGE_FLAG_DATA_PRESENT |
  673. STAGE_FLAG_CREATED | STAGE_FLAG_INSTALLING |
  674. STAGE_FLAG_INSTALLED | STAGE_FLAG_RERESERVE |
  675. STAGE_FLAG_COMPRESSED | STAGE_FLAG_COMPRESSION_FORMAT_KNOWN));
  676. } else {
  677. SetFlag(Flags, (STAGE_FLAG_DATA_PRESENT |
  678. STAGE_FLAG_CREATED | STAGE_FLAG_INSTALLING |
  679. STAGE_FLAG_INSTALLED | STAGE_FLAG_RERESERVE));
  680. }
  681. }
  682. //
  683. // ERROUT is now valid
  684. //
  685. //
  686. // Open the file
  687. //
  688. if (COC_FLAG_ON(Coc, CO_FLAG_COMPRESSED_STAGE) && (Flags & STAGE_FLAG_COMPRESSED) ) {
  689. StagePath = StuCreStgPath(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), STAGE_FINAL_COMPRESSED_PREFIX);
  690. if (!(Flags & STAGE_FLAG_COMPRESSION_FORMAT_KNOWN)) {
  691. //
  692. // Compression format is not known and should be zero. Read from stage header.
  693. //
  694. FRS_ASSERT(IS_GUID_ZERO(&CompressionFormatUsed));
  695. WStatus = StuOpenFile(StagePath, GENERIC_READ, &Handle);
  696. if (!HANDLE_IS_VALID(Handle)) {
  697. //
  698. // If the staging file is deleted by an external agent,
  699. // it will still be in the staging table. Jumping to
  700. // ERROUT_DELETE_STAGING_FILE will cause it to be
  701. // unreserved. It will be regenerated on retry
  702. // if required.
  703. //
  704. if (WStatus == ERROR_FILE_NOT_FOUND) {
  705. goto ERROUT_DELETE_STAGING_FILE;
  706. } else{
  707. goto ERROUT;
  708. }
  709. }
  710. if (!StuReadBlockFile(StagePath, Handle, &Header, sizeof(STAGE_HEADER))) {
  711. //
  712. // Error reading from file. File may be corrupt.
  713. // Delete the staging file and unreserve the space.
  714. // Staging file will be regenerated on retry.
  715. //
  716. goto ERROUT_DELETE_STAGING_FILE;
  717. }
  718. COPY_GUID(&CompressionFormatUsed, &Header.CompressionGuid);
  719. SetFlag(Flags, STAGE_FLAG_COMPRESSED);
  720. SetFlag(Flags, STAGE_FLAG_COMPRESSION_FORMAT_KNOWN);
  721. }
  722. //
  723. // This is checked by the replica
  724. // command server but this command packet can hang around on the
  725. // FetchCs queue for a while. During this time the connection can
  726. // be unjoined, moved to the deleted connection table, and recreated.
  727. // The recreated connection may not be fully initialized and this
  728. // can lead to an access violation. And yes this did happen.
  729. // StuGenerateStage can take a long time so we have to make this
  730. // check again because the state may have changed.
  731. //
  732. OutCxtion = RcsCheckCxtion(Cmd, DEBSUB, CHECK_CXTION_FOR_FETCHCS |
  733. CHECK_CXTION_JOIN_OK |
  734. CHECK_CXTION_OUTBOUND);
  735. //
  736. // This connection does not exist any more.
  737. //
  738. if (OutCxtion == NULL) {
  739. goto ERROUT;
  740. }
  741. //
  742. // There is a compressed staging file for this change order. Check if the
  743. // outbound partner understands this compression format.
  744. //
  745. if (!GTabIsEntryPresent(OutCxtion->CompressionTable, &CompressionFormatUsed, NULL)) {
  746. //
  747. // The outbound partner does not understand this compression format.
  748. //
  749. //
  750. // Unlock the cxtion table here so we do not hold the lock while generating
  751. // the staging file.
  752. //
  753. StagePath = FrsFree(StagePath);
  754. FRS_CLOSE(Handle);
  755. StagePath = StuCreStgPath(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), STAGE_FINAL_PREFIX);
  756. if (!(Flags & STAGE_FLAG_DECOMPRESSED)) {
  757. //
  758. // The the file is not decompressed yet. Create decompressed staging file.
  759. // Acquire exclusive access to the file if we didn't get it above.
  760. // Case is Stage file exists as compressed so we don't get exclusive
  761. // access above.
  762. //
  763. if (!BooleanFlagOn(Flags, STAGE_FLAG_EXCLUSIVE)) {
  764. StageRelease(CoGuid, FileName, Flags, NULL, NULL, &CompressionFormatUsed);
  765. Flags = STAGE_FLAG_RESERVE | STAGE_FLAG_EXCLUSIVE;
  766. if (CoCmdIsDirectory(Coc)) {
  767. SetFlag(Flags, STAGE_FLAG_FORCERESERVE);
  768. }
  769. WStatus = StageAcquire(CoGuid, FileName, RsFileSize(Cmd).QuadPart,
  770. &Flags, RsReplica(Cmd)->ReplicaNumber, NULL);
  771. CLEANUP_WS(0,"Error acquiring exclusive access for creating a decompressed staging file.",
  772. WStatus, ERROUT_NOACQUIRE);
  773. }
  774. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Decompressing stage for downlevel partner");
  775. WStatus = StuGenerateDecompressedStage(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), &CompressionFormatUsed);
  776. CLEANUP_WS(0,"Error generating decompressed staging file.", WStatus, ERROUT_DELETE_STAGING_FILE);
  777. SetFlag(Flags, STAGE_FLAG_DECOMPRESSED);
  778. CLEAR_COC_FLAG(Coc, CO_FLAG_COMPRESSED_STAGE);
  779. }
  780. }
  781. } else {
  782. StagePath = StuCreStgPath(RsReplica(Cmd)->Stage, RsCoGuid(Cmd), STAGE_FINAL_PREFIX);
  783. }
  784. if (!HANDLE_IS_VALID(Handle)) {
  785. WStatus = StuOpenFile(StagePath, GENERIC_READ, &Handle);
  786. }
  787. if (!HANDLE_IS_VALID(Handle)) {
  788. //
  789. // If the staging file is deleted by an external agent,
  790. // it will still be in the staging table. Jumping to
  791. // ERROUT_DELETE_STAGING_FILE will cause it to be
  792. // unreserved. It will be regenerated on retry
  793. // if required.
  794. //
  795. if (WStatus == ERROR_FILE_NOT_FOUND) {
  796. goto ERROUT_DELETE_STAGING_FILE;
  797. } else{
  798. goto ERROUT;
  799. }
  800. }
  801. if (RsFileOffset(Cmd).QuadPart == QUADZERO) {
  802. //
  803. // This is the first request for this file; Fill in the file size
  804. //
  805. if (!FrsGetFileInfoByHandle(StagePath, Handle, &Attrs)) {
  806. goto ERROUT;
  807. }
  808. RsFileSize(Cmd) = Attrs.EndOfFile;
  809. }
  810. if (Md5Valid) {
  811. if (MD5_EQUAL(Md5.digest, RsMd5Digest(Cmd))) {
  812. //
  813. // MD5 digest matches so downstream partner's file is good.
  814. // Set the offset to the size of the stage file so we don't send
  815. // any data.
  816. //
  817. RsFileOffset(Cmd).QuadPart = RsFileSize(Cmd).QuadPart;
  818. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Send Md5 matches, do not send");
  819. } else {
  820. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Send Md5 mismatch, send");
  821. //
  822. // Update the MD5 checksum in the cmd so we can send it downstream.
  823. //
  824. CopyMemory(RsMd5Digest(Cmd), Md5.digest, MD5DIGESTLEN);
  825. }
  826. }
  827. //
  828. // Calculate the block size of the next data chunk to deliver.
  829. //
  830. RsBlockSize(Cmd) = QUADZERO;
  831. if (RsFileOffset(Cmd).QuadPart < RsFileSize(Cmd).QuadPart) {
  832. //
  833. // Calc bytes left in file.
  834. //
  835. RsBlockSize(Cmd) = RsFileSize(Cmd).QuadPart - RsFileOffset(Cmd).QuadPart;
  836. //
  837. // But not more than max block size.
  838. //
  839. if (RsBlockSize(Cmd) > FETCHCS_MAX_BLOCK_SIZE) {
  840. RsBlockSize(Cmd) = FETCHCS_MAX_BLOCK_SIZE;
  841. }
  842. }
  843. //
  844. // If data left to deliver, allocate a buffer, seek to the block offset in
  845. // the file and read the data. If we fail to get the block of interest then
  846. // we error out by deleting the staging file.
  847. //
  848. RsBlock(Cmd) = NULL;
  849. if (RsBlockSize(Cmd) > QUADZERO) {
  850. RsBlock(Cmd) = FrsAlloc((ULONG)RsBlockSize(Cmd));
  851. WStatus = FrsSetFilePointer(StagePath, Handle, RsFileOffset(Cmd).HighPart,
  852. RsFileOffset(Cmd).LowPart);
  853. CLEANUP1_WS(0, "++ SetFilePointer failed on %ws;", StagePath, WStatus, ERROUT_DELETE_STAGING_FILE);
  854. if (!StuReadBlockFile(StagePath, Handle, RsBlock(Cmd), (ULONG)RsBlockSize(Cmd))) {
  855. //
  856. // Error reading from file. File may be corrupt.
  857. // Delete the staging file and unreserve the space.
  858. // Staging file will be regenerated on retry.
  859. //
  860. goto ERROUT_DELETE_STAGING_FILE;
  861. }
  862. }
  863. //
  864. // Done, transfer to the replica set command server
  865. //
  866. FRS_CLOSE(Handle);
  867. FrsFree(StagePath);
  868. if (!IS_GUID_ZERO(&CompressionFormatUsed)) {
  869. StageRelease(CoGuid, FileName, Flags, &GeneratedSize, NULL, &CompressionFormatUsed);
  870. } else {
  871. StageRelease(CoGuid, FileName, Flags, &GeneratedSize, NULL, NULL);
  872. }
  873. RcsSubmitTransferToRcs(Cmd, CMD_SENDING_STAGE);
  874. return;
  875. ERROUT_DELETE_STAGING_FILE:
  876. //
  877. // Delete the staging file if we reach here. If we have successfully
  878. // generated a staging file then we try to keep it.
  879. //
  880. DeleteStagingFile = TRUE;
  881. ERROUT:
  882. //
  883. // Delete the staging file, if possible. Don't delete a staging
  884. // file that has not been installed (it cannot be regenerated!).
  885. //
  886. if (DeleteStagingFile && (Flags & STAGE_FLAG_INSTALLED)) {
  887. //
  888. // Get exclusive access
  889. //
  890. WStatus = ERROR_SUCCESS;
  891. if (!(Flags & STAGE_FLAG_EXCLUSIVE)) {
  892. StageRelease(CoGuid, FileName, Flags, &GeneratedSize, NULL, NULL);
  893. Flags = STAGE_FLAG_RESERVE | STAGE_FLAG_EXCLUSIVE;
  894. if (CoCmdIsDirectory(Coc)) {
  895. SetFlag(Flags, STAGE_FLAG_FORCERESERVE);
  896. }
  897. WStatus = StageAcquire(CoGuid, FileName, Coc->FileSize, &Flags, RsReplica(Cmd)->ReplicaNumber, NULL);
  898. }
  899. if (WIN_SUCCESS(WStatus)) {
  900. //
  901. // Discard the current staging file
  902. //
  903. StageDeleteFile(Coc, NULL, FALSE);
  904. StageRelease(CoGuid, FileName, STAGE_FLAG_UNRESERVE, NULL, NULL, NULL);
  905. //
  906. // Make sure we start over at the beginning of the staging file
  907. //
  908. RsFileOffset(Cmd).QuadPart = QUADZERO;
  909. }
  910. } else {
  911. StageRelease(CoGuid, FileName, Flags, &GeneratedSize, NULL, NULL);
  912. }
  913. ERROUT_NOACQUIRE:
  914. FRS_CLOSE(Handle);
  915. if (StagePath) {
  916. FrsFree(StagePath);
  917. }
  918. RsBlock(Cmd) = FrsFree(RsBlock(Cmd));
  919. RsBlockSize(Cmd) = QUADZERO;
  920. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Send Retry on Error");
  921. if (FetchCsDelCsSubmit(Cmd, FALSE)) {
  922. return;
  923. }
  924. CHANGE_ORDER_COMMAND_TRACE(3, Coc, "Fetch Send Retry on Error");
  925. RcsSubmitTransferToRcs(Cmd, CMD_SEND_RETRY_FETCH);
  926. }
  927. DWORD
  928. MainFetchCs(
  929. PVOID Arg
  930. )
  931. /*++
  932. Routine Description:
  933. Entry point for a thread serving the Staging area Command Server.
  934. Arguments:
  935. Arg - thread
  936. Return Value:
  937. None.
  938. --*/
  939. {
  940. #undef DEBSUB
  941. #define DEBSUB "MainFetchCs:"
  942. DWORD WStatus = ERROR_SUCCESS;
  943. PCOMMAND_PACKET Cmd;
  944. PFRS_THREAD FrsThread = (PFRS_THREAD)Arg;
  945. //
  946. // Thread is pointing at the correct command server
  947. //
  948. FRS_ASSERT(FrsThread->Data == &FetchCs);
  949. FrsThread->Exit = ThSupExitWithTombstone;
  950. //
  951. // Try-Finally
  952. //
  953. try {
  954. //
  955. // Capture exception.
  956. //
  957. try {
  958. //
  959. // Pull entries off the queue and process them
  960. //
  961. cant_exit_yet:
  962. while (Cmd = FrsGetCommandServer(&FetchCs)) {
  963. switch (Cmd->Command) {
  964. case CMD_SEND_STAGE:
  965. DPRINT1(5, "Fetch: command send stage %08x\n", Cmd);
  966. FetchCsSendStage(Cmd);
  967. break;
  968. case CMD_RECEIVING_STAGE:
  969. DPRINT1(5, "Fetch: command receiving stage %08x\n", Cmd);
  970. FetchCsReceivingStage(Cmd);
  971. break;
  972. case CMD_RETRY_FETCH:
  973. DPRINT1(5, "Fetch: command retry fetch %08x\n", Cmd);
  974. FetchCsRetryFetch(Cmd);
  975. break;
  976. case CMD_ABORT_FETCH:
  977. DPRINT1(5, "Fetch: command abort fetch %08x\n", Cmd);
  978. CHANGE_ORDER_TRACEW(0, RsCoe(Cmd), "Aborting fetch", ERROR_SUCCESS);
  979. FetchCsAbortFetch(Cmd, ERROR_SUCCESS);
  980. break;
  981. default:
  982. DPRINT1(0, "Staging File Fetch: unknown command 0x%x\n", Cmd->Command);
  983. FrsCompleteCommand(Cmd, ERROR_INVALID_FUNCTION);
  984. break;
  985. }
  986. }
  987. //
  988. // Exit
  989. //
  990. FrsExitCommandServer(&FetchCs, FrsThread);
  991. goto cant_exit_yet;
  992. //
  993. // Get exception status.
  994. //
  995. } except (EXCEPTION_EXECUTE_HANDLER) {
  996. GET_EXCEPTION_CODE(WStatus);
  997. }
  998. } finally {
  999. if (WIN_SUCCESS(WStatus)) {
  1000. if (AbnormalTermination()) {
  1001. WStatus = ERROR_OPERATION_ABORTED;
  1002. }
  1003. }
  1004. DPRINT_WS(0, "MainFetchCs finally.", WStatus);
  1005. //
  1006. // Trigger FRS shutdown if we terminated abnormally.
  1007. //
  1008. if (!WIN_SUCCESS(WStatus)) {
  1009. DPRINT(0, "MainFetchCs terminated abnormally, forcing service shutdown.\n");
  1010. FrsIsShuttingDown = TRUE;
  1011. SetEvent(ShutDownEvent);
  1012. }
  1013. }
  1014. return (WStatus);
  1015. }
  1016. VOID
  1017. FrsFetchCsInitialize(
  1018. VOID
  1019. )
  1020. /*++
  1021. Routine Description:
  1022. Initialize the staging file fetcher
  1023. Arguments:
  1024. None.
  1025. Return Value:
  1026. None.
  1027. --*/
  1028. {
  1029. #undef DEBSUB
  1030. #define DEBSUB "FetchCsInitialize:"
  1031. //
  1032. // Initialize the command server
  1033. //
  1034. CfgRegReadDWord(FKC_MAX_STAGE_FETCHCS_THREADS, NULL, 0, &MaxFetchCsThreads);
  1035. FrsInitializeCommandServer(&FetchCs, MaxFetchCsThreads, L"FetchCs", MainFetchCs);
  1036. }
  1037. VOID
  1038. ShutDownFetchCs(
  1039. VOID
  1040. )
  1041. /*++
  1042. Routine Description:
  1043. Shutdown the staging file fetcher command server.
  1044. Arguments:
  1045. None.
  1046. Return Value:
  1047. None.
  1048. --*/
  1049. {
  1050. #undef DEBSUB
  1051. #define DEBSUB "ShutDownFetchCs:"
  1052. FrsRunDownCommandServer(&FetchCs, &FetchCs.Queue);
  1053. }
  1054. VOID
  1055. FrsFetchCsSubmitTransfer(
  1056. IN PCOMMAND_PACKET Cmd,
  1057. IN USHORT Command
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. Transfer a request to the staging file generator
  1062. Arguments:
  1063. Cmd
  1064. Return Value:
  1065. None.
  1066. --*/
  1067. {
  1068. #undef DEBSUB
  1069. #define DEBSUB "FrsFetchCsSubmitTransfer:"
  1070. //
  1071. // Submit a request to allocate staging area
  1072. //
  1073. Cmd->TargetQueue = &FetchCs.Queue;
  1074. Cmd->Command = Command;
  1075. RsTimeout(Cmd) = 0;
  1076. DPRINT1(5, "Fetch: submit 0x%x\n", Cmd);
  1077. FrsSubmitCommandServer(&FetchCs, Cmd);
  1078. }