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.

1918 lines
52 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. movelist.c
  5. Abstract:
  6. Implements APIs to order nested renames
  7. Author:
  8. 03-Jun-2001 Jim Schmidt (jimschm)
  9. Revision History:
  10. jimschm 03-Jun-2001 Moved from buildinf.c
  11. --*/
  12. #include "pch.h"
  13. #include "migutilp.h"
  14. #ifdef DEBUG
  15. //#define MOVE_TEST
  16. #endif
  17. //
  18. // Declare structures
  19. //
  20. #define MOVE_LIST_HASH_BUCKETS 11
  21. struct TAG_MOVE_LIST_NODEW;
  22. typedef struct {
  23. struct TAG_MOVE_LIST_NODEW *Left;
  24. struct TAG_MOVE_LIST_NODEW *Right;
  25. struct TAG_MOVE_LIST_NODEW *Parent;
  26. } BINTREE_LINKAGE, *PBINTREE_LINKAGE;
  27. #define SOURCE_LINKAGE 0
  28. #define DESTINATION_LINKAGE 1
  29. typedef struct TAG_MOVE_LIST_NODEW {
  30. BINTREE_LINKAGE Linkage[2];
  31. PCWSTR Source;
  32. PCWSTR Destination;
  33. PCWSTR FixedSource;
  34. PCWSTR FixedDestination;
  35. } MOVE_LIST_NODEW, *PMOVE_LIST_NODEW;
  36. typedef struct TAG_MOVE_LIST_GROUPW {
  37. PMOVE_LIST_NODEW SourceTreeRoot;
  38. struct TAG_MOVE_LIST_GROUPW *Next, *NextHash;
  39. UINT SourceLength;
  40. #ifdef MOVE_TEST
  41. UINT ItemCount;
  42. #endif
  43. } MOVE_LIST_GROUPW, *PMOVE_LIST_GROUPW;
  44. typedef struct TAG_MOVE_LISTW {
  45. PMOVE_LIST_GROUPW HeadGroup;
  46. PMOVE_LIST_GROUPW Buckets[MOVE_LIST_HASH_BUCKETS];
  47. struct TAG_MOVE_LISTW *NextChainedList;
  48. POOLHANDLE Pool;
  49. PMOVE_LIST_NODEW DestinationTreeRoot;
  50. #ifdef MOVE_TEST
  51. UINT DestItemCount;
  52. UINT GroupCount;
  53. #endif
  54. } MOVE_LISTW, *PMOVE_LISTW;
  55. typedef enum {
  56. BEGIN_LIST,
  57. BEGIN_LENGTH_GROUP,
  58. ENUM_RETURN_ITEM,
  59. ENUM_NEXT_ITEM,
  60. ENUM_NEXT_LENGTH_GROUP,
  61. ENUM_NEXT_LIST
  62. } MOVE_ENUM_STATE;
  63. typedef struct {
  64. // enum output
  65. PMOVE_LIST_NODEW Item;
  66. // private members
  67. MOVE_ENUM_STATE State;
  68. PMOVE_LIST_GROUPW LengthGroup;
  69. PMOVE_LISTW ThisList;
  70. PMOVE_LIST_NODEW StartFrom;
  71. } MOVE_LIST_ENUMW, *PMOVE_LIST_ENUMW;
  72. #ifdef MOVE_TEST
  73. VOID
  74. pTestList (
  75. IN PMOVE_LISTW List
  76. );
  77. INT
  78. pCountTreeNodes (
  79. IN PMOVE_LIST_GROUPW LengthGroup
  80. );
  81. INT
  82. pCountList (
  83. IN PMOVE_LISTW List,
  84. IN PMOVE_LIST_NODEW FromItem OPTIONAL
  85. );
  86. #endif
  87. BOOL
  88. pEnumFirstMoveListItem (
  89. OUT PMOVE_LIST_ENUMW EnumPtr,
  90. IN PMOVE_LISTW List
  91. );
  92. BOOL
  93. pEnumNextMoveListItem (
  94. OUT PMOVE_LIST_ENUMW EnumPtr
  95. );
  96. PMOVE_LISTW
  97. pAllocateMoveList (
  98. IN POOLHANDLE Pool
  99. )
  100. {
  101. PMOVE_LISTW moveList;
  102. moveList = (PMOVE_LISTW) PoolMemGetMemory (Pool, sizeof (MOVE_LISTW));
  103. if (!moveList) {
  104. return NULL;
  105. }
  106. ZeroMemory (moveList, sizeof (MOVE_LISTW));
  107. moveList->Pool = Pool;
  108. return moveList;
  109. }
  110. MOVELISTW
  111. AllocateMoveListW (
  112. IN POOLHANDLE Pool
  113. )
  114. {
  115. return (MOVELISTW) pAllocateMoveList (Pool);
  116. }
  117. PMOVE_LIST_GROUPW
  118. pGetMoveListGroup (
  119. IN OUT PMOVE_LISTW List,
  120. IN UINT SourceLength
  121. )
  122. /*++
  123. Routine Description:
  124. pGetMoveListGroup searches the move list for the structure that represents
  125. the specified length. If no structure is found, then a new structure is
  126. allocated and inserted in the reverse-length-sorted list.
  127. Arguments:
  128. List - Specifies the move list to search (as returned from pAllocateMoveList),
  129. receives updated pointers if an allocation occurred.
  130. SourceLength - Specifies the length of the source path, in WCHARs.
  131. Return Value:
  132. A pointer to the move list group.
  133. --*/
  134. {
  135. PMOVE_LIST_GROUPW thisGroup;
  136. PMOVE_LIST_GROUPW insertAfter;
  137. PMOVE_LIST_GROUPW insertBefore = NULL;
  138. UINT hash;
  139. //
  140. // Search the current list for SourceLength. List is sorted from biggest
  141. // to smallest.
  142. //
  143. hash = SourceLength % MOVE_LIST_HASH_BUCKETS;
  144. thisGroup = List->Buckets[hash];
  145. while (thisGroup) {
  146. if (thisGroup->SourceLength == SourceLength) {
  147. return thisGroup;
  148. }
  149. thisGroup = thisGroup->NextHash;
  150. }
  151. //
  152. // Not in hash table; locate insert position
  153. //
  154. thisGroup = List->HeadGroup;
  155. while (thisGroup) {
  156. if (thisGroup->SourceLength < SourceLength) {
  157. break;
  158. }
  159. insertBefore = thisGroup;
  160. thisGroup = thisGroup->Next;
  161. }
  162. insertAfter = insertBefore;
  163. insertBefore = thisGroup;
  164. MYASSERT (!insertAfter || (insertAfter->Next == insertBefore));
  165. //
  166. // Allocate a new item
  167. //
  168. thisGroup = (PMOVE_LIST_GROUPW) PoolMemGetMemory (List->Pool, sizeof (MOVE_LISTW));
  169. if (thisGroup) {
  170. //
  171. // Insert it into the linked list, then the hash table
  172. //
  173. thisGroup->SourceLength = SourceLength;
  174. thisGroup->SourceTreeRoot = NULL;
  175. thisGroup->Next = insertBefore; // insertBefore is on the right side
  176. if (insertAfter) {
  177. insertAfter->Next = thisGroup;
  178. } else {
  179. List->HeadGroup = thisGroup;
  180. }
  181. thisGroup->NextHash = List->Buckets[hash];
  182. List->Buckets[hash] = thisGroup;
  183. #ifdef MOVE_TEST
  184. thisGroup->ItemCount = 0;
  185. List->GroupCount += 1;
  186. #endif
  187. }
  188. return thisGroup;
  189. }
  190. INT
  191. pCompareBackwards (
  192. IN UINT Length,
  193. IN PCWSTR LeftString,
  194. IN PCWSTR RightString
  195. )
  196. {
  197. INT result = 0;
  198. PCWSTR start = LeftString;
  199. LeftString += Length;
  200. RightString += Length;
  201. MYASSERT (*LeftString == 0);
  202. MYASSERT (*RightString == 0);
  203. while (LeftString > start) {
  204. LeftString--;
  205. RightString--;
  206. result = (INT) towlower (*RightString) - (INT) towlower (*LeftString);
  207. if (result) {
  208. break;
  209. }
  210. }
  211. return result;
  212. }
  213. PMOVE_LIST_NODEW
  214. pFindNodeInTree (
  215. IN PMOVE_LIST_NODEW Root,
  216. IN UINT KeyLength,
  217. IN PCWSTR Key,
  218. OUT PMOVE_LIST_NODEW *Parent,
  219. OUT PINT WhichChild
  220. )
  221. /*++
  222. Routine Description:
  223. pFindNodeInTree searches the binary tree for the specified source or
  224. destination path.
  225. In the case of a source path, KeyLength is non-zero, and Key specifies the
  226. source path. All elements in the binary tree have equal length.
  227. In the case of a destination path, KeyLength is zero, and Key specifies the
  228. destination path. All destination paths are in the same binary tree,
  229. regardless of length.
  230. Arguments:
  231. Root - Specifies the root of the tree to search
  232. KeyLength - Specifies a non-zero wchar count of the characters in Key,
  233. excluding the terminator, or specifies zero for a destination path
  234. Key - Specifies the source or destination path to find
  235. Parent - Receives the pointer to the found node's parent, or NULL if the
  236. found node is the root of the tree. Receives an undefined value when a
  237. node is not found.
  238. WhichChild - Receives an indicator as to which child in Parent a new node
  239. should be inserted into.
  240. If the return value is non-NULL (a node was found), then WhichChild is
  241. set to zero.
  242. If the return value is NULL (a node was not found), then WhichChild is
  243. set to one of the following:
  244. < 0 - New node should be linked via Parent->Left
  245. > 0 - New node should be linked via Parent->Right
  246. 0 - New node is the root of the tree
  247. Return Value:
  248. A pointer to the found node, or NULL if the search key is not in the tree.
  249. --*/
  250. {
  251. PMOVE_LIST_NODEW thisNode;
  252. UINT linkageIndex;
  253. thisNode = Root;
  254. *Parent = NULL;
  255. *WhichChild = 0;
  256. linkageIndex = KeyLength ? SOURCE_LINKAGE : DESTINATION_LINKAGE;
  257. while (thisNode) {
  258. if (KeyLength) {
  259. *WhichChild = pCompareBackwards (KeyLength, thisNode->Source, Key);
  260. } else {
  261. *WhichChild = StringICompareW (Key, thisNode->Destination);
  262. }
  263. if (!(*WhichChild)) {
  264. return thisNode;
  265. }
  266. *Parent = thisNode;
  267. if (*WhichChild < 0) {
  268. thisNode = thisNode->Linkage[linkageIndex].Left;
  269. } else {
  270. thisNode = thisNode->Linkage[linkageIndex].Right;
  271. }
  272. }
  273. return NULL;
  274. }
  275. PMOVE_LIST_NODEW
  276. pFindDestination (
  277. IN PMOVE_LISTW List,
  278. IN PCWSTR Destination
  279. )
  280. {
  281. PMOVE_LIST_NODEW parent;
  282. INT compareResults;
  283. return pFindNodeInTree (
  284. List->DestinationTreeRoot,
  285. 0,
  286. Destination,
  287. &parent,
  288. &compareResults
  289. );
  290. }
  291. BOOL
  292. pInsertMovePairIntoEnabledGroup (
  293. IN PMOVE_LISTW List,
  294. IN PMOVE_LIST_GROUPW LengthGroup,
  295. IN PCWSTR Source,
  296. IN PCWSTR Destination
  297. )
  298. {
  299. PMOVE_LIST_NODEW node;
  300. PMOVE_LIST_NODEW srcParent;
  301. INT srcCompareResults;
  302. PMOVE_LIST_NODEW destNode;
  303. PMOVE_LIST_NODEW destParent;
  304. INT destCompareResults;
  305. #ifdef MOVE_TEST
  306. INT count = pCountTreeNodes (LengthGroup);
  307. #endif
  308. //
  309. // Check for duplicate dest
  310. //
  311. destNode = pFindNodeInTree (
  312. List->DestinationTreeRoot,
  313. 0,
  314. Destination,
  315. &destParent,
  316. &destCompareResults
  317. );
  318. if (destNode) {
  319. DEBUGMSGW_IF ((
  320. !StringIMatchW (Source, destNode->Source),
  321. DBG_WARNING,
  322. "Destination %s is already in the moved list for %s; ignoring duplicate",
  323. Destination,
  324. destNode->Source
  325. ));
  326. return FALSE;
  327. }
  328. //
  329. // Search the tree for an existing source/dest pair
  330. //
  331. MYASSERT (TcharCountW (Source) == LengthGroup->SourceLength);
  332. MYASSERT (LengthGroup->SourceLength > 0);
  333. node = pFindNodeInTree (
  334. LengthGroup->SourceTreeRoot,
  335. LengthGroup->SourceLength,
  336. Source,
  337. &srcParent,
  338. &srcCompareResults
  339. );
  340. if (node) {
  341. DEBUGMSGW ((
  342. DBG_WARNING,
  343. "Ignoring move of %s to %s because source is already moved to %s",
  344. Source,
  345. Destination,
  346. node->Destination
  347. ));
  348. return FALSE;
  349. }
  350. //
  351. // Not in the tree; add it
  352. //
  353. node = (PMOVE_LIST_NODEW) PoolMemGetMemory (List->Pool, sizeof (MOVE_LIST_NODEW));
  354. if (!node) {
  355. return FALSE;
  356. }
  357. MYASSERT(Source);
  358. node->Source = PoolMemDuplicateStringW (List->Pool, Source);
  359. MYASSERT(Destination);
  360. node->Destination = PoolMemDuplicateStringW (List->Pool, Destination);
  361. node->FixedSource = node->Source;
  362. node->FixedDestination = node->Destination;
  363. //
  364. // Put source in binary tree
  365. //
  366. node->Linkage[SOURCE_LINKAGE].Left = NULL;
  367. node->Linkage[SOURCE_LINKAGE].Right = NULL;
  368. node->Linkage[SOURCE_LINKAGE].Parent = srcParent;
  369. if (!srcParent) {
  370. LengthGroup->SourceTreeRoot = node;
  371. } else if (srcCompareResults < 0) {
  372. MYASSERT (srcParent->Linkage[SOURCE_LINKAGE].Left == NULL);
  373. srcParent->Linkage[SOURCE_LINKAGE].Left = node;
  374. } else {
  375. MYASSERT (srcParent->Linkage[SOURCE_LINKAGE].Right == NULL);
  376. srcParent->Linkage[SOURCE_LINKAGE].Right = node;
  377. }
  378. //
  379. // Put dest in binary tree
  380. //
  381. node->Linkage[DESTINATION_LINKAGE].Left = NULL;
  382. node->Linkage[DESTINATION_LINKAGE].Right = NULL;
  383. node->Linkage[DESTINATION_LINKAGE].Parent = destParent;
  384. if (!destParent) {
  385. List->DestinationTreeRoot = node;
  386. } else if (destCompareResults < 0) {
  387. MYASSERT (destParent->Linkage[DESTINATION_LINKAGE].Left == NULL);
  388. destParent->Linkage[DESTINATION_LINKAGE].Left = node;
  389. } else {
  390. MYASSERT (destParent->Linkage[DESTINATION_LINKAGE].Right == NULL);
  391. destParent->Linkage[DESTINATION_LINKAGE].Right = node;
  392. }
  393. #ifdef MOVE_TEST
  394. //
  395. // Verify the sanity of the data structures
  396. //
  397. LengthGroup->ItemCount += 1;
  398. List->DestItemCount += 1;
  399. pTestList (List);
  400. if (count + 1 != pCountTreeNodes (LengthGroup)) {
  401. DebugBreak();
  402. }
  403. #endif
  404. return TRUE;
  405. }
  406. PMOVE_LIST_NODEW
  407. pFindLeftmostNode (
  408. IN PMOVE_LIST_NODEW Node,
  409. IN UINT LinkageIndex
  410. )
  411. {
  412. if (!Node) {
  413. return NULL;
  414. }
  415. while (Node->Linkage[LinkageIndex].Left) {
  416. Node = Node->Linkage[LinkageIndex].Left;
  417. }
  418. return Node;
  419. }
  420. PMOVE_LIST_NODEW
  421. pFindRightmostNode (
  422. IN PMOVE_LIST_NODEW Node,
  423. IN UINT LinkageIndex
  424. )
  425. {
  426. if (!Node) {
  427. return NULL;
  428. }
  429. while (Node->Linkage[LinkageIndex].Right) {
  430. Node = Node->Linkage[LinkageIndex].Right;
  431. }
  432. return Node;
  433. }
  434. PMOVE_LIST_NODEW
  435. pEnumFirstItemInTree (
  436. IN PMOVE_LIST_NODEW Root,
  437. IN UINT LinkageIndex
  438. )
  439. {
  440. if (!Root) {
  441. return NULL;
  442. }
  443. return pFindLeftmostNode (Root, LinkageIndex);
  444. }
  445. PMOVE_LIST_NODEW
  446. pEnumNextItemInTree (
  447. IN PMOVE_LIST_NODEW LastItem,
  448. IN UINT LinkageIndex
  449. )
  450. {
  451. PMOVE_LIST_NODEW nextItem;
  452. if (!LastItem) {
  453. return NULL;
  454. }
  455. if (LastItem->Linkage[LinkageIndex].Right) {
  456. return pFindLeftmostNode (
  457. LastItem->Linkage[LinkageIndex].Right,
  458. LinkageIndex
  459. );
  460. }
  461. //
  462. // Go up the tree. If the parent's left pointer is not the last
  463. // item, then we are going up from the right side, and we need
  464. // to continue going up. It is important to note that the test
  465. // is not (nextItem->Right == LastItem) because we need to
  466. // support continuation from a deleted node. A deleted node
  467. // will not match any of the parent's children. If the deleted
  468. // node has no right pointer, then we need to keep going up.
  469. //
  470. // If the enum item was deleted, then left and parent point
  471. // to the next node.
  472. //
  473. nextItem = LastItem->Linkage[LinkageIndex].Parent;
  474. if (nextItem != LastItem->Linkage[LinkageIndex].Left) {
  475. while (nextItem && nextItem->Linkage[LinkageIndex].Left != LastItem) {
  476. LastItem = nextItem;
  477. nextItem = LastItem->Linkage[LinkageIndex].Parent;
  478. }
  479. }
  480. return nextItem;
  481. }
  482. #ifdef MOVE_TEST
  483. INT
  484. pCountList (
  485. IN PMOVE_LISTW List,
  486. IN PMOVE_LIST_NODEW FromItem OPTIONAL
  487. )
  488. {
  489. MOVE_LIST_ENUMW e;
  490. INT count = 0;
  491. BOOL startCounting;
  492. BOOL next = TRUE;
  493. INT debug = 2;
  494. if (!FromItem) {
  495. startCounting = TRUE;
  496. } else {
  497. startCounting = FALSE;
  498. }
  499. //
  500. // Count items in the binary tree
  501. //
  502. if (pEnumFirstMoveListItem (&e, List)) {
  503. do {
  504. if (FromItem == e.Item) {
  505. startCounting = TRUE;
  506. }
  507. if (startCounting) {
  508. if (debug) {
  509. debug--;
  510. DEBUGMSGW ((DBG_VERBOSE, "%i: %s", debug, e.Item->Source));
  511. }
  512. count++;
  513. }
  514. } while (pEnumNextMoveListItem (&e));
  515. }
  516. return count;
  517. }
  518. INT
  519. pCountTreeNodes (
  520. IN PMOVE_LIST_GROUPW LengthGroup
  521. )
  522. {
  523. INT itemCount;
  524. PMOVE_LIST_NODEW thisNode;
  525. //
  526. // Count items in the binary tree
  527. //
  528. itemCount = 0;
  529. thisNode = pEnumFirstItemInTree (LengthGroup->SourceTreeRoot, SOURCE_LINKAGE);
  530. while (thisNode) {
  531. itemCount++;
  532. thisNode = pEnumNextItemInTree (thisNode, SOURCE_LINKAGE);
  533. }
  534. return itemCount;
  535. }
  536. VOID
  537. pTestDeleteAndEnum (
  538. IN PMOVE_LIST_GROUPW LengthGroup,
  539. IN PMOVE_LIST_NODEW DeletedNode
  540. )
  541. {
  542. BOOL startCounting = FALSE;
  543. INT nodes;
  544. INT nodes2;
  545. PMOVE_LIST_NODEW nextNode;
  546. PMOVE_LIST_NODEW firstNodeAfterDeletion;
  547. //
  548. // Count # of nodes after DeletedNode
  549. //
  550. firstNodeAfterDeletion = pEnumNextItemInTree (DeletedNode, SOURCE_LINKAGE);
  551. nextNode = firstNodeAfterDeletion;
  552. nodes = 0;
  553. while (nextNode) {
  554. nodes++;
  555. nextNode = pEnumNextItemInTree (nextNode, SOURCE_LINKAGE);
  556. }
  557. //
  558. // Reenumerate the whole tree and verify the same # of nodes remain
  559. //
  560. nodes2 = 0;
  561. nextNode = pEnumFirstItemInTree (LengthGroup->SourceTreeRoot, SOURCE_LINKAGE);
  562. while (nextNode) {
  563. if (nextNode == firstNodeAfterDeletion) {
  564. startCounting = TRUE;
  565. }
  566. if (startCounting) {
  567. nodes2++;
  568. }
  569. nextNode = pEnumNextItemInTree (nextNode, SOURCE_LINKAGE);
  570. }
  571. if (nodes != nodes2) {
  572. DebugBreak();
  573. }
  574. }
  575. VOID
  576. pTestLengthGroup (
  577. IN PMOVE_LIST_GROUPW LengthGroup
  578. )
  579. {
  580. UINT itemCount;
  581. PMOVE_LIST_NODEW thisNode;
  582. MYASSERT(LengthGroup);
  583. //
  584. // Count items in the binary tree
  585. //
  586. itemCount = 0;
  587. thisNode = pEnumFirstItemInTree (LengthGroup->SourceTreeRoot, SOURCE_LINKAGE);
  588. while (thisNode) {
  589. itemCount++;
  590. thisNode = pEnumNextItemInTree (thisNode, SOURCE_LINKAGE);
  591. }
  592. MYASSERT (itemCount == LengthGroup->ItemCount);
  593. }
  594. VOID
  595. pTestList (
  596. IN PMOVE_LISTW List
  597. )
  598. {
  599. UINT itemCount;
  600. UINT groupCount;
  601. PMOVE_LIST_NODEW thisNode;
  602. PMOVE_LIST_GROUPW lengthGroup;
  603. MYASSERT(List);
  604. groupCount = 0;
  605. lengthGroup = List->HeadGroup;
  606. while (lengthGroup) {
  607. groupCount++;
  608. MYASSERT (pGetMoveListGroup (List, lengthGroup->SourceLength) == lengthGroup);
  609. pTestLengthGroup (lengthGroup);
  610. lengthGroup = lengthGroup->Next;
  611. }
  612. MYASSERT (groupCount == List->GroupCount);
  613. itemCount = 0;
  614. thisNode = pEnumFirstItemInTree (List->DestinationTreeRoot, DESTINATION_LINKAGE);
  615. while (thisNode) {
  616. itemCount++;
  617. thisNode = pEnumNextItemInTree (thisNode, DESTINATION_LINKAGE);
  618. }
  619. MYASSERT (itemCount == List->DestItemCount);
  620. }
  621. #endif
  622. PMOVE_LIST_NODEW *
  623. pFindParentChildLinkage (
  624. IN PMOVE_LIST_NODEW Child,
  625. IN PMOVE_LIST_NODEW *RootPointer,
  626. IN UINT LinkageIndex
  627. )
  628. {
  629. PMOVE_LIST_NODEW parent;
  630. MYASSERT(Child);
  631. parent = Child->Linkage[LinkageIndex].Parent;
  632. if (!parent) {
  633. return RootPointer;
  634. }
  635. if (parent->Linkage[LinkageIndex].Left == Child) {
  636. return &(parent->Linkage[LinkageIndex].Left);
  637. }
  638. MYASSERT (parent->Linkage[LinkageIndex].Right == Child);
  639. return &(parent->Linkage[LinkageIndex].Right);
  640. }
  641. VOID
  642. pDeleteNodeFromBinaryTree (
  643. OUT PMOVE_LIST_NODEW *RootPointer,
  644. IN PMOVE_LIST_NODEW ItemToDelete,
  645. IN UINT LinkageIndex
  646. )
  647. {
  648. PMOVE_LIST_NODEW *parentChildLinkage;
  649. PMOVE_LIST_NODEW *swapNodeParentChildLinkage;
  650. PMOVE_LIST_NODEW swapNode;
  651. PMOVE_LIST_NODEW leftmostNode;
  652. PMOVE_LIST_NODEW nextEnumNode = NULL;
  653. PBINTREE_LINKAGE deleteItemLinkage;
  654. PBINTREE_LINKAGE leftLinkage;
  655. PBINTREE_LINKAGE rightLinkage;
  656. PBINTREE_LINKAGE swapLinkage;
  657. PBINTREE_LINKAGE leftmostLinkage;
  658. nextEnumNode = pEnumNextItemInTree (ItemToDelete, LinkageIndex);
  659. //
  660. // A node structure has multiple binary trees. We use the convention
  661. // of fooNode to represent the entire node structure, and fooLinkage
  662. // to represent just the left/right/parent structure for the tree
  663. // we are interested in. Kind of ugly, but necessary. A generalized
  664. // tree would not provide the optimum relationships.
  665. //
  666. //
  667. // Get the parent's link to the child, or the root pointer
  668. //
  669. parentChildLinkage = pFindParentChildLinkage (
  670. ItemToDelete,
  671. RootPointer,
  672. LinkageIndex
  673. );
  674. //
  675. // Remove the node from the tree. The complicated case is when we have a
  676. // node with two children. We attempt to move the children up as best as
  677. // we can.
  678. //
  679. deleteItemLinkage = &(ItemToDelete->Linkage[LinkageIndex]);
  680. if (deleteItemLinkage->Left && deleteItemLinkage->Right) {
  681. leftLinkage = &((deleteItemLinkage->Left)->Linkage[LinkageIndex]);
  682. rightLinkage = &((deleteItemLinkage->Right)->Linkage[LinkageIndex]);
  683. //
  684. // Node has left & right children. Search for a leaf node
  685. // that we can swap. We try to move items up as high as possible.
  686. //
  687. swapNode = pFindLeftmostNode (deleteItemLinkage->Right, LinkageIndex);
  688. swapLinkage = &(swapNode->Linkage[LinkageIndex]);
  689. if (swapLinkage->Right == NULL) {
  690. //
  691. // Found swapable node on the right side of ItemToDelete
  692. //
  693. MYASSERT (swapLinkage->Left == NULL);
  694. swapLinkage->Left = deleteItemLinkage->Left;
  695. leftLinkage->Parent = swapNode;
  696. if (swapNode != deleteItemLinkage->Right) {
  697. swapLinkage->Right = deleteItemLinkage->Right;
  698. rightLinkage->Parent = swapNode;
  699. }
  700. } else {
  701. //
  702. // Try to get a swapable node on the left side. If that
  703. // isn't possible, rechain the tree.
  704. //
  705. swapNode = pFindRightmostNode (deleteItemLinkage->Left, LinkageIndex);
  706. swapLinkage = &(swapNode->Linkage[LinkageIndex]);
  707. MYASSERT (swapLinkage->Right == NULL);
  708. swapLinkage->Right = deleteItemLinkage->Right;
  709. rightLinkage->Parent = swapNode;
  710. leftmostNode = pFindLeftmostNode (swapLinkage->Left, LinkageIndex);
  711. if (leftmostNode && leftmostNode != deleteItemLinkage->Left) {
  712. leftmostLinkage = &(leftmostNode->Linkage[LinkageIndex]);
  713. MYASSERT (leftmostLinkage->Left == NULL);
  714. leftmostLinkage->Left = deleteItemLinkage->Left;
  715. leftLinkage->Parent = leftmostNode;
  716. } else if (!leftmostNode) {
  717. MYASSERT (swapLinkage->Left == NULL);
  718. swapLinkage->Left = deleteItemLinkage->Left;
  719. leftLinkage->Parent = swapNode;
  720. }
  721. }
  722. swapNodeParentChildLinkage = pFindParentChildLinkage (
  723. swapNode,
  724. RootPointer,
  725. LinkageIndex
  726. );
  727. *swapNodeParentChildLinkage = NULL;
  728. } else if (deleteItemLinkage->Left) {
  729. //
  730. // Node has only a left child. Replace ItemToDelete with left child.
  731. //
  732. swapNode = deleteItemLinkage->Left;
  733. } else {
  734. //
  735. // Node has a right child or no children. Replace ItemToDelete
  736. // with right child if it is present.
  737. //
  738. swapNode = deleteItemLinkage->Right;
  739. }
  740. *parentChildLinkage = swapNode;
  741. if (swapNode) {
  742. swapLinkage = &(swapNode->Linkage[LinkageIndex]);
  743. swapLinkage->Parent = deleteItemLinkage->Parent;
  744. }
  745. //
  746. // Fix delete node pointers so enumeration can continue without interruption.
  747. // If nextEnumNode is NULL, enumeration will end.
  748. //
  749. deleteItemLinkage->Parent = nextEnumNode;
  750. deleteItemLinkage->Right = NULL;
  751. deleteItemLinkage->Left = nextEnumNode;
  752. }
  753. VOID
  754. pDeleteMovePairFromGroup (
  755. IN PMOVE_LISTW List,
  756. IN PMOVE_LIST_GROUPW LengthGroup,
  757. IN PMOVE_LIST_NODEW ItemToDelete
  758. )
  759. {
  760. pDeleteNodeFromBinaryTree (
  761. &(LengthGroup->SourceTreeRoot),
  762. ItemToDelete,
  763. SOURCE_LINKAGE
  764. );
  765. pDeleteNodeFromBinaryTree (
  766. &(List->DestinationTreeRoot),
  767. ItemToDelete,
  768. DESTINATION_LINKAGE
  769. );
  770. #ifdef MOVE_TEST
  771. LengthGroup->ItemCount -= 1;
  772. List->DestItemCount -= 1;
  773. pTestList (List);
  774. #endif
  775. }
  776. BOOL
  777. pEnumNextMoveListItem (
  778. IN OUT PMOVE_LIST_ENUMW EnumPtr
  779. )
  780. {
  781. MYASSERT(EnumPtr);
  782. for (;;) {
  783. switch (EnumPtr->State) {
  784. case BEGIN_LIST:
  785. if (!EnumPtr->ThisList) {
  786. return FALSE;
  787. }
  788. EnumPtr->LengthGroup = (EnumPtr->ThisList)->HeadGroup;
  789. EnumPtr->State = BEGIN_LENGTH_GROUP;
  790. break;
  791. case BEGIN_LENGTH_GROUP:
  792. if (!EnumPtr->LengthGroup) {
  793. EnumPtr->State = ENUM_NEXT_LIST;
  794. } else {
  795. EnumPtr->Item = pEnumFirstItemInTree (
  796. EnumPtr->LengthGroup->SourceTreeRoot,
  797. SOURCE_LINKAGE
  798. );
  799. EnumPtr->State = ENUM_RETURN_ITEM;
  800. }
  801. break;
  802. case ENUM_NEXT_ITEM:
  803. MYASSERT (EnumPtr->LengthGroup);
  804. MYASSERT (EnumPtr->Item);
  805. EnumPtr->Item = pEnumNextItemInTree (
  806. EnumPtr->Item,
  807. SOURCE_LINKAGE
  808. );
  809. EnumPtr->State = ENUM_RETURN_ITEM;
  810. break;
  811. case ENUM_RETURN_ITEM:
  812. if (EnumPtr->Item) {
  813. EnumPtr->State = ENUM_NEXT_ITEM;
  814. return TRUE;
  815. }
  816. EnumPtr->State = ENUM_NEXT_LENGTH_GROUP;
  817. break;
  818. case ENUM_NEXT_LENGTH_GROUP:
  819. MYASSERT (EnumPtr->LengthGroup);
  820. EnumPtr->LengthGroup = (EnumPtr->LengthGroup)->Next;
  821. EnumPtr->State = BEGIN_LENGTH_GROUP;
  822. break;
  823. case ENUM_NEXT_LIST:
  824. MYASSERT (EnumPtr->ThisList);
  825. EnumPtr->ThisList = (EnumPtr->ThisList)->NextChainedList;
  826. EnumPtr->State = BEGIN_LIST;
  827. break;
  828. }
  829. }
  830. }
  831. BOOL
  832. pEnumFirstMoveListItem (
  833. OUT PMOVE_LIST_ENUMW EnumPtr,
  834. IN PMOVE_LISTW List
  835. )
  836. {
  837. MYASSERT(EnumPtr);
  838. if (!List) {
  839. return FALSE;
  840. }
  841. ZeroMemory (EnumPtr, sizeof (MOVE_LIST_ENUMW));
  842. EnumPtr->ThisList = List;
  843. EnumPtr->State = BEGIN_LIST;
  844. return pEnumNextMoveListItem (EnumPtr);
  845. }
  846. BOOL
  847. pInsertMoveIntoListWorker (
  848. IN PMOVE_LISTW List,
  849. IN PCWSTR Source,
  850. IN PCWSTR Destination
  851. )
  852. /*++
  853. Routine Description:
  854. pInsertMoveIntoListWorker adds a source/dest move pair to a list and orders
  855. the list by the length of source (from biggest to smallest). This ensures
  856. nesting is taken care of properly.
  857. The move list is stored in the caller-owned pool. Before calling
  858. InsertMoveIntoList for the first time, the caller must first create a pool,
  859. and allocate a list from AllocateMoveListW.
  860. After the list is no longer needed, the caller frees all resources of the
  861. list by simply destroying the pool.
  862. Arguments:
  863. List - Specifies the list to insert into
  864. Source - Specifies the source path
  865. Destination - Specifies the destination path
  866. Return Value:
  867. TRUE if successful, FALSE if memory allocation failed, or if source is already
  868. in the list.
  869. --*/
  870. {
  871. PMOVE_LIST_GROUPW lengthGroup;
  872. UINT sourceLen;
  873. MOVE_LIST_ENUMW e;
  874. MYASSERT(Source);
  875. sourceLen = TcharCountW (Source);
  876. lengthGroup = pGetMoveListGroup (List, sourceLen);
  877. if (!lengthGroup) {
  878. return FALSE;
  879. }
  880. //
  881. // Insert pair into the list
  882. //
  883. if (!pInsertMovePairIntoEnabledGroup (
  884. List,
  885. lengthGroup,
  886. Source,
  887. Destination
  888. )) {
  889. return FALSE;
  890. }
  891. return TRUE;
  892. }
  893. BOOL
  894. InsertMoveIntoListW (
  895. IN MOVELISTW List,
  896. IN PCWSTR Source,
  897. IN PCWSTR Destination
  898. )
  899. {
  900. return pInsertMoveIntoListWorker ((PMOVE_LISTW) List, Source, Destination);
  901. }
  902. VOID
  903. pChainLists (
  904. IN PMOVE_LISTW LeftList,
  905. IN PMOVE_LISTW RightList
  906. )
  907. {
  908. MYASSERT(LeftList);
  909. while (LeftList->NextChainedList) {
  910. LeftList = LeftList->NextChainedList;
  911. MYASSERT(LeftList);
  912. }
  913. LeftList->NextChainedList = RightList;
  914. }
  915. PMOVE_LISTW
  916. pRemoveMoveListOverlapWorker (
  917. IN PMOVE_LISTW List,
  918. IN BOOL SkipPrePostLists
  919. )
  920. /*++
  921. Routine Description:
  922. pRemoveMoveListOverlapWorker searches the length-sorted move list and
  923. discards moves that are taken care of through moves of a parent. For
  924. example, consider the following moves:
  925. 1. c:\a\b\c -> c:\x\c
  926. 2. c:\a\b -> c:\x
  927. In this case, line (1) is not needed, because it is implicit in line (2),
  928. even if line (1) is a file but line (2) is a subdirectory.
  929. This routine relies on the enumeration order. An item within that order
  930. is compared against items further down in the order.
  931. If there is a case such as:
  932. 1. c:\a\b\c -> c:\x\q
  933. 2. c:\a\b -> c:\x
  934. This will produce an error, because the move cannot be executed. Line (1)
  935. would have to be moved first, but because it creates the destination of
  936. line (2), the second move will fail.
  937. Arguments:
  938. List - Specifies the move list to check
  939. SkipPrePostLists - Specifies TRUE if the temp move algorithm should be
  940. skipped; FALSE normally.
  941. Return Value:
  942. The new move list that has overlaps removed. The caller must use the return
  943. value instead of the input List.
  944. --*/
  945. {
  946. PMOVE_LIST_NODEW currentNode;
  947. PMOVE_LIST_NODEW checkNode;
  948. PMOVE_LIST_NODEW collisionNode;
  949. UINT destLength;
  950. UINT collisionSrcLength = 0;
  951. BOOL disableThisPath;
  952. BOOL done;
  953. PCWSTR srcSubPath;
  954. PCWSTR destSubPath;
  955. PMOVE_LISTW preMoveList = NULL;
  956. PMOVE_LISTW postMoveList = NULL;
  957. WCHAR tempPathRoot[] = L"?:\\$tmp$dir.@xx";
  958. PCWSTR tempPath;
  959. PCWSTR subDir;
  960. PCWSTR collisionSrc;
  961. MOVE_LIST_ENUMW listEnum;
  962. PWSTR tempDest;
  963. PWSTR p;
  964. UINT currentNodeSrcLen;
  965. INT compareResult;
  966. PMOVE_LIST_GROUPW lengthGroup;
  967. BOOL currentMovedFirst;
  968. //
  969. // PASS 1: Minimize the list by eliminating nested moves
  970. //
  971. if (pEnumFirstMoveListItem (&listEnum, List)) {
  972. do {
  973. currentNode = listEnum.Item;
  974. currentNodeSrcLen = listEnum.LengthGroup->SourceLength;
  975. collisionNode = NULL;
  976. //
  977. // Locate a node that is further down the list but is
  978. // actually a parent of currentNode's destination
  979. //
  980. // That is, search for the following case:
  981. //
  982. // collisionNode: c:\a -> c:\x
  983. // currentNode: c:\b -> c:\x\y
  984. //
  985. // collisionNode is moved ahead of currentNode.
  986. //
  987. disableThisPath = FALSE;
  988. done = FALSE;
  989. MYASSERT(currentNode->Destination);
  990. tempDest = DuplicatePathStringW (currentNode->Destination, 0);
  991. p = wcschr (tempDest + 3, L'\\');
  992. while (p) {
  993. *p = 0;
  994. __try {
  995. checkNode = pFindDestination (List, tempDest);
  996. if (!checkNode || (checkNode == currentNode)) {
  997. __leave;
  998. }
  999. currentMovedFirst = TRUE;
  1000. MYASSERT(checkNode->Source);
  1001. collisionSrcLength = TcharCountW (checkNode->Source);
  1002. if (collisionSrcLength > currentNodeSrcLen) {
  1003. //
  1004. // checkNode is moved before currentNode
  1005. //
  1006. currentMovedFirst = FALSE;
  1007. } else if (currentNodeSrcLen == collisionSrcLength) {
  1008. //
  1009. // Need to compare source paths to see which one comes
  1010. // first. If the currentNode is alphabetically ahead of
  1011. // the collision, then its move will happen first.
  1012. //
  1013. compareResult = pCompareBackwards (
  1014. collisionSrcLength,
  1015. currentNode->Source,
  1016. checkNode->Source
  1017. );
  1018. if (compareResult < 0) {
  1019. currentMovedFirst = FALSE;
  1020. }
  1021. }
  1022. //
  1023. // currentNode's destination is a child of checkNode. We
  1024. // need to make sure currentNode's destination is not going
  1025. // to exist, or we need to ignore currentNode if it is
  1026. // implicitly handled by checkNode.
  1027. //
  1028. if (currentMovedFirst) {
  1029. //
  1030. // Record collision.
  1031. //
  1032. // currentNode->Source is moved ahead of checkNode->Source
  1033. // currentNode->Destination is a child of checkNode->Destination
  1034. //
  1035. if (!collisionNode) {
  1036. collisionNode = checkNode;
  1037. }
  1038. MYASSERT (TcharCountW (checkNode->Source) <= TcharCountW (currentNode->Source));
  1039. //
  1040. // If the subpath of currentNode's source is the same as its
  1041. // dest, and the base source path is the same for both,
  1042. // then remove currentNode. That is, we are testing for this case:
  1043. //
  1044. // currentNode: c:\a\y -> c:\x\y
  1045. // checkNode: c:\a -> c:\x
  1046. //
  1047. MYASSERT (currentNodeSrcLen == TcharCountW (currentNode->Source));
  1048. MYASSERT (collisionSrcLength == TcharCountW (checkNode->Source));
  1049. if (StringIMatchTcharCountW (
  1050. currentNode->Source,
  1051. checkNode->Source,
  1052. collisionSrcLength
  1053. )) {
  1054. if (currentNode->Source[collisionSrcLength] == L'\\') {
  1055. //
  1056. // Now we know currentNode->Source is a child of
  1057. // checkNode->Source.
  1058. //
  1059. destLength = TcharCountW (checkNode->Destination);
  1060. srcSubPath = currentNode->Source + collisionSrcLength;
  1061. destSubPath = currentNode->Destination + destLength;
  1062. if (StringIMatchW (srcSubPath, destSubPath)) {
  1063. //
  1064. // Now we know that the sub path is identical.
  1065. // The move in currentNode is handled implicitly
  1066. // by checkNode, so we'll skip currentNode.
  1067. //
  1068. disableThisPath = TRUE;
  1069. done = TRUE;
  1070. __leave;
  1071. }
  1072. }
  1073. }
  1074. } else if (!SkipPrePostLists) {
  1075. MYASSERT (!currentMovedFirst);
  1076. if (!StringIPrefixW (currentNode->Source + 3, L"user~tmp.@0") &&
  1077. !StringIPrefixW (currentNode->Destination + 3, L"user~tmp.@0")
  1078. ) {
  1079. //
  1080. // We need to fix the case where the second destination is
  1081. // nested under the first. That is, currentNode->Destination
  1082. // is a subdir of checkNode->Destination.
  1083. //
  1084. // This is used for the case where:
  1085. //
  1086. // checkNode: c:\a -> c:\x
  1087. // currentNode: c:\b -> c:\x\y
  1088. //
  1089. // We must ensure that c:\a\y is not present for move 2.
  1090. // Therefore, we add 2 additional move operations:
  1091. //
  1092. // c:\a\y -> c:\t\a\y
  1093. // c:\t\a\y -> c:\a\y
  1094. //
  1095. // This moves the collision out of the way, so that the parent
  1096. // can be moved, and then moves the folder back to its original
  1097. // location.
  1098. //
  1099. // The temp subdirectories for shell folders (user~tmp.@0?) are
  1100. // deliberatly ignored, because by definition they don't have
  1101. // collisions.
  1102. //
  1103. DEBUGMSGW ((
  1104. DBG_WARNING,
  1105. "Destination order collision:\n"
  1106. " Source: %s\n"
  1107. " Dest: %s\n"
  1108. " Collides with src: %s\n"
  1109. " Collides with dest: %s",
  1110. currentNode->Source,
  1111. currentNode->Destination,
  1112. checkNode->Source,
  1113. checkNode->Destination
  1114. ));
  1115. //
  1116. // compute pointer to subdir 'y' from c:\x\y
  1117. //
  1118. MYASSERT(checkNode->Destination);
  1119. destLength = TcharCountW (checkNode->Destination);
  1120. destSubPath = currentNode->Destination + destLength;
  1121. MYASSERT (*destSubPath == L'\\'); // this is because we tested by cutting at wacks above
  1122. destSubPath++;
  1123. MYASSERT (*destSubPath);
  1124. //
  1125. // build the path c:\a\y
  1126. //
  1127. MYASSERT(checkNode->Source);
  1128. collisionSrc = JoinPathsW (checkNode->Source, destSubPath);
  1129. //
  1130. // build the path c:\t\a\y
  1131. //
  1132. tempPathRoot[0] = currentNode->Destination[0];
  1133. subDir = wcschr (collisionSrc, L'\\');
  1134. MYASSERT (subDir);
  1135. subDir++;
  1136. MYASSERT (*subDir); // we should not ever move a root dir
  1137. tempPath = JoinPathsW (tempPathRoot, subDir);
  1138. //
  1139. // move c:\a\y (might not exist) to c:\t\a\y, then
  1140. // reverse the move
  1141. //
  1142. DEBUGMSGW ((
  1143. DBG_WARNING,
  1144. "Avoiding collision problems by deliberately not moving %s",
  1145. collisionSrc
  1146. ));
  1147. if (!preMoveList) {
  1148. preMoveList = pAllocateMoveList (List->Pool);
  1149. postMoveList = pAllocateMoveList (List->Pool);
  1150. }
  1151. if (preMoveList) {
  1152. pInsertMoveIntoListWorker (
  1153. preMoveList,
  1154. collisionSrc,
  1155. tempPath
  1156. );
  1157. pInsertMoveIntoListWorker (
  1158. postMoveList,
  1159. tempPath,
  1160. collisionSrc
  1161. );
  1162. }
  1163. FreePathStringW (collisionSrc);
  1164. FreePathStringW (tempPath);
  1165. }
  1166. }
  1167. }
  1168. __finally {
  1169. MYASSERT (TRUE); // workaround for debugging
  1170. }
  1171. if (done) {
  1172. break;
  1173. }
  1174. *p = L'\\';
  1175. p = wcschr (p + 1, L'\\');
  1176. }
  1177. FreePathStringW (tempDest);
  1178. if (disableThisPath) {
  1179. //
  1180. // Remove currentNode from the list
  1181. //
  1182. MYASSERT (collisionNode);
  1183. DEBUGMSGW ((
  1184. DBG_VERBOSE,
  1185. "Ignoring contained move:\n"
  1186. " Source: %s\n"
  1187. " Dest: %s\n"
  1188. " Contained src: %s\n"
  1189. " Contained dest: %s",
  1190. currentNode->Source,
  1191. currentNode->Destination,
  1192. collisionNode->Source,
  1193. collisionNode->Destination
  1194. ));
  1195. lengthGroup = pGetMoveListGroup (List, currentNodeSrcLen);
  1196. pDeleteMovePairFromGroup (List, lengthGroup, currentNode);
  1197. }
  1198. } while (pEnumNextMoveListItem (&listEnum));
  1199. }
  1200. //
  1201. // PASS 2: After list is minimized, correct order issues, so that
  1202. // all moves can succeed.
  1203. //
  1204. if (pEnumFirstMoveListItem (&listEnum, List)) {
  1205. do {
  1206. currentNode = listEnum.Item;
  1207. MYASSERT(currentNode->FixedSource);
  1208. currentNodeSrcLen = TcharCountW (currentNode->FixedSource);
  1209. MYASSERT(currentNode->FixedDestination);
  1210. destLength = TcharCountW (currentNode->FixedDestination);
  1211. //
  1212. // Locate a node that is further down the list but is actually a
  1213. // parent of currentNode's destination
  1214. //
  1215. // That is, search for the following case:
  1216. //
  1217. // checkNode: c:\a -> c:\x
  1218. // currentNode: c:\b -> c:\x\y
  1219. //
  1220. // checkNode is moved ahead of currentNode.
  1221. //
  1222. done = FALSE;
  1223. tempDest = DuplicatePathStringW (currentNode->FixedDestination, 0);
  1224. p = wcschr (tempDest + 3, L'\\');
  1225. while (p) {
  1226. *p = 0;
  1227. __try {
  1228. //
  1229. // Find destination that is created ahead of currentNode's dest
  1230. //
  1231. checkNode = pFindDestination (List, tempDest);
  1232. if (!checkNode || (checkNode == currentNode)) {
  1233. __leave;
  1234. }
  1235. if (destLength <= TcharCountW (checkNode->FixedDestination)) {
  1236. __leave;
  1237. }
  1238. currentMovedFirst = TRUE;
  1239. collisionSrcLength = TcharCountW (checkNode->FixedSource);
  1240. if (collisionSrcLength > currentNodeSrcLen) {
  1241. currentMovedFirst = FALSE;
  1242. } else if (currentNodeSrcLen == collisionSrcLength) {
  1243. compareResult = pCompareBackwards (
  1244. collisionSrcLength,
  1245. currentNode->FixedSource,
  1246. checkNode->FixedSource
  1247. );
  1248. if (compareResult < 0) {
  1249. currentMovedFirst = FALSE;
  1250. }
  1251. }
  1252. if (currentMovedFirst) {
  1253. MYASSERT (TcharCountW (checkNode->FixedSource) <= TcharCountW (currentNode->FixedSource));
  1254. //
  1255. // We found a move contradiction, such as the following...
  1256. //
  1257. // currentNode: c:\a -> c:\x\y
  1258. // checkNode: c:\b -> c:\x
  1259. //
  1260. // or
  1261. //
  1262. // currentNode: c:\b\a -> c:\x\y
  1263. // checkNode: c:\b -> c:\x
  1264. //
  1265. // ...so we must reverse the order of the moves. This is done
  1266. // by swapping the strings. We have a separate set of pointers,
  1267. // so that the binary tree properties are not disturbed.
  1268. //
  1269. currentNode->FixedSource = checkNode->Source;
  1270. currentNode->FixedDestination = checkNode->Destination;
  1271. checkNode->FixedSource = currentNode->Source;
  1272. checkNode->FixedDestination = currentNode->Destination;
  1273. DEBUGMSGW ((
  1274. DBG_WARNING,
  1275. "Source order and dest order contradict each other. Fixing by reversing the order to:\n\n"
  1276. "%s -> %s\n"
  1277. "- before -\n"
  1278. "%s -> %s",
  1279. currentNode->FixedSource,
  1280. currentNode->FixedDestination,
  1281. checkNode->FixedSource,
  1282. checkNode->FixedDestination
  1283. ));
  1284. currentNodeSrcLen = collisionSrcLength;
  1285. FreePathStringW (tempDest);
  1286. tempDest = DuplicatePathStringW (currentNode->FixedDestination, 0);
  1287. destLength = TcharCountW (currentNode->FixedDestination);
  1288. p = wcschr (tempDest, L'\\');
  1289. if (!p) {
  1290. MYASSERT (FALSE);
  1291. done = TRUE;
  1292. __leave;
  1293. }
  1294. }
  1295. }
  1296. __finally {
  1297. }
  1298. if (done) {
  1299. break;
  1300. }
  1301. *p = L'\\';
  1302. p = wcschr (p + 1, L'\\');
  1303. }
  1304. FreePathStringW (tempDest);
  1305. } while (pEnumNextMoveListItem (&listEnum));
  1306. }
  1307. //
  1308. // If we have a collision list, put the pre-moves at the head, and the
  1309. // post-moves at the tail. This leaves the list out of order from the
  1310. // point of view of longest to shortest source, so no additional
  1311. // add/removes should be done.
  1312. //
  1313. if (preMoveList) {
  1314. MYASSERT (postMoveList);
  1315. preMoveList = pRemoveMoveListOverlapWorker (preMoveList, TRUE);
  1316. postMoveList = pRemoveMoveListOverlapWorker (postMoveList, TRUE);
  1317. pChainLists (preMoveList, List);
  1318. pChainLists (List, postMoveList);
  1319. List = preMoveList;
  1320. }
  1321. return List;
  1322. }
  1323. MOVELISTW
  1324. RemoveMoveListOverlapW (
  1325. IN MOVELISTW List
  1326. )
  1327. {
  1328. return (MOVELISTW) pRemoveMoveListOverlapWorker ((PMOVE_LISTW) List, FALSE);
  1329. }
  1330. BOOL
  1331. pOutputMoveListWorker (
  1332. IN HANDLE File,
  1333. IN PMOVE_LISTW List, OPTIONAL
  1334. IN BOOL AddNestedMoves
  1335. )
  1336. /*++
  1337. Routine Description:
  1338. OutputMoveList writes every move pair in the specified list to the file
  1339. handle specified. The output is a UNICODE text file.
  1340. Arguments:
  1341. File - Specifies the file handle to write to
  1342. List - Specifies the list to output
  1343. AddNestedMoves - Specifies TRUE if the move list should contain extra
  1344. entries to ensure the move list records all subdirectories,
  1345. or FALSE if the move list should be the minimum list.
  1346. Return Value:
  1347. TRUE if the file was written, FALSE if an error occurred.
  1348. --*/
  1349. {
  1350. MOVE_LIST_ENUMW e;
  1351. DWORD dontCare;
  1352. HASHTABLE sourceMoveTable = NULL;
  1353. PCWSTR src;
  1354. PCWSTR dest;
  1355. TREE_ENUMW unicodeEnum;
  1356. PMOVE_LIST_NODEW node;
  1357. if (pEnumFirstMoveListItem (&e, List)) {
  1358. if (AddNestedMoves) {
  1359. sourceMoveTable = HtAllocW();
  1360. }
  1361. //
  1362. // Write UNICODE signature
  1363. //
  1364. // Do not write as a string. FE is a lead byte.
  1365. //
  1366. if (!WriteFile (File, "\xff\xfe", 2, &dontCare, NULL)) {
  1367. return FALSE;
  1368. }
  1369. do {
  1370. node = e.Item;
  1371. if (!WriteFile (File, node->FixedSource, ByteCountW (node->FixedSource), &dontCare, NULL)) {
  1372. return FALSE;
  1373. }
  1374. if (!WriteFile (File, L"\r\n", 4, &dontCare, NULL)) {
  1375. return FALSE;
  1376. }
  1377. if (!WriteFile (File, node->FixedDestination, ByteCountW (node->FixedDestination), &dontCare, NULL)) {
  1378. return FALSE;
  1379. }
  1380. if (!WriteFile (File, L"\r\n", 4, &dontCare, NULL)) {
  1381. return FALSE;
  1382. }
  1383. if (sourceMoveTable) {
  1384. HtAddStringW (sourceMoveTable, node->FixedSource);
  1385. }
  1386. if (AddNestedMoves) {
  1387. //
  1388. // We assume by implementation that this is only used on NT.
  1389. // If Win9x support is needed, this code would have to use
  1390. // the ANSI file enumeration APIs.
  1391. //
  1392. MYASSERT (ISNT());
  1393. if (EnumFirstFileInTreeW (&unicodeEnum, node->FixedSource, NULL, FALSE)) {
  1394. do {
  1395. src = unicodeEnum.FullPath;
  1396. if (unicodeEnum.Directory) {
  1397. //
  1398. // Skip previously processed trees
  1399. //
  1400. if (HtFindStringW (sourceMoveTable, src)) {
  1401. AbortEnumCurrentDirW (&unicodeEnum);
  1402. continue;
  1403. }
  1404. }
  1405. //
  1406. // Move subdirectory and files
  1407. //
  1408. dest = JoinPathsW (node->FixedDestination, unicodeEnum.SubPath);
  1409. if (!WriteFile (File, src, ByteCountW (src), &dontCare, NULL)) {
  1410. return FALSE;
  1411. }
  1412. if (!WriteFile (File, L"\r\n", 4, &dontCare, NULL)) {
  1413. return FALSE;
  1414. }
  1415. if (!WriteFile (File, dest, ByteCountW (dest), &dontCare, NULL)) {
  1416. return FALSE;
  1417. }
  1418. if (!WriteFile (File, L"\r\n", 4, &dontCare, NULL)) {
  1419. return FALSE;
  1420. }
  1421. FreePathStringW (dest);
  1422. } while (EnumNextFileInTreeW (&unicodeEnum));
  1423. }
  1424. //
  1425. // NOTE: We do not record the nested moves in sourceMoveTable,
  1426. // because it is a waste of time & memory. All nesting
  1427. // should be taken care of by the sort order of the list.
  1428. //
  1429. }
  1430. } while (pEnumNextMoveListItem (&e));
  1431. }
  1432. HtFree (sourceMoveTable);
  1433. return TRUE;
  1434. }
  1435. BOOL
  1436. OutputMoveListW (
  1437. IN HANDLE File,
  1438. IN MOVELISTW List, OPTIONAL
  1439. IN BOOL AddNestedMoves
  1440. )
  1441. {
  1442. return pOutputMoveListWorker (File, (PMOVE_LISTW) List, AddNestedMoves);
  1443. }