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.

14718 lines
561 KiB

  1. #define SERIALIZE_CO 0
  2. /*++
  3. Copyright (c) 1997-1999 Microsoft Corporation
  4. Module Name:
  5. chgorder.c
  6. Abstract:
  7. This module processes change orders from either the local
  8. journal or the inbound partners. It makes the accept/reject
  9. decision, putting the change order into the correct inbound log
  10. if accepted.
  11. Author:
  12. David A. Orbits 23-June-1997
  13. Environment
  14. User mode, winnt32
  15. --*/
  16. #include <ntreppch.h>
  17. #pragma hdrstop
  18. #include <frs.h>
  19. #include <tablefcn.h>
  20. #include <test.h>
  21. #include <perrepsr.h>
  22. //
  23. // Change order entry Flags.
  24. //
  25. FLAG_NAME_TABLE CoeFlagNameTable[] = {
  26. {COE_FLAG_IN_AGING_CACHE , "InAgingCache " },
  27. {COE_FLAG_RECOVERY_CO , "RecoveryCo " },
  28. {COE_FLAG_JUST_TOMBSTONE , "JustTombstone " },
  29. {COE_FLAG_VOL_COLIST_BLOCKED , "CoListBlked " },
  30. {COE_FLAG_MOVEOUT_ENUM_DONE , "MovOutEnumDone "},
  31. {COE_FLAG_NO_INBOUND , "NoInbound " },
  32. {COE_FLAG_STAGE_ABORTED , "StageAbort " },
  33. {COE_FLAG_STAGE_DELETED , "StageDeleted " },
  34. {COE_FLAG_REJECT_AT_RECONCILE , "RejectAtRecon " },
  35. {COE_FLAG_DELETE_GEN_CO , "DeleteGenCo " },
  36. {COE_FLAG_REANIMATION , "Reanimation " },
  37. {COE_FLAG_PARENT_REANIMATION , "ParReanimation "},
  38. {COE_FLAG_PARENT_RISE_REQ , "ParRiseRequest "},
  39. {COE_FLAG_MORPH_GEN_FOLLOWER , "MGFollower " },
  40. {COE_FLAG_MG_FOLLOWER_MADE , "MGFollowerMade "},
  41. {COE_FLAG_NEED_RENAME , "NeedRename " },
  42. {COE_FLAG_NEED_DELETE , "NeedDelete " },
  43. {COE_FLAG_PREINSTALL_CRE , "PreInstallCre " },
  44. {COE_FLAG_TRY_OVRIDE_INSTALL , "TryOvrRideInst "},
  45. {COE_FLAG_IDT_ORIG_PARENT_DEL , "IdtOrigParDel " },
  46. {COE_FLAG_IDT_ORIG_PARENT_ABS , "IdtOrigParAbs " },
  47. {COE_FLAG_IDT_NEW_PARENT_DEL , "IdtNewParDel " },
  48. {COE_FLAG_IDT_NEW_PARENT_ABS , "IdtNewParAbs " },
  49. {COE_FLAG_IDT_TARGET_DEL , "IdtTargetDel " },
  50. {COE_FLAG_IDT_TARGET_ABS , "IdtTargetAbs " },
  51. {COE_FLAG_OID_FROM_FILE , "OidFmFile " },
  52. {COE_FLAG_IDT_NEW_PARENT_DEL_DEF , "IdtNewParDelDef " },
  53. {0, NULL}
  54. };
  55. //
  56. // Issue Cleanup Flags.
  57. //
  58. FLAG_NAME_TABLE IscuFlagNameTable[] = {
  59. {ISCU_CO_ABORT , "AbortCo " },
  60. {ISCU_UPDATE_IDT_VVFLAGS , "UpdIdtVVFlags " },
  61. {ISCU_ACK_INBOUND , "AckInb " },
  62. {ISCU_UPDATEVV_DB , "UpdateVVDb " },
  63. {ISCU_ACTIVATE_VV , "ActivateVV " },
  64. {ISCU_ACTIVATE_VV_DISCARD , "ActVVDiscard " },
  65. {ISCU_INS_OUTLOG , "InsOutlog " },
  66. {ISCU_INS_OUTLOG_NEW_GUID , "InsOLogNewGuid "},
  67. {ISCU_DEL_IDT_ENTRY , "DelIdt " },
  68. {ISCU_UPDATE_IDT_ENTRY , "UpdIdt " },
  69. {ISCU_UPDATE_IDT_FLAGS , "UpdIDTFlags " },
  70. {ISCU_UPDATE_IDT_FILEUSN , "UpdIdtFileUsn " },
  71. {ISCU_UPDATE_IDT_VERSION , "UpdIDTVersion " },
  72. {ISCU_DEL_PREINSTALL , "DelPreInstall " },
  73. {ISCU_DEL_STAGE_FILE , "DelStageF " },
  74. {ISCU_DEL_STAGE_FILE_IF , "DelStageFIf " },
  75. {ISCU_AIBCO , "AIBCO " },
  76. {ISCU_ACTIVE_CHILD , "ActChild " },
  77. {ISCU_NC_TABLE , "NamConfTbl " },
  78. {ISCU_CHECK_ISSUE_BLOCK , "ChkIsBlk " },
  79. {ISCU_DEC_CO_REF , "DecCoRef " },
  80. {ISCU_DEL_RTCTX , "DelRtCtx " },
  81. {ISCU_DEL_INLOG , "DelInlog " },
  82. {ISCU_UPDATE_INLOG , "UpdInlog " },
  83. {ISCU_FREE_CO , "FreeCo " },
  84. {ISCU_NO_CLEANUP_MERGE , "NoFlagMrg " },
  85. {0, NULL}
  86. };
  87. //
  88. // Set to TRUE in ChgOrdAcceptShutdown() to shutdown the change order accept
  89. // thread. Errors and other problems may prevent the journal thread from
  90. // running down all of the change order accept queues. For now, use this
  91. // boolean to help shutdown the change order accept thread.
  92. //
  93. BOOL ChangeOrderAcceptIsShuttingDown;
  94. ULONG ChgOrdNextRetryTime;
  95. PCHAR IbcoStateNames[IBCO_MAX_STATE+1];
  96. ULONG UpdateInlogState[] = {COStatex, COFlagsx, COIFlagsx, COExtensionx
  97. /*, COContentCmdx, COLcmdx */};
  98. //
  99. // The following IDTable record fields may be updated when a change order goes
  100. // through the retry path.
  101. //
  102. ULONG IdtCoRetryFieldList[] = {Flagsx, CurrentFileUsnx};
  103. #define IdtCoRetryFieldCount (sizeof(IdtCoRetryFieldList) / sizeof(ULONG))
  104. //
  105. // The following IDTable record are updated for a CO that reanimates a
  106. // deleted file/dir.
  107. //
  108. ULONG IdtFieldReanimateList[] = {FileIDx, FileNamex, ParentGuidx, ParentFileIDx};
  109. #define IdtCoReanimateFieldCount (sizeof(IdtFieldReanimateList) / sizeof(ULONG))
  110. //
  111. // The max number of IDTable fields that might be updated in the retire path.
  112. //
  113. #define CO_ACCEPT_IDT_FIELD_UPDATE_MAX 13
  114. // Note: Change this to init from either the DS or the registry.
  115. #if SERIALIZE_CO
  116. BOOL SerializeAllChangeOrders = TRUE;
  117. #else
  118. BOOL SerializeAllChangeOrders = FALSE;
  119. #endif
  120. //
  121. // 64 bit Global sequence number and related lock.
  122. //
  123. ULONGLONG GlobSeqNum = QUADZERO;
  124. CRITICAL_SECTION GlobSeqNumLock;
  125. extern FRS_QUEUE FrsVolumeLayerCOList;
  126. extern FRS_QUEUE FrsVolumeLayerCOQueue;
  127. extern FRS_QUEUE VolumeMonitorQueue; // for test
  128. extern HANDLE JournalCompletionPort; // for test
  129. extern PCHAR CoLocationNames[];
  130. //
  131. // To test for change orders with bogus event times.
  132. //
  133. extern ULONGLONG MaxPartnerClockSkew; // 100 ns units.
  134. extern DWORD PartnerClockSkew; // minutes
  135. extern ULONG ChangeOrderAgingDelay;
  136. //
  137. // Fixed guid for the dummy cxtion (aka GhostCxtion) assigned to orphan remote
  138. // change orders whose inbound cxtion has been deleted from the DS but they
  139. // have already past the fetching state and can finish without the real cxtion
  140. // coming back. No authentication checks are made for this dummy cxtion.
  141. //
  142. extern GUID FrsGuidGhostCxtion;
  143. extern GUID FrsGhostJoinGuid;
  144. extern PCXTION FrsGhostCxtion;
  145. //
  146. // The following quadword is stored in a QHash table to track the number of
  147. // active change orders with a given parent directory. A change order that
  148. // arrives on the parent is blocked until all the changeorders on the children
  149. // have finished.
  150. //
  151. typedef struct _CHILD_ACTIVE_ {
  152. union {
  153. struct {
  154. ULONG Count; // Number of active children
  155. PCHANGE_ORDER_ENTRY ChangeOrder; //
  156. };
  157. ULONGLONG Data;
  158. };
  159. } CHILD_ACTIVE, *PCHILD_ACTIVE;
  160. typedef struct _MOVEIN_CONTEXT {
  161. ULONG NumFiles;
  162. ULONG NumDirs;
  163. ULONG NumSkipped;
  164. ULONG NumFiltered;
  165. LONGLONG ParentFileID;
  166. PREPLICA Replica;
  167. } MOVEIN_CONTEXT, *PMOVEIN_CONTEXT;
  168. //
  169. // Event time window default is 30 min (in 100 nanosec units). Inited
  170. // from registry.
  171. //
  172. LONGLONG RecEventTimeWindow;
  173. //
  174. // Limits on how many time and for how long we will continue to retry a
  175. // change order when the parent is missing.
  176. //
  177. extern ULONG MaxCoRetryTimeoutMinutes;
  178. extern ULONG MaxCoRetryTimeoutCount;
  179. //
  180. // Change order retry command server.
  181. //
  182. // ** WARNING ** There is currently only one thread to service the change order
  183. // retry scans for all replica sets. The ActiveInlogRetry table associated with each
  184. // replica set tracks the sequence numbers of the change orders that have been
  185. // resubmitted. This should also guard against the simultaneous submitting of
  186. // the same change order by one thread doing a single connection retry and
  187. // another thread doing an all connection retry for the same replica set.
  188. // However, this has not been tested.
  189. //
  190. #define CHGORD_RETRY_MAXTHREADS (1)
  191. COMMAND_SERVER ChgOrdRetryCS;
  192. //
  193. // The time in ms between checks for retries in the inbound log.
  194. // (FKC_INLOG_RETRY_TIME from registry)
  195. //
  196. ULONG InlogRetryInterval;
  197. //
  198. // The time to let a ChangeORder block a process queue waiting for a Join to
  199. // complete.
  200. //
  201. #define CHG_ORD_HOLD_ISSUE_LIMIT (ULONG)(2 * 60 * 1000) // 2 min
  202. //
  203. // Struct for the change order accept server
  204. // Contains info about the queues and the threads
  205. // Warning: Check for needed locks if we allow multiple threads.
  206. //
  207. #define CHGORDER_MAXTHREADS (1)
  208. #define CHGORDER_RETRY_MIN (1 * 1000) // 1 second
  209. #define CHGORDER_RETRY_MAX (5 * 1000) // 5 seconds
  210. #define ISSUE_OK 0
  211. #define ISSUE_CONFLICT 1
  212. #define ISSUE_DUPLICATE_REMOTE_CO 2
  213. #define ISSUE_REJECT_CO 4
  214. #define ISSUE_RETRY_LATER 5
  215. #define REANIMATE_SUCCESS 0
  216. #define REAMIMATE_RETRY_LATER 1
  217. #define REANIMATE_CO_REJECTED 2
  218. #define REANIMATE_FAILED 3
  219. #define REANIMATE_NO_NEED 4
  220. //
  221. // ChgOrdInsertProcQ Flag defs.
  222. //
  223. #define IPQ_HEAD 0x00000000
  224. #define IPQ_TAIL 0x00000001
  225. #define IPQ_TAKE_COREF 0x00000002
  226. #define IPQ_ASSERT_IF_ERR 0x00000004
  227. #define IPQ_DEREF_CXTION_IF_ERR 0x00000008
  228. #define IPQ_MG_LOOP_CHK 0x00000010
  229. #define SIZEOF_RENAME_SUFFIX (7 + 8) // _NTFRS_<tic count in hex>
  230. #define MAX_RETRY_SET_OBJECT_ID 10 // retry 10 times
  231. SubmitReplicaJoinWait(
  232. IN PREPLICA Replica,
  233. IN PCXTION Cxtion,
  234. IN ULONG TimeOut
  235. );
  236. ULONG
  237. ChgOrdFetchCmd(
  238. PFRS_QUEUE *pIdledQueue,
  239. PCHANGE_ORDER_ENTRY *pChangeOrder,
  240. PVOLUME_MONITOR_ENTRY *ppVme
  241. );
  242. ULONG
  243. ChgOrdHoldIssue(
  244. IN PREPLICA Replica,
  245. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  246. IN PFRS_QUEUE CoProcessQueue
  247. );
  248. BOOL
  249. ChgOrdReconcile(
  250. PREPLICA Replica,
  251. PCHANGE_ORDER_ENTRY ChangeOrder,
  252. PIDTABLE_RECORD IDTableRec
  253. );
  254. BOOL
  255. ChgOrdGenMorphCo(
  256. PTHREAD_CTX ThreadCtx,
  257. PTABLE_CTX TmpIDTableCtxNC,
  258. PCHANGE_ORDER_ENTRY ChangeOrder,
  259. PREPLICA Replica
  260. );
  261. BOOL
  262. ChgOrdApplyMorphCo(
  263. PCHANGE_ORDER_ENTRY ChangeOrder,
  264. PREPLICA Replica
  265. );
  266. ULONG
  267. ChgOrdReanimate(
  268. IN PTABLE_CTX IDTableCtx,
  269. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  270. IN PREPLICA Replica,
  271. IN PTHREAD_CTX ThreadCtx,
  272. IN PTABLE_CTX TmpIDTableCtx
  273. );
  274. BOOL
  275. ChgOrdMakeDeleteCo(
  276. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  277. IN PREPLICA Replica,
  278. IN PTHREAD_CTX ThreadCtx,
  279. IN PIDTABLE_RECORD IDTableRec
  280. );
  281. BOOL
  282. ChgOrdMakeRenameCo(
  283. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  284. IN PREPLICA Replica,
  285. IN PTHREAD_CTX ThreadCtx,
  286. IN PIDTABLE_RECORD IDTableRec
  287. );
  288. BOOL
  289. ChgOrdConvertCo(
  290. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  291. IN PREPLICA Replica,
  292. IN PTHREAD_CTX ThreadCtx,
  293. IN ULONG LocationCmd
  294. );
  295. BOOL
  296. ChgOrdReserve(
  297. IN PIDTABLE_RECORD IDTableRec,
  298. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  299. IN PREPLICA Replica
  300. );
  301. ULONG
  302. ChgOrdDispatch(
  303. PTHREAD_CTX ThreadCtx,
  304. PIDTABLE_RECORD IDTableRec,
  305. PCHANGE_ORDER_ENTRY ChangeOrder,
  306. PREPLICA Replica
  307. );
  308. VOID
  309. ChgOrdReject(
  310. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  311. IN PREPLICA Replica
  312. );
  313. ULONG
  314. ChgOrdIssueCleanup(
  315. PTHREAD_CTX ThreadCtx,
  316. PREPLICA Replica,
  317. PCHANGE_ORDER_ENTRY ChangeOrder,
  318. ULONG CleanUpFlags
  319. );
  320. ULONG
  321. ChgOrdUpdateIDTableRecord(
  322. PTHREAD_CTX ThreadCtx,
  323. PREPLICA Replica,
  324. PCHANGE_ORDER_ENTRY ChangeOrder
  325. );
  326. ULONG
  327. ChgOrdReadIDRecord(
  328. PTHREAD_CTX ThreadCtx,
  329. PTABLE_CTX IDTableCtx,
  330. PTABLE_CTX IDTableCtxNC,
  331. PCHANGE_ORDER_ENTRY ChangeOrder,
  332. PREPLICA Replica,
  333. BOOL *IDTableRecExists
  334. );
  335. ULONG
  336. ChgOrdInsertIDRecord(
  337. PTHREAD_CTX ThreadCtx,
  338. PTABLE_CTX IDTableCtx,
  339. PTABLE_CTX DIRTableCtx,
  340. PCHANGE_ORDER_ENTRY ChangeOrder,
  341. PREPLICA Replica
  342. );
  343. ULONG
  344. ChgOrdInsertDirRecord(
  345. PTHREAD_CTX ThreadCtx,
  346. PTABLE_CTX DIRTableCtx,
  347. PIDTABLE_RECORD IDTableRec,
  348. PCHANGE_ORDER_ENTRY ChangeOrder,
  349. PREPLICA Replica,
  350. BOOL InsertFlag
  351. );
  352. ULONG
  353. ChgOrdCheckAndFixTunnelConflict(
  354. PTHREAD_CTX ThreadCtx,
  355. PTABLE_CTX IDTableCtxNew,
  356. PTABLE_CTX IDTableCtxExist,
  357. PREPLICA Replica,
  358. PCHANGE_ORDER_ENTRY ChangeOrder,
  359. BOOL *IDTableRecExists
  360. );
  361. ULONG
  362. ChgOrdCheckNameMorphConflict(
  363. PTHREAD_CTX ThreadCtx,
  364. PTABLE_CTX IDTableCtxNC,
  365. ULONG ReplicaNumber,
  366. PCHANGE_ORDER_ENTRY ChangeOrder
  367. );
  368. VOID
  369. ChgOrdProcessControlCo(
  370. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  371. IN PREPLICA Replica
  372. );
  373. VOID
  374. ChgOrdTranslateGuidFid(
  375. PTHREAD_CTX ThreadCtx,
  376. PTABLE_CTX IDTableCtx,
  377. PCHANGE_ORDER_ENTRY ChangeOrder,
  378. PREPLICA Replica
  379. );
  380. ULONG
  381. ChgOrdInsertInlogRecord(
  382. PTHREAD_CTX ThreadCtx,
  383. PTABLE_CTX InLogTableCtx,
  384. PCHANGE_ORDER_ENTRY ChangeOrder,
  385. PREPLICA Replica,
  386. BOOL RetryOutOfOrder
  387. );
  388. typedef struct _MOVEOUT_CONTEXT {
  389. ULONG NumFiles;
  390. LONGLONG ParentFileID;
  391. PREPLICA Replica;
  392. } MOVEOUT_CONTEXT, *PMOVEOUT_CONTEXT;
  393. JET_ERR
  394. ChgOrdMoveoutWorker(
  395. IN PTHREAD_CTX ThreadCtx,
  396. IN PTABLE_CTX TableCtx,
  397. IN PVOID Record,
  398. IN PVOID Context
  399. );
  400. DWORD
  401. ChgOrdStealObjectId(
  402. IN PWCHAR Name,
  403. IN PVOID Fid,
  404. IN PVOLUME_MONITOR_ENTRY pVme,
  405. OUT USN *Usn,
  406. IN OUT PFILE_OBJECTID_BUFFER FileObjID
  407. );
  408. DWORD
  409. ChgOrdSkipBasicInfoChange(
  410. IN PCHANGE_ORDER_ENTRY Coe,
  411. IN PBOOL SkipCo
  412. );
  413. JET_ERR
  414. ChgOrdRetryWorker(
  415. IN PTHREAD_CTX ThreadCtx,
  416. IN PTABLE_CTX TableCtx,
  417. IN PVOID Record,
  418. IN PVOID Context
  419. );
  420. JET_ERR
  421. ChgOrdRetryWorkerPreRead(
  422. IN PTHREAD_CTX ThreadCtx,
  423. IN PTABLE_CTX TableCtx,
  424. IN PVOID Context
  425. );
  426. ULONG
  427. ChgOrdMoveInDirTree(
  428. IN PREPLICA Replica,
  429. IN PCHANGE_ORDER_ENTRY ChangeOrder
  430. );
  431. ULONG
  432. ChgOrdRetryThread(
  433. PVOID FrsThreadCtxArg
  434. );
  435. PCHANGE_ORDER_ENTRY
  436. ChgOrdMakeFromFile(
  437. IN PREPLICA Replica,
  438. IN HANDLE FileHandle,
  439. IN PULONGLONG ParentFid,
  440. IN ULONG LocationCmd,
  441. IN ULONG CoFlags,
  442. IN PWCHAR FileName,
  443. IN USHORT Length
  444. );
  445. PCHANGE_ORDER_ENTRY
  446. ChgOrdMakeFromIDRecord(
  447. IN PIDTABLE_RECORD IDTableRec,
  448. IN PREPLICA Replica,
  449. IN ULONG LocationCmd,
  450. IN ULONG CoFlags,
  451. IN GUID *CxtionGuid
  452. );
  453. ULONG
  454. ChgOrdInsertProcessQueue(
  455. IN PREPLICA Replica,
  456. IN PCHANGE_ORDER_COMMAND ChangeOrder,
  457. IN ULONG CoeFlags,
  458. IN PCXTION Cxtion
  459. );
  460. VOID
  461. ChgOrdRetrySubmit(
  462. IN PREPLICA Replica,
  463. IN PCXTION Cxtion,
  464. IN USHORT Command,
  465. IN BOOL Wait
  466. );
  467. PCHANGE_ORDER_RECORD_EXTENSION
  468. DbsDataConvertCocExtensionFromWin2K(
  469. IN PCO_RECORD_EXTENSION_WIN2K CocExtW2K
  470. );
  471. BOOL
  472. JrnlDoesChangeOrderHaveChildren(
  473. IN PTHREAD_CTX ThreadCtx,
  474. IN PTABLE_CTX TmpIDTableCtx,
  475. IN PCHANGE_ORDER_ENTRY ChangeOrder
  476. );
  477. ULONG
  478. JrnlGetPathAndLevel(
  479. IN PGENERIC_HASH_TABLE FilterTable,
  480. IN PLONGLONG StartDirFileID,
  481. OUT PULONG Level
  482. );
  483. ULONG
  484. JrnlAddFilterEntryFromCo(
  485. IN PREPLICA Replica,
  486. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  487. OUT PFILTER_TABLE_ENTRY *RetFilterEntry
  488. );
  489. ULONG
  490. JrnlDeleteDirFilterEntry(
  491. IN PGENERIC_HASH_TABLE FilterTable,
  492. IN PULONGLONG DFileID,
  493. IN PFILTER_TABLE_ENTRY ArgFilterEntry
  494. );
  495. ULONG
  496. JrnlHashCalcGuid (
  497. PVOID Buf,
  498. ULONG Length
  499. );
  500. ULONG
  501. OutLogInsertCo(
  502. PTHREAD_CTX ThreadCtx,
  503. PREPLICA Replica,
  504. PTABLE_CTX OutLogTableCtx,
  505. PCHANGE_ORDER_ENTRY ChangeOrder
  506. );
  507. VOID
  508. ChgOrdCalcHashGuidAndName (
  509. IN PUNICODE_STRING Name,
  510. IN GUID *Guid,
  511. OUT PULONGLONG HashValue
  512. );
  513. LONG
  514. FrsGuidCompare (
  515. IN GUID *Guid1,
  516. IN GUID *Guid2
  517. );
  518. DWORD
  519. FrsGetFileInternalInfoByHandle(
  520. IN HANDLE Handle,
  521. OUT PFILE_INTERNAL_INFORMATION InternalFileInfo
  522. );
  523. ULONG
  524. DbsUpdateIDTableFields(
  525. IN PTHREAD_CTX ThreadCtx,
  526. IN PREPLICA Replica,
  527. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  528. IN PULONG RecordFieldx,
  529. IN ULONG FieldCount
  530. );
  531. DWORD
  532. StuDelete(
  533. IN PCHANGE_ORDER_ENTRY Coe
  534. );
  535. DWORD
  536. StuCreatePreInstallFile(
  537. IN PCHANGE_ORDER_ENTRY Coe
  538. );
  539. VOID
  540. ChgOrdDelayCommand(
  541. IN PCOMMAND_PACKET Cmd,
  542. IN PFRS_QUEUE IdledQueue,
  543. IN PCOMMAND_SERVER CmdServer,
  544. IN OUT PULONG TimeOut
  545. )
  546. /*++
  547. Routine Description:
  548. Put the command back on the head of the queue and submit a
  549. delayed command to unidle the queue. When the "unidle" occurs,
  550. Cmd will be reissued. Retry forever.
  551. Arguments:
  552. Cmd -- The command packet to delay
  553. IdledQueue --
  554. CmdServer -- The command server to reactivate
  555. TimeOut -- ptr to the time out value.
  556. Return Value:
  557. None.
  558. --*/
  559. {
  560. //
  561. // Put the command back at the head of the queue
  562. //
  563. FrsUnSubmitCommand(Cmd);
  564. //
  565. // Extend the retry time (but not too long)
  566. //
  567. *TimeOut <<= 1;
  568. if (*TimeOut > CHGORDER_RETRY_MAX)
  569. *TimeOut = CHGORDER_RETRY_MAX;
  570. //
  571. // or too short
  572. //
  573. if (*TimeOut < CHGORDER_RETRY_MIN)
  574. *TimeOut = CHGORDER_RETRY_MIN;
  575. //
  576. // This queue will be unidled later. At that time, a thread will
  577. // retry the command at the head of the queue.
  578. //
  579. FrsDelCsSubmitUnIdled(CmdServer, IdledQueue, *TimeOut);
  580. }
  581. VOID
  582. ChgOrdRetryPerfmonCount(
  583. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  584. IN PREPLICA Replica
  585. )
  586. /*++
  587. Routine Description:
  588. Tally the perfmon counters for retry change orders.
  589. Arguments:
  590. ChangeOrder-- The change order.
  591. Replica -- The Replica struct.
  592. Return Value:
  593. None.
  594. --*/
  595. {
  596. #undef DEBSUB
  597. #define DEBSUB "ChgOrdRetryPerfmonCount:"
  598. BOOL LocalCo, ControlCo;
  599. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  600. ControlCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_CONTROL);
  601. //
  602. // Increment the Change orders (Local and Remote) retried at
  603. // (Generate, Fetch, Install, Rename)
  604. //
  605. if (LocalCo) {
  606. //
  607. // Retried at Generate
  608. //
  609. if (CO_STATE_IS(ChangeOrder, IBCO_STAGING_RETRY)) {
  610. PM_INC_CTR_REPSET(Replica, LCORetriedGen, 1);
  611. }
  612. //
  613. // Retried at Fetch
  614. //
  615. else if (CO_STATE_IS(ChangeOrder, IBCO_FETCH_RETRY)) {
  616. PM_INC_CTR_REPSET(Replica, LCORetriedFet, 1);
  617. }
  618. //
  619. // Retried at Install
  620. //
  621. else if (CO_STATE_IS(ChangeOrder, IBCO_INSTALL_RETRY)) {
  622. PM_INC_CTR_REPSET(Replica, LCORetriedIns, 1);
  623. }
  624. //
  625. // Retried at Rename or Delete
  626. //
  627. else if (CO_STATE_IS(ChangeOrder, IBCO_INSTALL_REN_RETRY) ||
  628. CO_STATE_IS(ChangeOrder, IBCO_INSTALL_DEL_RETRY)) {
  629. PM_INC_CTR_REPSET(Replica, LCORetriedRen, 1);
  630. }
  631. }
  632. //
  633. // If it's not Local and Control CO it must be a Remote CO
  634. //
  635. else if (!ControlCo) {
  636. //
  637. // Retried at Generate
  638. //
  639. if (CO_STATE_IS(ChangeOrder, IBCO_STAGING_RETRY)) {
  640. PM_INC_CTR_REPSET(Replica, RCORetriedGen, 1);
  641. }
  642. //
  643. // Retried at Fetch
  644. //
  645. else if (CO_STATE_IS(ChangeOrder, IBCO_FETCH_RETRY)) {
  646. PM_INC_CTR_REPSET(Replica, RCORetriedFet, 1);
  647. }
  648. //
  649. // Retried at Install
  650. //
  651. else if (CO_STATE_IS(ChangeOrder, IBCO_INSTALL_RETRY)) {
  652. PM_INC_CTR_REPSET(Replica, RCORetriedIns, 1);
  653. }
  654. //
  655. // Retried at Rename or Delete
  656. //
  657. else if (CO_STATE_IS(ChangeOrder, IBCO_INSTALL_REN_RETRY) ||
  658. CO_STATE_IS(ChangeOrder, IBCO_INSTALL_DEL_RETRY)) {
  659. PM_INC_CTR_REPSET(Replica, RCORetriedRen, 1);
  660. }
  661. }
  662. }
  663. DWORD
  664. ChgOrdInsertProcQ(
  665. IN PREPLICA Replica,
  666. IN PCHANGE_ORDER_ENTRY Coe,
  667. IN DWORD Flags
  668. )
  669. /*++
  670. Routine Description:
  671. Insert the change order (Coe) onto the process queue associated with this
  672. Replica's VME process queue.
  673. IPQ_HEAD - Insert on head of queue
  674. IPQ_TAIL - Insert on tail of queue
  675. IPQ_TAKE_COREF - Increment the ref count on the change order.
  676. IPQ_ASSERT_IF_ERR - If insert fails then ASSERT.
  677. IPQ_DEREF_CXTION_IF_ERR - If insert fails then drop the changeorder count
  678. for the connection assiociated with the CO.
  679. IPQ_MG_LOOP_CHK - Check if this CO is in a MorphGen Loop.
  680. Arguments:
  681. Replica -- The Replica struct.
  682. Coe -- The change order.
  683. Flags -- see above.
  684. Return Value:
  685. Win32 Status.
  686. --*/
  687. {
  688. #undef DEBSUB
  689. #define DEBSUB "ChgOrdInsertProcQ:"
  690. DWORD WStatus;
  691. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  692. if (BooleanFlagOn(Flags, IPQ_TAKE_COREF) && (++Coe->CoMorphGenCount > 50)) {
  693. DPRINT(0, "++ ERROR: ChangeOrder caught in morph gen loop\n");
  694. FRS_PRINT_TYPE(0, Coe);
  695. FRS_ASSERT(!"ChangeOrder caught in morph gen loop");
  696. return ERROR_OPERATION_ABORTED;
  697. }
  698. //
  699. // Init the cleanup flags, set the ONLIST CO flag, set entry create time
  700. // for no aging delay.
  701. //
  702. ZERO_ISSUE_CLEANUP(Coe);
  703. SET_CO_FLAG(Coe, CO_FLAG_ONLIST);
  704. Coe->EntryCreateTime = Coe->TimeToRun = CO_TIME_NOW(pVme);
  705. if (BooleanFlagOn(Flags, IPQ_TAKE_COREF)) {
  706. INCREMENT_CHANGE_ORDER_REF_COUNT(Coe);
  707. }
  708. if (BooleanFlagOn(Flags, IPQ_TAIL)) {
  709. WStatus = FrsRtlInsertTailQueue(&pVme->ChangeOrderList, &Coe->ProcessList);
  710. } else {
  711. WStatus = FrsRtlInsertHeadQueue(&pVme->ChangeOrderList, &Coe->ProcessList);
  712. }
  713. if (!WIN_SUCCESS(WStatus)) {
  714. DPRINT_WS(0, "++ ERROR - ChangeOrder insert failed.", WStatus);
  715. if (BooleanFlagOn(Flags, IPQ_DEREF_CXTION_IF_ERR)) {
  716. DROP_CO_CXTION_COUNT(Replica, Coe, WStatus);
  717. }
  718. if (BooleanFlagOn(Flags, IPQ_TAKE_COREF)) {
  719. DECREMENT_CHANGE_ORDER_REF_COUNT(Coe);
  720. }
  721. if (BooleanFlagOn(Flags, IPQ_ASSERT_IF_ERR)) {
  722. FRS_ASSERT(!"ChgOrdInsertProcQ: queue insert failed.");
  723. }
  724. }
  725. return WStatus;
  726. }
  727. DWORD
  728. ChgOrdAccept(
  729. PVOID FrsThreadCtxArg
  730. )
  731. /*++
  732. Routine Description:
  733. Entry point for processing change orders from the volume change order lists.
  734. Pull entries off the change order queue and:
  735. 1. See if they have expired. (They stay on the list for a short time to
  736. accumulate multiple changes.)
  737. 2. If there is a change order active on this file then idle the queue
  738. until the active change order completes.
  739. 3. Process expired CO by first deciding acceptance. This is
  740. provisional for early abort. The final decision occurs just prior
  741. to installing the staged file locally.
  742. 4. Make entry in the inbound hash table
  743. 5. Make entry in the inbound log for this replica set and get the
  744. CO sequence number. State is IBCO_INITIALIZING.
  745. 6. Send request to staging, setting state to IBCO_STAGING_REQUESTED.
  746. If a change order is already pending for a file we stop processing
  747. further change orders until the pending request completes. If the
  748. pending request ultimately fails (say because we can't stage the files)
  749. then we abort the change order. The DB version info on the file is not
  750. updated until after the file has been installed.
  751. As the change order progresses we update the current status in the
  752. change order entry in Jet at points where we need to preserve persistence.
  753. Inbound Change Order States:
  754. IBCO_INITIALIZING Initial state when CO is first put in log.
  755. IBCO_STAGING_REQUESTED Alloc staging file space for local CO
  756. IBCO_STAGING_INITIATED LocalCO Staging file copy has started
  757. IBCO_STAGING_COMPLETE LocalCO Staging file complete
  758. At this point prelim accept rechecked and
  759. becomes either final accept of abort.
  760. Abort is caused by more recent local change.
  761. IBCO_STAGING_RETRY Waiting to retry local CO stage file generation.
  762. IBCO_FETCH_REQUESTED Alloc staging file space for remote co
  763. IBCO_FETCH_INITIATED RemoteCO staging file fetch has started
  764. IBCO_FETCH_COMPLETE RemoteCO Staging file fetch complete
  765. IBCO_FETCH_RETRY Waiting to retry remote CO stage file fetch
  766. IBCO_INSTALL_REQUESTED File install requested
  767. IBCO_INSTALL_INITIATED File install has started
  768. IBCO_INSTALL_COMPLETE File install is complete
  769. IBCO_INSTALL_WAIT File install is waiting to try again.
  770. IBCO_INSTALL_RETRY File install is retrying.
  771. IBCO_INSTALL_REN_RETRY File install rename is retrying.
  772. IBCO_INSTALL_DEL_RETRY Delete or Moveout CO finished except for final on-disk delete.
  773. IBCO_OUTBOUND_REQUEST Request outbound propagaion
  774. IBCO_OUTBOUND_ACCEPTED Request accepted and now in Outbound log
  775. IBCO_COMMIT_STARTED DB state update started.
  776. IBCO_RETIRE_STARTED DB state update done, freeing change order.
  777. IBCO_ENUM_REQUESTED CO is being recycled to do a dir enum.
  778. IBCO_ABORTING CO is being aborted.
  779. Arguments:
  780. FrsThreadCtxArg - thread
  781. Return Value:
  782. WIN32 status
  783. --*/
  784. // Note: Need to Handle MOVERS.
  785. {
  786. #undef DEBSUB
  787. #define DEBSUB "ChgOrdAccept:"
  788. JET_ERR jerr, jerr1;
  789. ULONG FStatus, RStatus, WStatus;
  790. ULONG GStatus, GStatusAC;
  791. PFRS_QUEUE IdledQueue;
  792. PFRS_THREAD FrsThread = (PFRS_THREAD)FrsThreadCtxArg;
  793. ULONG TimeOut, ULong;
  794. PVOLUME_MONITOR_ENTRY pVme;
  795. PIDTABLE_RECORD IDTableRec;
  796. PCHANGE_ORDER_RECORD CoCmd;
  797. PCHANGE_ORDER_RECORD DupCoCmd;
  798. PVOID KeyArray[2];
  799. PCHANGE_ORDER_ENTRY ChangeOrder;
  800. ULONG Decision;
  801. PTHREAD_CTX ThreadCtx;
  802. PTABLE_CTX IDTableCtx, InLogTableCtx, DIRTableCtx;
  803. PTABLE_CTX TmpIDTableCtx, TmpIDTableCtxNC, TmpINLOGTableCtx;
  804. PREPLICA Replica;
  805. BOOL MorphOK;
  806. BOOL IDTableRecExists;
  807. BOOL NewFile, LocalCo, RetryCo, ControlCo, RecoveryCo;
  808. BOOL MorphGenCo;
  809. ULONG LocationCmd;
  810. PREPLICA_THREAD_CTX RtCtx = NULL;
  811. ULONG i;
  812. BOOL SkipCo, DuplicateCo, ReAnimate, UnIdleProcessQueue;
  813. BOOL RetryPreinstall, FilePresent;
  814. BOOL RaiseTheDead, DemandRefreshCo, NameMorphConflict;
  815. BOOL JustTombstone, ParentReanimation;
  816. MOVEOUT_CONTEXT MoveOutContext;
  817. CHAR FetchTag[32] = "CO Fetch **** ";
  818. DPRINT(0, "ChangeOrder Thread is starting.\n");
  819. TimeOut = 100;
  820. ChgOrdNextRetryTime = GetTickCount();
  821. IbcoStateNames[IBCO_INITIALIZING ] = "IBCO_INITIALIZING ";
  822. IbcoStateNames[IBCO_STAGING_REQUESTED] = "IBCO_STAGING_REQUESTED";
  823. IbcoStateNames[IBCO_STAGING_INITIATED] = "IBCO_STAGING_INITIATED";
  824. IbcoStateNames[IBCO_STAGING_COMPLETE ] = "IBCO_STAGING_COMPLETE ";
  825. IbcoStateNames[IBCO_STAGING_RETRY ] = "IBCO_STAGING_RETRY ";
  826. IbcoStateNames[IBCO_FETCH_REQUESTED ] = "IBCO_FETCH_REQUESTED ";
  827. IbcoStateNames[IBCO_FETCH_INITIATED ] = "IBCO_FETCH_INITIATED ";
  828. IbcoStateNames[IBCO_FETCH_COMPLETE ] = "IBCO_FETCH_COMPLETE ";
  829. IbcoStateNames[IBCO_FETCH_RETRY ] = "IBCO_FETCH_RETRY ";
  830. IbcoStateNames[IBCO_INSTALL_REQUESTED] = "IBCO_INSTALL_REQUESTED";
  831. IbcoStateNames[IBCO_INSTALL_INITIATED] = "IBCO_INSTALL_INITIATED";
  832. IbcoStateNames[IBCO_INSTALL_COMPLETE ] = "IBCO_INSTALL_COMPLETE ";
  833. IbcoStateNames[IBCO_INSTALL_WAIT ] = "IBCO_INSTALL_WAIT ";
  834. IbcoStateNames[IBCO_INSTALL_RETRY ] = "IBCO_INSTALL_RETRY ";
  835. IbcoStateNames[IBCO_INSTALL_REN_RETRY] = "IBCO_INSTALL_REN_RETRY";
  836. IbcoStateNames[IBCO_INSTALL_DEL_RETRY] = "IBCO_INSTALL_DEL_RETRY";
  837. IbcoStateNames[IBCO_UNUSED_16 ] = "IBCO_UNUSED_16 ";
  838. IbcoStateNames[IBCO_UNUSED_17 ] = "IBCO_UNUSED_17 ";
  839. IbcoStateNames[IBCO_UNUSED_18 ] = "IBCO_UNUSED_18 ";
  840. IbcoStateNames[IBCO_OUTBOUND_REQUEST ] = "IBCO_OUTBOUND_REQUEST ";
  841. IbcoStateNames[IBCO_OUTBOUND_ACCEPTED] = "IBCO_OUTBOUND_ACCEPTED";
  842. IbcoStateNames[IBCO_COMMIT_STARTED ] = "IBCO_COMMIT_STARTED ";
  843. IbcoStateNames[IBCO_RETIRE_STARTED ] = "IBCO_RETIRE_STARTED ";
  844. IbcoStateNames[IBCO_ENUM_REQUESTED ] = "IBCO_ENUM_REQUESTED ";
  845. IbcoStateNames[IBCO_ABORTING ] = "IBCO_ABORTING ";
  846. //
  847. // Get width of reconcilliation window from registry.
  848. //
  849. CfgRegReadDWord(FKC_RECONCILE_WINDOW, NULL, 0, &ULong);
  850. RecEventTimeWindow = (LONGLONG)ULong * (LONGLONG)CONVERT_FILETIME_TO_MINUTES;
  851. //
  852. // Get Inlog retry interval.
  853. //
  854. CfgRegReadDWord(FKC_INLOG_RETRY_TIME, NULL, 0, &ULong);
  855. InlogRetryInterval = ULong * 1000;
  856. //
  857. // Init Change order lock table.
  858. //
  859. for (i=0; i<NUMBER_CHANGE_ORDER_LOCKS; i++) {
  860. INITIALIZE_CRITICAL_SECTION(&ChangeOrderLockTable[i]);
  861. }
  862. INITIALIZE_CRITICAL_SECTION(&GlobSeqNumLock);
  863. //
  864. // Initialize the retry thread
  865. //
  866. FrsInitializeCommandServer(&ChgOrdRetryCS,
  867. CHGORD_RETRY_MAXTHREADS,
  868. L"ChgOrdRetryCS",
  869. ChgOrdRetryThread);
  870. //
  871. // Allocate a context for Jet to run in this thread.
  872. //
  873. ThreadCtx = FrsAllocType(THREAD_CONTEXT_TYPE);
  874. //
  875. // Allocate Temp table contexts once for the ID table and the INLOG Table.
  876. // Don't free them until we exit.
  877. //
  878. TmpIDTableCtx = FrsAlloc(sizeof(TABLE_CTX));
  879. TmpIDTableCtx->TableType = TABLE_TYPE_INVALID;
  880. TmpIDTableCtx->Tid = JET_tableidNil;
  881. TmpIDTableCtxNC = FrsAlloc(sizeof(TABLE_CTX));
  882. TmpIDTableCtxNC->TableType = TABLE_TYPE_INVALID;
  883. TmpIDTableCtxNC->Tid = JET_tableidNil;
  884. TmpINLOGTableCtx = FrsAlloc(sizeof(TABLE_CTX));
  885. TmpINLOGTableCtx->TableType = TABLE_TYPE_INVALID;
  886. TmpINLOGTableCtx->Tid = JET_tableidNil;
  887. //
  888. // Setup a Jet Session returning the session ID in ThreadCtx.
  889. //
  890. jerr = DbsCreateJetSession(ThreadCtx);
  891. if (JET_SUCCESS(jerr)) {
  892. DPRINT(5,"JetOpenDatabase complete\n");
  893. } else {
  894. DPRINT_JS(0,"ERROR - OpenDatabase failed. Thread exiting.", jerr);
  895. jerr = DbsCloseJetSession(ThreadCtx);
  896. ThreadCtx = FrsFreeType(ThreadCtx);
  897. return ERROR_GEN_FAILURE;
  898. }
  899. DPRINT(0, "ChangeOrder Thread has started.\n");
  900. DEBUG_FLUSH();
  901. SetEvent(ChgOrdEvent);
  902. //
  903. // Free up memory by reducing our working set size
  904. //
  905. SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
  906. //
  907. // Try-Finally so we shutdown Jet cleanly.
  908. //
  909. try {
  910. //
  911. // Capture exception.
  912. //
  913. try {
  914. while (TRUE) {
  915. //
  916. // Fetch the next change order command. If successfull this returns
  917. // with the FrsVolumeLayerCOList Lock held and a reference on the Vme.
  918. //
  919. FStatus = ChgOrdFetchCmd(&IdledQueue, &ChangeOrder, &pVme);
  920. if ((FStatus == FrsErrorQueueIsRundown) || !FRS_SUCCESS(FStatus)) {
  921. //
  922. // Treat non-success status from fetch as a normal termination
  923. // in the Try-Finally clause.
  924. //
  925. WStatus = ERROR_SUCCESS;
  926. break;
  927. }
  928. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  929. CoCmd = &ChangeOrder->Cmd;
  930. strcpy(&FetchTag[14], CoLocationNames[LocationCmd]);
  931. CHANGE_ORDER_TRACEX(3, ChangeOrder, FetchTag, CoCmd->Flags);
  932. ParentReanimation = COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_REANIMATION);
  933. RecoveryCo = COE_FLAG_ON(ChangeOrder, COE_FLAG_RECOVERY_CO);
  934. DemandRefreshCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_DEMAND_REFRESH);
  935. MorphGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN);
  936. ControlCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_CONTROL);
  937. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  938. RetryCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY);
  939. UnIdleProcessQueue = TRUE;
  940. NameMorphConflict = FALSE;
  941. IDTableRecExists = FALSE;
  942. RetryPreinstall = FALSE;
  943. FilePresent = FALSE;
  944. DuplicateCo = FALSE;
  945. NewFile = FALSE;
  946. Decision = ISSUE_OK;
  947. FRS_PRINT_TYPE(4, ChangeOrder);
  948. //
  949. // We did not decrement the ref count in ChgOrdFetchCmd since the
  950. // CO was not taken off the process queue.
  951. // Keep it until processing is done.
  952. // Another reference on the CO is taken by the VV code when the retire
  953. // slot is activated. Releasing the reference is done by a request to
  954. // the issue cleanup logic. But set up to decrement the ref if the
  955. // CO is rejected.
  956. //
  957. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO | ISCU_DEC_CO_REF);
  958. //
  959. // The change order goes to the target in
  960. // NewReplica unless it's null then use the original Replica.
  961. //
  962. Replica = CO_REPLICA(ChangeOrder);
  963. FRS_ASSERT(Replica != NULL);
  964. if (RetryCo) {
  965. //
  966. // Update perfmon counts for retry CO's
  967. //
  968. ChgOrdRetryPerfmonCount(ChangeOrder, Replica);
  969. }
  970. //
  971. // For local and remote CO's, check if the connection is no longer
  972. // joined. If this is true then we bail out here because it is
  973. // possible that a previously issued CO could be creating the parent
  974. // dir for this CO and if that CO didn't finish then we would be
  975. // propagating this one out of order.
  976. //
  977. if (!ControlCo) {
  978. FRS_ASSERT(LocalCo || ChangeOrder->Cxtion != NULL);
  979. LOCK_CXTION_TABLE(Replica);
  980. if ((ChangeOrder->Cxtion == NULL) ||
  981. !CxtionFlagIs(ChangeOrder->Cxtion, CXTION_FLAGS_JOIN_GUID_VALID) ||
  982. !GUIDS_EQUAL(&ChangeOrder->JoinGuid,
  983. &ChangeOrder->Cxtion->JoinGuid)) {
  984. UNLOCK_CXTION_TABLE(Replica);
  985. FrsRtlRemoveEntryQueueLock(IdledQueue, &ChangeOrder->ProcessList);
  986. //
  987. // The queue was never idled so don't try to unidle it.
  988. //
  989. UnIdleProcessQueue = FALSE;
  990. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  991. CHANGE_ORDER_TRACE(3, ChangeOrder, "Invalid Join Guid");
  992. goto CO_CLEANUP;
  993. }
  994. UNLOCK_CXTION_TABLE(Replica);
  995. }
  996. //
  997. // Check for a Control CO request. This is a change order entry
  998. // with a special flag set. It used for things that have to be
  999. // serialized with respect to other change orders already in the
  1000. // process queue.
  1001. //
  1002. if (ControlCo) {
  1003. //
  1004. // Remove the entry from the queue and idle the queue.
  1005. // Process the control change order, drop the queue lock and clean up.
  1006. //
  1007. FrsRtlRemoveEntryQueueLock(IdledQueue, &ChangeOrder->ProcessList);
  1008. FrsRtlIdleQueueLock(IdledQueue);
  1009. ChgOrdProcessControlCo(ChangeOrder, Replica);
  1010. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  1011. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1012. goto CO_CLEANUP;
  1013. }
  1014. //
  1015. // Translate the Fids to Guids for Local COs and vice versa for remote COs.
  1016. // Get the IDTable delete status for the target and the original and
  1017. // new parent dirs.
  1018. //
  1019. ChgOrdTranslateGuidFid(ThreadCtx, TmpIDTableCtx, ChangeOrder, Replica);
  1020. //
  1021. // Make sure we still have the FID in the IDTable for a Local
  1022. // change order if we are recovering after a crash.
  1023. //
  1024. if (LocalCo && RecoveryCo &&
  1025. (ChangeOrder->FileReferenceNumber == ZERO_FID)) {
  1026. //
  1027. // A local recovery CO can have a zero fid if there is no
  1028. // idtable record for the CO. This can happen if we crashed
  1029. // after deleting the idtable record but before deleting the
  1030. // inlog record for the CO. Reject the CO so that the inlog
  1031. // record can be cleaned up.
  1032. //
  1033. DPRINT(0, "++ ERROR - local recovery change order has 0 fid; reject\n");
  1034. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected; zero fid");
  1035. FRS_PRINT_TYPE(0, ChangeOrder);
  1036. // FRS_ASSERT(!"local recovery change order has 0 fid");
  1037. FrsRtlRemoveEntryQueueLock(IdledQueue, &ChangeOrder->ProcessList);
  1038. //
  1039. // The queue was never idled so don't try to unidle it.
  1040. //
  1041. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1042. UnIdleProcessQueue = FALSE;
  1043. ChgOrdReject(ChangeOrder, Replica);
  1044. goto CO_CLEANUP;
  1045. }
  1046. //
  1047. // Check for a dependency between this change order and the currently
  1048. // active change orders. If there is one then we idle the process
  1049. // queue and the active state is marked to unidle it when the conflicting
  1050. // CO is done. Since the queue is now idled we won't pick the CO up the
  1051. // next time around the loop.
  1052. //
  1053. Decision = (SerializeAllChangeOrders) ?
  1054. ISSUE_OK : ChgOrdHoldIssue(Replica, ChangeOrder, IdledQueue);
  1055. if (Decision == ISSUE_CONFLICT) {
  1056. CHANGE_ORDER_TRACE(3, ChangeOrder, "Issue Blocked");
  1057. DPRINT(4, "++ GAC: Conflict with active change order or Join wait.\n");
  1058. DPRINT1(4, "++ GAC: CO process queue: %ws is blocked.\n",
  1059. Replica->pVme->FSVolInfo.VolumeLabel);
  1060. //
  1061. // Drop the process queue lock.
  1062. //
  1063. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1064. UnIdleProcessQueue = FALSE;
  1065. goto CO_CONTINUE;
  1066. }
  1067. //
  1068. // Check to see if this CO is a duplicate of a currently active CO.
  1069. // If it is then ChgOrdHoldIssue() has removed the duplicate entry
  1070. // from the queue. We insert it into the inbound log in the event
  1071. // that the currently executing CO fails. Even if it succeeds we will
  1072. // reject this CO later and send an Ack to the inbound partner at that
  1073. // time.
  1074. //
  1075. DuplicateCo = (Decision == ISSUE_DUPLICATE_REMOTE_CO);
  1076. if (DuplicateCo) {
  1077. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1078. CHANGE_ORDER_TRACE(3, ChangeOrder, "Dup Remote Co");
  1079. UnIdleProcessQueue = FALSE;
  1080. goto CO_DEFER_DUP_CO;
  1081. }
  1082. //
  1083. // Before we try to reserve a retire slot, check to see if CO needs
  1084. // to be retried later. Occurs when parent dir not yet created.
  1085. //
  1086. if (Decision == ISSUE_RETRY_LATER) {
  1087. //
  1088. // Retry this CO later. Set DuplicateCO to alert retry thread.
  1089. // Note: This should only happen from remote COs. If this happened
  1090. // on a first-time-issue Local Co there would be no IDTable entry
  1091. // present for xlate the Guid to a Fid since we haven't inserted
  1092. // one yet.
  1093. //
  1094. FRS_ASSERT(!(LocalCo && !(RetryCo || RecoveryCo)));
  1095. DuplicateCo = TRUE;
  1096. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1097. UnIdleProcessQueue = FALSE;
  1098. if (ParentReanimation) {
  1099. //
  1100. // Don't retry parent reanimation COs. The base CO will get
  1101. // retried and regenerate the reanimation request. In this
  1102. // case the parent's parent was absent from the IDTable. This
  1103. // could happen if we got a tombstone for the parent and have
  1104. // not yet seen anything for its parent. Then a child create
  1105. // for the tombstoned parent shows up.
  1106. //
  1107. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Parent Reanimate. Absent Parent");
  1108. ChgOrdReject(ChangeOrder, Replica);
  1109. goto CO_CLEANUP;
  1110. }
  1111. CHANGE_ORDER_TRACE(3, ChangeOrder, "ISSUE_RETRY_LATER");
  1112. //
  1113. // How long are we going to wait for the parent to show up?
  1114. // Need to run a cleanup thread periodically to look for
  1115. // stranded COs and force them to cleanup and abort.
  1116. // See bug 70953 for a description of the problem.
  1117. //
  1118. goto CO_RETRY_LATER;
  1119. }
  1120. //
  1121. // Always Reserve a VV retire slot for remote COs since even if
  1122. // it's rejected a VV update and partner ACK are still needed.
  1123. // But, if this is a demand refresh CO (i.e. we demanded a file
  1124. // refresh from our inbound partner) then there is no Version Vector
  1125. // update and no CO in our inbound partner's outbound log to Ack.
  1126. //
  1127. // Do it here so we filter out the duplicate remote CO's first.
  1128. //
  1129. if (!LocalCo && !DemandRefreshCo) {
  1130. FStatus = VVReserveRetireSlot(Replica, ChangeOrder);
  1131. //
  1132. // Check if a prior CO left an activated slot on VV retire list.
  1133. // If it did and this CO is marked activated then this slot is
  1134. // for our CO and we are doing a retry. Otherwise the slot is for
  1135. // a different CO and so we either insert it in the INLOG or just
  1136. // skip it for now if it's a retry. Note: If we crashed then we
  1137. // lost the VV retire list so we don't get a duplicate key return.
  1138. // The CO is marked as a recovery CO when it is reprocessed so as
  1139. // to bypass some error checks that would apply to non-recovery COs.
  1140. //
  1141. if (FStatus == FrsErrorKeyDuplicate) {
  1142. //
  1143. // If this CO has VV_ACTIVATE set then this VV entry is ours.
  1144. // We are now a retry CO since we couldn't quite finish before.
  1145. // If this CO does not have VV_ACTIVATE set then it must be a dup.
  1146. // Reject the dup CO and ACk the partner. The VV entry is activated
  1147. // so it means that it is safe to ack the CO. The original CO
  1148. // that activated the entry may or may not be in the inlog.
  1149. //
  1150. DuplicateCo = !CO_FLAG_ON(ChangeOrder, CO_FLAG_VV_ACTIVATED);
  1151. if (DuplicateCo) {
  1152. FrsRtlRemoveEntryQueueLock(IdledQueue, &ChangeOrder->ProcessList);
  1153. FrsRtlIdleQueueLock(IdledQueue);
  1154. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1155. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  1156. ChgOrdReject(ChangeOrder, Replica);
  1157. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV);
  1158. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  1159. goto CO_CLEANUP;
  1160. } else {
  1161. //
  1162. // This is a remote CO with VV_ACTIVATED flag set so
  1163. // it should be a retry change order.
  1164. // The state should be greater than IBCO_FETCH_RETRY or
  1165. // it wouldn't be activated. It could be in an install or
  1166. // a rename retry state.
  1167. //
  1168. CHANGE_ORDER_TRACE(3, ChangeOrder, "Retry remote CO");
  1169. FRS_ASSERT(RetryCo);
  1170. FRS_ASSERT(!CO_STATE_IS_LE(ChangeOrder, IBCO_FETCH_RETRY));
  1171. //ChgOrdReject(ChangeOrder, Replica);
  1172. //goto CO_CLEANUP;
  1173. }
  1174. }
  1175. }
  1176. //
  1177. // If this Remote CO should be rejected then do it now.
  1178. // ChgOrdHoldIssue() has removed the CO from the queue.
  1179. //
  1180. if (Decision == ISSUE_REJECT_CO) {
  1181. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1182. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected");
  1183. ChgOrdReject(ChangeOrder, Replica);
  1184. UnIdleProcessQueue = FALSE;
  1185. goto CO_CLEANUP;
  1186. }
  1187. //
  1188. // No conflict we can issue the Change Order.
  1189. // Remove the entry from the queue and idle the queue.
  1190. //
  1191. FrsRtlRemoveEntryQueueLock(IdledQueue, &ChangeOrder->ProcessList);
  1192. FrsRtlIdleQueueLock(IdledQueue);
  1193. //
  1194. // We now have the change order off the queue. Drop the locks blocking
  1195. // the journal lookup. If the Journal was about to update this change
  1196. // order it will now not find it in the volume ChangeOrderTable so it
  1197. // creates a new one.
  1198. //
  1199. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  1200. //
  1201. // Not on the process list now.
  1202. //
  1203. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  1204. FRS_ASSERT(pVme == Replica->pVme);
  1205. //
  1206. // Allocate the replica thread context (RtCtx) for the change
  1207. // order's DB operations. Link it to the Replica context list head.
  1208. // We then use this to carry the DB related state of the CO thru the
  1209. // system. It also allows us to queue the CO retire operation to
  1210. // the DB system where it will have all the data to perform the
  1211. // changeorder entry update/delete and the IDTable update.
  1212. //
  1213. // To make the above work we can't return any state relative to
  1214. // specific table transactions thru the replica struct.
  1215. //
  1216. // Check on Replica->FStatus.
  1217. //
  1218. // Use RtCtx or cmdpkt instead. The ChangeOrder needs a ptr to the
  1219. // RtCtx. When the ChangeOrder is done all RtCtx tables are closed,
  1220. // The RtCtx is removed from the replica and thread ctx lists and the
  1221. // memory is freed. Also check that mult concurrent transactions on a
  1222. // replica set sync access to the config record in the replica struct.
  1223. // Before a thread passes the changeorder (and hence the RtCtx) on to
  1224. // another thread it will have to make sure any open tables are closed
  1225. // because the table opens are ThreadCtx specific.
  1226. //
  1227. // Note: If this is a Retry CO it will already have a RtCtx if we
  1228. // got it through VVReferenceRetireSlot(). So don't alloc another.
  1229. //
  1230. RtCtx = ChangeOrder->RtCtx;
  1231. if (RtCtx == NULL) {
  1232. RtCtx = FrsAllocTypeSize(REPLICA_THREAD_TYPE,
  1233. FLAG_FRSALLOC_NO_ALLOC_TBL_CTX);
  1234. FrsRtlInsertTailList(&Replica->ReplicaCtxListHead,
  1235. &RtCtx->ReplicaCtxList);
  1236. ChangeOrder->RtCtx = RtCtx;
  1237. } else {
  1238. //
  1239. // Add LocalCo because a localco winner of a name morph conflict
  1240. // will have generated a loser-rename-co before running. The act
  1241. // of generating the loser-rename-co leaves the original localco
  1242. // with an RtCtx.
  1243. //
  1244. FRS_ASSERT(RetryCo || ParentReanimation || MorphGenCo || LocalCo);
  1245. }
  1246. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_RTCTX);
  1247. IDTableCtx = &RtCtx->IDTable;
  1248. DIRTableCtx = &RtCtx->DIRTable;
  1249. InLogTableCtx = &RtCtx->INLOGTable;
  1250. //
  1251. // Read or initialize an IDTable Record for this file.
  1252. // Insert the GUIDs for originator, File, Old and new parent.
  1253. // FrsErrorNotFound indicates a new record has been initialized.
  1254. //
  1255. FStatus = ChgOrdReadIDRecord(ThreadCtx,
  1256. IDTableCtx,
  1257. TmpIDTableCtxNC,
  1258. ChangeOrder,
  1259. Replica,
  1260. &IDTableRecExists);
  1261. //
  1262. // If we have detected and fixed a tunnel conflict that remapped
  1263. // the File Guid to a new file instance (File ID).
  1264. // Need to restart processing of this CO as we would for a
  1265. // reanimated parent dir. This ensures that the CO issue interlock
  1266. // checks made in ChgOrdHoldIssue get rechecked.
  1267. //
  1268. if (FStatus == FrsErrorTunnelConflict) {
  1269. //
  1270. // Do the change order cleanup but don't free the CO.
  1271. //
  1272. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO);
  1273. if (!LocalCo && !DemandRefreshCo) {
  1274. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  1275. }
  1276. FStatus = ChgOrdIssueCleanup(ThreadCtx, Replica, ChangeOrder, 0);
  1277. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  1278. //
  1279. // Push this CO onto the head of the queue.
  1280. //
  1281. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Push Tunnel Retry to QHead");
  1282. WStatus = ChgOrdInsertProcQ(Replica, ChangeOrder, IPQ_HEAD | IPQ_TAKE_COREF |
  1283. IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  1284. if (!WIN_SUCCESS(WStatus)) {
  1285. //
  1286. // Insert CO onto process queue failed probably beccause process
  1287. // queue has been run down.
  1288. //
  1289. ReleaseVmeRef(pVme);
  1290. WStatus = ERROR_OPERATION_ABORTED;
  1291. break;
  1292. }
  1293. //
  1294. // The above put the CO back on the list so set the MorphGenCo flag so
  1295. // we don't decrement the LocalCoQueueCount at the bottom of the loop.
  1296. //
  1297. MorphGenCo = TRUE;
  1298. //
  1299. // CO ready to restart.
  1300. //
  1301. ChangeOrder = NULL;
  1302. IDTableRec = NULL;
  1303. goto CO_CONTINUE;
  1304. }
  1305. //
  1306. // Get ptr to ID Table record.
  1307. //
  1308. IDTableRec = (PIDTABLE_RECORD) (IDTableCtx->pDataRecord);
  1309. NameMorphConflict = (FStatus == FrsErrorNameMorphConflict);
  1310. NewFile = (FStatus == FrsErrorNotFound) ||
  1311. IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS);
  1312. if ((FStatus == FrsErrorTunnelConflictRejectCO) ||
  1313. (!FRS_SUCCESS(FStatus) && !(NewFile || NameMorphConflict))) {
  1314. CHANGE_ORDER_TRACEF(3, ChangeOrder, "Co Rejected", FStatus);
  1315. ChgOrdReject(ChangeOrder, Replica);
  1316. goto CO_CLEANUP;
  1317. }
  1318. //
  1319. // If this CO is on a new file and it's a Morph generated Co then
  1320. // the morph conflict may no longer exist because of the outcome of
  1321. // a prior change order (e.g. it got aborted).
  1322. // There are two case to consider: A MORPH_GEN_LEADER or a
  1323. // MORPH_GEN_FOLLOWER.
  1324. //
  1325. if (NewFile && MorphGenCo) {
  1326. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER)) {
  1327. // MorphGenFollower: This is the rename MorphGenCo produced
  1328. // as part of a Dir/Dir Name Morph Conflict (DLD Case). The
  1329. // fact that Newfile is TRUE means that the base create CO
  1330. // (the leader) failed and is in retry so reject this MorphGenCo.
  1331. // It gets refabricated later when the Leader is re-issued.
  1332. // ***** Note -- THere is similar code in ChgOrdHoldIssue() *****
  1333. //
  1334. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - MORPH_GEN_FOLLOWER");
  1335. ChgOrdReject(ChangeOrder, Replica);
  1336. goto CO_CLEANUP;
  1337. } else {
  1338. //
  1339. // Do not pitch MorphGen Delete Cos. An incoming Create Co that
  1340. // looses the name conflict produces a MorphGenLeader DelCo
  1341. // with no IDTable Entry.
  1342. // ***** Note -- THere is similar code in ChgOrdHoldIssue() *****
  1343. //
  1344. if ((LocationCmd != CO_LOCATION_DELETE) &&
  1345. BooleanFlagOn(ChangeOrder->Cmd.ContentCmd, USN_REASON_RENAME_NEW_NAME)) {
  1346. //
  1347. // MorphGenLeader: This is the rename MorphGenCo produced as
  1348. // part of a Dir/Dir Name
  1349. // Morph Conflict where ID table loses name (DDL Case).
  1350. // This CO is intended to free up the name so that the base CO can
  1351. // issue. The fact that Newfile is TRUE means that the conflicting
  1352. // ID table entry either does not exist any more or is still in
  1353. // NewFileInProgress state. Either way the conflicting dir is not
  1354. // on disk so the base CO will be able to proceed.
  1355. // Reject the Morph CO at this point.
  1356. //
  1357. if (!IDTableRecExists) {
  1358. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - MORPH_GEN_LEADER");
  1359. ChgOrdReject(ChangeOrder, Replica);
  1360. goto CO_CLEANUP;
  1361. } else {
  1362. CHANGE_ORDER_TRACE(3, ChangeOrder, "**** WARN - MORPH_GEN_LEADER but IDT entry exists");
  1363. DBS_DISPLAY_RECORD_SEV(3, IDTableCtx, TRUE);
  1364. FRS_PRINT_TYPE(3, ChangeOrder);
  1365. //
  1366. // IDTable record exists - Implies NewFileInProgress so
  1367. // there is at least one CO that is working on the ID table
  1368. // entry (probably in retry). We end up deleting the ID
  1369. // table entry as part of reject here.
  1370. // (ISCU_DEL_IDT_ENTRY). That is not a problem as the CO
  1371. // that is working on it will recreate it when it gets
  1372. // reissued again and will notice the name morph with our
  1373. // base CO and will do the right thing.
  1374. //
  1375. // WARN:
  1376. // This may cause problems so for now don't do it and
  1377. // wait for better data on this case.
  1378. //
  1379. // SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_IDT_ENTRY);
  1380. }
  1381. }
  1382. }
  1383. }
  1384. if (LocalCo && !NewFile) {
  1385. ChgOrdSkipBasicInfoChange(ChangeOrder, &SkipCo);
  1386. if (SkipCo) {
  1387. //
  1388. // Skip some basic info changes on local change orders
  1389. // but update the last USN field in the IDTable record so
  1390. // we don't get confused later in staging and think another
  1391. // change order will be coming with a more recent USN.
  1392. //
  1393. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Basicinfo");
  1394. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_UPDATE_IDT_FILEUSN);
  1395. ChgOrdReject(ChangeOrder, Replica);
  1396. goto CO_CLEANUP;
  1397. }
  1398. }
  1399. //
  1400. // Now decide if we accept this change order. This deals with the
  1401. // problem of multiple updates to the same file at different
  1402. // replica set members. In most cases the decision to accept the
  1403. // change is final. However it is possible for the target file on
  1404. // this machine to be modified directly between now and the time
  1405. // when we actually install the file. As an example a slow or
  1406. // faulty network could delay the fetch of the staging file from
  1407. // the inbound partner (perhaps for hours or days). To deal with this
  1408. // case we capture the file's USN now and then we check it again
  1409. // just before the final stage of the install.
  1410. //
  1411. // If this is a new file (not in our IDTable) we accept unless this
  1412. // is a retry of a previous delete that has been superceded by
  1413. // a reanimation. In the latter case, let reconcile have a look.
  1414. //
  1415. // If the Re-animate flag is set then we have already accepted this CO.
  1416. // Otherwise use reconcilliation algorithm to decide accept or reject.
  1417. //
  1418. // What if the reanimation co is a recoveryco or a retryco? Shouldn't
  1419. // reconcile be called? Should REANIMATION be cleared when a co
  1420. // is taken through retry?
  1421. //
  1422. if (NewFile && !CO_STATE_IS(ChangeOrder, IBCO_INSTALL_DEL_RETRY)) {
  1423. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - NewFile");
  1424. goto CO_ACCEPT;
  1425. }
  1426. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_REANIMATION) &&
  1427. !COE_FLAG_ON(ChangeOrder, COE_FLAG_REJECT_AT_RECONCILE)) {
  1428. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - Reanimated");
  1429. goto CO_ACCEPT;
  1430. }
  1431. if (ChgOrdReconcile(Replica,
  1432. ChangeOrder,
  1433. (PIDTABLE_RECORD) IDTableCtx->pDataRecord)) {
  1434. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - Reconcile");
  1435. goto CO_ACCEPT;
  1436. }
  1437. //
  1438. // This CO is rejected. Set necessary cleanup flags and go clean it up.
  1439. //
  1440. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Reconcile");
  1441. ChgOrdReject(ChangeOrder, Replica);
  1442. goto CO_CLEANUP;
  1443. CO_ACCEPT:
  1444. //
  1445. // This Change Order is being accepted. Now check for a Name Conflict
  1446. // in the parent dir. This may have been detected above when we searched
  1447. // for the IDTable record or it may have been detected in the past and
  1448. // this CO was marked as a MorphGenLeader and it needs to refabricate
  1449. // the MorphGenFollower rename CO because the leader got sent thru
  1450. // the retry path.
  1451. //
  1452. if (NameMorphConflict ||
  1453. (CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN_LEADER) &&
  1454. !COE_FLAG_ON(ChangeOrder, COE_FLAG_MG_FOLLOWER_MADE))) {
  1455. //
  1456. // Increment the Local and Remote CO Morphed counter
  1457. //
  1458. if (LocalCo) {
  1459. PM_INC_CTR_REPSET(Replica, LCOMorphed, 1);
  1460. }
  1461. else {
  1462. PM_INC_CTR_REPSET(Replica, RCOMorphed, 1);
  1463. }
  1464. MorphOK = ChgOrdGenMorphCo(ThreadCtx,
  1465. TmpIDTableCtxNC,
  1466. ChangeOrder,
  1467. Replica);
  1468. //
  1469. // If the code above put the change order back on the list then
  1470. // set the MorphGenCo flag so we don't decrement the
  1471. // LocalCoQueueCount at the bottom of the loop.
  1472. //
  1473. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_ONLIST)) {
  1474. MorphGenCo = TRUE;
  1475. }
  1476. if (!MorphOK) {
  1477. //
  1478. // Local Co's that fail to generate a Morph Co on the first time
  1479. // through (i.e. not a Retry or Recovery CO) must be rejected
  1480. // since there is no IDTable entry made that will translate the
  1481. // GUID to the FID when the CO is later retried.
  1482. //
  1483. if (LocalCo && !(RetryCo || RecoveryCo)) {
  1484. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Morph failure");
  1485. ChgOrdReject(ChangeOrder, Replica);
  1486. goto CO_CLEANUP;
  1487. }
  1488. //
  1489. // If this is a parent reanimation CO that has encountered a
  1490. // name morph conflict for the second time then reject it and
  1491. // let the base CO go thru retry. A case where this can happen
  1492. // is when a reanimated parent causes a name morph conflict
  1493. // in which it wins the name from the current owner in the
  1494. // IDTable (the DDL case.) But the rename MorphGenCo that was
  1495. // going to free the name failed because the underlying file
  1496. // was deleted (by a local file op). Now the reanimation CO
  1497. // for the winner of the name re-issues and hits the name morph
  1498. // conflict again. This time it has COE_FLAG_MORPH_GEN_FOLLOWER
  1499. // set so ChgOrdGenMorphCo() returns failure. Since this is
  1500. // a parent reanimation CO it should not go thru retry, instead
  1501. // the base CO will go thru retry. This lets the local CO that
  1502. // deleted the parent get processed so when the base CO is later
  1503. // retried and reanimates the parent, there won't be a name morph
  1504. // conflict.
  1505. //
  1506. if (ParentReanimation) {
  1507. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Parent Reanimate Morph failure");
  1508. ChgOrdReject(ChangeOrder, Replica);
  1509. goto CO_CLEANUP;
  1510. }
  1511. //
  1512. // Retry this CO later. Set DuplicateCO to alert retry thread.
  1513. //
  1514. DuplicateCo = TRUE;
  1515. CHANGE_ORDER_TRACE(3, ChangeOrder, "Retry Later");
  1516. goto CO_RETRY_LATER;
  1517. }
  1518. //
  1519. // Loop back to the top and process the new CO.
  1520. //
  1521. ChangeOrder = NULL;
  1522. IDTableRec = NULL;
  1523. goto CO_CONTINUE;
  1524. }
  1525. //
  1526. // The new change order has been accepted.
  1527. //
  1528. DBS_DISPLAY_RECORD_SEV(4, IDTableCtx, FALSE);
  1529. //
  1530. // Check for accepted Delete Co that missed in the IDTable.
  1531. // This is a remote CO delete that arrived before the create.
  1532. // Create a tombstone in the IDTable.
  1533. // A second case is a delete Co that arrives after the file has been
  1534. // deleted but has more recent event time info. We need to process
  1535. // this and update the IDTable to ensure a correct outcome if an update
  1536. // CO were to arrive from a third originator. See comment in
  1537. // ChgOrdReadIDRecord() re: "Del Co rcvd on deleted file".
  1538. //
  1539. JustTombstone = (//!LocalCo &&
  1540. (NewFile || IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED)) &&
  1541. (CO_LOCN_CMD_IS(ChangeOrder, CO_LOCATION_DELETE) ||
  1542. CO_LOCN_CMD_IS(ChangeOrder, CO_LOCATION_MOVEOUT)));
  1543. if (JustTombstone) {
  1544. SET_COE_FLAG(ChangeOrder, COE_FLAG_JUST_TOMBSTONE);
  1545. ReAnimate = FALSE;
  1546. goto CO_SKIP_REANIMATE;
  1547. }
  1548. //
  1549. // If this is a MOVEOUT Local CO on a directory then enumerate the
  1550. // IDTable and generate delete COs for all the children. The Journal
  1551. // sends the MOVEOUT to us in a bottom up order using the directory
  1552. // filter table.
  1553. //
  1554. if (LocalCo &&
  1555. CoIsDirectory(ChangeOrder) &&
  1556. CO_LOCN_CMD_IS(ChangeOrder, CO_LOCATION_MOVEOUT) &&
  1557. !COE_FLAG_ON(ChangeOrder, COE_FLAG_MOVEOUT_ENUM_DONE)) {
  1558. SET_COE_FLAG(ChangeOrder, COE_FLAG_MOVEOUT_ENUM_DONE);
  1559. //
  1560. // Push this CO back onto the front of the CO Process queue
  1561. // and then generate Delete COs for each child.
  1562. // Do the change order cleanup but don't free the CO.
  1563. //
  1564. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO);
  1565. FStatus = ChgOrdIssueCleanup(ThreadCtx, Replica, ChangeOrder, 0);
  1566. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  1567. //
  1568. // Initialize context block before CO is pushed back to queue.
  1569. //
  1570. MoveOutContext.NumFiles = 0;
  1571. MoveOutContext.ParentFileID = ChangeOrder->FileReferenceNumber;
  1572. MoveOutContext.Replica = Replica;
  1573. //
  1574. // Push this CO onto the head of the queue.
  1575. //
  1576. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Push MOVEOUT DIR CO to QHead");
  1577. WStatus = ChgOrdInsertProcQ(Replica, ChangeOrder, IPQ_HEAD |
  1578. IPQ_TAKE_COREF | IPQ_ASSERT_IF_ERR);
  1579. if (!WIN_SUCCESS(WStatus)) {
  1580. ChgOrdReject(ChangeOrder, Replica);
  1581. goto CO_CLEANUP;
  1582. }
  1583. ChangeOrder = NULL;
  1584. //
  1585. // The above put the change order back on the list so
  1586. // set the MorphGenCo flag so we don't decrement the
  1587. // LocalCoQueueCount at the bottom of the loop.
  1588. //
  1589. MorphGenCo = TRUE;
  1590. //
  1591. // Walk through the IDTable and Generate delete COs for each child
  1592. // by calling ChgOrdMoveoutWorker() for this Replica.
  1593. //
  1594. jerr = DbsOpenTable(ThreadCtx, TmpIDTableCtx, Replica->ReplicaNumber, IDTablex, NULL);
  1595. if (!JET_SUCCESS(jerr)) {
  1596. DPRINT_JS(0, "++ ERROR - IDTable Open Failed:", jerr);
  1597. ChgOrdReject(ChangeOrder, Replica);
  1598. goto CO_CLEANUP;
  1599. }
  1600. jerr = FrsEnumerateTable(ThreadCtx,
  1601. TmpIDTableCtx,
  1602. GuidIndexx,
  1603. ChgOrdMoveoutWorker,
  1604. &MoveOutContext);
  1605. if (jerr != JET_errNoCurrentRecord) {
  1606. DPRINT_JS(0, "++ FrsEnumerateTable error:", jerr);
  1607. }
  1608. DPRINT1(4, "++ MoveOut dir done: %d files\n", MoveOutContext.NumFiles);
  1609. //
  1610. // Close the IDTable, reset TableCtx Tid and Sesid. Macro writes 1st arg.
  1611. //
  1612. DbsCloseTable(jerr, ThreadCtx->JSesid, TmpIDTableCtx);
  1613. //
  1614. // Loop back to the top and process the new delete COs.
  1615. //
  1616. IDTableRec = NULL;
  1617. goto CO_CONTINUE;
  1618. }
  1619. //
  1620. // Does this CO need reanimation?
  1621. // Do not treat MorphGenCos for local deletes as reanimate cos.
  1622. //
  1623. // 1. A remote co for an update comes in for a tombstoned
  1624. // idtable entry.
  1625. //
  1626. // 2. The remote co becomes a reanimate co.
  1627. //
  1628. // 3. The reanimate remote co loses a name conflict to
  1629. // another idtable entry.
  1630. //
  1631. // 4. A MorphGenCo for a local delete is generated for the
  1632. // reanimate remote co that lost the name conflict.
  1633. //
  1634. // 5. The MorphGenCo is treated as a reanimate co because the
  1635. // idtable entry is deleted. This MorphGenCo for a local
  1636. // delete becomes a MorphGenCo for a local *CREATE* later in
  1637. // this function. Things go downhill from there.
  1638. //
  1639. //
  1640. ReAnimate = (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED) &&
  1641. (!DOES_CO_DELETE_FILE_NAME(CoCmd)) &&
  1642. !(MorphGenCo && (LocationCmd == CO_LOCATION_DELETE)));
  1643. //
  1644. // Change order is accepted. Check if the parent dir is deleted.
  1645. // This could happen if one member deletes the parent dir while another
  1646. // member creates a new file or updates what had been a deleted file
  1647. // underneath the parent. In this situation the parent dir is recreated
  1648. // by fetching it from the inbound parent that sent us this CO.
  1649. //
  1650. // Do we need to check that the LocationCmd is not a Moveout,
  1651. // movedir, movers, or a delete? Delete and moveout were filtered
  1652. // when we read the IDTable record above. The test below will
  1653. // select the IDT_NEW_PARENT_DEL test in the case of a movedir or
  1654. // movers so an additional test of the location cmd should not be necc.
  1655. //
  1656. if (ChangeOrder->NewReplica != NULL) {
  1657. RaiseTheDead = COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL);
  1658. } else {
  1659. RaiseTheDead = COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_DEL);
  1660. }
  1661. if (RaiseTheDead) {
  1662. RStatus = ChgOrdReanimate(IDTableCtx,
  1663. ChangeOrder,
  1664. Replica,
  1665. ThreadCtx,
  1666. TmpIDTableCtx);
  1667. switch (RStatus) {
  1668. case REANIMATE_SUCCESS:
  1669. //
  1670. // Parent reanimated. Go Issue the CO for it.
  1671. //
  1672. ChangeOrder = NULL;
  1673. IDTableRec = NULL;
  1674. goto CO_CONTINUE;
  1675. case REANIMATE_CO_REJECTED:
  1676. //
  1677. // Can't do the reanimation. Could be a local co or a nested
  1678. // reanimation failed and now we are failing all nested parents
  1679. // back to the base CO.
  1680. //
  1681. IDTableRec = NULL;
  1682. goto CO_CLEANUP;
  1683. case REANIMATE_FAILED:
  1684. //
  1685. // This is a system failure. Can't insert CO on process queue
  1686. // probably beccause process queue has been run down.
  1687. //
  1688. ReleaseVmeRef(pVme);
  1689. break;
  1690. case REAMIMATE_RETRY_LATER:
  1691. //
  1692. // Local Co's that fail to reanimate the parent could occur if
  1693. // both the file and the parent have been deleted out from under
  1694. // us. This might happen in an obscure case where the local
  1695. // child create ends up in a race with a remote co file create
  1696. // that is followed immediately by a remote co file delete and a
  1697. // parent dir delete. The remote COs would have to arrive just
  1698. // before the local CO create was inserted into the process
  1699. // queue. There would be no IDTable name conflict when the
  1700. // remote CO is processed so the remote CO create would just
  1701. // take over the file name. Later when the local CO create is
  1702. // processed our attempt to reanimate the parent will fail. I'm
  1703. // not even sure this can really happen but if it did and this
  1704. // is the first time through (i.e. not a Retry or Recovery CO)
  1705. // the CO must be rejected since there is no IDTable entry made
  1706. // that will translate the GUID to the FID when the CO is later
  1707. // retried.
  1708. //
  1709. //
  1710. // It doesn't seem like the above scenario actually happens.
  1711. // There is however a problem when we fail a reanimation
  1712. // triggered by a local Co and then abort the CO. (bug 464476)
  1713. //
  1714. /*
  1715. if (LocalCo && !(RetryCo || RecoveryCo)) {
  1716. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Parent Reanimate failed");
  1717. ChgOrdReject(ChangeOrder, Replica);
  1718. goto CO_CLEANUP;
  1719. }
  1720. */
  1721. //
  1722. // Retry this CO later. Set DuplicateCO to alert retry thread.
  1723. // This can happen if it's the base CO and we failed to
  1724. // reanimate one of the parents.
  1725. //
  1726. DuplicateCo = TRUE;
  1727. CHANGE_ORDER_TRACE(3, ChangeOrder, "Reanimate Retry Later");
  1728. goto CO_RETRY_LATER;
  1729. case REANIMATE_NO_NEED:
  1730. //
  1731. // No need to raise the co's parent because this is a
  1732. // delete change order. This can happen if the file
  1733. // is stuck in the preinstall or staging areas and isn't
  1734. // really under its parent.
  1735. //
  1736. CHANGE_ORDER_TRACE(3, ChangeOrder, "Reanimate Parent Not Needed");
  1737. break;
  1738. default:
  1739. DPRINT1(0, "++ ERROR: Invalid status return from ChgOrdReanimate: %d\n",
  1740. RStatus);
  1741. FRS_ASSERT(!"Invalid status return from ChgOrdReanimate");
  1742. }
  1743. if (RStatus == REANIMATE_FAILED) {
  1744. //
  1745. // This is a system failure. See above.
  1746. //
  1747. WStatus = ERROR_OPERATION_ABORTED;
  1748. break;
  1749. }
  1750. }
  1751. CO_SKIP_REANIMATE:
  1752. //
  1753. // Is a CO reanimation (but not a collateral parent reanimation)?
  1754. // location cmd could have changed in some function calls above.
  1755. //
  1756. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  1757. if (ReAnimate && !ParentReanimation) {
  1758. CHANGE_ORDER_TRACE(3, ChangeOrder, "Issue Reanimated Target");
  1759. //
  1760. // Clear the IDTable Flags and transform the CO into a create.
  1761. // If the location cmd was a movein then leave it alone since
  1762. // code elsewhere needs to know.
  1763. //
  1764. ClearIdRecFlag(IDTableRec, IDREC_FLAGS_ALL);
  1765. if (!CO_NEW_FILE(LocationCmd)) {
  1766. SET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command, CO_LOCATION_CREATE);
  1767. SET_CO_FLAG(ChangeOrder, CO_FLAG_LOCATION_CMD);
  1768. LocationCmd = CO_LOCATION_CREATE;
  1769. }
  1770. //
  1771. // Set the IDREC_FLAGS_NEW_FILE_IN_PROGRESS flag so that the
  1772. // CO gets renamed from preinstall file to final name even
  1773. // after it goes through retry. (Bug # 138742)
  1774. //
  1775. SetIdRecFlag(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS);
  1776. SET_COE_FLAG(ChangeOrder, COE_FLAG_REANIMATION);
  1777. FRS_PRINT_TYPE(3, ChangeOrder);
  1778. } else
  1779. if (ParentReanimation) {
  1780. CHANGE_ORDER_TRACE(3, ChangeOrder, "Issue Reanimated Parent");
  1781. } else {
  1782. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_GROUP_REANIMATE);
  1783. CHANGE_ORDER_TRACE(3, ChangeOrder, "GO Issue");
  1784. }
  1785. if (LocalCo) {
  1786. //
  1787. // Local Co -- Create the change order guid and update filesize.
  1788. //
  1789. // Morph generated Cos got their COGuid when they were originally
  1790. // created (for tracking purposes). If it's a retry or recovery
  1791. // CO then it came from the inlog and already has a COGuid.
  1792. //
  1793. if (!MorphGenCo && !RetryCo && !RecoveryCo) {
  1794. FrsUuidCreate(&CoCmd->ChangeOrderGuid);
  1795. }
  1796. CoCmd->FileSize = IDTableRec->FileSize;
  1797. //
  1798. // If it is a morph generated Co then perform the file op that
  1799. // frees the name. Have to wait until at least this point
  1800. // because otherwise there could be an outstanding CO working
  1801. // on the file. Only Morph generated COs that matter here are
  1802. // those that already have an ID table entry.
  1803. // If this is a MOVEOUT generated Delete Co then NO local op is done.
  1804. //
  1805. if (!NewFile && MorphGenCo &&
  1806. !COE_FLAG_ON(ChangeOrder, COE_FLAG_DELETE_GEN_CO)) {
  1807. if (!ChgOrdApplyMorphCo(ChangeOrder, Replica)) {
  1808. goto CO_CLEANUP;
  1809. }
  1810. }
  1811. } else {
  1812. //
  1813. // Remote Co -- Check if this is a new file or an accepted
  1814. // CO on a file that was previously deleted.
  1815. //
  1816. // A reanimation change order may be kicked through retry.
  1817. // If so the FID may still be valid. Code in ChgOrdReadIDRecord
  1818. // has already checked this. Another case is when the preinstall
  1819. // file for a reanimated co could not be renamed into place
  1820. // because of a sharing violation on the target parent dir so the
  1821. // CO was kicked through retry in state IBCO_INSTALL_REN_RETRY.
  1822. //
  1823. // Note: File is already present if COE_FLAG_NEED_DELETE is set
  1824. // because it is in the deferred delete state.
  1825. // Unless the FID is zero in wich case ChgOrdReadIDRecord()
  1826. // has determined that the FID was not valid.
  1827. //
  1828. if ((NewFile || ReAnimate) &&
  1829. !JustTombstone &&
  1830. (!COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_DELETE) ||
  1831. (IDTableRec->FileID == ZERO_FID)) ) {
  1832. if (IDTableRec->FileID == ZERO_FID) {
  1833. //
  1834. // This is a remote change order with a new or reanimated
  1835. // file or Dir. Create the target file so we can get its
  1836. // FID for the ID and DIR Table Entries. ChangeOrder's
  1837. // FileReferenceNumber field and the FileUsn field are
  1838. // filled in if the call succeeds.
  1839. //
  1840. WStatus = StuCreatePreInstallFile(ChangeOrder);
  1841. if (!WIN_SUCCESS(WStatus)) {
  1842. DPRINT1_WS(0, "++ WARN - StuCreatePreInstallFile(%ws) :",
  1843. CoCmd->FileName, WStatus);
  1844. //
  1845. // NOTE - All failures to create a preinstall file should
  1846. // be retried, even hardware failures. Once the hardware
  1847. // is fixed the retry should succeed. The CO timeout
  1848. // code will pitch a problem CO after a week.
  1849. //
  1850. //if (!WIN_RETRY_PREINSTALL(WStatus)) {
  1851. //
  1852. // Not retriable; Give up!
  1853. //
  1854. // CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Preinstall");
  1855. // ChgOrdReject(ChangeOrder, Replica);
  1856. // goto CO_CLEANUP;
  1857. //}
  1858. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  1859. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Retry - Preinstall");
  1860. RetryPreinstall = TRUE;
  1861. goto CO_RETRY_PREINSTALL;
  1862. }
  1863. IDTableRec->FileID = ChangeOrder->FileReferenceNumber;
  1864. //
  1865. // Clear COE_FLAG_NEED_DELETE so we don't assume we have
  1866. // the file/dir below and skip the refetch.
  1867. //
  1868. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_NEED_DELETE);
  1869. }
  1870. //
  1871. // Set flag to cleanup preinstall file if CO later aborts.
  1872. //
  1873. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_PREINSTALL);
  1874. SET_COE_FLAG(ChangeOrder, COE_FLAG_PREINSTALL_CRE);
  1875. }
  1876. }
  1877. //
  1878. // LOCAL or REMOTE CO:
  1879. //
  1880. // If this is a file create, insert the new entries in the ID Table.
  1881. // If this is an update to an existing file then we don't update the
  1882. // entry until the change order is retired. If it aborted we would
  1883. // have to roll it back and I'd rather not. In addition if an outbound
  1884. // partner needs to do a DB resync and we haven't finished a remote
  1885. // CO install then we would be in the state of having new filter
  1886. // info in the IDTable but old data in the disk file.
  1887. //
  1888. if (NewFile) {
  1889. //
  1890. // Write the new IDTable record into the database.
  1891. //
  1892. if (IDTableRecExists) {
  1893. //
  1894. // Info in the IDTable record inited by a prior CO that failed
  1895. // to complete (e.g. a FID) may be stale. The prior CO is most
  1896. // likely in the retry state so update the IDTable record with
  1897. // the state from the current CO.
  1898. //
  1899. FStatus = DbsUpdateTableRecordByIndex(ThreadCtx,
  1900. Replica,
  1901. IDTableCtx,
  1902. &IDTableRec->FileGuid,
  1903. GuidIndexx,
  1904. IDTablex);
  1905. //
  1906. // DirTable
  1907. //
  1908. if (FRS_SUCCESS(FStatus)) {
  1909. FStatus = ChgOrdInsertDirRecord(ThreadCtx,
  1910. DIRTableCtx,
  1911. IDTableRec,
  1912. ChangeOrder,
  1913. Replica,
  1914. FALSE);
  1915. //
  1916. // DirTable entry may not exist; create it
  1917. //
  1918. if (FStatus == FrsErrorNotFound) {
  1919. FStatus = ChgOrdInsertDirRecord(ThreadCtx,
  1920. DIRTableCtx,
  1921. IDTableRec,
  1922. ChangeOrder,
  1923. Replica,
  1924. TRUE);
  1925. }
  1926. }
  1927. } else if (JustTombstone) {
  1928. FStatus = DbsInsertTable(ThreadCtx,
  1929. Replica,
  1930. IDTableCtx,
  1931. IDTablex,
  1932. NULL);
  1933. } else {
  1934. FStatus = ChgOrdInsertIDRecord(ThreadCtx,
  1935. IDTableCtx,
  1936. DIRTableCtx,
  1937. ChangeOrder,
  1938. Replica);
  1939. }
  1940. if (!FRS_SUCCESS(FStatus)) {
  1941. DPRINT_FS(0, "++ ERROR - ChgOrdInsertIDRecord failed.", FStatus);
  1942. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - IDT Insert failed");
  1943. ChgOrdReject(ChangeOrder, Replica);
  1944. goto CO_CLEANUP;
  1945. }
  1946. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_IDT_ENTRY);
  1947. //
  1948. // Remember this is a new file so we can delete the IDTable entry
  1949. // if the CO fails.
  1950. //
  1951. SET_CO_FLAG(ChangeOrder, CO_FLAG_NEW_FILE);
  1952. } else if (ReAnimate) {
  1953. //
  1954. // Update the ID table record now to reflect the new fid and the
  1955. // new name / parent. to avoid problems if this change order is
  1956. // kicked into retry. One such problem occurs when the retry path
  1957. // resurrects the id table entry with its old name by marking the
  1958. // id table entry as "not deleted". This could cause false name
  1959. // morph conflicts later
  1960. // if the old name is in conflict with another id table entry and
  1961. // could potentially break the name morph logic or cause collisions
  1962. // in the name conflict table.
  1963. //
  1964. CopyMemory(IDTableRec->FileName, CoCmd->FileName, CoCmd->FileNameLength);
  1965. IDTableRec->FileName[CoCmd->FileNameLength/sizeof(WCHAR)] = UNICODE_NULL;
  1966. IDTableRec->ParentFileID = ChangeOrder->NewParentFid;
  1967. IDTableRec->ParentGuid = CoCmd->NewParentGuid;
  1968. CHANGE_ORDER_TRACE(3, ChangeOrder, "IDT Reanimate name field Update");
  1969. // SUDARC-DEV
  1970. // FRS_ASSERT(IDTableRec->ParentFileID != ZERO_FID);
  1971. if (IDTableRec->ParentFileID == ZERO_FID) {
  1972. DPRINT(4, "WARN - Parent fid is zero.\n");
  1973. }
  1974. // SUDARC-DEV
  1975. FRS_ASSERT(IDTableRec->FileID != ZERO_FID);
  1976. FStatus = DbsUpdateIDTableFields(ThreadCtx,
  1977. Replica,
  1978. ChangeOrder,
  1979. IdtFieldReanimateList,
  1980. IdtCoReanimateFieldCount);
  1981. if (!FRS_SUCCESS(FStatus)) {
  1982. DPRINT_FS(0, "++ ERROR - DbsUpdateIDTableFields();", FStatus);
  1983. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - IDT Update FID/Name failed");
  1984. ChgOrdReject(ChangeOrder, Replica);
  1985. goto CO_CLEANUP;
  1986. }
  1987. }
  1988. //
  1989. // If this is not a retry of a prior CO or a recovery CO or a parent
  1990. // reanimation CO or a MorphGenCo or Move out generated CO
  1991. // then insert the change order into the Inbound Log.
  1992. //
  1993. if (!RetryCo &&
  1994. !RecoveryCo &&
  1995. !MorphGenCo &&
  1996. !ParentReanimation &&
  1997. !COE_FLAG_ON(ChangeOrder, COE_FLAG_DELETE_GEN_CO)) {
  1998. FStatus = ChgOrdInsertInlogRecord(ThreadCtx,
  1999. InLogTableCtx,
  2000. ChangeOrder,
  2001. Replica,
  2002. DuplicateCo || RetryPreinstall);
  2003. FRS_ASSERT(!IS_GUID_ZERO(ChangeOrder->pParentGuid));
  2004. if (!FRS_SUCCESS(FStatus)) {
  2005. DPRINT_FS(0, "++ ERROR - ChgOrdInsertInlogRecord failed.", FStatus);
  2006. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - INLOG Insert Failed");
  2007. ChgOrdReject(ChangeOrder, Replica);
  2008. //
  2009. // Check if this is a duplicate remote CO. In that case do not delete
  2010. // the idtable entry or the preinstall file for this CO as there is
  2011. // another CO in the inbound log that is still processing this file.
  2012. // Check the state of the CO in the inbound log. If it has passed
  2013. // the IBCO_FETCH_RETRY state then send the ack for this CO. If it
  2014. // has not completed staging yet then do not send the ack for this CO.
  2015. //
  2016. if (FStatus == FrsErrorKeyDuplicate) {
  2017. //
  2018. // This is a duplicate CO so don't delete the preinstall file
  2019. // and the idtable record for it.
  2020. //
  2021. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_PREINSTALL);
  2022. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_IDT_ENTRY);
  2023. //
  2024. // Send an ack to the upstream member only if there is another CO in
  2025. // the inbound log and if its state is passed IBCO_FETCH_RETRY
  2026. // which we determine below by reading the record.
  2027. //
  2028. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_ACK_INBOUND);
  2029. KeyArray[0] = (PVOID)&CoCmd->CxtionGuid;
  2030. KeyArray[1] = (PVOID)&CoCmd->ChangeOrderGuid;
  2031. jerr = DbsOpenTable(ThreadCtx, TmpINLOGTableCtx, Replica->ReplicaNumber, INLOGTablex, NULL);
  2032. if (!JET_SUCCESS(jerr)) {
  2033. DPRINT1_JS(0, "DbsOpenTable (INLOG) on replica number %d failed.",
  2034. Replica->ReplicaNumber, jerr);
  2035. DbsCloseTable(jerr1, ThreadCtx->JSesid, TmpINLOGTableCtx);
  2036. DbsFreeTableCtx(TmpINLOGTableCtx, 1);
  2037. ChgOrdReject(ChangeOrder, Replica);
  2038. goto CO_CLEANUP;
  2039. }
  2040. FStatus = DbsRecordOperationMKey(ThreadCtx,
  2041. ROP_READ,
  2042. KeyArray,
  2043. ILCxtionGuidCoGuidIndexx,
  2044. TmpINLOGTableCtx);
  2045. DPRINT_FS(0,"++ ERROR - DbsRecordOperationMKey failed.", FStatus);
  2046. //
  2047. // Send an ack to the upstream member only if there is another CO in
  2048. // the inbound log and if its state is passed IBCO_FETCH_RETRY
  2049. //
  2050. if (FRS_SUCCESS(FStatus)) {
  2051. DupCoCmd = (PCHANGE_ORDER_RECORD)TmpINLOGTableCtx->pDataRecord;
  2052. if (DupCoCmd->State > IBCO_FETCH_RETRY) {
  2053. CHANGE_ORDER_TRACE(3, ChangeOrder, "Dup Co setting ISCU_ACK_INBOUND");
  2054. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACK_INBOUND);
  2055. }
  2056. DupCoCmd = NULL;
  2057. }
  2058. //
  2059. // Close the table and free the storage.
  2060. //
  2061. DbsCloseTable(jerr, ThreadCtx->JSesid, TmpINLOGTableCtx);
  2062. DPRINT_JS(0,"ERROR - JetCloseTable on TmpINLOGTableCtx failed :", jerr);
  2063. DbsFreeTableCtx(TmpINLOGTableCtx, 1);
  2064. }
  2065. goto CO_CLEANUP;
  2066. } else {
  2067. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co INLOG Insert");
  2068. }
  2069. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_INLOG);
  2070. } else {
  2071. if (MorphGenCo) {
  2072. //
  2073. // MorphGenCos are local Cos and start out in Staging Requested.
  2074. //
  2075. SET_CHANGE_ORDER_STATE(ChangeOrder, IBCO_STAGING_REQUESTED);
  2076. }
  2077. }
  2078. /////////////////////////////////////////////////////////////////////
  2079. // //
  2080. // A file can end up with different names on different computers //
  2081. // if an update wins out over a rename. The update will flow back //
  2082. // to the computers that have already executed the rename. The //
  2083. // update must rename the file back to its original position. //
  2084. // In some cases a movedir may get converted into a create when //
  2085. // the original parent dir has been deleted. So these creates //
  2086. // could turn out to be implicit movedirs too. //
  2087. // //
  2088. /////////////////////////////////////////////////////////////////////
  2089. //
  2090. // Not local change order
  2091. // Not a control change order
  2092. // Not a delete, movedir, moveout or movers
  2093. // unless it is a vvjoin CO (which could be a create or a delete)
  2094. // BUT the parent or the file name are different than those in the ID
  2095. // table. This must be an update that won out over a previously
  2096. // applied rename. Rename the file back to the name and parent at
  2097. // the time of the update.
  2098. //
  2099. // VVJOIN COs are special cased to cover the following scenario,
  2100. // A Dir move CO is still in the outbound log of upstream when the
  2101. // connection vvjoins (because it was deleted and created again or
  2102. // the outbound log has been trimmed) in this case we will get a vvjoin
  2103. // create CO for a file at its new location. This vvjoin CO should
  2104. // implicitly move the file or dir as the original CO for move is lost.
  2105. //
  2106. // However, in the case of delete change orders coming from a VVJoin
  2107. // we just want to create the tombstone even if the file had moved
  2108. // to a different parent before it was deleted on the upstream member.
  2109. //
  2110. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  2111. if (!LocalCo && !ControlCo &&
  2112. ( CO_NEW_FILE(LocationCmd) ||
  2113. !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCATION_CMD) ||
  2114. CO_FLAG_ON(ChangeOrder, CO_FLAG_VVJOIN_TO_ORIG)
  2115. ) &&
  2116. ( !GUIDS_EQUAL(&CoCmd->NewParentGuid, &IDTableRec->ParentGuid) ||
  2117. WSTR_NE(IDTableRec->FileName, CoCmd->FileName)
  2118. )
  2119. ) {
  2120. //
  2121. // Put out a trace record so we know when the new MOVEDIR cases hit.
  2122. //
  2123. if (FrsDoesCoAlterNameSpace(CoCmd) &&
  2124. !CO_FLAG_ON(ChangeOrder, CO_FLAG_VVJOIN_TO_ORIG)) {
  2125. CHANGE_ORDER_TRACE(3, ChangeOrder, "**** NEW Implicit MOVEDIR case");
  2126. }
  2127. ChangeOrder->OriginalParentFid = IDTableRec->ParentFileID;
  2128. CoCmd->OldParentGuid = IDTableRec->ParentGuid;
  2129. //
  2130. // Make it a MOVEDIR if the parent changed
  2131. //
  2132. if (!GUIDS_EQUAL(&CoCmd->NewParentGuid, &IDTableRec->ParentGuid)) {
  2133. //
  2134. // VVJOIN Cos do not have content command flag set so we should
  2135. // set it here so that StuInstallStage installs the file after
  2136. // moving it to the correct location.
  2137. //
  2138. // see above about no Implicit Movedir for delete Cos.
  2139. // this was 256531 hit by Cox.
  2140. //
  2141. if (!DOES_CO_DELETE_FILE_NAME(CoCmd)) {
  2142. CoCmd->ContentCmd |= USN_REASON_DATA_EXTEND;
  2143. SET_CO_FLAG(ChangeOrder, CO_FLAG_CONTENT_CMD);
  2144. SET_CO_FLAG(ChangeOrder, CO_FLAG_LOCATION_CMD);
  2145. SET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command, CO_LOCATION_MOVEDIR);
  2146. CHANGE_ORDER_TRACE(3, ChangeOrder, "Implicit MOVEDIR");
  2147. LocationCmd = CO_LOCATION_MOVEDIR;
  2148. }
  2149. //
  2150. // Make it a simple rename if the parent did not change
  2151. //
  2152. } else {
  2153. //
  2154. // A simple rename does not have a location command and it
  2155. // has a content command with the value USN_REASON_NEW_NAME.
  2156. // The location command needs to be cleared so that StuInstallStage
  2157. // correctly does the rename of the file to the correct location.
  2158. // see above about no Implicit rename for delete Cos.
  2159. //
  2160. if (!DOES_CO_DELETE_FILE_NAME(CoCmd)) {
  2161. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_LOCATION_CMD);
  2162. SET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command, CO_LOCATION_NUM_CMD);
  2163. LocationCmd = CO_LOCATION_NUM_CMD;
  2164. SET_CO_FLAG(ChangeOrder, CO_FLAG_CONTENT_CMD);
  2165. CoCmd->ContentCmd |= USN_REASON_RENAME_NEW_NAME;
  2166. CHANGE_ORDER_TRACE(3, ChangeOrder, "Implicit RENAME");
  2167. }
  2168. }
  2169. }
  2170. //
  2171. // Check for a Directory Enumerate pending on the IDTable entry for
  2172. // this file. If found, walk through and touch all the children.
  2173. // Note that this could be triggered by some other CO that is
  2174. // operating on this directory, not just the MOVEIN CO that put the
  2175. // dir into the ENUM_PENDING state. This solves the problem where
  2176. // one CO does a MOVEIN of a subdir and a subsequent CO does a MOVEDIR
  2177. // on a child dir before the parent of the child dir has the ENUM performed
  2178. // on it. (Actually not sure this can really happen because the
  2179. // child dir would still not be in the replica set so the journal should
  2180. // treat it as a MOVEIN.)
  2181. //
  2182. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_ENUM_PENDING)) {
  2183. //
  2184. // Do the enumeration.
  2185. //
  2186. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Enum Request Started");
  2187. WStatus = ChgOrdMoveInDirTree(Replica, ChangeOrder);
  2188. DPRINT1_WS(0, "++ ERROR - Failed to enum dir: %ws;",CoCmd->FileName, WStatus);
  2189. //
  2190. // Clear the flag bit in the IDTable record.
  2191. //
  2192. ClearIdRecFlag(IDTableRec, IDREC_FLAGS_ENUM_PENDING);
  2193. FStatus = ChgOrdIssueCleanup(ThreadCtx,
  2194. Replica,
  2195. ChangeOrder,
  2196. ISCU_NO_CLEANUP_MERGE |
  2197. ISCU_UPDATE_IDT_FLAGS
  2198. );
  2199. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  2200. }
  2201. //
  2202. // If this CO is a Directory Enum request then we are now done so
  2203. // clean it up.
  2204. //
  2205. if (CO_STATE_IS(ChangeOrder, IBCO_ENUM_REQUESTED)) {
  2206. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Enum Request Completed");
  2207. CLEAR_CO_IFLAG(ChangeOrder, CO_IFLAG_DIR_ENUM_PENDING);
  2208. ChgOrdReject(ChangeOrder, Replica);
  2209. goto CO_CLEANUP;
  2210. }
  2211. //
  2212. // Reserve resources for this CO in the hold issue interlock tables.
  2213. //
  2214. if (!ChgOrdReserve(IDTableRec, ChangeOrder, Replica)) {
  2215. goto CO_CLEANUP;
  2216. }
  2217. //
  2218. // PERF: At this point we should be able to unidle the queue
  2219. // to let other threads grab a change order since the entry in
  2220. // the hash table provides the necessary interlock.
  2221. // TRY THIS LATER ONCE A SINGLE THREAD WORKS
  2222. //
  2223. if (SerializeAllChangeOrders) {
  2224. SET_COE_FLAG(ChangeOrder, COE_FLAG_VOL_COLIST_BLOCKED);
  2225. }
  2226. //
  2227. // Close the replica tables. The RtCtx struct continues on with
  2228. // the change order until it retires.
  2229. //
  2230. jerr = DbsCloseReplicaTables(ThreadCtx, Replica, RtCtx, TRUE);
  2231. if (!JET_SUCCESS(jerr)) {
  2232. DPRINT_JS(0,"++ ERROR - DbsCloseReplicaTables failed:", jerr);
  2233. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Close Tbl Failed");
  2234. ChgOrdReject(ChangeOrder, Replica);
  2235. goto CO_CLEANUP;
  2236. }
  2237. //
  2238. // If COE_FLAG_NEED_DELETE is set then the file is in the deferred
  2239. // delete state which means the delete was never successfull
  2240. // (say because of a sharing violation). So we have the file.
  2241. // We still need to fetch the file from upstream because we may
  2242. // have a older copy of the file/dir. The only case when we
  2243. // don't want to fetch the file from upstream is when a local
  2244. // child create/update triggerred the reanimation of parent.
  2245. // The upstream may or may not have the parent in this case.
  2246. // As a future work we should send this parent reanimation CO
  2247. // to our outbound partners so that all the members have the same
  2248. // version of the parent.
  2249. //
  2250. // e.g. Everyone has V1 of parent.
  2251. // Member 2 locks V1.
  2252. // Everyone goes to V2 of parent.
  2253. // Member 1 locks V2
  2254. // Everyone deletes parent.
  2255. // Child created on M1 and M2.
  2256. // Both reanimate and keep their copy of parent.
  2257. // If parent reanimation is not sent out then both with have a different
  2258. // copy of parent.
  2259. //
  2260. FilePresent = COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_DELETE);
  2261. if (FilePresent && LocalCo && !ControlCo && ParentReanimation) {
  2262. //
  2263. // Retire the CO here if we already have the file and this is
  2264. // a reanimation. We don't need to have it deleted.
  2265. //
  2266. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_NEED_DELETE);
  2267. CHANGE_ORDER_TRACE(3, ChangeOrder, "File Present");
  2268. ChgOrdInboundRetired(ChangeOrder);
  2269. } else {
  2270. //
  2271. // Dispatch the change order to the appropriate next stage.
  2272. //
  2273. FStatus = ChgOrdDispatch(ThreadCtx, IDTableRec, ChangeOrder, Replica);
  2274. if (FStatus == FrsErrorCantMoveOut) {
  2275. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Retry - FrsErrorCantMoveOut");
  2276. // Following is commented because we want to retry the delete of parent dir.
  2277. // ChgOrdDispatch fails with FrsErrorCantMoveOut if there are
  2278. // valid children under the parent dir.
  2279. // ChgOrdReject(ChangeOrder, Replica);
  2280. // goto CO_CLEANUP;
  2281. goto CO_RETRY_LATER;
  2282. }
  2283. }
  2284. ChangeOrder = NULL;
  2285. //
  2286. // The CO was issued but if we are serializing all change orders
  2287. // on this volume then set the flag to leave the process queue idle.
  2288. //
  2289. UnIdleProcessQueue = !SerializeAllChangeOrders;
  2290. goto CO_CONTINUE;
  2291. CO_RETRY_LATER:
  2292. CO_RETRY_PREINSTALL:
  2293. CO_DEFER_DUP_CO:
  2294. //
  2295. // This is a duplicate CO which cannot be processed right now.
  2296. // If it's not a retry then insert CO into the inbound log.
  2297. // Either way, skip it for now. We should never be here with a
  2298. // deamnd refresh flag set in the CO because we should detect
  2299. // the case of a dup CO before we know the file is deleted.
  2300. //
  2301. if (!RetryCo && !RecoveryCo && !MorphGenCo) {
  2302. FRS_ASSERT(!CO_FLAG_ON(ChangeOrder, CO_FLAG_DEMAND_REFRESH));
  2303. FStatus = ChgOrdInsertInlogRecord(ThreadCtx,
  2304. TmpINLOGTableCtx,
  2305. ChangeOrder,
  2306. Replica,
  2307. DuplicateCo || RetryPreinstall);
  2308. FRS_ASSERT(!IS_GUID_ZERO(ChangeOrder->pParentGuid));
  2309. //
  2310. // The pDataRecord points to the change order command which will
  2311. // be freed with the change order. Also clear the Jet Set/Ret Col
  2312. // address fields for the Change Order Extension buffer to
  2313. // prevent reuse since that buffer also goes with the change order.
  2314. //
  2315. TmpINLOGTableCtx->pDataRecord = NULL;
  2316. DBS_SET_FIELD_ADDRESS(TmpINLOGTableCtx, COExtensionx, NULL);
  2317. if (FStatus == FrsErrorKeyDuplicate) {
  2318. //
  2319. // Got a new dup remote co before we could finish and ack the
  2320. // previous one. Probably a result of restart. Discard this
  2321. // one. Send an ack to the upstream member only if there the
  2322. // CO in the inbound log has passed IBCO_FETCH_RETRY state
  2323. // (which means it has already sent its Ack, which was
  2324. // probably dropped, and won't send another) which we
  2325. // determine below by reading the record.
  2326. //
  2327. FRS_ASSERT(!LocalCo);
  2328. ChgOrdReject(ChangeOrder, Replica);
  2329. //
  2330. // Do not discard the vv slot as it is being used by the duplicate
  2331. // Change order.
  2332. //
  2333. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_ACK_INBOUND |
  2334. ISCU_ACTIVATE_VV |
  2335. ISCU_ACTIVATE_VV_DISCARD);
  2336. KeyArray[0] = (PVOID)&CoCmd->CxtionGuid;
  2337. KeyArray[1] = (PVOID)&CoCmd->ChangeOrderGuid;
  2338. jerr = DbsOpenTable(ThreadCtx, TmpINLOGTableCtx, Replica->ReplicaNumber, INLOGTablex, NULL);
  2339. if (!JET_SUCCESS(jerr)) {
  2340. DPRINT1_JS(0, "DbsOpenTable (INLOG) on replica number %d failed.",
  2341. Replica->ReplicaNumber, jerr);
  2342. DbsCloseTable(jerr1, ThreadCtx->JSesid, TmpINLOGTableCtx);
  2343. DbsFreeTableCtx(TmpINLOGTableCtx, 1);
  2344. goto CO_CLEANUP;
  2345. }
  2346. FStatus = DbsRecordOperationMKey(ThreadCtx,
  2347. ROP_READ,
  2348. KeyArray,
  2349. ILCxtionGuidCoGuidIndexx,
  2350. TmpINLOGTableCtx);
  2351. DPRINT_FS(0,"++ ERROR - DbsRecordOperationMKey failed.", FStatus);
  2352. if (FRS_SUCCESS(FStatus)) {
  2353. DupCoCmd = (PCHANGE_ORDER_RECORD)TmpINLOGTableCtx->pDataRecord;
  2354. if (DupCoCmd->State > IBCO_FETCH_RETRY) {
  2355. CHANGE_ORDER_TRACE(3, ChangeOrder, "Dup Co setting ISCU_ACK_INBOUND");
  2356. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACK_INBOUND);
  2357. }
  2358. DupCoCmd = NULL;
  2359. }
  2360. //
  2361. // Close the table and free the storage.
  2362. //
  2363. DbsCloseTable(jerr, ThreadCtx->JSesid, TmpINLOGTableCtx);
  2364. DPRINT_JS(0,"ERROR - JetCloseTable on TmpINLOGTableCtx failed :", jerr);
  2365. DbsFreeTableCtx(TmpINLOGTableCtx, 1);
  2366. } else
  2367. if (!FRS_SUCCESS(FStatus)) {
  2368. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - INLOG Insert Failed");
  2369. DPRINT_FS(0, "++ ERROR - ChgOrdInsertInlogRecord failed.", FStatus);
  2370. FRS_ASSERT(!"INLOG Insert Failed");
  2371. ChgOrdReject(ChangeOrder, Replica);
  2372. }
  2373. } else {
  2374. //
  2375. // Set the retry flag so we will try again later.
  2376. //
  2377. InterlockedIncrement(&Replica->InLogRetryCount);
  2378. }
  2379. CO_CLEANUP:
  2380. //
  2381. // In general a branch to this point indicates that the entry in the
  2382. // InLog did not get done. If the CO was rejected for other reasons
  2383. // we flow through here to cleanup state. If it was a remote CO then
  2384. // update the entry in the Version vector for dampening and Ack the
  2385. // Co to our inbound partner. We also get here if we are draining
  2386. // the COs for this cxtion from the process queue.
  2387. //
  2388. FStatus = ChgOrdIssueCleanup(ThreadCtx, Replica, ChangeOrder, 0);
  2389. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  2390. CO_CONTINUE:
  2391. //
  2392. // All done. Decrement local change order queue count for this
  2393. // replica at the end so there is no window during which a save mark
  2394. // could occur and see a zero count even though we have yet to
  2395. // actually insert the CO into the Inlog.
  2396. // The LocalCoQueueCount only tracks local COs that came from
  2397. // the NTFS journal. i.e.,
  2398. // LocalCo, Not a Retry Co, Not a recovery Co, Not a block issue
  2399. // case, Not a control CO, and not a parent reanimation.
  2400. // Note: A Local Co should not have the CO_FLAG_PARENT_REANIMATION
  2401. // flag set.
  2402. //
  2403. // CO_FLAG_MOVEIN_GEN NOTE: This flag does not currently appear to be
  2404. // set anywhere (6/2002). It is tested in several places to bypass
  2405. // an INC_LOCAL_CO_QUEUE_COUNT . It appears that if this flag does
  2406. // get set then the below may NOT bypass the DECREMENT so the count will
  2407. // go negative. This would suppress updates of the journal read point
  2408. // during times of volume activity outside the replica tree and could
  2409. // result in a journal wrap. So if this flag does get set, consider
  2410. // revamping this code to use a COE flag set by journal code when
  2411. // a new local co is created and queued to the process queue. Then
  2412. // just test the flag to control the decrement.
  2413. //
  2414. if (LocalCo &&
  2415. !RetryCo &&
  2416. !RecoveryCo &&
  2417. (Decision != ISSUE_CONFLICT) &&
  2418. !ControlCo &&
  2419. !MorphGenCo) {
  2420. // FRS_ASSERT(!ParentReanimation);
  2421. if (!ParentReanimation) {
  2422. DEC_LOCAL_CO_QUEUE_COUNT(Replica);
  2423. }
  2424. }
  2425. //
  2426. // Unidle the queue, reset the timeout.
  2427. //
  2428. if (UnIdleProcessQueue) {
  2429. FrsRtlUnIdledQueue(IdledQueue);
  2430. }
  2431. TimeOut = 100;
  2432. ReleaseVmeRef(pVme);
  2433. } // End of while(TRUE)
  2434. //
  2435. // Get exception status.
  2436. //
  2437. } except (EXCEPTION_EXECUTE_HANDLER) {
  2438. GET_EXCEPTION_CODE(WStatus);
  2439. }
  2440. } finally {
  2441. //
  2442. // Shutdown change order accept.
  2443. //
  2444. if (WIN_SUCCESS(WStatus)) {
  2445. if (AbnormalTermination()) {
  2446. WStatus = ERROR_OPERATION_ABORTED;
  2447. }
  2448. }
  2449. DPRINT_WS(0, "Chg Order Accept finally.", WStatus);
  2450. //
  2451. // Stop the Retry thread.
  2452. //
  2453. FrsRunDownCommandServer(&ChgOrdRetryCS, &ChgOrdRetryCS.Queue);
  2454. //
  2455. // Close any open tables and terminate the Jet Session.
  2456. //
  2457. //
  2458. // Close the tables and free the table ctx structs and our thread ctx.
  2459. //
  2460. DbsFreeTableContext(TmpIDTableCtx, ThreadCtx->JSesid);
  2461. TmpIDTableCtx = NULL;
  2462. DbsFreeTableContext(TmpIDTableCtxNC, ThreadCtx->JSesid);
  2463. TmpIDTableCtxNC = NULL;
  2464. DbsFreeTableContext(TmpINLOGTableCtx, ThreadCtx->JSesid);
  2465. TmpINLOGTableCtx = NULL;
  2466. //
  2467. // Now close the jet session and free the Jet ThreadCtx.
  2468. //
  2469. jerr = DbsCloseJetSession(ThreadCtx);
  2470. if (!JET_SUCCESS(jerr)) {
  2471. DPRINT_JS(0,"++ DbsCloseJetSession :", jerr);
  2472. } else {
  2473. DPRINT(4,"++ DbsCloseJetSession complete\n");
  2474. }
  2475. ThreadCtx = FrsFreeType(ThreadCtx);
  2476. //
  2477. // Trigger FRS shutdown if we terminated abnormally.
  2478. //
  2479. if (!WIN_SUCCESS(WStatus)) {
  2480. DPRINT(0, "Changeorder accept terminated abnormally, forcing service shutdown.\n");
  2481. FrsIsShuttingDown = TRUE;
  2482. SetEvent(ShutDownEvent);
  2483. }
  2484. DPRINT(0, "ChangeOrder Thread is exiting\n");
  2485. }
  2486. return ERROR_SUCCESS;
  2487. }
  2488. ULONG
  2489. ChgOrdFetchCmd(
  2490. PFRS_QUEUE *pIdledQueue,
  2491. PCHANGE_ORDER_ENTRY *pChangeOrder,
  2492. PVOLUME_MONITOR_ENTRY *ppVme
  2493. )
  2494. /*++
  2495. Routine Description:
  2496. Fetch the next change order command from FrsVolumeLayerCOList.
  2497. FrsVolumeLayerCOList is a striped queue that combines the change order
  2498. process queues from each active volume. We return with the queue lock
  2499. and a pointer to the head entry on the queue. If the caller can
  2500. process the change order the caller removes the entry and drops the
  2501. queue lock. If the caller can't process the entry due to a conflict with
  2502. a change order currently in process then the caller leaves the entry
  2503. on the queue, idles the queue and calls us again. We can then pick up
  2504. work from another queue or wait until the conflict clears and the queue
  2505. is un-blocked.
  2506. Arguments:
  2507. pIdledQueue - Returns the ptr to the Change Order Process queue to idle.
  2508. pChangeOrder - Returns the ptr to the change order entry from the queue.
  2509. ppVme - Returns the ptr to the Volume Monitor entry associated with the queue.
  2510. Return Value:
  2511. FrsError status
  2512. If successful this returns with the FrsVolumeLayerCOList Lock held and a
  2513. reference taken on the Volume Monitor Entry (Vme).
  2514. The caller must release the lock and drop the reference.
  2515. --*/
  2516. {
  2517. #undef DEBSUB
  2518. #define DEBSUB "ChgOrdFetchCmd:"
  2519. ULONG WStatus;
  2520. PFRS_QUEUE Queue;
  2521. PLIST_ENTRY Entry;
  2522. PCHANGE_ORDER_ENTRY ChangeOrder;
  2523. PVOLUME_MONITOR_ENTRY pVme;
  2524. PREPLICA Replica;
  2525. ULONG TimeNow;
  2526. ULONG WaitTime;
  2527. BOOL LocalCo;
  2528. //
  2529. // We stay in the loop until we either get a change order to process or
  2530. // the queue is rundown.
  2531. //
  2532. while(TRUE) {
  2533. //
  2534. // Check if it's time to retry any inbound COs. If so then
  2535. // submit retry cmds to the CO Retry thread.
  2536. //
  2537. TimeNow = GetTickCount();
  2538. WaitTime = ChgOrdNextRetryTime - TimeNow;
  2539. if ((WaitTime > InlogRetryInterval) || (WaitTime == 0)) {
  2540. DPRINT3(4, "NextRetryTime: %08x Time: %08x Diff: %08x\n",
  2541. ChgOrdNextRetryTime, TimeNow, WaitTime);
  2542. ChgOrdNextRetryTime += InlogRetryInterval;
  2543. //
  2544. // Check for any CO retries needed on this replica set.
  2545. //
  2546. ForEachListEntry( &ReplicaListHead, REPLICA, ReplicaList,
  2547. // Loop iterator pE is type PREPLICA.
  2548. if ((pE->ServiceState == REPLICA_STATE_ACTIVE) &&
  2549. (pE->InLogRetryCount > 0)) {
  2550. //
  2551. // Allocate command packet and submit to the CO Retry thread.
  2552. //
  2553. ChgOrdRetrySubmit(pE, NULL, FCN_CORETRY_ALL_CXTIONS, FALSE);
  2554. }
  2555. );
  2556. }
  2557. //
  2558. // Wait on the queue so that rundowns can be detected. Waiting
  2559. // on the control queue (FrsVolumeLayerCOList) can hang shutdown
  2560. // because not all pVme->ChangeOrderLists are rundown; so the
  2561. // control queue remains "active".
  2562. //
  2563. WStatus = FrsRtlWaitForQueueFull(&FrsVolumeLayerCOList, 10000);
  2564. if (WStatus == ERROR_INVALID_HANDLE ||
  2565. ChangeOrderAcceptIsShuttingDown) {
  2566. DPRINT(1, "Shutting down ChgOrdAccept...\n");
  2567. //
  2568. // Queue was run down. Time to exit.
  2569. //
  2570. return FrsErrorQueueIsRundown;
  2571. }
  2572. if (WStatus == WAIT_TIMEOUT) {
  2573. continue;
  2574. }
  2575. if (!WIN_SUCCESS(WStatus)) {
  2576. //
  2577. // Some other error.
  2578. //
  2579. DPRINT_WS(0, "Wait for queue error", WStatus);
  2580. return FrsErrorQueueIsRundown;
  2581. }
  2582. //
  2583. // Perf: With multiple queues we aren't guaranteed to get the one
  2584. // nearest expiration. Replace below with a loop over each list on the
  2585. // full queue so we can compute the wait times for the head item on
  2586. // each queue then build an ordered list to tell us which entry to
  2587. // run next.
  2588. //
  2589. // Now peek at the first entry on the queue and see if time is up.
  2590. // This is ugly and it is the reason this isn't a command server.
  2591. //
  2592. FrsRtlAcquireListLock(&FrsVolumeLayerCOList);
  2593. if (FrsVolumeLayerCOList.ControlCount == 0) {
  2594. //
  2595. // Somebody got here before we did, drop lock and go wait on the queue.
  2596. //
  2597. DPRINT(4, "++ Control Count is zero\n");
  2598. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  2599. continue;
  2600. }
  2601. //
  2602. // Get next queue on the FULL list & get the pVme containing the queue.
  2603. //
  2604. Entry = GetListHead(&FrsVolumeLayerCOList.Full);
  2605. Queue = CONTAINING_RECORD(Entry, FRS_QUEUE, Full);
  2606. pVme = CONTAINING_RECORD(Queue, VOLUME_MONITOR_ENTRY, ChangeOrderList);
  2607. //
  2608. // Get the reference on the Vme. If the return is zero the Vme is gone.
  2609. // This could happen since there is a window between getting the queue
  2610. // and getting the entry off the queue.
  2611. //
  2612. if (AcquireVmeRef(pVme) == 0) {
  2613. continue;
  2614. }
  2615. //
  2616. // Get a pointer to the first change order entry on the queue.
  2617. //
  2618. Entry = GetListHead(&Queue->ListHead);
  2619. ChangeOrder = CONTAINING_RECORD(Entry, CHANGE_ORDER_ENTRY, ProcessList);
  2620. FRS_PRINT_TYPE(5, ChangeOrder);
  2621. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  2622. //
  2623. // If this is a Local Change order then Check the Jrnl Cxtion for this
  2624. // Replica Set. If it is unjoined then don't wait for Aging cache
  2625. // timeout. Fill in the Cxtion ptr in the Change order.
  2626. //
  2627. if (LocalCo && (ChangeOrder->Cxtion == NULL)) {
  2628. Replica = CO_REPLICA(ChangeOrder);
  2629. FRS_ASSERT(Replica != NULL);
  2630. ACQUIRE_CXTION_CO_REFERENCE(Replica, ChangeOrder);
  2631. }
  2632. //
  2633. // If the wait time has not expired then sleep for the time left.
  2634. // The local change order entry stays on the queue. It can accumulate
  2635. // additional changes or evaporate because it is still in the aging
  2636. // cache. Note that when remote change orders are inserted on the
  2637. // queue TimeNow and TimeToRun are set to the current time so they
  2638. // don't wait. If Cxtion is unjoined then don't wait.
  2639. //
  2640. if (ChangeOrder->Cxtion != NULL) {
  2641. TimeNow = GetTickCount();
  2642. WaitTime = ChangeOrder->TimeToRun - TimeNow;
  2643. if ((WaitTime <= ChangeOrderAgingDelay) && (WaitTime != 0)) {
  2644. DPRINT3(4, "TimeToRun: %08x Time: %08x Diff: %08x\n",
  2645. ChangeOrder->TimeToRun, TimeNow, WaitTime);
  2646. //
  2647. // PERF: Instead of sleeping post a queue wakeup (unidle) so
  2648. // we can continue to process other volume queues.
  2649. //
  2650. //
  2651. // Drop the lock and reference for the wait.
  2652. //
  2653. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  2654. ReleaseVmeRef(pVme);
  2655. //Sleep(WaitTime * 100);
  2656. Sleep(WaitTime);
  2657. continue;
  2658. }
  2659. }
  2660. //
  2661. // Pull the local change order out of the hash table. We have the
  2662. // change order process list lock, preventing the journal from
  2663. // updating it. Recovery COs and Retry COs come from the
  2664. // Inbound log and aren't in the aging cache.
  2665. //
  2666. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_IN_AGING_CACHE)) {
  2667. BOOL RetryCo, RecoveryCo, MorphGenCo;
  2668. ULONG GStatus;
  2669. RetryCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY);
  2670. RecoveryCo = RecoveryCo(ChangeOrder);
  2671. MorphGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN);
  2672. FRS_ASSERT(LocalCo && !RecoveryCo && !RetryCo && !MorphGenCo);
  2673. GStatus = GhtRemoveEntryByAddress(pVme->ChangeOrderTable,
  2674. ChangeOrder,
  2675. TRUE);
  2676. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_IN_AGING_CACHE);
  2677. if (GStatus != GHT_STATUS_SUCCESS) {
  2678. DPRINT(0, "++ ERROR - GhtRemoveEntryByAddress failed.\n");
  2679. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected");
  2680. FRS_PRINT_TYPE(0, ChangeOrder);
  2681. FRS_ASSERT(!"GhtRemoveEntryByAddress failed.");
  2682. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  2683. //ChgOrdReject(ChangeOrder, Replica);
  2684. return FrsErrorQueueIsRundown;
  2685. }
  2686. }
  2687. //
  2688. // Delay has elapsed.
  2689. // Return the change order and its associated queue and Vme.
  2690. // If the caller can process the change order the caller removes it
  2691. // from the queue.
  2692. //
  2693. *pIdledQueue = Queue;
  2694. *pChangeOrder = ChangeOrder;
  2695. *ppVme = pVme;
  2696. return FrsErrorSuccess;
  2697. }
  2698. }
  2699. ULONG
  2700. ChgOrdHoldIssue(
  2701. IN PREPLICA Replica,
  2702. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  2703. IN PFRS_QUEUE CoProcessQueue
  2704. )
  2705. /*++
  2706. Routine Description:
  2707. This routine ensures that a new change order does on conflict with a
  2708. change order already in progress. If it does then we set a flag in the
  2709. active state indicating the change order process queue is blocked and
  2710. and return status. The process queue is blocked while holding the change
  2711. order lock (using the FID or parent FID) to synchronize with the active
  2712. change order retire operation.
  2713. There are four file dependency cases to consider plus the case of duplicates.
  2714. 1. File update/create on file X must preceed any subsequent file
  2715. update/delete on file X. (X could be a file or a Dir)
  2716. 2. Parent dir create/update (e.g. ACL, RO) must preceed any
  2717. subsequent child file or dir operations.
  2718. 3. Child File or Dir operations must preceed any subsequent parent dir
  2719. update/delete.
  2720. 4. Any rename or delete operation that releases a filename must preceed
  2721. any rename or create operation that will reuse the filename.
  2722. 5. Duplicate change orders can arrive from multiple inbound partners.
  2723. If they arrive at the same time then we could have one in progress while
  2724. the second tries to issue. We can't immediately dampen the second CO because
  2725. the first may fail (E.G. can't complete the fetch) so we mark the duplicate
  2726. CO for retry and stick it in the Inbound log incase the currently active
  2727. CO fails.
  2728. Three hash tables are used to keep the active state:
  2729. - The ActiveInboundChangeOrderTable keeps an entry for the change order
  2730. when it is issued. The CO stays in the table until it retires or
  2731. a retry later is requested. This table is indexed by the FileGuid
  2732. so consecutive changes to the same file are performed in order.
  2733. - The ActiveChildren hash table tracks the Parent File IDs of all active
  2734. change orders. Each time a change order issues, the entry for its
  2735. parent is found (or created) and incremented. When the count goes
  2736. to zero the entry is removed. If a change order was waiting for the
  2737. parent count to go to zero it is unblocked.
  2738. - The Name Conflict table is indexed by a hash of the file name and
  2739. the parent directory object ID. If a conflict occurs with an
  2740. outstanding opertion the issuing change order must block until
  2741. the current change order completes. A deleted file and the source
  2742. file of a rename reserve an entry in the table so they can block issue
  2743. of a subsequent create or a rename with a target name that matches.
  2744. The reverse check is not needed since that is handled by the
  2745. ActiveInboundChangeOrderTable.
  2746. Assumptions:
  2747. 1. There are no direct interdependencies between Child File or Dir operations
  2748. and ancestor directories beyond the immediate parent.
  2749. 2. The caller has acquired the FrsVolumeLayerCOList lock.
  2750. Arguments:
  2751. Replica - The Replica set for the CO (also get us to the volume monitor
  2752. entry associated with the replica set).
  2753. ChangeOrder -- The new change order to check ordering conflicts.
  2754. CoProcessQueue -- The process queue this change order is on.
  2755. If the change order can't issue we idle the queue.
  2756. The caller has acquired the queue lock.
  2757. Return Value:
  2758. ISSUE_OK means no conflict so the change order can be issued.
  2759. ISSUE_DUPLICATE_REMOTE_CO means this is a duplicate change order.
  2760. ISSUE_CONFLICT means a dependency condition exists so this change order
  2761. can't issue.
  2762. ISSUE_RETRY_LATER means that the parent dir is not present so this CO
  2763. will have to be retried later.
  2764. ISSUE_REJECT_CO means that we have determined that this CO can be safely
  2765. rejected. This can happen:
  2766. -- if the inbound connection has been deleted so we
  2767. can't fetch the staging file.
  2768. -- if the CO event time is too far into the future so
  2769. it is considered bogus.
  2770. -- if the CO is a morph gen follower and it's apparent
  2771. that the leader didn't get its job done.
  2772. --*/
  2773. {
  2774. #undef DEBSUB
  2775. #define DEBSUB "ChgOrdHoldIssue:"
  2776. ULONGLONG QuadHashValue;
  2777. LONGLONG CurrentTime;
  2778. LONGLONG EventTimeDelta;
  2779. ULONG GStatus;
  2780. PQHASH_ENTRY QHashEntry;
  2781. ULONG LocationCmd;
  2782. BOOL RemoteCo;
  2783. BOOL DropRef = FALSE;
  2784. BOOL ForceNameConflictCheck = FALSE;
  2785. BOOL OrigParentPresent;
  2786. BOOL SelectedParentPresent, EitherParentAbsent;
  2787. BOOL TargetPresent;
  2788. BOOL DestParentAbsentOrDel, OrigParentAbsentOrDel;
  2789. BOOL DestParentDelDeferred;
  2790. BOOL MorphGenCo, CoFromInlog, DeleteCo;
  2791. PCHANGE_ORDER_ENTRY ActiveChangeOrder;
  2792. PCHANGE_ORDER_COMMAND CoCmd;
  2793. PVOLUME_MONITOR_ENTRY pVme;
  2794. PCXTION Cxtion;
  2795. ULONG GuidLockSlot = UNDEFINED_LOCK_SLOT;
  2796. CHAR GuidStr[GUID_CHAR_LEN];
  2797. #define CXTION_STR_MAX 256
  2798. WCHAR CxtionStr[CXTION_STR_MAX], WDelta[15];
  2799. pVme = Replica->pVme;
  2800. CoCmd = &ChangeOrder->Cmd;
  2801. RemoteCo = !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  2802. TargetPresent = !COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_TARGET_ABS);
  2803. OrigParentPresent = !COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_ABS);
  2804. EitherParentAbsent = COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_ABS |
  2805. COE_FLAG_IDT_NEW_PARENT_ABS);
  2806. SelectedParentPresent = !COE_FLAG_ON(ChangeOrder,
  2807. (ChangeOrder->NewReplica != NULL) ?
  2808. COE_FLAG_IDT_NEW_PARENT_ABS :
  2809. COE_FLAG_IDT_ORIG_PARENT_ABS);
  2810. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  2811. //
  2812. // Synchronize with the Replica command server thread that may be changing
  2813. // the cxtion's state, joinguid, or checking Cxtion->CoProcessQueue.
  2814. //
  2815. LOCK_CXTION_TABLE(Replica);
  2816. Cxtion = ChangeOrder->Cxtion;
  2817. if (Cxtion == NULL) {
  2818. DPRINT(0, "++ ERROR - No connection struct for inbound CO\n");
  2819. UNLOCK_CXTION_TABLE(Replica);
  2820. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  2821. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  2822. return ISSUE_REJECT_CO;
  2823. }
  2824. //
  2825. // Every remote change order was received from a cxtion. The change
  2826. // order must be "sent" to that cxtion again for processing. Find it.
  2827. // If it no longer exists then discard the change order.
  2828. //
  2829. // The cxtion can be in any of several states that will end up sending
  2830. // this change order through the retry path. Those states are checked
  2831. // later since they could change between now and issueing the change
  2832. // order to the replica command server.
  2833. //
  2834. if (RemoteCo) {
  2835. //
  2836. // Check if the Event Time on this CO is too far in the future by
  2837. // twice the MaxPartnerClockSkew.
  2838. // This could happen if a member disconnected and its time was
  2839. // set into the future while file operations were performed.
  2840. // Then when the time is set back it reconnects and Joins successfully
  2841. // but is now sending change orders with bogus times.
  2842. //
  2843. GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
  2844. EventTimeDelta = CoCmd->EventTime.QuadPart - CurrentTime;
  2845. if (EventTimeDelta > (LONGLONG)(MaxPartnerClockSkew<<1)) {
  2846. EventTimeDelta = EventTimeDelta / CONVERT_FILETIME_TO_MINUTES;
  2847. DPRINT1(0, "++ ERROR: ChangeOrder rejected based on Bogus Event Time > MaxPartnerClockSkew by %08x %08x minutes.\n",
  2848. PRINTQUAD(EventTimeDelta));
  2849. UNLOCK_CXTION_TABLE(Replica);
  2850. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  2851. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  2852. CHANGE_ORDER_TRACE(3, ChangeOrder, "Bogus Event Time - Rejected");
  2853. _snwprintf(CxtionStr, CXTION_STR_MAX, FORMAT_CXTION_PATH2W,
  2854. PRINT_CXTION_PATH2(Replica, Cxtion));
  2855. CxtionStr[CXTION_STR_MAX-1] = UNICODE_NULL;
  2856. _itow(PartnerClockSkew, WDelta, 10);
  2857. EPRINT3(EVENT_FRS_RMTCO_TIME_SKEW, WDelta, CoCmd->FileName, CxtionStr);
  2858. DPRINT1(0, "++ ERROR: Bogus Event Time on CO via connection: %ws\n", CxtionStr);
  2859. //
  2860. // Should we force an unjoin on this connection? Probably not
  2861. // because if the time on the upstream partner was out of whack
  2862. // and it originated a bunch of COs with the bad time but the
  2863. // time is now ok we would just end up unjoining and rejoining
  2864. // on every bum CO until we get them out of the system.
  2865. //
  2866. return ISSUE_REJECT_CO;
  2867. }
  2868. //
  2869. // Check if this is a "Recovery CO" from an inbound partner.
  2870. // If it is and the partner connection is not yet joined then we
  2871. // hold issue until the join completes.
  2872. //
  2873. if (RecoveryCo(ChangeOrder)) {
  2874. //
  2875. // If the connection is trying to join then hold issue on this CO
  2876. // until it either succeeds or fails.
  2877. //
  2878. if (CxtionStateIs(Cxtion, CxtionStateStarting) ||
  2879. CxtionStateIs(Cxtion, CxtionStateScanning) ||
  2880. CxtionStateIs(Cxtion, CxtionStateWaitJoin) ||
  2881. CxtionStateIs(Cxtion, CxtionStateSendJoin)) {
  2882. //
  2883. // Save the queue address we are blocked on.
  2884. // When JOIN finally succeeds or fails we get unblocked with
  2885. // the appropriate connection state change by a replica
  2886. // command server thread.
  2887. //
  2888. Cxtion->CoProcessQueue = CoProcessQueue;
  2889. GuidToStr(&CoCmd->ChangeOrderGuid, GuidStr);
  2890. DPRINT3(3, "++ CO hold issue on pending JOIN, vsn %08x %08x, CoGuid: %s for %ws\n",
  2891. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  2892. //
  2893. // Idle the queue and drop the connection table lock.
  2894. //
  2895. FrsRtlIdleQueueLock(CoProcessQueue);
  2896. UNLOCK_CXTION_TABLE(Replica);
  2897. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Cxtion Join");
  2898. return ISSUE_CONFLICT;
  2899. }
  2900. }
  2901. }
  2902. UNLOCK_CXTION_TABLE(Replica);
  2903. //
  2904. // If this is a remote CO then it came with a Guid. If this is a local CO
  2905. // then ChgOrdTranslateGuidFid() told us if there is a record in the IDTable.
  2906. //
  2907. // If the Guid is zero then this is a new file from a local change order
  2908. // that was not in the IDTable. So it can't be in the
  2909. // ActiveInboundChangeOrderTable.
  2910. //
  2911. GStatus = GHT_STATUS_NOT_FOUND;
  2912. if (RemoteCo || TargetPresent) {
  2913. /* !IS_GUID_ZERO(&CoCmd->FileGuid) */
  2914. FRS_ASSERT(!IS_GUID_ZERO(&CoCmd->FileGuid));
  2915. //
  2916. // Get the ChangeOrder lock from a lock table using a hash of the
  2917. // change order FileGuid. This resolves the race idleing the queue
  2918. // here and unidleing it in ChgOrdIssueCleanup().
  2919. //
  2920. GuidLockSlot = ChgOrdGuidLock(&CoCmd->FileGuid);
  2921. ChgOrdAcquireLock(GuidLockSlot);
  2922. //
  2923. // Check if the file has an active CO on it. If it does then
  2924. // we can't issue this CO because the active CO could be changing
  2925. // the ACL or the RO bit or it could just be a prior update to the
  2926. // file so ordering has to be maintained.
  2927. //
  2928. // ** NOTE ** Be careful with ActiveChangeOrder, we drop the ref on it later.
  2929. //
  2930. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  2931. &CoCmd->FileGuid,
  2932. TRUE,
  2933. &ActiveChangeOrder);
  2934. DropRef = (GStatus == GHT_STATUS_SUCCESS);
  2935. //
  2936. // Check if we have seen this change order before. This could happen if
  2937. // we got the same change order from two different inbound partners. We
  2938. // can't dampen the second one until the first one completes sucessfully.
  2939. // If for some reason the first one fails during fetch or install (say
  2940. // the install file is corrupt or a sharing violation on the target
  2941. // causes a retry) then we may need to get the change from the other
  2942. // partner. When the first change order completes successfully all the
  2943. // duplicates are rejected by reconcile when they are retried later.
  2944. // At that point we can Ack the inbound partner.
  2945. //
  2946. if (RemoteCo &&
  2947. (GStatus == GHT_STATUS_SUCCESS) &&
  2948. GUIDS_EQUAL(&CoCmd->ChangeOrderGuid,
  2949. &ActiveChangeOrder->Cmd.ChangeOrderGuid)) {
  2950. //DPRINT(5, "++ Hit a case of duplicate change orders.\n");
  2951. //DPRINT(5, "++ Incoming CO\n");
  2952. //FRS_PRINT_TYPE(5, ChangeOrder);
  2953. //DPRINT(5, "++ Active CO\n");
  2954. //FRS_PRINT_TYPE(5, ActiveChangeOrder);
  2955. //
  2956. // Remove the duplicate entry from the queue, drop the locks and
  2957. // return with duplicate remote co status.
  2958. //
  2959. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  2960. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  2961. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  2962. ActiveChangeOrder,
  2963. TRUE);
  2964. DropRef = FALSE;
  2965. //FrsRtlIdleQueueLock(CoProcessQueue);
  2966. ChgOrdReleaseLock(GuidLockSlot);
  2967. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  2968. return ISSUE_DUPLICATE_REMOTE_CO;
  2969. }
  2970. //
  2971. // If the conflict is with an active change order on the same file
  2972. // then set the flag in the active change order to unidle the queue
  2973. // when the active change order completes.
  2974. //
  2975. if (GStatus == GHT_STATUS_SUCCESS) {
  2976. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - File Busy");
  2977. goto CONFLICT;
  2978. }
  2979. if (DropRef) {
  2980. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  2981. ActiveChangeOrder,
  2982. TRUE);
  2983. DropRef = FALSE;
  2984. }
  2985. ChgOrdReleaseLock(GuidLockSlot);
  2986. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  2987. } else {
  2988. MorphGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN);
  2989. //
  2990. // Target absent and a Local CO.
  2991. //
  2992. //
  2993. // If this CO is a MorphGenFollower then it is the rename MorphGenCo
  2994. // produced as part of a Dir/Dir Name Morph Conflict (DLD Case). The
  2995. // fact that the target is still absent means that the base create CO
  2996. // (the leader) failed and is in retry so reject this MorphGenCo. It
  2997. // gets refabricated later when the Leader is re-issued.
  2998. // ***** Note -- THere is similar code in ChgOrdAccept() *****
  2999. //
  3000. if (MorphGenCo && COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER)) {
  3001. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3002. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3003. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - MORPH_GEN_FOLLOWER and leader failed");
  3004. return ISSUE_REJECT_CO;
  3005. }
  3006. // If this CO is a MorphGenleader then it is the rename MorphGenCo
  3007. // produced as part of a Dir/Dir Name Morph Conflict (DDL Case). The
  3008. // fact that the target is absent means that there isn't a conflict .
  3009. // anymore. Reject this CO as there is no need for a rename anymore.
  3010. //
  3011. // The following if makes sure that this is a morph gen rename CO generated
  3012. // to rename the idt entry.
  3013. //
  3014. // Do not pitch MorphGen Delete Cos. An incoming Create Co that
  3015. // looses the name conflict produces a MorphGenLeader DelCo with
  3016. // no IDTable Entry.
  3017. // ***** Note -- THere is similar code in ChgOrdAccept() *****
  3018. //
  3019. if (MorphGenCo &&
  3020. !COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER) &&
  3021. (GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command) != CO_LOCATION_DELETE) &&
  3022. BooleanFlagOn(ChangeOrder->Cmd.ContentCmd, USN_REASON_RENAME_NEW_NAME)) {
  3023. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3024. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3025. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - IDT deleted. No rename needed.");
  3026. return ISSUE_REJECT_CO;
  3027. }
  3028. //
  3029. // The FileGuid should be zero (i.e. The file wasn't found in the IDTable).
  3030. // Except for the case where this is a MorphGenCo (e.g. a delete) where the
  3031. // IDTable loses the name. It could also be a recovery local CO that
  3032. // came from the INlog. It could also be a parent reanimation CO that was
  3033. // triggerred by a local child update CO.
  3034. //
  3035. CoFromInlog = CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY) ||
  3036. RecoveryCo(ChangeOrder);
  3037. FRS_ASSERT(IS_GUID_ZERO(&CoCmd->FileGuid) || MorphGenCo || CoFromInlog ||
  3038. COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_REANIMATION));
  3039. if (!CO_NEW_FILE(LocationCmd)) {
  3040. if (IS_GUID_ZERO(&CoCmd->FileGuid)) {
  3041. DPRINT1(0, "++ WARN - FileGuid is zero, Location cmd is not a create: %d\n",
  3042. LocationCmd);
  3043. }
  3044. //
  3045. // An update CO could arrive out of order relative to its create
  3046. // if the create got delayed by a sharing violation. We don't
  3047. // want to lose the update so we make it look like a create.
  3048. // This also handles the case of delete change orders generated
  3049. // by name morph conflicts in which a rename arrives for a
  3050. // nonexistent file. We need to force a Name table conflict
  3051. // check below.
  3052. //
  3053. ForceNameConflictCheck = TRUE;
  3054. }
  3055. //
  3056. // We can't return yet. Still need to check Tunnelled OID and
  3057. // the parent dir.
  3058. //
  3059. }
  3060. //
  3061. // If no conflict with an active change order using Cmd.ChangeOrderGuid
  3062. // then recheck with the OID we read from the file that could have
  3063. // been tunnelled.
  3064. //
  3065. if (!RemoteCo &&
  3066. (COE_FLAG_ON(ChangeOrder, COE_FLAG_OID_FROM_FILE)) &&
  3067. !GUIDS_EQUAL(&CoCmd->FileGuid, &ChangeOrder->FileObjectId)) {
  3068. //
  3069. // Get the ChangeOrder lock from a lock table using a hash of the
  3070. // change order FileGuid. This resolves the race idleing the queue
  3071. // here and unidleing it in ChgOrdIssueCleanup().
  3072. //
  3073. GuidLockSlot = ChgOrdGuidLock(&ChangeOrder->FileObjectId);
  3074. ChgOrdAcquireLock(GuidLockSlot);
  3075. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  3076. &ChangeOrder->FileObjectId,
  3077. TRUE,
  3078. &ActiveChangeOrder);
  3079. DropRef = (GStatus == GHT_STATUS_SUCCESS);
  3080. //
  3081. // If the conflict is with an active change order on the same file
  3082. // then set the flag in the active change order to unidle the queue
  3083. // when the active change order completes.
  3084. //
  3085. if (GStatus == GHT_STATUS_SUCCESS) {
  3086. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict Tunnel - File Busy");
  3087. goto CONFLICT;
  3088. }
  3089. if (DropRef) {
  3090. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  3091. ActiveChangeOrder,
  3092. TRUE);
  3093. DropRef = FALSE;
  3094. }
  3095. ChgOrdReleaseLock(GuidLockSlot);
  3096. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  3097. }
  3098. //
  3099. // Check on the condition of the Parent Dirs.
  3100. //
  3101. //
  3102. // If the parent of a localCo is absent then reject the CO. This can happen
  3103. // if the child is renamed to a new dir (MOVEDIR) and then the old parent
  3104. // is deleted. e.g.
  3105. // Variation 1 Variation 2
  3106. //
  3107. // Cre parent1 cre parent1
  3108. // cre parent1\child move pre-existing-child to parent1
  3109. // rename child to another dir rename child to another dir
  3110. // del parent1 del parent1
  3111. //
  3112. // re: Variation 1-
  3113. // When processed the cre parent1 will not be on the disk since the delete
  3114. // has already been executed so the cre parent1 is rejected.
  3115. // When the cre parent1\child is processed there is no IDTable entry
  3116. // for parent1. So reject this Co.
  3117. // When the rename (MOVEDIR) is processed we treat it as a MOVEIN since
  3118. // there is no IDTable entry for the child but now we do have an IDTable
  3119. // entry for the parent. Del Parent1 is processed as a delete on a deleted
  3120. // file and is rejected.
  3121. //
  3122. // Variation 2 is similiar to 1 but by the time we process the move of
  3123. // a pre-existing child to parent1 (MOVEDIR) we see parent1 has been
  3124. // deleted (and there is no IDTable entry for it just like variation 1).
  3125. // In this case, parent1 is the new parent (or dest dir). The next CO
  3126. // then renames the child to some other dir and again there is no IDTable
  3127. // entry for parent1, the source dir in this case.
  3128. //
  3129. // The following sequence shows a case where both parents of a movedir
  3130. // are absent (movedir child to par2).
  3131. // cre par1 ; cre par1\child ; cre par2 ; movedir child to par2 ;
  3132. // cre par3 ; movedir child to par3 ; del par1 ; del par2
  3133. //
  3134. // Rule: If dest parent is absent or deleted then reject the CO BUT if the
  3135. // CO is a delete (or MOVEOUT) then this is the end of the line for
  3136. // this file and there won't be any follow-on CO.
  3137. //
  3138. // Note: If the dest parent is marked deleted in IDTable but the target file
  3139. // is present then there must be another operation coming up that does a
  3140. // MOVEDIR onthe file. We will treat that as a MOVEIN when it comes.
  3141. //
  3142. // Note: if the dest parent delete occured because of a remote CO delete
  3143. // that was processed before this CO then either the target file will have
  3144. // been deleted along with the parent (the NO ACTIVE CHILD case) or some
  3145. // MOVEDIR has placed it under a different parent.
  3146. //
  3147. // Either way reject this CO and let the file be handled by a subsequent CO.
  3148. //
  3149. // We can not reject the local CO if the parent is marked delete deferred.
  3150. // Instead we want this CO to reanimate the parent.
  3151. //
  3152. if (!RemoteCo) {
  3153. DestParentAbsentOrDel = COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_ABS |
  3154. COE_FLAG_IDT_NEW_PARENT_DEL);
  3155. DestParentDelDeferred = COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL_DEF);
  3156. DeleteCo = (LocationCmd == CO_LOCATION_DELETE) ||
  3157. (LocationCmd == CO_LOCATION_MOVEOUT);
  3158. if (DestParentAbsentOrDel && !DestParentDelDeferred && !DeleteCo) {
  3159. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3160. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3161. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - Dest Par Abs or Del");
  3162. return ISSUE_REJECT_CO;
  3163. }
  3164. if (LocationCmd == CO_LOCATION_MOVEDIR) {
  3165. OrigParentAbsentOrDel = COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_ABS |
  3166. COE_FLAG_IDT_ORIG_PARENT_DEL);
  3167. if (OrigParentAbsentOrDel) {
  3168. //
  3169. // If the source parent dir is absent or deleted then let the
  3170. // CO continue as a simple rename.
  3171. //
  3172. // A simple rename does not have a location command and it
  3173. // has a content command with the value USN_REASON_NEW_NAME.
  3174. // The location command needs to be cleared so that StuInstallStage
  3175. // correctly does the rename of the file to the correct location.
  3176. //
  3177. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_LOCATION_CMD);
  3178. SET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command, CO_LOCATION_NUM_CMD);
  3179. LocationCmd = CO_LOCATION_NUM_CMD;
  3180. SET_CO_FLAG(ChangeOrder, CO_FLAG_CONTENT_CMD);
  3181. CoCmd->ContentCmd |= USN_REASON_RENAME_NEW_NAME;
  3182. //
  3183. // If the original parent does not exist because of variation 1
  3184. // above then the FidToGuid translation will leave the
  3185. // oldparentguid zero. So copy the NewParentGuid to the OldParentGuid.
  3186. // Leaving the oldparentguid zero hits asserts related to hash
  3187. // table inserts.
  3188. //
  3189. if (IS_GUID_ZERO(&CoCmd->OldParentGuid)) {
  3190. COPY_GUID(&CoCmd->OldParentGuid, &CoCmd->NewParentGuid);
  3191. }
  3192. CHANGE_ORDER_TRACE(3, ChangeOrder, "Src Abs/Del cvt to simple RENAME");
  3193. }
  3194. }
  3195. }
  3196. // Check for CO Timeout --
  3197. //
  3198. // It is possible for us to get into a state where there is a CO on a child
  3199. // but the parent does not exist on any machine in the replica set. For
  3200. // example,the create CO for the parent may have been held up on the
  3201. // original machine andthe machine was removed form the replica set before
  3202. // sending the CO.
  3203. //
  3204. // Whatever the cause, we are not able to detect this situation. If the
  3205. // parent does exist, it can take an arbitrarily long amount of time to
  3206. // reach us. Since we cannot detect the total absense of the parent, all
  3207. // that we can do is wait a sufficiently long amount of time before giving
  3208. // up all hope of ever seeing it.
  3209. //
  3210. // We give up waiting for the parent once two conditions have been met.
  3211. // 1) We have retried this change order at least certain number of times.
  3212. // 2) A minimum amount of time has passed since we first tried this CO.
  3213. //
  3214. if (EitherParentAbsent && CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY)) {
  3215. PDATA_EXTENSION_RETRY_TIMEOUT CoCmdRetryTimeout;
  3216. CoCmdRetryTimeout = DbsDataExtensionFind(CoCmd->Extension, DataExtend_Retry_Timeout);
  3217. if(CoCmdRetryTimeout != NULL) {
  3218. GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
  3219. // increase retry count
  3220. CoCmdRetryTimeout->Count++;
  3221. //
  3222. // we must update the inlog table because we have
  3223. // modified the RetryTimeout counts.
  3224. //
  3225. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_UPDATE_INLOG);
  3226. // get the time difference in minutes.
  3227. EventTimeDelta = CurrentTime - CoCmdRetryTimeout->FirstTryTime;
  3228. EventTimeDelta = EventTimeDelta / CONVERT_FILETIME_TO_MINUTES;
  3229. // if the count is too high and enough time has passed, trash this CO
  3230. if((CoCmdRetryTimeout->Count > MaxCoRetryTimeoutCount)
  3231. &&(EventTimeDelta > MaxCoRetryTimeoutMinutes)) {
  3232. //
  3233. // Remove the CO entry from the queue and remove
  3234. // the preinstall file and the ID Table entry.
  3235. //
  3236. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3237. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3238. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_PREINSTALL);
  3239. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_IDT_ENTRY);
  3240. return ISSUE_REJECT_CO;
  3241. }
  3242. }
  3243. }
  3244. //
  3245. // Check if the parent DIR has an active CO on it. If it does then
  3246. // we can't issue this change order because the change on the parent
  3247. // could affect the ACL or the RO bit or ...
  3248. // Also check if there is a name conflict if this is a create or a rename.
  3249. //
  3250. if (SelectedParentPresent) {
  3251. FRS_ASSERT(!IS_GUID_ZERO(ChangeOrder->pParentGuid));
  3252. GuidLockSlot = ChgOrdGuidLock(ChangeOrder->pParentGuid);
  3253. ChgOrdAcquireLock(GuidLockSlot);
  3254. //
  3255. // Check for name conflict on a create or a rename target.
  3256. // Aliasing is possible in the hash table since we don't have the
  3257. // full key saved. So check for COs that remove a name since a
  3258. // second insert with the same hash value would cause a conflict.
  3259. //
  3260. // If there is a conflict then set the "queue blocked" flag in
  3261. // the name conflict table entry. The flag will be picked up by
  3262. // the co that decs the entry's reference count to 0.
  3263. //
  3264. // NameConflictTable Entry: An entry in the NameConflictTable
  3265. // contains a reference count of the number of active change
  3266. // orders that hash to that entry and a Flags word that is set
  3267. // to COE_FLAG_VOL_COLIST_BLOCKED if the process queue for this
  3268. // volume is idled while waiting on the active change orders for
  3269. // this entry to retire. The queue is idled when the entry at
  3270. // the head of the queue hashes to this entry and so may
  3271. // have a conflicting name (hence the name, "name conflict table").
  3272. //
  3273. // The NameConflictTable can give false positives. But this is okay
  3274. // because a false positive is rare and will only idle the process
  3275. // queue until the active change order retires. Getting rid
  3276. // of the rare false positives would degrade performance. The
  3277. // false positives that happen when inserting an entry into the
  3278. // NameConflictTable are handled by using the QData field in
  3279. // the QHashEntry as a the reference count.
  3280. //
  3281. // But, you ask, how can there be multiple active cos hashing to this
  3282. // entry if the process queue is idled when a conflict is detected?
  3283. // Easy, I say, because the filename in the co is used to detect
  3284. // collisions while the filename in the idtable is used to reserve the
  3285. // qhash entry. Why? Well, the name morph code handles the case of a
  3286. // co's name colliding with an idtable entry. But that code wouldn't
  3287. // work if active change orders were constantly changing the idtable.
  3288. // So, the NameConflictTable synchronizes the namespace amoung active
  3289. // change orders so that the name morph code can work against a static
  3290. // namespace.
  3291. //
  3292. // If this CO is a MORPH_GEN_FOLLOWER make sure we interlock with the
  3293. // MORPH_GEN_LEADER. The leader is removing the name so it makes an
  3294. // entry in the name conflict table. If the follower is a create it
  3295. // will check against the name with one of the DOES_CO predicates
  3296. // below, but if it is a file reanimation it may just look like an
  3297. // update so always make the interlock check if this CO is a follower.
  3298. //
  3299. if (ForceNameConflictCheck ||
  3300. DOES_CO_CREATE_FILE_NAME(CoCmd) ||
  3301. DOES_CO_REMOVE_FILE_NAME(CoCmd) ||
  3302. DOES_CO_DO_SIMPLE_RENAME(CoCmd) ||
  3303. COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER)) {
  3304. ChgOrdCalcHashGuidAndName(&ChangeOrder->UFileName,
  3305. ChangeOrder->pParentGuid,
  3306. &QuadHashValue);
  3307. //
  3308. // There should be no ref that needs to be dropped here.
  3309. //
  3310. FRS_ASSERT(!DropRef);
  3311. QHashAcquireLock(Replica->NameConflictTable);
  3312. //
  3313. // MOVERS: check if we are looking at correct replica if this is a movers
  3314. //
  3315. QHashEntry = QHashLookupLock(Replica->NameConflictTable,
  3316. &QuadHashValue);
  3317. if (QHashEntry != NULL) {
  3318. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Name Table");
  3319. GuidToStr(ChangeOrder->pParentGuid, GuidStr);
  3320. DPRINT4(4, "++ NameConflictTable hit on file %ws, ParentGuid %s, "
  3321. "Key %08x %08x, RefCount %08x %08x\n",
  3322. ChangeOrder->UFileName.Buffer, GuidStr,
  3323. PRINTQUAD(QuadHashValue), PRINTQUAD(QHashEntry->QData));
  3324. FRS_PRINT_TYPE(4, ChangeOrder);
  3325. //
  3326. // This flag will be picked up by the change order that
  3327. // decs this entry's count to 0.
  3328. //
  3329. QHashEntry->Flags |= (ULONG_PTR)COE_FLAG_VOL_COLIST_BLOCKED;
  3330. QHashReleaseLock(Replica->NameConflictTable);
  3331. goto NAME_CONFLICT;
  3332. }
  3333. QHashReleaseLock(Replica->NameConflictTable);
  3334. }
  3335. //
  3336. // Check for active change order on the parent Dir.
  3337. //
  3338. // ** NOTE ** Be careful with ActiveChangeOrder, we drop the ref on it later.
  3339. //
  3340. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  3341. ChangeOrder->pParentGuid,
  3342. TRUE,
  3343. &ActiveChangeOrder);
  3344. if (GStatus == GHT_STATUS_SUCCESS) {
  3345. DropRef = TRUE;
  3346. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Parent Busy");
  3347. goto CONFLICT;
  3348. }
  3349. FRS_ASSERT(!DropRef);
  3350. ChgOrdReleaseLock(GuidLockSlot);
  3351. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  3352. } else {
  3353. DPRINT(0, "++ ERROR - Neither parent is present.\n");
  3354. FRS_PRINT_TYPE(4, ChangeOrder);
  3355. }
  3356. //
  3357. // Check for a conflict with some operation on the Original parent FID if
  3358. // this is a MOVEOUT, MOVERS or a MOVEDIR.
  3359. //
  3360. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  3361. if (CO_MOVE_OUT_RS_OR_DIR(LocationCmd)) {
  3362. if (OrigParentPresent) {
  3363. FRS_ASSERT(!IS_GUID_ZERO(&CoCmd->OldParentGuid));
  3364. GuidLockSlot = ChgOrdGuidLock(&CoCmd->OldParentGuid);
  3365. ChgOrdAcquireLock(GuidLockSlot);
  3366. //
  3367. // ** NOTE ** Be careful with ActiveChangeOrder, we drop the ref on it later.
  3368. //
  3369. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  3370. &CoCmd->OldParentGuid,
  3371. TRUE,
  3372. &ActiveChangeOrder);
  3373. if (GStatus == GHT_STATUS_SUCCESS) {
  3374. DropRef = TRUE;
  3375. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Orig Parent Busy");
  3376. goto CONFLICT;
  3377. }
  3378. FRS_ASSERT(!DropRef);
  3379. ChgOrdReleaseLock(GuidLockSlot);
  3380. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  3381. } else {
  3382. DPRINT(0, "++ ERROR - Orig Parent not present.\n");
  3383. FRS_PRINT_TYPE(4, ChangeOrder);
  3384. }
  3385. }
  3386. //
  3387. // Check if this is an operation on a directory with change orders still
  3388. // active on any children. As an example there could be an active CO that
  3389. // deletes a file and this change order wants to delete the parent directory.
  3390. //
  3391. // But.. A new dir can't have any change orders active beneath it.
  3392. // Neither can a delete co generated to produce a tombstone.
  3393. //
  3394. if (!TargetPresent ||
  3395. (!RemoteCo && (LocationCmd == CO_LOCATION_DELETE) &&
  3396. CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN))) {
  3397. return ISSUE_OK;
  3398. }
  3399. QHashAcquireLock(pVme->ActiveChildren);
  3400. QHashEntry = QHashLookupLock(pVme->ActiveChildren,
  3401. &ChangeOrder->Cmd.FileGuid);
  3402. //
  3403. // If the conflict was on a dir that has change orders outstanding on one
  3404. // or more children then set the flag bit to unblock the queue when the
  3405. // count goes to zero.
  3406. //
  3407. if (QHashEntry != NULL) {
  3408. CHAR FileGuidString[GUID_CHAR_LEN];
  3409. GuidToStr(&ChangeOrder->Cmd.FileGuid, FileGuidString);
  3410. DPRINT2(4, "++ GAC: Interlock - Active children (%d) under GUID %s\n",
  3411. QHashEntry->QData>>1, FileGuidString);
  3412. if (!CoCmdIsDirectory(CoCmd)) {
  3413. DPRINT(0, "++ ERROR - Non Dir entry in pVme->ActiveChildren hash table.\n");
  3414. FRS_PRINT_TYPE(0, ChangeOrder);
  3415. }
  3416. //
  3417. // Set the flag and idle the queue and drop the table lock.
  3418. //
  3419. QHashEntry->QData |= 1;
  3420. FrsRtlIdleQueueLock(CoProcessQueue);
  3421. QHashReleaseLock(pVme->ActiveChildren);
  3422. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Child Busy");
  3423. return ISSUE_CONFLICT;
  3424. }
  3425. QHashReleaseLock(pVme->ActiveChildren);
  3426. //
  3427. // If there are any remote change orders pending for retry of the install then
  3428. // check to see if this change order guid matches. If it does then we
  3429. // can retire this change order since it is a duplicate and we already
  3430. // have the staging file.
  3431. //
  3432. if (0 && RemoteCo &&
  3433. !CoCmdIsDirectory(CoCmd) &&
  3434. (ChangeOrder->NewReplica->InLogRetryCount > 0)) {
  3435. //
  3436. // PERF: add code to lookup CO by GUID and check for match.
  3437. //
  3438. // return with a change order reject error code
  3439. //
  3440. // Remove the duplicate entry from the queue.
  3441. //
  3442. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3443. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3444. return ISSUE_REJECT_CO;
  3445. }
  3446. return ISSUE_OK;
  3447. //
  3448. // Issue conflict on the file or on the parent dir. Set flag in Active
  3449. // Change order to unblock the queue on retire and then drop the change
  3450. // order GUID lock.
  3451. //
  3452. CONFLICT:
  3453. SET_COE_FLAG(ActiveChangeOrder, COE_FLAG_VOL_COLIST_BLOCKED);
  3454. DPRINT1(4, "++ GAC Setting COE_FLAG_VOL_COLIST_BLOCKED, block on: %08x %08x\n",
  3455. PRINTQUAD(ActiveChangeOrder->FileReferenceNumber));
  3456. //
  3457. // Conflict detected in name conflict table
  3458. //
  3459. NAME_CONFLICT:
  3460. if (DropRef) {
  3461. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  3462. ActiveChangeOrder,
  3463. TRUE);
  3464. }
  3465. //
  3466. // Idle the queue and drop the change order lock.
  3467. //
  3468. FrsRtlIdleQueueLock(CoProcessQueue);
  3469. FRS_ASSERT(GuidLockSlot != UNDEFINED_LOCK_SLOT);
  3470. ChgOrdReleaseLock(GuidLockSlot);
  3471. return ISSUE_CONFLICT;
  3472. }
  3473. #define HI_AIB_T 0x00000001 // AIBCO on Target
  3474. #define HI_AIB_D 0x00000002 // AIBCO plus Dup Rmt Co check
  3475. #define HI_EPA 0x00000004 // Either parent absent
  3476. #define HI_NC 0x00000008 // name Conflict check
  3477. #define HI_ASP 0x00000010 // AIBCO on Selected Parent
  3478. #define HI_AOP 0x00000020 // AIBCO on Original Parent
  3479. #define HI_AC 0x00000040 // Check for active child under dir
  3480. #define HI_ISSUE_OK 0x10000000
  3481. #define HI_ISSUE_LATER 0x20000000 // return issue-retry-later status.
  3482. #define HI_ASSERT 0x40000000
  3483. #define HI_INVALID 0x80000000
  3484. // CoType -NewFile - (Create, Update before Cre, MovIn)
  3485. // DelFile - Delete file, MovOut, MovRS
  3486. // UpdFile - Update existing file, could be MovDir)
  3487. //SelParValid - Is selected parent (either NewPar or OrigPar) have a GUID or FID?
  3488. ULONG HoldIssueDecodeTable[32] = {
  3489. // AIBCO-Targ NameConf AIBCO-OldPar IssueOK // Rmt/ File/ CO Sel
  3490. // DupRmtCo? if movxxx // Lcl Dir Type Par
  3491. // EitherParAbs AIBCO on Active // Valid? -- Comment
  3492. // IsRetryLater Selected Par Child
  3493. HI_NC |HI_ASP |HI_ISSUE_OK, // Lcl File New y
  3494. HI_ASSERT, // Lcl File New n -- For local Cre, how come no parent?
  3495. HI_AIB_T |HI_NC |HI_ASP |HI_AOP |HI_ISSUE_OK, // Lcl File Del y -- If no target then we missed create. For local del, how come?
  3496. HI_ASSERT, // Lcl File Del n -- If either Parent missing then we missed ParCreate, For local Del, how come no parent?
  3497. HI_AIB_T |HI_NC |HI_ASP |HI_AOP |HI_ISSUE_OK, // Lcl File Upd y
  3498. HI_ASSERT, // Lcl File Upd n -- For local Upd, how come no parent?
  3499. HI_INVALID, // x
  3500. HI_INVALID, // x
  3501. HI_NC |HI_ASP |HI_ISSUE_OK, // Lcl Dir New y
  3502. HI_ASSERT, // Lcl Dir New n -- For local Cre, how come no parent?
  3503. HI_AIB_T |HI_NC |HI_ASP |HI_AOP |HI_AC |HI_ISSUE_OK, // Lcl Dir Del y -- If no target then we missed create. For local del, how come?
  3504. HI_ASSERT, // Lcl Dir Del n -- If either ParGuid is zero then we missed ParCreate, For local Del, how come no parent?
  3505. HI_AIB_T |HI_NC |HI_ASP |HI_AOP |HI_AC |HI_ISSUE_OK, // Lcl Dir Upd y
  3506. HI_ASSERT, // Lcl Dir Upd n -- For local Upd, how come no parent?
  3507. HI_INVALID, // x
  3508. HI_INVALID, // x
  3509. HI_AIB_D | HI_EPA |HI_NC |HI_ASP |HI_ISSUE_OK, // Rmt File New y -- Why retry later? Cre file in preinstall.
  3510. HI_ISSUE_LATER, // Rmt File New n -- IsRetryLater No parent means file lives in pre-install dir. Do This?
  3511. HI_AIB_D | HI_EPA |HI_NC |HI_ASP |HI_AOP |HI_ISSUE_OK, // Rmt File Del y -- If Target IDT Absent then we missed create. Do JustTombStone?
  3512. HI_ISSUE_LATER, // Rmt File Del n -- IsRetryLater If either Par IDT Absent then we missed ParCreate. Do JustTombStone?
  3513. HI_AIB_D | HI_EPA |HI_NC |HI_ASP |HI_AOP |HI_ISSUE_OK, // Rmt File Upd y
  3514. HI_ISSUE_LATER, // Rmt File Upd n -- IsRetryLater Why retry later? Update file in preinstall.
  3515. HI_INVALID, // x
  3516. HI_INVALID, // x
  3517. HI_AIB_D | HI_EPA |HI_NC |HI_ASP |HI_ISSUE_OK, // Rmt Dir New y -- Why retry later? Cre dir in preinstall.
  3518. HI_ISSUE_LATER, // Rmt Dir New n -- IsRetryLater No parent means dir lives in pre-install dir. Do This?
  3519. HI_AIB_D | HI_EPA |HI_NC |HI_ASP |HI_AOP |HI_AC |HI_ISSUE_OK, // Rmt Dir Del y -- If Target IDT Absent then we missed create. Do JustTombStone?
  3520. HI_ISSUE_LATER, // Rmt Dir Del n -- IsRetryLater If either Par IDT Absent then we missed ParCreate. Do JustTombStone?
  3521. HI_AIB_D | HI_EPA |HI_NC |HI_ASP |HI_AOP |HI_AC |HI_ISSUE_OK, // Rmt Dir Upd y -- Why retry later? Cre dir in preinstall.
  3522. HI_ISSUE_LATER, // Rmt Dir Upd n -- IsRetryLater No parent means dir lives in pre-install dir. Do This?
  3523. HI_INVALID, // x
  3524. HI_INVALID // x
  3525. };
  3526. ULONG
  3527. ChgOrdHoldIssue2(
  3528. IN PREPLICA Replica,
  3529. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  3530. IN PFRS_QUEUE CoProcessQueue
  3531. )
  3532. /*++
  3533. Routine Description:
  3534. This routine ensures that a new change order does on conflict with a
  3535. change order already in progress. If it does then we set a flag in the
  3536. active state indicating the change order process queue is blocked and
  3537. and return status. The process queue is blocked while holding the change
  3538. order lock (using the FID or parent FID) to synchronize with the active
  3539. change order retire operation.
  3540. There are four file dependency cases to consider plus the case of duplicates.
  3541. 1. File update/create on file X must preceed any subsequent file
  3542. update/delete on file X. (X could be a file or a Dir)
  3543. 2. Parent dir create/update (e.g. ACL, RO) must preceed any
  3544. subsequent child file or dir operations.
  3545. 3. Child File or Dir operations must preceed any subsequent parent dir
  3546. update/delete.
  3547. 4. Any rename or delete operation that releases a filename must preceed
  3548. any rename or create operation that will reuse the filename.
  3549. 5. Duplicate change orders can arrive from multiple inbound partners.
  3550. If they arrive at the same time then we could have one in progress while
  3551. the second tries to issue. We can't immediately dampen the second CO because
  3552. the first may fail (E.G. can't complete the fetch) so we mark the duplicate
  3553. CO for retry and stick it in the Inbound log incase the currently active
  3554. CO fails.
  3555. Three hash tables are used to keep the active state:
  3556. - The ActiveInboundChangeOrderTable keeps an entry for the change order
  3557. when it is issued. The CO stays in the table until it retires or
  3558. a retry later is requested. This table is indexed by the FileGuid
  3559. so consecutive changes to the same file are performed in order.
  3560. - The ActiveChildren hash table tracks the Parent File IDs of all active
  3561. change orders. Each time a change order issues, the entry for its
  3562. parent is found (or created) and incremented. When the count goes
  3563. to zero the entry is removed. If a change order was waiting for the
  3564. parent count to go to zero it is unblocked.
  3565. - The Name Conflict table is indexed by a hash of the file name and
  3566. the parent directory object ID. If a conflict occurs with an
  3567. outstanding opertion the issuing change order must block until
  3568. the current change order completes. A deleted file and the source
  3569. file of a rename reserve an entry in the table so they can block issue
  3570. of a subsequent create or a rename with a target name that matches.
  3571. The reverse check is not needed since that is handled by the
  3572. ActiveInboundChangeOrderTable.
  3573. Assumptions:
  3574. 1. There are no direct interdependencies between Child File or Dir operations
  3575. and ancestor directories beyond the immediate parent.
  3576. 2. The caller has acquired the FrsVolumeLayerCOList lock.
  3577. Arguments:
  3578. Replica - The Replica set for the CO (also get us to the volume monitor
  3579. entry associated with the replica set).
  3580. ChangeOrder -- The new change order to check ordering conflicts.
  3581. CoProcessQueue -- The process queue this change order is on.
  3582. If the change order can't issue we idle the queue.
  3583. The caller has acquired the queue lock.
  3584. Return Value:
  3585. ISSUE_OK means no conflict so the change order can be issued.
  3586. ISSUE_DUPLICATE_REMOTE_CO means this is a duplicate change order.
  3587. ISSUE_CONFLICT means a dependency condition exists so this change order
  3588. can't issue.
  3589. ISSUE_RETRY_LATER means that the parent dir is not present so this CO
  3590. will have to be retried later.
  3591. --*/
  3592. {
  3593. #undef DEBSUB
  3594. #define DEBSUB "ChgOrdHoldIssue2:"
  3595. ULONG GStatus;
  3596. PQHASH_ENTRY QHashEntry;
  3597. ULONG LocationCmd;
  3598. ULONG Decodex, DecodeMask;
  3599. BOOL RemoteCo;
  3600. BOOL DropRef = FALSE;
  3601. BOOL ForceNameConflictCheck = FALSE;
  3602. BOOL OrigParentPresent;
  3603. BOOL SelectedParentPresent, EitherParentAbsent;
  3604. BOOL TargetPresent;
  3605. PCHANGE_ORDER_ENTRY ActiveChangeOrder;
  3606. PCHANGE_ORDER_COMMAND CoCmd;
  3607. PVOLUME_MONITOR_ENTRY pVme;
  3608. PCXTION Cxtion;
  3609. ULONGLONG QuadHashValue;
  3610. ULONG GuidLockSlot = UNDEFINED_LOCK_SLOT;
  3611. CHAR GuidStr[GUID_CHAR_LEN];
  3612. #undef FlagChk
  3613. #define FlagChk(_flag_) BooleanFlagOn(DecodeMask, _flag_)
  3614. pVme = Replica->pVme;
  3615. CoCmd = &ChangeOrder->Cmd;
  3616. RemoteCo = !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  3617. TargetPresent = !COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_TARGET_ABS);
  3618. OrigParentPresent = !COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_ABS);
  3619. EitherParentAbsent = COE_FLAG_ON(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_ABS |
  3620. COE_FLAG_IDT_NEW_PARENT_ABS);
  3621. SelectedParentPresent = !COE_FLAG_ON(ChangeOrder,
  3622. (ChangeOrder->NewReplica != NULL) ?
  3623. COE_FLAG_IDT_NEW_PARENT_ABS :
  3624. COE_FLAG_IDT_ORIG_PARENT_ABS);
  3625. //
  3626. // Construct the dispatch index into the HoldIssueDecodeTable.
  3627. //
  3628. // 10 08 06 01
  3629. // Rmt/ File/ CO Sel
  3630. // Lcl Dir Type Par
  3631. // Valid?
  3632. //
  3633. Decodex = 0;
  3634. if (!RemoteCo) {
  3635. Decodex |= 0x10;
  3636. }
  3637. if (CoCmdIsDirectory(CoCmd)) {
  3638. Decodex |= 0x08;
  3639. }
  3640. if (!TargetPresent) {
  3641. // New Co
  3642. Decodex |= 0x00;
  3643. } else
  3644. if (DOES_CO_DELETE_FILE_NAME(CoCmd)) {
  3645. // Del Co (or MoveOut)
  3646. Decodex |= 0x02;
  3647. } else
  3648. if (TargetPresent) {
  3649. // Update existing file Co
  3650. Decodex |= 0x04;
  3651. } else
  3652. // Invalid Co
  3653. { Decodex |= 0x06; };
  3654. if (SelectedParentPresent) {
  3655. Decodex |= 0x01;
  3656. }
  3657. DecodeMask = HoldIssueDecodeTable[Decodex];
  3658. CHANGE_ORDER_TRACEX(3, ChangeOrder, "DecodeIndex=", Decodex);
  3659. CHANGE_ORDER_TRACEX(3, ChangeOrder, "DecodeMask= ", DecodeMask);
  3660. FRS_ASSERT(!FlagChk(HI_INVALID));
  3661. //
  3662. // Synchronize with the Replica command server thread that may be changing
  3663. // the cxtion's state, joinguid, or checking Cxtion->CoProcessQueue.
  3664. //
  3665. LOCK_CXTION_TABLE(Replica);
  3666. Cxtion = ChangeOrder->Cxtion;
  3667. if (Cxtion == NULL) {
  3668. DPRINT(0, "++ ERROR - No connection struct for inbound CO\n");
  3669. UNLOCK_CXTION_TABLE(Replica);
  3670. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3671. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3672. return ISSUE_REJECT_CO;
  3673. }
  3674. //
  3675. // Every remote change order was received from a cxtion. The change
  3676. // order must be "sent" to that cxtion again for processing. Find it.
  3677. // If it no longer exists then discard the change order.
  3678. //
  3679. // The cxtion can be in any of several states that will end up sending
  3680. // this change order through the retry path. Those states are checked
  3681. // later since they could change between now and issueing the change
  3682. // order to the replica command server.
  3683. //
  3684. if (RemoteCo) {
  3685. //
  3686. // Check if this is a "Recovery CO" from an inbound partner.
  3687. // If it is and the partner connection is not yet joined then we
  3688. // hold issue until the join completes.
  3689. //
  3690. if (RecoveryCo(ChangeOrder)) {
  3691. //
  3692. // If the connection is trying to join then hold issue on this CO
  3693. // until it either succeeds or fails.
  3694. //
  3695. if (CxtionStateIs(Cxtion, CxtionStateStarting) ||
  3696. CxtionStateIs(Cxtion, CxtionStateScanning) ||
  3697. CxtionStateIs(Cxtion, CxtionStateWaitJoin) ||
  3698. CxtionStateIs(Cxtion, CxtionStateSendJoin)) {
  3699. //
  3700. // Save the queue address we are blocked on.
  3701. // When JOIN finally succeeds or fails we get unblocked with
  3702. // the appropriate connection state change by a replica
  3703. // command server thread.
  3704. //
  3705. Cxtion->CoProcessQueue = CoProcessQueue;
  3706. GuidToStr(&CoCmd->ChangeOrderGuid, GuidStr);
  3707. DPRINT3(3, "++ CO hold issue on pending JOIN, vsn %08x %08x, CoGuid: %s for %ws\n",
  3708. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  3709. //
  3710. // Idle the queue and drop the connection table lock.
  3711. //
  3712. FrsRtlIdleQueueLock(CoProcessQueue);
  3713. UNLOCK_CXTION_TABLE(Replica);
  3714. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Cxtion Join");
  3715. return ISSUE_CONFLICT;
  3716. }
  3717. }
  3718. }
  3719. UNLOCK_CXTION_TABLE(Replica);
  3720. //
  3721. // If this is a remote CO then it came with a Guid. If this is a local CO
  3722. // then ChgOrdTranslateGuidFid() told us if there is a record in the IDTable.
  3723. //
  3724. // If the Guid is zero then this is a new file from a local change order
  3725. // that was not in the IDTable. So it can't be in the
  3726. // ActiveInboundChangeOrderTable.
  3727. //
  3728. if (FlagChk(HI_AIB_T | HI_AIB_D)) {
  3729. GStatus = GHT_STATUS_NOT_FOUND;
  3730. if (RemoteCo || TargetPresent) {
  3731. FRS_ASSERT(!IS_GUID_ZERO(&CoCmd->FileGuid));
  3732. //
  3733. // Get the ChangeOrder lock from a lock table using a hash of the
  3734. // change order FileGuid. This resolves the race idleing the queue
  3735. // here and unidleing it in ChgOrdIssueCleanup().
  3736. //
  3737. GuidLockSlot = ChgOrdGuidLock(&CoCmd->FileGuid);
  3738. ChgOrdAcquireLock(GuidLockSlot);
  3739. //
  3740. // Check if the file has an active CO on it. If it does then
  3741. // we can't issue this CO because the active CO could be changing
  3742. // the ACL or the RO bit or it could just be a prior update to the
  3743. // file so ordering has to be maintained.
  3744. //
  3745. // ** NOTE ** Be careful with ActiveChangeOrder, we drop the ref on it later.
  3746. //
  3747. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  3748. &CoCmd->FileGuid,
  3749. TRUE,
  3750. &ActiveChangeOrder);
  3751. DropRef = (GStatus == GHT_STATUS_SUCCESS);
  3752. //
  3753. // Check if we have seen this change order before. This could happen if
  3754. // we got the same change order from two different inbound partners. We
  3755. // can't dampen the second one until the first one completes sucessfully.
  3756. // If for some reason the first one fails during fetch or install (say
  3757. // the install file is corrupt or a sharing violation on the target
  3758. // causes a retry) then we may need to get the change from the other
  3759. // partner. When the first change order completes successfully all the
  3760. // duplicates are rejected by reconcile when they are retried later.
  3761. // At that point we can Ack the inbound partner.
  3762. //
  3763. if (FlagChk(HI_AIB_D)) {
  3764. if ((GStatus == GHT_STATUS_SUCCESS) &&
  3765. GUIDS_EQUAL(&CoCmd->ChangeOrderGuid,
  3766. &ActiveChangeOrder->Cmd.ChangeOrderGuid)) {
  3767. //DPRINT(5, "++ Hit a case of duplicate change orders.\n");
  3768. //DPRINT(5, "++ Incoming CO\n");
  3769. //FRS_PRINT_TYPE(5, ChangeOrder);
  3770. //DPRINT(5, "++ Active CO\n");
  3771. //FRS_PRINT_TYPE(5, ActiveChangeOrder);
  3772. //
  3773. // Remove the duplicate entry from the queue, drop the locks and
  3774. // return with duplicate remote co status.
  3775. //
  3776. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3777. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3778. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  3779. ActiveChangeOrder,
  3780. TRUE);
  3781. DropRef = FALSE;
  3782. //FrsRtlIdleQueueLock(CoProcessQueue);
  3783. ChgOrdReleaseLock(GuidLockSlot);
  3784. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  3785. return ISSUE_DUPLICATE_REMOTE_CO;
  3786. }
  3787. }
  3788. //
  3789. // If the conflict is with an active change order on the same file
  3790. // then set the flag in the active change order to unidle the queue
  3791. // when the active change order completes.
  3792. //
  3793. if (GStatus == GHT_STATUS_SUCCESS) {
  3794. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - File Busy");
  3795. goto CONFLICT;
  3796. }
  3797. if (DropRef) {
  3798. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  3799. ActiveChangeOrder,
  3800. TRUE);
  3801. DropRef = FALSE;
  3802. }
  3803. ChgOrdReleaseLock(GuidLockSlot);
  3804. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  3805. }
  3806. } // end of HI_AIB_T | HI_AIB_D
  3807. #if 0
  3808. else {
  3809. // Do we still need this?
  3810. //
  3811. // The FileGuid is zero (i.e. The file wasn't found in the IDTable).
  3812. // Make sure the location command is either a create or a movein.
  3813. //
  3814. FRS_ASSERT(IS_GUID_ZERO(&CoCmd->FileGuid));
  3815. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  3816. if (!CO_NEW_FILE(LocationCmd)) {
  3817. DPRINT1(0, "++ WARN - GUID is zero, Location cmd is not a create: %d\n",
  3818. LocationCmd);
  3819. //
  3820. // An update CO could arrive out of order relative to its create
  3821. // if the create got delayed by a sharing violation. We don't
  3822. // want to lose the update so we make it look like a create.
  3823. // This also handles the case of delte change orders generated
  3824. // by name morph conflicts in which a rename arrives for a
  3825. // nonexistent file. We need to force a Name table conflict
  3826. // check below.
  3827. //
  3828. ForceNameConflictCheck = TRUE;
  3829. }
  3830. //
  3831. // We can't return yet. Still need to check the parent dir.
  3832. //
  3833. }
  3834. #endif
  3835. if (FlagChk(HI_EPA) && EitherParentAbsent) {
  3836. //
  3837. // If a parent DIR is missing (either original or new) then try later.
  3838. // Note: The parent could be deleted but in that case we will reanimate it
  3839. // if the CO is accepted.
  3840. //
  3841. FRS_ASSERT((ChangeOrder->OriginalParentFid == ZERO_FID) ||
  3842. (ChangeOrder->NewParentFid == ZERO_FID));
  3843. //
  3844. // Remove the CO entry from the queue.
  3845. //
  3846. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  3847. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  3848. //
  3849. // Send this CO to retry to wait for the parent.
  3850. //
  3851. // we could still build the file/dir in the preinstall dir
  3852. return ISSUE_RETRY_LATER;
  3853. }
  3854. // need to check dep on old parent dir in the case of a MOVE DIR
  3855. if (FlagChk(HI_NC)) {
  3856. //
  3857. // Check if the parent DIR has an active CO on it. If it does then
  3858. // we can't issue this change order because the change on the parent
  3859. // could affect the ACL or the RO bit or ...
  3860. // Also check if there is a name conflict if this is a create or a rename.
  3861. //
  3862. FRS_ASSERT(SelectedParentPresent);
  3863. FRS_ASSERT(!IS_GUID_ZERO(ChangeOrder->pParentGuid));
  3864. FRS_ASSERT(GuidLockSlot == UNDEFINED_LOCK_SLOT);
  3865. GuidLockSlot = ChgOrdGuidLock(ChangeOrder->pParentGuid);
  3866. ChgOrdAcquireLock(GuidLockSlot);
  3867. //
  3868. // Check for name conflict on a create or a rename target.
  3869. // Aliasing is possible in the hash table since we don't have the
  3870. // full key saved. So check for COs that remove a name since a
  3871. // second insert with the same hash value would cause a conflict.
  3872. //
  3873. // If there is a conflict then set the "queue blocked" flag in
  3874. // the name conflict table entry. The flag will be picked up by
  3875. // the co that decs the entry's reference count to 0.
  3876. //
  3877. // NameConflictTable Entry: An entry in the NameConflictTable
  3878. // contains a reference count of the number of active change
  3879. // orders that hash to that entry and a Flags word that is set
  3880. // to COE_FLAG_VOL_COLIST_BLOCKED if the process queue for this
  3881. // volume is idled while waiting on the active change orders for
  3882. // this entry to retire. The queue is idled when the entry at
  3883. // the head of the queue hashes to this entry and so may
  3884. // have a conflicting name (hence the name, "name conflict table").
  3885. //
  3886. // The NameConflictTable can give false positives. But this is okay
  3887. // because a false positive is rare and will only idle the process
  3888. // queue until the active change order retires. Getting rid
  3889. // of the rare false positives would degrade performance. The
  3890. // false positives that happen when inserting an entry into the
  3891. // NameConflictTable are handled by using the QData field in
  3892. // the QHashEntry as a the reference count.
  3893. //
  3894. // But, you ask, how can there be multiple active cos hashing to this
  3895. // entry if the process queue is idled when a conflict is detected?
  3896. // Easy, I say, because the filename in the co is used to detect
  3897. // collisions while the filename in the idtable is used to reserve the
  3898. // qhash entry. Why? Well, the name morph code handles the case of a
  3899. // co's name colliding with an idtable entry. But that code wouldn't
  3900. // work if active change orders were constantly changing the idtable.
  3901. // So, the NameConflictTable synchronizes the namespace amoung active
  3902. // change orders so that the name morph code can work against a static
  3903. // namespace.
  3904. //
  3905. // If this CO is a MORPH_GEN_FOLLOWER make sure we interlock with the
  3906. // MORPH_GEN_LEADER. The leader is removing the name so it makes an
  3907. // entry in the name conflict table. If the follower is a create it
  3908. // will check against the name with one of the DOES_CO predicates
  3909. // below, but if it is a file reanimation it may just look like an
  3910. // update so always make the interlock check if this CO is a follower.
  3911. //
  3912. if (ForceNameConflictCheck ||
  3913. DOES_CO_CREATE_FILE_NAME(CoCmd) ||
  3914. DOES_CO_REMOVE_FILE_NAME(CoCmd) ||
  3915. DOES_CO_DO_SIMPLE_RENAME(CoCmd) ||
  3916. COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER)) {
  3917. ChgOrdCalcHashGuidAndName(&ChangeOrder->UFileName,
  3918. ChangeOrder->pParentGuid,
  3919. &QuadHashValue);
  3920. //
  3921. // There should be no ref that needs to be dropped here.
  3922. //
  3923. FRS_ASSERT(!DropRef);
  3924. QHashAcquireLock(Replica->NameConflictTable);
  3925. // MOVERS: check if we are looking at correct replica if this is a movers")
  3926. QHashEntry = QHashLookupLock(Replica->NameConflictTable,
  3927. &QuadHashValue);
  3928. if (QHashEntry != NULL) {
  3929. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Name Table");
  3930. GuidToStr(ChangeOrder->pParentGuid, GuidStr);
  3931. DPRINT4(4, "++ NameConflictTable hit on file %ws, ParentGuid %s, "
  3932. "Key %08x %08x, RefCount %08x %08x\n",
  3933. ChangeOrder->UFileName.Buffer, GuidStr,
  3934. PRINTQUAD(QuadHashValue), PRINTQUAD(QHashEntry->QData));
  3935. FRS_PRINT_TYPE(4, ChangeOrder);
  3936. //
  3937. // This flag will be picked up by the change order that
  3938. // decs this entry's count to 0.
  3939. //
  3940. QHashEntry->Flags |= (ULONG_PTR)COE_FLAG_VOL_COLIST_BLOCKED;
  3941. QHashReleaseLock(Replica->NameConflictTable);
  3942. goto NAME_CONFLICT;
  3943. }
  3944. QHashReleaseLock(Replica->NameConflictTable);
  3945. }
  3946. } // End of HI_NC
  3947. if (FlagChk(HI_ASP)) {
  3948. if (GuidLockSlot == UNDEFINED_LOCK_SLOT) {
  3949. GuidLockSlot = ChgOrdGuidLock(ChangeOrder->pParentGuid);
  3950. ChgOrdAcquireLock(GuidLockSlot);
  3951. }
  3952. //
  3953. // Check for active change order on the parent Dir.
  3954. //
  3955. // ** NOTE ** Be careful with ActiveChangeOrder, we drop the ref on it later.
  3956. //
  3957. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  3958. ChangeOrder->pParentGuid,
  3959. TRUE,
  3960. &ActiveChangeOrder);
  3961. if (GStatus == GHT_STATUS_SUCCESS) {
  3962. DropRef = TRUE;
  3963. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Parent Busy");
  3964. goto CONFLICT;
  3965. }
  3966. FRS_ASSERT(!DropRef);
  3967. ChgOrdReleaseLock(GuidLockSlot);
  3968. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  3969. } // end of HI_ASP
  3970. #if 0
  3971. // still need this?
  3972. } else {
  3973. DPRINT(0, "++ ERROR - ParentGuid is 0. Need to check if this op is on root\n");
  3974. FRS_PRINT_TYPE(0, ChangeOrder);
  3975. // add check for the FID or OID being the root dir of the tree
  3976. }
  3977. #endif
  3978. if (FlagChk(HI_AOP)) {
  3979. //
  3980. // Check for a conflict with some operation on the Original parent FID if
  3981. // this is a MOVEOUT, MOVERS or a MOVEDIR.
  3982. //
  3983. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  3984. if (CO_MOVE_OUT_RS_OR_DIR(LocationCmd)) {
  3985. FRS_ASSERT(OrigParentPresent);
  3986. FRS_ASSERT(!IS_GUID_ZERO(&CoCmd->OldParentGuid));
  3987. FRS_ASSERT(GuidLockSlot == UNDEFINED_LOCK_SLOT);
  3988. GuidLockSlot = ChgOrdGuidLock(&CoCmd->OldParentGuid);
  3989. ChgOrdAcquireLock(GuidLockSlot);
  3990. //
  3991. // ** NOTE ** Be careful with ActiveChangeOrder, we drop the
  3992. // ref on it later.
  3993. //
  3994. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  3995. &CoCmd->OldParentGuid,
  3996. TRUE,
  3997. &ActiveChangeOrder);
  3998. if (GStatus == GHT_STATUS_SUCCESS) {
  3999. DropRef = TRUE;
  4000. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Orig Parent Busy");
  4001. goto CONFLICT;
  4002. }
  4003. FRS_ASSERT(!DropRef);
  4004. ChgOrdReleaseLock(GuidLockSlot);
  4005. GuidLockSlot = UNDEFINED_LOCK_SLOT;
  4006. }
  4007. } // End of HI_AOP
  4008. #if 0
  4009. // Remove ???
  4010. //
  4011. // Check if this is an operation on a directory with change orders still
  4012. // active on any children. As an example there could be an active CO that
  4013. // deletes a file and this change order wants to delete the parent directory.
  4014. //
  4015. // But.. A new dir can't have any change orders active beneath it.
  4016. // Neither can a delete co generated to produce a tombstone.
  4017. //
  4018. if (!TargetPresent ||
  4019. (!RemoteCo && (LocationCmd == CO_LOCATION_DELETE) &&
  4020. CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN))) {
  4021. return ISSUE_OK;
  4022. }
  4023. #endif
  4024. if (FlagChk(HI_AC)) {
  4025. QHashAcquireLock(pVme->ActiveChildren);
  4026. QHashEntry = QHashLookupLock(pVme->ActiveChildren,
  4027. &ChangeOrder->Cmd.FileGuid);
  4028. //
  4029. // If the conflict was on a dir that has change orders outstanding on one
  4030. // or more children then set the flag bit to unblock the queue when the
  4031. // count goes to zero.
  4032. //
  4033. if (QHashEntry != NULL) {
  4034. CHAR FileGuidString[GUID_CHAR_LEN];
  4035. GuidToStr(&ChangeOrder->Cmd.FileGuid, FileGuidString);
  4036. DPRINT2(4, "++ GAC: Interlock - Active children (%d) under GUID %s\n",
  4037. QHashEntry->QData>>1, FileGuidString);
  4038. if (!CoCmdIsDirectory(CoCmd)) {
  4039. DPRINT(0, "++ ERROR - Non Dir entry in pVme->ActiveChildren hash table.\n");
  4040. FRS_PRINT_TYPE(4, ChangeOrder);
  4041. }
  4042. //
  4043. // Set the flag and idle the queue and drop the table lock.
  4044. //
  4045. QHashEntry->QData |= 1;
  4046. FrsRtlIdleQueueLock(CoProcessQueue);
  4047. QHashReleaseLock(pVme->ActiveChildren);
  4048. CHANGE_ORDER_TRACE(3, ChangeOrder, "IsConflict - Child Busy");
  4049. return ISSUE_CONFLICT;
  4050. }
  4051. QHashReleaseLock(pVme->ActiveChildren);
  4052. } // End of HI_AC.
  4053. //
  4054. // If there are any remote change orders pending for retry of the install then
  4055. // check to see if this change order guid matches. If it does then we
  4056. // can retire this change order since it is a duplicate and we already
  4057. // have the staging file.
  4058. //
  4059. // need to enable")
  4060. if (0 && RemoteCo &&
  4061. !CoCmdIsDirectory(CoCmd) &&
  4062. (ChangeOrder->NewReplica->InLogRetryCount > 0)) {
  4063. // PERF: add code to lookup CO by GUID and check for match")
  4064. // return with a change order reject error code
  4065. //
  4066. // Remove the duplicate entry from the queue.
  4067. //
  4068. FrsRtlRemoveEntryQueueLock(CoProcessQueue, &ChangeOrder->ProcessList);
  4069. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_ONLIST);
  4070. return ISSUE_REJECT_CO;
  4071. }
  4072. return ISSUE_OK;
  4073. //
  4074. // Issue conflict on the file or on the parent dir. Set flag in Active
  4075. // Change order to unblock the queue on retire and then drop the change
  4076. // order GUID lock.
  4077. //
  4078. CONFLICT:
  4079. SET_COE_FLAG(ActiveChangeOrder, COE_FLAG_VOL_COLIST_BLOCKED);
  4080. DPRINT1(4, "++ GAC Setting COE_FLAG_VOL_COLIST_BLOCKED, block on: %08x %08x\n",
  4081. PRINTQUAD(ActiveChangeOrder->FileReferenceNumber));
  4082. //
  4083. // Conflict detected in name conflict table
  4084. //
  4085. NAME_CONFLICT:
  4086. if (DropRef) {
  4087. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  4088. ActiveChangeOrder,
  4089. TRUE);
  4090. }
  4091. //
  4092. // Idle the queue and drop the change order lock.
  4093. //
  4094. FrsRtlIdleQueueLock(CoProcessQueue);
  4095. FRS_ASSERT(GuidLockSlot != UNDEFINED_LOCK_SLOT);
  4096. ChgOrdReleaseLock(GuidLockSlot);
  4097. return ISSUE_CONFLICT;
  4098. }
  4099. BOOL
  4100. ChgOrdReconcile(
  4101. PREPLICA Replica,
  4102. PCHANGE_ORDER_ENTRY ChangeOrder,
  4103. PIDTABLE_RECORD IDTableRec
  4104. )
  4105. /*++
  4106. Routine Description:
  4107. Reconcile the incoming change order with the current state of the file
  4108. or dir in question.
  4109. Some special casing is done for duplicate remote change orders. The same
  4110. change order can arrive from multiple inbound partners before the version
  4111. vector can advance to dampen it. The first CO in the set of duplicates is
  4112. called X for the purpose of discussion. The rest are called the
  4113. duplicates. The duplicate COs will end up in the inbound log if CO X fails
  4114. to fetch the staging file. This allows any of the duplicates to retry the
  4115. Fetch operation later. Any CO (either X or a duplicate) that ultimately
  4116. completes the Fetch successfully updates the IDTable entry and activates
  4117. the version vector retire slot that maintains ordering of the change orders
  4118. as they propagate to the outbound log. It (along with the outlog process)
  4119. is also responsible for cleaning up the staging file. Finally this CO is
  4120. responsible for updating the version vector (through activation of the VV
  4121. slot).
  4122. At this point if the CO fails to complete the file install phase its state
  4123. is marked as either IBCO_INSTALL_RETRY or IBCO_INSTALL_REN_RETRY or
  4124. IBCO_INSTALL_DEL_RETRY. Any
  4125. other Dup COs are in the fetch retry or fetch request state. The reconcile
  4126. state (used in this function) in the IDTable entry for the file will now
  4127. match the state in any of the duplicate COs still in the inbound log. So
  4128. they will be rejected on retry. Note that the duplicate COs still need to
  4129. ACK their inbound partners and delete their entry from the inbound log.
  4130. They do not update the version vector since that was done by the CO
  4131. completing the fetch.
  4132. Arguments:
  4133. Replica - Replica set this change order applies to. Used to get config
  4134. record.
  4135. ChangeOrder - Change order entry
  4136. IDTableRec - The IDTable record to test this change order against.
  4137. Return Value:
  4138. TRUE if change order is accepted, FALSE if rejected.
  4139. --*/
  4140. {
  4141. #undef DEBSUB
  4142. #define DEBSUB "ChgOrdReconcile:"
  4143. LONGLONG EventTimeDelta;
  4144. LONGLONG SizeDelta;
  4145. LONG VersionDelta;
  4146. LONG RStatus;
  4147. PCHANGE_ORDER_COMMAND CoCmd = &ChangeOrder->Cmd;
  4148. FRS_ASSERT(IDTableRec != NULL);
  4149. //
  4150. // The sequence of events are complicated:
  4151. //
  4152. // 1. A remote co for an update comes in for a tombstoned idtable entry.
  4153. //
  4154. // 2. The remote co becomes a reanimate co.
  4155. //
  4156. // 3. The reanimate remote co loses a name conflict to another idtable entry.
  4157. //
  4158. // 4. A MorphGenCo for a local delete is generated for the
  4159. // reanimate remote co that lost the name conflict.
  4160. //
  4161. // 5. The MorphGenCo completes and updates the version so that the
  4162. // original reanimate co that lost the name conflict will be
  4163. // rejected.
  4164. //
  4165. // 6. The upcoming check for a leaf node (i.e. no outbound partners)
  4166. // accepts the original reanimate co and the service goes back to step 1
  4167. // causing a Morph Gen loop. To avoid this the following flag is
  4168. // set when the Morph Gen Co is created.
  4169. //
  4170. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_REJECT_AT_RECONCILE)) {
  4171. DPRINT(4, "++ ChangeOrder rejected based on COE_FLAG_REJECT_AT_RECONCILE\n");
  4172. return FALSE;
  4173. }
  4174. //
  4175. // If this member has no outbound partners AND has changed the file
  4176. // previously we accept. We detect this by comparing the Originator GUID
  4177. // in the IDTable entry with the GUID of this member. This avoids problems
  4178. // where a leaf member modifies a file, thereby rejecting an update until
  4179. // the version number test or event time test causes an accept. A leaf
  4180. // member should not be trying to change replicated files.
  4181. //
  4182. if (NO_OUTLOG_PARTNERS(Replica) &&
  4183. GUIDS_EQUAL(&IDTableRec->OriginatorGuid, &Replica->ReplicaVersionGuid)) {
  4184. DPRINT(4, "++ ChangeOrder accepted based on leaf node\n");
  4185. return TRUE;
  4186. }
  4187. //
  4188. // If the change order is in the IBCO_INSTALL_DEL_RETRY state then
  4189. // accept or reject it based on the COE_FLAG_NEED_DELETE flag.
  4190. // COE_FLAG_NEED_DELETE was set in ChgOrdReadIDRecord() when we found the
  4191. // IDREC_FLAGS_DELETE_DEFERRED flag set. See comments in schema.h for
  4192. // IDREC_FLAGS_DELETE_DEFERRED for more details on this scenario.
  4193. //
  4194. if (CO_STATE_IS(ChangeOrder, IBCO_INSTALL_DEL_RETRY)) {
  4195. return COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_DELETE);
  4196. }
  4197. // PERF: Add test of USN on the file to USN in CO (if local) to see if
  4198. // another Local CO could be on its way.
  4199. //
  4200. // The EventTimeDelta is the difference between the changeorder EventTime
  4201. // and the saved EventTime in the IDTable of the last change order we
  4202. // accepted. If the EventTimeDelta is within plus or minus RecEventTimeWindow
  4203. // then we move to the next step of the decsion sequence. Otherwise
  4204. // we either accept or reject the changeorder.
  4205. //
  4206. EventTimeDelta = CoCmd->EventTime.QuadPart - IDTableRec->EventTime;
  4207. if (EventTimeDelta > (LONGLONG)(0)) {
  4208. if (EventTimeDelta > RecEventTimeWindow) {
  4209. DPRINT1(4, "++ ChangeOrder accepted based on EventTime > window: %08x %08x\n",
  4210. PRINTQUAD(EventTimeDelta));
  4211. return TRUE;
  4212. }
  4213. } else {
  4214. if (-EventTimeDelta > RecEventTimeWindow) {
  4215. DPRINT1(4, "++ ChangeOrder rejected based on EventTime < window: %08x %08x\n",
  4216. PRINTQUAD(EventTimeDelta));
  4217. return FALSE;
  4218. }
  4219. }
  4220. DPRINT1(4, "++ ChangeOrder EventTime inside window: %08x %08x\n", PRINTQUAD(EventTimeDelta));
  4221. //
  4222. // The Changeorder eventtime is within the RecEventTimeWindow. The next
  4223. // step is to look at the version number of the file. The version number
  4224. // is updated each time the file is closed after being modified. So it
  4225. // acts like a time based on the rate of modification to the file.
  4226. //
  4227. VersionDelta = (LONG) (CoCmd->FileVersionNumber - IDTableRec->VersionNumber);
  4228. if ( VersionDelta != 0 ) {
  4229. DPRINT2(4, "++ ChangeOrder %s based on version number: %d\n",
  4230. (VersionDelta > 0) ? "accepted" : "rejected", VersionDelta);
  4231. return (VersionDelta > 0);
  4232. }
  4233. DPRINT(4, "++ ChangeOrder VersionDelta is zero.\n");
  4234. //
  4235. // The EventTimeDelta is inside the window and the Version Numbers match.
  4236. // This means that the file was changed at about the same time on two
  4237. // different members. We have to pick a winner so we use EventTimeDelta
  4238. // again to decide.
  4239. //
  4240. if ( EventTimeDelta != 0 ) {
  4241. DPRINT2(4, "++ ChangeOrder %s based on narrow EventTime: %08x %08x\n",
  4242. (EventTimeDelta > 0) ? "accepted" : "rejected",
  4243. PRINTQUAD(EventTimeDelta));
  4244. return (EventTimeDelta > 0);
  4245. }
  4246. DPRINT(4, "++ ChangeOrder EventTimeDelta is zero.\n");
  4247. //
  4248. // Both the version numbers and EventTimes match. Check the file sizes.
  4249. //
  4250. SizeDelta = (LONGLONG) (CoCmd->FileSize - IDTableRec->FileSize);
  4251. if ( SizeDelta != 0 ) {
  4252. DPRINT2(4, "++ ChangeOrder %s based on file size: %d\n",
  4253. (SizeDelta > 0) ? "accepted" : "rejected", SizeDelta);
  4254. return (SizeDelta > 0);
  4255. }
  4256. DPRINT(4, "++ ChangeOrder SizeDelta is zero.\n");
  4257. //
  4258. // The final compare is to do a comparison of the Originator Guids.
  4259. // If the Originator Guid of the incoming change order is greater
  4260. // or equal to the Guid in the IDTable Record then we accept the
  4261. // new change order.
  4262. //
  4263. RStatus = FrsGuidCompare(&CoCmd->OriginatorGuid,
  4264. &IDTableRec->OriginatorGuid);
  4265. //
  4266. // But first check to see if this is a duplicate change order
  4267. // (Originator GUID matches value in ID record and OriginatorVSN
  4268. // matches too). This closes a window where the duplicate changeorder
  4269. // is blocked behind another change order in the queue but past the point
  4270. // of the dampening check. The Active CO retires, unblocking the queue, and
  4271. // now the blocking CO issues followed by the dup CO issue.
  4272. //
  4273. if (RStatus == 0) {
  4274. //
  4275. // If the change order is in the IBCO_INSTALL_REN_RETRY state then
  4276. // accept or reject it based on the COE_FLAG_NEED_RENAME flag.
  4277. // COE_FLAG_NEED_RENAME was set in ChgOrdReadIDRecord() when we found the
  4278. // IDREC_FLAGS_RENAME_DEFERRED flag set. See comments in
  4279. // ChgOrdReadIDRecord() for more details on this scenario.
  4280. //
  4281. // Note: We still have to go thru the above reconcile tests since there
  4282. // could be multiple COs in the IBCO_INSTALL_REN_RETRY state and we
  4283. // need to reject all of them except the most recent. A sharing violation
  4284. // on the target could cause this and depending on the timing of the
  4285. // close of the file the next INSTALL_REN_RETRY CO to issue will do
  4286. // the rename. (This may not be a problem since the data from the
  4287. // final rename should come from the IDTable entry not the CO. The
  4288. // IDTable entry should be current so that subsequent name morph
  4289. // collision checks are accurate.)
  4290. //
  4291. if (CO_STATE_IS(ChangeOrder, IBCO_INSTALL_REN_RETRY)) {
  4292. //
  4293. // Accept the CO if the file still needs to be renamed.
  4294. // If a dup CO got to it first we would still match but if the
  4295. // rename is done then don't do it again.
  4296. //
  4297. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_RENAME)) {
  4298. DPRINT(4, "++ Remote ChangeOrder accepted based on retry rename-only\n");
  4299. }
  4300. return COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_RENAME);
  4301. }
  4302. if (CO_STATE_IS(ChangeOrder, IBCO_INSTALL_DEL_RETRY)) {
  4303. //
  4304. // Accept the CO if the file still needs to be deleted.
  4305. // If a dup CO got to it first we would still match but if the
  4306. // delete is done then don't do it again.
  4307. //
  4308. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_DELETE)) {
  4309. DPRINT(4, "++ Remote ChangeOrder accepted based on retry delete-only\n");
  4310. }
  4311. return COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_DELETE);
  4312. }
  4313. if (IDTableRec->OriginatorVSN == CoCmd->FrsVsn) {
  4314. //
  4315. // Duplicate retries are okay if the retry is a Dir Enum request.
  4316. //
  4317. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY)) {
  4318. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO)) {
  4319. if (CO_STATE_IS(ChangeOrder, IBCO_ENUM_REQUESTED)) {
  4320. DPRINT(4, "++ Local ChangeOrder accepted based on ENUM REQUESTED\n");
  4321. return TRUE;
  4322. }
  4323. }
  4324. #if 0
  4325. //
  4326. // This code will also do a retry on a CO in the INSTALL_RETRY state.
  4327. // This is wrong since if the reconcile state matches the install has
  4328. // already been done and the staging file may have been deleted. The
  4329. // Cases for rename retry and delete retry are handled above.
  4330. //
  4331. // Butttt...if the disk fills we also go into install retry but if the
  4332. // CO is not accepted here it is rejected for sameness. Then it doesn't
  4333. // get installed. ??? Why does the IDTable get updated on install retry?
  4334. //
  4335. // The above comment may be moot at this point (3/2000) but we need
  4336. // to do some more disk full related tests to be sure we aren't dropping
  4337. // any files. -- Davidor
  4338. //
  4339. else {
  4340. if (CO_STATE_IS_INSTALL_RETRY(ChangeOrder)) {
  4341. DPRINT(4, "++ Remote ChangeOrder accepted based on retry rename-only\n");
  4342. return TRUE;
  4343. }
  4344. }
  4345. #endif
  4346. }
  4347. DPRINT(4, "++ ChangeOrder rejected for sameness\n");
  4348. return FALSE;
  4349. }
  4350. } else {
  4351. //
  4352. // In highly connected environments lots of identical name morph
  4353. // conflicts could occur, generating a flurry of undampened COs. Since
  4354. // it doesn't really matter which originator produced a given morph
  4355. // generated CO we use CO_FLAG_SKIP_ORIG_REC_CHK to tell downstream
  4356. // partners to skip this part of the reconcile check. When the
  4357. // same conflict is detected on two members they will produce change
  4358. // orders with the same event time, version number and size. When both
  4359. // COs arrive at another member the second will be rejected for sameness
  4360. // if we skip the originator guid check.
  4361. //
  4362. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_SKIP_ORIG_REC_CHK)) {
  4363. DPRINT(4, "++ ChangeOrder rejected for sameness -- SKIP_ORIG_REC_CHK\n");
  4364. return FALSE;
  4365. }
  4366. }
  4367. DPRINT2(4, "++ ChangeOrder %s based on guid (RStatus = %d)\n",
  4368. (RStatus >= 0) ? "accepted" : "rejected", RStatus);
  4369. return (RStatus >= 0);
  4370. }
  4371. BOOL
  4372. ChgOrdGenMorphCo(
  4373. PTHREAD_CTX ThreadCtx,
  4374. PTABLE_CTX TmpIDTableCtxNC,
  4375. PCHANGE_ORDER_ENTRY ChangeOrder,
  4376. PREPLICA Replica
  4377. )
  4378. /*++
  4379. Routine Description:
  4380. If there is a name morph conflict we have the following cases:
  4381. Incoming CO IDTable Entry with name conflict
  4382. File File Send out del CO for IDTable Entry to free name
  4383. File Dir Dir wins the name convert CO to delete CO. (1)
  4384. Dir File Dir wins the name build del CO for IDTable (2)
  4385. Dir Dir send out Tree del CO for IDTable Entry to free name. (3)
  4386. When a Local CO is built from an IDTable record the file version
  4387. number is set to the value in the IDTable record plus one. Since
  4388. this is marked as a MorphGenCo this version number will not be
  4389. revised later if this CO ends up going through retry.
  4390. If a Local Co is built from the incoming change order (ChgOrdConvertCo)
  4391. the file version number is set to the value of the incoming CO + 1.
  4392. This ensures the newly generated CO will be accepted even if there
  4393. already exists an IDTable entry for the incoming CO. The latter
  4394. could happen if the incoming CO is a rename or if it is a prior
  4395. create Co that has been stuck in the IBCO_INSTALL_RETRY or
  4396. IBCO_INSTALL_REN_RETRY states. In addition if the incoming CO is
  4397. a create CO that is losing its bid for the name then the delete Co
  4398. we create will go out first, creating a tombstone, and we want the
  4399. the original incoming CO to follow and be rejected when it matches
  4400. up against the tombstone.
  4401. Notes:
  4402. (1) The incoming CO must be sent out as a delete CO because other
  4403. replica set members may not experience the name conflict if a rename
  4404. on the Dir occurred before this CO arrived. So to ensure that the
  4405. file associated with the incoming CO is gone everywhere we send out
  4406. a delete Co.
  4407. (2) In the case of a name morph conflict where the incoming CO is a
  4408. directory and the current owner of the name is a file, the file
  4409. will lose. Otherwise we will have to abort all subsequent child creates.
  4410. Send out a Delete Co on the IDtable record for the same reason as in (1).
  4411. (3) re: tree del co.
  4412. This can be a problem. What if a new replica set member that has not
  4413. yet synced up gets a local create dir that conflicts with an existing
  4414. dir? We send out the create dir which then causes that entire
  4415. subtree to get deleted everywhere. This is not good. A better
  4416. solution in this case is to just morph the dir name, giving
  4417. priority to the name to the OLDER entry.
  4418. Notation:
  4419. The following 3 letter notation is used to distinguish the cases:
  4420. The first D or F refers to the incoming CO and says if it's a dir or a file.
  4421. The second D or F refers to the current entry in the IDTable with the same
  4422. name (hence the name morph conflict) and if it's a dir or a file.
  4423. The L appears as a suffix after the first (or second) D or F, indicating
  4424. that either the incoming CO lost the name morph conflict or the IDTable
  4425. entry lost the name morph conflict, respectively. So there are 6 cases:
  4426. Incoming CO is a IDTable entry is for a Outcome
  4427. FFL - file, file, IDT entry loses.
  4428. FLF - file, file, Incoming CO loses.
  4429. FLD - file, dir, Incoming CO loses.
  4430. DFL - dir, file, IDT entry loses.
  4431. DDL - dir, dir, IDT entry loses.
  4432. DLD - dir, dir, Incoming CO loses.
  4433. There are two change orders produced as the result of any name morph
  4434. conflict. One is the modified incoming CO and the other is refered to
  4435. as the MorphGenCo. The MorphGenCo is always a local CO that gets proped
  4436. to all other replica set members so they know the outcome of the morph
  4437. conflict decision. The MorphGenCo may be fabricated from either the
  4438. incoming CO or from the IDTable entry with the same name. If the loser
  4439. of a name morph conflict is a file then the MorphGenCo will be a delete
  4440. change order. On the other hand if the loser is a directory then the
  4441. MorphGenCo will be a rename change order to produce a nonconflicting
  4442. directory name.
  4443. In all cases above two change orders are pushed back onto the front of the
  4444. process queue and processing is started over again. The first CO that gets
  4445. pushed back onto the process queue is termed the "follower" since it will
  4446. issue second. The second CO that gets pushed onto the process queue is termed
  4447. the "leader" since it will issue first when processing resumes.
  4448. In all cases above, except DLD, the Leader is the MorphGenCo and its job
  4449. is to remove the name conflict. In the DLD case the incoming CO loses the
  4450. name so it is given a new name and will be leader. The MorphGenCo is
  4451. the follower in this case and is a rename CO that gets proped out to tell
  4452. all downstream partners about the name change.
  4453. Other details:
  4454. MorphGenCos never go into the inbound log. If the incoming CO fails to
  4455. complete and must go thru retry then the MorphGenCo is aborted if it is
  4456. the follower (DLD case). If the MorphGenCo is the leader and it fails
  4457. to complete then it is aborted and when the follower reissues it will
  4458. experience a recurrance of the name morph conflict implying that the leader
  4459. failed so it is sent thru retry and will be resubmitted later. The leader
  4460. might fail because of a sharing violation on the existing file or the
  4461. journal connection could be shutting down.
  4462. The reason that the MorphGenCos don't get put into the Inbound log is that
  4463. if we were to crash or shutdown between before completing both change
  4464. orders they could get restarted out of order. The MorphGenCo is a local
  4465. Co and the Incoming Change Order could be a remote CO. So at restart
  4466. the COs are inserted into the process queue as each inbound connection
  4467. is re-established.
  4468. Arguments:
  4469. ThreadCtx -- A Thread context to use for dbid and sesid.
  4470. TmpIDTableCtxNC -- The table context to use for IDT table access for the
  4471. entry with the name conflict.
  4472. ChangeOrder-- The change order.
  4473. Replica -- The Replica set context.
  4474. Return Value:
  4475. True if morph change orders were generated OK.
  4476. --*/
  4477. {
  4478. #undef DEBSUB
  4479. #define DEBSUB "ChgOrdGenMorphCo:"
  4480. #define MORPH_FILE_FILE 0
  4481. #define MORPH_FILE_DIR 1
  4482. #define MORPH_DIR_FILE 2
  4483. #define MORPH_DIR_DIR 3
  4484. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  4485. PIDTABLE_RECORD NameConflictIDTableRec;
  4486. ULONG MorphCase;
  4487. BOOL MorphNameIDTable, MorphOK, MorphGenLeader;
  4488. //
  4489. // If we have already been here once and marked as a MorphGenLeader
  4490. // then this is a create DIR and we go straight to the DIR/Dir case.
  4491. // NOTE: The NameConflictIDTableRec is not valid in this case since
  4492. // the name had already been morphed on a prior visit so use the
  4493. // existing name to refabricate the MorphGenFollower rename co.
  4494. //
  4495. MorphGenLeader = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN_LEADER);
  4496. NameConflictIDTableRec = (PIDTABLE_RECORD) TmpIDTableCtxNC->pDataRecord;
  4497. MorphCase = 0;
  4498. if (CoIsDirectory(ChangeOrder)) {
  4499. MorphCase += 2;
  4500. }
  4501. if (MorphGenLeader ||
  4502. BooleanFlagOn(NameConflictIDTableRec->FileAttributes,
  4503. FILE_ATTRIBUTE_DIRECTORY)) {
  4504. MorphCase += 1;
  4505. }
  4506. MorphOK = FALSE;
  4507. switch (MorphCase) {
  4508. case MORPH_FILE_FILE:
  4509. //
  4510. // Incoming CO: File IDTable Entry: File
  4511. //
  4512. // Reconcile decides who wins the name.
  4513. // If incoming Co loses then generate a delete CO with us
  4514. // as the originator. (get new VSN).
  4515. // If IDtable loses the name then push incoming CO back onto
  4516. // the process queue and generate a delete CO for the IDTable
  4517. // entry to free the name.
  4518. //
  4519. if (ChgOrdReconcile(Replica, ChangeOrder, NameConflictIDTableRec)) {
  4520. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - FFL, Name Morph, Del IDT");
  4521. //
  4522. // Push incoming CO first with same version number.
  4523. // Push local delete CO for IDT file with vers num = IDT+1
  4524. //
  4525. MorphOK = ChgOrdMakeDeleteCo(ChangeOrder,
  4526. Replica,
  4527. ThreadCtx,
  4528. NameConflictIDTableRec);
  4529. } else {
  4530. //
  4531. // Build a delete CO unless this is a duplicate CO.
  4532. //
  4533. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co rejected - FLF, Name Morph, Del Inc CO");
  4534. //
  4535. // Push incoming CO first with same version number.
  4536. // Push local delete CO for incoming CO with
  4537. // vers num = incoming CO + 1
  4538. //
  4539. // Ensure that incoming Co gets rejected later. If this was a
  4540. // Leaf node member (i.e. no outbound partners) we would always
  4541. // accept remote Cos and get into a Morph Gen loop. See comments
  4542. // in ChgOrdReconcile().
  4543. // Set this flag here since after the call below the CO is back
  4544. // on the queue and we can't touch it.
  4545. //
  4546. SET_COE_FLAG(ChangeOrder, COE_FLAG_REJECT_AT_RECONCILE);
  4547. MorphOK = ChgOrdConvertCo(ChangeOrder,
  4548. Replica,
  4549. ThreadCtx,
  4550. CO_LOCATION_DELETE);
  4551. if (!MorphOK) {
  4552. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_REJECT_AT_RECONCILE);
  4553. }
  4554. }
  4555. break;
  4556. case MORPH_FILE_DIR:
  4557. //
  4558. // Incoming CO: File IDTable Entry: Dir
  4559. //
  4560. // Name stays with the Dir in IDTable.
  4561. //
  4562. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co rejected - FLD, Name Morph, Del Inc CO");
  4563. //
  4564. // Push incoming CO first with same version number.
  4565. // Push local delete CO for incoming CO with
  4566. // vers num = incoming CO + 1
  4567. //
  4568. // Ensure that incoming Co gets rejected later. If this was a
  4569. // Leaf node member (i.e. no outbound partners) we would always
  4570. // accept remote Cos and get into a Morph Gen loop. See comments
  4571. // in ChgOrdReconcile().
  4572. // Set this flag here since after the call below the CO is back
  4573. // on the queue and we can't touch it.
  4574. //
  4575. SET_COE_FLAG(ChangeOrder, COE_FLAG_REJECT_AT_RECONCILE);
  4576. MorphOK = ChgOrdConvertCo(ChangeOrder, Replica, ThreadCtx, CO_LOCATION_DELETE);
  4577. if (!MorphOK) {
  4578. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_REJECT_AT_RECONCILE);
  4579. }
  4580. break;
  4581. case MORPH_DIR_FILE:
  4582. //
  4583. // Incoming CO: Dir IDTable Entry: File
  4584. //
  4585. // Name goes with incoming Dir CO.
  4586. //
  4587. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - DFL, Name Morph, Del IDT");
  4588. //
  4589. // Push incoming CO first with same version number.
  4590. // Push local delete CO for IDT file with vers num = IDT+1
  4591. //
  4592. MorphOK = ChgOrdMakeDeleteCo(ChangeOrder,
  4593. Replica,
  4594. ThreadCtx,
  4595. NameConflictIDTableRec);
  4596. break;
  4597. case MORPH_DIR_DIR:
  4598. //
  4599. // Incoming CO: Dir IDTable Entry: Dir
  4600. //
  4601. // Priority to the name goes to which ever is older.
  4602. // The other gets a new name.
  4603. // If incoming CO loses then just morph its name and send
  4604. // out a rename CO.
  4605. // If IDTable record loses then push this CO back onto the queue
  4606. // and generate a rename Co.
  4607. //
  4608. if (MorphGenLeader) {
  4609. // Use name as given in CO. See comment above.
  4610. MorphNameIDTable = FALSE;
  4611. //
  4612. // THis better be a dir CO and we should not be here if the
  4613. // MorphGenFollower has already been fabricated.
  4614. //
  4615. FRS_ASSERT(CoIsDirectory(ChangeOrder));
  4616. FRS_ASSERT(!COE_FLAG_ON(ChangeOrder, COE_FLAG_MG_FOLLOWER_MADE));
  4617. DPRINT(4, "++ MorphGenLeader - IDTable entry loses name\n");
  4618. } else
  4619. if (CoCmd->EventTime.QuadPart > NameConflictIDTableRec->EventTime) {
  4620. // IDTable wins the name. Morph the change order.
  4621. MorphNameIDTable = FALSE;
  4622. DPRINT2(4, "++ IDTable entry wins name - CoEvt: %08x %08x, IdEvt: %08x %08x\n",
  4623. PRINTQUAD(CoCmd->EventTime.QuadPart),
  4624. PRINTQUAD(NameConflictIDTableRec->EventTime));
  4625. } else
  4626. if (CoCmd->EventTime.QuadPart < NameConflictIDTableRec->EventTime) {
  4627. // Incoming CO wins the name, Morph the IDTable entry.
  4628. MorphNameIDTable = TRUE;
  4629. DPRINT2(4, "++ IDTable entry loses name - CoEvt: %08x %08x, IdEvt: %08x %08x\n",
  4630. PRINTQUAD(CoCmd->EventTime.QuadPart),
  4631. PRINTQUAD(NameConflictIDTableRec->EventTime));
  4632. } else {
  4633. LONG RStatus1;
  4634. DPRINT2(4, "++ IDTable Evt matches Co Evt - CoEvt: %08x %08x, IdEvt: %08x %08x\n",
  4635. PRINTQUAD(CoCmd->EventTime.QuadPart),
  4636. PRINTQUAD(NameConflictIDTableRec->EventTime));
  4637. RStatus1 = FrsGuidCompare(&CoCmd->OriginatorGuid,
  4638. &NameConflictIDTableRec->OriginatorGuid);
  4639. MorphNameIDTable = (RStatus1 >= 0);
  4640. DPRINT1(4, "++ Orig Guid Compare - IDTable entry %s name.\n",
  4641. (MorphNameIDTable ? "Loses" : "Wins"));
  4642. if (RStatus1 == 0) {
  4643. //
  4644. // Looks like a duplicate. Same event time same orig guid
  4645. //
  4646. DPRINT(0, "++ Warning: DIR_DIR Morph Conflict where Orig Guid in CO equals IDTrecord\n");
  4647. DBS_DISPLAY_RECORD_SEV(0, TmpIDTableCtxNC, TRUE);
  4648. FRS_PRINT_TYPE(0, ChangeOrder);
  4649. FRS_ASSERT(!"DIR_DIR Morph Conflict where Orig Guid in CO equals IDTrecord");
  4650. //
  4651. // add the following as part of the return path if we can ever
  4652. // hit this case legitimately.
  4653. //
  4654. //CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - DupCo");
  4655. //ChgOrdReject(ChangeOrder, Replica);
  4656. //goto CO_CLEANUP;
  4657. }
  4658. }
  4659. if (MorphNameIDTable) {
  4660. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - DDL, Name Morph IDT");
  4661. //
  4662. // Push incoming CO first with same version number.
  4663. // Push local rename CO for IDT Dir with vers num = IDT+1
  4664. //
  4665. MorphOK = ChgOrdMakeRenameCo(ChangeOrder,
  4666. Replica,
  4667. ThreadCtx,
  4668. NameConflictIDTableRec);
  4669. } else {
  4670. //
  4671. // Incoming CO loses the name.
  4672. //
  4673. if (MorphGenLeader) {
  4674. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - DLD, Follower Regen");
  4675. } else {
  4676. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Accepted - DLD, Dir-Dir Name Morph Inc CO");
  4677. }
  4678. //
  4679. // Push local rename CO for incoming CO with
  4680. // vers num = incoming CO + 1
  4681. // Push incoming CO with same version number and morphed name.
  4682. //
  4683. MorphOK = ChgOrdConvertCo(ChangeOrder,
  4684. Replica,
  4685. ThreadCtx,
  4686. CO_LOCATION_NO_CMD);
  4687. }
  4688. break;
  4689. default:
  4690. FRS_ASSERT(!"Invalid morph case");
  4691. } // end switch
  4692. return MorphOK;
  4693. }
  4694. BOOL
  4695. ChgOrdApplyMorphCo(
  4696. PCHANGE_ORDER_ENTRY ChangeOrder,
  4697. PREPLICA Replica
  4698. )
  4699. /*++
  4700. Routine Description:
  4701. Apply the name change requested by the MorphGenCo to resolve the name
  4702. conflict.
  4703. Arguments:
  4704. ChangeOrder-- The change order.
  4705. Replica -- The Replica set context.
  4706. Return Value:
  4707. True if name change required to resolve the name conflict succeeded.
  4708. --*/
  4709. {
  4710. #undef DEBSUB
  4711. #define DEBSUB "ChgOrdApplyMorphCo:"
  4712. ULONG WStatus;
  4713. ULONG LocationCmd;
  4714. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  4715. if (LocationCmd == CO_LOCATION_DELETE) {
  4716. //
  4717. // Delete the underlying file to free the name.
  4718. //
  4719. CHANGE_ORDER_TRACE(3, ChangeOrder, "Del Name Morph IDT loser");
  4720. WStatus = StuDelete(ChangeOrder);
  4721. if (WIN_SUCCESS(WStatus)) {
  4722. return TRUE;
  4723. }
  4724. CHANGE_ORDER_TRACEX(3, ChangeOrder, "Del IDT loser FAILED. WStatus=", WStatus);
  4725. //
  4726. // Let the MorphGenCo go if it is not the leader.
  4727. // Otherwise reject it so the follower is forced thru retry.
  4728. //
  4729. if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN_LEADER)) {
  4730. return TRUE;
  4731. }
  4732. } else
  4733. if (BooleanFlagOn(ChangeOrder->Cmd.ContentCmd, USN_REASON_RENAME_NEW_NAME)) {
  4734. //
  4735. // Rename the underlying dir to free the name.
  4736. //
  4737. CHANGE_ORDER_TRACE(3, ChangeOrder, "Ren Name Morph IDT loser");
  4738. WStatus = StuInstallRename(ChangeOrder, FALSE, TRUE);
  4739. if (WIN_SUCCESS(WStatus)) {
  4740. return TRUE;
  4741. }
  4742. CHANGE_ORDER_TRACEX(3, ChangeOrder, "Ren IDT loser FAILED. WStatus=", WStatus);
  4743. //
  4744. // Let the MorphGenCo go if it is not the leader.
  4745. // Otherwise reject it so the follower is forced thru retry.
  4746. // The install rename can fail if a dir create causes a
  4747. // parent reanimate and while the dir create is in process a second dir
  4748. // create arrives and wins the name. THis generates a rename MorphGenCo
  4749. // for the in-process dir create. Meanwhile if a local CO deletes the
  4750. // reanimated parent (no fooling) before the first dir create is renamed
  4751. // into place then the first dir create will go into rename retry state.
  4752. // That unblocks the process queue so the rename MorphGenco issues and
  4753. // it fails too because the parent dir is not present. It later tries
  4754. // to go thru retry and fails because local MorphGenCOs can't have
  4755. // rename retry failures.
  4756. // Another scenario is the DLD case (see ChgOrdGenMorphCo()) where
  4757. // the incoming dir create loses the name morph conflict. In this case
  4758. // the MorphGenCo is a follower that props out a rename CO. However if
  4759. // the stage file fetch for the leader Create CO fails (file was deleted
  4760. // upstream) then there is no file to rename. But since we can't tell
  4761. // if the file not found status is on the parent dir or on the actual
  4762. // target we let the FOLLOWER go. It will get aborted later in
  4763. // StageCsCreateStage() when we fail to generate the stage file.
  4764. //
  4765. if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN_LEADER)) {
  4766. return TRUE;
  4767. }
  4768. } else {
  4769. CHANGE_ORDER_TRACE(3, ChangeOrder, "Invalid MorphGenCo");
  4770. }
  4771. ChgOrdReject(ChangeOrder, Replica);
  4772. return FALSE;
  4773. }
  4774. ULONG
  4775. ChgOrdReanimate(
  4776. IN PTABLE_CTX IDTableCtx,
  4777. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  4778. IN PREPLICA Replica,
  4779. IN PTHREAD_CTX ThreadCtx,
  4780. IN PTABLE_CTX TmpIDTableCtx
  4781. )
  4782. /*++
  4783. Routine Description:
  4784. Check if we have to reanimate the parent directory for this change order.
  4785. Arguments:
  4786. IDTableCtx -- The IDTable context for the current change order.
  4787. ChangeOrder -- The change order.
  4788. Replica -- The Replica struct.
  4789. ThreadCtx -- ptr to the thread context for DB
  4790. TmpIDTableCtx -- The temporary ID Table context to use for reading the IDTable
  4791. to reanimate the CO.
  4792. Return Value:
  4793. REANIMATE_SUCCESS : Reanimation was required and succeeded.
  4794. REAMIMATE_RETRY_LATER : Parent reanimation failed, send base CO thru retry.
  4795. REANIMATE_CO_REJECTED : Can't reanimation for this CO so reject the CO.
  4796. REANIMATE_FAILED : Internal error.
  4797. --*/
  4798. {
  4799. #undef DEBSUB
  4800. #define DEBSUB "ChgOrdReanimate:"
  4801. JET_ERR jerr, jerr1;
  4802. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  4803. PCHANGE_ORDER_ENTRY DeadCoe;
  4804. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  4805. ULONG FStatus, GStatus, WStatus;
  4806. BOOL LocalCo, ReAnimate, DemandRefreshCo;
  4807. PIDTABLE_RECORD IDTableRec = (PIDTABLE_RECORD) (IDTableCtx->pDataRecord);
  4808. PIDTABLE_RECORD IDTableRecParent = NULL;
  4809. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  4810. DemandRefreshCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_DEMAND_REFRESH);
  4811. ReAnimate = IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED);
  4812. //
  4813. // If this is a local file op that was updating a file just milliseconds
  4814. // before a previously issued remote co deleted the file then don't
  4815. // try to reanimate since there is no data upstream anyway. There are
  4816. // 3 cases here:
  4817. // 1. If the local user does the file update after the remote co delete
  4818. // (via file reopen and write back) then a new file is created with
  4819. // a new FID and object ID. New data is replicated, User is happy.
  4820. // 2. If the user did the update just before we started processing
  4821. // the remote CO then reconcile will probably reject the remote co delete.
  4822. // 3. If the user does the update between the time where we issue
  4823. // the remote co delete and we actually delete the file then the user
  4824. // loses. This is no different than if the user lost the reconcile
  4825. // decision in step 2. Since we reject the local CO the remote CO
  4826. // delete continues on and the file is deleted everywhere.
  4827. //
  4828. // Note: There is one case where reanimation by a Local Co does make sense.
  4829. // If an app has a file open with sharing mode deny delete and we get a
  4830. // remote co to delete the file, the remote co will get processed and the
  4831. // IDTable entry is marked deleted (although our attempt to actually delete
  4832. // the file will fail with a sharing violation). Later the app updates the
  4833. // file and then closes it. We see the update USN record and need to
  4834. // reanimate since it will have a more recent event time.
  4835. //
  4836. if (ReAnimate &&
  4837. LocalCo &&
  4838. !IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED)) {
  4839. //
  4840. // A local Co can't reanimate a deleted file.
  4841. //
  4842. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - No Local Co revival");
  4843. DBS_DISPLAY_RECORD_SEV(3, IDTableCtx, FALSE);
  4844. FRS_PRINT_TYPE(3, ChangeOrder);
  4845. ChgOrdReject(ChangeOrder, Replica);
  4846. return REANIMATE_CO_REJECTED;
  4847. }
  4848. //
  4849. // Don't bother to reanimate the parent for a delete. This can happen
  4850. // if the file is stuck in the preinstall or staging areas and not
  4851. // really in the deleted directory. Let the delete continue w/o
  4852. // reanimation.
  4853. //
  4854. // Make this check before the local co check to avoid morphgenco
  4855. // asserts when this CoCmd is a del of the IDT loser.
  4856. //
  4857. if (DOES_CO_DELETE_FILE_NAME(CoCmd)) {
  4858. return REANIMATE_NO_NEED;
  4859. }
  4860. //
  4861. // Read the IDTable entry for the parent
  4862. //
  4863. jerr = DbsReadRecord(ThreadCtx, &CoCmd->NewParentGuid, GuidIndexx, TmpIDTableCtx);
  4864. if (!JET_SUCCESS(jerr)) {
  4865. DPRINT_JS(0,"++ ERROR - DbsReadRecord failed;", jerr);
  4866. ChgOrdReject(ChangeOrder, Replica);
  4867. return REANIMATE_CO_REJECTED;
  4868. }
  4869. //
  4870. // Close the table, reset the TableCtx Tid and Sesid.
  4871. // DbsCloseTable is a Macro, writes 1st arg.
  4872. //
  4873. // perf: With multiple replica sets we may need a different
  4874. // IDTable for the next CO we process. Could just check if
  4875. // there is a replica change and only close then. Or put the
  4876. // tmp table ctx in the replica struct so we can just switch.
  4877. //
  4878. DbsCloseTable(jerr1, ThreadCtx->JSesid, TmpIDTableCtx);
  4879. DPRINT_JS(0,"++ ERROR - JetCloseTable failed:", jerr1);
  4880. IDTableRecParent = (PIDTABLE_RECORD) (TmpIDTableCtx->pDataRecord);
  4881. //
  4882. // Parent should be marked deleted or why are we here?
  4883. //
  4884. FRS_ASSERT(IsIdRecFlagSet(IDTableRecParent, IDREC_FLAGS_DELETED));
  4885. if (LocalCo && !IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED) &&
  4886. !IsIdRecFlagSet(IDTableRecParent, IDREC_FLAGS_DELETE_DEFERRED)) {
  4887. //
  4888. // Don't know how to revive a dead parent of a local CO. How did it
  4889. // get to be dead in the first place? Here is how: If a local file op
  4890. // is updating a file just before a previously issued remote CO
  4891. // executes to delete the file followed by a second remote co to
  4892. // delete the parent then we end up here with an update Local Co with
  4893. // a deleted parent. So, don't assert just reject the local co and
  4894. // let the remote Co win since it already won upstream so there is no
  4895. // data around to reanimate the file with.
  4896. //
  4897. CHANGE_ORDER_TRACE(3, ChangeOrder, "Cant revive dead par of lcl co");
  4898. DBS_DISPLAY_RECORD_SEV(3, IDTableCtx, FALSE);
  4899. FRS_PRINT_TYPE(3, ChangeOrder);
  4900. ChgOrdReject(ChangeOrder, Replica);
  4901. return REANIMATE_CO_REJECTED;
  4902. }
  4903. // Need to revive the parent. Push this CO back on the front of
  4904. // the CO queue and then make a reanimation CO for the parent.
  4905. // Push this CO onto the front of the queue and then turn the
  4906. // loop. This handles nested reanimations.
  4907. //
  4908. //
  4909. // If this CO has already requested its parent to be reanimated then we
  4910. // shouldn't be here because we should have blocked on the issue check for
  4911. // its parent in the ActiveInboundChangeOrderTable. When the issue block
  4912. // was released we performed the GUIDFID translation again and this time
  4913. // the IDT Delete Status should show the parent is not deleted.
  4914. //
  4915. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_RISE_REQ)) {
  4916. //
  4917. // The parent reanimation must have failed. Reject this CO.
  4918. //
  4919. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_REANIMATION)) {
  4920. FRS_ASSERT(DemandRefreshCo);
  4921. //
  4922. // This CO is for a reanimated parent v.s. the base
  4923. // CO that started it all. There is no inbound log entry
  4924. // for it, no inbound partner to ack, etc.
  4925. //
  4926. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_INSTALL_INCOMPLETE);
  4927. SET_CO_FLAG(ChangeOrder, CO_FLAG_ABORT_CO);
  4928. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_STAGE_FILE);
  4929. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Nested Parent Rise Failed");
  4930. return REANIMATE_CO_REJECTED;
  4931. } else {
  4932. //
  4933. // This is the base CO that triggered the reanimation.
  4934. // The parent still isn't here so send it through retry.
  4935. //
  4936. // This can happen if the parent and child were deleted on the
  4937. // inbound partner before we could fetch the child. And then we
  4938. // did a retry on the child CO before we see the delete CO for
  4939. // the child from the inbound partner. So the parent reanimation
  4940. // fails and the child has no parent FID. Eventually we will get
  4941. // a delete CO for the child and we will create a tombstone thus
  4942. // causing this CO to be rejected when it next is retried.
  4943. //
  4944. // We may not get the delete of this target CO if the upstream is
  4945. // waiting for us to ACK some COs. By not sending an ACK we will
  4946. // block any further progress. The downside of sending an ACK before
  4947. // fetching the file is the upstream may have to regenerate the staging
  4948. // file again but if the target file is absent on upstream then it is not
  4949. // going to be able to generate the staging file in any case.
  4950. // Send an ACK here so we keep getting the COs from downstream and
  4951. // the expectation is that eventually we will sought things out.
  4952. // Also this will be inserted in the inbound log (before ChgOrdIssueCleanup)
  4953. // so we will keep retrying it.
  4954. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACK_INBOUND);
  4955. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  4956. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Parent Rise Failed");
  4957. return REAMIMATE_RETRY_LATER;
  4958. }
  4959. }
  4960. //
  4961. // Build a Remote change order entry for the parent from the IDtable
  4962. // Tombstone record. This is a fetch CO to the inbound partner
  4963. // that sent us the base CO to begin with.
  4964. //
  4965. if (LocalCo && IsIdRecFlagSet(IDTableRecParent, IDREC_FLAGS_DELETE_DEFERRED)) {
  4966. DeadCoe = ChgOrdMakeFromIDRecord(IDTableRecParent,
  4967. Replica,
  4968. CO_LOCATION_CREATE,
  4969. CO_FLAG_GROUP_RAISE_DEAD_PARENT | CO_FLAG_LOCALCO,
  4970. &CoCmd->CxtionGuid);
  4971. SET_CHANGE_ORDER_STATE(DeadCoe, IBCO_STAGING_REQUESTED);
  4972. } else {
  4973. DeadCoe = ChgOrdMakeFromIDRecord(IDTableRecParent,
  4974. Replica,
  4975. CO_LOCATION_CREATE,
  4976. CO_FLAG_GROUP_RAISE_DEAD_PARENT,
  4977. &CoCmd->CxtionGuid);
  4978. SET_CHANGE_ORDER_STATE(DeadCoe, IBCO_FETCH_REQUESTED);
  4979. }
  4980. //
  4981. // Update the version number by 1 and send the reanimation CO
  4982. // forward. This will make sure that this CO is accepted
  4983. // on the downstream partners that have the same tombstone.
  4984. //
  4985. DeadCoe->Cmd.FileVersionNumber = IDTableRecParent->VersionNumber + 1;
  4986. SET_COE_FLAG(DeadCoe, COE_FLAG_GROUP_RAISE_DEAD_PARENT);
  4987. //
  4988. // Mark this CO as reanimated if in fact it had been deleted
  4989. // and now a new update has come in, overriding the deletion.
  4990. // The other possibility is that it could be a NewFile create
  4991. // under a deleted parent. Not a reanimation in this case.
  4992. //
  4993. if (!COE_FLAG_ON(ChangeOrder, COE_FLAG_REANIMATION) && ReAnimate) {
  4994. SET_COE_FLAG(ChangeOrder, COE_FLAG_REANIMATION);
  4995. }
  4996. //
  4997. // Do the change order cleanup but don't free the CO.
  4998. // There is a VV slot to clean up if this is the CO that started
  4999. // parent reanimation.
  5000. //
  5001. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO);
  5002. if (!LocalCo && !DemandRefreshCo) {
  5003. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  5004. }
  5005. FStatus = ChgOrdIssueCleanup(ThreadCtx, Replica, ChangeOrder, 0);
  5006. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  5007. //
  5008. // Remember that this CO has already requested its parent to rise.
  5009. //
  5010. SET_COE_FLAG(ChangeOrder, COE_FLAG_PARENT_RISE_REQ);
  5011. // Dont we still have this ref from when it was last on
  5012. // the process queue?
  5013. //INCREMENT_CHANGE_ORDER_REF_COUNT(ChangeOrder);
  5014. //
  5015. // Inherit the recovery flag so this CO will block if the
  5016. // cxtion is not yet joined.
  5017. //
  5018. if (RecoveryCo(ChangeOrder)) {
  5019. SET_COE_FLAG(DeadCoe, COE_FLAG_RECOVERY_CO);
  5020. }
  5021. //
  5022. // Inherit the Connection and the Join Guid from the child.
  5023. // If the Cxtion fails, all these COs should fail.
  5024. //
  5025. DeadCoe->Cxtion = ChangeOrder->Cxtion;
  5026. FRS_ASSERT(ChangeOrder->Cxtion);
  5027. LOCK_CXTION_TABLE(Replica);
  5028. INCREMENT_CXTION_CHANGE_ORDER_COUNT(Replica, DeadCoe->Cxtion);
  5029. DeadCoe->JoinGuid = ChangeOrder->JoinGuid;
  5030. UNLOCK_CXTION_TABLE(Replica);
  5031. //
  5032. // Push this CO onto the head of the queue.
  5033. //
  5034. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Push to QHead");
  5035. WStatus = ChgOrdInsertProcQ(Replica, ChangeOrder, IPQ_HEAD | IPQ_TAKE_COREF |
  5036. IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5037. if (!WIN_SUCCESS(WStatus)) {
  5038. return REANIMATE_FAILED;
  5039. }
  5040. //
  5041. // If we just reinserted a base local co on to the process list then increment the
  5042. // LocalCoQueueCount for this replica.
  5043. //
  5044. if (LocalCo &&
  5045. !CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY |
  5046. CO_FLAG_CONTROL |
  5047. CO_FLAG_MOVEIN_GEN |
  5048. CO_FLAG_MORPH_GEN) &&
  5049. !RecoveryCo(ChangeOrder) &&
  5050. !COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_REANIMATION)) {
  5051. INC_LOCAL_CO_QUEUE_COUNT(Replica);
  5052. }
  5053. //
  5054. // Push the reanimate CO onto the head of the queue.
  5055. //
  5056. CHANGE_ORDER_TRACE(3, DeadCoe, "Co Push Parent to QHead");
  5057. WStatus = ChgOrdInsertProcQ(Replica, DeadCoe, IPQ_HEAD |
  5058. IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5059. if (!WIN_SUCCESS(WStatus)) {
  5060. return REANIMATE_FAILED;
  5061. }
  5062. //
  5063. // Now go give the parent CO we just pushed onto the QHead a spin.
  5064. // It could issue or it might generate another change order to
  5065. // reanimate its parent. The recursion will stop when we hit
  5066. // a live parent or the root of the replica tree.
  5067. //
  5068. return REANIMATE_SUCCESS;
  5069. }
  5070. BOOL
  5071. ChgOrdMakeDeleteCo(
  5072. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  5073. IN PREPLICA Replica,
  5074. IN PTHREAD_CTX ThreadCtx,
  5075. IN PIDTABLE_RECORD IDTableRec
  5076. )
  5077. /*++
  5078. Routine Description:
  5079. Given the IDTable record build a delete change order.
  5080. Make us the originator.
  5081. Push the supplied change order onto the head of the process queue after
  5082. doing some cleanup and then push the delete CO.
  5083. Arguments:
  5084. ChangeOrder -- The change order.
  5085. Replica -- The Replica struct.
  5086. ThreadCtx -- ptr to the thread context for DB
  5087. IDTableRec -- The ID Table record to build the delte change order from.
  5088. Return Value:
  5089. TRUE: Success
  5090. FALSE: Failed to insert CO on queue. Probably queue was run down.
  5091. --*/
  5092. {
  5093. #undef DEBSUB
  5094. #define DEBSUB "ChgOrdMakeDeleteCo:"
  5095. PCONFIG_TABLE_RECORD ConfigRecord;
  5096. PCHANGE_ORDER_ENTRY DelCoe;
  5097. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  5098. ULONG FStatus, WStatus;
  5099. BOOL LocalCo, DemandRefreshCo, MorphGenFollower;
  5100. FRS_ASSERT(!(IDTableRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY));
  5101. MorphGenFollower = COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER);
  5102. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  5103. DemandRefreshCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_DEMAND_REFRESH);
  5104. //
  5105. // There is a VV slot to clean up if this is the CO that caused the
  5106. // name morph conflict.
  5107. //
  5108. if (!LocalCo && !DemandRefreshCo) {
  5109. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  5110. }
  5111. //
  5112. // If this CO has been here once before then this is a recurrance of a
  5113. // name morph conflict so the MorphGenCo Leader must have aborted.
  5114. // Send the follower thru retry so it can try again later.
  5115. //
  5116. if (MorphGenFollower) {
  5117. CHANGE_ORDER_TRACE(3, ChangeOrder, "Recurrance of Morph Confl, RETRY");
  5118. return FALSE;
  5119. }
  5120. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  5121. //
  5122. // Build a delete change order entry for the IDtable record.
  5123. // This is a local CO that originates from this member.
  5124. //
  5125. DelCoe = ChgOrdMakeFromIDRecord(IDTableRec,
  5126. Replica,
  5127. CO_LOCATION_DELETE,
  5128. CO_FLAG_LOCATION_CMD,
  5129. NULL);
  5130. //
  5131. // Generate a new Volume Sequnce Number for the change order.
  5132. // But since it gets put on the front of the CO process queue it
  5133. // is probably out of order so set the flag to avoid screwing up
  5134. // dampening.
  5135. //
  5136. NEW_VSN(pVme, &DelCoe->Cmd.FrsVsn);
  5137. DelCoe->Cmd.OriginatorGuid = ConfigRecord->ReplicaVersionGuid;
  5138. //
  5139. // Use the event time from the winning CO as long as it is greater than
  5140. // the event time in the IDTable record. If we use current time then
  5141. // when the name morph conflict is detected at other nodes they would
  5142. // generate a Del Co with yet a different time and cause unnecessary
  5143. // replication of Delete Cos.
  5144. //
  5145. if (ChangeOrder->Cmd.EventTime.QuadPart > IDTableRec->EventTime) {
  5146. //
  5147. // Event time winning CO.
  5148. //
  5149. DelCoe->Cmd.EventTime.QuadPart = ChangeOrder->Cmd.EventTime.QuadPart;
  5150. } else {
  5151. //
  5152. // Event time from IDTable record biased by Reconcile EventTimeWindow+1
  5153. // The above had probs: Adding in RecEventTimeWindow may be causing desired
  5154. // reanimations to be rejected since the tombstone has a
  5155. // much larger event time so the version number check doesn't matter
  5156. // just make it +1 for now.
  5157. //
  5158. DelCoe->Cmd.EventTime.QuadPart = IDTableRec->EventTime +
  5159. /* RecEventTimeWindow */ + 1;
  5160. }
  5161. //
  5162. // Bump Version number to ensure the CO is accepted.
  5163. //
  5164. DelCoe->Cmd.FileVersionNumber = IDTableRec->VersionNumber + 1;
  5165. //
  5166. // Note: We wouldn't need to mark this CO as out of order if we
  5167. // resequenced all change order VSNs (for new COs only) as they
  5168. // were fetched off the process queue.
  5169. //
  5170. SET_CO_FLAG(DelCoe, CO_FLAG_LOCALCO |
  5171. CO_FLAG_MORPH_GEN |
  5172. CO_FLAG_OUT_OF_ORDER);
  5173. //
  5174. // Set the Jrnl Cxtion Guid and Cxtion ptr for this Local CO.
  5175. //
  5176. INIT_LOCALCO_CXTION_AND_COUNT(Replica, DelCoe);
  5177. //
  5178. // Tell downstream partners to skip the originator check on this Morph
  5179. // generated CO. See ChgOrdReconcile() for details.
  5180. //
  5181. SET_CO_FLAG(DelCoe, CO_FLAG_SKIP_ORIG_REC_CHK);
  5182. //
  5183. // Do the change order cleanup but don't free the CO.
  5184. //
  5185. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO);
  5186. FStatus = ChgOrdIssueCleanup(ThreadCtx, Replica, ChangeOrder, 0);
  5187. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  5188. //
  5189. // Mark the base CO as a MorphGenFollower so it gets sent thru retry
  5190. // if the leader gets aborted.
  5191. //
  5192. SET_COE_FLAG(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER);
  5193. //
  5194. // Push this CO onto the head of the queue.
  5195. //
  5196. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Push MORPH_GEN_FOLLOWER - Winner to QHead");
  5197. WStatus = ChgOrdInsertProcQ(Replica, ChangeOrder, IPQ_HEAD | IPQ_TAKE_COREF |
  5198. IPQ_MG_LOOP_CHK | IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5199. if (!WIN_SUCCESS(WStatus)) {
  5200. return FALSE;
  5201. }
  5202. //
  5203. // Push the IDTable Delete Co onto the head of the queue.
  5204. //
  5205. CHANGE_ORDER_TRACE(3, DelCoe, "Co Push MORPH_GEN_LEADER - IDT loser to QHead");
  5206. WStatus = ChgOrdInsertProcQ(Replica, DelCoe, IPQ_HEAD |
  5207. IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5208. if (!WIN_SUCCESS(WStatus)) {
  5209. return FALSE;
  5210. }
  5211. //
  5212. // Now go give the delete CO we just pushed onto the QHead a spin.
  5213. //
  5214. return TRUE;
  5215. }
  5216. BOOL
  5217. ChgOrdMakeRenameCo(
  5218. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  5219. IN PREPLICA Replica,
  5220. IN PTHREAD_CTX ThreadCtx,
  5221. IN PIDTABLE_RECORD IDTableRec
  5222. )
  5223. /*++
  5224. Routine Description:
  5225. Given the IDTable record build a rename change order.
  5226. Make us the originator.
  5227. Push the supplied change order onto the head of the process queue after
  5228. doing some cleanup and then push the rename CO.
  5229. Arguments:
  5230. ChangeOrder -- The change order.
  5231. Replica -- The Replica struct.
  5232. ThreadCtx -- ptr to the thread context for DB
  5233. IDTableRec -- The ID Table record to build the delte change order from.
  5234. Return Value:
  5235. TRUE: Success
  5236. FALSE: Failed to insert CO on queue. Probably queue was run down.
  5237. --*/
  5238. {
  5239. #undef DEBSUB
  5240. #define DEBSUB "ChgOrdMakeRenameCo:"
  5241. PCONFIG_TABLE_RECORD ConfigRecord;
  5242. PCHANGE_ORDER_ENTRY RenameCoe;
  5243. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  5244. ULONG FStatus, WStatus;
  5245. BOOL LocalCo, DemandRefreshCo, MorphGenFollower;
  5246. ULONG NameLen;
  5247. FRS_ASSERT(IDTableRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  5248. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  5249. DemandRefreshCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_DEMAND_REFRESH);
  5250. MorphGenFollower = COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER);
  5251. //
  5252. // There is a VV slot to clean up if this is the CO that caused the
  5253. // name morph conflict.
  5254. //
  5255. if (!LocalCo && !DemandRefreshCo) {
  5256. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  5257. }
  5258. //
  5259. // If this CO has been here once before then this is a recurrance of a
  5260. // name morph conflict so the MorphGenCo Leader must have aborted.
  5261. // Send the follower thru retry so it can try again later.
  5262. //
  5263. if (MorphGenFollower) {
  5264. CHANGE_ORDER_TRACE(3, ChangeOrder, "Recurrance of Morph Confl, RETRY");
  5265. return FALSE;
  5266. }
  5267. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  5268. //
  5269. // Build a rename change order entry for the IDtable record.
  5270. // This is a local CO that originates from this member.
  5271. //
  5272. RenameCoe = ChgOrdMakeFromIDRecord(IDTableRec,
  5273. Replica,
  5274. CO_LOCATION_NO_CMD,
  5275. CO_FLAG_CONTENT_CMD,
  5276. NULL);
  5277. RenameCoe->Cmd.ContentCmd = USN_REASON_RENAME_NEW_NAME;
  5278. //
  5279. // Suffix "_NTFRS_<tic count in hex>" to the filename.
  5280. //
  5281. DPRINT1(4, "++ %ws will be morphed\n", RenameCoe->Cmd.FileName);
  5282. NameLen = wcslen(RenameCoe->Cmd.FileName);
  5283. if (NameLen + SIZEOF_RENAME_SUFFIX > (MAX_PATH - 1)) {
  5284. NameLen -= ((NameLen + SIZEOF_RENAME_SUFFIX) - (MAX_PATH - 1));
  5285. }
  5286. swprintf(&RenameCoe->Cmd.FileName[NameLen], L"_NTFRS_%08x", GetTickCount());
  5287. RenameCoe->Cmd.FileNameLength = wcslen(RenameCoe->Cmd.FileName) * sizeof(WCHAR);
  5288. RenameCoe->UFileName.Length = RenameCoe->Cmd.FileNameLength;
  5289. DPRINT1(4, "++ Morphing to %ws\n", RenameCoe->Cmd.FileName);
  5290. //
  5291. // Generate a new Volume Sequnce Number for the change order.
  5292. // But since it gets put on the front of the CO process queue it
  5293. // is probably out of order so set the flag to avoid screwing up
  5294. // dampening.
  5295. //
  5296. NEW_VSN(pVme, &RenameCoe->Cmd.FrsVsn);
  5297. RenameCoe->Cmd.OriginatorGuid = ConfigRecord->ReplicaVersionGuid;
  5298. //
  5299. // Use the event time from the winning CO as long as it is greater than
  5300. // the event time in the IDTable record. If we use current time then
  5301. // when the name morph conflict is detected at other nodes they would
  5302. // generate a rename Co with yet a different time and cause unnecessary
  5303. // replication of rename Cos.
  5304. //
  5305. if (ChangeOrder->Cmd.EventTime.QuadPart > IDTableRec->EventTime) {
  5306. //
  5307. // Event time winning CO.
  5308. //
  5309. RenameCoe->Cmd.EventTime.QuadPart = ChangeOrder->Cmd.EventTime.QuadPart;
  5310. } else {
  5311. //
  5312. // Event time from IDTable record biased by Reconcile EventTimeWindow+1
  5313. //
  5314. RenameCoe->Cmd.EventTime.QuadPart = IDTableRec->EventTime +
  5315. /* RecEventTimeWindow */ + 1;
  5316. //GetSystemTimeAsFileTime((PFILETIME)&RenameCoe->Cmd.EventTime.QuadPart);
  5317. }
  5318. //
  5319. // Bump Version number to ensure the CO is accepted.
  5320. //
  5321. RenameCoe->Cmd.FileVersionNumber = IDTableRec->VersionNumber + 1;
  5322. //
  5323. // Note: We wouldn't need to mark this CO as out of order if we
  5324. // resequenced all change order VSNs (for new COs only) as they
  5325. // were fetched off the process queue.
  5326. //
  5327. SET_CO_FLAG(RenameCoe, CO_FLAG_LOCALCO |
  5328. CO_FLAG_MORPH_GEN |
  5329. CO_FLAG_OUT_OF_ORDER);
  5330. //
  5331. // Set the Jrnl Cxtion Guid and Cxtion ptr for this Local CO.
  5332. //
  5333. INIT_LOCALCO_CXTION_AND_COUNT(Replica, RenameCoe);
  5334. //
  5335. // Tell downstream partners to skip the originator check on this Morph
  5336. // generated CO. See ChgOrdReconcile() for details.
  5337. //
  5338. SET_CO_FLAG(RenameCoe, CO_FLAG_SKIP_ORIG_REC_CHK);
  5339. //
  5340. // Do the change order cleanup but don't free the CO.
  5341. //
  5342. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO);
  5343. FStatus = ChgOrdIssueCleanup(ThreadCtx, Replica, ChangeOrder, 0);
  5344. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  5345. //
  5346. // Mark the base CO as a MorphGenFollower so it gets sent thru retry
  5347. // if the leader gets aborted.
  5348. //
  5349. SET_COE_FLAG(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER);
  5350. //
  5351. // Push this CO onto the head of the queue.
  5352. //
  5353. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Push MORPH_GEN_FOLLOWER - Winner to QHead");
  5354. WStatus = ChgOrdInsertProcQ(Replica, ChangeOrder, IPQ_HEAD | IPQ_TAKE_COREF |
  5355. IPQ_MG_LOOP_CHK | IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5356. if (!WIN_SUCCESS(WStatus)) {
  5357. return FALSE;
  5358. }
  5359. //
  5360. // Push the IDTable Delete Co onto the head of the queue.
  5361. //
  5362. CHANGE_ORDER_TRACE(3, RenameCoe, "Co Push MORPH_GEN_LEADER - IDT loser to QHead");
  5363. WStatus = ChgOrdInsertProcQ(Replica, RenameCoe, IPQ_HEAD |
  5364. IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5365. if (!WIN_SUCCESS(WStatus)) {
  5366. return FALSE;
  5367. }
  5368. //
  5369. // Now go give the delete CO we just pushed onto the QHead a spin.
  5370. //
  5371. return TRUE;
  5372. }
  5373. VOID
  5374. ChgOrdInjectControlCo(
  5375. IN PREPLICA Replica,
  5376. IN PCXTION Cxtion,
  5377. IN ULONG ContentCmd
  5378. )
  5379. /*++
  5380. Routine Description:
  5381. Generate a directed control change order for Cxtion.
  5382. Arguments:
  5383. Replica - Replica for cxtion
  5384. Cxtion - outbound cxtion
  5385. ContentCmd - Type of control co
  5386. Thread Return Value:
  5387. None.
  5388. --*/
  5389. {
  5390. #undef DEBSUB
  5391. #define DEBSUB "ChgOrdInjectControlCo:"
  5392. PCHANGE_ORDER_ENTRY Coe;
  5393. PCHANGE_ORDER_COMMAND Coc;
  5394. //
  5395. // Change order entry
  5396. //
  5397. Coe = FrsAllocType(CHANGE_ORDER_ENTRY_TYPE);
  5398. INCREMENT_CHANGE_ORDER_REF_COUNT(Coe);
  5399. //
  5400. // Jet context for outlog insertion
  5401. //
  5402. Coe->RtCtx = FrsAllocTypeSize(REPLICA_THREAD_TYPE,
  5403. FLAG_FRSALLOC_NO_ALLOC_TBL_CTX);
  5404. FrsRtlInsertTailList(&Replica->ReplicaCtxListHead,
  5405. &Coe->RtCtx->ReplicaCtxList);
  5406. //
  5407. // Change order command
  5408. //
  5409. Coc = &Coe->Cmd;
  5410. //
  5411. // Original and New Replica are the same (not a rename)
  5412. //
  5413. Coe->OriginalReplica = Replica;
  5414. Coe->NewReplica = Replica;
  5415. Coc->OriginalReplicaNum = ReplicaAddrToId(Coe->OriginalReplica);
  5416. Coc->NewReplicaNum = ReplicaAddrToId(Coe->NewReplica);
  5417. //
  5418. // Assign a change order guid
  5419. //
  5420. FrsUuidCreate(&Coc->ChangeOrderGuid);
  5421. //
  5422. // Phoney File guid
  5423. //
  5424. FrsUuidCreate(&Coc->FileGuid);
  5425. if (ContentCmd == FCN_CO_ABNORMAL_VVJOIN_TERM) {
  5426. wcscpy(Coe->Cmd.FileName, L"VVJoinTerminateAbnormal"); // for tracing.
  5427. } else if (ContentCmd == FCN_CO_NORMAL_VVJOIN_TERM) {
  5428. wcscpy(Coe->Cmd.FileName, L"VVJoinTerminateNormal"); // for tracing.
  5429. } else if (ContentCmd == FCN_CO_END_OF_JOIN) {
  5430. wcscpy(Coe->Cmd.FileName, L"EndOfJoin"); // for tracing.
  5431. } else if (ContentCmd == FCN_CO_END_OF_OPTIMIZED_VVJOIN) {
  5432. wcscpy(Coe->Cmd.FileName, L"EndOfOptVVJoin"); // for tracing.
  5433. } else {
  5434. wcscpy(Coe->Cmd.FileName, L"UnknownControl"); // for tracing.
  5435. }
  5436. //
  5437. // Cxtion's guid (identifies the cxtion)
  5438. //
  5439. Coc->CxtionGuid = *Cxtion->Name->Guid;
  5440. //
  5441. // The location command is either create or delete
  5442. //
  5443. Coc->ContentCmd = ContentCmd;
  5444. //
  5445. // Control command co
  5446. //
  5447. SET_CO_FLAG(Coe, CO_FLAG_CONTROL);
  5448. //
  5449. // Directed at one cxtion
  5450. //
  5451. SET_CO_FLAG(Coe, CO_FLAG_DIRECTED_CO);
  5452. //
  5453. // Mascarade as a local change order since the staging file
  5454. // is created from the local version of the file and isn't
  5455. // from a inbound partner.
  5456. //
  5457. SET_CO_FLAG(Coe, CO_FLAG_LOCALCO);
  5458. //
  5459. // Refresh change orders will not be propagated by our outbound
  5460. // partner to its outbound partners.
  5461. //
  5462. // Note that only the termination change order is set to refresh.
  5463. // While we want the vvjoin change orders to propagate as regular
  5464. // change orders, we do not want the termination co to propagate.
  5465. //
  5466. SET_CO_FLAG(Coe, CO_FLAG_REFRESH);
  5467. //
  5468. // By "out of order" we mean that the VSN on this change order
  5469. // should not be used to update the version vector because there
  5470. // may be other files or dirs with lower VSNs that will be sent
  5471. // later. We wouldn't want our partner to dampen them.
  5472. //
  5473. SET_CO_FLAG(Coe, CO_FLAG_OUT_OF_ORDER);
  5474. //
  5475. // Some control Cos aren't valid across joins.
  5476. //
  5477. Coc->EventTime.QuadPart = Cxtion->LastJoinTime;
  5478. //
  5479. // Just a guess
  5480. //
  5481. SET_CHANGE_ORDER_STATE(Coe, IBCO_OUTBOUND_REQUEST);
  5482. //
  5483. // The DB server is responsible for updating the outbound log.
  5484. //
  5485. DbsPrepareCmdPkt(NULL, // CmdPkt,
  5486. Replica, // Replica,
  5487. CMD_DBS_INJECT_OUTBOUND_CO, // CmdRequest,
  5488. NULL, // TableCtx,
  5489. Coe, // CallContext,
  5490. 0, // TableType,
  5491. 0, // AccessRequest,
  5492. 0, // IndexType,
  5493. NULL, // KeyValue,
  5494. 0, // KeyValueLength,
  5495. TRUE); // Submit
  5496. }
  5497. BOOL
  5498. ChgOrdConvertCo(
  5499. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  5500. IN PREPLICA Replica,
  5501. IN PTHREAD_CTX ThreadCtx,
  5502. IN ULONG LocationCmd
  5503. )
  5504. /*++
  5505. Routine Description:
  5506. Create a new CO of the specified type using the current CO.
  5507. Make us the originator.
  5508. Push the current CO back onto the queue.
  5509. Push the new change order onto the head of the process
  5510. queue after doing some cleanup.
  5511. The new CO is a local CO while the original CO may be a remote CO that
  5512. needs to ack its inbound partner.
  5513. Convert is generating a new change order (rename or delete) for an
  5514. incoming change order for a NEW FILE. As such there is no existing file
  5515. whose name needs to be removed, unlike ChgOrdMakeRenameCo() or
  5516. ChgOrdMakeDeleteCo() which produce change orders from an existing IDTable
  5517. entry.
  5518. Arguments:
  5519. ChangeOrder -- The change order.
  5520. Replica -- The Replica struct.
  5521. ThreadCtx -- ptr to the thread context for DB
  5522. LocationCmd -- Tells us what kind of CO to build.
  5523. Current choices are a rename CO or a delete CO.
  5524. Return Value:
  5525. TRUE: Success
  5526. FALSE: Failed to insert CO on queue. Probably queue was run down.
  5527. --*/
  5528. {
  5529. #undef DEBSUB
  5530. #define DEBSUB "ChgOrdConvertCo:"
  5531. PCONFIG_TABLE_RECORD ConfigRecord;
  5532. PCHANGE_ORDER_ENTRY CvtCoe;
  5533. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  5534. ULONG FStatus, WStatus, WStatus1;
  5535. BOOL LocalCo, DemandRefreshCo, MorphGenFollower;
  5536. ULONG NameLen;
  5537. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  5538. DemandRefreshCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_DEMAND_REFRESH);
  5539. MorphGenFollower = COE_FLAG_ON(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER);
  5540. //
  5541. // There is a VV slot to clean up if this is the CO that caused the
  5542. // name morph conflict.
  5543. //
  5544. if (!LocalCo && !DemandRefreshCo) {
  5545. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  5546. }
  5547. //
  5548. // If this CO has been here once before then this is a recurrance of a
  5549. // name morph conflict so the MorphGenCo Leader must have aborted.
  5550. // Send the follower thru retry so it can try again later.
  5551. //
  5552. if (MorphGenFollower) {
  5553. CHANGE_ORDER_TRACE(3, ChangeOrder, "Recurrance of Morph Confl, RETRY");
  5554. return FALSE;
  5555. }
  5556. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  5557. //
  5558. // Build a new CO out of this change order entry.
  5559. // This is a local CO that originates from this member.
  5560. //
  5561. // Allocate a change order entry with room for the filename and copy
  5562. // the change order + Filename into it. Init the ref count to one
  5563. // since the CO is going on the process queue.
  5564. //
  5565. CvtCoe = FrsAllocType(CHANGE_ORDER_ENTRY_TYPE);
  5566. CopyMemory(&CvtCoe->Cmd, &ChangeOrder->Cmd, sizeof(CHANGE_ORDER_COMMAND));
  5567. CvtCoe->Cmd.Extension = NULL;
  5568. CvtCoe->UFileName.Length = CvtCoe->Cmd.FileNameLength;
  5569. //
  5570. // Copy the Change Order Extension if provided.
  5571. //
  5572. if ((ChangeOrder->Cmd.Extension != NULL) &&
  5573. (ChangeOrder->Cmd.Extension->FieldSize > 0)) {
  5574. CvtCoe->Cmd.Extension = FrsAlloc(ChangeOrder->Cmd.Extension->FieldSize);
  5575. CopyMemory(CvtCoe->Cmd.Extension, ChangeOrder->Cmd.Extension,
  5576. ChangeOrder->Cmd.Extension->FieldSize);
  5577. }
  5578. //
  5579. // Normally the change order record extension is allocated for local COs
  5580. // when the cocmd is inserted into the inbound log (by the call to
  5581. // DbsAllocRecordStorage()). But MorphGenCos don't get inserted into
  5582. // the inlog so we need to do it here.
  5583. //
  5584. if (ChangeOrder->Cmd.Extension == NULL){
  5585. CvtCoe->Cmd.Extension = FrsAlloc(sizeof(CHANGE_ORDER_RECORD_EXTENSION));
  5586. DbsDataInitCocExtension(CvtCoe->Cmd.Extension);
  5587. DPRINT(4, "Allocating initial Coc Extension for morphco\n");
  5588. }
  5589. //
  5590. // Assign a change order guid and zero the connection GUID since this
  5591. // is a local CO.
  5592. //
  5593. FrsUuidCreate(&CvtCoe->Cmd.ChangeOrderGuid);
  5594. ZeroMemory(&CvtCoe->Cmd.CxtionGuid, sizeof(GUID));
  5595. CvtCoe->HashEntryHeader.ReferenceCount = 0;
  5596. INCREMENT_CHANGE_ORDER_REF_COUNT(CvtCoe); // for tracking
  5597. //
  5598. // File's fid and parent fid
  5599. //
  5600. CvtCoe->FileReferenceNumber = ChangeOrder->FileReferenceNumber;
  5601. CvtCoe->ParentFileReferenceNumber = ChangeOrder->ParentFileReferenceNumber;
  5602. //
  5603. // Original parent and New parent are the same (not a rename)
  5604. //
  5605. CvtCoe->OriginalParentFid = ChangeOrder->OriginalParentFid;
  5606. CvtCoe->NewParentFid = ChangeOrder->NewParentFid;
  5607. //
  5608. // Copy New and orig Replica. (CO Numbers got copied with CO above).
  5609. //
  5610. CvtCoe->OriginalReplica = ChangeOrder->OriginalReplica;
  5611. CvtCoe->NewReplica = ChangeOrder->NewReplica;
  5612. //
  5613. // File's attributes
  5614. //
  5615. CvtCoe->FileAttributes = ChangeOrder->FileAttributes;
  5616. //
  5617. // Create and write times
  5618. //
  5619. CvtCoe->FileCreateTime = ChangeOrder->FileCreateTime;
  5620. CvtCoe->FileWriteTime = ChangeOrder->FileWriteTime;
  5621. //
  5622. // The sequence number is zero initially. It may get a value when
  5623. // the CO is inserted into an inbound or outbound log.
  5624. //
  5625. CvtCoe->Cmd.SequenceNumber = 0;
  5626. CvtCoe->Cmd.PartnerAckSeqNumber = 0;
  5627. //
  5628. // Use the event time from the supplied CO biased by
  5629. // Reconcile EventTimeWindow+1. If we use current time then
  5630. // when the name morph conflict is detected at other nodes they would
  5631. // generate a Del or Rename Co with yet a different time and cause
  5632. // unnecessary replication of Del or rename Cos.
  5633. //
  5634. CvtCoe->Cmd.EventTime.QuadPart = ChangeOrder->Cmd.EventTime.QuadPart +
  5635. /* RecEventTimeWindow */ + 1;
  5636. // GetSystemTimeAsFileTime((PFILETIME)&CvtCoe->Cmd.EventTime.QuadPart);
  5637. //
  5638. // FileVersionNumber bumped by one so this CO will not get rejected.
  5639. //
  5640. CvtCoe->Cmd.FileVersionNumber += 1;
  5641. //
  5642. // File's USN
  5643. //
  5644. CvtCoe->Cmd.FileUsn = 0;
  5645. CvtCoe->Cmd.JrnlUsn = 0;
  5646. CvtCoe->Cmd.JrnlFirstUsn = 0;
  5647. //
  5648. // Generate a new Volume Sequnce Number for the change order.
  5649. // But since it gets put on the front of the CO process queue it
  5650. // is probably out of order so set the flag to avoid screwing up
  5651. // dampening.
  5652. //
  5653. NEW_VSN(pVme, &CvtCoe->Cmd.FrsVsn);
  5654. CvtCoe->Cmd.OriginatorGuid = ConfigRecord->ReplicaVersionGuid;
  5655. //
  5656. // Note: We wouldn't need to mark this CO as out of order if we
  5657. // resequenced all change order VSNs (for new COs only) as they
  5658. // were fetched off the process queue.
  5659. //
  5660. CvtCoe->Cmd.Flags = 0;
  5661. CvtCoe->Cmd.IFlags = 0;
  5662. SET_CO_FLAG(CvtCoe, CO_FLAG_LOCALCO |
  5663. CO_FLAG_MORPH_GEN |
  5664. CO_FLAG_OUT_OF_ORDER);
  5665. //
  5666. // Set the Jrnl Cxtion Guid and Cxtion ptr for this Local CO.
  5667. //
  5668. INIT_LOCALCO_CXTION_AND_COUNT(Replica, CvtCoe);
  5669. //
  5670. // Tell downstream partners to skip the originator check on this Morph
  5671. // generated CO. See ChgOrdReconcile() for details.
  5672. //
  5673. SET_CO_FLAG(CvtCoe, CO_FLAG_SKIP_ORIG_REC_CHK);
  5674. //
  5675. // Delete, Rename
  5676. //
  5677. SET_CO_LOCATION_CMD(CvtCoe->Cmd, Command, LocationCmd);
  5678. if (LocationCmd == CO_LOCATION_NO_CMD) {
  5679. //
  5680. // We're giving this object a new name. Init the new name into
  5681. // both the original and the new CO.
  5682. //
  5683. CvtCoe->Cmd.ContentCmd = USN_REASON_RENAME_NEW_NAME;
  5684. SET_CO_FLAG(CvtCoe, CO_FLAG_CONTENT_CMD);
  5685. ChangeOrder->Cmd.ContentCmd |= USN_REASON_RENAME_NEW_NAME;
  5686. SET_CO_FLAG(ChangeOrder, CO_FLAG_CONTENT_CMD);
  5687. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN_LEADER)) {
  5688. //
  5689. // This CO is marked as a MorphGenLeader. This means it has been
  5690. // through here before and so it already has the Morphed dir
  5691. // name in the CO. Use it as is in the rename CO.
  5692. // It was already copied into the CvtCoe above.
  5693. //
  5694. NOTHING;
  5695. } else {
  5696. //
  5697. // Not a MorphGenLeader so build a new name.
  5698. // Suffix "_NTFRS_<tic count in hex>" to the filename.
  5699. //
  5700. DPRINT1(4, "++ %ws will be morphed\n", CvtCoe->Cmd.FileName);
  5701. NameLen = wcslen(CvtCoe->Cmd.FileName);
  5702. if (NameLen + SIZEOF_RENAME_SUFFIX > (MAX_PATH - 1)) {
  5703. NameLen -= ((NameLen + SIZEOF_RENAME_SUFFIX) - (MAX_PATH - 1));
  5704. }
  5705. swprintf(&CvtCoe->Cmd.FileName[NameLen], L"_NTFRS_%08x", GetTickCount());
  5706. CvtCoe->Cmd.FileNameLength = wcslen(CvtCoe->Cmd.FileName) * sizeof(WCHAR);
  5707. CvtCoe->UFileName.Length = CvtCoe->Cmd.FileNameLength;
  5708. DPRINT1(4, "++ Morphing to %ws\n", CvtCoe->Cmd.FileName);
  5709. //
  5710. // MAYBE: Though it is unlikely, still need to verify that the
  5711. // friggen name is not in use.
  5712. //
  5713. CopyMemory(ChangeOrder->Cmd.FileName, CvtCoe->Cmd.FileName,
  5714. CvtCoe->Cmd.FileNameLength + sizeof(WCHAR));
  5715. ChangeOrder->Cmd.FileNameLength = CvtCoe->Cmd.FileNameLength;
  5716. ChangeOrder->UFileName.Length = ChangeOrder->Cmd.FileNameLength;
  5717. }
  5718. } else
  5719. if (LocationCmd == CO_LOCATION_DELETE) {
  5720. //
  5721. // The old code was setting USN_REASON_FILE_DELETE and
  5722. // CO_FLAG_LOCATION_CMD. This is incorrect since later code
  5723. // treats the co as a CO_LOCATION_CREATE. The fix was to
  5724. // set both the content and location to specify delete.
  5725. //
  5726. CvtCoe->Cmd.ContentCmd = USN_REASON_FILE_DELETE;
  5727. SET_CO_FLAG(CvtCoe, CO_FLAG_CONTENT_CMD);
  5728. SET_CO_LOCATION_CMD(CvtCoe->Cmd, Command, CO_LOCATION_DELETE);
  5729. SET_CO_LOCATION_CMD(CvtCoe->Cmd, DirOrFile,
  5730. (CvtCoe->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
  5731. CO_LOCATION_DIR : CO_LOCATION_FILE);
  5732. SET_CO_FLAG(CvtCoe, CO_FLAG_LOCATION_CMD);
  5733. //
  5734. // If the FID is zero then load a fake value for it since this is
  5735. // an IDTable index and there is no actual file if we are generating
  5736. // a delete CO since it will be issued first.
  5737. //
  5738. if (CvtCoe->FileReferenceNumber == ZERO_FID) {
  5739. FrsInterlockedIncrement64(CvtCoe->FileReferenceNumber,
  5740. GlobSeqNum,
  5741. &GlobSeqNumLock);
  5742. }
  5743. } else {
  5744. DPRINT1(0, "++ ERROR: Invalid Location Cmd - %d\n", LocationCmd);
  5745. FRS_ASSERT(!"Invalid Location Cmd");
  5746. }
  5747. SET_CHANGE_ORDER_STATE(CvtCoe, IBCO_INITIALIZING);
  5748. if (LocationCmd == CO_LOCATION_NO_CMD) {
  5749. //
  5750. // Push the new Co onto the head of the queue. No aging delay.
  5751. // Do this first in the case of a rename because we want the
  5752. // create CO to issue first to create the object.
  5753. // We then follow this with a Local rename Co (using the same name)
  5754. // so we can ensure the rename will propagate everywhere.
  5755. //
  5756. // This is necessary because a down stream partner may not encounter
  5757. // the name morph conflict because the ultimate name winner may not
  5758. // have arrived yet or a delete could have occurred on our winner
  5759. // follwed by the arrival of this CO via another connection path to
  5760. // the downstream partner. In either case we must prop a rename to
  5761. // ensure the name is the same everywhere.
  5762. //
  5763. //
  5764. // Mark the Rename CO as a MorphGenFollower so it gets rejected if the
  5765. // MorphGenLeader fails to create the file.
  5766. //
  5767. SET_COE_FLAG(CvtCoe, COE_FLAG_MORPH_GEN_FOLLOWER);
  5768. CHANGE_ORDER_TRACE(3, CvtCoe, "Co Push MORPH_GEN_FOLLOWER to QHead");
  5769. WStatus1 = ChgOrdInsertProcQ(Replica, CvtCoe, IPQ_HEAD |
  5770. IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5771. }
  5772. //
  5773. // Do the change order cleanup but don't free the CO.
  5774. //
  5775. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO);
  5776. FStatus = ChgOrdIssueCleanup(ThreadCtx, Replica, ChangeOrder, 0);
  5777. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  5778. if (LocationCmd != CO_LOCATION_NO_CMD) {
  5779. //
  5780. // Mark the base CO as a MorphGenFollower so it gets sent thru retry
  5781. // if the leader gets aborted. Only set this flag if this CO is the
  5782. // follower. In the CO_LOCATION_NO_CMD case the Morph Gen Co was
  5783. // pushed on the queue above so this CO is the Leader.
  5784. //
  5785. SET_COE_FLAG(ChangeOrder, COE_FLAG_MORPH_GEN_FOLLOWER);
  5786. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Push MORPH_GEN_FOLLOWER to QHead");
  5787. } else {
  5788. //
  5789. // Mark the base CO as a MorphGenLeader so if it gets reissued later
  5790. // we know to refabricate the MorphGenFollower rename CO.
  5791. //
  5792. SET_CO_FLAG(ChangeOrder, CO_FLAG_MORPH_GEN_LEADER);
  5793. //
  5794. // Set volatile flag indicating we (the MorphGenLeader) has fabricated
  5795. // the MorphGenFollower and pushed it onto the process queue. This
  5796. // prevents us from doing it again when we leave here and restart
  5797. // processing on the MorphGenLeader CO.
  5798. //
  5799. SET_COE_FLAG(ChangeOrder, COE_FLAG_MG_FOLLOWER_MADE);
  5800. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Push MORPH_GEN_LEADER to QHead");
  5801. }
  5802. //
  5803. // Push this CO onto the head of the queue.
  5804. //
  5805. WStatus = ChgOrdInsertProcQ(Replica, ChangeOrder, IPQ_HEAD | IPQ_TAKE_COREF |
  5806. IPQ_MG_LOOP_CHK | IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5807. if (!WIN_SUCCESS(WStatus)) {
  5808. return FALSE;
  5809. }
  5810. if (LocationCmd != CO_LOCATION_NO_CMD) {
  5811. //
  5812. // Push the new Co onto the head of the queue. No aging delay.
  5813. // Do this second in the case of the DeleteCo since we want to
  5814. // issue the Delete first so we can prevent the subsequent name
  5815. // conflict when the original change order issues. The delete co
  5816. // issues as a local CO so it propagates everywhere. The original
  5817. // CO that follows behind it will hit the tombstone created by the
  5818. // delete and be aborted.
  5819. //
  5820. CHANGE_ORDER_TRACE(3, CvtCoe, "Co Push MORPH_GEN_LEADER to QHead");
  5821. WStatus1 = ChgOrdInsertProcQ(Replica, CvtCoe, IPQ_HEAD |
  5822. IPQ_DEREF_CXTION_IF_ERR | IPQ_ASSERT_IF_ERR);
  5823. }
  5824. if (!WIN_SUCCESS(WStatus1)) {
  5825. return FALSE;
  5826. }
  5827. //
  5828. // Now go give the delete CO we just pushed onto the QHead a spin.
  5829. //
  5830. return TRUE;
  5831. }
  5832. BOOL
  5833. ChgOrdReserve(
  5834. IN PIDTABLE_RECORD IDTableRec,
  5835. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  5836. IN PREPLICA Replica
  5837. )
  5838. /*++
  5839. Routine Description:
  5840. Reserve resources for this change order so that subsequent change orders
  5841. will properly interlock with this change order.
  5842. Arguments:
  5843. IDTableRec -- The IDTable record for this file / dir.
  5844. ChangeOrder-- The change order.
  5845. Replica -- The Replica struct.
  5846. Return Value:
  5847. TRUE if reservation succeeded.
  5848. --*/
  5849. {
  5850. #undef DEBSUB
  5851. #define DEBSUB "ChgOrdReserve:"
  5852. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  5853. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  5854. PQHASH_ENTRY QHashEntry;
  5855. UNICODE_STRING UnicodeStr;
  5856. ULONG GStatus;
  5857. GUID *NParentGuid;
  5858. GUID *OldParentGuid;
  5859. CHAR GuidStr[GUID_CHAR_LEN];
  5860. //
  5861. // Make entry in the Active inbound change order hash table. This is
  5862. // indexed by the file Guid in the change order so we can interlock against
  5863. // activity on files that are being reanimated. Their IDTable entries are
  5864. // marked deleted but the FID is not updated until the CO completes
  5865. // successfully so an incoming duplicate CO on a deleted file could get
  5866. // thru if the index was a FID.
  5867. //
  5868. GStatus = GhtInsert(pVme->ActiveInboundChangeOrderTable,
  5869. ChangeOrder,
  5870. TRUE,
  5871. FALSE);
  5872. if (GStatus != GHT_STATUS_SUCCESS) {
  5873. DPRINT1(0, "++ ERROR - ActiveInboundChangeOrderTable GhtInsert Failed: %d\n", GStatus);
  5874. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Rejected - AIBCO Insert Failed");
  5875. FRS_PRINT_TYPE(0, ChangeOrder);
  5876. FRS_ASSERT(!"AIBCO Insert Failed");
  5877. ChgOrdReject(ChangeOrder, Replica);
  5878. return FALSE;
  5879. }
  5880. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_AIBCO);
  5881. //
  5882. // Make entry in the Name conflict table if a file name is being
  5883. // removed so this CO interlocks with a subsequent CO that wants
  5884. // to bring the name back.
  5885. //
  5886. // Don't update the name conflict table if this is a delete co
  5887. // for a deleted id table entry. Deleted id table entries are not
  5888. // factored into the name morph conflict detection and hence can
  5889. // lead to duplicate entries in the name conflict table.
  5890. //
  5891. // NameConflictTable Entry: An entry in the NameConflictTable
  5892. // contains a reference count of the number of active change
  5893. // orders that hash to that entry and a Flags word that is set
  5894. // to COE_FLAG_VOL_COLIST_BLOCKED if the process queue for this
  5895. // volume is idled while waiting on the active change orders for
  5896. // this entry to retire. The queue is idled when the entry at
  5897. // the head of the queue hashes to this entry and so may
  5898. // have a conflicting name (hence the name, "name conflict table").
  5899. //
  5900. // The NameConflictTable can give false positives. But this is okay
  5901. // because a false positive is rare and will only idle the process
  5902. // queue until the active change order retires. Getting rid
  5903. // of the rare false positives would degrade performance. The
  5904. // false positives that happen when inserting an entry into the
  5905. // NameConflictTable are handled by using the QData field in
  5906. // the QHashEntry as a the reference count.
  5907. //
  5908. // But, you ask, how can there be multiple active cos hashing
  5909. // to this entry if the process queue is idled when a conflict
  5910. // is detected? Easy, I say, because the filename in the co
  5911. // is used to detect collisions while the filename in the idtable
  5912. // is used to reserve the qhash entry. Why? Well, the name morph
  5913. // code handles the case of a co's name colliding with an
  5914. // idtable entry. But that code wouldn't work if active change
  5915. // orders were constantly changing the idtable. So, the
  5916. // NameConflictTable synchronizes the namespace amoung
  5917. // active change orders so that the name morph code can work
  5918. // against a static namespace.
  5919. //
  5920. if (DOES_CO_DO_SIMPLE_RENAME(CoCmd) ||
  5921. (DOES_CO_REMOVE_FILE_NAME(CoCmd) &&
  5922. !IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED))) {
  5923. //
  5924. // The IDTable has the current location of the file. Note that in the
  5925. // case of a remote CO the file may have already been moved either by a
  5926. // local action or the processing of a remote CO that arrived earlier
  5927. // than this one. Save the hash value in the change order because when
  5928. // it retires the the data is gone since the IDTable has been updated.
  5929. //
  5930. RtlInitUnicodeString(&UnicodeStr,
  5931. IDTableRec->FileName);
  5932. ChgOrdCalcHashGuidAndName(&UnicodeStr,
  5933. &IDTableRec->ParentGuid,
  5934. &ChangeOrder->NameConflictHashValue);
  5935. QHashAcquireLock(Replica->NameConflictTable);
  5936. QHashEntry = QHashInsertLock(Replica->NameConflictTable,
  5937. &ChangeOrder->NameConflictHashValue,
  5938. NULL,
  5939. (ULONG_PTR) 0);
  5940. QHashEntry->QData++;
  5941. QHashReleaseLock(Replica->NameConflictTable);
  5942. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_NC_TABLE);
  5943. } else {
  5944. ChangeOrder->NameConflictHashValue = QUADZERO;
  5945. }
  5946. //
  5947. // Update the count in an existing parent entry or create a new entry
  5948. // in the ActiveChildren hash table.
  5949. // Skip it if this is a TombStone IDTable Entry create and the parent
  5950. // FID is zero.
  5951. //
  5952. if (!COE_FLAG_ON(ChangeOrder, COE_FLAG_JUST_TOMBSTONE) ||
  5953. (ChangeOrder->ParentFileReferenceNumber != ZERO_FID)) {
  5954. if (ChangeOrder->ParentFileReferenceNumber == ZERO_FID) {
  5955. CHANGE_ORDER_TRACE(3, ChangeOrder, "Warning - parent fid zero");
  5956. FRS_PRINT_TYPE(4, ChangeOrder);
  5957. }
  5958. NParentGuid = FrsAlloc(sizeof(GUID));
  5959. OldParentGuid = FrsAlloc(sizeof(GUID));
  5960. if((NParentGuid != NULL) && (OldParentGuid != NULL)) {
  5961. memcpy(NParentGuid, &CoCmd->NewParentGuid , sizeof(GUID));
  5962. GuidToStr(NParentGuid, GuidStr);
  5963. QHashAcquireLock(pVme->ActiveChildren);
  5964. QHashEntry = QHashInsertLock(pVme->ActiveChildren,
  5965. NParentGuid,
  5966. NULL,
  5967. (ULONG_PTR)NParentGuid);
  5968. QHashEntry->QData += 2;
  5969. DPRINT2(4, "++ GAC - bump count on GUID: %s, QData %08x \n",
  5970. GuidStr, QHashEntry->QData);
  5971. QHashEntry = NULL;
  5972. //
  5973. // If this CO has a old parent and a new parent (e.g. MOVE_DIR) then
  5974. // we need to block both parents. Check for that and block both.
  5975. //
  5976. if (!GUIDS_EQUAL(&CoCmd->NewParentGuid, &CoCmd->OldParentGuid)) {
  5977. memcpy(OldParentGuid, &CoCmd->OldParentGuid , sizeof(GUID));
  5978. GuidToStr(OldParentGuid, GuidStr);
  5979. QHashEntry = QHashInsertLock(pVme->ActiveChildren,
  5980. OldParentGuid,
  5981. NULL,
  5982. (ULONG_PTR)OldParentGuid);
  5983. QHashEntry->QData += 2;
  5984. DPRINT2(4, "++ GAC - bump count on GUID: %s, QData %08x \n",
  5985. GuidStr, QHashEntry->QData);
  5986. QHashEntry = NULL;
  5987. } else {
  5988. FrsFree(OldParentGuid);
  5989. }
  5990. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVE_CHILD);
  5991. QHashReleaseLock(pVme->ActiveChildren);
  5992. }
  5993. }
  5994. return TRUE;
  5995. }
  5996. ULONG
  5997. ChgOrdDispatch(
  5998. PTHREAD_CTX ThreadCtx,
  5999. PIDTABLE_RECORD IDTableRec,
  6000. PCHANGE_ORDER_ENTRY ChangeOrder,
  6001. PREPLICA Replica
  6002. )
  6003. /*++
  6004. Routine Description:
  6005. Dispatch the change order to one of the following:
  6006. 1. RemoteCoAccepted() ,2. LocalCoAccepted(), 3. InboundRetired()
  6007. 4. InstallRetry()
  6008. NOTE: On return the ChangeOrder now belongs to some other thread.
  6009. Arguments:
  6010. IDTableRec -- The IDTable record for this file / dir.
  6011. ChangeOrder-- The change order.
  6012. Replica -- The Replica struct.
  6013. Return Value:
  6014. FrsErrorStatus
  6015. --*/
  6016. {
  6017. #undef DEBSUB
  6018. #define DEBSUB "ChgOrdDispatch:"
  6019. JET_ERR jerr, jerr1;
  6020. PTABLE_CTX TmpIDTableCtx;
  6021. BOOL ChildrenExist;
  6022. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  6023. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  6024. BOOL LocalCo;
  6025. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  6026. //
  6027. // Since we are going to issue this CO clear those flags that will get
  6028. // set later when the CO either goes thru Retire or are handled in
  6029. // a different way after this point.
  6030. //
  6031. CLEAR_ISSUE_CLEANUP(ChangeOrder, ISCU_GOIS_CLEANUP);
  6032. if (LocalCo) {
  6033. //
  6034. // Increment the Local Change Orders Issued
  6035. //
  6036. PM_INC_CTR_REPSET(Replica, LCOIssued, 1);
  6037. if (ChangeOrder->StreamLastMergeSeqNum > pVme->StreamSequenceNumberFetched) {
  6038. pVme->StreamSequenceNumberFetched = ChangeOrder->StreamLastMergeSeqNum;
  6039. }
  6040. FRS_ASSERT(CO_STATE_IS(ChangeOrder, IBCO_STAGING_REQUESTED) ||
  6041. CO_STATE_IS(ChangeOrder, IBCO_STAGING_RETRY));
  6042. VVReserveRetireSlot(Replica, ChangeOrder);
  6043. DPRINT(4,"++ Submitting ChangeOrder to stager\n");
  6044. FRS_PRINT_TYPE(4, ChangeOrder);
  6045. RcsSubmitLocalCoAccepted(ChangeOrder);
  6046. } else {
  6047. //
  6048. // The code in StuDeleteEmptyDirectory() will
  6049. // delete all of the subdirs and files if the change order
  6050. // makes it past this check. Hence, any newly created files
  6051. // and subdirs will disappear once this retry-delete makes
  6052. // it past this check.
  6053. //
  6054. // The check isn't made when the change order is not in
  6055. // retry mode because the checks in
  6056. // JrnlDoesChangeOrderHaveChildren are expensive and, in most
  6057. // cases, the delete will not encounter children.
  6058. //
  6059. // The call is made in the context of the ChangeOrderAccetp
  6060. // thread because this thread has other references to the
  6061. // tables referenced by JrnlDoesChangeOrderHaveChildren() and
  6062. // doesn't require new hash table locks. Calling the function
  6063. // out of StuDeleteEmptyDirectory() may require new locks
  6064. // around the hash tables.
  6065. //
  6066. // 210642 B3SS:Replica Synchronization test throws Alpha computer into
  6067. // error state. No further replication is possible.
  6068. // StuDeleteEmptyDirectory() was deleting the contents whenever
  6069. // CO_FLAG_RETRY was set BUT this functin was only checking for
  6070. // children when the co's state was IBCO_INSTALL_RETRY. Fix by
  6071. // checking for any CO_FLAG_RETRY of a directory delete..
  6072. //
  6073. //
  6074. // Increment the Remote Change Orders Issued
  6075. //
  6076. if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_CONTROL)){
  6077. //
  6078. // If its not local and control, its a remote CO
  6079. //
  6080. PM_INC_CTR_REPSET(Replica, RCOIssued, 1);
  6081. }
  6082. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY) &&
  6083. CoIsDirectory(ChangeOrder) &&
  6084. DOES_CO_DELETE_FILE_NAME(CoCmd) &&
  6085. (CO_STATE_IS(ChangeOrder, IBCO_FETCH_REQUESTED) ||
  6086. CO_STATE_IS(ChangeOrder, IBCO_FETCH_RETRY) ||
  6087. CO_STATE_IS(ChangeOrder, IBCO_INSTALL_DEL_RETRY) ||
  6088. CO_STATE_IS(ChangeOrder, IBCO_INSTALL_RETRY))){
  6089. TmpIDTableCtx = FrsAlloc(sizeof(TABLE_CTX));
  6090. TmpIDTableCtx->TableType = TABLE_TYPE_INVALID;
  6091. TmpIDTableCtx->Tid = JET_tableidNil;
  6092. jerr = DbsOpenTable(ThreadCtx, TmpIDTableCtx, Replica->ReplicaNumber, IDTablex, NULL);
  6093. if (JET_SUCCESS(jerr)) {
  6094. ChildrenExist = JrnlDoesChangeOrderHaveChildren(ThreadCtx, TmpIDTableCtx, ChangeOrder);
  6095. DbsCloseTable(jerr1, ThreadCtx->JSesid, TmpIDTableCtx);
  6096. DbsFreeTableCtx(TmpIDTableCtx, 1);
  6097. FrsFree(TmpIDTableCtx);
  6098. if (ChildrenExist) {
  6099. //
  6100. // Attempting to delete a directory with children;
  6101. // retry and hope the condition clears up later.
  6102. //
  6103. // Non-replicating children don't count; the Install
  6104. // thread will delete them.
  6105. //
  6106. CHANGE_ORDER_TRACE(3, ChangeOrder, "Retry Delete; has children");
  6107. //
  6108. // Consider the case where machine A does a movein of a subdir
  6109. // and before all the files are populated on machine B, machine
  6110. // B does a moveout of the dir. Since B is not fully populated
  6111. // B does not issue delete COs for all the children in the tree.
  6112. // When the delete COs for the parent dirs arrive back at machine
  6113. // A we will see that it has populated children and we will
  6114. // retry those failing delete dir COs forever. Not good.
  6115. //
  6116. // Not clear if this is a real problem. A will continue to send
  6117. // COs for the rest of the subtree to machine B. This will cause
  6118. // B to reanimate the parent dirs, getting the dir state from A.
  6119. // The file delete COs sent from B to A for files will get deleted
  6120. // on A. The Dir delete Cos sent from B to A will have active
  6121. // children and will not get deleted on A and these same dirs
  6122. // should get reanimated on B.
  6123. //
  6124. // See bug 71033
  6125. return FrsErrorCantMoveOut;
  6126. }
  6127. } else {
  6128. DPRINT1_JS(0, "DbsOpenTable (IDTABLE) on replica number %d failed.",
  6129. Replica->ReplicaNumber, jerr);
  6130. DbsCloseTable(jerr1, ThreadCtx->JSesid, TmpIDTableCtx);
  6131. DbsFreeTableCtx(TmpIDTableCtx, 1);
  6132. FrsFree(TmpIDTableCtx);
  6133. }
  6134. }
  6135. //
  6136. // Check for install retry on the remote CO.
  6137. //
  6138. switch (CO_STATE(ChangeOrder)) {
  6139. case IBCO_FETCH_REQUESTED:
  6140. case IBCO_FETCH_RETRY:
  6141. //
  6142. // Go fetch the remote file.
  6143. //
  6144. RcsSubmitRemoteCoAccepted(ChangeOrder);
  6145. break;
  6146. case IBCO_INSTALL_RETRY:
  6147. FRS_ASSERT(CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY));
  6148. //
  6149. // Retry the install.
  6150. //
  6151. SET_CHANGE_ORDER_STATE(ChangeOrder, IBCO_INSTALL_REQUESTED);
  6152. RcsSubmitRemoteCoInstallRetry(ChangeOrder);
  6153. break;
  6154. case IBCO_INSTALL_REN_RETRY:
  6155. FRS_ASSERT(CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY));
  6156. //
  6157. // The install completed but the rename of a newly created
  6158. // file into its final name failed. Since this CO made it past
  6159. // reconcile it should have picked up the state of the deferred
  6160. // rename flag from the IDTable record. Check that it did.
  6161. // The retire code is responsible for doing the file rename.
  6162. //
  6163. FRS_ASSERT(COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_RENAME));
  6164. CHANGE_ORDER_TRACE(3, ChangeOrder, "Complete final rename");
  6165. ChgOrdInboundRetired(ChangeOrder);
  6166. break;
  6167. case IBCO_INSTALL_DEL_RETRY:
  6168. FRS_ASSERT(CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY));
  6169. //
  6170. // The CO completed but the actual delete has yet to be finished.
  6171. // Since this CO made it past reconcile it should have picked up
  6172. // the state of the deferred delete flag from the IDTable record.
  6173. // Check that it did. The retire code is responsible for doing
  6174. // the final delete.
  6175. //
  6176. FRS_ASSERT(COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_DELETE));
  6177. CHANGE_ORDER_TRACE(3, ChangeOrder, "Complete final delete");
  6178. ChgOrdInboundRetired(ChangeOrder);
  6179. break;
  6180. default:
  6181. CHANGE_ORDER_TRACE(3, ChangeOrder, "Invalid Issue State");
  6182. FRS_ASSERT(!"ChgOrdDispatch: Invalid Issue State");
  6183. } // end switch
  6184. }
  6185. return FrsErrorSuccess;
  6186. }
  6187. VOID
  6188. ChgOrdReject(
  6189. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  6190. IN PREPLICA Replica
  6191. )
  6192. /*++
  6193. Routine Description:
  6194. This change order has been rejected. Set the Issue Cleanup flags depending
  6195. on what needs to be undone.
  6196. Arguments:
  6197. ChangeOrder-- The change order.
  6198. Replica -- The Replica struct.
  6199. Return Value:
  6200. None.
  6201. --*/
  6202. {
  6203. #undef DEBSUB
  6204. #define DEBSUB "ChgOrdReject:"
  6205. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  6206. BOOL RemoteCo, RetryCo, RecoveryCo;
  6207. RemoteCo = !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  6208. RetryCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY);
  6209. RecoveryCo = RecoveryCo(ChangeOrder);
  6210. //
  6211. // For whatever reason, reject this changeorder. The flags in
  6212. // ChangeOrder->IssueCleanup specify what must be un-done.
  6213. // Note: Perf: The IFLAG abort bit may be sufficient?
  6214. //
  6215. SET_CO_FLAG(ChangeOrder, CO_FLAG_ABORT_CO);
  6216. if (RetryCo || RecoveryCo) {
  6217. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_INLOG);
  6218. }
  6219. FRS_PRINT_TYPE(5, ChangeOrder);
  6220. if (RemoteCo) {
  6221. //
  6222. // Remote CO rejected. Activate VV retire slot if not yet done
  6223. // so we update the version vector entry for this originator.
  6224. //
  6225. if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_VV_ACTIVATED)) {
  6226. FRS_ASSERT(CO_STATE_IS_LE(ChangeOrder, IBCO_FETCH_RETRY));
  6227. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACK_INBOUND);
  6228. #if 0
  6229. //
  6230. // If the Aborted CO is in the IBCO_FETCH_RETRY state then
  6231. // it is possible that a staging file was created before the
  6232. // CO was sent thru retry. It needs to be deleted.
  6233. //
  6234. // PERF: What if the same CO from mult inbound partners
  6235. // are in retry? Will the later COs that get aborted
  6236. // del the stage file still in use by first one to succeed?
  6237. // YUP. FOR NOW COMMENT THIS OUT AND LET THE RESTART CODE CLEAN
  6238. // UP THE LEFTOVER STAGING FILES,
  6239. //
  6240. if (CO_STATE_IS(ChangeOrder, IBCO_FETCH_RETRY)) {
  6241. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_DEL_STAGE_FILE);
  6242. }
  6243. #endif
  6244. //
  6245. // Don't bother updating the ondisk version vector if the
  6246. // version vector has already seen this Vsn. This condition
  6247. // can arise if a machine receives the same series of change
  6248. // orders from two machines. The change orders could easily
  6249. // complete out of order.
  6250. //
  6251. if (VVHasVsn(Replica->VVector, CoCmd)) {
  6252. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD);
  6253. } else {
  6254. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV);
  6255. //SET_CO_FLAG(ChangeOrder, CO_FLAG_VV_ACTIVATED);
  6256. }
  6257. } else {
  6258. //
  6259. // VVretire slot has been activated for this CO.
  6260. // If VVRETIRE_EXECuted is set then the the Outlog record has
  6261. // been inserted.
  6262. //
  6263. FRS_ASSERT(!CO_STATE_IS_LE(ChangeOrder, IBCO_FETCH_RETRY));
  6264. LOCK_GEN_TABLE(Replica->VVector);
  6265. if (CO_IFLAG_ON(ChangeOrder, CO_IFLAG_VVRETIRE_EXEC)) {
  6266. //
  6267. // Cause the install incomplete flag in the outlog record
  6268. // to be cleared so OutLog process can delete the staging
  6269. // file when it's done.
  6270. //
  6271. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_CO_ABORT |
  6272. ISCU_DEL_STAGE_FILE_IF);
  6273. } else {
  6274. //
  6275. // ISCU_ACTIVATE_VV_DISCARD will clear the INS_OUTLOG
  6276. // action in the VV slot but still updates the VV at the
  6277. // right time.
  6278. //
  6279. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_ACTIVATE_VV_DISCARD |
  6280. ISCU_DEL_STAGE_FILE);
  6281. SET_CO_IFLAG(ChangeOrder, CO_IFLAG_CO_ABORT);
  6282. }
  6283. UNLOCK_GEN_TABLE(Replica->VVector);
  6284. }
  6285. //
  6286. // Install is done (or if it's not, it is now).
  6287. //
  6288. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_INSTALL_INCOMPLETE);
  6289. } else {
  6290. //
  6291. // If this CO was going to do a Directory Enumeration then turn it off.
  6292. //
  6293. if (CO_IFLAG_ON(ChangeOrder, CO_IFLAG_DIR_ENUM_PENDING)) {
  6294. CLEAR_CO_IFLAG(ChangeOrder, CO_IFLAG_DIR_ENUM_PENDING);
  6295. }
  6296. //
  6297. // Advance the inlog commit point for local COs that are not reanimated
  6298. // COs. Need to check if greater than because a retry CO that gets
  6299. // rejected can move the commit point back.
  6300. //
  6301. if (CoCmd->JrnlFirstUsn != (USN) 0) {
  6302. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  6303. AcquireQuadLock(&pVme->QuadWriteLock);
  6304. if (Replica->InlogCommitUsn < CoCmd->JrnlFirstUsn) {
  6305. Replica->InlogCommitUsn = CoCmd->JrnlFirstUsn;
  6306. DPRINT1(4, "++ Replica->InlogCommitUsn: %08x %08x\n",
  6307. PRINTQUAD(Replica->InlogCommitUsn));
  6308. }
  6309. ReleaseQuadLock(&pVme->QuadWriteLock);
  6310. }
  6311. }
  6312. }
  6313. ULONG
  6314. ChgOrdInboundRetired(
  6315. IN PCHANGE_ORDER_ENTRY ChangeOrder
  6316. )
  6317. /*++
  6318. Routine Description:
  6319. The processing of an inbound change order is completed.
  6320. Send a request to the DB subsystem to update the IDTable and delete the
  6321. record from the inbound log table.
  6322. Arguments:
  6323. ChangeOrder -- The change order
  6324. Return Value:
  6325. Frs Status
  6326. --*/
  6327. {
  6328. #undef DEBSUB
  6329. #define DEBSUB "ChgOrdInboundRetired:"
  6330. PREPLICA Replica;
  6331. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Enter Retire");
  6332. Replica = CO_REPLICA(ChangeOrder);
  6333. if (Replica == NULL) {
  6334. DPRINT(0, "++ ERROR - ChangeOrder NewReplica and OrigReplica are NULL.\n");
  6335. FRS_ASSERT(!"ChangeOrder NewReplica and OrigReplica are NULL");
  6336. return FrsErrorInternalError;
  6337. }
  6338. //
  6339. // This routine can execute in any thread so use the DB server to delete
  6340. // the record from the inbound log and update the IDTable.
  6341. // Once the Database updates are complete the DBService will call
  6342. // us back through ChgOrdIssueCleanup() so we can cleanup our structs.
  6343. //
  6344. DbsPrepareCmdPkt(NULL, // CmdPkt,
  6345. Replica, // Replica,
  6346. CMD_DBS_RETIRE_INBOUND_CO, // CmdRequest,
  6347. NULL, // TableCtx,
  6348. ChangeOrder, // CallContext,
  6349. 0, // TableType,
  6350. 0, // AccessRequest,
  6351. 0, // IndexType,
  6352. NULL, // KeyValue,
  6353. 0, // KeyValueLength,
  6354. TRUE); // Submit
  6355. return FrsErrorSuccess;
  6356. }
  6357. ULONG
  6358. ChgOrdInboundRetry(
  6359. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  6360. IN ULONG NewState
  6361. )
  6362. /*++
  6363. Routine Description:
  6364. The Install/generate phase of an inbound change order has failed
  6365. and must be retried later.
  6366. Send a request to the DB subsystem to update the change order in the inbound
  6367. log and get the outbound log process started. When the change order is
  6368. later retried it goes through all the normal issue and reconcilliation
  6369. checks.
  6370. Arguments:
  6371. ChangeOrder -- The change order
  6372. NewState -- The new IBCO_xx state telling what kind of CO retry is needed.
  6373. Return Value:
  6374. Frs Status
  6375. --*/
  6376. {
  6377. #undef DEBSUB
  6378. #define DEBSUB "ChgOrdInboundRetry:"
  6379. PREPLICA Replica;
  6380. ULONG LocationCmd;
  6381. //
  6382. // If the connection to the inbound partner was lost then just send it
  6383. // to the inlog for later retry.
  6384. //
  6385. if (!COE_FLAG_ON(ChangeOrder, COE_FLAG_NO_INBOUND)) {
  6386. //
  6387. // We can't do retry for directory creates because subsequent file ops could
  6388. // fail. Although a local Co may be retried because of a lack of
  6389. // staging space. Hence,
  6390. //
  6391. if (CoIsDirectory(ChangeOrder)&& !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO)) {
  6392. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  6393. if (CO_NEW_FILE(LocationCmd)) {
  6394. //
  6395. // Caller should have forced an UnJoin so we don't come here.
  6396. // This causes any further change orders from this partner
  6397. // to go to the INLOG.
  6398. //
  6399. CHANGE_ORDER_TRACE(3, ChangeOrder, "Error - can't retry failed dir cre");
  6400. FRS_PRINT_TYPE(0, ChangeOrder);
  6401. FRS_ASSERT(!"ChgOrdInboundRetry: can't retry failed dir cre");
  6402. return ChgOrdInboundRetired(ChangeOrder);
  6403. }
  6404. }
  6405. }
  6406. Replica = CO_REPLICA(ChangeOrder);
  6407. if (Replica == NULL) {
  6408. DPRINT(0, "++ ERROR - ChangeOrder NewReplica and OrigReplica are NULL.\n");
  6409. FRS_ASSERT(!"ChgOrdInboundRetry: ChangeOrder NewReplica and OrigReplica are NULL");
  6410. return FrsErrorInternalError;
  6411. }
  6412. SET_CHANGE_ORDER_STATE(ChangeOrder, NewState);
  6413. //
  6414. // This routine can execute in any thread so use the DB server to update
  6415. // the record in the inbound log for retry.
  6416. // Once the Database updates are complete the DBService will call
  6417. // us back through ChgOrdIssueCleanup() so we can cleanup our structs.
  6418. //
  6419. DbsPrepareCmdPkt(NULL, // CmdPkt,
  6420. Replica, // Replica,
  6421. CMD_DBS_RETRY_INBOUND_CO, // CmdRequest,
  6422. NULL, // TableCtx,
  6423. ChangeOrder, // CallContext,
  6424. 0, // TableType,
  6425. 0, // AccessRequest,
  6426. 0, // IndexType,
  6427. NULL, // KeyValue,
  6428. 0, // KeyValueLength,
  6429. TRUE); // Submit
  6430. return FrsErrorSuccess;
  6431. }
  6432. ULONG
  6433. ChgOrdIssueCleanup(
  6434. PTHREAD_CTX ThreadCtx,
  6435. PREPLICA Replica,
  6436. PCHANGE_ORDER_ENTRY ChangeOrder,
  6437. ULONG CleanUpFlags
  6438. )
  6439. /*++
  6440. Routine Description:
  6441. This is a common cleanup routine used for change order abort during or after
  6442. issue and for change order completion.
  6443. It uses the DB thread context of the caller's thread.
  6444. The cleanup flags drive the opertions performed. See below.
  6445. The sequence of the code segments below is important for both correct
  6446. operation and recoverability. DO NOT CHANGE unless you understand.
  6447. Different calls use different sets of operation. They could be broken out
  6448. into seperate functions. However having the code segments in one routine
  6449. helps both maintainence and to see the overall relationship between segments.
  6450. Arguments:
  6451. ThreadCtx -- ptr to the thread context for DB
  6452. Replica -- ptr to replica struct.
  6453. ChangeOrder -- ptr to change order entry that has just committed.
  6454. CleanUpFlags -- The clean up flag bits control what cleanup operations are done.
  6455. ISCU_DEL_PREINSTALL - Delete the pre-install file
  6456. ISCU_DEL_IDT_ENTRY - Delete the IDTable entry
  6457. ISCU_UPDATE_IDT_ENTRY - Update the IDTable entry
  6458. ISCU_DEL_INLOG - Delete the INlog entry (if RefCnt == 0)
  6459. ISCU_AIBCO - Delete Active Inbound Change Order table entry
  6460. ISCU_ACTIVE_CHILD - Delete the Active child entry
  6461. ISCU_CO_GUID - Delete the entry from the change order GUID table
  6462. ISCU_CHECK_ISSUE_BLOCK - Check if CO is blocking another and unidle queue.
  6463. ISCU_DEL_RTCTX - Delete the Replica Thread Ctx struct (if RefCnt==0)
  6464. ISCU_ACTIVATE_VV - Activate the Version Vector retire slot for this CO
  6465. ISCU_UPDATEVV_DB - Update the Version Vector entry in the database.
  6466. ISCU_ACTIVATE_VV_DISCARD - Discard the VV retire slot.
  6467. ISCU_ACK_INBOUND - Notify the Inbound partner or update our local VV
  6468. ISCU_INS_OUTLOG - Insert the CO into the outbound log.
  6469. ISCU_INS_OUTLOG_NEW_GUID - Re-Insert the CO into the outbound log with a new CO Guid.
  6470. ISCU_UPDATE_INLOG - Update the State, Flags and IFlags fields in the DB CO record.
  6471. ISCU_DEL_STAGE_FILE - Delete the staging file.
  6472. ISCU_DEL_STAGE_FILE_IF - Delete staging file only IF no outbound partners
  6473. ISCU_FREE_CO - Free the Change Order (if RefCnt==0)
  6474. ISCU_DEC_CO_REF - Decrement the ref count on the CO.
  6475. ISCU_CO_ABORT - This CO is aborting.
  6476. ISCU_NC_TABLE - Delete the entry in the name conflict table.
  6477. ISCU_UPDATE_IDT_FLAGS - Update just IDTable record flags, not entire record
  6478. ISCU_UPDATE_IDT_FILEUSN- Update just IDTable record file USN, not entire record.
  6479. ISCU_UPDATE_IDT_VERSION- Update just the version info on the file.
  6480. Return Value:
  6481. Frs Status
  6482. --*/
  6483. {
  6484. #undef DEBSUB
  6485. #define DEBSUB "ChgOrdIssueCleanup:"
  6486. #undef FlagChk
  6487. #define FlagChk(_flag_) BooleanFlagOn(CleanUpFlags, _flag_)
  6488. ULONGLONG SeqNum;
  6489. PVOLUME_MONITOR_ENTRY pVme;
  6490. PCHANGE_ORDER_ENTRY ActiveChangeOrder;
  6491. PCHANGE_ORDER_ENTRY DupChangeOrder;
  6492. ULONG GStatus, WStatus, WStatus2;
  6493. ULONG FStatus = FrsErrorSuccess;
  6494. BOOL RestartQueue = FALSE;
  6495. PQHASH_TABLE ActiveChildren;
  6496. PQHASH_ENTRY QHashEntry;
  6497. BOOL PendingCoOnParent;
  6498. BOOL RetryCo;
  6499. GUID *BusyParentGuid = NULL;
  6500. CHAR BusyParentGuidStr[GUID_CHAR_LEN];
  6501. PCHANGE_ORDER_COMMAND CoCmd = &ChangeOrder->Cmd;
  6502. PTABLE_CTX TableCtx;
  6503. PTABLE_CTX InLogTableCtx;
  6504. PWCHAR TmpName = NULL;
  6505. TABLE_CTX TempTableCtx;
  6506. PSINGLE_LIST_ENTRY Entry;
  6507. ULONG RefCount;
  6508. ULONG SaveState, SaveFlags;
  6509. JET_ERR jerr;
  6510. ULONG FieldIndex[8];
  6511. PVOID pDataRecord;
  6512. BOOL DelStage, UpdateOutLog, InstallIncomplete;
  6513. BOOL NotifyRetryThread = FALSE;
  6514. ULONG IdtFieldUpdateList[CO_ACCEPT_IDT_FIELD_UPDATE_MAX];
  6515. ULONG IdtFieldUpdateCount;
  6516. ULONG MissMatch;
  6517. GUID OldGuid;
  6518. CHAR FlagBuffer[160];
  6519. CHANGE_ORDER_TRACEX(3, ChangeOrder, "CO IssueCleanup, FlgsA", CleanUpFlags);
  6520. CHANGE_ORDER_TRACEX(3, ChangeOrder, "CO IssueCleanup, FlgsC", ChangeOrder->IssueCleanup);
  6521. MissMatch = (~CleanUpFlags) & ChangeOrder->IssueCleanup;
  6522. if (MissMatch != 0) {
  6523. CHANGE_ORDER_TRACEX(3, ChangeOrder, "CO IssueCleanup, FlgsE", MissMatch);
  6524. }
  6525. //
  6526. // Merge flags with bits from CO if caller says ok.
  6527. //
  6528. if (!FlagChk(ISCU_NO_CLEANUP_MERGE)) {
  6529. CleanUpFlags |= ChangeOrder->IssueCleanup;
  6530. }
  6531. CHANGE_ORDER_TRACEX(3, ChangeOrder, "CO IssueCleanup, Flgs ", CleanUpFlags);
  6532. if (CleanUpFlags == 0) {
  6533. return FrsErrorSuccess;
  6534. }
  6535. FrsFlagsToStr(CleanUpFlags, IscuFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  6536. DPRINT2(3, ":: CoG %08x, Flags [%s]\n", CoCmd->ChangeOrderGuid.Data1, FlagBuffer);
  6537. FRS_ASSERT(Replica != NULL);
  6538. pVme = Replica->pVme;
  6539. FRS_ASSERT((pVme != NULL) || !REPLICA_IS_ACTIVE(Replica));
  6540. if ((pVme == NULL) || (AcquireVmeRef(pVme) == 0)) {
  6541. FStatus = FrsErrorChgOrderAbort;
  6542. pVme = NULL;
  6543. //
  6544. // Cleanup what we can. The following depend on the Vme.
  6545. //
  6546. ClearFlag(CleanUpFlags, ISCU_AIBCO |
  6547. ISCU_ACTIVE_CHILD |
  6548. ISCU_DEL_IDT_ENTRY |
  6549. ISCU_DEL_PREINSTALL );
  6550. } else {
  6551. ActiveChildren = pVme->ActiveChildren;
  6552. }
  6553. //
  6554. // If this is a Demand Refresh CO then some cleanup actions are not
  6555. // required. Instead of littering the various paths to this function,
  6556. // Retry, VV execute, Retire, Reject, ... with tests for Demand Refresh
  6557. // we just clear the appropriate flags here so the other paths can
  6558. // ignore it for the most part.
  6559. //
  6560. // We now want to send the demand refresh COs forward to other partners
  6561. // to ensure convergence so do not clear the ISCU_INS_OUTLOG and
  6562. // ISCU_INS_OUTLOG_NEW_GUID flags.
  6563. //
  6564. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_DEMAND_REFRESH)) {
  6565. ClearFlag(CleanUpFlags, ISCU_ACK_INBOUND |
  6566. ISCU_ACTIVATE_VV |
  6567. ISCU_UPDATEVV_DB |
  6568. ISCU_ACTIVATE_VV_DISCARD |
  6569. // ISCU_INS_OUTLOG |
  6570. // ISCU_INS_OUTLOG_NEW_GUID |
  6571. ISCU_DEL_INLOG |
  6572. ISCU_UPDATE_INLOG);
  6573. CHANGE_ORDER_TRACEX(3, ChangeOrder, "CO ISCU DemandRefresh, Flgs", CleanUpFlags);
  6574. } else
  6575. //
  6576. // If this is a MorphGenCo then it is a local CO that never was inserted
  6577. // into the Inbound Log. Clear the Inlog related flags here.
  6578. //
  6579. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN)) {
  6580. ClearFlag(CleanUpFlags, ISCU_DEL_INLOG |
  6581. ISCU_UPDATE_INLOG);
  6582. CHANGE_ORDER_TRACEX(3, ChangeOrder, "CO ISCU MORPH GEN, Flgs", CleanUpFlags);
  6583. }
  6584. RetryCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY);
  6585. //
  6586. // ISCU_DEL_PREINSTALL
  6587. // Delete the pre-Install File
  6588. //
  6589. if (FlagChk(ISCU_DEL_PREINSTALL)) {
  6590. HANDLE Handle;
  6591. //
  6592. // Open the pre-install file and delete it
  6593. //
  6594. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO PreInstall File Delete");
  6595. TmpName = FrsCreateGuidName(&CoCmd->ChangeOrderGuid,
  6596. PRE_INSTALL_PREFIX);
  6597. WStatus2 = FrsCreateFileRelativeById(&Handle,
  6598. ChangeOrder->NewReplica->PreInstallHandle,
  6599. NULL,
  6600. 0,
  6601. 0,
  6602. TmpName,
  6603. (USHORT)(wcslen(TmpName) *
  6604. sizeof(WCHAR)),
  6605. NULL,
  6606. FILE_OPEN,
  6607. ATTR_ACCESS | DELETE);
  6608. if (!WIN_SUCCESS(WStatus2)) {
  6609. DPRINT2_WS(0, "++ WARN - Failed to open pre-install file %ws for %ws for delete;",
  6610. TmpName, CoCmd->FileName, WStatus2);
  6611. } else {
  6612. FrsResetAttributesForReplication(TmpName, Handle);
  6613. WStatus2 = FrsDeleteByHandle(TmpName, Handle);
  6614. DPRINT2_WS(0, "++ WARN - Failed to delete pre-install file %ws for %ws;",
  6615. TmpName, CoCmd->FileName, WStatus2);
  6616. FrsCloseWithUsnDampening(TmpName,
  6617. &Handle,
  6618. ChangeOrder->NewReplica->pVme->FrsWriteFilter,
  6619. &CoCmd->FileUsn);
  6620. }
  6621. FrsFree(TmpName);
  6622. }
  6623. //
  6624. // ISCU_DEL_STAGE_FILE
  6625. // ISCU_DEL_STAGE_FILE_IF
  6626. // ISCU_CO_ABORT
  6627. // Delete the staging file if so ordered or if there are no outbound
  6628. // partners and either the CO is aborted or a conditional delete is requested.
  6629. //
  6630. DelStage = FlagChk(ISCU_DEL_STAGE_FILE) ||
  6631. (NO_OUTLOG_PARTNERS(Replica) &&
  6632. (FlagChk(ISCU_DEL_STAGE_FILE_IF) || FlagChk(ISCU_CO_ABORT)));
  6633. if (DelStage) {
  6634. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO StageFile Delete");
  6635. if (!StageDeleteFile(CoCmd, NULL, TRUE)) {
  6636. DPRINT1(0, "++ ERROR - Failed to delete staging file: %ws\n",
  6637. CoCmd->FileName);
  6638. FStatus = FrsErrorStageFileDelFailed;
  6639. }
  6640. }
  6641. //
  6642. // ISCU_DEL_STAGE_FILE_IF
  6643. // ISCU_CO_ABORT
  6644. // Update the Flags field on the outbound log record if we have partners
  6645. // and the record was created and either the CO is aborted or a conditional
  6646. // staging file delete is requested.
  6647. //
  6648. UpdateOutLog = !NO_OUTLOG_PARTNERS(Replica) &&
  6649. CO_IFLAG_ON(ChangeOrder, CO_IFLAG_VVRETIRE_EXEC) &&
  6650. (FlagChk(ISCU_DEL_STAGE_FILE_IF) || FlagChk(ISCU_CO_ABORT));
  6651. InstallIncomplete = COC_FLAG_ON(CoCmd, CO_FLAG_INSTALL_INCOMPLETE);
  6652. if (UpdateOutLog && (InstallIncomplete ||
  6653. CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY) ||
  6654. COE_FLAG_ON(ChangeOrder, COE_FLAG_RECOVERY_CO) ||
  6655. FlagChk(ISCU_CO_ABORT))) {
  6656. //
  6657. // Clear the Install Incomplete flag or set abort flag so the staging
  6658. // file can be deleted when the outbound log process is done.
  6659. // Use local table ctx if not provided.
  6660. //
  6661. if (ChangeOrder->RtCtx == NULL) {
  6662. TableCtx = &TempTableCtx;
  6663. TableCtx->TableType = TABLE_TYPE_INVALID;
  6664. TableCtx->Tid = JET_tableidNil;
  6665. } else {
  6666. TableCtx = &ChangeOrder->RtCtx->OUTLOGTable;
  6667. }
  6668. DbsAllocTableCtxWithRecord(OUTLOGTablex, TableCtx, CoCmd);
  6669. DPRINT2(4, "++ Clear Install Incomplete in outlog for Index %d, File: %ws\n",
  6670. CoCmd->SequenceNumber, CoCmd->FileName);
  6671. CLEAR_COC_FLAG(CoCmd, CO_FLAG_INSTALL_INCOMPLETE);
  6672. // ISCU_CO_ABORT
  6673. if (FlagChk(ISCU_CO_ABORT)) {
  6674. SET_COC_FLAG(CoCmd, CO_FLAG_ABORT_CO);
  6675. DPRINT2(4, "++ Set abort flag in outlog for Index %d, File: %ws\n",
  6676. CoCmd->SequenceNumber, CoCmd->FileName);
  6677. }
  6678. //
  6679. // Clear the same flags that are cleared during insert
  6680. // (except for ABORT).
  6681. //
  6682. SAVE_CHANGE_ORDER_STATE(ChangeOrder, SaveState, SaveFlags);
  6683. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_GROUP_OL_CLEAR);
  6684. if (FlagChk(ISCU_CO_ABORT)) {
  6685. SET_COC_FLAG(CoCmd, CO_FLAG_ABORT_CO);
  6686. }
  6687. //
  6688. // ** WARNING ** WARNING ** WARNING **
  6689. // The following doesn't work if the Outbound Log process saves any
  6690. // state in the flags word because we overwrite it here. The Outlog
  6691. // process currently has no need to modify these bits so this is OK.
  6692. // Change orders generated during VVJoin set these flags but these
  6693. // change orders are never processed by the inlog process so we do
  6694. // not write to their flags field. If the Outlog process needs to
  6695. // write these bits this code must be updated so state is not lost,
  6696. // or the Outlog process could add its own flag word to the change order.
  6697. //
  6698. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO OutLog Update");
  6699. FStatus = DbsUpdateRecordField(ThreadCtx,
  6700. Replica,
  6701. TableCtx,
  6702. OLChangeOrderGuidIndexx,
  6703. &CoCmd->ChangeOrderGuid,
  6704. COFlagsx);
  6705. RESTORE_CHANGE_ORDER_STATE(ChangeOrder, SaveState, SaveFlags);
  6706. TableCtx->pDataRecord = NULL;
  6707. //
  6708. // Clear the Jet Set/Ret Col address fields for the Change Order
  6709. // Extension buffer to prevent reuse since that buffer goes with the CO.
  6710. //
  6711. DBS_SET_FIELD_ADDRESS(TableCtx, COExtensionx, NULL);
  6712. if (TableCtx == &TempTableCtx) {
  6713. DbsFreeTableCtx(TableCtx, 1);
  6714. }
  6715. if (!FRS_SUCCESS(FStatus)) {
  6716. CHANGE_ORDER_TRACEF(3, ChangeOrder, "Error Updating Outlog", FStatus);
  6717. //
  6718. // The Outlog record might not be there if we have previously gone
  6719. // through an IBCO_INSTALL_REN_RETRY state and already cleared the
  6720. // CO_FLAG_INSTALL_INCOMPLETE flag. This would have allowed the
  6721. // Outlog cleanup thread to delete the outlog record when it
  6722. // finished sending the CO even if the install rename had not yet
  6723. // completed. But if InstallIncomplete is TRUE then the out log
  6724. // record should be there.
  6725. //
  6726. // A similar situation occurs on deletes where a sharing violation
  6727. // marks the IDTable as IDREC_FLAGS_DELETE_DEFERRED and sends the CO
  6728. // thru retry in the IBCO_INSTALL_DEL_RETRY state. The outlog
  6729. // process can process the delete CO and cleanup so we don't find the record.
  6730. //
  6731. if (InstallIncomplete || (FStatus != FrsErrorNotFound)) {
  6732. DPRINT_FS(0,"++ ERROR - Update of Flags in Outlog record failed.", FStatus);
  6733. FStatus = FrsErrorSuccess;
  6734. }
  6735. }
  6736. }
  6737. //
  6738. // ISCU_UPDATE_IDT_ENTRY
  6739. // Update the IDTable record for this CO. This must be done before we
  6740. // Activate the VV slot in case this CO is next in line to retire.
  6741. // OutLog process needs to see current IDTable entry.
  6742. //
  6743. if (FlagChk(ISCU_UPDATE_IDT_ENTRY)) {
  6744. DPRINT(5, "Updating the IDTable record -----------\n");
  6745. FStatus = ChgOrdUpdateIDTableRecord(ThreadCtx, Replica, ChangeOrder);
  6746. DPRINT_FS(0,"++ ERROR - ChgOrdUpdateIDTableRecord failed.", FStatus);
  6747. FRS_ASSERT(FRS_SUCCESS(FStatus) || !"IssueCleanup: ISCU_UPDATE_IDT_ENTRY failed");
  6748. } else {
  6749. PIDTABLE_RECORD IDTableRec;
  6750. //
  6751. // ISCU_UPDATE_IDT_FLAGS
  6752. // Not updating the entire record so check the field flags.
  6753. //
  6754. IdtFieldUpdateCount = 0;
  6755. if (FlagChk(ISCU_UPDATE_IDT_FLAGS)) {
  6756. IdtFieldUpdateList[IdtFieldUpdateCount++] = Flagsx;
  6757. }
  6758. //
  6759. // ISCU_UPDATE_IDT_VVFLAGS
  6760. //
  6761. if (FlagChk(ISCU_UPDATE_IDT_VVFLAGS)) {
  6762. IdtFieldUpdateList[IdtFieldUpdateCount++] = IdtVVFlagsx;
  6763. }
  6764. // ISCU_UPDATE_IDT_FILEUSN
  6765. if (FlagChk(ISCU_UPDATE_IDT_FILEUSN)) {
  6766. IdtFieldUpdateList[IdtFieldUpdateCount++] = CurrentFileUsnx;
  6767. IDTableRec = (PIDTABLE_RECORD)(ChangeOrder->RtCtx->IDTable.pDataRecord);
  6768. IDTableRec->CurrentFileUsn = CoCmd->FileUsn;
  6769. }
  6770. // ISCU_UPDATE_IDT_VERSION
  6771. // Update all the info related to file reconcilation so the info is
  6772. // consistent for future reconcile checks and so correct version
  6773. // info can be generated when local changes occur to the file/dir.
  6774. //
  6775. if (FlagChk(ISCU_UPDATE_IDT_VERSION)) {
  6776. IdtFieldUpdateList[IdtFieldUpdateCount++] = VersionNumberx;
  6777. IdtFieldUpdateList[IdtFieldUpdateCount++] = EventTimex;
  6778. IdtFieldUpdateList[IdtFieldUpdateCount++] = OriginatorGuidx;
  6779. IdtFieldUpdateList[IdtFieldUpdateCount++] = OriginatorVSNx;
  6780. IdtFieldUpdateList[IdtFieldUpdateCount++] = FileSizex;
  6781. IDTableRec = (PIDTABLE_RECORD)(ChangeOrder->RtCtx->IDTable.pDataRecord);
  6782. IDTableRec->VersionNumber = CoCmd->FileVersionNumber;
  6783. IDTableRec->EventTime = CoCmd->EventTime.QuadPart;
  6784. IDTableRec->OriginatorGuid = CoCmd->OriginatorGuid;
  6785. IDTableRec->OriginatorVSN = CoCmd->FrsVsn;
  6786. IDTableRec->FileSize = CoCmd->FileSize;
  6787. }
  6788. FRS_ASSERT(IdtFieldUpdateCount <= CO_ACCEPT_IDT_FIELD_UPDATE_MAX);
  6789. if (IdtFieldUpdateCount > 0) {
  6790. FStatus = DbsUpdateIDTableFields(ThreadCtx,
  6791. Replica,
  6792. ChangeOrder,
  6793. IdtFieldUpdateList,
  6794. IdtFieldUpdateCount);
  6795. DPRINT_FS(0,"++ ERROR - DbsUpdateIDTableFields failed.", FStatus);
  6796. FRS_ASSERT(FRS_SUCCESS(FStatus) || !"ISCU_UPDATE_IDT_VERSION - update fields failed");
  6797. }
  6798. }
  6799. //
  6800. // ISCU_ACTIVATE_VV
  6801. // ISCU_ACTIVATE_VV_DISCARD
  6802. // Update the version vector. This could result in just marking the
  6803. // retire slot entry or we could end up getting called back to
  6804. // write the outbound log entry, decrement the ref count, or update the
  6805. // Version vector table in the database. The cleanup flags that are passed
  6806. // through are saved in the retire slot for use when the entry is processed.
  6807. // The INLOG flags may not be set if this CO is rejected on the first pass
  6808. // so the caller must tell us.
  6809. //
  6810. // code improvement: Use IssueCleanup flags in COe now instead of
  6811. // saving them in the vv retire slot.
  6812. //
  6813. if (FlagChk(ISCU_ACTIVATE_VV) || FlagChk(ISCU_ACTIVATE_VV_DISCARD)) {
  6814. //
  6815. // PERF: optimize the case where this CO is at the head of the VV retire
  6816. // list and is the only CO to retire. In this case the func could
  6817. // just return and avoid the recursive call back.
  6818. //
  6819. if (!FlagChk(ISCU_ACTIVATE_VV_DISCARD)) {
  6820. //
  6821. // Only do this once for the change order.
  6822. //
  6823. SET_CO_FLAG(ChangeOrder, CO_FLAG_VV_ACTIVATED);
  6824. }
  6825. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO Activate VV slot");
  6826. FStatus = VVRetireChangeOrder(ThreadCtx,
  6827. Replica,
  6828. ChangeOrder,
  6829. (CleanUpFlags & (ISCU_INS_OUTLOG |
  6830. ISCU_INS_OUTLOG_NEW_GUID |
  6831. ISCU_DEL_INLOG |
  6832. ISCU_UPDATE_INLOG |
  6833. ISCU_ACTIVATE_VV_DISCARD))
  6834. | ISCU_NO_CLEANUP_MERGE
  6835. );
  6836. if (FRS_SUCCESS(FStatus)) {
  6837. //
  6838. // Propagating the change order is now the job of VVRetireChangeOrder.
  6839. //
  6840. ClearFlag(CleanUpFlags, (ISCU_INS_OUTLOG | ISCU_INS_OUTLOG_NEW_GUID));
  6841. } else
  6842. if (FStatus == FrsErrorVVSlotNotFound) {
  6843. //
  6844. // No slot for an Out of order CO so we handle all cleanup ourselves.
  6845. //
  6846. FStatus = FrsErrorSuccess;
  6847. } else {
  6848. DPRINT_FS(0, "++ ERROR - VVRetireChangeOrder failed.", FStatus);
  6849. FRS_ASSERT(FRS_SUCCESS(FStatus) || !"VVRetireChangeOrder failed");
  6850. }
  6851. }
  6852. //
  6853. // ISCU_UPDATEVV_DB
  6854. // Update the version vector in the database.
  6855. //
  6856. if (FlagChk(ISCU_UPDATEVV_DB)) {
  6857. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO DB Update VV");
  6858. FStatus = DbsUpdateVV(ThreadCtx,
  6859. Replica,
  6860. ChangeOrder->RtCtx,
  6861. ChangeOrder->Cmd.FrsVsn,
  6862. &ChangeOrder->Cmd.OriginatorGuid);
  6863. DPRINT_FS(0,"++ ERROR - DbsUpdateVV failed.", FStatus);
  6864. FRS_ASSERT(FRS_SUCCESS(FStatus) || !"ISCU_UPDATEVV_DB failed");
  6865. }
  6866. //
  6867. // ISCU_ACK_INBOUND
  6868. // Let the replica subsystem know this change order is done.
  6869. // Restore the Partner's sequence number to use in the Ack.
  6870. // The Ack is done after the VV update so we pickup the VSN for the
  6871. // most recent VV update for this change order originator.
  6872. //
  6873. if (FlagChk(ISCU_ACK_INBOUND)) {
  6874. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO Ack Inbound");
  6875. RcsInboundCommitOk(Replica, ChangeOrder);
  6876. }
  6877. //
  6878. // ISCU_INS_OUTLOG
  6879. // ISCU_INS_OUTLOG_NEW_GUID
  6880. // Insert the change order into the Outbound log if any outbound partners.
  6881. //
  6882. if (FlagChk((ISCU_INS_OUTLOG | ISCU_INS_OUTLOG_NEW_GUID)) &&
  6883. !NO_OUTLOG_PARTNERS(Replica)) {
  6884. //
  6885. // Need the list lock to preserve lock ordering when we unidle the queue.
  6886. // This serializes any insert into outlog and update of inlog.
  6887. // OutlogInserCo overwrites SequenceNumber which can confuse inlog update.
  6888. //
  6889. FrsRtlAcquireListLock(&FrsVolumeLayerCOList);
  6890. ChgOrdAcquireLockGuid(ChangeOrder);
  6891. //
  6892. // Increment the (Local and Remote) CO propagated counters
  6893. //
  6894. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO)) {
  6895. PM_INC_CTR_REPSET(Replica, LCOPropagated, 1);
  6896. }
  6897. //
  6898. // It's a Remote CO
  6899. //
  6900. else if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_CONTROL)) {
  6901. PM_INC_CTR_REPSET(Replica, RCOPropagated, 1);
  6902. }
  6903. TableCtx = (ChangeOrder->RtCtx != NULL) ?
  6904. &ChangeOrder->RtCtx->OUTLOGTable : NULL;
  6905. FRS_ASSERT(TableCtx != NULL);
  6906. SAVE_CHANGE_ORDER_STATE(ChangeOrder, SaveState, SaveFlags);
  6907. //
  6908. // Update the CO state and clear the flags that should not be
  6909. // sent to the outbound partner.
  6910. //
  6911. SET_CHANGE_ORDER_STATE(ChangeOrder, IBCO_OUTBOUND_REQUEST);
  6912. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_GROUP_OL_CLEAR);
  6913. //
  6914. // Remote retry change orders are inserted into the outbound log
  6915. // twice; once after the failed install and again after the successful
  6916. // install. Needless to say, the second insert may generate duplicate
  6917. // key errors unless it is assigned a new guid.
  6918. //
  6919. // This deals with the problem of an outbound partner doing a VVJOIN
  6920. // while we still have remote COs in an install retry state. If the
  6921. // original remote CO was inserted into the outlog before the VVJOIN
  6922. // request arrived then the remote CO may never have been sent
  6923. // on to the outbound partner. But when processing the VVJOIN, the
  6924. // IDTable won't be up to date yet (since the install isn't finished)
  6925. // so the downstream partner may not see the file (or the new contents
  6926. // if this is an update). Inserting the remote CO (for a retry) into
  6927. // the outlog a second time closes this window.
  6928. //
  6929. if (FlagChk(ISCU_INS_OUTLOG_NEW_GUID)) {
  6930. COPY_GUID(&OldGuid, &ChangeOrder->Cmd.ChangeOrderGuid);
  6931. FrsUuidCreate(&ChangeOrder->Cmd.ChangeOrderGuid);
  6932. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO Ins Outlog with new guid");
  6933. }
  6934. FStatus = OutLogInsertCo(ThreadCtx, Replica, TableCtx, ChangeOrder);
  6935. //
  6936. // Restore the original guid
  6937. //
  6938. if (FlagChk(ISCU_INS_OUTLOG_NEW_GUID)) {
  6939. COPY_GUID(&ChangeOrder->Cmd.ChangeOrderGuid, &OldGuid);
  6940. }
  6941. RESTORE_CHANGE_ORDER_STATE(ChangeOrder, SaveState, SaveFlags);
  6942. if (FStatus == FrsErrorKeyDuplicate) {
  6943. //
  6944. // Currently we can't rely on this check to filter out bogus
  6945. // asserts. Consider the following case: The same CO arrives from
  6946. // two inbound partners, A & B. CO-A completes the stage file
  6947. // fetch but was blocked from the install due to sharing violation.
  6948. // It goes thru retry and does its partner ACK and the outlog
  6949. // insert and the VV Update.
  6950. //
  6951. // Now CO-B, which was in the process queue so it got past the VV
  6952. // checks, goes to Fetch and finds the stage file already there
  6953. // then it goes thru retry with the same sharing violation (or it
  6954. // may even finish the install) and then it too tries to do the
  6955. // OUTLOG insert. It's not a recovery CO so it will Assert. We
  6956. // could test to see if the stage file was already present and
  6957. // skip the assert but that doesn't handle the case of two delete
  6958. // Change Orders since they don't have a staging file.
  6959. //
  6960. // So, until we can devise a more competent test we
  6961. // will treat it as a warning and bag the assert for now.
  6962. //
  6963. if (!RecoveryCo(ChangeOrder)) {
  6964. DPRINT(1, "++ WARNING -- Duplicate key insert into OUTLOG. Not a recovery CO\n");
  6965. }
  6966. FStatus = FrsErrorSuccess;
  6967. }
  6968. else
  6969. if (!FRS_SUCCESS(FStatus)) {
  6970. DPRINT1(0, "++ ERROR - Don't send %08x %08x to outbound; database error\n",
  6971. PRINTQUAD(CoCmd->FrsVsn));
  6972. FRS_PRINT_TYPE(0, ChangeOrder);
  6973. FRS_ASSERT(!"ISCU_INS_OUTLOG failed");
  6974. }
  6975. //
  6976. // Release the lock to allow parallelism.
  6977. //
  6978. ChgOrdReleaseLockGuid(ChangeOrder);
  6979. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  6980. }
  6981. //
  6982. // ISCU_DEL_IDT_ENTRY
  6983. // Delete the IDTable record for this CO.
  6984. //
  6985. if (FlagChk(ISCU_DEL_IDT_ENTRY)) {
  6986. TableCtx = (ChangeOrder->RtCtx != NULL) ? &ChangeOrder->RtCtx->IDTable
  6987. : NULL;
  6988. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO IDTable Delete");
  6989. FStatus = DbsDeleteIDTableRecord(ThreadCtx,
  6990. Replica,
  6991. TableCtx,
  6992. &CoCmd->FileGuid);
  6993. DPRINT_FS(0,"++ ERROR - DbsDeleteIDTableRecord failed.", FStatus);
  6994. FRS_ASSERT(FRS_SUCCESS(FStatus) || !"ISCU_DEL_IDT_ENTRY failed");
  6995. //
  6996. // Remove the parent FID table entry too. Might not be there if didn't
  6997. // get to Install Rename state on the CO.
  6998. //
  6999. FRS_ASSERT(pVme != NULL); // to make prefast happy
  7000. GStatus = QHashDelete(pVme->ParentFidTable, &ChangeOrder->FileReferenceNumber);
  7001. if (GStatus != GHT_STATUS_SUCCESS ) {
  7002. DPRINT1(4, "++ WARNING: QHashDelete of ParentFidTable Entry, status: %d\n", GStatus);
  7003. }
  7004. if (CoCmdIsDirectory(CoCmd)) {
  7005. //
  7006. // If this was an aborted create there might be a dir table entry.
  7007. // Delete it and the filter table entry too.
  7008. //
  7009. TableCtx = (ChangeOrder->RtCtx != NULL) ? &ChangeOrder->RtCtx->DIRTable
  7010. : NULL;
  7011. FStatus = DbsDeleteDIRTableRecord(ThreadCtx,
  7012. Replica,
  7013. TableCtx,
  7014. &CoCmd->FileGuid);
  7015. if (FRS_SUCCESS(FStatus)) {
  7016. //
  7017. // If this is a remote CO then we may need to clean up the
  7018. // Journal's Dir Filter table too. If it's a local CO
  7019. // then leave it alone since the journal handles the
  7020. // creates and deletes of filter table entries by itself.
  7021. //
  7022. if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO)) {
  7023. JrnlAcquireChildLock(Replica);
  7024. WStatus = JrnlDeleteDirFilterEntry(pVme->FilterTable,
  7025. &ChangeOrder->FileReferenceNumber,
  7026. NULL);
  7027. JrnlReleaseChildLock(Replica);
  7028. DPRINT1_WS(4, "++ WARN - deleting dir filter entry for %ws;", CoCmd->FileName, WStatus);
  7029. }
  7030. } else {
  7031. DPRINT_FS(4,"++ WARNING - Dir table record delete failed.", FStatus);
  7032. }
  7033. }
  7034. }
  7035. //
  7036. // Check if any hold issue conflict table cleanup flag is on otherwise we can
  7037. // skip getting the lock.
  7038. //
  7039. if (FlagChk(ISCU_HOLDIS_CLEANUP)) {
  7040. //
  7041. // Need the list lock to preserve lock ordering when we unidle the queue.
  7042. //
  7043. FrsRtlAcquireListLock(&FrsVolumeLayerCOList);
  7044. ChgOrdAcquireLockGuid(ChangeOrder);
  7045. //
  7046. // Don't go to the Retire Started state if this is a Retry Co.
  7047. //
  7048. if (!RetryCo) {
  7049. SET_CHANGE_ORDER_STATE(ChangeOrder, IBCO_RETIRE_STARTED);
  7050. }
  7051. //
  7052. // ISCU_AIBCO
  7053. // Find the change order in the Active inbound change order table.
  7054. // Returns a reference.
  7055. //
  7056. if (FlagChk(ISCU_AIBCO)) {
  7057. GStatus = GhtLookup(pVme->ActiveInboundChangeOrderTable,
  7058. &CoCmd->FileGuid,
  7059. TRUE,
  7060. &ActiveChangeOrder);
  7061. if (GStatus != GHT_STATUS_SUCCESS) {
  7062. DPRINT(0, "++ ERROR - Inbound ChangeOrder not found in ActiveInboundChangeOrderTable.\n");
  7063. ReleaseVmeRef(pVme);
  7064. ChgOrdReleaseLockGuid(ChangeOrder);
  7065. FRS_ASSERT(!"Inbound CO not found in ActiveInboundChangeOrderTable");
  7066. }
  7067. if (ChangeOrder != ActiveChangeOrder) {
  7068. DPRINT(0, "++ ERROR - Inbound ChangeOrder not equal ActiveChangeOrder\n");
  7069. DPRINT(0, "++ Change order in Active Table:\n");
  7070. FRS_PRINT_TYPE(0, ActiveChangeOrder);
  7071. DPRINT(0, "++ Change order to be retired:\n");
  7072. FRS_PRINT_TYPE(0, ChangeOrder);
  7073. FRS_ASSERT(!"Inbound ChangeOrder not equal ActiveChangeOrder");
  7074. ChgOrdReleaseLockGuid(ChangeOrder);
  7075. }
  7076. }
  7077. //
  7078. // ISCU_NC_TABLE
  7079. // Remove the entry in the Name conflict table
  7080. //
  7081. // If the reference count goes to 0, then pull the "Unblock the pVme
  7082. // process queue" flag into the change order and remove the qhash entry.
  7083. //
  7084. // NameConflictTable Entry:
  7085. // An entry in the NameConflictTable contains a reference count of the
  7086. // number of active change orders that hash to that entry and a Flags
  7087. // word that is set to COE_FLAG_VOL_COLIST_BLOCKED if the process queue
  7088. // for this volume is idled while waiting on the active change orders
  7089. // for this entry to retire. The queue is idled when the entry at the
  7090. // head of the queue hashes to this entry and so may have a conflicting
  7091. // name (hence the name, "name conflict table").
  7092. //
  7093. // The NameConflictTable can give false positives. But this is okay
  7094. // because a false positive is rare and will only idle the process queue
  7095. // until the active change order retires. Getting rid of the rare false
  7096. // positives would degrade performance. The false positives that happen
  7097. // when inserting an entry into the NameConflictTable are handled by
  7098. // using the QData field in the QHashEntry as a the reference count.
  7099. //
  7100. // But, you ask, how can there be multiple active cos hashing to this
  7101. // entry if the process queue is idled when a conflict is detected?
  7102. // Easy, I say, because the filename in the co is used to detect
  7103. // collisions while the filename in the idtable is used to reserve the
  7104. // qhash entry. Why? Well, the name morph code handles the case of a
  7105. // co's name colliding with an idtable entry. But that code wouldn't
  7106. // work if active change orders were constantly changing the idtable.
  7107. // So, the NameConflictTable synchronizes the namespace amoung active
  7108. // change orders so that the name morph code can work against a static
  7109. // namespace.
  7110. //
  7111. if (FlagChk(ISCU_NC_TABLE) &&
  7112. (ChangeOrder->NameConflictHashValue != QUADZERO) &&
  7113. (DOES_CO_REMOVE_FILE_NAME(CoCmd) || DOES_CO_DO_SIMPLE_RENAME(CoCmd))) {
  7114. PQHASH_TABLE Nct = Replica->NameConflictTable;
  7115. //
  7116. // Lock the name conflict table and find the entry for this CO
  7117. //
  7118. QHashAcquireLock(Nct);
  7119. QHashEntry = QHashLookupLock(Nct, &ChangeOrder->NameConflictHashValue);
  7120. //
  7121. // NO ENTRY! ASSERT
  7122. //
  7123. if (QHashEntry == NULL) {
  7124. DPRINT1(0, "++ ERROR - QHashLookupLock(NameConflictTable, %08x %08x) not found.\n",
  7125. PRINTQUAD(ChangeOrder->NameConflictHashValue));
  7126. FRS_PRINT_TYPE(0, ChangeOrder);
  7127. FRS_ASSERT(!"IssueCleanup: NameConflictTable entry not found");
  7128. } else {
  7129. //
  7130. // If last reference, unblock the process queue if it's blocked
  7131. // and delete the entry.
  7132. //
  7133. if ((--QHashEntry->QData) == QUADZERO) {
  7134. if (QHashEntry->Flags & ((ULONG_PTR)COE_FLAG_VOL_COLIST_BLOCKED)) {
  7135. SET_COE_FLAG(ChangeOrder, COE_FLAG_VOL_COLIST_BLOCKED);
  7136. }
  7137. QHashDeleteLock(Nct, &ChangeOrder->NameConflictHashValue);
  7138. }
  7139. //
  7140. // Unlock the name conflict table and remove the CO reference.
  7141. //
  7142. QHashReleaseLock(Nct);
  7143. ChangeOrder->NameConflictHashValue = QUADZERO;
  7144. }
  7145. }
  7146. //
  7147. // ISCU_CHECK_ISSUE_BLOCK
  7148. // Check the queue blocked flag on the change order.
  7149. // Note - The value for RestartQueue may be overwritten below.
  7150. //
  7151. if (FlagChk(ISCU_CHECK_ISSUE_BLOCK)) {
  7152. RestartQueue = COE_FLAG_ON(ChangeOrder, COE_FLAG_VOL_COLIST_BLOCKED);
  7153. if (RestartQueue) {
  7154. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_VOL_COLIST_BLOCKED);
  7155. }
  7156. }
  7157. //
  7158. // ISCU_AIBCO
  7159. // Drop the reference above on the Change order and remove the entry
  7160. // in the AIBCO table.
  7161. //
  7162. if (FlagChk(ISCU_AIBCO)) {
  7163. GhtDereferenceEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  7164. ActiveChangeOrder,
  7165. TRUE);
  7166. //
  7167. // Take the change order out of the Active table.
  7168. //
  7169. GhtRemoveEntryByAddress(pVme->ActiveInboundChangeOrderTable,
  7170. ActiveChangeOrder,
  7171. TRUE);
  7172. }
  7173. //
  7174. // ISCU_ACTIVE_CHILD
  7175. // Check the parent entry in the ActiveChildren hash table. If there is
  7176. // a change order pending on the parent and the count of active children
  7177. // has gone to zero then we can restart the queue.
  7178. //
  7179. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_JUST_TOMBSTONE) &&
  7180. (ChangeOrder->ParentFileReferenceNumber == ZERO_FID)) {
  7181. //
  7182. // There is no active child interlock if this was a tombstone
  7183. // create and there was no parent. Barf.
  7184. //
  7185. ClearFlag(CleanUpFlags, ISCU_ACTIVE_CHILD);
  7186. }
  7187. if (FlagChk(ISCU_ACTIVE_CHILD)) {
  7188. QHashAcquireLock(ActiveChildren);
  7189. BusyParentGuid = &CoCmd->NewParentGuid;
  7190. GuidToStr(BusyParentGuid, BusyParentGuidStr);
  7191. QHashEntry = QHashLookupLock(ActiveChildren, BusyParentGuid);
  7192. if (QHashEntry != NULL) {
  7193. ULONGLONG Count;
  7194. QHashEntry->QData -= 2;
  7195. DPRINT2(4, "++ GAC Dec Count on %s, QData %08x \n",
  7196. BusyParentGuidStr, QHashEntry->QData);
  7197. PendingCoOnParent = (QHashEntry->QData & 1) != 0;
  7198. Count = QHashEntry->QData >> 1;
  7199. //
  7200. // If the count is zero and there is a blocked pending CO
  7201. // waiting on our parent to be unbusy then unblock the queue.
  7202. // Either way when the count is zero remove the entry from the
  7203. // table. Also propagate the restart queue flag forward.
  7204. //
  7205. if (Count == 0) {
  7206. if (!RestartQueue) {
  7207. RestartQueue = PendingCoOnParent;
  7208. }
  7209. QHashDeleteLock(ActiveChildren, BusyParentGuid);
  7210. DPRINT3(4, "++ GAC - ActiveChild count zero on %s, PendCoOnParent %d, Rq %d\n",
  7211. BusyParentGuidStr, PendingCoOnParent, RestartQueue);
  7212. } else {
  7213. DPRINT4(4, "++ GAC - ActiveChild count (%d) not zero on %s, PendCoOnParent %d, Rq %d\n",
  7214. Count, BusyParentGuidStr, PendingCoOnParent,
  7215. RestartQueue);
  7216. }
  7217. } else {
  7218. DPRINT1(0, "++ ERROR - GAC - Did not find entry in ActiveChildren Table for %s\n",
  7219. BusyParentGuidStr);
  7220. FRS_ASSERT(!"Did not find entry in ActiveChildren Table");
  7221. }
  7222. //
  7223. // If this CO has a old parent and a new parent (e.g. MOVE_DIR) then
  7224. // we need to unblock both parents. Check for that and unblock both.
  7225. //
  7226. if (!GUIDS_EQUAL(&CoCmd->NewParentGuid, &CoCmd->OldParentGuid)) {
  7227. BusyParentGuid = &CoCmd->OldParentGuid;
  7228. GuidToStr(BusyParentGuid, BusyParentGuidStr);
  7229. QHashEntry = QHashLookupLock(ActiveChildren, BusyParentGuid);
  7230. if (QHashEntry != NULL) {
  7231. ULONGLONG Count;
  7232. QHashEntry->QData -= 2;
  7233. DPRINT2(4, "++ GAC Dec Count on %s, QData %08x \n",
  7234. BusyParentGuidStr, QHashEntry->QData);
  7235. PendingCoOnParent = (QHashEntry->QData & 1) != 0;
  7236. Count = QHashEntry->QData >> 1;
  7237. //
  7238. // If the count is zero and there is a blocked pending CO
  7239. // waiting on our parent to be unbusy then unblock the queue.
  7240. // Either way when the count is zero remove the entry from the
  7241. // table. Also propagate the restart queue flag forward.
  7242. //
  7243. if (Count == 0) {
  7244. if (!RestartQueue) {
  7245. RestartQueue = PendingCoOnParent;
  7246. }
  7247. QHashDeleteLock(ActiveChildren, BusyParentGuid);
  7248. DPRINT3(4, "++ GAC - ActiveChild count zero on %s, PendCoOnParent %d, Rq %d\n",
  7249. BusyParentGuidStr, PendingCoOnParent, RestartQueue);
  7250. } else {
  7251. DPRINT4(4, "++ GAC - ActiveChild count (%d) not zero on %s, PendCoOnParent %d, Rq %d\n",
  7252. Count, BusyParentGuidStr, PendingCoOnParent,
  7253. RestartQueue);
  7254. }
  7255. } else {
  7256. DPRINT1(0, "++ ERROR - GAC - Did not find entry in ActiveChildren Table for %s\n",
  7257. BusyParentGuidStr);
  7258. FRS_ASSERT(!"Did not find entry in ActiveChildren Table");
  7259. }
  7260. }
  7261. }
  7262. //
  7263. // Now un-idle change order process queue for this volume and drop the locks.
  7264. //
  7265. if (RestartQueue && (pVme != NULL)) {
  7266. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO Process Q Unblock");
  7267. FrsRtlUnIdledQueueLock(&pVme->ChangeOrderList);
  7268. }
  7269. if (FlagChk(ISCU_ACTIVE_CHILD)) {
  7270. QHashReleaseLock(ActiveChildren);
  7271. }
  7272. ChgOrdReleaseLockGuid(ChangeOrder);
  7273. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  7274. } // end of if (FlagChk(ISCU_HOLDIS_CLEANUP))
  7275. if (pVme != NULL) {
  7276. ReleaseVmeRef(pVme);
  7277. }
  7278. //
  7279. // Need the list lock to preserve lock ordering when we unidle the queue.
  7280. // This serializes any insert into outlog and update of inlog.
  7281. // OutlogInserCo overwrites SequenceNumber which can confuse inlog update.
  7282. //
  7283. FrsRtlAcquireListLock(&FrsVolumeLayerCOList);
  7284. ChgOrdAcquireLockGuid(ChangeOrder);
  7285. //
  7286. // ISCU_DEC_CO_REF
  7287. // Decrement the ref count on the change order.
  7288. // Then use the ref count to gate whether or not to do the mem free ops
  7289. // and delete the inbound log record.
  7290. //
  7291. if (FlagChk(ISCU_DEC_CO_REF)) {
  7292. RefCount = DECREMENT_CHANGE_ORDER_REF_COUNT(ChangeOrder);
  7293. } else {
  7294. RefCount = GET_CHANGE_ORDER_REF_COUNT(ChangeOrder);
  7295. }
  7296. //
  7297. // ISCU_DEL_INLOG (1)
  7298. // If this CO has directory enumeration pending then do not delete it
  7299. // from the inbound log. Instead update the state and flags.
  7300. // The retry thread will later reprocess it to perform the enumeration
  7301. // and perform the final cleanup.
  7302. // The dir enum decision was made by ChgOrdUpdateIDTableRecord() above.
  7303. // Currently this is the only instance of a change order record being
  7304. // recycled for a "second pass" operation. Once the inlog record state is
  7305. // updated we can notify the INLOG retry thread.
  7306. //
  7307. if ((RefCount == 0) && FlagChk(ISCU_DEL_INLOG) && !FlagChk(ISCU_CO_ABORT)) {
  7308. CHANGE_ORDER_TRACE(3, ChangeOrder, "Checking ENUM Pending");
  7309. if (CO_IFLAG_ON(ChangeOrder, CO_IFLAG_DIR_ENUM_PENDING)) {
  7310. ClearFlag(CleanUpFlags, ISCU_DEL_INLOG);
  7311. SetFlag(CleanUpFlags, ISCU_UPDATE_INLOG);
  7312. SET_CHANGE_ORDER_STATE(ChangeOrder, IBCO_ENUM_REQUESTED);
  7313. NotifyRetryThread = TRUE;
  7314. //
  7315. // This CO is now a retry change order.
  7316. //
  7317. SET_CO_FLAG(ChangeOrder, CO_FLAG_RETRY);
  7318. //
  7319. // Prevent deletion of IDTable entry later when we retry.
  7320. //
  7321. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_NEW_FILE);
  7322. FRS_PRINT_TYPE(3, ChangeOrder);
  7323. }
  7324. }
  7325. //
  7326. // ISCU_DEL_INLOG (2)
  7327. // Delete the inbound log record for this CO. This could happen either on
  7328. // the main retire path if the change order propagates to the outbound log
  7329. // immediately or it could happen later when the propagation is unblocked.
  7330. //
  7331. if ((RefCount == 0) && FlagChk(ISCU_DEL_INLOG)) {
  7332. if (ChangeOrder->RtCtx != NULL) {
  7333. TableCtx = &ChangeOrder->RtCtx->INLOGTable;
  7334. pDataRecord = TableCtx->pDataRecord;
  7335. } else {
  7336. TableCtx = NULL;
  7337. pDataRecord = NULL;
  7338. }
  7339. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO INlog Delete");
  7340. //
  7341. // If a retry CO, bump the Inbound retry table seq num so the retry
  7342. // thread can detect a table change when it is re-issuing retry COs.
  7343. //
  7344. if (RetryCo) {
  7345. InterlockedIncrement(&Replica->AIRSequenceNum);
  7346. }
  7347. FStatus = DbsDeleteTableRecordByIndex(ThreadCtx,
  7348. Replica,
  7349. TableCtx,
  7350. &CoCmd->SequenceNumber,
  7351. ILSequenceNumberIndexx,
  7352. INLOGTablex);
  7353. DPRINT_FS(0,"++ ERROR - DbsDeleteTableRecordByIndex failed.", FStatus);
  7354. if (!FRS_SUCCESS(FStatus)) {
  7355. FRS_PRINT_TYPE(0, ChangeOrder);
  7356. if(FStatus == FrsErrorDbWriteConflict) {
  7357. //
  7358. // We weren't able to Delete the table record,
  7359. // possibly because another thread was processing a duplicate CO
  7360. // at the same time. So let's retry this later.
  7361. //
  7362. DPRINT(0, "ISCU_DEL_INLOG (2) failed.");
  7363. NotifyRetryThread = TRUE;
  7364. } else {
  7365. //
  7366. // We failed to delete the record for some other reason.
  7367. // ASSERT!!
  7368. //
  7369. FRS_ASSERT(!"ISCU_DEL_INLOG (2) failed.");
  7370. }
  7371. }
  7372. //
  7373. // The data record for the Inbound Log is the Change Order Command.
  7374. // Make the ptr NULL here so when we free the TableCtx later we don't
  7375. // also try to free the data record.
  7376. //
  7377. if (pDataRecord != NULL) {
  7378. TableCtx->pDataRecord = NULL;
  7379. //
  7380. // Clear the Jet Set/Ret Col address fields for the Change Order
  7381. // Extension buffer to prevent reuse since that buffer goes with the CO.
  7382. //
  7383. DBS_SET_FIELD_ADDRESS(TableCtx, COExtensionx, NULL);
  7384. }
  7385. } else
  7386. // ISCU_UPDATE_INLOG
  7387. if (FlagChk(ISCU_UPDATE_INLOG)) {
  7388. //
  7389. // If we aren't deleting the inlog record and the request is to update
  7390. // the state fields then do that now.
  7391. //
  7392. // The inbound log table associated with this change order may not
  7393. // be open if this is a retry operation that has been re-issued.
  7394. //
  7395. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO INlog Update");
  7396. // FRS_ASSERT(ChangeOrder->RtCtx != NULL)
  7397. if (ChangeOrder->RtCtx == NULL) {
  7398. DPRINT(5, "Using temp inlog tablectx\n");
  7399. InLogTableCtx = &TempTableCtx;
  7400. InLogTableCtx->TableType = TABLE_TYPE_INVALID;
  7401. InLogTableCtx->Tid = JET_tableidNil;
  7402. InLogTableCtx->pDataRecord = NULL;
  7403. } else {
  7404. InLogTableCtx = &ChangeOrder->RtCtx->INLOGTable;
  7405. }
  7406. if (InLogTableCtx->pDataRecord == NULL) {
  7407. jerr = DbsOpenTable(ThreadCtx,
  7408. InLogTableCtx,
  7409. Replica->ReplicaNumber,
  7410. INLOGTablex,
  7411. CoCmd);
  7412. if (!JET_SUCCESS(jerr)) {
  7413. JrnlSetReplicaState(Replica, REPLICA_STATE_ERROR);
  7414. FStatus = DbsTranslateJetError(jerr, TRUE);
  7415. FRS_ASSERT(!"ISCU_UPDATE_INLOG - DbsOpenTable failed");
  7416. }
  7417. }
  7418. //
  7419. // Seek to the change order record. Use the SequenceNumber
  7420. // instead of the ChangeOrderGuid because the ChangeOrderGuid
  7421. // is not unique.
  7422. //
  7423. jerr = DbsSeekRecord(ThreadCtx,
  7424. &CoCmd->SequenceNumber,
  7425. ILSequenceNumberIndexx,
  7426. InLogTableCtx);
  7427. if (!JET_SUCCESS(jerr)) {
  7428. DPRINT1_JS(0, "++ ERROR - DbsSeekRecord on %ws :",
  7429. Replica->ReplicaName->Name, jerr);
  7430. JrnlSetReplicaState(Replica, REPLICA_STATE_ERROR);
  7431. FRS_ASSERT(!"ISCU_UPDATE_INLOG - DbsSeekRecord failed");
  7432. }
  7433. //
  7434. // Update the state fields in the inbound log change order record.
  7435. //
  7436. FStatus = DbsWriteTableFieldMult(ThreadCtx,
  7437. Replica->ReplicaNumber,
  7438. InLogTableCtx,
  7439. UpdateInlogState,
  7440. ARRAY_SZ(UpdateInlogState));
  7441. DPRINT_FS(0,"++ ERROR - Update of State in Inlog record failed.", FStatus);
  7442. FRS_ASSERT(FRS_SUCCESS(FStatus) || !"ISCU_UPDATE_INLOG: UpdateInlogState failed");
  7443. DPRINT2(4, "++ Updating CO Flags for retry: %08x , CO_IFLAGS %08x \n",
  7444. CoCmd->Flags, CoCmd->IFlags);
  7445. //
  7446. // The data record for the Inbound Log is in the Change Order Command.
  7447. // Make the ptr NULL here so when we free the TableCtx below we don't
  7448. // also try to free the data record.
  7449. //
  7450. InLogTableCtx->pDataRecord = NULL;
  7451. //
  7452. // Close the inlog table.
  7453. //
  7454. DbsCloseTable(jerr, ThreadCtx->JSesid, InLogTableCtx);
  7455. //
  7456. // Clear the Jet Set/Ret Col address fields for the Change Order
  7457. // Extension buffer to prevent reuse since that buffer goes with the CO.
  7458. //
  7459. DBS_SET_FIELD_ADDRESS(InLogTableCtx, COExtensionx, NULL);
  7460. }
  7461. //
  7462. // ISCU_DEL_RTCTX
  7463. // Free the Replica-Thread context.
  7464. //
  7465. if ((RefCount == 0) && FlagChk(ISCU_DEL_RTCTX)) {
  7466. FStatus = DbsFreeRtCtx(ThreadCtx, Replica, ChangeOrder->RtCtx, TRUE);
  7467. ChangeOrder->RtCtx = NULL;
  7468. }
  7469. //
  7470. // ISCU_FREE_CO
  7471. // Free the change order and any duplicates. The active change order
  7472. // serves as the head of the list.
  7473. //
  7474. if ((RefCount == 0) && FlagChk(ISCU_FREE_CO)) {
  7475. //
  7476. // Pull the entry for this CO out of the inlog retry table.
  7477. // If the sequence number is zero then it never went into the Inlog.
  7478. // e.g. A control change order or a rejected change order.
  7479. //
  7480. SeqNum = (ULONGLONG) CoCmd->SequenceNumber;
  7481. if (SeqNum != QUADZERO) {
  7482. //
  7483. // Get the lock on the Active Retry table.
  7484. //
  7485. QHashAcquireLock(Replica->ActiveInlogRetryTable);
  7486. if (QHashLookupLock(Replica->ActiveInlogRetryTable, &SeqNum) != NULL) {
  7487. QHashDeleteLock(Replica->ActiveInlogRetryTable, &SeqNum);
  7488. } else {
  7489. DPRINT1(1, "++ Warning: ActiveInlogRetryTable QHashDelete error on seq num: %08x %08x\n",
  7490. PRINTQUAD(SeqNum));
  7491. }
  7492. //
  7493. // Bump the sequence number to signal that the state of the
  7494. // Inlog has changed so the retry thread can know to do a re-read
  7495. // of the inlog record if the retry thread is currently active.
  7496. // See ChgOrdRetryWorker().
  7497. //
  7498. InterlockedIncrement(&Replica->AIRSequenceNum);
  7499. QHashReleaseLock(Replica->ActiveInlogRetryTable);
  7500. }
  7501. if (ChangeOrder->Cxtion != NULL) {
  7502. DROP_CO_CXTION_COUNT(Replica, ChangeOrder, ERROR_SUCCESS);
  7503. }
  7504. FRS_ASSERT(ChangeOrder->DupCoList.Next == NULL);
  7505. #if 0
  7506. while (ChangeOrder->DupCoList.Next != NULL) {
  7507. Entry = PopEntryList(&ChangeOrder->DupCoList);
  7508. DupChangeOrder = CONTAINING_RECORD(Entry, CHANGE_ORDER_ENTRY, DupCoList);
  7509. //
  7510. // Let the replica subsystem know this change order is done. These
  7511. // change orders were never issued so their Sequence number is OK.
  7512. //
  7513. RcsInboundCommitOk(Replica, DupChangeOrder);
  7514. FrsFreeType(DupChangeOrder);
  7515. }
  7516. #endif
  7517. //
  7518. // RELEASE the lock BEFORE freeing the CO.
  7519. //
  7520. ChgOrdReleaseLockGuid(ChangeOrder);
  7521. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  7522. FrsFreeType(ChangeOrder);
  7523. } else {
  7524. //
  7525. // RELEASE the lock IF NOT freeing the CO.
  7526. //
  7527. ChgOrdReleaseLockGuid(ChangeOrder);
  7528. FrsRtlReleaseListLock(&FrsVolumeLayerCOList);
  7529. }
  7530. //
  7531. // Tell the Inlog retry thread there is work to do.
  7532. //
  7533. if (NotifyRetryThread) {
  7534. InterlockedIncrement(&Replica->InLogRetryCount);
  7535. }
  7536. return FStatus;
  7537. }
  7538. ULONG
  7539. ChgOrdUpdateIDTableRecord(
  7540. PTHREAD_CTX ThreadCtx,
  7541. PREPLICA Replica,
  7542. PCHANGE_ORDER_ENTRY ChangeOrder
  7543. )
  7544. /*++
  7545. Routine Description:
  7546. Update the ID Table record for this change order. Update the DIR table
  7547. entry as well.
  7548. Arguments:
  7549. ThreadCtx -- A Thread context to use for dbid and sesid.
  7550. Replica -- The Replica ID table to do the lookup in.
  7551. ChangeOrder-- The change order.
  7552. Return Value:
  7553. Frs Status
  7554. --*/
  7555. {
  7556. #undef DEBSUB
  7557. #define DEBSUB "ChgOrdUpdateIDTableRecord:"
  7558. ULONGLONG TombstoneLife;
  7559. FRS_ERROR_CODE FStatus;
  7560. ULONG GStatus;
  7561. PREPLICA_THREAD_CTX RtCtx;
  7562. PTABLE_CTX IDTableCtx, DIRTableCtx;
  7563. PCHANGE_ORDER_COMMAND CoCmd = &ChangeOrder->Cmd;
  7564. PIDTABLE_RECORD IDTableRec;
  7565. PCONFIG_TABLE_RECORD ConfigRecord;
  7566. ULONG Len;
  7567. ULONG LocationCmd;
  7568. BOOL DeleteCo, NewFile, RemoteCo;
  7569. BOOL InsertFlag;
  7570. FRS_ASSERT(Replica != NULL);
  7571. FRS_ASSERT(ChangeOrder != NULL);
  7572. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  7573. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  7574. DeleteCo = (LocationCmd == CO_LOCATION_DELETE) ||
  7575. (LocationCmd == CO_LOCATION_MOVEOUT);
  7576. RemoteCo = !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  7577. //
  7578. // The Replica Thread Ctx in the change order has all the update info.
  7579. //
  7580. RtCtx = ChangeOrder->RtCtx;
  7581. FRS_ASSERT(RtCtx != NULL);
  7582. IDTableCtx = &RtCtx->IDTable;
  7583. FRS_PRINT_TYPE(4, ChangeOrder);
  7584. IDTableRec = IDTableCtx->pDataRecord;
  7585. FRS_ASSERT(IDTableRec != NULL);
  7586. if (ChangeOrder->FileReferenceNumber == ZERO_FID) {
  7587. //
  7588. // This should not occur.
  7589. //
  7590. FRS_PRINT_TYPE(0, ChangeOrder);
  7591. DBS_DISPLAY_RECORD_SEV(0, IDTableCtx, FALSE);
  7592. FRS_ASSERT(!"ChgOrdUpdateIDTableRecord failed with zero FID");
  7593. }
  7594. //
  7595. // Update the IDTable record from the change order.
  7596. //
  7597. IDTableRec->FileID = ChangeOrder->FileReferenceNumber;
  7598. IDTableRec->ParentGuid = CoCmd->NewParentGuid;
  7599. IDTableRec->ParentFileID = ChangeOrder->NewParentFid;
  7600. IDTableRec->VersionNumber = CoCmd->FileVersionNumber;
  7601. IDTableRec->EventTime = CoCmd->EventTime.QuadPart;
  7602. IDTableRec->OriginatorGuid = CoCmd->OriginatorGuid;
  7603. IDTableRec->OriginatorVSN = CoCmd->FrsVsn;
  7604. IDTableRec->CurrentFileUsn = CoCmd->FileUsn;
  7605. IDTableRec->FileCreateTime = ChangeOrder->FileCreateTime;
  7606. IDTableRec->FileWriteTime = ChangeOrder->FileWriteTime;
  7607. IDTableRec->FileSize = CoCmd->FileSize;
  7608. //IDTableRec->FileObjID =
  7609. Len = (ULONG) CoCmd->FileNameLength;
  7610. CopyMemory(IDTableRec->FileName, CoCmd->FileName, Len);
  7611. IDTableRec->FileName[Len/sizeof(WCHAR)] = UNICODE_NULL;
  7612. IDTableRec->FileIsDir = CoCmdIsDirectory(CoCmd);
  7613. IDTableRec->FileAttributes = CoCmd->FileAttributes;
  7614. IDTableRec->ReplEnabled = TRUE;
  7615. if (!DeleteCo) {
  7616. PDATA_EXTENSION_CHECKSUM CocDataChkSum, IdtDataChkSum;
  7617. PIDTABLE_RECORD_EXTENSION IdtExt;
  7618. PCHANGE_ORDER_RECORD_EXTENSION CocExt;
  7619. //
  7620. // If the Change Order has a file checksum, save it in the IDTable Record.
  7621. //
  7622. CocExt = CoCmd->Extension;
  7623. CocDataChkSum = DbsDataExtensionFind(CocExt, DataExtend_MD5_CheckSum);
  7624. if (CocDataChkSum != NULL) {
  7625. if (CocDataChkSum->Prefix.Size != sizeof(DATA_EXTENSION_CHECKSUM)) {
  7626. DPRINT1(0, "<MD5_CheckSum Size (%08x) invalid>\n",
  7627. CocDataChkSum->Prefix.Size);
  7628. }
  7629. DPRINT4(4, "NEW COC MD5: %08x %08x %08x %08x\n",
  7630. *(((ULONG *) &CocDataChkSum->Data[0])),
  7631. *(((ULONG *) &CocDataChkSum->Data[4])),
  7632. *(((ULONG *) &CocDataChkSum->Data[8])),
  7633. *(((ULONG *) &CocDataChkSum->Data[12])));
  7634. //
  7635. // We have a change order data checksum. Look for
  7636. // a data checksum entry in the IDTable record.
  7637. //
  7638. IdtExt = &IDTableRec->Extension;
  7639. IdtDataChkSum = DbsDataExtensionFind(IdtExt, DataExtend_MD5_CheckSum);
  7640. if (IdtDataChkSum != NULL) {
  7641. if (IdtDataChkSum->Prefix.Size != sizeof(DATA_EXTENSION_CHECKSUM)) {
  7642. DPRINT1(0, "<MD5_CheckSum Size (%08x) invalid>\n",
  7643. IdtDataChkSum->Prefix.Size);
  7644. }
  7645. DPRINT4(4, "OLD IDT MD5: %08x %08x %08x %08x\n",
  7646. *(((ULONG *) &IdtDataChkSum->Data[0])),
  7647. *(((ULONG *) &IdtDataChkSum->Data[4])),
  7648. *(((ULONG *) &IdtDataChkSum->Data[8])),
  7649. *(((ULONG *) &IdtDataChkSum->Data[12])));
  7650. } else {
  7651. //
  7652. // Init the extension buffer.
  7653. //
  7654. DPRINT(4, "OLD IDT MD5: Not present\n");
  7655. DbsDataInitIDTableExtension(IdtExt);
  7656. IdtDataChkSum = &IdtExt->DataChecksum;
  7657. }
  7658. //
  7659. // Copy the MD5 checksum into the IDTable Record.
  7660. //
  7661. if (IdtDataChkSum != NULL) {
  7662. CopyMemory(IdtDataChkSum->Data, CocDataChkSum->Data, MD5DIGESTLEN);
  7663. }
  7664. }
  7665. }
  7666. //
  7667. // This CO may have been on a New File. Always clear the flag here.
  7668. //
  7669. NewFile = IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS);
  7670. ClearIdRecFlag(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS);
  7671. if (CO_NEW_FILE(LocationCmd)) {
  7672. //
  7673. // A staging file for the local change order could not be created.
  7674. // We are giving up but we need any following changes to the file
  7675. // to be marked as a "create" so that future updates or renames
  7676. // will create the remote file. Otherwise, the remote side may attempt
  7677. // to "rename" a non-existant file.
  7678. //
  7679. // Note: if this CO is for a dir MOVEIN then the affect of aborting
  7680. // will be to skip the ENUM of the subtree. Not the best result.
  7681. //
  7682. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_STAGE_ABORTED)) {
  7683. SetIdRecFlag(IDTableRec, IDREC_FLAGS_CREATE_DEFERRED);
  7684. }
  7685. else
  7686. if (!RemoteCo && CO_MOVEIN_FILE(LocationCmd)) {
  7687. CHANGE_ORDER_TRACE(3, ChangeOrder, "MOVEIN Detected");
  7688. //
  7689. // This is a MOVEIN change order. If this is a directory then
  7690. // we need to enumerate the directory. Mark the IDTable entry as
  7691. // ENUM_PENDING. Ditto for the change order.
  7692. //
  7693. if (CoCmdIsDirectory(CoCmd)) {
  7694. CHANGE_ORDER_TRACE(3, ChangeOrder, "MOVEIN DIR Detected");
  7695. SetIdRecFlag(IDTableRec, IDREC_FLAGS_ENUM_PENDING);
  7696. SET_CO_IFLAG(ChangeOrder, CO_IFLAG_DIR_ENUM_PENDING);
  7697. }
  7698. }
  7699. }
  7700. //
  7701. // File is still using its temporary name. Rename the file when
  7702. // retiring the next change order for the file. Retry this
  7703. // change order in the interrim.
  7704. //
  7705. if ((!DeleteCo) &&
  7706. COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_RENAME)) {
  7707. SetIdRecFlag(IDTableRec, IDREC_FLAGS_RENAME_DEFERRED);
  7708. }
  7709. if (DeleteCo) {
  7710. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_DELETE)) {
  7711. //
  7712. // Remember that we still need to delete this file/dir.
  7713. //
  7714. SetIdRecFlag(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED);
  7715. } else {
  7716. //
  7717. // Clear the flag if we don't need to delete this file/dir.
  7718. //
  7719. ClearIdRecFlag(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED);
  7720. }
  7721. //
  7722. // Mark the file as deleted, clear the FID.
  7723. // Save the time in the TombStone garbage collect field.
  7724. //
  7725. SetIdRecFlag(IDTableRec, IDREC_FLAGS_DELETED);
  7726. TombstoneLife = (ULONGLONG) ConfigRecord->TombstoneLife; // days
  7727. TombstoneLife = TombstoneLife * (24 * 60 * 60); // convert to sec.
  7728. TombstoneLife = TombstoneLife * (10*1000*1000); // convert to 100 ns units.
  7729. TombstoneLife = TombstoneLife + CoCmd->EventTime.QuadPart;
  7730. COPY_TIME(&IDTableRec->TombStoneGC, &TombstoneLife);
  7731. } else
  7732. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_REANIMATION)) {
  7733. //
  7734. // This file or dir is coming back from the dead.
  7735. //
  7736. ClearIdRecFlag(IDTableRec, IDREC_FLAGS_DELETED);
  7737. ClearIdRecFlag(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED);
  7738. TombstoneLife = QUADZERO;
  7739. COPY_TIME(&IDTableRec->TombStoneGC, &TombstoneLife);
  7740. CHANGE_ORDER_TRACE(3, ChangeOrder, "Reviving IDTable Rec");
  7741. }
  7742. //
  7743. // Update the ID Table record.
  7744. //
  7745. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_REANIMATION)) {
  7746. DBS_DISPLAY_RECORD_SEV(3, IDTableCtx, FALSE);
  7747. FRS_PRINT_TYPE(3, ChangeOrder);
  7748. } else {
  7749. DBS_DISPLAY_RECORD_SEV(5, IDTableCtx, FALSE);
  7750. }
  7751. FStatus = DbsUpdateTableRecordByIndex(ThreadCtx,
  7752. Replica,
  7753. IDTableCtx,
  7754. &IDTableRec->FileGuid,
  7755. GuidIndexx,
  7756. IDTablex);
  7757. CLEANUP_FS(0,"++ ERROR - DbsUpdateTableRecordByIndex failed.", FStatus, ERROR_RETURN);
  7758. //
  7759. // Update the volume parent file ID table for:
  7760. // 1. Remote Change Orders that perform either a MOVEDIR or a MOVERS or
  7761. // 2. Local change orders that are generated by a MOVEIN sub-dir operation.
  7762. //
  7763. // Now that the file is installed we could start seeing local COs
  7764. // for it. For remote CO new file creates this update doesn't occur until
  7765. // the rename install of the pre-install file is done. See DbsRenameFid().
  7766. //
  7767. GStatus = GHT_STATUS_SUCCESS;
  7768. if (RemoteCo) {
  7769. if (CO_MOVE_RS_OR_DIR(LocationCmd)) {
  7770. CHANGE_ORDER_TRACE(3, ChangeOrder, "MOVEDIR Par Fid Update");
  7771. GStatus = QHashUpdate(Replica->pVme->ParentFidTable,
  7772. &ChangeOrder->FileReferenceNumber,
  7773. &IDTableRec->ParentFileID,
  7774. Replica->ReplicaNumber);
  7775. }
  7776. } else {
  7777. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_MOVEIN_GEN)) {
  7778. CHANGE_ORDER_TRACE(3, ChangeOrder, "MOVEIN Par Fid Update");
  7779. GStatus = QHashUpdate(Replica->pVme->ParentFidTable,
  7780. &ChangeOrder->FileReferenceNumber,
  7781. &IDTableRec->ParentFileID,
  7782. Replica->ReplicaNumber);
  7783. }
  7784. }
  7785. if (GStatus != GHT_STATUS_SUCCESS ) {
  7786. DPRINT1(0, "++ WARNING - QHashUpdate on parent FID table status: %d\n", GStatus);
  7787. }
  7788. //
  7789. // Update the DIR Table record.
  7790. //
  7791. // Currently at startup the Journal parent filter table is constructed
  7792. // from the DIRTable. If we were to crash between the update of the IDTable
  7793. // above and the update below it is possible that the change order retry
  7794. // at the next startup could fail to correct this problem. This would leave
  7795. // a missing dir entry in the Journal Filter table.
  7796. // This problem should get fixed as part of the support for a guest
  7797. // FRS member since the content of the dir tree will be sparse.
  7798. //
  7799. if (CoCmdIsDirectory(CoCmd)) {
  7800. DIRTableCtx = &RtCtx->DIRTable;
  7801. //
  7802. // Insert or update the DIR table and the journal filter table.
  7803. //
  7804. if (!DeleteCo) {
  7805. FStatus = ChgOrdInsertDirRecord(ThreadCtx,
  7806. DIRTableCtx,
  7807. IDTableRec,
  7808. ChangeOrder,
  7809. Replica,
  7810. FALSE);
  7811. //
  7812. // DirTable entry may not exist if this is a reanimation
  7813. // CO. For all other COs it should exist.
  7814. //
  7815. if ((FStatus == FrsErrorNotFound) &&
  7816. COE_FLAG_ON(ChangeOrder, COE_FLAG_REANIMATION)) {
  7817. FStatus = ChgOrdInsertDirRecord(ThreadCtx,
  7818. DIRTableCtx,
  7819. IDTableRec,
  7820. ChangeOrder,
  7821. Replica,
  7822. TRUE);
  7823. }
  7824. CLEANUP_FS(0,"++ ERROR - ChgOrdInsertDirRecord failed.", FStatus, ERROR_RETURN);
  7825. } else if (!IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED)) {
  7826. DPRINT(4, "++ Deleting the DirTable entry -----------\n");
  7827. //
  7828. // Note: We don't have to update the Volume Filter Table here
  7829. // because that was handled by the journal code when the
  7830. // delete change order was processed for the directory. Even if
  7831. // this was a remote co where we issue the directory delete we still
  7832. // see an NTFS journal close record because the file isn't deleted
  7833. // until the last handle is closed. Our attempt to filter that
  7834. // USN record fails so the delete CO is processed by the journal
  7835. // code and later discarded by change order accept.
  7836. //
  7837. FStatus = DbsDeleteDIRTableRecord(ThreadCtx,
  7838. Replica,
  7839. DIRTableCtx,
  7840. &IDTableRec->FileGuid);
  7841. //
  7842. // If this update is on a new file entry there won't be a DirTable entry.
  7843. // e.g. A Dir delete arrives before the create and we just create
  7844. // a tombstone.
  7845. //
  7846. if (!FRS_SUCCESS(FStatus) && !NewFile) {
  7847. DPRINT_FS(0,"++ ERROR - DbsDeleteTableRecordByIndex failed.", FStatus);
  7848. //
  7849. // Note: if we crashed in the middle of a retire the record
  7850. // would not be there. See comment above re: making the whole
  7851. // retire a single transaction.
  7852. }
  7853. }
  7854. }
  7855. FStatus = FrsErrorSuccess;
  7856. ERROR_RETURN:
  7857. if (!FRS_SUCCESS(FStatus)) {
  7858. JrnlSetReplicaState(Replica, REPLICA_STATE_ERROR);
  7859. Replica->FStatus = FStatus; // note: not thread safe
  7860. }
  7861. return FStatus;
  7862. }
  7863. ULONG
  7864. ChgOrdReadIDRecordNewFile(
  7865. PTHREAD_CTX ThreadCtx,
  7866. PTABLE_CTX IDTableCtx,
  7867. PTABLE_CTX IDTableCtxNC,
  7868. PCHANGE_ORDER_ENTRY ChangeOrder,
  7869. PREPLICA Replica,
  7870. BOOL *IDTableRecExists
  7871. )
  7872. /*++
  7873. Routine Description:
  7874. Create an IDTable entry for a new file. Also called when the flag
  7875. IDREC_FLAGS_NEW_FILE_IN_PROGRESS is set to perform some re-init.
  7876. This is for the change order from the replica set
  7877. specified by the ReplicaNumber. If there is no ID Table record for
  7878. this file and the location operation is a create or a movein then
  7879. open the file and get or set the object ID on the file. In the case
  7880. of no ID Table record we init one here, all except a few fields for
  7881. remote change orders.
  7882. The volume monitor entry in the Replica struct provides the volume
  7883. handle for the open. On return the IDTable is closed.
  7884. If this turns out to be a new file or a reanimation of an old file then
  7885. IDTableCtxNC is used to do an ID Table lookup on the Parent Guid/Name
  7886. Index to check for a potential name conflict.
  7887. Arguments:
  7888. ThreadCtx -- A Thread context to use for dbid and sesid.
  7889. IDTableCtx -- The table context containing the ID Table record.
  7890. IDTableCtxNC -- The table context containing the ID Table record for the Name Conflict.
  7891. ChangeOrder-- The change order.
  7892. Replica -- The Replica ID table to do the lookup in.
  7893. IDTableRecExists - Set to TRUE if the idtable record exists. The
  7894. caller then knows that the idtable entry shouldn't
  7895. be inserted.
  7896. Return Value:
  7897. Frs Status
  7898. FrsErrorSuccess - IDTable record was found. Data returned.
  7899. FrsErrorNotFound - IDTable record was not found so treat as new file.
  7900. FrsErrorNameMorphConflict - This is like FrsErrorNotFound, i.e. it is
  7901. a create or reanimate (i.e. tombstone
  7902. found) but there is a parent guid/name conflict with
  7903. another entry in the IDTable. Check for a Tunneled
  7904. Object ID to see if this is a bogus conflict.
  7905. FrsErrorTunnelConflict - OID got tunneled so this is really like FrsErrorSuccess
  7906. except the ChgOrdAccept code needs to restart the processing
  7907. of this CO so the proper HoldIssue interlock checks get made.
  7908. For other error status return codes see description for ChgOrdReadIDRecord().
  7909. --*/
  7910. {
  7911. #undef DEBSUB
  7912. #define DEBSUB "ChgOrdReadIDRecordNewFile:"
  7913. JET_ERR jerr;
  7914. DWORD WStatus;
  7915. ULONG FStatus, Status, FStatus2;
  7916. ULONG LocationCmd;
  7917. ULONG ReplicaNumber;
  7918. HANDLE FileHandle;
  7919. BOOL MorphGenCo, ExistingOid;
  7920. BOOL LocalCo;
  7921. PIDTABLE_RECORD IDTableRec;
  7922. PCHANGE_ORDER_COMMAND CoCmd;
  7923. ////////////////////////////////////////////////////////////////////////////
  7924. // //
  7925. // IDTable record not found. See if change order is a create or a Movein.//
  7926. // //
  7927. ////////////////////////////////////////////////////////////////////////////
  7928. ReplicaNumber = Replica->ReplicaNumber;
  7929. CoCmd = &ChangeOrder->Cmd;
  7930. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  7931. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  7932. MorphGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN);
  7933. FStatus = FrsErrorNotFound;
  7934. IDTableRec = IDTableCtx->pDataRecord;
  7935. if (CO_NEW_FILE(LocationCmd)) {
  7936. //
  7937. // New file create. Check if name conflict would occur.
  7938. //
  7939. FStatus = ChgOrdCheckNameMorphConflict(ThreadCtx,
  7940. IDTableCtxNC,
  7941. ReplicaNumber,
  7942. ChangeOrder);
  7943. if (FStatus == FrsErrorNameMorphConflict) {
  7944. DPRINT(0,"++ NM: Possible Name Morph Conflict on new file\n");
  7945. DBS_DISPLAY_RECORD_SEV(4, IDTableCtxNC, TRUE);
  7946. CHANGE_ORDER_TRACE(3, ChangeOrder, "Poss Morph Conflict - cre");
  7947. } else
  7948. if (FRS_SUCCESS(FStatus)) {
  7949. //
  7950. // Return not found so caller will treat the CO as a new file.
  7951. //
  7952. FStatus = FrsErrorNotFound;
  7953. } else {
  7954. DPRINT_FS(0,"++ NM: WARNING - Unexpected result from ChgOrdCheckNameMorphConflict.", FStatus);
  7955. return FStatus;
  7956. }
  7957. } else
  7958. if ((!LocalCo || MorphGenCo) &&
  7959. ((LocationCmd == CO_LOCATION_DELETE) ||
  7960. (LocationCmd == CO_LOCATION_MOVEOUT))) {
  7961. //
  7962. // Return not found for remote (or name morph gened) co deletes so
  7963. // the tombstone gets created. Needed to make name morphing work.
  7964. // Load a fake value for the File ID since this is an IDTable
  7965. // index and there is no actual file. In the case of a local Co that
  7966. // generates a name conflict we will use its FID.
  7967. //
  7968. if (ChangeOrder->FileReferenceNumber == ZERO_FID) {
  7969. FrsInterlockedIncrement64(ChangeOrder->FileReferenceNumber,
  7970. GlobSeqNum,
  7971. &GlobSeqNumLock);
  7972. }
  7973. IDTableRec->FileID = ChangeOrder->FileReferenceNumber;
  7974. FStatus = FrsErrorNotFound;
  7975. } else
  7976. if (!LocalCo && (LocationCmd == CO_LOCATION_MOVEDIR)) {
  7977. //
  7978. // MOVERS: need to handle MOVERS too when that code is implemented
  7979. //
  7980. // A MoveDir can show up if a file is created before we VVJoin with
  7981. // an inbound partner and then during the VVJoin a MoveDir is done
  7982. // on the file. The MoveDir on the file causes the VVJoin scan to
  7983. // skip over the file since it knows the MoveDir CO will get sent
  7984. // when we rescan the outlog. So in this case we see the MoveDir
  7985. // but we have not seen the original create for the file.
  7986. // Since we can't be sure, treat it as a create and be happy.
  7987. // BTW: It should be marked as an out of order CO.
  7988. //
  7989. SET_CO_LOCATION_CMD(*CoCmd, Command, CO_LOCATION_CREATE);
  7990. LocationCmd = CO_LOCATION_CREATE;
  7991. SET_CO_FLAG(ChangeOrder, CO_FLAG_LOCATION_CMD);
  7992. SetFlag(CoCmd->ContentCmd, USN_REASON_FILE_CREATE);
  7993. FStatus = FrsErrorNotFound;
  7994. } else
  7995. if (LocalCo && (LocationCmd == CO_LOCATION_MOVEDIR)) {
  7996. //
  7997. // We come here in the case of a local Co that is a movedir.
  7998. // In the case of the following sequence where the object ID arrived
  7999. // on the new file (i.e. NEW FID) via the tunnel cache and the tunnel
  8000. // cache hit was triggered by a MOVEDIR on the file then we need
  8001. // to handle this by calling ChgOrdCheckAndFixTunnelConflict() later on.
  8002. // The sequence is:
  8003. //
  8004. // A. create \BankFile\1000013106\100000497\1597.TXT
  8005. //
  8006. // B. delete \BankFile\1000013106\100001385\1613.TXT
  8007. //
  8008. // C. rename \BankFile\1000013106\100000497\1597.TXT TO
  8009. // \BankFile\1000013106\100001385\1613.TXT
  8010. //
  8011. // By the time we process CO-A the OID tunneling from 1613 to 1597 will
  8012. // already be complete (aging cache delay). So CO-A is rejected because
  8013. // of a tunneling conflict. CO-B is processed as usual. CO-C is then
  8014. // treated as a movedir so we are here. It renames the file across
  8015. // parent dirs.
  8016. //
  8017. FStatus = FrsErrorNotFound;
  8018. } else
  8019. if (LocationCmd == CO_LOCATION_NO_CMD) {
  8020. //
  8021. // An update CO could arrive out of order relative to its create
  8022. // if the create got delayed by a sharing violation. We don't
  8023. // want to lose the update so we make it look like a create.
  8024. // This also handles the case of delete change orders generated
  8025. // by name morph conflicts in which a rename arrives for a
  8026. // nonexistent file. Also above MOVEDIR example applies regarding
  8027. // Update during a VVJoin.
  8028. //
  8029. //
  8030. // if (!LocalCo &&
  8031. // (LocationCmd == CO_LOCATION_NO_CMD) &&
  8032. // BooleanFlagOn(CoCmd->ContentCmd, USN_REASON_RENAME_NEW_NAME)) {
  8033. //
  8034. // Looks like a bare rename. Probably originated as a MorphGenCo
  8035. // so turn it into a create.
  8036. //
  8037. SET_CO_LOCATION_CMD(*CoCmd, Command, CO_LOCATION_CREATE);
  8038. LocationCmd = CO_LOCATION_CREATE;
  8039. SET_CO_FLAG(ChangeOrder, CO_FLAG_LOCATION_CMD);
  8040. SetFlag(CoCmd->ContentCmd, USN_REASON_FILE_CREATE);
  8041. FStatus = FrsErrorNotFound;
  8042. } else {
  8043. //
  8044. // The IDTable entry is deleted if a file disappears early in the
  8045. // change order processing. The change order for the delete is then
  8046. // rejected by returning an error from this function.
  8047. //
  8048. FStatus = FrsErrorInvalidChangeOrder;
  8049. if (LocationCmd != CO_LOCATION_DELETE &&
  8050. LocationCmd != CO_LOCATION_MOVEOUT) {
  8051. DPRINT(0, "++ WARN - IDTable record not found and Location cmd not create or movein\n");
  8052. FRS_PRINT_TYPE(1, ChangeOrder);
  8053. }
  8054. return FStatus;
  8055. }
  8056. //
  8057. // FStatus is either FrsErrorNameMorphConflict or FrsErrorNotFound
  8058. // at this point.
  8059. //
  8060. DbsCloseTable(jerr, ThreadCtx->JSesid, IDTableCtx);
  8061. //
  8062. // New fILE. Initialize an IDTable Entry for it.
  8063. // Caller will decide if IDTable gets updated.
  8064. //
  8065. ExistingOid = PreserveFileOID;
  8066. WStatus = DbsInitializeIDTableRecord(IDTableCtx,
  8067. NULL,
  8068. Replica,
  8069. ChangeOrder,
  8070. CoCmd->FileName,
  8071. &ExistingOid);
  8072. if (LocalCo) {
  8073. //
  8074. // Get the File's current USN so we can check for consistency later
  8075. // when the change order is about to be sent to an outbound partner.
  8076. // Put the FileGuid for the new local file in the change order.
  8077. //
  8078. CoCmd->FileUsn = IDTableRec->CurrentFileUsn;
  8079. CoCmd->FileGuid = IDTableRec->FileGuid;
  8080. //
  8081. // Return the FileSize in the Local Change order.
  8082. //
  8083. CoCmd->FileSize = IDTableRec->FileSize;
  8084. } else {
  8085. //
  8086. // For remote change order, set the Parent FID from the NewParent Fid.
  8087. //
  8088. ChangeOrder->ParentFileReferenceNumber = ChangeOrder->NewParentFid;
  8089. }
  8090. //
  8091. // The file was deleted before we could get to it; ignore change order
  8092. //
  8093. if (WIN_NOT_FOUND(WStatus)) {
  8094. // FStatus = FrsErrorInvalidChangeOrder;
  8095. SET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command, CO_LOCATION_DELETE);
  8096. return FStatus;
  8097. }
  8098. if (ExistingOid && WIN_SUCCESS(WStatus)) {
  8099. FStatus2 = ChgOrdCheckAndFixTunnelConflict(ThreadCtx,
  8100. IDTableCtx,
  8101. IDTableCtxNC,
  8102. Replica,
  8103. ChangeOrder,
  8104. IDTableRecExists);
  8105. if (FStatus2 == FrsErrorTunnelConflict) {
  8106. //
  8107. // If the OID got tunneled to this new file then any Name Morph
  8108. // conflict detected above is bogus.
  8109. //
  8110. if (FStatus == FrsErrorNameMorphConflict) {
  8111. CHANGE_ORDER_TRACE(3, ChangeOrder, "No Morph Conflict - Tunnel");
  8112. }
  8113. return FrsErrorTunnelConflict;
  8114. } else
  8115. if (FStatus2 != FrsErrorSuccess) {
  8116. FStatus = FStatus2;
  8117. return FStatus;
  8118. }
  8119. }
  8120. if (WIN_SUCCESS(WStatus)) {
  8121. //
  8122. // Initialize the JetSet/RetCol arrays and data record buffer
  8123. // addresses to read and write the fields of the data record.
  8124. //
  8125. DbsSetJetColSize(IDTableCtx);
  8126. DbsSetJetColAddr(IDTableCtx);
  8127. //
  8128. // Update the JetSet/RetCol arrays for variable len fields.
  8129. //
  8130. Status = DbsAllocRecordStorage(IDTableCtx);
  8131. if (!NT_SUCCESS(Status)) {
  8132. DPRINT_NT(0, "++ ERROR - DbsAllocRecordStorage failed to alloc buffers.", Status);
  8133. return FrsErrorResource;
  8134. }
  8135. //
  8136. // FStatus is either FrsErrorNameMorphConflict
  8137. // or FrsErrorNotFound
  8138. // or FrsErrorTunnelConflict
  8139. //
  8140. // Set new file flag so a crashed CO can restart correctly.
  8141. // If we crash and later the creating change order gets rejected
  8142. // when reprocessed at recovery this flag is used by the tombstone
  8143. // delete code to clean out the IDTable entry. A corrected tunnel
  8144. // conflict means the file already exists so don't set new file flag.
  8145. //
  8146. if (FStatus != FrsErrorTunnelConflict) {
  8147. SetIdRecFlag(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS);
  8148. }
  8149. return FStatus;
  8150. }
  8151. //
  8152. // Couldn't construct the entry. Probably a local change order and we
  8153. // couldn't open the file to get the required info or set the object ID.
  8154. //
  8155. FStatus = FrsErrorAccess;
  8156. return FStatus;
  8157. }
  8158. ULONG
  8159. ChgOrdReadIDRecord(
  8160. PTHREAD_CTX ThreadCtx,
  8161. PTABLE_CTX IDTableCtx,
  8162. PTABLE_CTX IDTableCtxNC,
  8163. PCHANGE_ORDER_ENTRY ChangeOrder,
  8164. PREPLICA Replica,
  8165. BOOL *IDTableRecExists
  8166. )
  8167. /*++
  8168. Routine Description:
  8169. Read the ID Table record for this change order from the replica set
  8170. specified by the ReplicaNumber. If there is no ID Table record for
  8171. this file and the location operation is a create or a movein then
  8172. open the file and get or set the object ID on the file. In the case
  8173. of no ID Table record we init one here, all except a few fields for
  8174. remote change orders.
  8175. The volume monitor entry in the Replica struct provides the volume
  8176. handle for the open. On return the IDTable is closed.
  8177. If this turns out to be a new file or a reanimation of an old file then
  8178. IDTableCtxNC is used to do an ID Table lookup on the Parent Guid/Name
  8179. Index to check for a potential name conflict.
  8180. Arguments:
  8181. ThreadCtx -- A Thread context to use for dbid and sesid.
  8182. IDTableCtx -- The table context containing the ID Table record.
  8183. IDTableCtxNC -- The table context containing the ID Table record for the Name Conflict.
  8184. ChangeOrder-- The change order.
  8185. Replica -- The Replica ID table to do the lookup in.
  8186. IDTableRecExists - Set to TRUE if the idtable record exists. The
  8187. caller then knows that the idtable entry shouldn't
  8188. be inserted.
  8189. David, please review -- should the idtable entry be updated in this
  8190. case?
  8191. Return Value:
  8192. Frs Status
  8193. FrsErrorSuccess - IDTable record was found. Data returned.
  8194. FrsErrorInvalidChangeOrder - IDTable record not found and the change order
  8195. is not a create or movein.
  8196. FrsErrorNotFound - IDTable record was not found but this is a create
  8197. or movein change order so we constructed what we could
  8198. based on whether the CO is local or remote.
  8199. FrsErrorNameMorphConflict - This is like FrsErrorNotFound, i.e. it is
  8200. a create or reanimate (i.e. tombstone
  8201. found) but there is a parent guid/name conflict with
  8202. another entry in the IDTable. Data for this conflicting
  8203. entry is returned in IDTableCtxNC.
  8204. Note: This can also be returned in the event of a
  8205. reanimation or a rename with a conflict on the target.
  8206. FrsErrorTunnelConflict - OID got tunneled so this is really like FrsErrorSuccess
  8207. except the ChgOrdAccept code needs to restart the processing
  8208. of this CO so the proper HoldIssue interlock checks get made.
  8209. Any other error is a failure.
  8210. --*/
  8211. {
  8212. #undef DEBSUB
  8213. #define DEBSUB "ChgOrdReadIDRecord:"
  8214. USN CurrentFileUsn;
  8215. ULONGLONG CurrParentFid;
  8216. FILE_NETWORK_OPEN_INFORMATION FileNetworkOpenInfo;
  8217. JET_ERR jerr, jerr1;
  8218. DWORD WStatus, WStatus1;
  8219. ULONG FStatus, Status, GStatus, FStatus2;
  8220. ULONG LocationCmd;
  8221. PVOID pKey;
  8222. PVOID KeyArray[2];
  8223. ULONG IndexCode;
  8224. ULONG ReplicaNumber;
  8225. HANDLE FileHandle;
  8226. LONG RStatus;
  8227. BOOL MorphGenCo, RetryCo, ExistingOid;
  8228. PIDTABLE_RECORD IDTableRec;
  8229. PCONFIG_TABLE_RECORD ConfigRecord;
  8230. PCHANGE_ORDER_COMMAND CoCmd;
  8231. BOOL LocalCo;
  8232. ReplicaNumber = Replica->ReplicaNumber;
  8233. CoCmd = &ChangeOrder->Cmd;
  8234. LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  8235. *IDTableRecExists = FALSE;
  8236. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  8237. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  8238. MorphGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN);
  8239. RetryCo = BooleanFlagOn(CoCmd->Flags, CO_FLAG_RETRY);
  8240. //
  8241. // Read the IDTable Record for this file. If a local ChangeOrder then
  8242. // do the lookup by FID and if remote do it by Guid.
  8243. // Insert the GUIDs for originator, File, Old and new parent.
  8244. //
  8245. jerr = DbsOpenTable(ThreadCtx, IDTableCtx, ReplicaNumber, IDTablex, NULL);
  8246. CLEANUP_JS(0, "IDTable open failed.", jerr, RETURN_JERR);
  8247. if (!LocalCo || MorphGenCo) {
  8248. pKey = (PVOID)&CoCmd->FileGuid;
  8249. IndexCode = GuidIndexx;
  8250. } else {
  8251. pKey = (PVOID)&ChangeOrder->FileReferenceNumber;
  8252. IndexCode = FileIDIndexx;
  8253. }
  8254. jerr = DbsReadRecord(ThreadCtx, pKey, IndexCode, IDTableCtx);
  8255. //
  8256. // If no record there then we have a new object, file/dir.
  8257. //
  8258. if (jerr == JET_errRecordNotFound) {
  8259. FStatus = ChgOrdReadIDRecordNewFile(ThreadCtx,
  8260. IDTableCtx,
  8261. IDTableCtxNC,
  8262. ChangeOrder,
  8263. Replica,
  8264. IDTableRecExists);
  8265. if ((FStatus == FrsErrorNotFound) ||
  8266. //
  8267. // For tunnel conflict, Fid to Guid xlate is now updated, start over.
  8268. //
  8269. (FStatus == FrsErrorTunnelConflict) ||
  8270. (FStatus == FrsErrorNameMorphConflict)) {
  8271. return FStatus;
  8272. }
  8273. goto RETURN_ERROR;
  8274. }
  8275. *IDTableRecExists = TRUE;
  8276. //
  8277. // Bail if some error other than record not found.
  8278. //
  8279. CLEANUP_JS(4, "IDTable record not found.", jerr, RETURN_JERR);
  8280. //
  8281. // We found the record in the table. Fill in the remaining local
  8282. // fields in the change order entry from the IDTable entry
  8283. // if this is a Remote Change Order.
  8284. //
  8285. IDTableRec = IDTableCtx->pDataRecord;
  8286. //
  8287. // Reflect state of IDREC_FLAGS_DELETE_DEFERRED in the change order.
  8288. // See related comment below on IDREC_FLAGS_RENAME_DEFERRED.
  8289. //
  8290. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED)) {
  8291. SET_COE_FLAG(ChangeOrder, COE_FLAG_NEED_DELETE);
  8292. // SUDARC-DEV - Comment out this part so that we preserve the flags in the idtable
  8293. // record.
  8294. // ClearIdRecFlag(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED);
  8295. CHANGE_ORDER_TRACE(3, ChangeOrder, "IDREC_FLAGS_DELETE_DEFERRED set");
  8296. }
  8297. //
  8298. // If the IDREC_FLAGS_NEW_FILE_IN_PROGRESS flag is set then this or some
  8299. // other CO did not finish. There are a few ways in which this could happen.
  8300. //
  8301. // 1. A local or remote CO was issued and the IDTable record created but
  8302. // the system crashed before the issued CO went thru retire or retry paths
  8303. // to clear the NEW_FILE_IN_PROGRESS Flag.
  8304. //
  8305. // 2. A remote CO that fails to fetch the file (e.g. cxtion unjoins) will
  8306. // go thru the retry path in the IBCO_FETCH_RETRY state. This leaves the
  8307. // NEW_FILE_IN_PROGRESS Flag set because another remote CO from a different
  8308. // inbound partner could arrive and it should be treated as a new file
  8309. // by reconcile (since the first inbound partner may never reconnect).
  8310. //
  8311. // 3. A local CO gets thru the retry path in the IBCO_STAGING_RETRY state
  8312. // because a sharing violation has prevented us from opening the file to
  8313. // generate the staging file. We may be giving up for now or we may be
  8314. // shutting down. Either way the Local CO is in the inbound log so the
  8315. // journal commit point has advanced past the USN record that gave rise
  8316. // to the CO. On journal restart we will not process that USN record again.
  8317. // But the CO saved in the inbound log does NOT have the file ID for the
  8318. // file. This is kept in the IDTable record and used in the Guid to Fid
  8319. // translation to reconstruct the FID. Actually this case is really the
  8320. // local CO varient of case 1 since, for local COs in the staging retry
  8321. // state, the retry path will set the IDREC_FLAGS_CREATE_DEFERRED flag
  8322. // in the IDTable record and clear the IDREC_FLAGS_NEW_FILE_IN_PROGRESS bit.
  8323. //
  8324. // 4. A remote CO on a new-file-create fails to install because of a disk
  8325. // full condition. The initial ID table contents will have matching version
  8326. // info so on retry the CO is rejected for sameness. The NEW_FILE_IN_PROGRESS
  8327. // flag ensures that the CO gets accepted.
  8328. //
  8329. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS)) {
  8330. CHANGE_ORDER_TRACE(3, ChangeOrder, "IDREC_FLAGS_NEW_FILE_IN_PROGRESS");
  8331. DBS_DISPLAY_RECORD_SEV(3, IDTableCtx, TRUE);
  8332. FRS_PRINT_TYPE(3, ChangeOrder);
  8333. //
  8334. // If the CO came from the INlog as a recovery CO then we did a Guid
  8335. // to FID translation earlier so the FID should be valid.
  8336. // If this is a remote CO then a new pre-install file may need to
  8337. // be created if startup recovery removed it.
  8338. //
  8339. // Even though IDREC_FLAGS_NEW_FILE_IN_PROGRESS is set we still need
  8340. // to check for the presence of the pre-install file.
  8341. // The scenario is:
  8342. // If a remote CO arrives and creates a pre-install file but we crash
  8343. // before writing the INLOG record (say the disk was full) then when we
  8344. // restart the preinstall file cleanup code will delete the preinstall
  8345. // file since there is no change order for it in the inlog. Later when
  8346. // the upstream partner re-sends the CO we end up here with a FID in the
  8347. // IDTable for a deleted file. If we don't check for it then the CO
  8348. // will fetch the staging file but will then fail to install the CO
  8349. // because the pre-install file is gone. The CO will then enter an
  8350. // install retry loop.
  8351. //
  8352. if (!LocalCo && (IDTableRec->FileID != ZERO_FID)) {
  8353. FileHandle = INVALID_HANDLE_VALUE;
  8354. WStatus = FrsOpenSourceFileById(&FileHandle,
  8355. &FileNetworkOpenInfo,
  8356. NULL,
  8357. Replica->pVme->VolumeHandle,
  8358. &IDTableRec->FileID,
  8359. FILE_ID_LENGTH,
  8360. // READ_ACCESS,
  8361. READ_ATTRIB_ACCESS,
  8362. ID_OPTIONS,
  8363. SHARE_ALL,
  8364. FILE_OPEN);
  8365. if (WIN_SUCCESS(WStatus) &&
  8366. WIN_SUCCESS(FrsReadFileParentFid(FileHandle, &CurrParentFid)) &&
  8367. (CurrParentFid == Replica->PreInstallFid)) {
  8368. NOTHING;
  8369. } else {
  8370. //
  8371. // Either can't access the file or it's not in pre-install dir
  8372. //
  8373. CHANGE_ORDER_TRACEX(3, ChangeOrder, "Force create of new Pre-Install file", WStatus);
  8374. IDTableRec->FileID = ZERO_FID;
  8375. ChangeOrder->FileReferenceNumber = ZERO_FID;
  8376. }
  8377. FRS_CLOSE(FileHandle);
  8378. }
  8379. //
  8380. // Treat this CO like a new file.
  8381. //
  8382. FStatus = ChgOrdReadIDRecordNewFile(ThreadCtx,
  8383. IDTableCtx,
  8384. IDTableCtxNC,
  8385. ChangeOrder,
  8386. Replica,
  8387. IDTableRecExists);
  8388. if ((FStatus == FrsErrorNotFound) ||
  8389. //
  8390. // For tunnel conflict, Fid to Guid xlate is now updated, start over.
  8391. //
  8392. (FStatus == FrsErrorTunnelConflict) ||
  8393. (FStatus == FrsErrorNameMorphConflict)) {
  8394. return FStatus;
  8395. }
  8396. goto RETURN_ERROR;
  8397. }
  8398. DbsCloseTable(jerr, ThreadCtx->JSesid, IDTableCtx);
  8399. //
  8400. // If the IDTable record is marked deleted and
  8401. // o the incoming CO is a delete or a moveout then continue processing
  8402. // the delete CO because the event time / version info may be more
  8403. // recent then what we already have recorded in our IDTable entry.
  8404. // This is necessary because of the following case. Consider three
  8405. // members A, B and C (all disconnected at the moment). At time T=0
  8406. // MA deletes file foo, At time T=5 MB updates file foo, and at time
  8407. // T=10 MC deletes file foo. Depending on the topology and therefor
  8408. // the order of arrival of the COs from A, B and C a given machine
  8409. // could decide to reanimate foo if we didn't process the second delete
  8410. // CO. E.G. if the arrival order was CoA, CoC, CoB foo would get
  8411. // reanimated because we ignored Coc. If the arrival order was Coc,
  8412. // CoA, CoB we would not reanimate foo since the event time on CoC is
  8413. // more recent that the time on CoB. Therefore send the delete COs
  8414. // on to reconcile to decide.
  8415. //
  8416. // o this CO is an update, rename, ... then transform it into a Create
  8417. // so the file can come back.
  8418. //
  8419. // Make the IDTable record FID zero here rather than using zero for all
  8420. // deleted entries which would create a duplicate key in the DB and make
  8421. // the index less balanced.
  8422. //
  8423. // Depending on the execution timing of local and remote COs where
  8424. // one is a delete another could well be pending while the delete
  8425. // is being performed. Reconcile will decide if we reanimate the file.
  8426. //
  8427. // Skip this test if this is a Morph Generated CO because of the following:
  8428. //
  8429. // 1. A remote co for an update comes in for a tombstoned idtable entry.
  8430. //
  8431. // 2. The remote co becomes a reanimate co.
  8432. //
  8433. // 3. The reanimate remote co loses a name conflict to another idtable entry.
  8434. //
  8435. // 4. A MorphGenCo for a local delete is generated for the
  8436. // reanimate remote co that lost the name conflict.
  8437. //
  8438. // 5. The MorphGenCo is skipped here because the idtable entry is
  8439. // tombstoned. The service returns to step 1.
  8440. //
  8441. // The fix is to allow MorphGenCo's to proceed even if the idtable
  8442. // entry is deleted. The delete will end up being a NOP (file doesn't
  8443. // exist) but will then be sent out to the partners to insure
  8444. // consistency amoung the members.
  8445. //
  8446. if (!MorphGenCo && IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED)
  8447. && !IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED)) {
  8448. if ((LocationCmd == CO_LOCATION_DELETE) ||
  8449. (LocationCmd == CO_LOCATION_MOVEOUT)) {
  8450. if (LocalCo && (IDTableRec->EventTime != CoCmd->EventTime.QuadPart)) {
  8451. //
  8452. // Swat down this CO if it is local and the eventtime in the idtable
  8453. // is not the same as the event time in the CoCmd.
  8454. // We do this because if
  8455. // a remote CO delete comes in with a very old event time we
  8456. // update the event time in the IDTable record with the old time.
  8457. // Now when the delete is installed we generate a very recent
  8458. // USN record. If we let this local delete get to reconcile it
  8459. // will accept it based on the event time difference. But this
  8460. // local delete was generated by us processing a remote co delete
  8461. // and now we are going to propagate it down stream.
  8462. // Once change order backlogs form this can result in a cascade
  8463. // effect until the backlogs dissapate. Note that backlogs
  8464. // could form at either outbound or inbound logs but the effect
  8465. // is the same even though the specific scenario is different.
  8466. //
  8467. // If we get a valid local delete and update the idtable but crash
  8468. // before we write it to the outlog. Then when we come back and process
  8469. // the local delete from the inlog we do not want to skip it. Hence we
  8470. // don't skip the local delete if the event times match.
  8471. //
  8472. FStatus = FrsErrorInvalidChangeOrder;
  8473. CHANGE_ORDER_TRACE(3, ChangeOrder, "Lcl Del Co rcvd on deleted file -- Skipping");
  8474. return FStatus;
  8475. }
  8476. if (LocalCo) {
  8477. CHANGE_ORDER_TRACE(3, ChangeOrder, "Lcl Del Co rcvd on deleted file");
  8478. } else {
  8479. CHANGE_ORDER_TRACE(3, ChangeOrder, "Rmt Del Co rcvd on deleted file");
  8480. }
  8481. FStatus = FrsErrorSuccess;
  8482. return FStatus;
  8483. }
  8484. FStatus = FrsErrorSuccess;
  8485. if (!LocalCo) {
  8486. //
  8487. // If the FID is non-zero in the IDTable then check if it is valid.
  8488. // If this is a retry fetch CO for a reanimate then the pre-install
  8489. // file was already created the first time around so it should be
  8490. // there. This could also be a recovery CO if we crashed after
  8491. // updating the FID but before the CO was marked as retry in the
  8492. // INLOG. This could also be a dup CO that arrived after a previous
  8493. // CO started the reanimation but went into fetch retry cuz the
  8494. // connection failed. So we can't condition this check on the
  8495. // retry or recovery flags in the CO. The only way we can know is
  8496. // to try to open the FID and see if the file is there.
  8497. //
  8498. if (IDTableRec->FileID != ZERO_FID) {
  8499. FileHandle = INVALID_HANDLE_VALUE;
  8500. WStatus = FrsOpenSourceFileById(&FileHandle,
  8501. &FileNetworkOpenInfo,
  8502. NULL,
  8503. Replica->pVme->VolumeHandle,
  8504. &IDTableRec->FileID,
  8505. FILE_ID_LENGTH,
  8506. // READ_ACCESS,
  8507. READ_ATTRIB_ACCESS,
  8508. ID_OPTIONS,
  8509. SHARE_ALL,
  8510. FILE_OPEN);
  8511. if (WIN_SUCCESS(WStatus) &&
  8512. WIN_SUCCESS(FrsReadFileParentFid(FileHandle, &CurrParentFid)) &&
  8513. (CurrParentFid == Replica->PreInstallFid)) {
  8514. NOTHING;
  8515. } else {
  8516. //
  8517. // Either can't access the file or it's not in pre-install dir
  8518. //
  8519. CHANGE_ORDER_TRACEX(3, ChangeOrder, "Force create of new Pre-Install file", WStatus);
  8520. IDTableRec->FileID = ZERO_FID;
  8521. ChangeOrder->FileReferenceNumber = ZERO_FID;
  8522. }
  8523. FRS_CLOSE(FileHandle);
  8524. }
  8525. //
  8526. // Not a delete CO. Check if name conflict would occur
  8527. // if reanimation took place. Caller will decide if this
  8528. // CO is actually rejected or not before any name morph
  8529. // change orders are created.
  8530. //
  8531. FStatus = ChgOrdCheckNameMorphConflict(ThreadCtx,
  8532. IDTableCtxNC,
  8533. ReplicaNumber,
  8534. ChangeOrder);
  8535. if (FStatus == FrsErrorNameMorphConflict) {
  8536. DPRINT(0,"++ NM: Possible Name Morph Conflict - reanimate\n");
  8537. DBS_DISPLAY_RECORD_SEV(4, IDTableCtxNC, TRUE);
  8538. CHANGE_ORDER_TRACE(3, ChangeOrder, "Poss Morph Conflict - reani");
  8539. } else {
  8540. CLEANUP_FS(0,"++ NM: WARNING - Unexpected result from ChgOrdCheckNameMorphConflict.",
  8541. FStatus, RETURN_ERROR);
  8542. }
  8543. } else {
  8544. //
  8545. // LOCAL CO.
  8546. // Tombstoned IDTable entry hit on Non-MorphGen Local Change Order.
  8547. // Update FileUsn to the USN of the most recent USN record that
  8548. // we've seen. This is probably a MOVEIN local CO.
  8549. //
  8550. CoCmd->FileUsn = CoCmd->JrnlUsn;
  8551. if (!RetryCo) {
  8552. //
  8553. // Bump Version number to ensure the CO is accepted if this is
  8554. // the first time we have seen this Local Co.
  8555. //
  8556. CoCmd->FileVersionNumber = IDTableRec->VersionNumber + 1;
  8557. }
  8558. }
  8559. CHANGE_ORDER_TRACE(3, ChangeOrder, "Co Possible Reanimate");
  8560. FRS_PRINT_TYPE(3, ChangeOrder);
  8561. //
  8562. // Done if this is a Tombstone Reanimation.
  8563. //
  8564. return FStatus;
  8565. }
  8566. ////////////////////////////////////////////////////////////////////////////
  8567. // //
  8568. // IDTable record found and not marked deleted. //
  8569. // //
  8570. ////////////////////////////////////////////////////////////////////////////
  8571. FStatus = FrsErrorSuccess;
  8572. if (!LocalCo || MorphGenCo) {
  8573. //
  8574. // Handle Remote Co
  8575. //
  8576. ChangeOrder->FileReferenceNumber = IDTableRec->FileID;
  8577. //
  8578. // Check and update the state of the parent fid.
  8579. //
  8580. if (ChangeOrder->ParentFileReferenceNumber !=
  8581. (ULONGLONG) IDTableRec->ParentFileID) {
  8582. if (CO_MOVE_OUT_RS_OR_DIR(LocationCmd)) {
  8583. //
  8584. // Expected current location of file is in IDTable.
  8585. //
  8586. ChangeOrder->ParentFileReferenceNumber = IDTableRec->ParentFileID;
  8587. } else {
  8588. //
  8589. // If this CO has requested its parent to be reanimated then
  8590. // the Guid to Fid translation done earlier now has the current
  8591. // (and correct) parent FID for this file. Update the IDTable
  8592. // entry to reflect the change in parent FID. This can happen
  8593. // if a local file op deletes the parent while create for a
  8594. // child is in process. when the Child create tries to do the
  8595. // final rename it will fail since the parent is now gone. This
  8596. // sends it thru retry in the IBCO_INSTALL_REN_RETRY state.
  8597. // Later the local delete is processed and the parent is marked
  8598. // as deleted. When the remote CO for the child is retried
  8599. // it reanimates the parent but the IDTable entry that was
  8600. // originally created for the child still has the File ID for
  8601. // the old parent.
  8602. //
  8603. if (!COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_RISE_REQ)) {
  8604. CHANGE_ORDER_TRACE(3, ChangeOrder, "WARN - Unexpected mismatch in parent FIDs");
  8605. DBS_DISPLAY_RECORD_SEV(3, IDTableCtx, TRUE);
  8606. FRS_PRINT_TYPE(3, ChangeOrder);
  8607. //
  8608. // This condition may not be an error because we could get
  8609. // the above case (rmt child create with concurrent parent
  8610. // local delete) where the rmt child create is followed by
  8611. // the local Del and then immediately by a 2nd rmt file
  8612. // update. The first rmt CO will go thru install and then
  8613. // go to retry on the final rename. The local delete is
  8614. // processed for the parent. Then the 2nd rmt co finds the
  8615. // IDTable for the parent is marked as deleted and so it
  8616. // reanimates the parent. It then finds the IDTable entry
  8617. // for the target file (created by the first rmt CO) with
  8618. // the IDREC_FLAGS_RENAME_DEFERRED bit set and the stale
  8619. // value for the parent FID created by the first rmt co.
  8620. // So the parent FIDs will again be mismatched but the 2nd
  8621. // remote CO is not any kind of a retry CO.
  8622. //
  8623. // A simpler scenario is a local rename (MOVDIR) happening at the
  8624. // same time as a remote update. If the rename is processed
  8625. // first then the parent FID in the IDTable is updated to
  8626. // the new parent. When the remote CO arrives its parent
  8627. // GUID is for the old parent which is translated to the
  8628. // old parent FID in the change order.
  8629. //
  8630. // What all this means is that if the parent FIDs don't
  8631. // match and this is not some flavor of a directory MOVE
  8632. // then the GUID to FID XLATE done on the CO before we
  8633. // get here has the correct value for the parent FID so
  8634. // we need to update the IDTable record accordingly.
  8635. //
  8636. // This is taken care of later in change order accept where
  8637. // we deal with the problem of simultaneous update and rename
  8638. // COs where the rename CO arrives first but the update CO
  8639. // ultimately wins and must implicitly rename the file back
  8640. // to its previous parent. An update to IDTableRec->ParentFileID
  8641. // here will interfere with that test.
  8642. } else {
  8643. IDTableRec->ParentFileID = ChangeOrder->ParentFileReferenceNumber;
  8644. }
  8645. }
  8646. }
  8647. CoCmd->FileUsn = IDTableRec->CurrentFileUsn;
  8648. //
  8649. // A newly created file is first installed into a temporary file
  8650. // and then renamed to its final destination. The deferred rename
  8651. // flag is set in the IDTable record if the rename fails because
  8652. // of sharing violations or a name conflict. However a subsequent
  8653. // CO may arrive for this file so the flag in the IDTable record
  8654. // tells us to retry the rename when this change order completes.
  8655. // The old create change order will later be discarded by the reconcile
  8656. // code in ChgOrdAccept().
  8657. // NOTE: the flag is ignored when retiring the change order if
  8658. // the change order is a delete.
  8659. // NOTE: Clearing the IDTable flag here does not actually update the
  8660. // database record until the CO finally retires. At that point if
  8661. // the COE_FLAG_NEED_RENAME is still set then the IDTable flag is
  8662. // turned back on.
  8663. //
  8664. //
  8665. // COE_FLAG_NEED_RENAME is an incore flag that tells the retire
  8666. // code to rename the successfully installed file to its final
  8667. // destination. It is set when the staging file is successfully
  8668. // installed in the preinstall file or when IDREC_FLAGS_RENAME_DEFERRED
  8669. // is set in the idtable entry. Note that there can be multiple COs
  8670. // active against the same file (serialized of course). Some may have
  8671. // made it to the rename retry state and have set IDREC_FLAGS_RENAME_DEFERRED
  8672. // while others may be in fetch retry or install retry states. Only when
  8673. // a CO makes it as far as the install rename retry state do we update
  8674. // the version (and file name) info in the idtable record.
  8675. //
  8676. // IDREC_FLAGS_RENAME_DEFERRED is set when renaming the preinstall
  8677. // file to its final destination failed. The first change order that
  8678. // next comes through for this file picks up the COE_FLAG_NEED_RENAME
  8679. // bit and the IDREC_FLAGS_RENAME_DEFERRED bit is cleared.
  8680. //
  8681. // IBCO_INSTALL_REN_RETRY is a CO command State that is set when
  8682. // a rename fails with a retriable condition. The IDtable entry
  8683. // is marked with IDREC_FLAGS_RENAME_DEFERRED incase another change
  8684. // order is accepted prior to this retry change order.
  8685. // Only one CO at a time can have the COE_FLAG_NEED_RENAME set because
  8686. // ChgOrdHoldIssue() ensures only one CO at a time is in process on a
  8687. // given file.
  8688. //
  8689. // Sharing violations preventing final rename install --
  8690. //
  8691. // Note that more than one change order can be pending in the
  8692. // inbound log for the same file and all can be in the IBCO_INSTALL_REN_RETRY
  8693. // state. Each one could be renaming the file to a different name
  8694. // or parent dir and could have been blocked by a sharing violation.
  8695. // Because of this the data in the IDTable entry will have the
  8696. // currently correct target name/ parent dir for the file based on
  8697. // those COs that have won reconcilliation. An added wrinkle is that
  8698. // in the case of a DIR-DIR name morph conflict where the incoming
  8699. // remote CO loses the name we insert a local rename CO into the stream
  8700. // to prop the name change to all outbound partners. This increases the
  8701. // version number but since it is a local CO it will not go through
  8702. // rename path. If the original incoming CO failed to complete the
  8703. // rename when it later retries it will lose in reconcile based on
  8704. // a lower version number. This strands the file in the pre-install
  8705. // area. To avoid this problem we always accept retry change orders that
  8706. // are in the IBCO_INSTALL_REN_RETRY state when the
  8707. // IDREC_FLAGS_RENAME_DEFERRED flag is set. If the RENAME_DEFERRED
  8708. // flag is clear then the final rename has been done by another CO and
  8709. // we can just flush the REN_RETRY change order.
  8710. //
  8711. // BTW: Rename is coded to be idempotent. The opens are by ID, not name,
  8712. // and renaming a file to itself turns into a successful NOP.
  8713. //
  8714. // A parallel set of flags exist for deferred deletes. That is,
  8715. // COE_FLAG_NEED_DELETE, IDREC_FLAGS_DELETE_DEFERRED,
  8716. // IBCO_INSTALL_DEL_RETRY. See scenario description for
  8717. // IDREC_FLAGS_DELETE_DEFERRED in schema.h.
  8718. //
  8719. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_RENAME_DEFERRED)) {
  8720. SET_COE_FLAG(ChangeOrder, COE_FLAG_NEED_RENAME);
  8721. // SUDARC-DEV - Comment out this part so that we preserve the flags in the idtable
  8722. // record.
  8723. // ClearIdRecFlag(IDTableRec, IDREC_FLAGS_RENAME_DEFERRED);
  8724. }
  8725. //
  8726. // If this is a rename CO or there is a pending rename on this file
  8727. // (was marked in the IDTable) then check for possible name morph
  8728. // conflict on the new name.
  8729. //
  8730. if (!MorphGenCo &&
  8731. (BooleanFlagOn(CoCmd->ContentCmd, USN_REASON_RENAME_NEW_NAME) ||
  8732. CO_STATE_IS(ChangeOrder, IBCO_INSTALL_REN_RETRY) ||
  8733. COE_FLAG_ON(ChangeOrder, COE_FLAG_NEED_RENAME))) {
  8734. FStatus = ChgOrdCheckNameMorphConflict(ThreadCtx,
  8735. IDTableCtxNC,
  8736. ReplicaNumber,
  8737. ChangeOrder);
  8738. if (FStatus == FrsErrorNameMorphConflict) {
  8739. DPRINT(0,"++ NM: Possible Name Morph Conflict on rename\n");
  8740. DBS_DISPLAY_RECORD_SEV(4, IDTableCtxNC, TRUE);
  8741. CHANGE_ORDER_TRACE(3, ChangeOrder, "Poss Morph Conflict - ren");
  8742. //
  8743. // It is possible that a previous change order has already
  8744. // resolved the conflict. If so don't do it again.
  8745. // The originator guid and the VSN must match but the filename
  8746. // must be different.
  8747. //
  8748. DPRINT(0,"++ NM: Checking for name conflict already resolved.\n");
  8749. RStatus = FrsGuidCompare(&CoCmd->OriginatorGuid,
  8750. &IDTableRec->OriginatorGuid);
  8751. if ((RStatus == 0) &&
  8752. (IDTableRec->OriginatorVSN == CoCmd->FrsVsn)) {
  8753. //
  8754. // Conflict resolved previously. Bag this CO.
  8755. //
  8756. FStatus = FrsErrorInvalidChangeOrder;
  8757. //
  8758. // File name on this IDTable entry better be different.
  8759. //
  8760. // The file name in the IDTable entry may be the same
  8761. // if a previous duplicate co won the name and the
  8762. // delete of the loser follows this soon-to-be rejected
  8763. // change order. The original remote co won the name
  8764. // because the name did not yet exist. The rename of
  8765. // the preinstall file to its final name resulted in
  8766. // the deletion of the local file. I assume this is
  8767. // the correct protocol for a remote co to "win" the
  8768. // name over a very recently named local file. The
  8769. // local file appeared with the same name as the
  8770. // result of a rename of an existing local file while
  8771. // the staging file was being fetched or installed
  8772. // into the preinstall area.
  8773. //
  8774. // FRS_ASSERT(wcscmp(CoCmd->FileName, IDTableRec->FileName) != 0);
  8775. }
  8776. } else {
  8777. CLEANUP_FS(0,"++ NM: WARNING - Unexpected result from ChgOrdCheckNameMorphConflict.",
  8778. FStatus, RETURN_ERROR);
  8779. }
  8780. }
  8781. #if 0
  8782. // TEST CODE VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
  8783. //
  8784. // test code to be sure the CurrentFileUsn Is still right.
  8785. //
  8786. WStatus1 = FrsOpenSourceFileById(&FileHandle,
  8787. &FileNetworkOpenInfo,
  8788. NULL,
  8789. Replica->pVme->VolumeHandle,
  8790. (PULONG)&ChangeOrder->FileReferenceNumber,
  8791. sizeof(ULONGLONG),
  8792. // READ_ACCESS,
  8793. READ_ATTRIB_ACCESS,
  8794. ID_OPTIONS,
  8795. SHARE_ALL,
  8796. FILE_OPEN);
  8797. if (WIN_SUCCESS(WStatus1)) {
  8798. FrsReadFileUsnData(FileHandle, &CurrentFileUsn);
  8799. FRS_CLOSE(FileHandle);
  8800. if (CurrentFileUsn != IDTableRec->CurrentFileUsn) {
  8801. DPRINT(0, "++ WARNING - ID Table record to file mismatch\n");
  8802. DPRINT1(0, "++ File's Usn is: %08x %08x\n",
  8803. PRINTQUAD(CurrentFileUsn));
  8804. DPRINT1(0, "++ IDTable->CurrentFileUsn is: %08x %08x\n",
  8805. PRINTQUAD(IDTableRec->CurrentFileUsn));
  8806. }
  8807. } else {
  8808. DPRINT(0, "++ ERROR - Can't open target file to check USN\n");
  8809. }
  8810. // TEST CODE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
  8811. #endif
  8812. } else {
  8813. //
  8814. // IDTable hit on Non-MorphGen Local Change Order. Update FileUsn to
  8815. // the USN of the most recent USN record that we've seen.
  8816. //
  8817. CoCmd->FileUsn = CoCmd->JrnlUsn;
  8818. if (!RetryCo) {
  8819. //
  8820. // Bump Version number to ensure the CO is accepted if this is
  8821. // the first time we have seen this Local Co.
  8822. //
  8823. CoCmd->FileVersionNumber = IDTableRec->VersionNumber + 1;
  8824. }
  8825. //
  8826. // Needed to dampen basic info changes (e.g., resetting the archive bit)
  8827. // Copied from the idtable entry when the change order is created and
  8828. // used to update the change order when the change order is retired.
  8829. //
  8830. ChangeOrder->FileCreateTime = IDTableRec->FileCreateTime;
  8831. ChangeOrder->FileWriteTime = IDTableRec->FileWriteTime;
  8832. ChangeOrder->FileAttributes = IDTableRec->FileAttributes;
  8833. //
  8834. // Note: If this CO was generated off of a directory filter table
  8835. // entry (e.g. Moveout) then the ChangeOrder->Cmd.FileAttributes will
  8836. // be zero. Insert the file attributes from the IDTable record since
  8837. // we can't get them from the original file.
  8838. //
  8839. if (ChangeOrder->Cmd.FileAttributes == 0) {
  8840. ChangeOrder->Cmd.FileAttributes = IDTableRec->FileAttributes;
  8841. }
  8842. //
  8843. // Check if a create change order on this file has been deferred
  8844. // because of:
  8845. // 1. a USN change detected by the stager,
  8846. // 2. a sharing violation prevented us from generating the stage file
  8847. // or stamping the OID on the file.
  8848. //
  8849. // If so change the Location Command to a create and clear the
  8850. // Create Deferred bit. Don't do this for a Delete or Moveout
  8851. // since the file is gone and the stager will just bag the change
  8852. // order.
  8853. //
  8854. if (IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_CREATE_DEFERRED) &&
  8855. !((LocationCmd == CO_LOCATION_DELETE) ||
  8856. (LocationCmd == CO_LOCATION_MOVEOUT))) {
  8857. if (RetryCo) {
  8858. //
  8859. // Bump Version number to ensure the CO is accepted if this is
  8860. // a retry CO AND we are still trying to generate the create
  8861. // change order for the file. See DbsRetryInboundCo() for
  8862. // explanation.
  8863. //
  8864. CoCmd->FileVersionNumber = IDTableRec->VersionNumber + 1;
  8865. }
  8866. ClearIdRecFlag(IDTableRec, IDREC_FLAGS_CREATE_DEFERRED);
  8867. SET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command, CO_LOCATION_CREATE);
  8868. LocationCmd = CO_LOCATION_CREATE;
  8869. SET_CO_FLAG(ChangeOrder, CO_FLAG_LOCATION_CMD);
  8870. SetFlag(CoCmd->ContentCmd, USN_REASON_FILE_CREATE);
  8871. }
  8872. }
  8873. //
  8874. // End of IDTable record found code.
  8875. //
  8876. return FStatus;
  8877. RETURN_JERR:
  8878. FStatus = DbsTranslateJetError(jerr, FALSE);
  8879. RETURN_ERROR:
  8880. //
  8881. // LOG an error message so the user or admin can see what happened.
  8882. //
  8883. DPRINT1(1, "++ WARN - Replication disabled for file %ws\n", CoCmd->FileName);
  8884. IDTableRec = IDTableCtx->pDataRecord;
  8885. IDTableRec->ReplEnabled = FALSE;
  8886. //
  8887. // Close the table, reset the TableCtx Tid and Sesid.
  8888. // DbsCloseTable is a Macro, writes 1st arg.
  8889. //
  8890. DbsCloseTable(jerr, ThreadCtx->JSesid, IDTableCtx);
  8891. DPRINT_JS(0,"++ ERROR - JetCloseTable on IDTable failed:", jerr);
  8892. return FStatus;
  8893. }
  8894. ULONG
  8895. ChgOrdInsertIDRecord(
  8896. PTHREAD_CTX ThreadCtx,
  8897. PTABLE_CTX IDTableCtx,
  8898. PTABLE_CTX DIRTableCtx,
  8899. PCHANGE_ORDER_ENTRY ChangeOrder,
  8900. PREPLICA Replica
  8901. )
  8902. /*++
  8903. Routine Description:
  8904. Insert a new record into the ID table and DIR table for files that
  8905. have either been created or renamed into the replcia set.
  8906. The caller supplies the ID and DIR table contexts to avoid
  8907. a storage realloc on every call.
  8908. On return the DIR and IDTables are closed.
  8909. Arguments:
  8910. ThreadCtx -- A Thread context to use for dbid and sesid.
  8911. IDTableCtx -- The table context containing the ID Table record.
  8912. DIRTableCtx -- The table context to use for DIR table access.
  8913. ChangeOrder-- The change order.
  8914. Replica -- The Replica ID table to do the lookup in.
  8915. Return Value:
  8916. Frs Status
  8917. --*/
  8918. {
  8919. #undef DEBSUB
  8920. #define DEBSUB "ChgOrdInsertIDRecord:"
  8921. FILE_OBJECTID_BUFFER FileObjID;
  8922. JET_ERR jerr, jerr1;
  8923. ULONG FStatus;
  8924. ULONG WStatus;
  8925. ULONG GStatus;
  8926. ULONG ReplicaNumber = Replica->ReplicaNumber;
  8927. BOOL RemoteCo, MorphGenCo;
  8928. PDIRTABLE_RECORD DIRTableRec;
  8929. PIDTABLE_RECORD IDTableRec;
  8930. //
  8931. // remote vs. local change order
  8932. //
  8933. RemoteCo = !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  8934. MorphGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN);
  8935. IDTableRec = IDTableCtx->pDataRecord;
  8936. //
  8937. // We should never be inserting an IDTable record with a zero fid.
  8938. //
  8939. FRS_ASSERT(IDTableRec->FileID != ZERO_FID);
  8940. //
  8941. // Write the new IDTable record into the database.
  8942. //
  8943. FStatus = DbsInsertTable(ThreadCtx, Replica, IDTableCtx, IDTablex, NULL);
  8944. //
  8945. // NTFS tunneling will tunnel the object from a recently deleted
  8946. // file onto a newly created file. This confuses the idtable because
  8947. // we now have a fid whose object id matches an existing fid. We
  8948. // workaround the problem by assigning a new object id to the file.
  8949. //
  8950. // link tracking: The tombstone retains the old OID so FRS blocks OID Tunneling.
  8951. // This might be fixed by implementing tunneling support in FRS.
  8952. //
  8953. if (FStatus == FrsErrorKeyDuplicate && !RemoteCo && !MorphGenCo) {
  8954. ZeroMemory(&IDTableRec->FileObjID, sizeof(IDTableRec->FileObjID));
  8955. FrsUuidCreate((GUID *)(&IDTableRec->FileObjID.ObjectId[0]));
  8956. WStatus = ChgOrdStealObjectId(ChangeOrder->Cmd.FileName,
  8957. (PULONG)&ChangeOrder->FileReferenceNumber,
  8958. Replica->pVme,
  8959. &ChangeOrder->Cmd.FileUsn,
  8960. &IDTableRec->FileObjID);
  8961. CLEANUP_WS(0, "++ WORKAROUND FAILED: duplicate key error.", WStatus, ERROR_RETURN);
  8962. COPY_GUID(&ChangeOrder->Cmd.FileGuid, IDTableRec->FileObjID.ObjectId);
  8963. COPY_GUID(&IDTableRec->FileGuid, IDTableRec->FileObjID.ObjectId);
  8964. FStatus = DbsInsertTable(ThreadCtx, Replica, IDTableCtx, IDTablex, NULL);
  8965. CLEANUP_FS(0, "++ WORKAROUND FAILED: duplicate key error.", FStatus, ERROR_RETURN);
  8966. if (FRS_SUCCESS(FStatus)) {
  8967. DPRINT(3, "++ WORKAROUND SUCCEEDED: Ignore duplicate key error\n");
  8968. }
  8969. }
  8970. if (!FRS_SUCCESS(FStatus)) {
  8971. goto ERROR_RETURN;
  8972. }
  8973. //
  8974. // If this is a dir entry insert a new entry in the DIR Table.
  8975. // For remote COs, defer the update of the journal ParentFidTable and
  8976. // the directory FilterTable until the rename install occurs.
  8977. //
  8978. //
  8979. if (IDTableRec->FileIsDir) {
  8980. FStatus = ChgOrdInsertDirRecord(ThreadCtx,
  8981. DIRTableCtx,
  8982. IDTableRec,
  8983. ChangeOrder,
  8984. Replica,
  8985. TRUE);
  8986. }
  8987. if (FRS_SUCCESS(FStatus)) {
  8988. return FrsErrorSuccess;
  8989. }
  8990. ERROR_RETURN:
  8991. //
  8992. // LOG an error message so the user or admin can see what happened.
  8993. //
  8994. DPRINT1(0, "++ ERROR - Replication disabled for file %ws\n", ChangeOrder->Cmd.FileName);
  8995. //
  8996. // Close the table, reset the TableCtx Tid and Sesid.
  8997. // DbsCloseTable is a Macro, writes 1st arg.
  8998. //
  8999. DbsCloseTable(jerr, ThreadCtx->JSesid, DIRTableCtx);
  9000. DPRINT_JS(0,"++ ERROR - JetCloseTable on DIRTable failed:", jerr);
  9001. return FStatus;
  9002. }
  9003. ULONG
  9004. ChgOrdInsertDirRecord(
  9005. PTHREAD_CTX ThreadCtx,
  9006. PTABLE_CTX DIRTableCtx,
  9007. PIDTABLE_RECORD IDTableRec,
  9008. PCHANGE_ORDER_ENTRY ChangeOrder,
  9009. PREPLICA Replica,
  9010. BOOL InsertFlag
  9011. )
  9012. /*++
  9013. Routine Description:
  9014. Insert or update a record into the DIR table for directories that
  9015. have either been created, reanimated, or renamed into the replcia set.
  9016. The caller supplies the DIR table contexts to avoid a storage realloc
  9017. on every call.
  9018. Arguments:
  9019. ThreadCtx -- A Thread context to use for dbid and sesid.
  9020. DIRTableCtx -- The table context to use for DIR table access.
  9021. IDTableRec - ID table record.
  9022. ChangeOrder-- The change order.
  9023. Replica -- The Replica ID table to do the lookup in.
  9024. InsertFlag -- True if this is an insert. False if this is an update.
  9025. Return Value:
  9026. Frs Status
  9027. --*/
  9028. {
  9029. #undef DEBSUB
  9030. #define DEBSUB "ChgOrdInsertDirRecord:"
  9031. JET_ERR jerr, jerr1;
  9032. ULONG FStatus;
  9033. ULONG WStatus;
  9034. ULONG ReplicaNumber = Replica->ReplicaNumber;
  9035. ULONG LocationCmd;
  9036. BOOL RemoteCo;
  9037. BOOL MoveInGenCo;
  9038. PDIRTABLE_RECORD DIRTableRec;
  9039. //
  9040. // If this is a directory entry insert a new entry in the DIR Table.
  9041. //
  9042. if (!IDTableRec->FileIsDir) {
  9043. return FrsErrorSuccess;
  9044. }
  9045. //
  9046. // remote vs. local change order
  9047. //
  9048. RemoteCo = !CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  9049. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  9050. //
  9051. // Open the DIR table for this replica.
  9052. //
  9053. jerr = DbsOpenTable(ThreadCtx, DIRTableCtx, ReplicaNumber, DIRTablex, NULL);
  9054. CLEANUP_JS(0, "++ error opening DIRTable.", jerr, ERROR_RETURN_JERR);
  9055. DIRTableRec = DIRTableCtx->pDataRecord;
  9056. //
  9057. // Build the DIRTable record and insert it into the DIR table.
  9058. //
  9059. DIRTableRec->DFileGuid = IDTableRec->FileGuid;
  9060. DIRTableRec->DFileID = IDTableRec->FileID;
  9061. DIRTableRec->DParentFileID = IDTableRec->ParentFileID;
  9062. DIRTableRec->DReplicaNumber = Replica->ReplicaNumber;
  9063. wcscpy(DIRTableRec->DFileName, IDTableRec->FileName);
  9064. if (InsertFlag) {
  9065. jerr = DbsInsertTable2(DIRTableCtx);
  9066. //
  9067. // Update the volume filter table for a local CO that:
  9068. // 1. Is marked recovery since we won't see it in the Journal again, or
  9069. // 2. Is the product of a MoveIn sub-dir op since it never came from
  9070. // the Journal process.
  9071. //
  9072. MoveInGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MOVEIN_GEN);
  9073. if (!RemoteCo && (RecoveryCo(ChangeOrder) || MoveInGenCo)) {
  9074. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_REANIMATION)) {
  9075. CHANGE_ORDER_TRACE(3, ChangeOrder, "Add Vol Dir Filter - Reanimate");
  9076. } else {
  9077. CHANGE_ORDER_TRACE(3, ChangeOrder, "Add Vol Dir Filter");
  9078. }
  9079. if (MoveInGenCo) {
  9080. CHANGE_ORDER_TRACE(3, ChangeOrder, "Add Vol Dir Filter - MoveInGenCo");
  9081. }
  9082. WStatus = JrnlAddFilterEntryFromCo(Replica, ChangeOrder, NULL);
  9083. FStatus = FrsTranslateWin32Error(WStatus);
  9084. CLEANUP_WS(4, "++ JrnlAddFilterEntryFromCo failed.", WStatus, ERROR_RETURN);
  9085. }
  9086. } else {
  9087. //
  9088. // Update an existing record.
  9089. //
  9090. jerr = DbsSeekRecord(ThreadCtx,
  9091. &DIRTableRec->DFileGuid,
  9092. DFileGuidIndexx,
  9093. DIRTableCtx);
  9094. if (JET_SUCCESS(jerr)) {
  9095. jerr = DbsUpdateTable(DIRTableCtx);
  9096. if (JET_SUCCESS(jerr) && RemoteCo && CO_MOVE_RS_OR_DIR(LocationCmd)) {
  9097. //
  9098. // Update the volume filter table for new dir in
  9099. // remote change orders. For new file creates this update doesn't
  9100. // occur until the rename install of the pre-install file is done.
  9101. // See DbsRenameFid().
  9102. //
  9103. if (LocationCmd == CO_LOCATION_MOVERS) {
  9104. CHANGE_ORDER_TRACE(3, ChangeOrder, "RmtCo UpdVolDir Filter - MoveRs");
  9105. } else {
  9106. CHANGE_ORDER_TRACE(3, ChangeOrder, "RmtCo UpdVolDir Filter - MoveDir");
  9107. }
  9108. WStatus = JrnlAddFilterEntryFromCo(Replica, ChangeOrder, NULL);
  9109. DPRINT_WS(4, "++ JrnlAddFilterEntryFromCo failed.", WStatus);
  9110. }
  9111. } else if (jerr == JET_errRecordNotFound){
  9112. //
  9113. // The record may not be found in the dir table in some cases.
  9114. // e.g. A update that needs reanimation goes through retry. At this point
  9115. // it has updated the idtable and removed the delete flag. Also going through
  9116. // retry has caused it to lose the entry flags (reanimation flag). For this CO
  9117. // the update will fail with RecordNotFound.
  9118. //
  9119. DPRINT2(1, "++ WARN - DbsSeekRecord on %ws failed. Attempting insert: JStatus = %s\n",
  9120. Replica->ReplicaName->Name, ErrLabelJet(jerr));
  9121. jerr = DbsInsertTable2(DIRTableCtx);
  9122. //
  9123. // Update the volume filter table for a local CO that:
  9124. // 1. Is marked recovery since we won't see it in the Journal again, or
  9125. // 2. Is the product of a MoveIn sub-dir op since it never came from
  9126. // the Journal process.
  9127. //
  9128. MoveInGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MOVEIN_GEN);
  9129. if (!RemoteCo && (RecoveryCo(ChangeOrder) || MoveInGenCo)) {
  9130. if (COE_FLAG_ON(ChangeOrder, COE_FLAG_REANIMATION)) {
  9131. CHANGE_ORDER_TRACE(3, ChangeOrder, "Add Vol Dir Filter - Reanimate");
  9132. } else {
  9133. CHANGE_ORDER_TRACE(3, ChangeOrder, "Add Vol Dir Filter");
  9134. }
  9135. if (MoveInGenCo) {
  9136. CHANGE_ORDER_TRACE(3, ChangeOrder, "Add Vol Dir Filter - MoveInGenCo");
  9137. }
  9138. WStatus = JrnlAddFilterEntryFromCo(Replica, ChangeOrder, NULL);
  9139. FStatus = FrsTranslateWin32Error(WStatus);
  9140. CLEANUP_WS(4, "++ JrnlAddFilterEntryFromCo failed.", WStatus, ERROR_RETURN);
  9141. }
  9142. } else {
  9143. goto ERROR_RETURN_JERR;
  9144. }
  9145. }
  9146. CLEANUP1_JS(0, "++ Error %s DIRTable record :",
  9147. (InsertFlag ? "inserting" : "updating"), jerr, ERROR_RETURN_JERR);
  9148. DbsCloseTable(jerr, ThreadCtx->JSesid, DIRTableCtx);
  9149. DPRINT_JS(0,"++ ERROR - JetCloseTable on DIRTable failed:", jerr);
  9150. return FrsErrorSuccess;
  9151. ERROR_RETURN_JERR:
  9152. FStatus = DbsTranslateJetError(jerr, FALSE);
  9153. ERROR_RETURN:
  9154. //
  9155. // LOG an error message so the user or admin can see what happened.
  9156. //
  9157. DBS_DISPLAY_RECORD_SEV(0, DIRTableCtx, FALSE);
  9158. DPRINT1(0, "++ ERROR - Replication disabled for Dir %ws\n", ChangeOrder->Cmd.FileName);
  9159. //
  9160. // Close the table, reset the TableCtx Tid and Sesid.
  9161. // DbsCloseTable is a Macro, writes 1st arg.
  9162. //
  9163. DbsCloseTable(jerr, ThreadCtx->JSesid, DIRTableCtx);
  9164. DPRINT_JS(0,"++ ERROR - JetCloseTable on DIRTable failed:", jerr);
  9165. return FStatus;
  9166. }
  9167. ULONG
  9168. ChgOrdCheckAndFixTunnelConflict(
  9169. PTHREAD_CTX ThreadCtx,
  9170. PTABLE_CTX IDTableCtxNew,
  9171. PTABLE_CTX IDTableCtxExist,
  9172. PREPLICA Replica,
  9173. PCHANGE_ORDER_ENTRY ChangeOrder,
  9174. BOOL *IDTableRecExists
  9175. )
  9176. /*++
  9177. Routine Description:
  9178. On input, the ChangeOrder must be for a new file and have been
  9179. assigned a guid (even if the guid has not been hammered onto
  9180. the file, yet). In other words, this function is designed to
  9181. be called out of ChgOrdReadIDRecord() after the call to
  9182. DbsInitializeIDTableRecord().
  9183. 267544 ChangeOrder->COMorphGenCount < 50 assertion in NTFRS.
  9184. Tunneling assigns the guid of a deleted file to a newly created
  9185. local file. In ChgOrdReadIDRecord(), a new idtable entry for
  9186. the file is initialized with the tunneled guid. The file
  9187. then loses a morph conflict. A delete-co is generated.
  9188. When the delete-co is passed to ChgOrdReadIDRecord(), the
  9189. wrong idtable record is read because the file's guid is used
  9190. as the index. The delete-co is updated with the wrong FID and
  9191. finally retires and updates the wrong idtable entry. The co
  9192. for the newly created file again enters ChgOrdReadIDRecord()
  9193. and the process is repeated until the ASSERT(morphgencount < 50).
  9194. Read the ID Table record by guid for this change order.
  9195. If found and the FID does not match then steal the existing
  9196. id table entry by updating the FID to be the FID for this file.
  9197. In other words, keep the guid/fid relationship intact in the idtable.
  9198. Arguments:
  9199. ThreadCtx -- A Thread context to use for dbid and sesid.
  9200. IDTableCtxNew -- The table context for new ID Table record
  9201. IDTableCtxExist -- The table context for existing ID Table record
  9202. Replica -- The Replica for the ID Table to do the lookup in.
  9203. ChangeOrder-- The change order.
  9204. IDTableRecExists - Set to TRUE if the idtable record exists. The
  9205. caller then knows that the idtable entry shouldn't
  9206. be inserted.
  9207. Return Value:
  9208. Frs Status:
  9209. FrsErrorTunnelConflict - tunnel conflict detected and resolved.
  9210. FrsErrorSuccess - no conflict
  9211. Any other error is a failure.
  9212. --*/
  9213. {
  9214. #undef DEBSUB
  9215. #define DEBSUB "ChgOrdCheckAndFixTunnelConflict:"
  9216. JET_ERR jerr;
  9217. ULONG FStatus;
  9218. ULONG WStatus;
  9219. PIDTABLE_RECORD IDTableRecNew;
  9220. PIDTABLE_RECORD IDTableRecExist;
  9221. ULONG IdtFieldUpdateList[1];
  9222. PCHANGE_ORDER_COMMAND CoCmd = &ChangeOrder->Cmd;
  9223. //
  9224. // Open the idtable
  9225. //
  9226. jerr = DbsOpenTable(ThreadCtx,
  9227. IDTableCtxExist,
  9228. Replica->ReplicaNumber,
  9229. IDTablex,
  9230. NULL);
  9231. //
  9232. // Unexpected problem opening the idtable
  9233. //
  9234. if (!JET_SUCCESS(jerr)) {
  9235. FStatus = DbsTranslateJetError(jerr, TRUE);
  9236. return FStatus;
  9237. }
  9238. //
  9239. // Read the existing idtable record by guid (stored in new idtable entry)
  9240. //
  9241. IDTableRecNew = IDTableCtxNew->pDataRecord;
  9242. jerr = DbsReadRecord(ThreadCtx,
  9243. &IDTableRecNew->FileGuid,
  9244. GuidIndexx,
  9245. IDTableCtxExist);
  9246. //
  9247. // No conflict; done
  9248. //
  9249. if (jerr == JET_errRecordNotFound) {
  9250. FStatus = FrsErrorSuccess;
  9251. goto CLEANUP;
  9252. }
  9253. //
  9254. // Unexpected problem reading the idtable record
  9255. //
  9256. if (!JET_SUCCESS(jerr)) {
  9257. FStatus = DbsTranslateJetError(jerr, TRUE);
  9258. goto CLEANUP;
  9259. }
  9260. //
  9261. // Does the existing idtable entry have a different fid? If so,
  9262. // steal the entry if it is for a deleted file. This can happen
  9263. // when tunneling assigns an old OID to a new FID.
  9264. //
  9265. IDTableRecExist = IDTableCtxExist->pDataRecord;
  9266. if (IDTableRecNew->FileID == IDTableRecExist->FileID) {
  9267. FStatus = FrsErrorSuccess;
  9268. goto CLEANUP;
  9269. }
  9270. //
  9271. // TUNNEL CONFLICT
  9272. //
  9273. DBS_DISPLAY_RECORD_SEV(5, IDTableCtxExist, TRUE);
  9274. DBS_DISPLAY_RECORD_SEV(5, IDTableCtxNew, TRUE);
  9275. if (IsIdRecFlagSet(IDTableRecExist, IDREC_FLAGS_DELETED)) {
  9276. //
  9277. // This case occurs when the delete of the original file preceeds the
  9278. // create and rename of the new file to the target name.
  9279. //
  9280. // del FID_original (has target_name, loads NTFS tunnel cache)
  9281. // cre FID_new (has a temp name)
  9282. // ren FID_new to target_name (hits in NTFS tunnel cache)
  9283. //
  9284. // When we see the USN record for the create the rename has already
  9285. // been done and FID_new has the OID from FID_original via the tunnel
  9286. // cache. Since we have seen the delete the IDT entry has been marked
  9287. // deleted.
  9288. //
  9289. CHANGE_ORDER_TRACE(3, ChangeOrder, "Tunnel Conflict - Updating IDT");
  9290. } else {
  9291. //
  9292. // This case occurs when the delete of the original file follows the
  9293. // create of the new file.
  9294. //
  9295. // cre FID_new (has a temp name)
  9296. // del FID_original (has target_name, loads NTFS tunnel cache)
  9297. // ren FID_new to target_name (hits in NTFS tunnel cache)
  9298. //
  9299. // As above when we see the USN record for the create the rename has
  9300. // already been done and FID_new has the OID from FID_original via
  9301. // the tunnel cache. BUT in this case we have not yet seen the
  9302. // USN record for the delete so the IDT entry has not been marked
  9303. // deleted. Since the OID got tunneled we know there is another USN
  9304. // record coming that will reference FID_new so we can reject the
  9305. // create CO now and let it be handled at that time. This is similar
  9306. // to updating a file that wasn't in the IDTable because it matched
  9307. // the file filter at the time it was created so the USN record was
  9308. // skipped.
  9309. //
  9310. //
  9311. // Actually we cannot skip the create because the other CO that "must"
  9312. // be coming could have been combined with this one in the aging cache.
  9313. // THis problem occurred on saves of excel files.
  9314. // It is even possible not to have another CO on the way:
  9315. //
  9316. // Consider an ap that does this:
  9317. // Open A
  9318. // Rename A => B
  9319. // Close "B" (opened as A)
  9320. // Open B
  9321. // Mark B Deleted
  9322. // Open A
  9323. // Close A
  9324. // Close B
  9325. //
  9326. // The Oid from the original A will be tunneled to the new A,
  9327. // but the order of the closes will make the USN look like this:
  9328. //
  9329. // Rename A => B
  9330. // Create A
  9331. // Delete B
  9332. //
  9333. //
  9334. // Now we process the create of A with the tunneled Oid before the delete of B.
  9335. //
  9336. CHANGE_ORDER_TRACE(3, ChangeOrder, "Tunnel Conflict - Updating IDT [Original File not marked Deleted]");
  9337. // CHANGE_ORDER_TRACE(3, ChangeOrder, "Tunnel Conflict - Reject CO");
  9338. // FStatus = FrsErrorTunnelConflictRejectCO;
  9339. // goto CLEANUP;
  9340. }
  9341. //
  9342. // Pick up the version of the existing record + 1 just as if this
  9343. // change order were a local co against the existing record (which
  9344. // it is). Otherwise, the change order will be rejected on our
  9345. // partners because the version on a new file is set to 0.
  9346. //
  9347. CoCmd->FileVersionNumber = IDTableRecExist->VersionNumber + 1;
  9348. //
  9349. // Update the fid in the existing id table entry
  9350. //
  9351. // Perf: Is the below necessary? the idtable rec will be updated
  9352. // by ChgOrdAccept() on return because IDTableRecExists
  9353. // is set to TRUE below.
  9354. //
  9355. IdtFieldUpdateList[0] = FileIDx;
  9356. FStatus = DbsUpdateIDTableFields(ThreadCtx, Replica, ChangeOrder, IdtFieldUpdateList, 1);
  9357. CLEANUP_FS(0, "++ ERROR - DbsUpdateIDTableFields failed.", FStatus, CLEANUP);
  9358. //
  9359. // The existing idtable entry now belongs to the new file that
  9360. // was assigned an old OID (probably by tunneling).
  9361. //
  9362. *IDTableRecExists = TRUE;
  9363. //
  9364. // Tunnel fixup was a success.
  9365. //
  9366. FStatus = FrsErrorTunnelConflict;
  9367. CLEANUP:
  9368. DbsCloseTable(jerr, ThreadCtx->JSesid, IDTableCtxExist);
  9369. return FStatus;
  9370. }
  9371. ULONG
  9372. ChgOrdCheckNameMorphConflict(
  9373. PTHREAD_CTX ThreadCtx,
  9374. PTABLE_CTX IDTableCtxNC,
  9375. ULONG ReplicaNumber,
  9376. PCHANGE_ORDER_ENTRY ChangeOrder
  9377. )
  9378. /*++
  9379. Routine Description:
  9380. Read the ID Table record for the parent guid - filename for this change
  9381. order from the replica set specified by the ReplicaNumber.
  9382. Scan all records with matching parent guid and filename looking for an
  9383. undeleted entry with a non-matching file guid. If found then we have
  9384. a name morph conflict.
  9385. Arguments:
  9386. ThreadCtx -- A Thread context to use for dbid and sesid.
  9387. IDTableCtxNC -- The table context containing the ID Table record for the Name Conflict.
  9388. ReplicaNumber -- The Replica ID for the ID Table to do the lookup in.
  9389. ChangeOrder-- The change order.
  9390. Return Value:
  9391. Frs Status:
  9392. FrsErrorSuccess - IDTable record was not found so there is no conflict.
  9393. FrsErrorNameMorphConflict - A parent guid/name conflict with
  9394. another entry in the IDTable was found.
  9395. Data for this conflicting
  9396. entry is returned in IDTableCtxNC.
  9397. Any other error is a failure.
  9398. --*/
  9399. {
  9400. #undef DEBSUB
  9401. #define DEBSUB "ChgOrdCheckNameMorphConflict:"
  9402. UNICODE_STRING TempUStr;
  9403. JET_ERR jerr, jerr1;
  9404. ULONG FStatus;
  9405. PVOID KeyArray[2];
  9406. PIDTABLE_RECORD IDTableRec;
  9407. JET_SESID Sesid;
  9408. JET_TABLEID Tid;
  9409. USHORT IDTFileNameLength;
  9410. USHORT SaveCoFileNameLength, LenLimit;
  9411. PCHANGE_ORDER_COMMAND CoCmd = &ChangeOrder->Cmd;
  9412. #define ESE_INDEX_LENGTH_LIMIT (JET_cbKeyMost & 0XFFFFFFFE)
  9413. jerr = DbsOpenTable(ThreadCtx, IDTableCtxNC, ReplicaNumber, IDTablex, NULL);
  9414. if (!JET_SUCCESS(jerr)) {
  9415. FStatus = DbsTranslateJetError(jerr, TRUE);
  9416. return FStatus;
  9417. }
  9418. Sesid = ThreadCtx->JSesid;
  9419. Tid = IDTableCtxNC->Tid;
  9420. FRS_ASSERT(ChangeOrder->pParentGuid != NULL);
  9421. KeyArray[0] = (PVOID)ChangeOrder->pParentGuid;
  9422. KeyArray[1] = (PVOID)CoCmd->FileName;
  9423. SaveCoFileNameLength = ChangeOrder->UFileName.Length;
  9424. //
  9425. // Seek to the first record with a matching parent GUID and Filename.
  9426. //
  9427. FStatus = DbsRecordOperationMKey(ThreadCtx,
  9428. ROP_SEEK,
  9429. KeyArray,
  9430. ParGuidFileNameIndexx,
  9431. IDTableCtxNC);
  9432. //
  9433. // Read subsequent records (there could be multiples)
  9434. // checking for a non-tombstone.
  9435. //
  9436. while (FRS_SUCCESS(FStatus)) {
  9437. //
  9438. // Read the record and check for tombstone
  9439. //
  9440. FStatus = DbsTableRead(ThreadCtx, IDTableCtxNC);
  9441. IDTableRec = IDTableCtxNC->pDataRecord;
  9442. if (!GUIDS_EQUAL(ChangeOrder->pParentGuid, &IDTableRec->ParentGuid)) {
  9443. //
  9444. // No more matching records.
  9445. //
  9446. FStatus = FrsErrorNotFound;
  9447. break;
  9448. }
  9449. IDTFileNameLength = wcslen(IDTableRec->FileName) * sizeof(WCHAR);
  9450. //
  9451. // Indexes in Jet are limited to a max size of ESE_INDEX_LENGTH_LIMIT
  9452. // bytes for LongText Columns.
  9453. //
  9454. // So we can only rely on the length check to tell us when there
  9455. // are no more matchng entries in this key when we are going after
  9456. // a filename shorter than this or Jet returns us a result shorter
  9457. // than this.
  9458. //
  9459. if ((SaveCoFileNameLength != IDTFileNameLength) &&
  9460. ((SaveCoFileNameLength < ESE_INDEX_LENGTH_LIMIT) ||
  9461. (IDTFileNameLength < ESE_INDEX_LENGTH_LIMIT))){
  9462. FStatus = FrsErrorNotFound;
  9463. break;
  9464. }
  9465. //
  9466. // Finally do the case insensitive compare.
  9467. //
  9468. FrsSetUnicodeStringFromRawString(&TempUStr,
  9469. SIZEOF(IDTABLE_RECORD, FileName),
  9470. IDTableRec->FileName,
  9471. IDTFileNameLength);
  9472. //
  9473. // Either the lengths are the same or they are both over
  9474. // ESE_INDEX_LENGTH_LIMIT, possibly both. Either way, for the
  9475. // purpose of loop termination we can't compare more than this.
  9476. //
  9477. LenLimit = min(SaveCoFileNameLength, ESE_INDEX_LENGTH_LIMIT);
  9478. LenLimit = min(LenLimit, IDTFileNameLength);
  9479. if (LenLimit == ESE_INDEX_LENGTH_LIMIT) {
  9480. TempUStr.Length = LenLimit;
  9481. ChangeOrder->UFileName.Length = LenLimit;
  9482. }
  9483. if (!RtlEqualUnicodeString(&TempUStr, &ChangeOrder->UFileName, TRUE)) {
  9484. ChangeOrder->UFileName.Length = SaveCoFileNameLength;
  9485. FStatus = FrsErrorNotFound;
  9486. break;
  9487. }
  9488. if (LenLimit == ESE_INDEX_LENGTH_LIMIT) {
  9489. TempUStr.Length = IDTFileNameLength;
  9490. ChangeOrder->UFileName.Length = SaveCoFileNameLength;
  9491. }
  9492. //
  9493. // We found a match, maybe.
  9494. //
  9495. DBS_DISPLAY_RECORD_SEV(5, IDTableCtxNC, TRUE);
  9496. //
  9497. // Check for undeleted record where the File Guid does not match the CO.
  9498. // If such a record is found then we have a name morph conflict.
  9499. //
  9500. if ((!IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED) ||
  9501. IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETE_DEFERRED))&&
  9502. !GUIDS_EQUAL(&CoCmd->FileGuid, &IDTableRec->FileGuid)) {
  9503. //
  9504. // Now do the full len compare if the lengths were greater than
  9505. // ESE_INDEX_LENGTH_LIMIT. If the lengths are over the index
  9506. // limit and the strings fail to compare equal then keep looking.
  9507. //
  9508. if ((LenLimit != ESE_INDEX_LENGTH_LIMIT) ||
  9509. RtlEqualUnicodeString(&TempUStr, &ChangeOrder->UFileName, TRUE)) {
  9510. FStatus = FrsErrorSuccess;
  9511. break;
  9512. }
  9513. }
  9514. //
  9515. // go to next record in this index.
  9516. //
  9517. jerr = JetMove(Sesid, Tid, JET_MoveNext, 0);
  9518. //
  9519. // If the record is not there then no conflict
  9520. //
  9521. if (jerr == JET_errNoCurrentRecord ) {
  9522. FStatus = FrsErrorNotFound;
  9523. break;
  9524. }
  9525. if (!JET_SUCCESS(jerr)) {
  9526. FStatus = DbsTranslateJetError(jerr, FALSE);
  9527. DPRINT_FS(0, "++ JetMove error:", FStatus);
  9528. break;
  9529. }
  9530. }
  9531. //
  9532. // Success return above means we found a record with a matching parent
  9533. // Guid and File name but a different File Guid. This is a name morph
  9534. // conflict.
  9535. //
  9536. if (FRS_SUCCESS(FStatus)) {
  9537. DPRINT(0,"++ NM: Possible Name Morph Conflict\n");
  9538. DBS_DISPLAY_RECORD_SEV(4, IDTableCtxNC, TRUE);
  9539. FStatus = FrsErrorNameMorphConflict;
  9540. }
  9541. else
  9542. if (FStatus != FrsErrorNotFound) {
  9543. DPRINT_FS(0,"++ NM: WARNING - Unexpected result from DbsTableRead.", FStatus);
  9544. } else {
  9545. FStatus = FrsErrorSuccess;
  9546. }
  9547. DbsCloseTable(jerr, ThreadCtx->JSesid, IDTableCtxNC);
  9548. return FStatus;
  9549. }
  9550. VOID
  9551. ChgOrdProcessControlCo(
  9552. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  9553. IN PREPLICA Replica
  9554. )
  9555. /*++
  9556. Routine Description:
  9557. Process the control change order.
  9558. Arguments:
  9559. ChangeOrder-- The change order.
  9560. Replica -- The Replica struct.
  9561. Return Value:
  9562. None.
  9563. --*/
  9564. {
  9565. #undef DEBSUB
  9566. #define DEBSUB "ChgOrdProcessControlCo:"
  9567. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  9568. PCXTION CtlCxtion;
  9569. switch (CoCmd->ContentCmd) {
  9570. case FCN_CORETRY_LOCAL_ONLY:
  9571. CHANGE_ORDER_TRACE(3, ChangeOrder, "CORETRY_LOCAL_ONLY Ctrl CO");
  9572. break;
  9573. //
  9574. // Retrying COs from a single inbound connection.
  9575. //
  9576. case FCN_CORETRY_ONE_CXTION:
  9577. CHANGE_ORDER_TRACE(3, ChangeOrder, "CORETRY_ONE_CXTION Ctrl CO");
  9578. //
  9579. // Find the inbound cxtion for this CO and advance state to starting.
  9580. //
  9581. LOCK_CXTION_TABLE(Replica);
  9582. CtlCxtion = GTabLookupNoLock(Replica->Cxtions, &CoCmd->CxtionGuid, NULL);
  9583. if (CtlCxtion != NULL) {
  9584. FRS_ASSERT(CtlCxtion->Inbound);
  9585. //
  9586. // If cxtion no longer in the request start state leave it alone
  9587. // else advance to STARTING. Doing this here ensures that we
  9588. // don't hold issue on the process queue (and hang) if there are
  9589. // lingering COs for this Conection ahead of us. I.E. any COs
  9590. // we see for this cxtion now are either retry COs from the inlog
  9591. // or newly arrived COs.
  9592. //
  9593. if (CxtionStateIs(CtlCxtion, CxtionStateStarting)) {
  9594. //
  9595. // The cxtion is attempting to unjoin. Skip the inbound log
  9596. // scan and advance to the SENDJOIN state so the UNJOIN will
  9597. // proceed.
  9598. //
  9599. if (CxtionFlagIs(CtlCxtion, (CXTION_FLAGS_DEFERRED_UNJOIN |
  9600. CXTION_FLAGS_DEFERRED_DELETE))) {
  9601. SetCxtionState(CtlCxtion, CxtionStateSendJoin);
  9602. RcsSubmitReplicaCxtion(Replica, CtlCxtion, CMD_JOIN_CXTION);
  9603. } else {
  9604. SetCxtionState(CtlCxtion, CxtionStateScanning);
  9605. //
  9606. // Connection startup request. Fork a thread and
  9607. // paw thru Inlog.
  9608. //
  9609. ChgOrdRetrySubmit(Replica,
  9610. CtlCxtion,
  9611. FCN_CORETRY_ONE_CXTION,
  9612. FALSE);
  9613. }
  9614. }
  9615. } else {
  9616. DPRINT(0, "++ ERROR - No connection struct for control cmd.\n");
  9617. FRS_ASSERT(!"No connection struct for control CO");
  9618. }
  9619. UNLOCK_CXTION_TABLE(Replica);
  9620. break;
  9621. case FCN_CORETRY_ALL_CXTIONS:
  9622. CHANGE_ORDER_TRACE(3, ChangeOrder, "CORETRY_ALL_CXTIONS Ctrl CO");
  9623. break;
  9624. case FCN_CO_NORMAL_VVJOIN_TERM:
  9625. CHANGE_ORDER_TRACE(3, ChangeOrder, "CO_NORMAL_VVJOIN_TER Ctrl CO");
  9626. break;
  9627. default:
  9628. DPRINT1(0, "++ ERROR - Invalid control command: %d\n", CoCmd->ContentCmd);
  9629. FRS_ASSERT(!"Invalid control command in CO");
  9630. }
  9631. //
  9632. // Done with control cmd. Caller is expected to
  9633. // release process queue lock, cleanup and unidle the queue.
  9634. //
  9635. }
  9636. VOID
  9637. ChgOrdTranslateGuidFid(
  9638. PTHREAD_CTX ThreadCtx,
  9639. PTABLE_CTX IDTableCtx,
  9640. PCHANGE_ORDER_ENTRY ChangeOrder,
  9641. PREPLICA Replica
  9642. )
  9643. /*++
  9644. Routine Description:
  9645. Perform translation of FIDs to Guids and vice versa.
  9646. Return bit mask for delete status of target file and the original and
  9647. new parent dirs.
  9648. Arguments:
  9649. ThreadCtx -- A Thread context to use for dbid and sesid.
  9650. IDTableCtx -- The table context to use for the IDTable lookup.
  9651. ChangeOrder-- The change order.
  9652. Replica -- The Replica struct.
  9653. Return Value:
  9654. The results of the call are returned in the change order.
  9655. --*/
  9656. {
  9657. #undef DEBSUB
  9658. #define DEBSUB "ChgOrdTranslateGuidFid:"
  9659. FILE_OBJECTID_BUFFER ObjectIdBuffer;
  9660. ULONGLONG TargetFid;
  9661. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  9662. BOOL CoFromInlog, MorphGenCo;
  9663. ULONG FStatus;
  9664. HANDLE Handle;
  9665. DWORD WStatus;
  9666. CoFromInlog = CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY) ||
  9667. RecoveryCo(ChangeOrder);
  9668. MorphGenCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_MORPH_GEN);
  9669. CLEAR_COE_FLAG(ChangeOrder, (COE_FLAG_IDT_ORIG_PARENT_DEL |
  9670. COE_FLAG_IDT_ORIG_PARENT_ABS |
  9671. COE_FLAG_IDT_NEW_PARENT_DEL |
  9672. COE_FLAG_IDT_NEW_PARENT_ABS |
  9673. COE_FLAG_IDT_NEW_PARENT_DEL_DEF |
  9674. COE_FLAG_IDT_TARGET_DEL |
  9675. COE_FLAG_IDT_TARGET_ABS));
  9676. //
  9677. // Local COs translate FIDs to Guids. Unless they came from the Inlog
  9678. // or were produced by Name Morph resolution.
  9679. //
  9680. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO) && !MorphGenCo && !CoFromInlog) {
  9681. //
  9682. // Fill in the Originator Guid for the local change order.
  9683. //
  9684. COPY_GUID(&CoCmd->OriginatorGuid, &Replica->ReplicaVersionGuid);
  9685. //
  9686. // (Do it here in case we create a new ID table record).
  9687. // We supply IDTableCtx to avoid a storage realloc on every call.
  9688. //
  9689. // Get status of original parent.
  9690. //
  9691. FStatus = DbsFidToGuid(ThreadCtx,
  9692. ChangeOrder->OriginalReplica,
  9693. IDTableCtx,
  9694. &ChangeOrder->OriginalParentFid,
  9695. &CoCmd->OldParentGuid);
  9696. if ((FStatus == FrsErrorIdtFileIsDeleted) ||
  9697. (FStatus == FrsErrorIdtFileIsDeleteDef)) {
  9698. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_DEL);
  9699. } else
  9700. if (!FRS_SUCCESS(FStatus)) {
  9701. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_ABS);
  9702. }
  9703. //
  9704. // Get Status of New Parent.
  9705. //
  9706. if (ChangeOrder->NewParentFid != ChangeOrder->OriginalParentFid) {
  9707. FStatus = DbsFidToGuid(ThreadCtx,
  9708. ChangeOrder->NewReplica,
  9709. IDTableCtx,
  9710. &ChangeOrder->NewParentFid,
  9711. &CoCmd->NewParentGuid);
  9712. } else {
  9713. CoCmd->NewParentGuid = CoCmd->OldParentGuid;
  9714. }
  9715. if (FStatus == FrsErrorIdtFileIsDeleted) {
  9716. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL);
  9717. } else if (FStatus == FrsErrorIdtFileIsDeleteDef) {
  9718. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL);
  9719. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL_DEF);
  9720. } else if (!FRS_SUCCESS(FStatus)) {
  9721. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_ABS);
  9722. }
  9723. ChangeOrder->pParentGuid = (ChangeOrder->NewReplica != NULL) ?
  9724. &CoCmd->NewParentGuid :
  9725. &CoCmd->OldParentGuid;
  9726. //
  9727. // Get status of target file or dir.
  9728. //
  9729. FStatus = DbsFidToGuid(ThreadCtx,
  9730. Replica,
  9731. IDTableCtx,
  9732. &ChangeOrder->FileReferenceNumber,
  9733. &CoCmd->FileGuid);
  9734. if ((FStatus == FrsErrorIdtFileIsDeleted) ||
  9735. (FStatus == FrsErrorIdtFileIsDeleteDef)){
  9736. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_TARGET_DEL);
  9737. } else
  9738. if (!FRS_SUCCESS(FStatus)) {
  9739. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_TARGET_ABS);
  9740. }
  9741. //
  9742. // Read object ID from file. This is done here so we have it for
  9743. // the hold issue check to deal with a tunnelled OID.
  9744. //
  9745. CLEAR_COE_FLAG(ChangeOrder, COE_FLAG_OID_FROM_FILE);
  9746. WStatus = FrsOpenSourceFileById(&Handle,
  9747. NULL,
  9748. NULL,
  9749. Replica->pVme->VolumeHandle,
  9750. &ChangeOrder->FileReferenceNumber,
  9751. FILE_ID_LENGTH,
  9752. READ_ATTRIB_ACCESS,
  9753. ID_OPTIONS,
  9754. SHARE_ALL,
  9755. FILE_OPEN);
  9756. if (WIN_SUCCESS(WStatus)) {
  9757. WStatus = FrsGetObjectId(Handle, &ObjectIdBuffer);
  9758. FRS_CLOSE(Handle);
  9759. if (WIN_SUCCESS(WStatus)) {
  9760. COPY_GUID(&ChangeOrder->FileObjectId, &ObjectIdBuffer.ObjectId);
  9761. SET_COE_FLAG(ChangeOrder, COE_FLAG_OID_FROM_FILE);
  9762. }
  9763. } else {
  9764. DPRINT1_WS(4, "++ Could not open (Id %08x %08x) for oid read;",
  9765. PRINTQUAD(ChangeOrder->FileReferenceNumber), WStatus);
  9766. }
  9767. } else {
  9768. //
  9769. // Remote COs and recovery and retry Local COs translate Guids to Fids
  9770. // because the FID is not saved in the change order command.
  9771. // **NOTE** -- If this happened to be a CO for a child that just
  9772. // forced the reanimation of its parent then this CO was pushed back
  9773. // onto the CO process queue with the "old parent FID" so the parent
  9774. // reanimation could then proceed. Now when the child has been
  9775. // restarted the parent FID will have changed and the GUID to FID
  9776. // translation here will pick that change up.
  9777. //
  9778. Replica = ChangeOrder->NewReplica;
  9779. //
  9780. // Get status of original parent.
  9781. //
  9782. FStatus = DbsGuidToFid(ThreadCtx,
  9783. Replica,
  9784. IDTableCtx,
  9785. &CoCmd->OldParentGuid,
  9786. &ChangeOrder->OriginalParentFid);
  9787. if ((FStatus == FrsErrorIdtFileIsDeleted) ||
  9788. (FStatus == FrsErrorIdtFileIsDeleteDef)) {
  9789. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_DEL);
  9790. } else
  9791. if (!FRS_SUCCESS(FStatus)) {
  9792. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_ORIG_PARENT_ABS);
  9793. }
  9794. //
  9795. // Get Status of New Parent.
  9796. //
  9797. if (!GUIDS_EQUAL(&CoCmd->NewParentGuid, &CoCmd->OldParentGuid)) {
  9798. FStatus = DbsGuidToFid(ThreadCtx,
  9799. Replica,
  9800. IDTableCtx,
  9801. &CoCmd->NewParentGuid,
  9802. &ChangeOrder->NewParentFid);
  9803. } else {
  9804. ChangeOrder->NewParentFid = ChangeOrder->OriginalParentFid;
  9805. }
  9806. if (FStatus == FrsErrorIdtFileIsDeleted) {
  9807. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL);
  9808. } else if (FStatus == FrsErrorIdtFileIsDeleteDef) {
  9809. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL);
  9810. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_DEL_DEF);
  9811. } else if (!FRS_SUCCESS(FStatus)) {
  9812. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_NEW_PARENT_ABS);
  9813. }
  9814. //
  9815. // For remote change orders, set the Parent FID from the NewParent Fid.
  9816. //
  9817. ChangeOrder->ParentFileReferenceNumber = ChangeOrder->NewParentFid;
  9818. ChangeOrder->pParentGuid = &CoCmd->NewParentGuid;
  9819. //
  9820. // For a morph generated CO that has a FID already, preserve it.
  9821. // Delete MorphGenCos are an example where the incoming CO is losing
  9822. // so the FID is fake for the tombstone.
  9823. // Still need to get the status of the IDTable entry for the target.
  9824. // This is used to abort the MorphGenCo later in those cases where
  9825. // the conflict causing entry was deleted or aborted.
  9826. //
  9827. FStatus = DbsGuidToFid(ThreadCtx,
  9828. Replica,
  9829. IDTableCtx,
  9830. &CoCmd->FileGuid,
  9831. &TargetFid);
  9832. if ((FStatus == FrsErrorIdtFileIsDeleted) ||
  9833. (FStatus == FrsErrorIdtFileIsDeleteDef)) {
  9834. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_TARGET_DEL);
  9835. } else
  9836. if (!FRS_SUCCESS(FStatus)) {
  9837. SET_COE_FLAG(ChangeOrder, COE_FLAG_IDT_TARGET_ABS);
  9838. }
  9839. if (!MorphGenCo || (ChangeOrder->FileReferenceNumber == ZERO_FID)) {
  9840. ChangeOrder->FileReferenceNumber = TargetFid;
  9841. }
  9842. }
  9843. return;
  9844. }
  9845. ULONG
  9846. ChgOrdInsertInlogRecord(
  9847. PTHREAD_CTX ThreadCtx,
  9848. PTABLE_CTX InLogTableCtx,
  9849. PCHANGE_ORDER_ENTRY ChangeOrder,
  9850. PREPLICA Replica,
  9851. BOOL RetryOutOfOrder
  9852. )
  9853. /*++
  9854. Routine Description:
  9855. Insert the Change order command into the inbound log.
  9856. Update the Inlog Commit USN.
  9857. Arguments:
  9858. ThreadCtx -- A Thread context to use for dbid and sesid.
  9859. InLogTableCtx -- The table context to use for the Inbound log insert.
  9860. ChangeOrder-- The change order.
  9861. Replica -- The Replica struct.
  9862. RetryOutOfOrder -- TRUE - insert into the inbound log with the
  9863. retry flag set and marked as out of order.
  9864. FALSE - Don't set retry or out-of-order.
  9865. Return Value:
  9866. FrsErrorStatus
  9867. --*/
  9868. {
  9869. #undef DEBSUB
  9870. #define DEBSUB "ChgOrdInsertInlogRecord:"
  9871. ULONGLONG SeqNum;
  9872. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  9873. ULONG FStatus;
  9874. ULONG LocationCmd;
  9875. ULONG NewState;
  9876. BOOL LocalCo = CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO);
  9877. //
  9878. // Convert the replica pointers to the local replica ID numbers for storing
  9879. // the record in the database.
  9880. //
  9881. CoCmd->OriginalReplicaNum = ReplicaAddrToId(ChangeOrder->OriginalReplica);
  9882. CoCmd->NewReplicaNum = ReplicaAddrToId(ChangeOrder->NewReplica);
  9883. //
  9884. // Transform ChangeOrder as needed:
  9885. // 1. MOVEIN -> CREATE
  9886. // 2. MOVEOUT -> DELETE
  9887. // 3. MOVEDIR -> MOVEDIR
  9888. // 4. MOVERS -> DELETE to old rs, CREATE to new rs.
  9889. //
  9890. LocationCmd = GET_CO_LOCATION_CMD(ChangeOrder->Cmd, Command);
  9891. //
  9892. // MOVERS: Add code for movers case when supported.
  9893. //
  9894. //
  9895. // Change order starts out in the staging file requested state unless it
  9896. // is a duplicate co or a retry-preinstall co. If duplicate co or a
  9897. // retry-preinstall co then mark it as retry out-of-order.
  9898. //
  9899. if (LocalCo) {
  9900. NewState = IBCO_STAGING_REQUESTED;
  9901. //
  9902. // Normally the change order record extension is allocated for local COs
  9903. // when the co cmd is inserted into the inbound log (by the call to
  9904. // DbsAllocRecordStorage()). But this can be a problem if the local
  9905. // co ends up going thru retry (e.g. sharing violation) where we update
  9906. // the extension in the inlog record. If the extension is null then
  9907. // we end up failing an assert. So allocate and init the contents here.
  9908. //
  9909. if (CoCmd->Extension == NULL) {
  9910. CoCmd->Extension = FrsAlloc(sizeof(CHANGE_ORDER_RECORD_EXTENSION));
  9911. DbsDataInitCocExtension(CoCmd->Extension);
  9912. DPRINT(4, "Allocating initial Coc Extension for localco\n");
  9913. }
  9914. } else {
  9915. NewState = (RetryOutOfOrder ? IBCO_FETCH_RETRY : IBCO_FETCH_REQUESTED);
  9916. }
  9917. //
  9918. // During retry, the duplicate co or preinstall-retry co will not
  9919. // get a retire slot because it is marked out-of-order.
  9920. //
  9921. if (RetryOutOfOrder) {
  9922. SET_CO_FLAG(ChangeOrder, CO_FLAG_RETRY | CO_FLAG_OUT_OF_ORDER);
  9923. }
  9924. SET_CHANGE_ORDER_STATE(ChangeOrder, NewState);
  9925. //
  9926. // Get the lock on the Active Retry table.
  9927. //
  9928. QHashAcquireLock(Replica->ActiveInlogRetryTable);
  9929. //
  9930. // Insert the record into the Inbound log table. The value of the Jet
  9931. // assigned sequence number is returned in the Sequence number field.
  9932. // Set the record data pointer to command portion of the change order
  9933. // entry. The RtCtx (containing the InLogTableCtx) accompanies the
  9934. // change order as it is processed.
  9935. //
  9936. FStatus = DbsInsertTable(ThreadCtx,
  9937. Replica,
  9938. InLogTableCtx,
  9939. INLOGTablex,
  9940. &ChangeOrder->Cmd);
  9941. if (!FRS_SUCCESS(FStatus) && (RetryOutOfOrder)) {
  9942. //
  9943. // Never made it into the inlog so clear the flags.
  9944. //
  9945. CLEAR_CO_FLAG(ChangeOrder, CO_FLAG_RETRY | CO_FLAG_OUT_OF_ORDER);
  9946. }
  9947. if ((FStatus == FrsErrorKeyDuplicate) ||
  9948. (FStatus == FrsErrorDbWriteConflict)) {
  9949. //
  9950. // This can happen duing restart if we already have a change order
  9951. // from a given connection in the inbound log and the inbound
  9952. // partner sends it again because it didn't get an Ack when it sent
  9953. // it the first time. This should be a remote CO.
  9954. //
  9955. // The FrsErrorDbWriteConflict is returned when the change order is
  9956. // being deleted while the insert is attempted. Treat this insert
  9957. // as a duplicate.
  9958. //
  9959. QHashReleaseLock(Replica->ActiveInlogRetryTable);
  9960. return FrsErrorKeyDuplicate;
  9961. } else
  9962. if (!FRS_SUCCESS(FStatus)) {
  9963. DPRINT_FS(0, "++ Error from DbsInsertTable.", FStatus);
  9964. QHashReleaseLock(Replica->ActiveInlogRetryTable);
  9965. return FStatus;
  9966. }
  9967. // There is a small window here where the new CO is in the inlog
  9968. // and the CO SequenceNumber is not in the ActiveInlogRetryTable
  9969. // below. If the retry thread was active and saw the inlog record
  9970. // but did not see the entry in the ActiveInlogRetryTable then it
  9971. // could process the entry and try to ref the CO on the VVRetire
  9972. // list. If this was a DUP CO it may cause an assert because the CO
  9973. // is not marked activated yet this is a retry CO. The problem is
  9974. // that entry on the VVRetire list is for ANOTHER CO already in
  9975. // process. Although that in-process CO has not yet activated the
  9976. // VV Slot.
  9977. // >>>>> One way to fix this is to stop having Jet assign the seqnum
  9978. // Additional problem below requires the table lock.
  9979. //
  9980. // Another way we run into problems is as follows: (BUG 213163)
  9981. // 1. Dup Remote Co arrives
  9982. // 2. Insert into inlog and code below adds seq num to ActiveInlogRetryTable.
  9983. // 3. Ctx switch to retry thread resubmits this CO (Instance A) and makes
  9984. // entry in ActiveInlogRetryTable.
  9985. // 4. Ctx switch back to Dup Co which cleans up and removes entry from
  9986. // ActiveInlogRetryTable.
  9987. // 5. Retry thread runs again and re-inserts Dup Remote Co a 2nd time, Instance B.
  9988. // 6. Instance A runs to completion (may get rejected) and deletes inlog record.
  9989. // 7. Instance B runs to completion and asserts when it tries to delete
  9990. // inlog record a 2nd time and doesn't find it.
  9991. //
  9992. // To prevent this we take the lock on the ActiveInlogRetryTable above.
  9993. //
  9994. //
  9995. // All COs go into the inlog retry table to lock against reissue by the
  9996. // retry thread. See comment at FCN_CORETRY_ALL_CXTIONS for reason.
  9997. // Qhash Insert has to go after DB insert cuz DB Insert assigns the seq num.
  9998. //
  9999. SeqNum = (ULONGLONG) ChangeOrder->Cmd.SequenceNumber;
  10000. QHashInsertLock(Replica->ActiveInlogRetryTable, &SeqNum, NULL, 0);
  10001. QHashReleaseLock(Replica->ActiveInlogRetryTable);
  10002. //
  10003. // Update count of change orders we need to retry for this replica.
  10004. //
  10005. if (RetryOutOfOrder) {
  10006. InterlockedIncrement(&Replica->InLogRetryCount);
  10007. }
  10008. //
  10009. // Update the USN Journal Save point for this replica set. This is the
  10010. // value saved the next time we update the save point in the config record.
  10011. // Local change orders enter the volume process queue in order.
  10012. // So any change orders earlier than this have either been dampened, rejected,
  10013. // or written to the inbound log. JrnlFirstUsn can be zero if it is a
  10014. // change order produced by a sub-tree operation. Only the Change Order
  10015. // on the root dir will update the InlogCommitUsn.
  10016. //
  10017. if (LocalCo) {
  10018. if (CoCmd->JrnlFirstUsn != (USN) 0) {
  10019. DPRINT1(4, "++ Replica->InlogCommitUsn: %08x %08x\n",
  10020. PRINTQUAD(Replica->InlogCommitUsn));
  10021. if (Replica->InlogCommitUsn < CoCmd->JrnlFirstUsn) {
  10022. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  10023. //
  10024. // Get the lock and make the test again.
  10025. //
  10026. AcquireQuadLock(&pVme->QuadWriteLock);
  10027. if (Replica->InlogCommitUsn <= CoCmd->JrnlFirstUsn) {
  10028. Replica->InlogCommitUsn = CoCmd->JrnlFirstUsn;
  10029. ReleaseQuadLock(&pVme->QuadWriteLock);
  10030. } else {
  10031. ReleaseQuadLock(&pVme->QuadWriteLock);
  10032. FRS_ASSERT(Replica->InlogCommitUsn <= CoCmd->JrnlFirstUsn);
  10033. }
  10034. }
  10035. }
  10036. }
  10037. return FrsErrorSuccess;
  10038. }
  10039. JET_ERR
  10040. ChgOrdMoveoutWorker(
  10041. IN PTHREAD_CTX ThreadCtx,
  10042. IN PTABLE_CTX TableCtx,
  10043. IN PVOID Record,
  10044. IN PVOID Context
  10045. )
  10046. /*++
  10047. Routine Description:
  10048. This is a worker function passed to FrsEnumerateTable(). Each time
  10049. it is called with an IDTable record it tests the IDTable for a match
  10050. with the parent file ID parameter. If it matches it generates a
  10051. Delete Change Order for the child and pushes it onto the change order
  10052. process queue.
  10053. Arguments:
  10054. ThreadCtx - Needed to access Jet.
  10055. TableCtx - A ptr to an IDTable context struct.
  10056. Record - A ptr to a IDTable record.
  10057. Context - A ptr to a MOVEOUT_CONTEXT struct.
  10058. Thread Return Value:
  10059. A Jet error status. Success means call us with the next record.
  10060. Failure means don't call again and pass our status back to the
  10061. caller of FrsEnumerateTable().
  10062. --*/
  10063. {
  10064. #undef DEBSUB
  10065. #define DEBSUB "ChgOrdMoveoutWorker:"
  10066. JET_ERR jerr = JET_errSuccess;
  10067. PCHANGE_ORDER_ENTRY DelCoe;
  10068. ULONG WStatus;
  10069. ULONG LocationCmd;
  10070. PREPLICA Replica;
  10071. PCONFIG_TABLE_RECORD ConfigRecord;
  10072. PVOLUME_MONITOR_ENTRY pVme;
  10073. PMOVEOUT_CONTEXT MoveOutContext = (PMOVEOUT_CONTEXT) Context;
  10074. PIDTABLE_RECORD IDTableRec = (PIDTABLE_RECORD) Record ;
  10075. Replica = MoveOutContext->Replica;
  10076. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  10077. pVme = Replica->pVme;
  10078. //
  10079. // Include the entry if replication is enabled and not marked for deletion
  10080. // and not a new file being created when we last shutdown.
  10081. //
  10082. if (IDTableRec->ReplEnabled &&
  10083. !IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_DELETED) &&
  10084. !IsIdRecFlagSet(IDTableRec, IDREC_FLAGS_NEW_FILE_IN_PROGRESS) &&
  10085. (MoveOutContext->ParentFileID == IDTableRec->ParentFileID)) {
  10086. //
  10087. // Normally there should be no dirs that are children of this parent
  10088. // but this can happen if a remote co create is executed for a
  10089. // dir at the same time the subtree containing this dir is being
  10090. // moved out of the replica tree (which is why we are here).
  10091. // The journal code removes the filter entries immediately when it
  10092. // detects a moveout so it can skip future file changes in the subtree.
  10093. // So any dir we now find must have come from a remote CO create that
  10094. // occurred between the time when the journal processed the rename for
  10095. // moveout subdir and now when we are processing the resulting dir
  10096. // moveout CO. We handle this by continuing the delete process on this
  10097. // subidr. This will also get rid of the orphaned journal filter table
  10098. // entry created when the remote co for this dir create was processed.
  10099. // See comment in JrnlFilterLinkChildNoError().
  10100. //
  10101. LocationCmd = CO_LOCATION_DELETE;
  10102. if (IDTRecIsDirectory(IDTableRec)) {
  10103. LocationCmd = CO_LOCATION_MOVEOUT;
  10104. DBS_DISPLAY_RECORD_SEV(0, TableCtx, TRUE);
  10105. DPRINT(3, "++ Hit a dir during MOVEOUT subdir processing\n");
  10106. }
  10107. //
  10108. // Build a delete or moveout change order entry for the IDtable record.
  10109. // This is a local CO that originates from this member.
  10110. //
  10111. DelCoe = ChgOrdMakeFromIDRecord(IDTableRec,
  10112. Replica,
  10113. LocationCmd,
  10114. CO_FLAG_LOCATION_CMD,
  10115. NULL);
  10116. //
  10117. // Set the Jrnl Cxtion Guid and Cxtion ptr for this Local CO.
  10118. //
  10119. INIT_LOCALCO_CXTION_AND_COUNT(Replica, DelCoe);
  10120. //
  10121. // Generate a new Volume Sequnce Number for the change order.
  10122. // But since it gets put on the front of the CO process queue it
  10123. // is probably out of order so set the flag to avoid screwing up
  10124. // dampening.
  10125. //
  10126. NEW_VSN(pVme, &DelCoe->Cmd.FrsVsn);
  10127. DelCoe->Cmd.OriginatorGuid = ConfigRecord->ReplicaVersionGuid;
  10128. //
  10129. // Event time from current time.
  10130. //
  10131. // Note: An alternative is to base this event time on the event time
  10132. // in the original USN record that initiated the moveout?
  10133. // Currently there is no compelling reason to change.
  10134. //
  10135. GetSystemTimeAsFileTime((PFILETIME)&DelCoe->Cmd.EventTime.QuadPart);
  10136. //
  10137. // Bump Version number to ensure the CO is accepted.
  10138. //
  10139. DelCoe->Cmd.FileVersionNumber = IDTableRec->VersionNumber + 1;
  10140. //
  10141. // Note: We wouldn't need to mark this CO as out of order if we
  10142. // resequenced all change order VSNs (for new COs only) as they
  10143. // were fetched off the process queue.
  10144. //
  10145. SET_CO_FLAG(DelCoe, CO_FLAG_LOCALCO |
  10146. CO_FLAG_MORPH_GEN |
  10147. CO_FLAG_OUT_OF_ORDER);
  10148. //
  10149. // Mark this as a MOVEOUT generated Delete CO. This prevents the
  10150. // CO from being inserted into the INLOG and keeps it from deleting
  10151. // the actual file on the Local Machine.
  10152. //
  10153. SET_COE_FLAG(DelCoe, COE_FLAG_DELETE_GEN_CO);
  10154. SET_CHANGE_ORDER_STATE(DelCoe, IBCO_STAGING_REQUESTED);
  10155. if (LocationCmd == CO_LOCATION_DELETE) {
  10156. CHANGE_ORDER_TRACE(3, DelCoe, "Co Push Moveout DelCo to QHead");
  10157. } else {
  10158. CHANGE_ORDER_TRACE(3, DelCoe, "Co Push Moveout subdir to QHead");
  10159. }
  10160. //
  10161. // Push the IDTable Delete Co onto the head of the queue.
  10162. //
  10163. MoveOutContext->NumFiles += 1;
  10164. WStatus = ChgOrdInsertProcQ(Replica, DelCoe, IPQ_HEAD |
  10165. IPQ_DEREF_CXTION_IF_ERR);
  10166. if (!WIN_SUCCESS(WStatus)) {
  10167. jerr = JET_errNotInitialized;
  10168. }
  10169. }
  10170. return jerr;
  10171. }
  10172. ChgOrdMoveInDirTreeWorker(
  10173. IN HANDLE DirectoryHandle,
  10174. IN PWCHAR DirectoryName,
  10175. IN DWORD DirectoryLevel,
  10176. IN PFILE_DIRECTORY_INFORMATION DirectoryRecord,
  10177. IN DWORD DirectoryFlags,
  10178. IN PWCHAR FileName,
  10179. IN PMOVEIN_CONTEXT MoveInContext
  10180. )
  10181. {
  10182. /*++
  10183. Routine Description:
  10184. This is a worker function passed to FrsEnumerateDirectory() to touch files
  10185. and cause USN records to be created so the files will be replicated.
  10186. Before the files are touched checks are made against the filename or
  10187. directory name exclusion filters and the directory level.
  10188. The MoveInContext supplies the FID of the parent dir, the pointer to the
  10189. Replica struct and a flag indicating if this is a Directory scan pass
  10190. (if TRUE) or a File Scan Pass (if FALSE).
  10191. Arguments:
  10192. DirectoryHandle - Handle for this directory.
  10193. DirectoryName - Relative name of directory
  10194. DirectoryLevel - Directory level (0 == root)
  10195. DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
  10196. DirectoryRecord - Record from DirectoryHandle
  10197. FileName - From DirectoryRecord (w/terminating NULL)
  10198. MoveInContext - global info and state
  10199. Return Value:
  10200. WIN32 Error Status.
  10201. --*/
  10202. #undef DEBSUB
  10203. #define DEBSUB "ChgOrdMoveInDirTreeWorker:"
  10204. FILE_BASIC_INFORMATION FileBasicInfo;
  10205. FILE_INTERNAL_INFORMATION FileInternalInfo;
  10206. UNICODE_STRING ObjectName;
  10207. OBJECT_ATTRIBUTES ObjectAttributes;
  10208. IO_STATUS_BLOCK IoStatusBlock;
  10209. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  10210. DWORD WStatus;
  10211. NTSTATUS NtStatus;
  10212. PREPLICA Replica;
  10213. PCONFIG_TABLE_RECORD ConfigRecord;
  10214. PVOLUME_MONITOR_ENTRY pVme;
  10215. ULONG LevelCheck;
  10216. BOOL FileIsDir;
  10217. BOOL Excluded;
  10218. if (FrsIsShuttingDown) {
  10219. DPRINT(0, "++ WARN - Movein aborted; service shutting down\n");
  10220. return ERROR_OPERATION_ABORTED;
  10221. }
  10222. //
  10223. // Filter out temporary files.
  10224. //
  10225. if (DirectoryRecord->FileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
  10226. return ERROR_SUCCESS;
  10227. }
  10228. FileIsDir = BooleanFlagOn(DirectoryRecord->FileAttributes,
  10229. FILE_ATTRIBUTE_DIRECTORY);
  10230. //
  10231. // Choose filter list and level (caller has filtered . and ..)
  10232. //
  10233. Replica = MoveInContext->Replica;
  10234. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  10235. pVme = Replica->pVme;
  10236. if (FileIsDir) {
  10237. //
  10238. // No dirs at the bottom level in the volume filter table.
  10239. //
  10240. LevelCheck = ConfigRecord->ReplDirLevelLimit-1;
  10241. MoveInContext->NumDirs++;
  10242. } else {
  10243. //
  10244. // Files are allowed at the bottom level.
  10245. //
  10246. LevelCheck = ConfigRecord->ReplDirLevelLimit;
  10247. MoveInContext->NumFiles++;
  10248. }
  10249. //
  10250. // If the Level Limit is exceeded then skip the file or dir.
  10251. // Skip files or dirs matching an entry in the respective exclusion list.
  10252. //
  10253. if ((DirectoryLevel >= LevelCheck)) {
  10254. MoveInContext->NumFiltered++;
  10255. return ERROR_SUCCESS;
  10256. }
  10257. ObjectName.Length = (USHORT)DirectoryRecord->FileNameLength;
  10258. ObjectName.MaximumLength = (USHORT)DirectoryRecord->FileNameLength;
  10259. ObjectName.Buffer = DirectoryRecord->FileName;
  10260. LOCK_REPLICA(Replica);
  10261. Excluded = FALSE;
  10262. if (FileIsDir) {
  10263. if (!FrsCheckNameFilter(&ObjectName, &Replica->DirNameInclFilterHead)) {
  10264. Excluded = FrsCheckNameFilter(&ObjectName, &Replica->DirNameFilterHead);
  10265. }
  10266. } else {
  10267. if (!FrsCheckNameFilter(&ObjectName, &Replica->FileNameInclFilterHead)) {
  10268. Excluded = FrsCheckNameFilter(&ObjectName, &Replica->FileNameFilterHead);
  10269. }
  10270. }
  10271. UNLOCK_REPLICA(Replica);
  10272. if (Excluded) {
  10273. MoveInContext->NumFiltered++;
  10274. return ERROR_SUCCESS;
  10275. }
  10276. //
  10277. // Open the file to get the FID for the local CO.
  10278. // Open with WRITE Access in case we need to write the Object ID.
  10279. // Relative open
  10280. //
  10281. ZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
  10282. ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
  10283. ObjectAttributes.ObjectName = &ObjectName;
  10284. ObjectAttributes.RootDirectory = DirectoryHandle;
  10285. NtStatus = NtCreateFile(
  10286. &FileHandle,
  10287. // READ_ACCESS | WRITE_ACCESS | ATTR_ACCESS,
  10288. READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS,
  10289. &ObjectAttributes,
  10290. &IoStatusBlock,
  10291. NULL, // AllocationSize
  10292. FILE_ATTRIBUTE_NORMAL,
  10293. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  10294. FILE_OPEN,
  10295. OPEN_OPTIONS,
  10296. NULL, // EA buffer
  10297. 0); // EA buffer size
  10298. //
  10299. // Error opening file or directory
  10300. //
  10301. if (!NT_SUCCESS(NtStatus)) {
  10302. DPRINT1_NT(0, "++ ERROR - Skipping %ws: NtCreateFile().", FileName, NtStatus);
  10303. //
  10304. // Could be a file attribute problem. Get the FID and try again.
  10305. //
  10306. NtStatus = NtCreateFile(&FileHandle,
  10307. READ_ATTRIB_ACCESS,
  10308. &ObjectAttributes,
  10309. &IoStatusBlock,
  10310. NULL, // AllocationSize
  10311. FILE_ATTRIBUTE_NORMAL,
  10312. SHARE_ALL,
  10313. FILE_OPEN,
  10314. OPEN_OPTIONS,
  10315. NULL, // EA buffer
  10316. 0); // EA buffer size
  10317. if (!NT_SUCCESS(NtStatus)) {
  10318. DPRINT1_NT(0, "++ ERROR - Skipping %ws: NtCreateFile()", FileName, NtStatus);
  10319. FileHandle = INVALID_HANDLE_VALUE;
  10320. WStatus = FrsSetLastNTError(NtStatus);
  10321. goto RETURN;
  10322. }
  10323. WStatus = FrsGetFileInternalInfoByHandle(FileHandle, &FileInternalInfo);
  10324. CLEANUP1_WS(0, "++ ERROR - FrsGetFileInternalInfoByHandle(%ws); ",
  10325. FileName, WStatus, RETURN);
  10326. FRS_CLOSE(FileHandle);
  10327. WStatus = FrsForceOpenId(&FileHandle,
  10328. NULL,
  10329. pVme,
  10330. &FileInternalInfo.IndexNumber.QuadPart,
  10331. FILE_ID_LENGTH,
  10332. // READ_ACCESS | WRITE_ACCESS | ATTR_ACCESS,
  10333. READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS,
  10334. ID_OPTIONS,
  10335. SHARE_ALL,
  10336. FILE_OPEN);
  10337. //
  10338. // File has been deleted; done
  10339. //
  10340. if (WIN_NOT_FOUND(WStatus)) {
  10341. DPRINT2(4, "++ %ws (Id %08x %08x) has been deleted\n",
  10342. FileName, PRINTQUAD(FileInternalInfo.IndexNumber.QuadPart));
  10343. goto RETURN;
  10344. }
  10345. if (!WIN_SUCCESS(WStatus)) {
  10346. goto RETURN;
  10347. }
  10348. }
  10349. //
  10350. // Get the file's last write time.
  10351. //
  10352. ZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
  10353. NtStatus = NtQueryInformationFile(FileHandle,
  10354. &IoStatusBlock,
  10355. &FileBasicInfo,
  10356. sizeof(FileBasicInfo),
  10357. FileBasicInformation);
  10358. if (!NT_SUCCESS(NtStatus)) {
  10359. DPRINT_NT(0, "++ NtQueryInformationFile - FileBasicInformation failed.",
  10360. NtStatus);
  10361. WStatus = FrsSetLastNTError(NtStatus);
  10362. goto RETURN;
  10363. }
  10364. //
  10365. // Poke the file by setting the last write time.
  10366. //
  10367. FileBasicInfo.LastWriteTime.QuadPart += 1;
  10368. if (!SetFileTime(FileHandle, NULL, NULL, (PFILETIME) &FileBasicInfo.LastWriteTime)) {
  10369. WStatus = GetLastError();
  10370. DPRINT1_WS(0, "++ ERROR - Unable to set last write time on %ws.", FileName, WStatus);
  10371. goto RETURN;
  10372. }
  10373. FileBasicInfo.LastWriteTime.QuadPart -= 1;
  10374. if (!SetFileTime(FileHandle, NULL, NULL, (PFILETIME) &FileBasicInfo.LastWriteTime)) {
  10375. WStatus = GetLastError();
  10376. DPRINT1_WS(0, "++ ERROR - Unable to set last write time on %ws.", FileName, WStatus);
  10377. goto RETURN;
  10378. }
  10379. FRS_CLOSE(FileHandle);
  10380. return ERROR_SUCCESS;
  10381. RETURN:
  10382. if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE) {
  10383. WStatus = ERROR_SUCCESS;
  10384. }
  10385. MoveInContext->NumSkipped++;
  10386. FRS_CLOSE(FileHandle);
  10387. return WStatus;
  10388. }
  10389. ULONG
  10390. ChgOrdMoveInDirTree(
  10391. IN PREPLICA Replica,
  10392. IN PCHANGE_ORDER_ENTRY ChangeOrder
  10393. )
  10394. {
  10395. /*++
  10396. Routine Description:
  10397. Walk the Sub dir specified in the MOVEIN Change order and produce change
  10398. orders for each child.
  10399. Arguments:
  10400. Replica -- ptr to the replica struct.
  10401. ChangeOrder -- The MoveIn Dir change order.
  10402. Return Value:
  10403. WIN32 Error Status.
  10404. --*/
  10405. #undef DEBSUB
  10406. #define DEBSUB "ChgOrdMoveInDirTree:"
  10407. MOVEIN_CONTEXT MoveInContext;
  10408. ULONG WStatus;
  10409. ULONG FStatus;
  10410. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  10411. ULONG Level;
  10412. PCHANGE_ORDER_RECORD CoCmd = &ChangeOrder->Cmd;
  10413. PVOLUME_MONITOR_ENTRY pVme = Replica->pVme;
  10414. FRS_ASSERT(CoIsDirectory(ChangeOrder));
  10415. //
  10416. // Open the subdir directory that is the root of the MoveIn.
  10417. //
  10418. WStatus = FrsForceOpenId(&FileHandle,
  10419. NULL,
  10420. pVme,
  10421. &ChangeOrder->FileReferenceNumber,
  10422. FILE_ID_LENGTH,
  10423. // READ_ACCESS,
  10424. READ_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
  10425. ID_OPTIONS,
  10426. SHARE_ALL,
  10427. FILE_OPEN);
  10428. CLEANUP1_WS(4, "++ Couldn't force open the fid for %ws;",
  10429. CoCmd->FileName, WStatus, CLEANUP);
  10430. //
  10431. // Get the sub-dir nesting level on this directory.
  10432. // Note - Can only make this call on a local CO that came from the journal.
  10433. // Otherwise get the level from the CO itself. If this dir was generated
  10434. // elsewhere (like in move in dir tree worker) it may not have gone far
  10435. // enough to make it into the filter table.
  10436. //
  10437. if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_MOVEIN_GEN)) {
  10438. FStatus = JrnlGetPathAndLevel(pVme->FilterTable,
  10439. &ChangeOrder->FileReferenceNumber,
  10440. &Level);
  10441. CLEANUP_FS(0, "JrnlGetPathAndLevel Failed.", FStatus, CLEANUP);
  10442. } else {
  10443. Level = ChangeOrder->DirNestingLevel;
  10444. }
  10445. //
  10446. // Advance to the next level
  10447. //
  10448. ZeroMemory(&MoveInContext, sizeof(MoveInContext));
  10449. MoveInContext.ParentFileID = ChangeOrder->FileReferenceNumber;
  10450. MoveInContext.Replica = Replica;
  10451. WStatus = FrsEnumerateDirectory(FileHandle,
  10452. CoCmd->FileName,
  10453. Level,
  10454. ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE,
  10455. &MoveInContext,
  10456. ChgOrdMoveInDirTreeWorker);
  10457. if (!WIN_SUCCESS(WStatus)) {
  10458. goto CLEANUP;
  10459. }
  10460. DPRINT4(4, "++ MoveIn dir done: %d dirs; %d files; %d skipped, %d filtered\n",
  10461. MoveInContext.NumDirs, MoveInContext.NumFiles,
  10462. MoveInContext.NumSkipped, MoveInContext.NumFiltered);
  10463. WStatus = ERROR_SUCCESS;
  10464. CLEANUP:
  10465. FRS_CLOSE(FileHandle);
  10466. return WStatus;
  10467. }
  10468. #if _MSC_FULL_VER >= 13008827
  10469. #pragma warning(push)
  10470. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  10471. #endif
  10472. ULONG
  10473. ChgOrdRetryThread(
  10474. PVOID FrsThreadCtxArg
  10475. )
  10476. /*++
  10477. Routine Description:
  10478. Entry point for processing inbound change orders that need to be retried.
  10479. This is a command server thread. It gets its actual command requests
  10480. from the ChgOrdRetryCS queue. Submitting a request to this queue with
  10481. FrsSubmitCommandServer() starts this thread if it is not already running.
  10482. Arguments:
  10483. FrsThreadCtxArg - FrsThread struct.
  10484. Return Value:
  10485. WIN32 Status
  10486. --*/
  10487. {
  10488. #undef DEBSUB
  10489. #define DEBSUB "ChgOrdRetryThread:"
  10490. JET_ERR jerr, jerr1;
  10491. ULONG WStatus;
  10492. ULONG FStatus;
  10493. PFRS_THREAD FrsThread = (PFRS_THREAD)FrsThreadCtxArg;
  10494. PTHREAD_CTX ThreadCtx;
  10495. PCOMMAND_PACKET Cmd;
  10496. ULONG ReplicaNumber;
  10497. TABLE_CTX TempTableCtx;
  10498. PTABLE_CTX TableCtx = &TempTableCtx;
  10499. PREPLICA Replica;
  10500. PCXTION Cxtion;
  10501. DPRINT(4, "++ IBCO ChgOrdRetryCS processor is starting.\n");
  10502. //
  10503. // Thread is pointing at the correct command server
  10504. //
  10505. FRS_ASSERT(FrsThread->Data == &ChgOrdRetryCS);
  10506. cant_exit_yet:
  10507. //
  10508. // Allocate a context for Jet to run in this thread.
  10509. //
  10510. ThreadCtx = FrsAllocType(THREAD_CONTEXT_TYPE);
  10511. //
  10512. // Setup a Jet Session returning the session ID in ThreadCtx.
  10513. //
  10514. jerr = DbsCreateJetSession(ThreadCtx);
  10515. if (JET_SUCCESS(jerr)) {
  10516. DPRINT(4,"++ JetOpenDatabase complete\n");
  10517. } else {
  10518. FStatus = DbsTranslateJetError(jerr, FALSE);
  10519. DPRINT_FS(0,"++ ERROR - OpenDatabase failed. Thread exiting:", FStatus);
  10520. WStatus = ERROR_GEN_FAILURE;
  10521. goto EXIT_THREAD;
  10522. }
  10523. TableCtx->TableType = TABLE_TYPE_INVALID;
  10524. TableCtx->Tid = JET_tableidNil;
  10525. //
  10526. // Look for command packets with the Replcia and Cxtion args to process.
  10527. //
  10528. while (Cmd = FrsGetCommandServer(&ChgOrdRetryCS)) {
  10529. Replica = CoRetryReplica(Cmd);
  10530. ReplicaNumber = Replica->ReplicaNumber;
  10531. //
  10532. // Init the table ctx and open the inbound log table for this Replica.
  10533. //
  10534. jerr = DbsOpenTable(ThreadCtx, TableCtx, ReplicaNumber, INLOGTablex, NULL);
  10535. if (!JET_SUCCESS(jerr)) {
  10536. FStatus = DbsTranslateJetError(jerr, FALSE);
  10537. DPRINT1_FS(0,"++ ERROR - INLOG open for replica %d failed:",
  10538. ReplicaNumber, FStatus);
  10539. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  10540. continue;
  10541. }
  10542. //
  10543. // Walk through the inbound log looking for change orders to reissue.
  10544. //
  10545. // Clear the retry CO count before making the scan. If any reissued
  10546. // change orders go thru retry again they bump the count to enable
  10547. // a later re-scan.
  10548. //
  10549. Replica->InLogRetryCount = 0;
  10550. jerr = DbsEnumerateTable2(ThreadCtx,
  10551. TableCtx,
  10552. ILSequenceNumberIndexx,
  10553. ChgOrdRetryWorker,
  10554. Cmd,
  10555. ChgOrdRetryWorkerPreRead);
  10556. if ((!JET_SUCCESS(jerr)) &&
  10557. (jerr != JET_errRecordNotFound) &&
  10558. (jerr != JET_errNoCurrentRecord) &&
  10559. (jerr != JET_wrnTableEmpty)) {
  10560. DPRINT_JS(0, "++ ERROR - FrsEnumerateTable for ChgOrdRetryThread :", jerr);
  10561. }
  10562. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  10563. DPRINT_JS(0,"++ ERROR - JetCloseTable on ChgOrdRetryThread failed:", jerr);
  10564. //
  10565. // If we are restarting a single connection then find
  10566. // Cxtion struct and advance the state to JOINING.
  10567. //
  10568. if (Cmd->Command == FCN_CORETRY_ONE_CXTION) {
  10569. Cxtion = CoRetryCxtion(Cmd);
  10570. LOCK_CXTION_TABLE(Replica);
  10571. //
  10572. // If it is no longer in the Connection Starting state leave it
  10573. // alone else advance to JOINING
  10574. //
  10575. if (CxtionStateIs(Cxtion, CxtionStateScanning)) {
  10576. SetCxtionState(Cxtion, CxtionStateSendJoin);
  10577. //
  10578. // Build cmd pkt with Cxtion GName
  10579. // Submit to Replica cmd server.
  10580. // Calls ReplicaJoinOne() and xlate cxtion GName to ptr
  10581. // Calls SubmitReplicaJoin()
  10582. // Builds Comm pkt for cxtion and call SndCsSubmit() to send it.
  10583. //
  10584. RcsSubmitReplicaCxtion(Replica, Cxtion, CMD_JOIN_CXTION);
  10585. }
  10586. UNLOCK_CXTION_TABLE(Replica);
  10587. }
  10588. //
  10589. // Retire the command.
  10590. //
  10591. FrsCompleteCommand(Cmd, ERROR_SUCCESS);
  10592. } // End while
  10593. DbsFreeTableCtx(TableCtx, 1);
  10594. EXIT_THREAD:
  10595. //
  10596. // No work left. Close the jet session and free the Jet ThreadCtx.
  10597. //
  10598. jerr = DbsCloseJetSession(ThreadCtx);
  10599. if (!JET_SUCCESS(jerr)) {
  10600. DPRINT_JS(0,"++ DbsCloseJetSession error:", jerr);
  10601. } else {
  10602. DPRINT(4,"++ DbsCloseJetSession complete\n");
  10603. }
  10604. ThreadCtx = FrsFreeType(ThreadCtx);
  10605. DPRINT(4, "++ Inbound change order retry thread is exiting.\n");
  10606. //
  10607. // Exit
  10608. //
  10609. FrsExitCommandServer(&ChgOrdRetryCS, FrsThread);
  10610. //
  10611. // A new command packet may have appeared on our queue while we were
  10612. // cleaning up for exiting. The command server subsystem will not
  10613. // allow us to exit until we check the command queue again because
  10614. // we may be the only thread active on the command queue at this time.
  10615. //
  10616. goto cant_exit_yet;
  10617. return ERROR_SUCCESS;
  10618. }
  10619. #if _MSC_FULL_VER >= 13008827
  10620. #pragma warning(pop)
  10621. #endif
  10622. JET_ERR
  10623. ChgOrdRetryWorker(
  10624. IN PTHREAD_CTX ThreadCtx,
  10625. IN PTABLE_CTX TableCtx,
  10626. IN PVOID Record,
  10627. IN PVOID Context
  10628. )
  10629. /*++
  10630. Routine Description:
  10631. This is a worker function passed to FrsEnumerateTable(). Each time
  10632. it is called it processes a record from the Inbound log table.
  10633. There are three cases:
  10634. 1. A normal retry where all Local COs and remote COs from all inbound
  10635. cxtions are considered (cmd is FCN_CORETRY_ALL_CXTIONS). If the record is
  10636. marked for retry (has the CO_FLAG_RETRY bit set) then resubmit the CO to try
  10637. and complete it. The necessary work could be to complete the install,
  10638. or the fetch, or generate a stage file.
  10639. 2. A single connection restart/recovery request (FCN_CORETRY_ONE_CXTION).
  10640. In this case the connection is assumed to be inactive and this is part of
  10641. the startup procedure. Scan the entire inbound log and requeue any change
  10642. order with a matching connection guid. This ensures these COs are in the
  10643. queue ahead of any new COs that arrive after we join with the inbound
  10644. partner.
  10645. Note - If a remote CO from a partner was in process when the cxtion
  10646. went away and it was a RETRY CO, that CO will not be reissued when the
  10647. FCN_CORETRY_ONE_CXTION" "is processed because the CO is still in the
  10648. active retry table. Since it is a retry CO it is already out of order
  10649. so it doesn't really matter. If the dead connection causes it to fail
  10650. it will get retried again later.
  10651. 3. Resubmit all Local COs (FCN_CORETRY_LOCAL_ONLY). This is typically
  10652. only done at Replica set startup where the local COs are restarted
  10653. regardless if they are in a retry state or not. If this is done at any other
  10654. time we could end up reissuing a CO that is still on its "First Issue"
  10655. because it is not in the ActiveInlogRetry table. This could cause a Local
  10656. CO to look like a duplicate which will probably ASSERT.
  10657. Arguments:
  10658. ThreadCtx - Needed to access Jet.
  10659. TableCtx - A ptr to an inbound log context struct.
  10660. Record - A ptr to a change order command record.
  10661. Context - A ptr to the CO Retry command we are working on.
  10662. Thread Return Value:
  10663. JET_errSuccess if enum is to continue.
  10664. JET_errInvalidObject if table may have changed and the record must be re-read.
  10665. JET_errRecordNotFound when we hit the first record without the retry flag set.
  10666. --*/
  10667. {
  10668. #undef DEBSUB
  10669. #define DEBSUB "ChgOrdRetryWorker:"
  10670. ULONG FStatus;
  10671. PCOMMAND_PACKET Cmd = (PCOMMAND_PACKET) Context;
  10672. PCHANGE_ORDER_COMMAND CoCmd = (PCHANGE_ORDER_COMMAND)Record;
  10673. PREPLICA Replica;
  10674. PVOLUME_MONITOR_ENTRY pVme;
  10675. ULONGLONG SeqNum;
  10676. PQHASH_TABLE ActiveInlogRetryTable;
  10677. PCXTION Cxtion = NULL;
  10678. GUID *SingleCxtionGuid = NULL;
  10679. ULONG CoeFlags = 0;
  10680. BOOL RemoteCo, RetryCo;
  10681. CHAR GuidStr[GUID_CHAR_LEN];
  10682. JET_ERR jerr;
  10683. //
  10684. // Abort retry worker if FRS is shutting down.
  10685. //
  10686. if (FrsIsShuttingDown) {
  10687. return JET_errTermInProgress;
  10688. }
  10689. Replica = CoRetryReplica(Cmd);
  10690. pVme = Replica->pVme;
  10691. ActiveInlogRetryTable = Replica->ActiveInlogRetryTable;
  10692. GuidToStr(&CoCmd->ChangeOrderGuid, GuidStr);
  10693. DBS_DISPLAY_RECORD_SEV(4, TableCtx, TRUE);
  10694. //
  10695. // Get the lock on the Active Retry table.
  10696. //
  10697. QHashAcquireLock(ActiveInlogRetryTable);
  10698. //
  10699. // Check if an inlog record has been deleted (seq num changed).
  10700. // If so then return to re-read the record incase this was the one deleted.
  10701. //
  10702. if (Replica->AIRSequenceNum != Replica->AIRSequenceNumSample) {
  10703. DPRINT2(4, "++ Seq num changed: Sample %08x, Counter %08x -- reread record.\n",
  10704. Replica->AIRSequenceNumSample, Replica->AIRSequenceNum);
  10705. QHashReleaseLock(ActiveInlogRetryTable);
  10706. return JET_errInvalidObject;
  10707. }
  10708. //
  10709. // Check if it's in Active Retry table. If so then it has not yet
  10710. // finished from a prior retry so we skip it this time.
  10711. //
  10712. SeqNum = (ULONGLONG) CoCmd->SequenceNumber;
  10713. if (QHashLookupLock(ActiveInlogRetryTable, &SeqNum) != NULL) {
  10714. DPRINT1(4, "++ ActiveInlogRetryTable hit on seq num %08x %08x, skipping reissue\n",
  10715. PRINTQUAD(SeqNum));
  10716. DPRINT3(4, "++ CO Retry skip, vsn %08x %08x, CoGuid: %s for %ws -- Active\n",
  10717. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10718. QHashReleaseLock(ActiveInlogRetryTable);
  10719. return JET_errSuccess;
  10720. }
  10721. RemoteCo = !BooleanFlagOn(CoCmd->Flags, CO_FLAG_LOCALCO);
  10722. RetryCo = BooleanFlagOn(CoCmd->Flags, CO_FLAG_RETRY);
  10723. //
  10724. // Initialize the Ghost Cxtion. This cxtion is assigned to orphan remote change
  10725. // orders in the inbound log whose cxtion is deleted from the DS but who have already
  10726. // past the fetching state and do not need the cxtion to complete processing. No
  10727. // authentication checks are made for this dummy cxtion.
  10728. //
  10729. if (FrsGhostCxtion == NULL) {
  10730. FrsGhostCxtion = FrsAllocType(CXTION_TYPE);
  10731. FrsGhostCxtion->Name = FrsBuildGName(FrsDupGuid(&FrsGuidGhostCxtion),
  10732. FrsWcsDup(L"<Ghost Cxtion>"));
  10733. FrsGhostCxtion->Partner = FrsBuildGName(FrsDupGuid(&FrsGuidGhostCxtion),
  10734. FrsWcsDup(L"<Ghost Cxtion>"));
  10735. FrsGhostCxtion->PartSrvName = FrsWcsDup(L"<Ghost Cxtion>");
  10736. FrsGhostCxtion->PartnerPrincName = FrsWcsDup(L"<Ghost Cxtion>");
  10737. FrsGhostCxtion->PartnerDnsName = FrsWcsDup(L"<Ghost Cxtion>");
  10738. FrsGhostCxtion->PartnerSid = FrsWcsDup(L"<Ghost Cxtion>");
  10739. FrsGhostCxtion->PartnerAuthLevel = CXTION_AUTH_NONE;
  10740. FrsGhostCxtion->Inbound = TRUE;
  10741. //
  10742. // Start the ghost connection out as Joined and give it a JOIN guid.
  10743. //
  10744. DPRINT1(0, "***** JOINED "FORMAT_CXTION_PATH2"\n",
  10745. PRINT_CXTION_PATH2(Replica, FrsGhostCxtion));
  10746. SetCxtionState(FrsGhostCxtion, CxtionStateJoined);
  10747. COPY_GUID(&FrsGhostCxtion->JoinGuid, &FrsGhostJoinGuid);
  10748. SetCxtionFlag(FrsGhostCxtion, CXTION_FLAGS_JOIN_GUID_VALID |
  10749. CXTION_FLAGS_UNJOIN_GUID_VALID);
  10750. }
  10751. switch (Cmd->Command) {
  10752. case FCN_CORETRY_LOCAL_ONLY:
  10753. //
  10754. // Skip the remote COs.
  10755. //
  10756. if (RemoteCo) {
  10757. DPRINT3(3, "++ CO Retry skip, vsn %08x %08x, CoGuid: %s for %ws -- Local only\n",
  10758. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10759. QHashReleaseLock(ActiveInlogRetryTable);
  10760. goto SKIP_RECORD;
  10761. }
  10762. CoeFlags = COE_FLAG_RECOVERY_CO;
  10763. COPY_GUID(&CoCmd->CxtionGuid, &Replica->JrnlCxtionGuid);
  10764. CHANGE_ORDER_COMMAND_TRACE(3, CoCmd, "Co FCN_CORETRY_LOCAL_ONLY");
  10765. break;
  10766. case FCN_CORETRY_ONE_CXTION:
  10767. //
  10768. // Doing single connection restart. CxtionGuid must match.
  10769. // Don't bother checking cxtion state since we were explicitly told
  10770. // to retry these COs. Some of these COs may not have the retry
  10771. // flag set since they could be first issue COs when the inbound partner
  10772. // connection died or the system crashed.
  10773. //
  10774. Cxtion = CoRetryCxtion(Cmd);
  10775. SingleCxtionGuid = Cxtion->Name->Guid;
  10776. FRS_ASSERT(Cxtion);
  10777. if ((SingleCxtionGuid != NULL) &&
  10778. !GUIDS_EQUAL(&CoCmd->CxtionGuid, SingleCxtionGuid)) {
  10779. DPRINT3(3, "++ CO Retry skip, vsn %08x %08x, CoGuid: %s for %ws -- Wrong Cxtion\n",
  10780. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10781. QHashReleaseLock(ActiveInlogRetryTable);
  10782. goto SKIP_RECORD;
  10783. }
  10784. //
  10785. // Mark this as a recovery change order. This state moderates error
  10786. // checking behavior. E.G. CO could already be in OutLog which is not
  10787. // an error if this is a recovery / restart CO.
  10788. //
  10789. CoeFlags = COE_FLAG_RECOVERY_CO;
  10790. CHANGE_ORDER_COMMAND_TRACE(3, CoCmd, "Co FCN_CORETRY_ONE_CXTION");
  10791. break;
  10792. case FCN_CORETRY_ALL_CXTIONS:
  10793. //
  10794. // Skip the COs that don't have retry set. We used to stop the enum
  10795. // when the first CO without retry set was hit. This does not work
  10796. // because dup COs are marked as retry and they can get traped behind
  10797. // new COs. When an inlog scan is triggered it clears the scan flag
  10798. // and when the new CO is hit it would stop the scan. Now the
  10799. // condition is the scan is done, the flag is clear but there are retry
  10800. // COs still in the inlog that need to be processed. Their inbound
  10801. // partner is still waiting for the ACK so it can delete the staging
  10802. // file (and clear any Ack Vector wrap condition).
  10803. //
  10804. if (!RetryCo) {
  10805. DPRINT3(3, "++ CO Retry skip, vsn %08x %08x, CoGuid: %s for %ws -- Not retry\n",
  10806. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10807. QHashReleaseLock(ActiveInlogRetryTable);
  10808. goto SKIP_RECORD;
  10809. }
  10810. //
  10811. // If the inbound connection is not JOINED then there is no point in processing it.
  10812. //
  10813. // NOTE: If the connection were to go live during a scan then COs could be
  10814. // retried out of order. This should not happen because we are called
  10815. // before the connection join begins and the connection won't proceed
  10816. // until we finish the scan and change the connection state. If later
  10817. // a Retry All is in process when a connection goes to the JOINED state
  10818. // all its COs will be in the Active retry table so they will not be
  10819. // requeued.
  10820. //
  10821. // Perf: Since we don't need the joined connections once the stage file
  10822. // is fetched we could process those remote COs that are in install
  10823. // retry or rename retry state but for now keep them in order.
  10824. //
  10825. if (!RemoteCo) {
  10826. //
  10827. // Update the Local Co's connection guid to match current value
  10828. // in case this replica set was stoped and then restarted.
  10829. //
  10830. COPY_GUID(&CoCmd->CxtionGuid, &Replica->JrnlCxtionGuid);
  10831. }
  10832. //
  10833. // Find the inbound cxtion for this CO
  10834. //
  10835. LOCK_CXTION_TABLE(Replica);
  10836. Cxtion = GTabLookupNoLock(Replica->Cxtions, &CoCmd->CxtionGuid, NULL);
  10837. if (Cxtion == NULL) {
  10838. if (CoCmd->State > IBCO_FETCH_RETRY) {
  10839. DPRINT3(2, "++ CO Retry submit, vsn %08x %08x, CoGuid: %s for %ws -- No Cxtion - Using Ghost Cxtion.\n",
  10840. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10841. FRS_ASSERT(FrsGhostCxtion != NULL);
  10842. Cxtion = FrsGhostCxtion;
  10843. } else {
  10844. DPRINT3(2, "++ CO Retry delete, vsn %08x %08x, CoGuid: %s for %ws -- No Cxtion - Deleting inlog record.\n",
  10845. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10846. jerr = DbsDeleteTableRecord(TableCtx);
  10847. DPRINT1_JS(0, "ERROR - DbsDeleteRecord on %ws :", Replica->ReplicaName->Name, jerr);
  10848. UNLOCK_CXTION_TABLE(Replica);
  10849. QHashReleaseLock(ActiveInlogRetryTable);
  10850. goto SKIP_RECORD;
  10851. }
  10852. }
  10853. FRS_ASSERT(Cxtion->Inbound);
  10854. //
  10855. // We are in the middle of a scan all so we should not see any
  10856. // remote CO with a connection in the JOINING state. All those
  10857. // COs should have been requeued when the cxtion was in the
  10858. // STARTING State and be in the ActiveInlogRetryTable Table where they
  10859. // are filtered out above.
  10860. //
  10861. // WELL THIS ISN'T QUITE TRUE.
  10862. // There appears to be a case where a previously submitted retry CO
  10863. // for a cxtion is flushed because of an invalid join guid but the
  10864. // cxtion is left in WaitJoin state so when the next FCN_CORETRY_ALL_CXTIONS
  10865. // occurs it is not in the ActiveInlogRetryTable and we trigger the
  10866. // assert. This might be happening when the join attempt times out
  10867. // and the cxtion state isn't changed to unjoined. Bug 319812 hit this.
  10868. //
  10869. // It looks like there is a potential for COs to get resubmitted out of
  10870. // order when a previous join attempt fails and some COs were delayed
  10871. // behind some other CO that blocked the queue. In this case we could
  10872. // be in the middle of flushing the old series of COs when the next
  10873. // rejoin starts. Since some of these could still be in the
  10874. // ActiveInlogRetryTable they won't get resubmitted. Not sure if this
  10875. // is a real problem though.
  10876. //
  10877. //FRS_ASSERT(!CxtionStateIs(Cxtion, CxtionStateSendJoin) &&
  10878. // !CxtionStateIs(Cxtion, CxtionStateWaitJoin));
  10879. //
  10880. // If the connection isn't joined then skip the CO.
  10881. // COs for connections in the starting state are processed
  10882. // by a single connection restart request.
  10883. //
  10884. if (!CxtionStateIs(Cxtion, CxtionStateJoined)) {
  10885. if (CoCmd->State > IBCO_FETCH_RETRY) {
  10886. DPRINT3(2, "++ CO Retry submit, vsn %08x %08x, CoGuid: %s for %ws -- Cxtion not joined - Using Ghost Cxtion.\n",
  10887. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10888. FRS_ASSERT(FrsGhostCxtion != NULL);
  10889. Cxtion = FrsGhostCxtion;
  10890. } else {
  10891. UNLOCK_CXTION_TABLE(Replica);
  10892. QHashReleaseLock(ActiveInlogRetryTable);
  10893. DPRINT3(2, "++ CO Retry skip, vsn %08x %08x, CoGuid: %s for %ws -- Cxtion not joined\n",
  10894. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10895. goto SKIP_RECORD;
  10896. }
  10897. }
  10898. UNLOCK_CXTION_TABLE(Replica);
  10899. CHANGE_ORDER_COMMAND_TRACE(3, CoCmd, "Co FCN_CORETRY_ALL_CXTIONS");
  10900. break;
  10901. default:
  10902. DPRINT1(0, "ChgOrdRetryWorker: unknown command 0x%x\n", Cmd->Command);
  10903. DPRINT3(0, "++ CO Retry skip, vsn %08x %08x, CoGuid: %s for %ws -- bad cmd\n",
  10904. PRINTQUAD(CoCmd->FrsVsn), GuidStr, CoCmd->FileName);
  10905. FRS_ASSERT(!"ChgOrdRetryWorker: bad cmd");
  10906. QHashReleaseLock(ActiveInlogRetryTable);
  10907. goto SKIP_RECORD;
  10908. }
  10909. //
  10910. // All COs go into the inlog retry table to lock against reissue by the
  10911. // retry thread. This is because of the change to the retry thread where
  10912. // we no longer stop the inlog enum when we hit the first CO that is not
  10913. // marked as retry. See comment above at FCN_CORETRY_ALL_CXTIONS.
  10914. //
  10915. if (!RetryCo) {
  10916. FRS_ASSERT(BooleanFlagOn(CoeFlags, COE_FLAG_RECOVERY_CO));
  10917. }
  10918. QHashInsertLock(ActiveInlogRetryTable, &SeqNum, NULL, 0);
  10919. QHashReleaseLock(ActiveInlogRetryTable);
  10920. CHANGE_ORDER_COMMAND_TRACE(3, CoCmd, "CO Retry Submit");
  10921. DPRINT2(4, "++ ++ CO retry submit for Index %d, State: %s\n",
  10922. CoCmd->SequenceNumber, PRINT_COCMD_STATE(CoCmd));
  10923. //
  10924. // Re-insert the change order into the process queue.
  10925. //
  10926. FStatus = ChgOrdInsertProcessQueue(Replica, CoCmd, CoeFlags, Cxtion);
  10927. return JET_errSuccess;
  10928. SKIP_RECORD:
  10929. //
  10930. // The record is being skipped but bump the count so we know to come
  10931. // back and try again later.
  10932. //
  10933. InterlockedIncrement(&Replica->InLogRetryCount);
  10934. return JET_errSuccess;
  10935. }
  10936. JET_ERR
  10937. ChgOrdRetryWorkerPreRead(
  10938. IN PTHREAD_CTX ThreadCtx,
  10939. IN PTABLE_CTX TableCtx,
  10940. IN PVOID Context
  10941. )
  10942. /*++
  10943. Routine Description:
  10944. This is a worker function passed to DbsEnumerateTable2().
  10945. It is called before each DB record read operation.
  10946. It is used to change the seek location in the table or to setup
  10947. table access synchronization.
  10948. Arguments:
  10949. ThreadCtx - Needed to access Jet.
  10950. TableCtx - A ptr to an inbound log context struct.
  10951. Context - A ptr to the CO Retry command we are working on.
  10952. Thread Return Value:
  10953. JET_errSuccess if enum is to continue.
  10954. --*/
  10955. {
  10956. #undef DEBSUB
  10957. #define DEBSUB "ChgOrdRetryWorkerPreRead:"
  10958. PCOMMAND_PACKET Cmd = (PCOMMAND_PACKET) Context;
  10959. PREPLICA Replica = CoRetryReplica(Cmd);
  10960. //
  10961. // Sample the Active Inlog retry sequence number. If it changes after
  10962. // the DB read then the record may have been deleted from the table and
  10963. // the read must be tried again.
  10964. //
  10965. Replica->AIRSequenceNumSample = Replica->AIRSequenceNum;
  10966. return JET_errSuccess;
  10967. }
  10968. DWORD
  10969. ChgOrdSkipBasicInfoChange(
  10970. IN PCHANGE_ORDER_ENTRY Coe,
  10971. IN PBOOL SkipCo
  10972. )
  10973. /*++
  10974. Routine Description:
  10975. If the changes to the file were unimportant, skip the change order.
  10976. An example of an unimportant change is resetting the archive bit.
  10977. Arguments:
  10978. Coe
  10979. SkipCo
  10980. Thread Return Value:
  10981. WIN32 STATUS and TRUE if the caller should skip the change order,
  10982. otherwise FALSE.
  10983. --*/
  10984. {
  10985. #undef DEBSUB
  10986. #define DEBSUB "ChgOrdSkipBasicInfoChange:"
  10987. ULONG CocAttrs;
  10988. ULONG CoeAttrs;
  10989. ULONG CocContentCmd;
  10990. DWORD WStatus;
  10991. HANDLE Handle;
  10992. PCHANGE_ORDER_COMMAND Coc = &Coe->Cmd;
  10993. FILE_NETWORK_OPEN_INFORMATION FileInfo;
  10994. //
  10995. // Don't skip it, yet
  10996. //
  10997. *SkipCo = FALSE;
  10998. //
  10999. // Remote Co can't be skipped
  11000. //
  11001. if (!CO_FLAG_ON(Coe, CO_FLAG_LOCALCO)) {
  11002. DPRINT1(4, "++ Don't skip %ws; not local\n", Coc->FileName);
  11003. return ERROR_SUCCESS;
  11004. }
  11005. //
  11006. // Location commands can't be skipped
  11007. //
  11008. if (CO_FLAG_ON(Coe, CO_FLAG_LOCATION_CMD)) {
  11009. DPRINT1(4, "++ Don't skip %ws; location cmd\n", Coc->FileName);
  11010. return ERROR_SUCCESS;
  11011. }
  11012. //
  11013. // No location command *AND* no content changes! Huh?
  11014. //
  11015. if (!CO_FLAG_ON(Coe, CO_FLAG_CONTENT_CMD)) {
  11016. DPRINT1(4, "++ Don't skip %ws; no content cmd\n", Coc->FileName);
  11017. return ERROR_SUCCESS;
  11018. }
  11019. //
  11020. // Something other than just a basic info change
  11021. //
  11022. CocContentCmd = (Coc->ContentCmd & CO_CONTENT_MASK) &
  11023. ~USN_REASON_BASIC_INFO_CHANGE;
  11024. if (CocContentCmd) {
  11025. DPRINT2(4, "++ Don't skip %ws; not just basic info change\n",
  11026. Coc->FileName, CocContentCmd);
  11027. return ERROR_SUCCESS;
  11028. }
  11029. //
  11030. // Only ARCHIVE changes can be skipped
  11031. //
  11032. CocAttrs = Coc->FileAttributes &
  11033. ~(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL);
  11034. CoeAttrs = Coe->FileAttributes &
  11035. ~(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL);
  11036. if (CocAttrs != CoeAttrs) {
  11037. DPRINT3(4, "++ Don't skip %ws; not just archive (%08x != %08x)\n",
  11038. Coc->FileName, CocAttrs, CoeAttrs);
  11039. return ERROR_SUCCESS;
  11040. }
  11041. //
  11042. // Check create and write times
  11043. // If can't access the file. The caller should retry later (if possible)
  11044. //
  11045. WStatus = FrsOpenSourceFileById(&Handle,
  11046. &FileInfo,
  11047. NULL,
  11048. Coe->NewReplica->pVme->VolumeHandle,
  11049. &Coe->FileReferenceNumber,
  11050. FILE_ID_LENGTH,
  11051. // READ_ACCESS,
  11052. READ_ATTRIB_ACCESS,
  11053. ID_OPTIONS,
  11054. SHARE_ALL,
  11055. FILE_OPEN);
  11056. CLEANUP1_WS(4, "++ Cannot check basic info changes for %ws;",
  11057. Coc->FileName, WStatus, RETURN);
  11058. FRS_CLOSE(Handle);
  11059. //
  11060. // It is possible for the file attributes to have changed between the
  11061. // time the USN record was processed and now. Record the current
  11062. // attributes in the change order. This is especially true for dir
  11063. // creates since while the dir was open other changes may have occurred.
  11064. //
  11065. if (Coc->FileAttributes != FileInfo.FileAttributes) {
  11066. CHANGE_ORDER_TRACEX(3, Coe, "New File Attr= ", FileInfo.FileAttributes);
  11067. Coc->FileAttributes = FileInfo.FileAttributes;
  11068. WStatus = ERROR_SUCCESS;
  11069. if (Coc->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
  11070. WStatus = FrsCheckReparse(Coc->FileName,
  11071. (PULONG)&Coe->FileReferenceNumber,
  11072. FILE_ID_LENGTH,
  11073. Coe->NewReplica->pVme->VolumeHandle);
  11074. if (!WIN_SUCCESS(WStatus)) {
  11075. return WStatus;
  11076. }
  11077. }
  11078. }
  11079. if (FileInfo.CreationTime.QuadPart != Coe->FileCreateTime.QuadPart) {
  11080. DPRINT3(4, "++ Don't skip %ws; create time %08x %08x %08x %08x\n",
  11081. Coc->FileName, PRINTQUAD(FileInfo.CreationTime.QuadPart),
  11082. PRINTQUAD(Coe->FileCreateTime.QuadPart));
  11083. return ERROR_SUCCESS;
  11084. }
  11085. if (FileInfo.LastWriteTime.QuadPart != Coe->FileWriteTime.QuadPart) {
  11086. DPRINT3(4, "++ Don't skip %ws; write time %08x %08x %08x %08x\n",
  11087. Coc->FileName, PRINTQUAD(FileInfo.LastWriteTime.QuadPart),
  11088. PRINTQUAD(Coe->FileWriteTime.QuadPart));
  11089. return ERROR_SUCCESS;
  11090. }
  11091. //
  11092. // SKIP IT
  11093. //
  11094. DPRINT1(0, "++ Skip local co for %ws\n", Coc->FileName);
  11095. *SkipCo = TRUE;
  11096. RETURN:
  11097. return WStatus;
  11098. }
  11099. DWORD
  11100. ChgOrdHammerObjectId(
  11101. IN PWCHAR Name,
  11102. IN PVOID Id,
  11103. IN DWORD IdLen,
  11104. IN PVOLUME_MONITOR_ENTRY pVme,
  11105. IN BOOL CallerSupplied,
  11106. OUT USN *Usn,
  11107. IN OUT PFILE_OBJECTID_BUFFER FileObjID,
  11108. IN OUT BOOL *ExistingOid
  11109. )
  11110. /*++
  11111. Routine Description:
  11112. Hammer an object id onto the file by fid. The open-for-write-access
  11113. is retried several times before giving up. The file's attributes
  11114. may be temporarily reset.
  11115. Arguments:
  11116. Name - File name for error messages
  11117. Id - Fid or Oid
  11118. pVme - volume monitor entry
  11119. CallerSupplied - TRUE if caller is supplying object id
  11120. Usn - Address for dampened usn
  11121. FileObjID - New object id if CallerSupplied is TRUE.
  11122. Assigned object id if returning ERROR_SUCCESS
  11123. ExistingOid -- INPUT: TRUE means use existing File OID if found.
  11124. RETURN: TRUE means an existing File OID was used.
  11125. Thread Return Value:
  11126. Win32 Status
  11127. --*/
  11128. {
  11129. #undef DEBSUB
  11130. #define DEBSUB "ChgOrdHammerObjectId:"
  11131. DWORD WStatus;
  11132. ULONG RetrySetObjectId;
  11133. HANDLE Handle;
  11134. BOOL OidMatch = FALSE;
  11135. DPRINT3(5, "++ Attempting to hammer object id for %ws Id 0x%08x 0x%08x (%d bytes)\n",
  11136. Name, PRINTQUAD((*((PULONGLONG)Id))), IdLen);
  11137. //
  11138. // For proper cleanup in the event of failure
  11139. //
  11140. Handle = INVALID_HANDLE_VALUE;
  11141. //
  11142. // Attempt to open the file for write access
  11143. //
  11144. // The loop is repeated 10 times with a .1 second sleep between cycles
  11145. //
  11146. for (RetrySetObjectId = 0;
  11147. RetrySetObjectId <= MAX_RETRY_SET_OBJECT_ID;
  11148. ++RetrySetObjectId, Sleep(100)) {
  11149. //
  11150. // FIRST see if the object id is already on the file
  11151. //
  11152. WStatus = FrsOpenSourceFileById(&Handle,
  11153. NULL,
  11154. NULL,
  11155. pVme->VolumeHandle,
  11156. Id,
  11157. IdLen,
  11158. // READ_ACCESS,
  11159. READ_ATTRIB_ACCESS,
  11160. ID_OPTIONS,
  11161. SHARE_ALL,
  11162. FILE_OPEN);
  11163. //
  11164. // File has been deleted; done
  11165. //
  11166. if (WIN_NOT_FOUND(WStatus)) {
  11167. DPRINT2(4, "++ %ws (Id %08x %08x) has been deleted\n",
  11168. Name, PRINTQUAD(*((PULONGLONG)Id)));
  11169. return WStatus;
  11170. }
  11171. if (!WIN_SUCCESS(WStatus)) {
  11172. //
  11173. // We are having problems...
  11174. //
  11175. DPRINT3_WS(4, "++ Problems opening %ws (Id %08x %08x) for read hammer (loop %d);",
  11176. Name, PRINTQUAD(*((PULONGLONG)Id)), RetrySetObjectId, WStatus);
  11177. continue;
  11178. }
  11179. if (CallerSupplied) {
  11180. WStatus = FrsCheckObjectId(Name,
  11181. Handle,
  11182. (GUID *)&FileObjID->ObjectId[0]);
  11183. OidMatch = WIN_SUCCESS(WStatus);
  11184. } else {
  11185. WStatus = FrsGetObjectId(Handle, FileObjID);
  11186. }
  11187. if (WIN_SUCCESS(WStatus)) {
  11188. WStatus = FrsReadFileUsnData(Handle, Usn);
  11189. }
  11190. FRS_CLOSE(Handle);
  11191. //
  11192. // If an object id is already on the file and we are keeping them
  11193. // then we're done.
  11194. //
  11195. if (OidMatch || (ExistingOid && WIN_SUCCESS(WStatus))) {
  11196. DPRINT2(4, "++ Using existing oid for %ws (Id %08x %08x)\n",
  11197. Name, PRINTQUAD((*((PULONGLONG)Id))) );
  11198. *ExistingOid = TRUE;
  11199. return WStatus;
  11200. } else
  11201. if (!CallerSupplied && !ExistingOid) {
  11202. //
  11203. // Set up to slam a new OID on the file.
  11204. //
  11205. CallerSupplied = TRUE;
  11206. ZeroMemory(FileObjID, sizeof(FILE_OBJECTID_BUFFER));
  11207. FrsUuidCreate((GUID *)FileObjID->ObjectId);
  11208. }
  11209. //
  11210. // HAMMER THE OBJECT ID
  11211. //
  11212. *ExistingOid = FALSE;
  11213. //
  11214. // Open the file for write access
  11215. //
  11216. // overlap disables (FILE_SYNCHRONOUS_IO_NONALERT) during
  11217. // the following open. Hence I think this causes IO_PENDING
  11218. // to be returned by GetOrSetObjectId() below.
  11219. // I am disabling oplocks for now.
  11220. //
  11221. WStatus = FrsForceOpenId(&Handle,
  11222. NULL, // use oplock when safe
  11223. pVme,
  11224. Id,
  11225. IdLen,
  11226. // READ_ACCESS | WRITE_ACCESS | ATTR_ACCESS,
  11227. // STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | ACCESS_SYSTEM_SECURITY | SYNCHRONIZE,
  11228. READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS,
  11229. ID_OPTIONS,
  11230. SHARE_ALL,
  11231. FILE_OPEN);
  11232. //
  11233. // File has been opened for write access; hammer the object id
  11234. //
  11235. if (WIN_SUCCESS(WStatus)) {
  11236. break;
  11237. }
  11238. //
  11239. // File has been deleted; done
  11240. //
  11241. if (WIN_NOT_FOUND(WStatus)) {
  11242. CLEANUP2_WS(4, "++ %ws (Id %08x %08x) has been deleted :",
  11243. Name, PRINTQUAD(*((PULONGLONG)Id)), WStatus, RETURN);
  11244. }
  11245. //
  11246. // We are having problems...
  11247. //
  11248. DPRINT3_WS(4, "++ Problems opening %ws (Fid %08x %08x) for hammer (loop %d);",
  11249. Name, PRINTQUAD(*((PULONGLONG)Id)), RetrySetObjectId, WStatus);
  11250. }
  11251. //
  11252. // Couldn't open the file
  11253. //
  11254. CLEANUP2_WS(0, "++ ERROR - Giving up on %ws (Id %08x %08x):",
  11255. Name, PRINTQUAD(*((PULONGLONG)Id)), WStatus, RETURN);
  11256. //
  11257. // Handles can be marked so that any usn records resulting from
  11258. // operations on the handle will have the same "mark". In this
  11259. // case, the mark is a bit in the SourceInfo field of the usn
  11260. // record. The mark tells NtFrs to ignore the usn record during
  11261. // recovery because this was a NtFrs generated change.
  11262. //
  11263. WStatus = FrsMarkHandle(pVme->VolumeHandle, Handle);
  11264. if (!WIN_SUCCESS(WStatus)) {
  11265. DPRINT2_WS(0, "++ WARN - FrsMarkHandle(%ws (Id %08x %08x));",
  11266. Name, PRINTQUAD(*((PULONGLONG)Id)), WStatus);
  11267. WStatus = ERROR_SUCCESS;
  11268. }
  11269. //
  11270. // Get or set the object ID.
  11271. //
  11272. WStatus = FrsGetOrSetFileObjectId(Handle, Name, CallerSupplied, FileObjID);
  11273. #if 0
  11274. //
  11275. // WARN - Even though we marked the handle above we are not seeing the source
  11276. // info data in the USN journal. The following may be affecting it.
  11277. // SP1:
  11278. // In any case the following does not work because the Journal thread
  11279. // can process the close record before this thread is able to update the
  11280. // Write Filter. The net effect is that we could end up processing
  11281. // an install as a local CO update and re-replicate the file.
  11282. //
  11283. FrsCloseWithUsnDampening(Name, &Handle, pVme->FrsWriteFilter, Usn);
  11284. #endif
  11285. // Not the USN of the close record but.. whatever.
  11286. if (Usn != NULL) {
  11287. FrsReadFileUsnData(Handle, Usn);
  11288. }
  11289. FRS_CLOSE(Handle);
  11290. //
  11291. // Couldn't set object id
  11292. //
  11293. if (!WIN_SUCCESS(WStatus)) {
  11294. DPRINT2_WS(0, "++ ERROR - Cannot hammer object id for %ws (Id %08x %08x) :",
  11295. Name, PRINTQUAD(*((PULONGLONG)Id)), WStatus);
  11296. //
  11297. // Just to make sure the error code doesn't imply a deleted file
  11298. // BUT don't remap ERROR_DUP_NAME because the callers of this
  11299. // function may key off this error code and retry this operation
  11300. // after stealing the object id from another file.
  11301. //
  11302. // We do not want to mask the error if it is a retriable error.
  11303. // E.g. A DISK_FULL condition will cause the above FrsGetOrSetFileObjectId
  11304. // call to fail. We do not want StageCsCreateStage to sbort this
  11305. // local CO on that error. Masking a retriable error will cause
  11306. // StageCsCreateStage to abort such COs. (Bug 164114)
  11307. //
  11308. if ((WStatus != ERROR_DUP_NAME) && !WIN_RETRY_STAGE(WStatus)) {
  11309. WIN_SET_FAIL(WStatus);
  11310. }
  11311. }
  11312. RETURN:
  11313. return WStatus;
  11314. }
  11315. DWORD
  11316. ChgOrdStealObjectId(
  11317. IN PWCHAR Name,
  11318. IN PVOID Fid,
  11319. IN PVOLUME_MONITOR_ENTRY pVme,
  11320. OUT USN *Usn,
  11321. IN OUT PFILE_OBJECTID_BUFFER FileObjID
  11322. )
  11323. /*++
  11324. Routine Description:
  11325. Hammer an object id onto the file by fid. The open-for-write-access
  11326. is retried several times before giving up. The file's attributes
  11327. may be temporarily reset. If some other file is using our object
  11328. id, assign a new object id to the other file.
  11329. Arguments:
  11330. Name - File name for error messages
  11331. Id - Fid or Oid
  11332. IdLen - FILE_ID_LENGTH or OBJECT_ID_LENGTH
  11333. pVme - volume monitor entry
  11334. Usn - Address for dampened usn
  11335. FileObjID - New object id
  11336. Assigned object id if returning ERROR_SUCCESS
  11337. Thread Return Value:
  11338. Win32 Status
  11339. --*/
  11340. {
  11341. #undef DEBSUB
  11342. #define DEBSUB "ChgOrdStealObjectId:"
  11343. DWORD WStatus;
  11344. FILE_OBJECTID_BUFFER NewFileObjID;
  11345. BOOL ExistingOid;
  11346. RETRY_HAMMER:
  11347. //
  11348. // Hammer the object id to the desired value
  11349. //
  11350. DPRINT2(5, "++ Hammering object id for %ws (Fid %08x %08x)\n",
  11351. Name, PRINTQUAD((*((PULONGLONG)Fid))));
  11352. ExistingOid = FALSE;
  11353. WStatus = ChgOrdHammerObjectId(Name, //Name,
  11354. Fid, //Id,
  11355. FILE_ID_LENGTH, //IdLen,
  11356. pVme, //pVme,
  11357. TRUE, //CallerSupplied
  11358. Usn, //*Usn,
  11359. FileObjID, //FileObjID,
  11360. &ExistingOid); //*ExistingOid
  11361. //
  11362. // Object id in use; replace the object id on whatever file is claiming it
  11363. //
  11364. if (WStatus == ERROR_DUP_NAME) {
  11365. DPRINT2(4, "++ Stealing object id for %ws (Fid %08x %08x)\n",
  11366. Name, PRINTQUAD((*((PULONGLONG)Fid))));
  11367. ZeroMemory(&NewFileObjID, sizeof(NewFileObjID));
  11368. FrsUuidCreate((GUID *)(&NewFileObjID.ObjectId[0]));
  11369. ExistingOid = FALSE;
  11370. WStatus = ChgOrdHammerObjectId(Name, //Name,
  11371. &FileObjID->ObjectId[0], //Id,
  11372. OBJECT_ID_LENGTH, //IdLen,
  11373. pVme, //pVme,
  11374. TRUE, //CallerSupplied
  11375. Usn, //*Usn,
  11376. &NewFileObjID, //FileObjID,
  11377. &ExistingOid); //*ExistingOid
  11378. if (WIN_SUCCESS(WStatus)) {
  11379. DPRINT2(4, "++ Success Stealing object id for %ws (Fid %08x %08x)\n",
  11380. Name, PRINTQUAD((*((PULONGLONG)Fid))));
  11381. goto RETRY_HAMMER;
  11382. } else {
  11383. DPRINT2_WS(0, "++ ERROR - Could not steal object id for %ws (Fid %08x %08x);",
  11384. Name, PRINTQUAD((*((PULONGLONG)Fid))), WStatus);
  11385. }
  11386. } else {
  11387. DPRINT2_WS(4, "++ Hammer(%ws, Fid %08x %08x) Failed:",
  11388. Name, PRINTQUAD((*((PULONGLONG)Fid))), WStatus);
  11389. }
  11390. return WStatus;
  11391. }
  11392. ULONG
  11393. ChgOrdAcceptInitialize(
  11394. VOID
  11395. )
  11396. /*++
  11397. Routine Description:
  11398. Initialize the send command server subsystem.
  11399. Arguments:
  11400. None.
  11401. Return Value:
  11402. Frs Status
  11403. --*/
  11404. {
  11405. #undef DEBSUB
  11406. #define DEBSUB "ChgOrdAcceptInitialize:"
  11407. //
  11408. // Create the File System monitor thread. It inits its process queue
  11409. // and then waits for a packet. First packet should be to init.
  11410. //
  11411. if (!ThSupCreateThread(L"COAccept", NULL, ChgOrdAccept, ThSupExitThreadNOP)) {
  11412. DPRINT(0, "++ ERROR - Could not create ChgOrdAccept thread\n");
  11413. return FrsErrorResource;
  11414. }
  11415. return FrsErrorSuccess;
  11416. }
  11417. VOID
  11418. ChgOrdAcceptShutdown(
  11419. VOID
  11420. )
  11421. /*++
  11422. Routine Description:
  11423. Run down the change order list.
  11424. queue a shutdown change order to the process queue for this volume.
  11425. Arguments:
  11426. None.
  11427. Return Value:
  11428. None.
  11429. --*/
  11430. {
  11431. #undef DEBSUB
  11432. #define DEBSUB "ChgOrdAcceptShutdown:"
  11433. LIST_ENTRY RunDown;
  11434. DPRINT1(3, "<<<<<<<...E N T E R I N G -- %s...>>>>>>>>\n", DEBSUB);
  11435. FrsRtlRunDownQueue(&FrsVolumeLayerCOQueue, &RunDown);
  11436. ChangeOrderAcceptIsShuttingDown = TRUE;
  11437. }
  11438. #if 0
  11439. DWORD
  11440. ChgOrdTestJournalPause(
  11441. PVOID FrsThreadCtxArg
  11442. )
  11443. /*++
  11444. Routine Description:
  11445. Entry point for test thread that pauses and unpauses the Journal.
  11446. Arguments:
  11447. FrsThreadCtxArg - thread
  11448. Return Value:
  11449. WIN32 status
  11450. --*/
  11451. {
  11452. #undef DEBSUB
  11453. #define DEBSUB "ChgOrdTestJournalPause:"
  11454. PVOLUME_MONITOR_ENTRY pVme;
  11455. ULONG i, Select, WStatus;;
  11456. //
  11457. // Wait till we see a completion port for the journal.
  11458. //
  11459. while (!HANDLE_IS_VALID(JournalCompletionPort)) {
  11460. Sleep(1000);
  11461. }
  11462. Sleep(3000);
  11463. while (TRUE) {
  11464. for (i=0; i<FrsRtlCountQueue(&VolumeMonitorQueue); i++) {
  11465. //
  11466. // Every 3 sec get an entry from the VolumeMonitorQueue
  11467. // and pause its journal for 5 seconds.
  11468. //
  11469. Select = i;
  11470. pVme = NULL;
  11471. ForEachListEntry(&VolumeMonitorQueue, VOLUME_MONITOR_ENTRY, ListEntry,
  11472. pVme = pE;
  11473. if ((Select--) == 0) {
  11474. break;
  11475. }
  11476. );
  11477. if (pVme != NULL) {
  11478. DPRINT(4, "Pausing the volume ----------------------\n");
  11479. WStatus = JrnlPauseVolume(pVme, 5000);
  11480. DPRINT_WS(0, "Status from Pause", WStatus);
  11481. if (WIN_SUCCESS(WStatus)) {
  11482. DPRINT(4, "Delaying 3 sec ----------------------\n");
  11483. Sleep(3000);
  11484. DPRINT(4, "Unpausing the volume ----------------------\n");
  11485. pVme->JournalState = JRNL_STATE_STARTING;
  11486. WStatus = JrnlUnPauseVolume(pVme, NULL, FALSE);
  11487. DPRINT_WS(0, "Status from Unpause", WStatus);
  11488. DPRINT(4, "Delaying 1 sec ----------------------\n");
  11489. Sleep(1000);
  11490. }
  11491. if (!WIN_SUCCESS(WStatus)) {
  11492. DPRINT(0, "Error Abort-----------------------------\n");
  11493. return 0;
  11494. }
  11495. }
  11496. }
  11497. }
  11498. return 0;
  11499. }
  11500. #endif 0
  11501. ULONG
  11502. ChgOrdInsertRemoteCo(
  11503. IN PCOMMAND_PACKET Cmd,
  11504. IN PCXTION Cxtion
  11505. )
  11506. /*++
  11507. Routine Description:
  11508. This is how remote change orders arrive for input acceptance processing.
  11509. The communication layer calls this routine to insert the remote
  11510. change order on the volume change order list used by this Replica
  11511. set. Allocate a change order entry and copy the change order command
  11512. into it.
  11513. Arguments:
  11514. Cmd -- The command packet containing the change order command.
  11515. Return Value:
  11516. Frs Status
  11517. --*/
  11518. {
  11519. #undef DEBSUB
  11520. #define DEBSUB "ChgOrdInsertRemoteCo:"
  11521. PREPLICA Replica;
  11522. PCHANGE_ORDER_COMMAND CoCmd;
  11523. Replica = RsReplica(Cmd);
  11524. CoCmd = RsPartnerCoc(Cmd);
  11525. //
  11526. // Increment the Remote Change Orders Received Variable for both the
  11527. // replica set and the connection.
  11528. //
  11529. PM_INC_CTR_REPSET(Replica, RCOReceived, 1);
  11530. PM_INC_CTR_CXTION(Cxtion, RCOReceived, 1);
  11531. //
  11532. // Mark this as a remote change order and insert it into the process queue.
  11533. // A zero value for the SequenceNumber tells the cleanup code that the
  11534. // CO was never inserted into the ActiveInlogRetryTable which happens
  11535. // if the CO is rejected on the first attempt.
  11536. //
  11537. CoCmd->SequenceNumber = 0;
  11538. ClearFlag(CoCmd->Flags, CO_FLAG_NOT_REMOTELY_VALID);
  11539. CoCmd->Spare1Wcs = NULL;
  11540. CoCmd->Spare2Wcs = NULL;
  11541. CoCmd->Spare2Bin = NULL;
  11542. SET_CHANGE_ORDER_STATE_CMD(CoCmd, IBCO_INITIALIZING);
  11543. return ChgOrdInsertProcessQueue(RsReplica(Cmd), CoCmd, 0, Cxtion);
  11544. }
  11545. ULONG
  11546. ChgOrdInsertProcessQueue(
  11547. IN PREPLICA Replica,
  11548. IN PCHANGE_ORDER_COMMAND CoCmd,
  11549. IN ULONG CoeFlags,
  11550. IN PCXTION Cxtion
  11551. )
  11552. /*++
  11553. Routine Description:
  11554. Insert the Change order into the volume process queue for input
  11555. acceptance processing. This could be a remote change order or a retry of
  11556. either a local or remote CO. Allocate a change order entry and copy the
  11557. change order command into it. Translate what we can to local member
  11558. information.
  11559. The change order process thread handles the GUID to FID translations
  11560. since the since it already has the database tables open and it would
  11561. tie up our caller's thread to do it here.
  11562. The NewReplica field of the change order command is set to the pointer
  11563. to the Replica struct.
  11564. Arguments:
  11565. Replica -- ptr to replica struct.
  11566. CoCmd -- ptr to change order command to submit.
  11567. CoeFlags -- Additional control flags to OR into Coe->EntryFlags.
  11568. Cxtion - The cxtion that received the remote co
  11569. Return Value:
  11570. Frs Status
  11571. --*/
  11572. {
  11573. #undef DEBSUB
  11574. #define DEBSUB "ChgOrdInsertProcessQueue:"
  11575. ULONG WStatus, FStatus;
  11576. ULONG ExtSize;
  11577. PCHANGE_ORDER_ENTRY ChangeOrder = NULL;
  11578. BOOL Activated, Executed;
  11579. PULONG pULong;
  11580. PDATA_EXTENSION_RETRY_TIMEOUT CoCmdRetryTimeout;
  11581. LONGLONG CurrentTime;
  11582. //
  11583. // If this is a change order retry we may already have a change order
  11584. // entry sitting in the Version Vector retire list. If so, find it,
  11585. // get a reference and use that one (if the connection Guid matches).
  11586. // This way if we end up aborting we can discard the VV retire slot
  11587. // and not propagate the CO to the outbound log. We can also accurately
  11588. // determine the state of the VV retire executed flag.
  11589. //
  11590. // The following table relates the state of the VV retire activated and
  11591. // executed flags in the change order being retried with its presence on
  11592. // the VV retire list.
  11593. //
  11594. // Found INLog Retry
  11595. // On List State
  11596. // Activated
  11597. // Executed Description
  11598. // y y y DB update beat the retry read but entry
  11599. // is still on the VV retire list. Use Reference.
  11600. // y y n DB read beat the update. Use Reference.
  11601. // y n y Error - Can't have exec set w/o activate.
  11602. // y n n Error - Not activated. Shouldn't be on list.
  11603. // n y y DB update beat the read & list entry removed.
  11604. // n y n DB read beat the update but then update completed
  11605. // & list entry deleted before retry could get the
  11606. // reference. Set Executed flag new CO for retry.
  11607. // n n y Error. Can't have exec set w/o activate.
  11608. // n n n OK. CO didn't make it far enough last time
  11609. // to activate the VV slot.
  11610. //
  11611. if (BooleanFlagOn(CoCmd->Flags, CO_FLAG_RETRY)) {
  11612. Executed = BooleanFlagOn(CoCmd->IFlags, CO_IFLAG_VVRETIRE_EXEC);
  11613. Activated = BooleanFlagOn(CoCmd->Flags, CO_FLAG_VV_ACTIVATED);
  11614. if (!Activated && Executed) {
  11615. DPRINT(0, "++ ERROR - Retry CO has CO_IFLAG_VVRETIRE_EXEC flag set but"
  11616. " CO_FLAG_VV_ACTIVATED is clear.\n");
  11617. FRS_ASSERT(!"ChgOrdInsertProcessQueue: CO_IFLAG_VVRETIRE_EXEC flag set, CO_FLAG_VV_ACTIVATED is clear");
  11618. }
  11619. ChangeOrder = VVReferenceRetireSlot(Replica, CoCmd);
  11620. if (ChangeOrder != NULL) {
  11621. if (!Activated) {
  11622. DPRINT(0, "++ ERROR - Retry CO has CO_FLAG_VV_ACTIVATED flag clear"
  11623. " but is on the VV retire list.\n");
  11624. FRS_ASSERT(!"ChgOrdInsertProcessQueue: CO_FLAG_VV_ACTIVATED is clear but is on the VV retire list");
  11625. }
  11626. } else {
  11627. //
  11628. // If CO_FLAG_VV_ACTIVATED is set then set CO_IFLAG_VVRETIRE_EXEC.
  11629. // Since we didn't find the slot on the VV retire list it must have
  11630. // been retired while this CO was waiting for retry in the INLOG.
  11631. // This is only true if this is not a change order submitted as
  11632. // part of recovery. With a recovery CO there may be no entry
  11633. // so leave the bits alone.
  11634. //
  11635. // NO, IGNORE RECOVERY FLAG!
  11636. // CO_FLAG_VV_ACTIVATED prevents this co from reserving a slot
  11637. // and from being activated during retire. Hence, the slot
  11638. // has been effectively executed. The executed flag should be
  11639. // set so that the INSTALL_INCOMPLETE bit can be turned off in
  11640. // the outbound log. So ignore the recovery flag.
  11641. //
  11642. // Although, David thought that the original intent of the
  11643. // check was to help recover from crashes. In that case,
  11644. // "Activated but not Executed" implies that the co was not
  11645. // propagated to the outlog. However, not setting _EXEC
  11646. // doesn't help that case since Activated prevents outlog
  11647. // insertion. It might help to clear _ACTIVATED in this case
  11648. // instead of setting _EXEC. Of course, the co may have
  11649. // made it into the outlog so be prepared for dup errors.
  11650. //
  11651. // WARN: inlog cos may not be propagated to outlog during crash recovery.
  11652. //
  11653. // if (Activated && !Executed &&
  11654. // !BooleanFlagOn(CoeFlags, COE_FLAG_RECOVERY_CO)) {
  11655. if (Activated && !Executed) {
  11656. SetFlag(CoCmd->IFlags, CO_IFLAG_VVRETIRE_EXEC);
  11657. }
  11658. }
  11659. }
  11660. if (ChangeOrder == NULL) {
  11661. //
  11662. // Allocate a change order entry with room for the filename and copy
  11663. // the change order + Filename into it. Init the ref count to one
  11664. // since the CO is going on the process queue.
  11665. //
  11666. ChangeOrder = FrsAllocType(CHANGE_ORDER_ENTRY_TYPE);
  11667. CopyMemory(&ChangeOrder->Cmd, CoCmd, sizeof(CHANGE_ORDER_COMMAND));
  11668. ChangeOrder->Cmd.Extension = NULL;
  11669. ChangeOrder->UFileName.Length = ChangeOrder->Cmd.FileNameLength;
  11670. ChangeOrder->HashEntryHeader.ReferenceCount = 0;
  11671. INCREMENT_CHANGE_ORDER_REF_COUNT(ChangeOrder); // for tracking
  11672. //
  11673. // Copy the Change Order Extension if provided.
  11674. //
  11675. ExtSize = sizeof(CHANGE_ORDER_RECORD_EXTENSION);
  11676. if ((CoCmd->Extension != NULL) &&
  11677. (CoCmd->Extension->FieldSize > 0)) {
  11678. if (CoCmd->Extension->FieldSize >= REALLY_BIG_EXTENSION_SIZE) {
  11679. pULong = (PULONG) CoCmd->Extension;
  11680. DPRINT5(5, "Extension Buffer: (%08x) %08x %08x %08x %08x\n",
  11681. pULong, *(pULong+0), *(pULong+1), *(pULong+2), *(pULong+3));
  11682. DPRINT5(5, "Extension Buffer: (%08x) %08x %08x %08x %08x\n",
  11683. (PCHAR)pULong+16, *(pULong+4), *(pULong+5), *(pULong+6), *(pULong+7));
  11684. FRS_ASSERT(!"CoCmd->Extension->FieldSize corrupted");
  11685. }
  11686. //
  11687. // Convert the CO_RECORD_EXTENSION_WIN2K struct from downlevel members
  11688. // to a CHANGE_ORDER_RECORD_EXTENSION struct that we understand.
  11689. //
  11690. if (CoCmd->Extension->Major == CO_RECORD_EXTENSION_VERSION_WIN2K) {
  11691. ChangeOrder->Cmd.Extension =
  11692. DbsDataConvertCocExtensionFromWin2K((PCO_RECORD_EXTENSION_WIN2K)CoCmd->Extension);
  11693. } else {
  11694. //
  11695. // If incoming CO has larger extension, use it.
  11696. //
  11697. if (ExtSize < CoCmd->Extension->FieldSize) {
  11698. ExtSize = CoCmd->Extension->FieldSize;
  11699. }
  11700. ChangeOrder->Cmd.Extension = FrsAlloc(ExtSize);
  11701. CopyMemory(ChangeOrder->Cmd.Extension, CoCmd->Extension, ExtSize);
  11702. }
  11703. } else {
  11704. //
  11705. // Incoming extension NULL or Field Size zero. Init new one.
  11706. //
  11707. ChangeOrder->Cmd.Extension = FrsAlloc(ExtSize);
  11708. DbsDataInitCocExtension(ChangeOrder->Cmd.Extension);
  11709. }
  11710. CoCmdRetryTimeout = DbsDataExtensionFind(ChangeOrder->Cmd.Extension, DataExtend_Retry_Timeout);
  11711. if(CoCmdRetryTimeout != NULL) {
  11712. GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
  11713. //
  11714. // if this is the first time through, set the time.
  11715. //
  11716. if(CoCmdRetryTimeout->Count == 0) {
  11717. CoCmdRetryTimeout->FirstTryTime = CurrentTime;
  11718. }
  11719. }
  11720. }
  11721. //
  11722. // MOVERS is only for local ChangeOrders. They are transformed into
  11723. // a delete and create change orders targeted to their respective
  11724. // replica sets.
  11725. //
  11726. if (CO_LOCN_CMD_IS(ChangeOrder, CO_LOCATION_MOVERS)) {
  11727. DPRINT(0, "++ ERROR - change order command is MOVERS.");
  11728. FRS_PRINT_TYPE(0, ChangeOrder);
  11729. FrsFreeType(ChangeOrder);
  11730. return FrsErrorInvalidChangeOrder;
  11731. }
  11732. //
  11733. // Save ptr to target replica struct.
  11734. //
  11735. ChangeOrder->NewReplica = Replica;
  11736. ChangeOrder->Cmd.NewReplicaNum = ReplicaAddrToId(Replica);
  11737. //
  11738. // We will never see a remotely generated MOVERS. We always
  11739. // see a delete to the old RS followed by a create in the
  11740. // new RS. So set both replica ptrs to our Replica struct.
  11741. //
  11742. // Note: MOVERS: This may not work for a LocalCo retry of a MOVERS.
  11743. ChangeOrder->OriginalReplica = Replica;
  11744. ChangeOrder->Cmd.OriginalReplicaNum = ReplicaAddrToId(Replica);
  11745. FRS_ASSERT( CO_STATE(ChangeOrder) < IBCO_MAX_STATE );
  11746. SET_COE_FLAG(ChangeOrder, CoeFlags);
  11747. //
  11748. // Put the change order on the tail end of the volume change order list.
  11749. //
  11750. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO) &&
  11751. !CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY |
  11752. CO_FLAG_CONTROL |
  11753. CO_FLAG_MOVEIN_GEN |
  11754. CO_FLAG_MORPH_GEN) &&
  11755. !RecoveryCo(ChangeOrder) &&
  11756. !COE_FLAG_ON(ChangeOrder, COE_FLAG_PARENT_REANIMATION)) {
  11757. INC_LOCAL_CO_QUEUE_COUNT(Replica);
  11758. }
  11759. //
  11760. // Pick up the cxtion's join guid (session id). The change orders
  11761. // will be sent back through the retry path if the cxtion's
  11762. // state has changed by the time the change orders make it
  11763. // to the replica command server. The join guid is used for
  11764. // asserting that we never have a change order for a joined
  11765. // cxtion with a mismatched join guid.
  11766. //
  11767. // The count of remote change orders is used when transitioning
  11768. // the cxtion from UNJOINING to UNJOINED. Further requests to
  11769. // join are ignored until the transition. Basically, we are
  11770. // waiting for the change orders to be put into the retry state
  11771. // for later recovery.
  11772. //
  11773. // Synchronize with change order accept and the replica command server
  11774. //
  11775. if (!CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO)) {
  11776. //
  11777. // This change order may already have a cxtion if it was
  11778. // pulled from the active retry table and there is no
  11779. // guarantee that the change order in the table is from
  11780. // the same cxtion as this change order since the table
  11781. // is sorted by change order guid.
  11782. //
  11783. if (ChangeOrder->Cxtion == NULL) {
  11784. ChangeOrder->Cxtion = Cxtion;
  11785. }
  11786. FRS_ASSERT(ChangeOrder->Cxtion);
  11787. //
  11788. // Bump the change order count associated with this connection.
  11789. // Any change order with a non-NULL ChangeOrder->Cxtion will have
  11790. // its remote change order count decremented at issue cleanup.
  11791. // The count applies to control change orders too.
  11792. //
  11793. LOCK_CXTION_TABLE(Replica);
  11794. INCREMENT_CXTION_CHANGE_ORDER_COUNT(Replica, ChangeOrder->Cxtion);
  11795. UNLOCK_CXTION_TABLE(Replica);
  11796. } else {
  11797. //
  11798. // Set the Jrnl Cxtion Guid and Cxtion ptr for this Local CO.
  11799. // This bumps the Cxtion CO count.
  11800. // Any change order with a non-NULL ChangeOrder->Cxtion will have
  11801. // its remote change order count decremented at issue cleanup.
  11802. // This GUID changes each time the service starts the replica set.
  11803. //
  11804. ChangeOrder->Cmd.CxtionGuid = Replica->JrnlCxtionGuid;
  11805. ACQUIRE_CXTION_CO_REFERENCE(Replica, ChangeOrder);
  11806. if (ChangeOrder->Cxtion == NULL) {
  11807. return FrsErrorInvalidChangeOrder;
  11808. }
  11809. }
  11810. //
  11811. // Refresh the Join Guid.
  11812. //
  11813. ChangeOrder->JoinGuid = ChangeOrder->Cxtion->JoinGuid;
  11814. WStatus = ChgOrdInsertProcQ(Replica, ChangeOrder, IPQ_TAIL | IPQ_DEREF_CXTION_IF_ERR);
  11815. if (!WIN_SUCCESS(WStatus)) {
  11816. SET_ISSUE_CLEANUP(ChangeOrder, ISCU_FREE_CO | ISCU_DEC_CO_REF);
  11817. FStatus = ChgOrdIssueCleanup(NULL, Replica, ChangeOrder, 0);
  11818. DPRINT_FS(0, "++ ChgOrdIssueCleanup error.", FStatus);
  11819. return FrsErrorInternalError;
  11820. }
  11821. return FrsErrorSuccess;
  11822. }
  11823. VOID
  11824. ChgOrdStartJoinRequest(
  11825. IN PREPLICA Replica,
  11826. IN PCXTION Cxtion
  11827. )
  11828. /*++
  11829. Routine Description:
  11830. An inbound connection wants to start up. Submit a control change order
  11831. that will scan the inlog and requeue any pending COs from this connection.
  11832. Arguments:
  11833. Replica -- The replica set owning the connection.
  11834. Cxtion -- the connection being started.
  11835. Return Value:
  11836. None.
  11837. --*/
  11838. {
  11839. #undef DEBSUB
  11840. #define DEBSUB "ChgOrdStartJoinRequest:"
  11841. CHANGE_ORDER_COMMAND CoCmd;
  11842. //
  11843. // Mark this as a control change order and insert it into the process queue.
  11844. // When it gets to the head of the queue we know all COs from this Cxtion
  11845. // that may have been ahead of it in the queue are now in the inbound Log.
  11846. //
  11847. // Use a temp CoCmd since it gets copied into the change order entry when
  11848. // it is inserted onto the process queue.
  11849. //
  11850. ZeroMemory( &CoCmd, sizeof(CHANGE_ORDER_COMMAND));
  11851. CoCmd.ContentCmd = FCN_CORETRY_ONE_CXTION;
  11852. COPY_GUID(&CoCmd.CxtionGuid, Cxtion->Name->Guid);
  11853. SetFlag(CoCmd.Flags, CO_FLAG_CONTROL);
  11854. wcscpy(CoCmd.FileName, L"VVJoinStartRequest"); // for tracing.
  11855. SET_CHANGE_ORDER_STATE_CMD(&CoCmd, IBCO_INITIALIZING);
  11856. ChgOrdInsertProcessQueue(Replica, &CoCmd, 0, Cxtion);
  11857. return;
  11858. }
  11859. PCHANGE_ORDER_ENTRY
  11860. ChgOrdMakeFromFile(
  11861. IN PREPLICA Replica,
  11862. IN HANDLE FileHandle,
  11863. IN PULONGLONG ParentFid,
  11864. IN ULONG LocationCmd,
  11865. IN ULONG CoFlags,
  11866. IN PWCHAR FileName,
  11867. IN USHORT Length
  11868. )
  11869. /*++
  11870. Routine Description:
  11871. This functions allocates a change order entry and inits some of the fields.
  11872. Depending on the change order some of these fields may be overwritten later.
  11873. Example Call:
  11874. //
  11875. // Allocate and init a local change order using the supplied file handle.
  11876. //
  11877. //NewCoe = ChgOrdMakeFromFile(Replica,
  11878. // FileHandle,
  11879. // &MoveInContext->ParentFileID,
  11880. // CO_LOCATION_MOVEIN,
  11881. // CO_FLAG_LOCALCO | CO_FLAG_LOCATION_CMD,
  11882. // DirectoryRecord->FileName,
  11883. // (USHORT)DirectoryRecord->FileNameLength);
  11884. Arguments:
  11885. Replica - ptr to replica set for this change order.
  11886. FileHandle - The open file handle.
  11887. ParentFid - The parent file reference number for this file.
  11888. LocationCmd -- A Create, delete, ... change order.
  11889. CoFlags -- The change order option flags. see schema.h
  11890. FileName - Filename for this file. For a sub tree op it comes from the
  11891. filter entry.
  11892. Length - the file name length in bytes.
  11893. Return Value:
  11894. ptr to change order entry. NULL if can't get file info.
  11895. --*/
  11896. {
  11897. #undef DEBSUB
  11898. #define DEBSUB "ChgOrdMakeFromFile:"
  11899. DWORD WStatus;
  11900. FILE_INTERNAL_INFORMATION FileInternalInfo;
  11901. FILE_NETWORK_OPEN_INFORMATION FileInfo;
  11902. BOOL IsDirectory;
  11903. PCHANGE_ORDER_ENTRY ChangeOrder;
  11904. PCHANGE_ORDER_COMMAND Coc;
  11905. PCONFIG_TABLE_RECORD ConfigRecord;
  11906. //
  11907. // Get the file's attributes
  11908. //
  11909. if (!FrsGetFileInfoByHandle(FileName, FileHandle, &FileInfo)) {
  11910. DPRINT1(4, "++ Can't get attributes for %ws\n", FileName);
  11911. return NULL;
  11912. }
  11913. //
  11914. // Get the fid of preinstall area for filtering.
  11915. //
  11916. WStatus = FrsGetFileInternalInfoByHandle(Replica->PreInstallHandle,
  11917. &FileInternalInfo);
  11918. DPRINT_WS(0, "++ ERROR - FrsGetFileInternalInfoByHandle(PreInstallDir).", WStatus);
  11919. if (!WIN_SUCCESS(WStatus)) {
  11920. return NULL;
  11921. }
  11922. IsDirectory = (FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  11923. //
  11924. // Note: This function is currently (11/19/98) unused. If it is ever used,
  11925. // and the change order allocated below turns out to be a local CO,
  11926. // remember to set the JrnlCxtion field of the change order command
  11927. //
  11928. // Construct new change order and allocate a change order extension.
  11929. //
  11930. ChangeOrder = FrsAllocType(CHANGE_ORDER_ENTRY_TYPE);
  11931. ChangeOrder->Cmd.Extension = FrsAlloc(sizeof(CHANGE_ORDER_RECORD_EXTENSION));
  11932. DbsDataInitCocExtension(ChangeOrder->Cmd.Extension);
  11933. Coc = &ChangeOrder->Cmd;
  11934. //
  11935. // Set the initial reference count to 1.
  11936. //
  11937. INCREMENT_CHANGE_ORDER_REF_COUNT(ChangeOrder);
  11938. //
  11939. // Capture the file name.
  11940. //
  11941. FRS_ASSERT(Length <= MAX_PATH*2);
  11942. CopyMemory(ChangeOrder->Cmd.FileName, FileName, Length);
  11943. Coc->FileName[Length/2] = UNICODE_NULL;
  11944. ChangeOrder->UFileName.Length = Length;
  11945. Coc->FileNameLength = Length;
  11946. //
  11947. // Set New and orig Replica fields to the replica.
  11948. //
  11949. ChangeOrder->OriginalReplica = Replica;
  11950. ChangeOrder->NewReplica = Replica;
  11951. Coc->OriginalReplicaNum = ReplicaAddrToId(ChangeOrder->OriginalReplica);
  11952. Coc->NewReplicaNum = ReplicaAddrToId(ChangeOrder->NewReplica);
  11953. //
  11954. // Set New and orig parent FID fields to the parent FID.
  11955. //
  11956. ChangeOrder->OriginalParentFid = *ParentFid;
  11957. ChangeOrder->NewParentFid = *ParentFid;
  11958. ChangeOrder->FileReferenceNumber = FileInternalInfo.IndexNumber.QuadPart;
  11959. ChangeOrder->ParentFileReferenceNumber = *ParentFid;
  11960. //
  11961. // EntryCreateTime is a tick count for aging.
  11962. //
  11963. ChangeOrder->EntryCreateTime = CO_TIME_NOW(Replica->pVme);
  11964. //
  11965. // Event time from current time.
  11966. //
  11967. GetSystemTimeAsFileTime((PFILETIME)&Coc->EventTime.QuadPart);
  11968. //
  11969. // File's attributes, Create time, Write time
  11970. //
  11971. ChangeOrder->FileAttributes = FileInfo.FileAttributes;
  11972. Coc->FileAttributes = FileInfo.FileAttributes;
  11973. ChangeOrder->FileCreateTime = FileInfo.CreationTime;
  11974. ChangeOrder->FileWriteTime = FileInfo.LastWriteTime;
  11975. Coc->FileSize = FileInfo.AllocationSize.QuadPart;
  11976. ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
  11977. Coc->OriginatorGuid = ConfigRecord->ReplicaVersionGuid;
  11978. Coc->FileVersionNumber = 0;
  11979. //
  11980. // File's USN
  11981. //
  11982. Coc->FileUsn = 0;
  11983. Coc->JrnlUsn = 0;
  11984. Coc->JrnlFirstUsn = 0;
  11985. //
  11986. // File is dir?
  11987. //
  11988. SET_CO_LOCATION_CMD(*Coc,
  11989. DirOrFile,
  11990. (IsDirectory ? CO_LOCATION_DIR : CO_LOCATION_FILE));
  11991. //
  11992. // Create, Delete, ..
  11993. //
  11994. SET_CO_LOCATION_CMD(*Coc, Command, LocationCmd);
  11995. if (LocationCmd == CO_LOCATION_NO_CMD) {
  11996. Coc->ContentCmd = USN_REASON_DATA_OVERWRITE;
  11997. } else
  11998. if (CO_NEW_FILE(LocationCmd)) {
  11999. Coc->ContentCmd = USN_REASON_FILE_CREATE;
  12000. } else
  12001. if (CO_DELETE_FILE(LocationCmd)) {
  12002. Coc->ContentCmd = USN_REASON_FILE_DELETE;
  12003. } else {
  12004. DPRINT1(0, "++ Error - Invalid location cmd: %d\n", LocationCmd);
  12005. Coc->ContentCmd = USN_REASON_DATA_OVERWRITE;
  12006. }
  12007. //
  12008. // Set the change order flags.
  12009. //
  12010. SET_CO_FLAG(ChangeOrder, CoFlags);
  12011. return ChangeOrder;
  12012. }
  12013. PCHANGE_ORDER_ENTRY
  12014. ChgOrdMakeFromIDRecord(
  12015. IN PIDTABLE_RECORD IDTableRec,
  12016. IN PREPLICA Replica,
  12017. IN ULONG LocationCmd,
  12018. IN ULONG CoFlags,
  12019. IN GUID *CxtionGuid
  12020. )
  12021. /*++
  12022. Routine Description:
  12023. Create and init a change order. It starts out with a ref count of one.
  12024. Note - Since only a single Replica arg is passed in and only a single
  12025. parent guid is available for the IDTable record this function can not
  12026. create rename change orders.
  12027. Arguments:
  12028. IDTableRec - ID table record.
  12029. Replica -- The Replica set this CO is for.
  12030. LocationCmd -- A Create, delete, ... change order.
  12031. CoFlags -- The change order option flags. see schema.h
  12032. CxtionGuid -- The Guid of the connection this CO will be sent to.
  12033. NULL if unneeded.
  12034. Thread Return Value:
  12035. A ptr to a change order entry.
  12036. --*/
  12037. {
  12038. #undef DEBSUB
  12039. #define DEBSUB "ChgOrdMakeFromIDRecord:"
  12040. PCHANGE_ORDER_ENTRY Coe;
  12041. PCHANGE_ORDER_COMMAND Coc;
  12042. PDATA_EXTENSION_CHECKSUM IdtDataChkSum;
  12043. PIDTABLE_RECORD_EXTENSION IdtExt;
  12044. PCHANGE_ORDER_RECORD_EXTENSION CocExt;
  12045. FRS_ASSERT((LocationCmd == CO_LOCATION_NO_CMD) ||
  12046. (LocationCmd == CO_LOCATION_CREATE) ||
  12047. (LocationCmd == CO_LOCATION_MOVEOUT) ||
  12048. (LocationCmd == CO_LOCATION_DELETE));
  12049. //
  12050. // Alloc Change order entry and extension.
  12051. //
  12052. Coe = FrsAllocType(CHANGE_ORDER_ENTRY_TYPE);
  12053. Coe->Cmd.Extension = FrsAlloc(sizeof(CHANGE_ORDER_RECORD_EXTENSION));
  12054. DbsDataInitCocExtension(Coe->Cmd.Extension);
  12055. Coc = &Coe->Cmd;
  12056. //
  12057. // Assign a change order guid and init ref count.
  12058. //
  12059. FrsUuidCreate(&Coc->ChangeOrderGuid);
  12060. INCREMENT_CHANGE_ORDER_REF_COUNT(Coe);
  12061. //
  12062. // File's fid and parent FID
  12063. //
  12064. Coe->FileReferenceNumber = IDTableRec->FileID;
  12065. Coe->ParentFileReferenceNumber = IDTableRec->ParentFileID;
  12066. //
  12067. // Original parent and New parent are the same (not a rename)
  12068. //
  12069. Coe->OriginalParentFid = IDTableRec->ParentFileID;
  12070. Coe->NewParentFid = IDTableRec->ParentFileID;
  12071. //
  12072. // File's attributes, create and write times.
  12073. //
  12074. Coe->FileAttributes = IDTableRec->FileAttributes;
  12075. Coe->FileCreateTime = IDTableRec->FileCreateTime;
  12076. Coe->FileWriteTime = IDTableRec->FileWriteTime;
  12077. //
  12078. // Jet context for outlog insertion
  12079. //
  12080. Coe->RtCtx = FrsAllocTypeSize(REPLICA_THREAD_TYPE,
  12081. FLAG_FRSALLOC_NO_ALLOC_TBL_CTX);
  12082. FrsRtlInsertTailList(&Replica->ReplicaCtxListHead,
  12083. &Coe->RtCtx->ReplicaCtxList);
  12084. //
  12085. // The sequence number is zero initially. It may get a value when
  12086. // the CO is inserted into an inbound or outbound log.
  12087. //
  12088. Coc->SequenceNumber = 0;
  12089. Coc->PartnerAckSeqNumber = 0;
  12090. //
  12091. // File's attributes, again
  12092. //
  12093. Coc->FileAttributes = Coe->FileAttributes;
  12094. //
  12095. // File's version number, size, VSN, and event time.
  12096. //
  12097. Coc->FileVersionNumber = IDTableRec->VersionNumber;
  12098. Coc->FileSize = IDTableRec->FileSize;
  12099. Coc->FrsVsn = IDTableRec->OriginatorVSN;
  12100. Coc->EventTime.QuadPart = IDTableRec->EventTime;
  12101. //
  12102. // Original and New Replica are the same (not a rename)
  12103. //
  12104. Coe->OriginalReplica = Replica;
  12105. Coe->NewReplica = Replica;
  12106. Coc->OriginalReplicaNum = ReplicaAddrToId(Coe->OriginalReplica);
  12107. Coc->NewReplicaNum = ReplicaAddrToId(Coe->NewReplica);
  12108. //
  12109. // Originator guid, File guid, old and new parent guid.
  12110. //
  12111. Coc->OriginatorGuid = IDTableRec->OriginatorGuid;
  12112. Coc->FileGuid = IDTableRec->FileGuid;
  12113. Coc->OldParentGuid = IDTableRec->ParentGuid;
  12114. Coc->NewParentGuid = IDTableRec->ParentGuid;
  12115. //
  12116. // Cxtion's guid (identifies the cxtion)
  12117. //
  12118. if (CxtionGuid != NULL) {
  12119. Coc->CxtionGuid = *CxtionGuid;
  12120. }
  12121. //
  12122. // Filename length in bytes not including the terminating NULL
  12123. //
  12124. Coc->FileNameLength = wcslen(IDTableRec->FileName) * sizeof(WCHAR);
  12125. Coe->UFileName.Length = Coc->FileNameLength;
  12126. //
  12127. // Filename (including the terminating NULL)
  12128. //
  12129. CopyMemory(Coc->FileName, IDTableRec->FileName, Coc->FileNameLength + sizeof(WCHAR));
  12130. //
  12131. // File's USN
  12132. //
  12133. Coc->FileUsn = IDTableRec->CurrentFileUsn;
  12134. Coc->JrnlUsn = 0;
  12135. Coc->JrnlFirstUsn = 0;
  12136. //
  12137. // If the IDTable Record has a file checksum, copy it into the changeorder.
  12138. //
  12139. IdtExt = &IDTableRec->Extension;
  12140. IdtDataChkSum = DbsDataExtensionFind(IdtExt, DataExtend_MD5_CheckSum);
  12141. CocExt = Coc->Extension;
  12142. if (IdtDataChkSum != NULL) {
  12143. if (IdtDataChkSum->Prefix.Size != sizeof(DATA_EXTENSION_CHECKSUM)) {
  12144. DPRINT1(0, "<MD5_CheckSum Size (%08x) invalid>\n",
  12145. IdtDataChkSum->Prefix.Size);
  12146. //
  12147. // Format is hosed. Reinit it and zero checksum.
  12148. //
  12149. DbsDataInitIDTableExtension(IdtExt);
  12150. IdtDataChkSum = DbsDataExtensionFind(IdtExt, DataExtend_MD5_CheckSum);
  12151. ZeroMemory(IdtDataChkSum->Data, MD5DIGESTLEN);
  12152. }
  12153. DPRINT4(4, "IDT MD5: %08x %08x %08x %08x\n",
  12154. *(((ULONG *) &IdtDataChkSum->Data[0])),
  12155. *(((ULONG *) &IdtDataChkSum->Data[4])),
  12156. *(((ULONG *) &IdtDataChkSum->Data[8])),
  12157. *(((ULONG *) &IdtDataChkSum->Data[12])));
  12158. CopyMemory(CocExt->DataChecksum.Data, IdtDataChkSum->Data, MD5DIGESTLEN);
  12159. } else {
  12160. ZeroMemory(CocExt->DataChecksum.Data, MD5DIGESTLEN);
  12161. }
  12162. //
  12163. // File is dir?
  12164. //
  12165. SET_CO_LOCATION_CMD(*Coc,
  12166. DirOrFile,
  12167. ((IDTableRec->FileIsDir) ?
  12168. CO_LOCATION_DIR : CO_LOCATION_FILE));
  12169. //
  12170. // Consistency check.
  12171. //
  12172. FRS_ASSERT(BooleanFlagOn(IDTableRec->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)
  12173. == ((BOOLEAN) IDTableRec->FileIsDir));
  12174. //
  12175. // Create, Delete, ..
  12176. //
  12177. SET_CO_LOCATION_CMD(*Coc, Command, LocationCmd);
  12178. if (LocationCmd == CO_LOCATION_NO_CMD) {
  12179. Coc->ContentCmd = USN_REASON_DATA_OVERWRITE;
  12180. } else
  12181. if (LocationCmd == CO_LOCATION_CREATE) {
  12182. Coc->ContentCmd = USN_REASON_FILE_CREATE;
  12183. } else
  12184. if (LocationCmd == CO_LOCATION_DELETE) {
  12185. Coc->ContentCmd = USN_REASON_FILE_DELETE;
  12186. } else
  12187. if (LocationCmd == CO_LOCATION_MOVEOUT) {
  12188. Coc->ContentCmd = 0;
  12189. } else {
  12190. DPRINT1(0, "++ Error - Invalid location cmd: %d\n", LocationCmd);
  12191. Coc->ContentCmd = USN_REASON_DATA_OVERWRITE;
  12192. }
  12193. //
  12194. // Set the change order flags.
  12195. //
  12196. SET_CO_FLAG(Coe, CoFlags);
  12197. //
  12198. // Return CO to caller.
  12199. //
  12200. return Coe;
  12201. }
  12202. VOID
  12203. ChgOrdRetrySubmit(
  12204. IN PREPLICA Replica,
  12205. IN PCXTION Cxtion,
  12206. IN USHORT Command,
  12207. IN BOOL Wait
  12208. )
  12209. /*++
  12210. Routine Description:
  12211. Submit retry request to change order retry cmd server.
  12212. Arguments:
  12213. Replica -- Replica set on which to perform retry scan.
  12214. CxtionGuid -- Inbound cxtion guid if doing a single cxtion only.
  12215. Command --
  12216. FCN_CORETRY_ONE_CXTION -- Do scan for COs from single inbound partner.
  12217. FCN_CORETRY_ALL_CXTIONS -- Do Scan for COs from all inbound partners.
  12218. FCN_CORETRY_LOCAL_ONLY -- Do scan for Local COs only.
  12219. Wait -- True if we are to wait until command completes.
  12220. Return Value:
  12221. None.
  12222. --*/
  12223. {
  12224. #undef DEBSUB
  12225. #define DEBSUB "ChgOrdRetrySubmit:"
  12226. PCOMMAND_PACKET Cmd;
  12227. CHAR GuidStr[GUID_CHAR_LEN];
  12228. ULONG WStatus;
  12229. //
  12230. // Build command to scan inlog for retry COs. Pass Replica ptr and
  12231. // Cxtion Guid. If Cxtion Quid is Null then all Cxtions are done.
  12232. //
  12233. Cmd = FrsAllocCommand(&ChgOrdRetryCS.Queue, Command);
  12234. CoRetryReplica(Cmd) = Replica;
  12235. CoRetryCxtion(Cmd) = Cxtion;
  12236. if (Cxtion) {
  12237. GuidToStr(Cxtion->Name->Guid, GuidStr);
  12238. DPRINT2(1, "++ ChgOrdRetryCS: submit for Replica %ws for Cxtion %s\n",
  12239. Replica->ReplicaName->Name, GuidStr);
  12240. } else {
  12241. DPRINT1(1, "++ ChgOrdRetryCS: submit for Replica %ws\n",
  12242. Replica->ReplicaName->Name);
  12243. }
  12244. if (Wait) {
  12245. //
  12246. // Make the call synchronous.
  12247. // Don't free the packet when the command completes.
  12248. //
  12249. FrsSetCompletionRoutine(Cmd, FrsCompleteKeepPkt, NULL);
  12250. //
  12251. // SUBMIT retry request Cmd and wait for completion.
  12252. //
  12253. WStatus = FrsSubmitCommandServerAndWait(&ChgOrdRetryCS, Cmd, INFINITE);
  12254. DPRINT_WS(0, "++ ERROR - DB Command failed.", WStatus);
  12255. FrsFreeCommand(Cmd, NULL);
  12256. } else {
  12257. //
  12258. // Fire and forget the command.
  12259. //
  12260. FrsSubmitCommandServer(&ChgOrdRetryCS, Cmd);
  12261. }
  12262. }
  12263. VOID
  12264. ChgOrdCalcHashGuidAndName (
  12265. IN PUNICODE_STRING Name,
  12266. IN GUID *Guid,
  12267. OUT PULONGLONG HashValue
  12268. )
  12269. /*++
  12270. Routine Description:
  12271. This routine forms a 32 bit hash of the name and guid args.
  12272. It returns this in the low 32 bits of HashValue. The upper 32 bits are zero.
  12273. Arguments:
  12274. Name - The filename to hash.
  12275. Guid - The Guid to hash.
  12276. HashValue - The resulting quadword hash value.
  12277. Return Value:
  12278. None
  12279. --*/
  12280. {
  12281. #undef DEBSUB
  12282. #define DEBSUB "ChgOrdCalcHashGuidAndName:"
  12283. PUSHORT p;
  12284. ULONG NameHash = 0;
  12285. ULONG Shift = 0;
  12286. ULONG GuidHash;
  12287. CHAR GuidStr[GUID_CHAR_LEN];
  12288. FRS_ASSERT( ValueIsMultOf2(Name->Length) );
  12289. FRS_ASSERT( Name->Length != 0 );
  12290. //
  12291. // Combine each unicode character into the hash value, shifting 4 bits
  12292. // each time. Start at the end of the name so file names with different
  12293. // type codes will hash to different table offsets.
  12294. //
  12295. for( p = Name->Buffer + (Name->Length / sizeof(WCHAR)) - 1;
  12296. p >= Name->Buffer;
  12297. p-- ) {
  12298. // NameHash = NameHash ^ (((ULONG)*p) << Shift);
  12299. NameHash = NameHash ^ (((ULONG)towupper(*p)) << Shift);
  12300. Shift = (Shift < 16) ? Shift + 4 : 0;
  12301. }
  12302. //
  12303. // Combine each USHORT of the Guid into the hash value, shifting 4 bits
  12304. // each time.
  12305. //
  12306. GuidHash = JrnlHashCalcGuid(Guid, sizeof(GUID));
  12307. //p = (PUSHORT) Guid;
  12308. //GuidHash = (((ULONG)*(p+0)) << 0) ^ (((ULONG)*(p+1)) << 4)
  12309. // ^ (((ULONG)*(p+2)) << 8) ^ (((ULONG)*(p+3)) << 12)
  12310. // ^ (((ULONG)*(p+4)) << 16) ^ (((ULONG)*(p+5)) << 0)
  12311. // ^ (((ULONG)*(p+6)) << 4) ^ (((ULONG)*(p+7)) << 8);
  12312. if (GuidHash == 0) {
  12313. GuidToStr(Guid, GuidStr);
  12314. DPRINT2(0, "++ Warning - GuidHash is zero. Guid: %s Name: %ws\n",
  12315. Guid, Name->Buffer);
  12316. }
  12317. *HashValue = (ULONGLONG) (NameHash + GuidHash);
  12318. }