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.

2515 lines
75 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. vvjoin.c
  5. Abstract:
  6. Our outbound partner requested a join but the outbound log does not
  7. contain the change orders needed to bring our outbound partner up-to-
  8. date. Instead, this thread will scan our idtable and generate
  9. refresh change orders for our outbound partner. This process is
  10. termed a vvjoin because the outbound partner's version vector is
  11. used as a filter when deciding which files and directories in our
  12. idtable will be sent.
  13. Once the vvjoin is completed, our outbound partner will regenerate
  14. his version vector and attempt the join again with the outbound log
  15. sequence number saved at the beginning of the vvjoin. If that sequence
  16. number is no longer available in the oubound log, the vvjoin is
  17. repeated with, hopefully, fewer files being sent to our outbound
  18. partner.
  19. Author:
  20. Billy J Fuller 27-Jan-1998
  21. Environment
  22. User mode, winnt32
  23. --*/
  24. #include <ntreppch.h>
  25. #pragma hdrstop
  26. #undef DEBSUB
  27. #define DEBSUB "vvjoin:"
  28. #include <frs.h>
  29. #include <tablefcn.h>
  30. #include <perrepsr.h>
  31. #if DBG
  32. DWORD VvJoinTrigger = 0;
  33. DWORD VvJoinReset = 0;
  34. DWORD VvJoinInc = 0;
  35. #define VV_JOIN_TRIGGER(_VvJoinContext_) \
  36. { \
  37. if (VvJoinTrigger && !--VvJoinTrigger) { \
  38. VvJoinReset += VvJoinInc; \
  39. VvJoinTrigger = VvJoinReset; \
  40. DPRINT1(0, ":V: DEBUG -- VvJoin Trigger HIT; reset to %d\n", VvJoinTrigger); \
  41. *((DWORD *)&_VvJoinContext_->JoinGuid) += 1; \
  42. } \
  43. }
  44. #else DBG
  45. #define VV_JOIN_TRIGGER(_VvJoinContext_)
  46. #endif DBG
  47. #if DBG
  48. #define VVJOIN_TEST() VvJoinTest()
  49. #define VVJOIN_PRINT(_S_, _VC_) VvJoinPrint(_S_, _VC_)
  50. #define VVJOIN_PRINT_NODE(_S_, _I_, _N_) VvJoinPrintNode(_S_, _I_, _N_)
  51. #define VVJOIN_TEST_SKIP_BEGIN(_X_, _C_) VvJoinTestSkipBegin(_X_, _C_)
  52. #define VVJOIN_TEST_SKIP_CHECK(_X, _F, _B_) VvJoinTestSkipCheck(_X, _F, _B_)
  53. #define VVJOIN_TEST_SKIP_END(_X_) VvJoinTestSkipEnd(_X_)
  54. #define VVJOIN_TEST_SKIP(_C_) VvJoinTestSkip(_C_)
  55. #else DBG
  56. #define VVJOIN_TEST()
  57. #define VVJOIN_PRINT(_S_, _VC_)
  58. #define VVJOIN_PRINT_NODE(_S_, _I_, _N_)
  59. #define VVJOIN_TEST_SKIP_BEGIN(_X_, _C_)
  60. #define VVJOIN_TEST_SKIP_CHECK(_X_, _F_, _B_)
  61. #define VVJOIN_TEST_SKIP_END(_X_)
  62. #endif
  63. //
  64. // Context global to a vvjoin thread
  65. //
  66. typedef struct _VVJOIN_CONTEXT {
  67. PRTL_GENERIC_TABLE Guids; // all nodes by guid
  68. PRTL_GENERIC_TABLE Originators; // originator nodes by originator
  69. DWORD (*Send)();
  70. // IN PVVJOIN_CONTEXT VvJoinContext,
  71. // IN PVVJOIN_NODE VvJoinNode);
  72. DWORD NumberSent;
  73. PREPLICA Replica;
  74. PCXTION Cxtion;
  75. PGEN_TABLE CxtionVv;
  76. PGEN_TABLE ReplicaVv;
  77. PTHREAD_CTX ThreadCtx;
  78. PTABLE_CTX TableCtx;
  79. GUID JoinGuid;
  80. LONG MaxOutstandingCos;
  81. LONG OutstandingCos;
  82. LONG OlTimeout;
  83. LONG OlTimeoutMax;
  84. PWCHAR SkipDir;
  85. PWCHAR SkipFile1;
  86. PWCHAR SkipFile2;
  87. BOOL SkippedDir;
  88. BOOL SkippedFile1;
  89. BOOL SkippedFile2;
  90. } VVJOIN_CONTEXT, *PVVJOIN_CONTEXT;
  91. //
  92. // One node per file from the idtable
  93. //
  94. typedef struct _VVJOIN_NODE {
  95. ULONG Flags;
  96. GUID FileGuid;
  97. GUID ParentGuid;
  98. GUID Originator;
  99. ULONGLONG Vsn;
  100. PRTL_GENERIC_TABLE Vsns;
  101. } VVJOIN_NODE, *PVVJOIN_NODE;
  102. //
  103. // Maximum timeout (unless overridden by registry)
  104. //
  105. #define VVJOIN_TIMEOUT_MAX (180 * 1000)
  106. //
  107. // Flags for VVJOIN_NODE
  108. //
  109. #define VVJOIN_FLAGS_ISDIR 0x00000001
  110. #define VVJOIN_FLAGS_SENT 0x00000002
  111. #define VVJOIN_FLAGS_ROOT 0x00000004
  112. #define VVJOIN_FLAGS_PROCESSING 0x00000008
  113. #define VVJOIN_FLAGS_OUT_OF_ORDER 0x00000010
  114. #define VVJOIN_FLAGS_DELETED 0x00000020
  115. //
  116. // Maximum number of vvjoin threads PER CXTION
  117. //
  118. #define VVJOIN_MAXTHREADS_PER_CXTION (1)
  119. //
  120. // Next entry in any gen table (don't splay)
  121. //
  122. #define VVJOIN_NEXT_ENTRY(_T_, _K_) \
  123. (PVOID)RtlEnumerateGenericTableWithoutSplaying(_T_, _K_)
  124. PCHANGE_ORDER_ENTRY
  125. ChgOrdMakeFromIDRecord(
  126. IN PIDTABLE_RECORD IDTableRec,
  127. IN PREPLICA Replica,
  128. IN ULONG LocationCmd,
  129. IN ULONG CoFlags,
  130. IN GUID *CxtionGuid
  131. );
  132. PVOID
  133. VvJoinAlloc(
  134. IN PRTL_GENERIC_TABLE Table,
  135. IN DWORD NodeSize
  136. )
  137. /*++
  138. Routine Description:
  139. Allocate space for a table entry. The entry includes the user-defined
  140. struct and some overhead used by the generic table routines. The
  141. generic table routines call this function when they need memory.
  142. Arguments:
  143. Table - Address of the table (not used).
  144. NodeSize - Bytes to allocate
  145. Return Value:
  146. Address of newly allocated memory.
  147. --*/
  148. {
  149. #undef DEBSUB
  150. #define DEBSUB "VvJoinAlloc:"
  151. //
  152. // Need to check if NodeSize == 0 as FrsAlloc asserts if called with 0 as the first parameter (prefix fix).
  153. //
  154. if (NodeSize == 0) {
  155. return NULL;
  156. }
  157. return FrsAlloc(NodeSize);
  158. }
  159. VOID
  160. VvJoinFree(
  161. IN PRTL_GENERIC_TABLE Table,
  162. IN PVOID Buffer
  163. )
  164. /*++
  165. Routine Description:
  166. Free the space allocated by VvJoinAlloc(). The generic table
  167. routines call this function to free memory.
  168. Arguments:
  169. Table - Address of the table (not used).
  170. Buffer - Address of previously allocated memory
  171. Return Value:
  172. None.
  173. --*/
  174. {
  175. #undef DEBSUB
  176. #define DEBSUB "VvJoinFree:"
  177. FrsFree(Buffer);
  178. }
  179. PVOID
  180. VvJoinFreeContext(
  181. IN PVVJOIN_CONTEXT VvJoinContext
  182. )
  183. /*++
  184. Routine Description:
  185. Free the context and its contents
  186. Arguments:
  187. VvJoinContext
  188. Return Value:
  189. None.
  190. --*/
  191. {
  192. #undef DEBSUB
  193. #define DEBSUB "VvJoinFreeContext:"
  194. JET_ERR jerr;
  195. PVVJOIN_NODE VvJoinNode;
  196. PVVJOIN_NODE *Entry;
  197. PVVJOIN_NODE *SubEntry;
  198. //
  199. // Done
  200. //
  201. if (!VvJoinContext) {
  202. return NULL;
  203. }
  204. //
  205. // Free the entries in the generic table of originators. The nodes
  206. // addressed by the entries are freed below.
  207. //
  208. if (VvJoinContext->Originators) {
  209. while (Entry = RtlEnumerateGenericTable(VvJoinContext->Originators, TRUE)) {
  210. VvJoinNode = *Entry;
  211. if (VvJoinNode->Vsns) {
  212. while (SubEntry = RtlEnumerateGenericTable(VvJoinNode->Vsns, TRUE)) {
  213. RtlDeleteElementGenericTable(VvJoinNode->Vsns, SubEntry);
  214. }
  215. VvJoinNode->Vsns = FrsFree(VvJoinNode->Vsns);
  216. }
  217. RtlDeleteElementGenericTable(VvJoinContext->Originators, Entry);
  218. }
  219. VvJoinContext->Originators = FrsFree(VvJoinContext->Originators);
  220. }
  221. //
  222. // Free the entries in the generic table of files. The nodes are freed,
  223. // too, because no other table addresses them.
  224. //
  225. if (VvJoinContext->Guids) {
  226. while (Entry = RtlEnumerateGenericTable(VvJoinContext->Guids, TRUE)) {
  227. VvJoinNode = *Entry;
  228. RtlDeleteElementGenericTable(VvJoinContext->Guids, Entry);
  229. FrsFree(VvJoinNode);
  230. }
  231. VvJoinContext->Guids = FrsFree(VvJoinContext->Guids);
  232. }
  233. //
  234. // Free the version vector and the cxtion guid.
  235. //
  236. VVFreeOutbound(VvJoinContext->CxtionVv);
  237. VVFreeOutbound(VvJoinContext->ReplicaVv);
  238. //
  239. // Jet table context
  240. //
  241. if (VvJoinContext->TableCtx) {
  242. DbsFreeTableContext(VvJoinContext->TableCtx,
  243. VvJoinContext->ThreadCtx->JSesid);
  244. }
  245. //
  246. // Now close the jet session and free the Jet ThreadCtx.
  247. //
  248. if (VvJoinContext->ThreadCtx) {
  249. jerr = DbsCloseJetSession(VvJoinContext->ThreadCtx);
  250. if (!JET_SUCCESS(jerr)) {
  251. DPRINT_JS(1,":V: DbsCloseJetSession : ", jerr);
  252. } else {
  253. DPRINT(4, ":V: DbsCloseJetSession complete\n");
  254. }
  255. VvJoinContext->ThreadCtx = FrsFreeType(VvJoinContext->ThreadCtx);
  256. }
  257. //
  258. // Free the context
  259. //
  260. FrsFree(VvJoinContext);
  261. //
  262. // DONE
  263. //
  264. return NULL;
  265. }
  266. RTL_GENERIC_COMPARE_RESULTS
  267. VvJoinCmpGuids(
  268. IN PRTL_GENERIC_TABLE Guids,
  269. IN PVVJOIN_NODE *VvJoinNode1,
  270. IN PVVJOIN_NODE *VvJoinNode2
  271. )
  272. /*++
  273. Routine Description:
  274. Compare the Guids of the two entries.
  275. This function is used by the gentable runtime to compare entries.
  276. In this case, each entry in the table addresses a node.
  277. Arguments:
  278. Guids - Sorted by Guid
  279. VvJoinNode1 - PVVJOIN_NODE
  280. VvJoinNode2 - PVVJOIN_NODE
  281. Return Value:
  282. <0 First < Second
  283. =0 First == Second
  284. >0 First > Second
  285. --*/
  286. {
  287. #undef DEBSUB
  288. #define DEBSUB "VvJoinCmpGuids:"
  289. INT Cmp;
  290. FRS_ASSERT(VvJoinNode1 && VvJoinNode2 && *VvJoinNode1 && *VvJoinNode2);
  291. Cmp = memcmp(&(*VvJoinNode1)->FileGuid,
  292. &(*VvJoinNode2)->FileGuid,
  293. sizeof(GUID));
  294. if (Cmp < 0) {
  295. return GenericLessThan;
  296. } else if (Cmp > 0) {
  297. return GenericGreaterThan;
  298. }
  299. return GenericEqual;
  300. }
  301. RTL_GENERIC_COMPARE_RESULTS
  302. VvJoinCmpVsns(
  303. IN PRTL_GENERIC_TABLE Vsns,
  304. IN PVVJOIN_NODE *VvJoinNode1,
  305. IN PVVJOIN_NODE *VvJoinNode2
  306. )
  307. /*++
  308. Routine Description:
  309. Compare the vsns of the two entries as unsigned values.
  310. This function is used by the gentable runtime to compare entries.
  311. In this case, each entry in the table addresses a node.
  312. Arguments:
  313. Vsns - Sorted by Vsn
  314. VvJoinNode1 - PVVJOIN_NODE
  315. VvJoinNode2 - PVVJOIN_NODE
  316. Return Value:
  317. <0 First < Second
  318. =0 First == Second
  319. >0 First > Second
  320. --*/
  321. {
  322. #undef DEBSUB
  323. #define DEBSUB "VvJoinCmpVsns:"
  324. INT Cmp;
  325. FRS_ASSERT(VvJoinNode1 && VvJoinNode2 && *VvJoinNode1 && *VvJoinNode2);
  326. if ((ULONGLONG)(*VvJoinNode1)->Vsn > (ULONGLONG)(*VvJoinNode2)->Vsn) {
  327. return GenericGreaterThan;
  328. } else if ((ULONGLONG)(*VvJoinNode1)->Vsn < (ULONGLONG)(*VvJoinNode2)->Vsn) {
  329. return GenericLessThan;
  330. }
  331. return GenericEqual;
  332. }
  333. RTL_GENERIC_COMPARE_RESULTS
  334. VvJoinCmpOriginators(
  335. IN PRTL_GENERIC_TABLE Originators,
  336. IN PVVJOIN_NODE *VvJoinNode1,
  337. IN PVVJOIN_NODE *VvJoinNode2
  338. )
  339. /*++
  340. Routine Description:
  341. Compare the originators ID of the two entries.
  342. This function is used by the gentable runtime to compare entries.
  343. In this case, each entry in the table addresses a node.
  344. Arguments:
  345. Originators - Sorted by Guid
  346. VvJoinNode1 - PVVJOIN_NODE
  347. VvJoinNode2 - PVVJOIN_NODE
  348. Return Value:
  349. <0 First < Second
  350. =0 First == Second
  351. >0 First > Second
  352. --*/
  353. {
  354. #undef DEBSUB
  355. #define DEBSUB "VvJoinCmpOriginators:"
  356. INT Cmp;
  357. FRS_ASSERT(VvJoinNode1 && VvJoinNode2 && *VvJoinNode1 && *VvJoinNode2);
  358. Cmp = memcmp(&(*VvJoinNode1)->Originator, &(*VvJoinNode2)->Originator, sizeof(GUID));
  359. if (Cmp < 0) {
  360. return GenericLessThan;
  361. } else if (Cmp > 0) {
  362. return GenericGreaterThan;
  363. }
  364. return GenericEqual;
  365. }
  366. DWORD
  367. VvJoinInsertEntry(
  368. IN PVVJOIN_CONTEXT VvJoinContext,
  369. IN DWORD Flags,
  370. IN GUID *FileGuid,
  371. IN GUID *ParentGuid,
  372. IN GUID *Originator,
  373. IN PULONGLONG Vsn
  374. )
  375. /*++
  376. Routine Description:
  377. Insert the entry into the table of files (Guids) and the
  378. table of originators (Originators). This function is called
  379. during the IDTable scan.
  380. Arguments:
  381. VvJoinContext
  382. Flags
  383. FileGuid
  384. ParentGuid
  385. Originator
  386. Vsn
  387. Thread Return Value:
  388. Win32 Status
  389. --*/
  390. {
  391. #undef DEBSUB
  392. #define DEBSUB "VvJoinInsertEntry:"
  393. PVVJOIN_NODE VvJoinNode;
  394. PVVJOIN_NODE VvJoinOriginators;
  395. PVVJOIN_NODE *Entry;
  396. BOOLEAN IsNew;
  397. //
  398. // First call, allocate the table of files
  399. //
  400. if (!VvJoinContext->Guids) {
  401. VvJoinContext->Guids = FrsAlloc(sizeof(RTL_GENERIC_TABLE));
  402. RtlInitializeGenericTable(VvJoinContext->Guids,
  403. VvJoinCmpGuids,
  404. VvJoinAlloc,
  405. VvJoinFree,
  406. NULL);
  407. }
  408. //
  409. // First call, allocate the table of originators
  410. //
  411. if (!VvJoinContext->Originators) {
  412. VvJoinContext->Originators = FrsAlloc(sizeof(RTL_GENERIC_TABLE));
  413. RtlInitializeGenericTable(VvJoinContext->Originators,
  414. VvJoinCmpOriginators,
  415. VvJoinAlloc,
  416. VvJoinFree,
  417. NULL);
  418. }
  419. //
  420. // One node per file
  421. //
  422. VvJoinNode = FrsAlloc(sizeof(VVJOIN_NODE));
  423. VvJoinNode->FileGuid = *FileGuid;
  424. VvJoinNode->ParentGuid = *ParentGuid;
  425. VvJoinNode->Originator = *Originator;
  426. VvJoinNode->Vsn = *Vsn;
  427. VvJoinNode->Flags = Flags;
  428. //
  429. // Insert into the table of files
  430. //
  431. RtlInsertElementGenericTable(VvJoinContext->Guids,
  432. &VvJoinNode,
  433. sizeof(PVVJOIN_NODE),
  434. &IsNew);
  435. //
  436. // Duplicate Guids! The IDTable must be corrupt. Give up.
  437. //
  438. if (!IsNew) {
  439. return ERROR_DUP_NAME;
  440. }
  441. //
  442. // Must be the root of the replicated tree. The root directory
  443. // isn't replicated but is included in the table to make the
  444. // code for "directory scans" cleaner.
  445. //
  446. if (Flags & VVJOIN_FLAGS_SENT) {
  447. return ERROR_SUCCESS;
  448. }
  449. //
  450. // The table of originators is used to order the files when
  451. // sending them to our outbound partner.
  452. //
  453. // The table is really a table of tables. The first table
  454. // is indexed by originator, and the second by vsn.
  455. //
  456. Entry = RtlInsertElementGenericTable(VvJoinContext->Originators,
  457. &VvJoinNode,
  458. sizeof(PVVJOIN_NODE),
  459. &IsNew);
  460. VvJoinOriginators = *Entry;
  461. FRS_ASSERT((IsNew && !VvJoinOriginators->Vsns) ||
  462. (!IsNew && VvJoinOriginators->Vsns));
  463. if (!VvJoinOriginators->Vsns) {
  464. VvJoinOriginators->Vsns = FrsAlloc(sizeof(RTL_GENERIC_TABLE));
  465. RtlInitializeGenericTable(VvJoinOriginators->Vsns,
  466. VvJoinCmpVsns,
  467. VvJoinAlloc,
  468. VvJoinFree,
  469. NULL);
  470. }
  471. RtlInsertElementGenericTable(VvJoinOriginators->Vsns,
  472. &VvJoinNode,
  473. sizeof(PVVJOIN_NODE),
  474. &IsNew);
  475. //
  476. // Every vsn should be unique. IDTable must be corrupt. give up
  477. //
  478. if (!IsNew) {
  479. return ERROR_DUP_NAME;
  480. }
  481. return ERROR_SUCCESS;
  482. }
  483. #if DBG
  484. VOID
  485. VvJoinPrintNode(
  486. ULONG Sev,
  487. PWCHAR Indent,
  488. PVVJOIN_NODE VvJoinNode
  489. )
  490. /*++
  491. Routine Description:
  492. Print a node
  493. Arguments:
  494. Indent
  495. VvJoinNode
  496. Thread Return Value:
  497. None.
  498. --*/
  499. {
  500. #undef DEBSUB
  501. #define DEBSUB "VvJoinPrintNode:"
  502. CHAR Originator[GUID_CHAR_LEN];
  503. CHAR FileGuidA[GUID_CHAR_LEN];
  504. CHAR ParentGuidA[GUID_CHAR_LEN];
  505. GuidToStr(&VvJoinNode->Originator, Originator);
  506. GuidToStr(&VvJoinNode->FileGuid, FileGuidA);
  507. GuidToStr(&VvJoinNode->ParentGuid, ParentGuidA);
  508. DPRINT2(Sev, ":V: %wsNode: %08x\n", Indent, VvJoinNode);
  509. DPRINT2(Sev, ":V: %ws\tFlags : %08x\n", Indent, VvJoinNode->Flags);
  510. DPRINT2(Sev, ":V: %ws\tFileGuid : %s\n", Indent, FileGuidA);
  511. DPRINT2(Sev, ":V: %ws\tParentGuid: %s\n", Indent, ParentGuidA);
  512. DPRINT2(Sev, ":V: %ws\tOriginator: %s\n", Indent, Originator);
  513. DPRINT2(Sev, ":V: %ws\tVsn : %08x %08x\n", Indent, PRINTQUAD(VvJoinNode->Vsn));
  514. DPRINT2(Sev, ":V: %ws\tVsns : %08x\n", Indent, VvJoinNode->Vsns);
  515. }
  516. VOID
  517. VvJoinPrint(
  518. ULONG Sev,
  519. PVVJOIN_CONTEXT VvJoinContext
  520. )
  521. /*++
  522. Routine Description:
  523. Print the tables
  524. Arguments:
  525. VvJoinContext
  526. Thread Return Value:
  527. None.
  528. --*/
  529. {
  530. #undef DEBSUB
  531. #define DEBSUB "VvJoinPrint:"
  532. PVOID Key;
  533. PVOID SubKey;
  534. PVVJOIN_NODE *Entry;
  535. PVVJOIN_NODE VvJoinNode;
  536. DPRINT1(Sev, ":V: >>>>> %s\n", DEBSUB);
  537. DPRINT(Sev, "\n");
  538. DPRINT(Sev, ":V: GUIDS\n");
  539. if (VvJoinContext->Guids) {
  540. Key = NULL;
  541. while (Entry = VVJOIN_NEXT_ENTRY(VvJoinContext->Guids, &Key)) {
  542. VvJoinPrintNode(Sev, L"", *Entry);
  543. }
  544. }
  545. DPRINT(Sev, "\n");
  546. DPRINT(Sev, ":V: ORIGINATORS\n");
  547. if (VvJoinContext->Originators) {
  548. Key = NULL;
  549. while (Entry = VVJOIN_NEXT_ENTRY(VvJoinContext->Originators, &Key)) {
  550. VvJoinPrintNode(Sev, L"", *Entry);
  551. VvJoinNode = *Entry;
  552. DPRINT(Sev, "\n");
  553. DPRINT(Sev, ":V: \tVSNS\n");
  554. if (VvJoinNode->Vsns) {
  555. SubKey = NULL;
  556. while (Entry = VVJOIN_NEXT_ENTRY(VvJoinNode->Vsns, &SubKey)) {
  557. VvJoinPrintNode(Sev, L"\t", *Entry);
  558. }
  559. }
  560. }
  561. }
  562. }
  563. //
  564. // Used to test the code that sends files
  565. //
  566. VVJOIN_NODE TestNodes[] = {
  567. // Flags FileGuid ParentGuid Originator Vsn Vsns
  568. 7, 1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,2,3,4,5,6,7,8, 9, NULL,
  569. 1, 0,0,0,0,0,0,0,0,0,0,2, 1,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,2,3,4,5,6,7,8, 39, NULL,
  570. 1, 0,0,0,0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0,0,0,2, 0,0,0,1,2,3,4,5,6,7,8, 29, NULL,
  571. 1, 0,0,0,0,0,0,0,0,0,0,12, 1,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,2,3,4,5,6,7,9, 39, NULL,
  572. 1, 0,0,0,0,0,0,0,0,0,0,11, 0,0,0,0,0,0,0,0,0,0,12, 0,0,0,1,2,3,4,5,6,7,9, 29, NULL,
  573. 1, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,0,0,0,0,0,0,0,96, 0,0,0,1,2,3,4,5,6,7,7, 39, NULL,
  574. 1, 0,0,0,0,0,0,0,0,0,0,21, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,7, 29, NULL,
  575. 0, 0,0,0,0,0,0,0,0,0,0,31, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,7, 49, NULL,
  576. 0, 0,0,0,0,0,0,0,0,0,0,41, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,8, 49, NULL,
  577. 0, 0,0,0,0,0,0,0,0,0,0,51, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,9, 49, NULL,
  578. 0, 0,0,0,0,0,0,0,0,0,0,61, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,7, 9, NULL,
  579. 0, 0,0,0,0,0,0,0,0,0,0,71, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,8, 9, NULL,
  580. 0, 0,0,0,0,0,0,0,0,0,0,81, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,9, 9, NULL,
  581. 0, 0,0,0,0,0,0,0,0,0,0,91, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,9, 10, NULL,
  582. 0, 0,0,0,0,0,0,0,0,0,0,92, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,9, 11, NULL,
  583. 0, 0,0,0,0,0,0,0,0,0,0,93, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,9, 12, NULL,
  584. 0, 0,0,0,0,0,0,0,0,0,0,94, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,9, 13, NULL,
  585. 0, 0,0,0,0,0,0,0,0,0,0,95, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,1,2,3,4,5,6,7,9, 14, NULL,
  586. 1, 0,0,0,0,0,0,0,0,0,0,96, 1,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,2,3,4,5,6,7,9, 99, NULL,
  587. };
  588. //
  589. // Expected send order of the above array
  590. //
  591. VVJOIN_NODE TestNodesExpected[] = {
  592. // Flags FileGuid ParentGuid Originator Vsn Vsns
  593. 0x19, 0,0,0,0,0,0,0,0,0,0,96, 1,0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 99, NULL,
  594. 0x19, 0,0,0,0,0,0,0,0,0,0,22, 0,0,0,0,0,0,0,0,0,0,96, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 39, NULL,
  595. 0, 0,0,0,0,0,0,0,0,0,0,61, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, NULL,
  596. 0x01, 0,0,0,0,0,0,0,0,0,0,21, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 29, NULL,
  597. 0, 0,0,0,0,0,0,0,0,0,0,31, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 49, NULL,
  598. 0, 0,0,0,0,0,0,0,0,0,0,71, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, NULL,
  599. 0, 0,0,0,0,0,0,0,0,0,0,81, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 9, NULL,
  600. 0, 0,0,0,0,0,0,0,0,0,0,91, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, NULL,
  601. 0, 0,0,0,0,0,0,0,0,0,0,92, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, NULL,
  602. 0, 0,0,0,0,0,0,0,0,0,0,93, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 12, NULL,
  603. 0, 0,0,0,0,0,0,0,0,0,0,94, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 13, NULL,
  604. 0, 0,0,0,0,0,0,0,0,0,0,95, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 14, NULL,
  605. 0x19, 0,0,0,0,0,0,0,0,0,0,2, 1,0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 39, NULL,
  606. 0x01, 0,0,0,0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0,0,0,2, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 29, NULL,
  607. 0, 0,0,0,0,0,0,0,0,0,0,41, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 49, NULL,
  608. 0x19, 0,0,0,0,0,0,0,0,0,0,12, 1,0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 39, NULL,
  609. 0x01, 0,0,0,0,0,0,0,0,0,0,11, 0,0,0,0,0,0,0,0,0,0,12, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 29, NULL,
  610. 0, 0,0,0,0,0,0,0,0,0,0,51, 0,0,0,0,0,0,0,0,0,0,22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 49, NULL,
  611. };
  612. DWORD NumberOfTestNodes = ARRAY_SZ(TestNodes);
  613. DWORD NumberOfExpected = ARRAY_SZ(TestNodesExpected);
  614. DWORD
  615. VvJoinTestSend(
  616. IN PVVJOIN_CONTEXT VvJoinContext,
  617. IN PVVJOIN_NODE VvJoinNode
  618. )
  619. /*++
  620. Routine Description:
  621. Pretend to send a test node. Compare the node with the expected
  622. results.
  623. Arguments:
  624. VvJoinContext
  625. VvJoinNode
  626. Thread Return Value:
  627. Win32 Status
  628. --*/
  629. {
  630. #undef DEBSUB
  631. #define DEBSUB "VvJoinTestSend:"
  632. CHAR VGuid[GUID_CHAR_LEN];
  633. CHAR EGuid[GUID_CHAR_LEN];
  634. PVVJOIN_NODE Expected;
  635. VVJOIN_PRINT_NODE(5, L"TestSend ", VvJoinNode);
  636. //
  637. // Sending too many!
  638. //
  639. if (VvJoinContext->NumberSent >= NumberOfTestNodes) {
  640. DPRINT2(0, ":V: ERROR - TOO MANY (%d > %d)\n",
  641. VvJoinContext->NumberSent, NumberOfTestNodes);
  642. return ERROR_GEN_FAILURE;
  643. }
  644. //
  645. // Compare this node with the node we expected to send
  646. //
  647. Expected = &TestNodesExpected[VvJoinContext->NumberSent];
  648. VvJoinContext->NumberSent++;
  649. if (!GUIDS_EQUAL(&VvJoinNode->FileGuid, &Expected->FileGuid)) {
  650. GuidToStr(&VvJoinNode->FileGuid, VGuid);
  651. GuidToStr(&Expected->FileGuid, EGuid);
  652. DPRINT2(0, ":V: ERROR - UNEXPECTED ORDER (FileGuid %s != %s)\n", VGuid, EGuid);
  653. return ERROR_GEN_FAILURE;
  654. }
  655. if (VvJoinNode->Flags != Expected->Flags) {
  656. DPRINT2(0, ":V: ERROR - UNEXPECTED ORDER (Flags %08x != %08x)\n",
  657. VvJoinNode->Flags, Expected->Flags);
  658. return ERROR_GEN_FAILURE;
  659. }
  660. if (!GUIDS_EQUAL(&VvJoinNode->ParentGuid, &Expected->ParentGuid)) {
  661. GuidToStr(&VvJoinNode->ParentGuid, VGuid);
  662. GuidToStr(&Expected->ParentGuid, EGuid);
  663. DPRINT2(0, ":V: ERROR - UNEXPECTED ORDER (ParentGuid %s != %s)\n", VGuid, EGuid);
  664. return ERROR_GEN_FAILURE;
  665. }
  666. if (VvJoinNode->Vsn != Expected->Vsn) {
  667. DPRINT(0, ":V: ERROR - UNEXPECTED ORDER (Vsn)\n");
  668. return ERROR_GEN_FAILURE;
  669. }
  670. if (!GUIDS_EQUAL(&VvJoinNode->Originator, &Expected->Originator)) {
  671. GuidToStr(&VvJoinNode->Originator, VGuid);
  672. GuidToStr(&Expected->Originator, EGuid);
  673. DPRINT2(0, ":V: ERROR - UNEXPECTED ORDER (Originator %s != %s)\n", VGuid, EGuid);
  674. return ERROR_GEN_FAILURE;
  675. }
  676. return ERROR_SUCCESS;
  677. }
  678. VOID
  679. VvJoinTest(
  680. VOID
  681. )
  682. /*++
  683. Routine Description:
  684. Test ordering by filling the tables from a hardwired array and then
  685. calling the ordering code to send them. Check that the ordering
  686. code sends the nodes in the correct order.
  687. Arguments:
  688. None.
  689. Thread Return Value:
  690. None.
  691. --*/
  692. {
  693. #undef DEBSUB
  694. #define DEBSUB "VvJoinTest:"
  695. DWORD WStatus;
  696. DWORD i;
  697. DWORD NumberSent;
  698. PVVJOIN_CONTEXT VvJoinContext;
  699. DWORD VvJoinSendInOrder(IN PVVJOIN_CONTEXT VvJoinContext);
  700. DWORD VvJoinSendOutOfOrder(IN PVVJOIN_CONTEXT VvJoinContext);
  701. if (!DebugInfo.VvJoinTests) {
  702. DPRINT(4, ":V: VvJoin tests disabled\n");
  703. return;
  704. }
  705. //
  706. // Pretend that the hardwired array is an IDTable and fill up the tables
  707. //
  708. DPRINT1(0, ":V: >>>>> %s\n", DEBSUB);
  709. DPRINT2(0, ":V: >>>>> %s Starting (%d entries)\n", DEBSUB, NumberOfTestNodes);
  710. VvJoinContext = FrsAlloc(sizeof(VVJOIN_CONTEXT));
  711. VvJoinContext->Send = VvJoinTestSend;
  712. for (i = 0; i < NumberOfTestNodes; ++i) {
  713. WStatus = VvJoinInsertEntry(VvJoinContext,
  714. TestNodes[i].Flags,
  715. &TestNodes[i].FileGuid,
  716. &TestNodes[i].ParentGuid,
  717. &TestNodes[i].Originator,
  718. &TestNodes[i].Vsn);
  719. if (!WIN_SUCCESS(WStatus)) {
  720. DPRINT_WS(0, ":V: ERROR - inserting test nodes;", WStatus);
  721. break;
  722. }
  723. }
  724. VVJOIN_PRINT(5, VvJoinContext);
  725. //
  726. // Send the "files" through our send routine
  727. //
  728. do {
  729. NumberSent = VvJoinContext->NumberSent;
  730. WStatus = VvJoinSendInOrder(VvJoinContext);
  731. if (WIN_SUCCESS(WStatus) &&
  732. NumberSent == VvJoinContext->NumberSent) {
  733. WStatus = VvJoinSendOutOfOrder(VvJoinContext);
  734. }
  735. } while (WIN_SUCCESS(WStatus) &&
  736. NumberSent != VvJoinContext->NumberSent);
  737. //
  738. // DONE
  739. //
  740. if (!WIN_SUCCESS(WStatus)) {
  741. DPRINT_WS(0, ":V: ERROR - TEST FAILED;", WStatus);
  742. } else if (VvJoinContext->NumberSent != NumberOfExpected) {
  743. DPRINT2(0, ":V: ERROR - TEST FAILED; Expected to send %d; not %d\n",
  744. NumberOfExpected, VvJoinContext->NumberSent);
  745. } else {
  746. DPRINT(0, ":V: TEST PASSED\n");
  747. }
  748. VvJoinContext = VvJoinFreeContext(VvJoinContext);
  749. }
  750. VOID
  751. VvJoinTestSkipBegin(
  752. IN PVVJOIN_CONTEXT VvJoinContext,
  753. IN PCOMMAND_PACKET Cmd
  754. )
  755. /*++
  756. Routine Description:
  757. Create a directory and files that will be skipped.
  758. Arguments:
  759. VvJoinContext
  760. Cmd
  761. Thread Return Value:
  762. None.
  763. --*/
  764. {
  765. #undef DEBSUB
  766. #define DEBSUB "VvJoinTestSkipBegin:"
  767. HANDLE Handle;
  768. DWORD WStatus;
  769. if (!DebugInfo.VvJoinTests) {
  770. DPRINT(4, ":V: VvJoin tests disabled\n");
  771. return;
  772. }
  773. VvJoinContext->SkipDir = FrsWcsPath(RsReplica(Cmd)->Root, L"SkipDir");
  774. VvJoinContext->SkipFile1 = FrsWcsPath(VvJoinContext->SkipDir, L"SkipFile1");
  775. VvJoinContext->SkipFile2 = FrsWcsPath(RsReplica(Cmd)->Root, L"SkipFile2");
  776. if (!WIN_SUCCESS(FrsCreateDirectory(VvJoinContext->SkipDir))) {
  777. DPRINT1(0, ":V: ERROR - Can't create %ws\n", VvJoinContext->SkipDir);
  778. }
  779. WStatus = StuCreateFile(VvJoinContext->SkipFile1, &Handle);
  780. if (!HANDLE_IS_VALID(Handle) || !WIN_SUCCESS(WStatus)) {
  781. DPRINT1(0, ":V: ERROR - Can't create %ws\n", VvJoinContext->SkipFile1);
  782. } else {
  783. CloseHandle(Handle);
  784. }
  785. WStatus = StuCreateFile(VvJoinContext->SkipFile2, &Handle);
  786. if (!HANDLE_IS_VALID(Handle) || !WIN_SUCCESS(WStatus)) {
  787. DPRINT1(0, ":V: ERROR - Can't create %ws\n", VvJoinContext->SkipFile2);
  788. } else {
  789. CloseHandle(Handle);
  790. }
  791. //
  792. // Wait for the local change orders to propagate
  793. //
  794. Sleep(10 * 1000);
  795. }
  796. VOID
  797. VvJoinTestSkipCheck(
  798. IN PVVJOIN_CONTEXT VvJoinContext,
  799. IN PWCHAR FileName,
  800. IN BOOL IsDir
  801. )
  802. /*++
  803. Routine Description:
  804. Did we skip the correct files/dirs?
  805. Arguments:
  806. VvJoinContext
  807. FileName
  808. IsDir
  809. Thread Return Value:
  810. None.
  811. --*/
  812. {
  813. #undef DEBSUB
  814. #define DEBSUB "VvJoinTestSkipCheck:"
  815. if (!DebugInfo.VvJoinTests) {
  816. return;
  817. }
  818. if (IsDir && WSTR_EQ(FileName, L"SkipDir")) {
  819. VvJoinContext->SkippedDir = TRUE;
  820. } else if (!IsDir && WSTR_EQ(FileName, L"SkipFile1")) {
  821. VvJoinContext->SkippedFile1 = TRUE;
  822. } else if (!IsDir && WSTR_EQ(FileName, L"SkipFile2")) {
  823. VvJoinContext->SkippedFile2 = TRUE;
  824. }
  825. }
  826. VOID
  827. VvJoinTestSkipEnd(
  828. IN PVVJOIN_CONTEXT VvJoinContext
  829. )
  830. /*++
  831. Routine Description:
  832. Did we skip the correct files/dirs?
  833. Arguments:
  834. VvJoinContext
  835. Thread Return Value:
  836. None.
  837. --*/
  838. {
  839. #undef DEBSUB
  840. #define DEBSUB "VvJoinTestSkipEnd:"
  841. if (!DebugInfo.VvJoinTests) {
  842. return;
  843. }
  844. if (VvJoinContext->SkippedDir &&
  845. VvJoinContext->SkippedFile1 &&
  846. VvJoinContext->SkippedFile2) {
  847. DPRINT(0, ":V: Skip Test Passed\n");
  848. } else {
  849. DPRINT(0, ":V: ERROR - Skip Test failed\n");
  850. }
  851. FrsDeleteFile(VvJoinContext->SkipFile1);
  852. FrsDeleteFile(VvJoinContext->SkipFile2);
  853. FrsDeleteFile(VvJoinContext->SkipDir);
  854. VvJoinContext->SkipDir = FrsFree(VvJoinContext->SkipDir);
  855. VvJoinContext->SkipFile1 = FrsFree(VvJoinContext->SkipFile1);
  856. VvJoinContext->SkipFile2 = FrsFree(VvJoinContext->SkipFile2);
  857. }
  858. #endif DBG
  859. PVVJOIN_NODE
  860. VvJoinFindNode(
  861. PVVJOIN_CONTEXT VvJoinContext,
  862. GUID *FileGuid
  863. )
  864. /*++
  865. Routine Description:
  866. Find the node by Guid in the file table.
  867. Arguments:
  868. VvJoinContext
  869. FileGuid
  870. Thread Return Value:
  871. Node or NULL
  872. --*/
  873. {
  874. #undef DEBSUB
  875. #define DEBSUB "VvJoinFindNode:"
  876. VVJOIN_NODE Node;
  877. VVJOIN_NODE *pNode;
  878. PVVJOIN_NODE *Entry;
  879. //
  880. // No file table? Let the caller deal with it
  881. //
  882. if (!VvJoinContext->Guids) {
  883. return NULL;
  884. }
  885. //
  886. // Build a phoney node to use as a key
  887. //
  888. Node.FileGuid = *FileGuid;
  889. pNode = &Node;
  890. Entry = RtlLookupElementGenericTable(VvJoinContext->Guids, &pNode);
  891. if (Entry) {
  892. return *Entry;
  893. }
  894. return NULL;
  895. }
  896. DWORD
  897. VvJoinSendInOrder(
  898. PVVJOIN_CONTEXT VvJoinContext
  899. )
  900. /*++
  901. Routine Description:
  902. Look through the originator tables and find a node at the
  903. head of the list that can be sent in the proper VSN order.
  904. Stop looping when none of the nodes can be sent in order.
  905. Even if a node is in order, its parent may not have been sent,
  906. and so the file represented by the node cannot be sent.
  907. Arguments:
  908. VvJoinContext
  909. Thread Return Value:
  910. Win32 Status
  911. --*/
  912. {
  913. #undef DEBSUB
  914. #define DEBSUB "VvJoinSendInOrder:"
  915. DWORD WStatus;
  916. BOOL SentOne;
  917. PVOID Key;
  918. PVOID SubKey;
  919. PVVJOIN_NODE *Entry;
  920. PVVJOIN_NODE *SubEntry;
  921. PVVJOIN_NODE VvJoinNode;
  922. PVVJOIN_NODE VsnNode;
  923. PVVJOIN_NODE ParentNode;
  924. CHAR ParentGuidA[GUID_CHAR_LEN];
  925. CHAR FileGuidA[GUID_CHAR_LEN];
  926. //
  927. // Continue to scan the originator tables until nothing can be sent
  928. //
  929. DPRINT1(4, ":V: >>>>> %s\n", DEBSUB);
  930. do {
  931. WStatus = ERROR_SUCCESS;
  932. SentOne = FALSE;
  933. //
  934. // No tables or no entries; nothing to do
  935. //
  936. if (!VvJoinContext->Originators) {
  937. goto CLEANUP;
  938. }
  939. if (!RtlNumberGenericTableElements(VvJoinContext->Originators)) {
  940. VvJoinContext->Originators = FrsFree(VvJoinContext->Originators);
  941. goto CLEANUP;
  942. }
  943. //
  944. // Examine the head of each originator table. If the entry can
  945. // be sent in order, do so.
  946. Key = NULL;
  947. while (Entry = VVJOIN_NEXT_ENTRY(VvJoinContext->Originators, &Key)) {
  948. VvJoinNode = *Entry;
  949. //
  950. // No entries; done
  951. //
  952. if (!RtlNumberGenericTableElements(VvJoinNode->Vsns)) {
  953. RtlDeleteElementGenericTable(VvJoinContext->Originators, Entry);
  954. VvJoinNode->Vsns = FrsFree(VvJoinNode->Vsns);
  955. Key = NULL;
  956. continue;
  957. }
  958. //
  959. // Scan for an unsent entry
  960. //
  961. SubKey = NULL;
  962. while (SubEntry = VVJOIN_NEXT_ENTRY(VvJoinNode->Vsns, &SubKey)) {
  963. VsnNode = *SubEntry;
  964. VVJOIN_PRINT_NODE(5, L"CHECKING ", VsnNode);
  965. //
  966. // Entry was previously sent; remove it and continue
  967. //
  968. if (VsnNode->Flags & VVJOIN_FLAGS_SENT) {
  969. DPRINT(5, ":V: ALREADY SENT\n");
  970. RtlDeleteElementGenericTable(VvJoinNode->Vsns, SubEntry);
  971. SubKey = NULL;
  972. continue;
  973. }
  974. //
  975. // VsnNode is the head of this list and can be sent in
  976. // order iff its parent has been sent. Find its parent
  977. // and check it.
  978. //
  979. ParentNode = VvJoinFindNode(VvJoinContext, &VsnNode->ParentGuid);
  980. //
  981. // Something is really wrong; everyone's parent should
  982. // be in the file table UNLESS we picked up a file whose
  983. // parent was created after we began the IDTable scan.
  984. //
  985. // But this is okay if the idtable entry is a tombstoned
  986. // delete. I am not sure why but I think it has to do
  987. // with name morphing, reanimation, and out-of-order
  988. // directory tree deletes.
  989. //
  990. if (!ParentNode) {
  991. if (VsnNode->Flags & VVJOIN_FLAGS_DELETED) {
  992. //
  993. // Let the reconcile code decide to accept
  994. // this change order. Don't let it be dampened.
  995. //
  996. VsnNode->Flags |= VVJOIN_FLAGS_OUT_OF_ORDER;
  997. } else {
  998. GuidToStr(&VsnNode->ParentGuid, ParentGuidA);
  999. GuidToStr(&VsnNode->FileGuid, FileGuidA);
  1000. DPRINT2(0, ":V: ERROR - Can't find parent node %s for %s\n",
  1001. ParentGuidA, FileGuidA);
  1002. WStatus = ERROR_NOT_FOUND;
  1003. goto CLEANUP;
  1004. }
  1005. }
  1006. //
  1007. // Parent hasn't been sent; we can't send this node
  1008. // Unless it is a tombstoned delete that lacks a parent
  1009. // node. See above.
  1010. //
  1011. if (ParentNode && !(ParentNode->Flags & VVJOIN_FLAGS_SENT)) {
  1012. break;
  1013. }
  1014. //
  1015. // Parent has been sent; send this node to the outbound
  1016. // log to be sent on to our outbound partner. If we
  1017. // cannot create this change order give up and return
  1018. // to our caller.
  1019. //
  1020. WStatus = (VvJoinContext->Send)(VvJoinContext, VsnNode);
  1021. if (!WIN_SUCCESS(WStatus)) {
  1022. goto CLEANUP;
  1023. }
  1024. //
  1025. // Remove it from the originator table
  1026. //
  1027. VsnNode->Flags |= VVJOIN_FLAGS_SENT;
  1028. SentOne = TRUE;
  1029. RtlDeleteElementGenericTable(VvJoinNode->Vsns, SubEntry);
  1030. SubKey = NULL;
  1031. continue;
  1032. }
  1033. }
  1034. } while (WIN_SUCCESS(WStatus) && SentOne);
  1035. CLEANUP:
  1036. return WStatus;
  1037. }
  1038. BOOL
  1039. VvJoinInOrder(
  1040. PVVJOIN_CONTEXT VvJoinContext,
  1041. PVVJOIN_NODE VvJoinNode
  1042. )
  1043. /*++
  1044. Routine Description:
  1045. Is this node at the head of an originator list? In other words,
  1046. can this node be sent in order?
  1047. Arguments:
  1048. VvJoinContext
  1049. VvJoinNode
  1050. Thread Return Value:
  1051. TRUE - head of list
  1052. FALSE - NOT
  1053. --*/
  1054. {
  1055. #undef DEBSUB
  1056. #define DEBSUB "VvJoinInOrder:"
  1057. PVOID Key;
  1058. PVVJOIN_NODE *Entry;
  1059. PVVJOIN_NODE OriginatorNode;
  1060. CHAR FileGuidA[GUID_CHAR_LEN];
  1061. //
  1062. // No originators or no entries. In either case, this node
  1063. // cannot possibly be on the head of a list.
  1064. //
  1065. DPRINT1(4, ":V: >>>>> %s\n", DEBSUB);
  1066. if (!VvJoinContext->Originators) {
  1067. goto NOTFOUND;
  1068. }
  1069. if (!RtlNumberGenericTableElements(VvJoinContext->Originators)) {
  1070. goto NOTFOUND;
  1071. }
  1072. //
  1073. // Find the originator table.
  1074. //
  1075. Entry = RtlLookupElementGenericTable(VvJoinContext->Originators,
  1076. &VvJoinNode);
  1077. if (!Entry || !*Entry) {
  1078. goto NOTFOUND;
  1079. }
  1080. //
  1081. // No entries; this node cannot possibly be on the head of a list.
  1082. //
  1083. OriginatorNode = *Entry;
  1084. if (!OriginatorNode->Vsns) {
  1085. goto NOTFOUND;
  1086. }
  1087. if (!RtlNumberGenericTableElements(OriginatorNode->Vsns)) {
  1088. goto NOTFOUND;
  1089. }
  1090. //
  1091. // Head of this list of nodes sorted by vsn
  1092. //
  1093. Key = NULL;
  1094. Entry = VVJOIN_NEXT_ENTRY(OriginatorNode->Vsns, &Key);
  1095. if (!Entry || !*Entry) {
  1096. goto NOTFOUND;
  1097. }
  1098. //
  1099. // This node is at the head of the list if the entry at the head
  1100. // of the list points to it.
  1101. //
  1102. return (*Entry == VvJoinNode);
  1103. NOTFOUND:
  1104. GuidToStr(&VvJoinNode->FileGuid, FileGuidA);
  1105. DPRINT1(0, ":V: ERROR - node %s is not in a list\n", FileGuidA);
  1106. return FALSE;
  1107. }
  1108. DWORD
  1109. VvJoinSendIfParentSent(
  1110. PVVJOIN_CONTEXT VvJoinContext,
  1111. PVVJOIN_NODE VvJoinNode
  1112. )
  1113. /*++
  1114. Routine Description:
  1115. Send this node if its parent has been sent. Otherwise, try to
  1116. send its parent.
  1117. Arguments:
  1118. VvJoinContext
  1119. VvJoinNode
  1120. Thread Return Value:
  1121. Win32 Status
  1122. --*/
  1123. {
  1124. #undef DEBSUB
  1125. #define DEBSUB "VvJoinSendIfParentSent:"
  1126. DWORD WStatus;
  1127. PVVJOIN_NODE ParentNode;
  1128. CHAR ParentGuidA[GUID_CHAR_LEN];
  1129. CHAR FileGuidA[GUID_CHAR_LEN];
  1130. DPRINT1(4, ":V: >>>>> %s\n", DEBSUB);
  1131. //
  1132. // Find this node's parent
  1133. //
  1134. ParentNode = VvJoinFindNode(VvJoinContext, &VvJoinNode->ParentGuid);
  1135. //
  1136. // Something is really wrong. Every node should have a parent UNLESS
  1137. // its parent was created after the IDTable scan began. In either case,
  1138. // give up.
  1139. //
  1140. //
  1141. // But this is okay if the idtable entry is a tombstoned
  1142. // delete. I am not sure why but I think it has to do
  1143. // with name morphing, reanimation, and out-of-order
  1144. // directory tree deletes.
  1145. //
  1146. if (!ParentNode) {
  1147. if (VvJoinNode->Flags & VVJOIN_FLAGS_DELETED) {
  1148. //
  1149. // Let the reconcile code decide to accept
  1150. // this change order. Don't let it be dampened.
  1151. //
  1152. VvJoinNode->Flags |= VVJOIN_FLAGS_OUT_OF_ORDER;
  1153. } else {
  1154. GuidToStr(&VvJoinNode->ParentGuid, ParentGuidA);
  1155. GuidToStr(&VvJoinNode->FileGuid, FileGuidA);
  1156. DPRINT2(0, ":V: ERROR - Can't find parent node %s for %s\n",
  1157. ParentGuidA, FileGuidA);
  1158. WStatus = ERROR_NOT_FOUND;
  1159. goto CLEANUP;
  1160. }
  1161. }
  1162. //
  1163. // A loop in the directory hierarchy!? Give up.
  1164. //
  1165. if (ParentNode && (ParentNode->Flags & VVJOIN_FLAGS_PROCESSING)) {
  1166. GuidToStr(&VvJoinNode->ParentGuid, ParentGuidA);
  1167. GuidToStr(&VvJoinNode->FileGuid, FileGuidA);
  1168. DPRINT2(0, ":V: ERROR - LOOP parent node %s for %s\n", ParentGuidA, FileGuidA);
  1169. WStatus = ERROR_INVALID_DATA;
  1170. goto CLEANUP;
  1171. }
  1172. //
  1173. // Has this node's parent been sent?
  1174. //
  1175. if (!ParentNode || (ParentNode->Flags & VVJOIN_FLAGS_SENT)) {
  1176. //
  1177. // Sending out of order; don't update the version vector
  1178. //
  1179. if (ParentNode && !VvJoinInOrder(VvJoinContext, VvJoinNode)) {
  1180. VvJoinNode->Flags |= VVJOIN_FLAGS_OUT_OF_ORDER;
  1181. }
  1182. //
  1183. // Send this node (its parent has been sent)
  1184. //
  1185. WStatus = (VvJoinContext->Send)(VvJoinContext, VvJoinNode);
  1186. if (!WIN_SUCCESS(WStatus)) {
  1187. goto CLEANUP;
  1188. }
  1189. VvJoinNode->Flags |= VVJOIN_FLAGS_SENT;
  1190. } else {
  1191. //
  1192. // Recurse to see if we can send the parent
  1193. //
  1194. ParentNode->Flags |= VVJOIN_FLAGS_PROCESSING;
  1195. WStatus = VvJoinSendIfParentSent(VvJoinContext, ParentNode);
  1196. ParentNode->Flags &= ~VVJOIN_FLAGS_PROCESSING;
  1197. }
  1198. CLEANUP:
  1199. return WStatus;
  1200. }
  1201. DWORD
  1202. VvJoinSendOutOfOrder(
  1203. PVVJOIN_CONTEXT VvJoinContext
  1204. )
  1205. /*++
  1206. Routine Description:
  1207. This function is called after VvJoinSendInOrder(). All of the nodes
  1208. that could be sent in order have been sent. At this time, we take
  1209. the first entry of the first originator table and send it or, if
  1210. its parent hasn't been sent, its parent. The search for the parent
  1211. recurses until we have a node whose parent has been sent. The
  1212. recursion will stop because we will either hit the root or we will
  1213. loop.
  1214. Arguments:
  1215. VvJoinContext
  1216. Thread Return Value:
  1217. Win32 Status
  1218. --*/
  1219. {
  1220. #undef DEBSUB
  1221. #define DEBSUB "VvJoinSendOutOfOrder:"
  1222. DWORD WStatus = ERROR_SUCCESS;
  1223. PVOID Key;
  1224. PVOID SubKey;
  1225. PVVJOIN_NODE *Entry = NULL;
  1226. PVVJOIN_NODE *SubEntry = NULL;
  1227. PVVJOIN_NODE VvJoinNode = NULL;
  1228. PVVJOIN_NODE VsnNode = NULL;
  1229. PVVJOIN_NODE ParentNode = NULL;
  1230. DPRINT1(4, ":V: >>>>> %s\n", DEBSUB);
  1231. //
  1232. // No table or no entries; nothing to do
  1233. //
  1234. if (!VvJoinContext->Originators) {
  1235. WStatus = ERROR_SUCCESS;
  1236. goto CLEANUP;
  1237. }
  1238. if (!RtlNumberGenericTableElements(VvJoinContext->Originators)) {
  1239. VvJoinContext->Originators = FrsFree(VvJoinContext->Originators);
  1240. WStatus = ERROR_SUCCESS;
  1241. goto CLEANUP;
  1242. }
  1243. //
  1244. // Find the first originator table with entries
  1245. //
  1246. Key = NULL;
  1247. while (Entry = VVJOIN_NEXT_ENTRY(VvJoinContext->Originators, &Key)) {
  1248. VvJoinNode = *Entry;
  1249. if (!RtlNumberGenericTableElements(VvJoinNode->Vsns)) {
  1250. RtlDeleteElementGenericTable(VvJoinContext->Originators, Entry);
  1251. VvJoinNode->Vsns = FrsFree(VvJoinNode->Vsns);
  1252. Key = NULL;
  1253. continue;
  1254. }
  1255. //
  1256. // Scan for an unsent entry
  1257. //
  1258. SubKey = NULL;
  1259. while (SubEntry = VVJOIN_NEXT_ENTRY(VvJoinNode->Vsns, &SubKey)) {
  1260. VsnNode = *SubEntry;
  1261. VVJOIN_PRINT_NODE(5, L"CHECKING ", VsnNode);
  1262. //
  1263. // Entry was previously sent; remove it and continue
  1264. //
  1265. if (VsnNode->Flags & VVJOIN_FLAGS_SENT) {
  1266. DPRINT(5, ":V: ALREADY SENT\n");
  1267. RtlDeleteElementGenericTable(VvJoinNode->Vsns, SubEntry);
  1268. SubKey = NULL;
  1269. continue;
  1270. }
  1271. break;
  1272. }
  1273. //
  1274. // No unsent entries; reset the loop indicator back to the first
  1275. // entry so that the code at the top of the loop will delete
  1276. // this originator and then look at other originators.
  1277. //
  1278. if (!SubEntry) {
  1279. Key = NULL;
  1280. } else {
  1281. break;
  1282. }
  1283. }
  1284. //
  1285. // No more entries; done
  1286. //
  1287. if (!SubEntry) {
  1288. WStatus = ERROR_SUCCESS;
  1289. goto CLEANUP;
  1290. }
  1291. //
  1292. // Send this entry if its parent has been sent. Otherwise, recurse.
  1293. // The VVJOIN_FLAGS_PROCESSING detects loops.
  1294. //
  1295. VsnNode = *SubEntry;
  1296. VsnNode->Flags |= VVJOIN_FLAGS_PROCESSING;
  1297. WStatus = VvJoinSendIfParentSent(VvJoinContext, VsnNode);
  1298. VsnNode->Flags &= ~VVJOIN_FLAGS_PROCESSING;
  1299. CLEANUP:
  1300. return WStatus;
  1301. }
  1302. JET_ERR
  1303. VvJoinBuildTables(
  1304. IN PTHREAD_CTX ThreadCtx,
  1305. IN PTABLE_CTX TableCtx,
  1306. IN PIDTABLE_RECORD IDTableRec,
  1307. IN PVVJOIN_CONTEXT VvJoinContext
  1308. )
  1309. /*++
  1310. Routine Description:
  1311. This is a worker function passed to FrsEnumerateTable(). Each time
  1312. it is called it inserts an entry into the vvjoin tables. These
  1313. tables will be used later to send the appropriate files and directories
  1314. to our outbound partner.
  1315. Files that would have been dampened are not included.
  1316. All directories are included but directories that would have been
  1317. dampened are marked as "SENT". Keeping the complete directory
  1318. hierarchy makes other code in this subsystem easier.
  1319. Arguments:
  1320. ThreadCtx - Needed to access Jet.
  1321. TableCtx - A ptr to an IDTable context struct.
  1322. IDTableRec - A ptr to a IDTable record.
  1323. VvJoinContext
  1324. Thread Return Value:
  1325. A Jet error status. Success means call us with the next record.
  1326. Failure means don't call again and pass our status back to the
  1327. caller of FrsEnumerateTable().
  1328. --*/
  1329. {
  1330. #undef DEBSUB
  1331. #define DEBSUB "VvJoinBuildTables:"
  1332. ULONGLONG SystemTime;
  1333. ULONGLONG ExpireTime;
  1334. ULONGLONG OriginatorVSN = IDTableRec->OriginatorVSN;
  1335. GUID *OriginatorGuid = &IDTableRec->OriginatorGuid;
  1336. PGEN_TABLE ReplicaVv = VvJoinContext->ReplicaVv;
  1337. DWORD WStatus;
  1338. DWORD Flags = 0;
  1339. //
  1340. // First check to see if our partner already has this file by comparing
  1341. // the OriginatorVSN in our IDTable record with the corresponding value
  1342. // in the version vector we got from the partner.
  1343. //
  1344. if (VVHasVsnNoLock(VvJoinContext->CxtionVv, OriginatorGuid, OriginatorVSN)) {
  1345. //
  1346. // Yes, buts its a dir; include in the tables but mark it as sent. We
  1347. // include it in the tables because the code that checks for an
  1348. // existing parent requires the entire directory tree.
  1349. //
  1350. if (IDTableRec->FileIsDir) {
  1351. DPRINT1(4, ":V: Dir %ws is in the Vv; INSERT SENT.\n", IDTableRec->FileName);
  1352. Flags |= VVJOIN_FLAGS_SENT;
  1353. } else {
  1354. //
  1355. // Dampened file, done
  1356. //
  1357. DPRINT1(4, ":V: File %ws is in the Vv; SKIP.\n", IDTableRec->FileName);
  1358. return JET_errSuccess;
  1359. }
  1360. }
  1361. //
  1362. // Ignore files that have changed since the beginning of the IDTable scan.
  1363. // Keep directories but mark them as sent out-of-order. We must still
  1364. // send the directories because a file with a lower VSN may depend on this
  1365. // directory's existance. We mark the directory out-of-order because we
  1366. // can't be certain that we have seen all of the directories and files
  1367. // with VSNs lower than this.
  1368. //
  1369. // Note: If the orginator guid is not found in our Replica VV then we have
  1370. // to send the file and let the partner accept/reject. This can happen
  1371. // if one or more COs arrived here with a new originator guid and were
  1372. // all marked out-of-order. Because the VVRetire code will not update the
  1373. // VV for out-of-order COs the new originator guid will not get added to
  1374. // our Replica VV so VVHasVsn will return FALSE making us think that a new
  1375. // CO has arrived since the scan started (and get sent out) which is
  1376. // not the case. The net effect is that the file won't get sent.
  1377. // (Yes this actually happened.)
  1378. //
  1379. if (!VVHasVsnNoLock(ReplicaVv, OriginatorGuid, OriginatorVSN) &&
  1380. VVHasOriginatorNoLock(ReplicaVv, OriginatorGuid)) {
  1381. if (IDTableRec->FileIsDir) {
  1382. DPRINT1(4, ":V: Dir %ws is not in the ReplicaVv; Mark out-of-order.\n",
  1383. IDTableRec->FileName);
  1384. Flags |= VVJOIN_FLAGS_OUT_OF_ORDER;
  1385. VVJOIN_TEST_SKIP_CHECK(VvJoinContext, IDTableRec->FileName, TRUE);
  1386. } else {
  1387. //
  1388. // Ignored file, done
  1389. //
  1390. DPRINT1(4, ":V: File %ws is not in the ReplicaVv; Skip.\n",
  1391. IDTableRec->FileName);
  1392. VVJOIN_TEST_SKIP_CHECK(VvJoinContext, IDTableRec->FileName, FALSE);
  1393. return JET_errSuccess;
  1394. }
  1395. }
  1396. //
  1397. // Deleted
  1398. //
  1399. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED)) {
  1400. Flags |= VVJOIN_FLAGS_DELETED;
  1401. //
  1402. // Check for expired tombstones and don't send them.
  1403. //
  1404. GetSystemTimeAsFileTime((PFILETIME)&SystemTime);
  1405. COPY_TIME(&ExpireTime, &IDTableRec->TombStoneGC);
  1406. if ((ExpireTime < SystemTime) && (ExpireTime != QUADZERO)) {
  1407. //
  1408. // IDTable record has expired. Delete it.
  1409. //
  1410. if (IDTableRec->FileIsDir) {
  1411. DPRINT1(4, ":V: Dir %ws IDtable record has expired. Don't send.\n",
  1412. IDTableRec->FileName);
  1413. Flags |= VVJOIN_FLAGS_SENT;
  1414. } else {
  1415. //
  1416. // Expired and not a dir so don't even insert in tables.
  1417. //
  1418. DPRINT1(4, ":V: File %ws is expired; SKIP.\n", IDTableRec->FileName);
  1419. return JET_errSuccess;
  1420. }
  1421. }
  1422. }
  1423. //
  1424. // Ignore incomplete entries. Check for the IDREC_FLAGS_NEW_FILE_IN_PROGRESS flag.
  1425. //
  1426. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS)) {
  1427. DPRINT1(4, ":V: %ws is new file in progress; SKIP.\n", IDTableRec->FileName);
  1428. return JET_errSuccess;
  1429. }
  1430. //
  1431. // The root node is never sent.
  1432. //
  1433. if (GUIDS_EQUAL(&IDTableRec->FileGuid,
  1434. VvJoinContext->Replica->ReplicaRootGuid)) {
  1435. DPRINT1(4, ":V: %ws is root\n", IDTableRec->FileName);
  1436. Flags |= VVJOIN_FLAGS_ROOT | VVJOIN_FLAGS_SENT;
  1437. }
  1438. //
  1439. // Is a directory
  1440. //
  1441. if (IDTableRec->FileIsDir) {
  1442. DPRINT1(4, ":V: %ws is directory\n", IDTableRec->FileName);
  1443. Flags |= VVJOIN_FLAGS_ISDIR;
  1444. }
  1445. //
  1446. // Include in the vvjoin tables.
  1447. //
  1448. WStatus = VvJoinInsertEntry(VvJoinContext,
  1449. Flags,
  1450. &IDTableRec->FileGuid,
  1451. &IDTableRec->ParentGuid,
  1452. OriginatorGuid,
  1453. &OriginatorVSN);
  1454. CLEANUP3_WS(4, ":V: ERROR - inserting %ws for %ws\\%ws;",
  1455. IDTableRec->FileName, VvJoinContext->Replica->SetName->Name,
  1456. VvJoinContext->Replica->MemberName->Name, WStatus, CLEANUP);
  1457. //
  1458. // Stop the VvJoin if the cxtion is no longer joined
  1459. //
  1460. VV_JOIN_TRIGGER(VvJoinContext);
  1461. if (!CxtionFlagIs(VvJoinContext->Cxtion, CXTION_FLAGS_JOIN_GUID_VALID) ||
  1462. !GUIDS_EQUAL(&VvJoinContext->JoinGuid, &VvJoinContext->Cxtion->JoinGuid)) {
  1463. DPRINT(0, ":V: VVJOIN ABORTED; MISMATCHED JOIN GUIDS\n");
  1464. goto CLEANUP;
  1465. }
  1466. return JET_errSuccess;
  1467. CLEANUP:
  1468. return JET_errKeyDuplicate;
  1469. }
  1470. DWORD
  1471. VvJoinSend(
  1472. IN PVVJOIN_CONTEXT VvJoinContext,
  1473. IN PVVJOIN_NODE VvJoinNode
  1474. )
  1475. /*++
  1476. Routine Description:
  1477. Generate a refresh change order and inject it into the outbound
  1478. log. The staging file will be generated on demand. See the
  1479. outlog.c code for more information.
  1480. Arguments:
  1481. VvJoinContext
  1482. VvJoinNode
  1483. Thread Return Value:
  1484. Win32 Status
  1485. --*/
  1486. {
  1487. #undef DEBSUB
  1488. #define DEBSUB "VvJoinSend:"
  1489. JET_ERR jerr;
  1490. PCOMMAND_PACKET Cmd;
  1491. PIDTABLE_RECORD IDTableRec;
  1492. PCHANGE_ORDER_ENTRY Coe;
  1493. ULONG CoFlags;
  1494. LONG OlTimeout = VvJoinContext->OlTimeout;
  1495. ULONG LocationCmd = CO_LOCATION_CREATE;
  1496. VVJOIN_PRINT_NODE(5, L"Sending ", VvJoinNode);
  1497. //
  1498. // Read the IDTable entry for this file
  1499. //
  1500. jerr = DbsReadRecord(VvJoinContext->ThreadCtx,
  1501. &VvJoinNode->FileGuid,
  1502. GuidIndexx,
  1503. VvJoinContext->TableCtx);
  1504. if (!JET_SUCCESS(jerr)) {
  1505. DPRINT_JS(0, "DbsReadRecord:", jerr);
  1506. return ERROR_NOT_FOUND;
  1507. }
  1508. //
  1509. // Deleted
  1510. //
  1511. IDTableRec = VvJoinContext->TableCtx->pDataRecord;
  1512. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED)) {
  1513. VvJoinNode->Flags |= VVJOIN_FLAGS_DELETED;
  1514. LocationCmd = CO_LOCATION_DELETE;
  1515. }
  1516. //
  1517. // How could this happen?!
  1518. //
  1519. if (!GUIDS_EQUAL(&VvJoinNode->FileGuid, &IDTableRec->FileGuid)) {
  1520. return ERROR_OPERATION_ABORTED;
  1521. } else {
  1522. DPRINT1(4, ":V: Read IDTable entry for %ws\n", IDTableRec->FileName);
  1523. }
  1524. //
  1525. // File or dir changed after the IDTable scan. Ignore files
  1526. // but send directories marked as out-of-order. We do this
  1527. // because a file with a lower VSN may require this directory.
  1528. // Note that a directory's VSN may be higher because it was
  1529. // newly created or simply altered by, say, changing its times
  1530. // or adding an alternate data stream.
  1531. //
  1532. if (!GUIDS_EQUAL(&IDTableRec->OriginatorGuid, &VvJoinNode->Originator) ||
  1533. IDTableRec->OriginatorVSN != VvJoinNode->Vsn) {
  1534. DPRINT3(4, ":V: WARN: VSN/ORIGINATOR Mismatch for %ws\\%ws %ws\n",
  1535. VvJoinContext->Replica->SetName->Name,
  1536. VvJoinContext->Replica->MemberName->Name,
  1537. IDTableRec->FileName);
  1538. if (VvJoinNode->Flags & VVJOIN_FLAGS_DELETED) {
  1539. //
  1540. // If this entry was marked deleted it is possible that it was
  1541. // reamimated with a demand refresh CO. If that happened then
  1542. // the VSN in the IDTable would have changed (getting us here)
  1543. // but no CO would get placed in the Outbound log (since demand
  1544. // refresh COs don't propagate). So let the reconcile code
  1545. // decide to accept/reject this change order. Don't let it be dampened.
  1546. //
  1547. DPRINT1(4, ":V: Sending delete tombstone for %ws out of order\n", IDTableRec->FileName);
  1548. VvJoinNode->Flags |= VVJOIN_FLAGS_OUT_OF_ORDER;
  1549. } else
  1550. if (VvJoinNode->Flags & VVJOIN_FLAGS_ISDIR) {
  1551. DPRINT1(4, ":V: Sending directory %ws out of order\n", IDTableRec->FileName);
  1552. VvJoinNode->Flags |= VVJOIN_FLAGS_OUT_OF_ORDER;
  1553. } else {
  1554. DPRINT1(4, ":V: Skipping file %ws\n", IDTableRec->FileName);
  1555. VvJoinNode->Flags |= VVJOIN_FLAGS_SENT;
  1556. }
  1557. }
  1558. if (!(VvJoinNode->Flags & VVJOIN_FLAGS_SENT)) {
  1559. if (VvJoinNode->Flags & VVJOIN_FLAGS_DELETED) {
  1560. FRS_CO_FILE_PROGRESS(IDTableRec->FileName,
  1561. IDTableRec->OriginatorVSN,
  1562. "VVjoin sending delete");
  1563. } else {
  1564. FRS_CO_FILE_PROGRESS(IDTableRec->FileName,
  1565. IDTableRec->OriginatorVSN,
  1566. "VVjoin sending create");
  1567. }
  1568. CoFlags = 0;
  1569. //
  1570. // Change order has a Location command
  1571. //
  1572. SetFlag(CoFlags, CO_FLAG_LOCATION_CMD);
  1573. //
  1574. // Mascarade as a local change order since the staging file
  1575. // is created from the local version of the file and isn't
  1576. // from a inbound partner.
  1577. //
  1578. SetFlag(CoFlags, CO_FLAG_LOCALCO);
  1579. //
  1580. // Refresh change orders will not be propagated by our outbound
  1581. // partner to its outbound partners.
  1582. //
  1583. // Change of plans; allow the propagation to occur so that
  1584. // parallel vvjoins and vvjoinings work (A is vvjoining B is
  1585. // vvjoining C).
  1586. //
  1587. // SetFlag(CoFlags, CO_FLAG_REFRESH);
  1588. //
  1589. // Directed at one cxtion
  1590. //
  1591. SetFlag(CoFlags, CO_FLAG_DIRECTED_CO);
  1592. //
  1593. // This vvjoin requested change order may end up going back to its
  1594. // originator because the originator's version vector doesn't seem to
  1595. // have this file/dir. This could happen if the database was deleted on
  1596. // the originator and is being re-synced. Since the originator could be
  1597. // several hops away the bit stays set to suppress dampening back to the
  1598. // originator. If it gets to the originator then reconcile logic there
  1599. // decides to accept or reject.
  1600. //
  1601. SetFlag(CoFlags, CO_FLAG_VVJOIN_TO_ORIG);
  1602. //
  1603. // Increment the LCO Sent At Join counters
  1604. // for both the Replic set and connection objects.
  1605. //
  1606. PM_INC_CTR_REPSET(VvJoinContext->Replica, LCOSentAtJoin, 1);
  1607. PM_INC_CTR_CXTION(VvJoinContext->Cxtion, LCOSentAtJoin, 1);
  1608. //
  1609. // By "out of order" we mean that the VSN on this change order
  1610. // should not be used to update the version vector because there
  1611. // may be other files or dirs with lower VSNs that will be sent
  1612. // later. We wouldn't want our partner to dampen them.
  1613. //
  1614. if (VvJoinNode->Flags & VVJOIN_FLAGS_OUT_OF_ORDER) {
  1615. SetFlag(CoFlags, CO_FLAG_OUT_OF_ORDER);
  1616. }
  1617. //
  1618. // Build a change order entry from the IDtable Record.
  1619. //
  1620. Coe = ChgOrdMakeFromIDRecord(IDTableRec,
  1621. VvJoinContext->Replica,
  1622. LocationCmd,
  1623. CoFlags,
  1624. VvJoinContext->Cxtion->Name->Guid);
  1625. //
  1626. // Set CO state
  1627. //
  1628. SET_CHANGE_ORDER_STATE(Coe, IBCO_OUTBOUND_REQUEST);
  1629. //
  1630. // The DB server is responsible for updating the outbound log.
  1631. // WARNING -- The operation is synchronous so that an command
  1632. // packets will not be sitting on the DB queue once an unjoin
  1633. // command has completed. If this call is made async; then
  1634. // some thread must wait for the DB queue to drain before
  1635. // completing the unjoin operation. E.g., use the inbound
  1636. // changeorder count for these outbound change orders and don't
  1637. // unjoin until the count hits 0.
  1638. //
  1639. Cmd = DbsPrepareCmdPkt(NULL, // CmdPkt,
  1640. VvJoinContext->Replica, // Replica,
  1641. CMD_DBS_INJECT_OUTBOUND_CO, // CmdRequest,
  1642. NULL, // TableCtx,
  1643. Coe, // CallContext,
  1644. 0, // TableType,
  1645. 0, // AccessRequest,
  1646. 0, // IndexType,
  1647. NULL, // KeyValue,
  1648. 0, // KeyValueLength,
  1649. FALSE); // Submit
  1650. FrsSetCompletionRoutine(Cmd, FrsCompleteKeepPkt, NULL);
  1651. FrsSubmitCommandServerAndWait(&DBServiceCmdServer, Cmd, INFINITE);
  1652. FrsFreeCommand(Cmd, NULL);
  1653. //
  1654. // Stats
  1655. //
  1656. VvJoinContext->NumberSent++;
  1657. VvJoinContext->OutstandingCos++;
  1658. }
  1659. //
  1660. // Stop the vvjoin thread
  1661. //
  1662. if (FrsIsShuttingDown) {
  1663. return ERROR_PROCESS_ABORTED;
  1664. }
  1665. //
  1666. // Stop the VvJoin if the cxtion is no longer joined
  1667. //
  1668. VV_JOIN_TRIGGER(VvJoinContext);
  1669. RETEST:
  1670. if (!CxtionFlagIs(VvJoinContext->Cxtion, CXTION_FLAGS_JOIN_GUID_VALID) ||
  1671. !GUIDS_EQUAL(&VvJoinContext->JoinGuid, &VvJoinContext->Cxtion->JoinGuid)) {
  1672. DPRINT(0, ":V: VVJOIN ABORTED; MISMATCHED JOIN GUIDS\n");
  1673. return ERROR_OPERATION_ABORTED;
  1674. }
  1675. //
  1676. // Throttle the number of vvjoin change orders outstanding so
  1677. // that we don't fill up the staging area or the database.
  1678. //
  1679. if (VvJoinContext->OutstandingCos >= VvJoinContext->MaxOutstandingCos) {
  1680. if (VvJoinContext->Cxtion->OLCtx->OutstandingCos) {
  1681. DPRINT2(0, ":V: Throttling for %d ms; %d OutstandingCos\n",
  1682. OlTimeout, VvJoinContext->Cxtion->OLCtx->OutstandingCos);
  1683. Sleep(OlTimeout);
  1684. OlTimeout <<= 1;
  1685. //
  1686. // Too small
  1687. //
  1688. if (OlTimeout < VvJoinContext->OlTimeout) {
  1689. OlTimeout = VvJoinContext->OlTimeout;
  1690. }
  1691. //
  1692. // Too large
  1693. //
  1694. if (OlTimeout > VvJoinContext->OlTimeoutMax) {
  1695. OlTimeout = VvJoinContext->OlTimeoutMax;
  1696. }
  1697. goto RETEST;
  1698. }
  1699. //
  1700. // The number of outstanding cos went to 0; send another slug
  1701. // of change orders to the outbound log process.
  1702. //
  1703. VvJoinContext->OutstandingCos = 0;
  1704. }
  1705. return ERROR_SUCCESS;
  1706. }
  1707. VOID
  1708. ChgOrdInjectControlCo(
  1709. IN PREPLICA Replica,
  1710. IN PCXTION Cxtion,
  1711. IN ULONG ContentCmd
  1712. );
  1713. ULONG
  1714. MainVvJoin(
  1715. PVOID FrsThreadCtxArg
  1716. )
  1717. /*++
  1718. Routine Description:
  1719. Entry point for processing vvjoins. This thread scans the IDTable
  1720. and generates change orders for files and dirs that our outbound
  1721. partner lacks. The outbound partner's version vector is used
  1722. to select the files and dirs. This thread is invoked during
  1723. join iff the change orders needed by our outbound partner have
  1724. been deleted from the outbound log. See outlog.c for more info
  1725. about this decision.
  1726. This process is termed a vvjoin to distinguish it from a normal
  1727. join. A normal join sends the change orders in the outbound
  1728. log to our outbound partner without invoking this thread.
  1729. This thread is a command server and is associated with a
  1730. cxtion by a PCOMMAND_SERVER field (VvJoinCs) in the cxtion.
  1731. Like all command servers, this thread will exit after a
  1732. few minutes if there is no work and will be spawned when
  1733. work shows up on its queue.
  1734. Arguments:
  1735. FrsThreadCtxArg - Frs thread context
  1736. Return Value:
  1737. WIN32 Status
  1738. --*/
  1739. {
  1740. #undef DEBSUB
  1741. #define DEBSUB "MainVvJoin:"
  1742. JET_ERR jerr;
  1743. ULONG WStatus = ERROR_SUCCESS;
  1744. ULONG FStatus;
  1745. DWORD NumberSent;
  1746. PCOMMAND_PACKET Cmd;
  1747. PFRS_THREAD FrsThread = (PFRS_THREAD)FrsThreadCtxArg;
  1748. PCOMMAND_SERVER VvJoinCs = FrsThread->Data;
  1749. PVVJOIN_CONTEXT VvJoinContext = NULL;
  1750. DPRINT(1, ":S: VvJoin Thread is starting.\n");
  1751. FrsThread->Exit = ThSupExitWithTombstone;
  1752. //
  1753. // Quick verification test
  1754. //
  1755. VVJOIN_TEST();
  1756. //
  1757. // Try-Finally
  1758. //
  1759. try {
  1760. //
  1761. // Capture exception.
  1762. //
  1763. try {
  1764. CANT_EXIT_YET:
  1765. DPRINT(1, ":S: VvJoin Thread has started.\n");
  1766. while((Cmd = FrsGetCommandServer(VvJoinCs)) != NULL) {
  1767. //
  1768. // Shutting down; stop accepting command packets
  1769. //
  1770. if (FrsIsShuttingDown) {
  1771. FrsRunDownCommandServer(VvJoinCs, &VvJoinCs->Queue);
  1772. }
  1773. switch (Cmd->Command) {
  1774. case CMD_VVJOIN_START: {
  1775. DPRINT3(1, ":V: Start vvjoin for %ws\\%ws\\%ws\n",
  1776. RsReplica(Cmd)->SetName->Name, RsReplica(Cmd)->MemberName->Name,
  1777. RsCxtion(Cmd)->Name);
  1778. //
  1779. // The database must be started before we create a jet session
  1780. // WARN: The database event may be set by the shutdown
  1781. // code in order to force threads to exit.
  1782. //
  1783. WaitForSingleObject(DataBaseEvent, INFINITE);
  1784. if (FrsIsShuttingDown) {
  1785. RcsSubmitTransferToRcs(Cmd, CMD_UNJOIN);
  1786. WStatus = ERROR_PROCESS_ABORTED;
  1787. break;
  1788. }
  1789. //
  1790. // Global info
  1791. //
  1792. VvJoinContext = FrsAlloc(sizeof(VVJOIN_CONTEXT));
  1793. VvJoinContext->Send = VvJoinSend;
  1794. //
  1795. // Outstanding change orders
  1796. //
  1797. CfgRegReadDWord(FKC_VVJOIN_LIMIT, NULL, 0, &VvJoinContext->MaxOutstandingCos);
  1798. DPRINT1(4, ":V: VvJoin Max OutstandingCos is %d\n",
  1799. VvJoinContext->MaxOutstandingCos);
  1800. //
  1801. // Outbound Log Throttle Timeout
  1802. //
  1803. CfgRegReadDWord(FKC_VVJOIN_TIMEOUT, NULL, 0, &VvJoinContext->OlTimeout);
  1804. if (VvJoinContext->OlTimeout < VVJOIN_TIMEOUT_MAX) {
  1805. VvJoinContext->OlTimeoutMax = VVJOIN_TIMEOUT_MAX;
  1806. } else {
  1807. VvJoinContext->OlTimeoutMax = VvJoinContext->OlTimeout;
  1808. }
  1809. DPRINT2(4, ":V: VvJoin Outbound Log Throttle Timeout is %d (%d max)\n",
  1810. VvJoinContext->OlTimeout, VvJoinContext->OlTimeoutMax);
  1811. //
  1812. // Allocate a context for Jet to run in this thread.
  1813. //
  1814. VvJoinContext->ThreadCtx = FrsAllocType(THREAD_CONTEXT_TYPE);
  1815. VvJoinContext->TableCtx = DbsCreateTableContext(IDTablex);
  1816. //
  1817. // Setup a Jet Session (returning the session ID in ThreadCtx).
  1818. //
  1819. jerr = DbsCreateJetSession(VvJoinContext->ThreadCtx);
  1820. if (JET_SUCCESS(jerr)) {
  1821. DPRINT(4,":V: JetOpenDatabase complete\n");
  1822. } else {
  1823. DPRINT_JS(0,":V: ERROR - OpenDatabase failed.", jerr);
  1824. FStatus = DbsTranslateJetError(jerr, FALSE);
  1825. RcsSubmitTransferToRcs(Cmd, CMD_UNJOIN);
  1826. break;
  1827. }
  1828. //
  1829. // Pull over the params from the command packet into our context
  1830. //
  1831. //
  1832. // Replica
  1833. //
  1834. VvJoinContext->Replica = RsReplica(Cmd);
  1835. //
  1836. // Outbound version vector
  1837. //
  1838. VvJoinContext->CxtionVv = RsVVector(Cmd);
  1839. RsVVector(Cmd) = NULL;
  1840. //
  1841. // Replica's version vector
  1842. //
  1843. VvJoinContext->ReplicaVv = RsReplicaVv(Cmd);
  1844. RsReplicaVv(Cmd) = NULL;
  1845. //
  1846. // Join Guid
  1847. //
  1848. COPY_GUID(&VvJoinContext->JoinGuid, RsJoinGuid(Cmd));
  1849. //
  1850. // Cxtion
  1851. //
  1852. VvJoinContext->Cxtion = GTabLookup(VvJoinContext->Replica->Cxtions,
  1853. RsCxtion(Cmd)->Guid,
  1854. NULL);
  1855. if (!VvJoinContext->Cxtion) {
  1856. DPRINT2(4, ":V: No Cxtion for %ws\\%ws; unjoining\n",
  1857. VvJoinContext->Replica->SetName->Name,
  1858. VvJoinContext->Replica->MemberName->Name);
  1859. RcsSubmitTransferToRcs(Cmd, CMD_UNJOIN);
  1860. break;
  1861. }
  1862. DPRINT2(4, ":V: VvJoining %ws\\%ws\n",
  1863. VvJoinContext->Replica->SetName->Name,
  1864. VvJoinContext->Replica->MemberName->Name);
  1865. VV_PRINT_OUTBOUND(4, L"Cxtion ", VvJoinContext->CxtionVv);
  1866. VV_PRINT_OUTBOUND(4, L"Replica ", VvJoinContext->ReplicaVv);
  1867. VVJOIN_TEST_SKIP_BEGIN(VvJoinContext, Cmd);
  1868. //
  1869. // Init the table context and open the ID table.
  1870. //
  1871. jerr = DbsOpenTable(VvJoinContext->ThreadCtx,
  1872. VvJoinContext->TableCtx,
  1873. VvJoinContext->Replica->ReplicaNumber,
  1874. IDTablex,
  1875. NULL);
  1876. if (!JET_SUCCESS(jerr)) {
  1877. DPRINT_JS(0,":V: ERROR - DbsOpenTable failed.", jerr);
  1878. RcsSubmitTransferToRcs(Cmd, CMD_UNJOIN);
  1879. break;
  1880. }
  1881. //
  1882. // Scan thru the IDTable by the FileGuidIndex calling
  1883. // VvJoinBuildTables() for each record to make entires
  1884. // in the vvjoin tables.
  1885. //
  1886. jerr = FrsEnumerateTable(VvJoinContext->ThreadCtx,
  1887. VvJoinContext->TableCtx,
  1888. GuidIndexx,
  1889. VvJoinBuildTables,
  1890. VvJoinContext);
  1891. //
  1892. // We're done. Return success if we made it to the end
  1893. // of the ID Table.
  1894. //
  1895. if (jerr != JET_errNoCurrentRecord ) {
  1896. DPRINT_JS(0,":V: ERROR - FrsEnumerateTable failed.", jerr);
  1897. RcsSubmitTransferToRcs(Cmd, CMD_UNJOIN);
  1898. break;
  1899. }
  1900. VVJOIN_PRINT(5, VvJoinContext);
  1901. //
  1902. // Send the files and dirs to our outbound partner in order,
  1903. // if possible. Otherwise send them on out-of-order. Stop
  1904. // on error or shutdown.
  1905. //
  1906. do {
  1907. //
  1908. // Send in order
  1909. //
  1910. NumberSent = VvJoinContext->NumberSent;
  1911. WStatus = VvJoinSendInOrder(VvJoinContext);
  1912. //
  1913. // Send out of order
  1914. //
  1915. // If none could be sent in order, send one out-of-order
  1916. // and then try to send in-order again.
  1917. //
  1918. if (WIN_SUCCESS(WStatus) &&
  1919. !FrsIsShuttingDown &&
  1920. NumberSent == VvJoinContext->NumberSent) {
  1921. WStatus = VvJoinSendOutOfOrder(VvJoinContext);
  1922. }
  1923. } while (WIN_SUCCESS(WStatus) &&
  1924. !FrsIsShuttingDown &&
  1925. NumberSent != VvJoinContext->NumberSent);
  1926. //
  1927. // Shutting down; abort
  1928. //
  1929. if (FrsIsShuttingDown) {
  1930. WStatus = ERROR_PROCESS_ABORTED;
  1931. }
  1932. DPRINT5(1, ":V: vvjoin %s for %ws\\%ws\\%ws (%d sent)\n",
  1933. (WIN_SUCCESS(WStatus)) ? "succeeded" : "failed",
  1934. RsReplica(Cmd)->SetName->Name, RsReplica(Cmd)->MemberName->Name,
  1935. RsCxtion(Cmd)->Name, VvJoinContext->NumberSent);
  1936. VVJOIN_TEST_SKIP_END(VvJoinContext);
  1937. //
  1938. // We either finished without problems or we force an unjoin
  1939. //
  1940. if (WIN_SUCCESS(WStatus)) {
  1941. ChgOrdInjectControlCo(VvJoinContext->Replica,
  1942. VvJoinContext->Cxtion,
  1943. FCN_CO_NORMAL_VVJOIN_TERM);
  1944. VvJoinContext->NumberSent++;
  1945. if (CxtionFlagIs(VvJoinContext->Cxtion, CXTION_FLAGS_TRIGGER_SCHEDULE)) {
  1946. ChgOrdInjectControlCo(VvJoinContext->Replica,
  1947. VvJoinContext->Cxtion,
  1948. FCN_CO_END_OF_JOIN);
  1949. VvJoinContext->NumberSent++;
  1950. }
  1951. RcsSubmitTransferToRcs(Cmd, CMD_VVJOIN_SUCCESS);
  1952. } else {
  1953. ChgOrdInjectControlCo(VvJoinContext->Replica,
  1954. VvJoinContext->Cxtion,
  1955. FCN_CO_ABNORMAL_VVJOIN_TERM);
  1956. VvJoinContext->NumberSent++;
  1957. RcsSubmitTransferToRcs(Cmd, CMD_UNJOIN);
  1958. }
  1959. break;
  1960. }
  1961. case CMD_VVJOIN_DONE: {
  1962. DPRINT3(1, ":V: Stop vvjoin for %ws\\%ws\\%ws\n",
  1963. RsReplica(Cmd)->SetName->Name, RsReplica(Cmd)->MemberName->Name,
  1964. RsCxtion(Cmd)->Name);
  1965. FrsRunDownCommandServer(VvJoinCs, &VvJoinCs->Queue);
  1966. FrsCompleteCommand(Cmd, ERROR_SUCCESS);
  1967. break;
  1968. }
  1969. case CMD_VVJOIN_DONE_UNJOIN: {
  1970. DPRINT3(1, ":V: Stop vvjoin for unjoining %ws\\%ws\\%ws\n",
  1971. RsReplica(Cmd)->SetName->Name, RsReplica(Cmd)->MemberName->Name,
  1972. RsCxtion(Cmd)->Name);
  1973. FrsCompleteCommand(Cmd, ERROR_SUCCESS);
  1974. break;
  1975. }
  1976. default: {
  1977. DPRINT1(0, ":V: ERROR - Unknown command %08x\n", Cmd->Command);
  1978. FrsCompleteCommand(Cmd, ERROR_INVALID_PARAMETER);
  1979. break;
  1980. }
  1981. } // end of switch
  1982. //
  1983. // Clean up our context
  1984. //
  1985. VvJoinContext = VvJoinFreeContext(VvJoinContext);
  1986. }
  1987. VvJoinContext = VvJoinFreeContext(VvJoinContext);
  1988. DPRINT(1, ":S: Vv Join Thread is exiting.\n");
  1989. FrsExitCommandServer(VvJoinCs, FrsThread);
  1990. DPRINT(1, ":S: CAN'T EXIT, YET; Vv Join Thread is still running.\n");
  1991. goto CANT_EXIT_YET;
  1992. //
  1993. // Get exception status.
  1994. //
  1995. } except (EXCEPTION_EXECUTE_HANDLER) {
  1996. GET_EXCEPTION_CODE(WStatus);
  1997. }
  1998. } finally {
  1999. if (WIN_SUCCESS(WStatus)) {
  2000. if (AbnormalTermination()) {
  2001. WStatus = ERROR_OPERATION_ABORTED;
  2002. }
  2003. }
  2004. DPRINT_WS(0, "VvJoinCs finally.", WStatus);
  2005. //
  2006. // Trigger FRS shutdown if we terminated abnormally.
  2007. //
  2008. if (!WIN_SUCCESS(WStatus) && (WStatus != ERROR_PROCESS_ABORTED)) {
  2009. DPRINT(0, "VvJoinCs terminated abnormally, forcing service shutdown.\n");
  2010. FrsIsShuttingDown = TRUE;
  2011. SetEvent(ShutDownEvent);
  2012. } else {
  2013. WStatus = ERROR_SUCCESS;
  2014. }
  2015. }
  2016. return WStatus;
  2017. }
  2018. VOID
  2019. SubmitVvJoin(
  2020. IN PREPLICA Replica,
  2021. IN PCXTION Cxtion,
  2022. IN USHORT Command
  2023. )
  2024. /*++
  2025. Routine Description:
  2026. Submit a command to a vvjoin command server.
  2027. Arguments:
  2028. Replica
  2029. Cxtion
  2030. Command
  2031. Return Value:
  2032. None.
  2033. --*/
  2034. {
  2035. #undef DEBSUB
  2036. #define DEBSUB "SubmitVvJoin:"
  2037. PCOMMAND_PACKET Cmd;
  2038. //
  2039. // Don't create command servers during shutdown
  2040. // Obviously, the check isn't protected and so
  2041. // a command server may get kicked off and never
  2042. // rundown if the replica subsystem shutdown
  2043. // function has already been called BUT the
  2044. // vvjoin threads use the exittombstone so
  2045. // the shutdown thread won't wait too long for the
  2046. // vvjoin threads to exit.
  2047. //
  2048. if (FrsIsShuttingDown) {
  2049. return;
  2050. }
  2051. //
  2052. // First submission; create the command server
  2053. //
  2054. if (!Cxtion->VvJoinCs) {
  2055. Cxtion->VvJoinCs = FrsAlloc(sizeof(COMMAND_SERVER));
  2056. FrsInitializeCommandServer(Cxtion->VvJoinCs,
  2057. VVJOIN_MAXTHREADS_PER_CXTION,
  2058. L"VvJoinCs",
  2059. MainVvJoin);
  2060. }
  2061. Cmd = FrsAllocCommand(&Cxtion->VvJoinCs->Queue, Command);
  2062. FrsSetCompletionRoutine(Cmd, RcsCmdPktCompletionRoutine, NULL);
  2063. //
  2064. // Outbound version vector
  2065. //
  2066. RsReplica(Cmd) = Replica;
  2067. RsCxtion(Cmd) = FrsDupGName(Cxtion->Name);
  2068. RsJoinGuid(Cmd) = FrsDupGuid(&Cxtion->JoinGuid);
  2069. RsVVector(Cmd) = VVDupOutbound(Cxtion->VVector);
  2070. RsReplicaVv(Cmd) = VVDupOutbound(Replica->VVector);
  2071. //
  2072. // And away we go
  2073. //
  2074. DPRINT5(4, ":V: Submit %08x for Cmd %08x %ws\\%ws\\%ws\n",
  2075. Cmd->Command, Cmd, RsReplica(Cmd)->SetName->Name,
  2076. RsReplica(Cmd)->MemberName->Name, RsCxtion(Cmd)->Name);
  2077. FrsSubmitCommandServer(Cxtion->VvJoinCs, Cmd);
  2078. }
  2079. DWORD
  2080. SubmitVvJoinSync(
  2081. IN PREPLICA Replica,
  2082. IN PCXTION Cxtion,
  2083. IN USHORT Command
  2084. )
  2085. /*++
  2086. Routine Description:
  2087. Submit a command to a vvjoin command server.
  2088. Arguments:
  2089. Replica
  2090. Cxtion
  2091. Command
  2092. Return Value:
  2093. None.
  2094. --*/
  2095. {
  2096. #undef DEBSUB
  2097. #define DEBSUB "SubmitVvJoinSync:"
  2098. PCOMMAND_PACKET Cmd;
  2099. DWORD WStatus;
  2100. //
  2101. // First submission; done
  2102. //
  2103. if (!Cxtion->VvJoinCs) {
  2104. return ERROR_SUCCESS;
  2105. }
  2106. Cmd = FrsAllocCommand(&Cxtion->VvJoinCs->Queue, Command);
  2107. FrsSetCompletionRoutine(Cmd, RcsCmdPktCompletionRoutine, NULL);
  2108. //
  2109. // Outbound version vector
  2110. //
  2111. RsReplica(Cmd) = Replica;
  2112. RsCxtion(Cmd) = FrsDupGName(Cxtion->Name);
  2113. RsCompletionEvent(Cmd) = FrsCreateEvent(TRUE, FALSE);
  2114. //
  2115. // And away we go
  2116. //
  2117. DPRINT5(4, ":V: Submit Sync %08x for Cmd %08x %ws\\%ws\\%ws\n",
  2118. Cmd->Command, Cmd, RsReplica(Cmd)->SetName->Name,
  2119. RsReplica(Cmd)->MemberName->Name, RsCxtion(Cmd)->Name);
  2120. FrsSubmitCommandServer(Cxtion->VvJoinCs, Cmd);
  2121. //
  2122. // Wait for the command to finish
  2123. //
  2124. WaitForSingleObject(RsCompletionEvent(Cmd), INFINITE);
  2125. FRS_CLOSE(RsCompletionEvent(Cmd));
  2126. WStatus = Cmd->ErrorStatus;
  2127. FrsCompleteCommand(Cmd, Cmd->ErrorStatus);
  2128. return WStatus;
  2129. }