Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4279 lines
111 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. userloop.c
  5. Abstract:
  6. This module implements a user loop that performs two fundamental operations:
  7. 1. Queue up instructions for user shell folder migration
  8. 2. Queue up instructions for registry migration
  9. The user loop also calls SysMig_MigrateUser, so special per-user code can be
  10. added easily.
  11. Author:
  12. Calin Negreanu (calinn) 15-Aug-1998 (complete redesign)
  13. Revision History:
  14. Ovidiu Temereanca (ovidiut) 18-May-1999 (support for shell folders as migration dirs)
  15. --*/
  16. #include "pch.h"
  17. #include "sysmigp.h"
  18. #include "merge.h"
  19. #define DBG_USERLOOP "User Loop"
  20. #define SHELL_FOLDER_FILTERS \
  21. DEFMAC(pSendToFilter) \
  22. DEFMAC(pDirRenameFilter) \
  23. DEFMAC(pRecordCacheFolders) \
  24. typedef enum {
  25. GLOBAL_INITIALIZE,
  26. PER_FOLDER_INITIALIZE,
  27. PROCESS_PATH,
  28. PER_FOLDER_TERMINATE,
  29. GLOBAL_TERMINATE
  30. } CALL_CONTEXT;
  31. #define SHELLFILTER_OK 0
  32. #define SHELLFILTER_SKIP_FILE 1
  33. #define SHELLFILTER_SKIP_DIRECTORY 2
  34. #define MAX_SHELLFOLDER_NAME 64
  35. typedef struct {
  36. IN PCTSTR FixedUserName; OPTIONAL
  37. IN PCTSTR ShellFolderIdentifier;
  38. IN OUT TCHAR TempSourcePath[MEMDB_MAX];
  39. IN OUT TCHAR DestinationPath[MEMDB_MAX];
  40. IN PCTSTR SrcRootPath;
  41. IN PCTSTR DestRootPath;
  42. IN OUT DWORD Attributes;
  43. IN OUT DWORD State;
  44. IN CALL_CONTEXT Context;
  45. } PROFILE_MERGE_DATA, *PPROFILE_MERGE_DATA;
  46. typedef DWORD(PROFILEMERGEFILTER_PROTOTYPE)(IN OUT PPROFILE_MERGE_DATA Data);
  47. typedef PROFILEMERGEFILTER_PROTOTYPE * PROFILEMERGEFILTER;
  48. typedef struct {
  49. PROFILEMERGEFILTER Fn;
  50. DWORD State;
  51. } SHELL_FOLDER_FILTER, *PSHELL_FOLDER_FILTER;
  52. #define DEFMAC(fn) PROFILEMERGEFILTER_PROTOTYPE fn;
  53. SHELL_FOLDER_FILTERS
  54. #undef DEFMAC
  55. #define DEFMAC(fn) {fn},
  56. static SHELL_FOLDER_FILTER g_Filters[] = {
  57. SHELL_FOLDER_FILTERS /* , */
  58. {NULL}
  59. };
  60. #undef DEFMAC
  61. #define MAP_FLAG_EXPAND 0x0001
  62. #define MAP_PARENT_FIELD 0x0002
  63. #define MAP_ARG_MUST_BE_ONE 0x0004
  64. #define MAP_ARG_MUST_BE_ZERO 0x0008
  65. #define MAP_RECORD_IN_MEMDB 0x0010
  66. #define MAP_REVERSE 0x0020
  67. #define MAP_FLAG_NONE 0
  68. typedef enum {
  69. DEFAULT_COMMON = 0x0001,
  70. DEFAULT_PER_USER = 0x0002,
  71. DEFAULT_ALT_COMMON = 0x0010,
  72. DEFAULT_ALT_PER_USER = 0x0020
  73. } WHICHDEFAULT;
  74. #define ANY_COMMON (DEFAULT_COMMON|DEFAULT_ALT_COMMON)
  75. #define ANY_PER_USER (DEFAULT_PER_USER|DEFAULT_ALT_PER_USER)
  76. #define ANY_DEFAULT (ANY_COMMON|ANY_PER_USER)
  77. #define SFSTRUCT(e) ((PSHELLFOLDER) e.dwValue)
  78. typedef struct _SHELLFOLDER {
  79. PCTSTR Name;
  80. PCTSTR FixedUserName;
  81. PCTSTR UserName;
  82. PCTSTR SrcPath;
  83. PCTSTR DestPath;
  84. PCTSTR TempPath;
  85. BOOL CanBeCollapsed;
  86. BOOL MergedIntoOtherShellFolder;
  87. BOOL SourceExists;
  88. struct _SHELLFOLDER *Next;
  89. } SHELLFOLDER, *PSHELLFOLDER;
  90. PSHELLFOLDER g_ShellFolders = NULL;
  91. POOLHANDLE g_SFPool = NULL;
  92. PCTSTR g_UserProfileRoot;
  93. UINT g_SfSequencer = 0;
  94. DWORD g_TotalUsers = 0;
  95. HASHTABLE g_SkippedTable;
  96. HASHTABLE g_MassiveDirTable;
  97. HASHTABLE g_PreservedSfTable;
  98. HASHTABLE g_DirRenameTable;
  99. HASHTABLE g_CollapseRestrictions;
  100. PMAPSTRUCT g_ShortNameMap;
  101. PMAPSTRUCT g_SfRenameMap;
  102. PMAPSTRUCT g_DefaultDirMap;
  103. PMAPSTRUCT g_AlternateCommonDirMap;
  104. PMAPSTRUCT g_AlternatePerUserDirMap;
  105. PMAPSTRUCT g_DefaultShortDirMap;
  106. PMAPSTRUCT g_CommonFromPerUserMap;
  107. PMAPSTRUCT g_PerUserFromCommonMap;
  108. GROWLIST g_CollisionPriorityList;
  109. PMAPSTRUCT g_CacheShellFolders; // used by the HTML file edit helper in migapp\helpers.c
  110. BOOL
  111. pIsPerUserWanted (
  112. IN PCTSTR ShellFolderTag
  113. );
  114. VOID
  115. MsgSettingsIncomplete (
  116. IN PCTSTR UserDatPath,
  117. IN PCTSTR UserName,
  118. IN BOOL CompletelyBusted
  119. )
  120. /*++
  121. Routine Description:
  122. MsgSettingsIncomplete adds a message to the incompatibility
  123. report when a user is found that cannot be migrated.
  124. Arguments:
  125. UserDatPath - Specifies the location of the invalid user's registry
  126. UserName - Specifies the name of the bad user
  127. CompletelyBusted - Specifies TRUE if the bad user cannot be migrated at
  128. all, or FALSE if only their shell settings are damaged.
  129. Return Value:
  130. none
  131. --*/
  132. {
  133. PCTSTR MsgGroup = NULL;
  134. PCTSTR SubGroup = NULL;
  135. PCTSTR RootGroup = NULL;
  136. PCTSTR tempRoot = NULL;
  137. PCTSTR NoUserName = NULL;
  138. MYASSERT(UserDatPath);
  139. //
  140. // Sanitize user name
  141. //
  142. __try {
  143. NoUserName = GetStringResource (MSG_REG_SETTINGS_EMPTY_USER);
  144. RootGroup = GetStringResource (MSG_INSTALL_NOTES_ROOT);
  145. SubGroup = GetStringResource (CompletelyBusted ?
  146. MSG_REG_SETTINGS_SUBGROUP :
  147. MSG_SHELL_SETTINGS_SUBGROUP
  148. );
  149. if (!NoUserName || !RootGroup || !SubGroup) {
  150. MYASSERT (FALSE);
  151. __leave;
  152. }
  153. if (!UserName || !UserName[0]) {
  154. UserName = NoUserName;
  155. }
  156. //
  157. // Build Install Notes\User Accounts\User Name
  158. //
  159. tempRoot = JoinPaths (RootGroup, SubGroup);
  160. MsgGroup = JoinPaths (tempRoot, UserName);
  161. FreePathString (tempRoot);
  162. //
  163. // Send message to report, and turn off all other messages for the user
  164. // account.
  165. //
  166. MsgMgr_ObjectMsg_Add (UserDatPath, MsgGroup, S_EMPTY);
  167. HandleObject (UserName, TEXT("UserName"));
  168. }
  169. __finally {
  170. //
  171. // Clean up
  172. //
  173. FreeStringResource (NoUserName);
  174. FreeStringResource (RootGroup);
  175. FreeStringResource (SubGroup);
  176. FreePathString (MsgGroup);
  177. }
  178. }
  179. HASHTABLE
  180. pCreateSfList (
  181. IN PCTSTR SectionName,
  182. IN BOOL ExpandEnvVars
  183. )
  184. {
  185. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  186. PCTSTR unexpandedData;
  187. TCHAR expandedPath[MEMDB_MAX];
  188. HASHTABLE Table;
  189. Table = HtAlloc();
  190. //
  191. // Load the entire INF section and copy it to the string table
  192. //
  193. if (InfFindFirstLine (g_Win95UpgInf, SectionName, NULL, &is)) {
  194. do {
  195. unexpandedData = InfGetStringField (&is, 1);
  196. if (unexpandedData) {
  197. //
  198. // Expand the data only if the user wants it to be expanded
  199. //
  200. if (ExpandEnvVars) {
  201. ExpandNtEnvironmentVariables (unexpandedData, expandedPath, sizeof (expandedPath));
  202. HtAddString (Table, expandedPath);
  203. } else {
  204. HtAddString (Table, unexpandedData);
  205. }
  206. }
  207. } while (InfFindNextLine (&is));
  208. }
  209. InfCleanUpInfStruct (&is);
  210. return Table;
  211. }
  212. VOID
  213. pDestroySfList (
  214. IN HASHTABLE Table
  215. )
  216. {
  217. HtFree (Table);
  218. }
  219. BOOL
  220. pCreateDirRenameTable (
  221. VOID
  222. )
  223. {
  224. INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
  225. PCTSTR sfId, sfWhere, sfPath9x, sfPathNt;
  226. PCTSTR tempPath1, tempPath2, NtPath;
  227. g_DirRenameTable = HtAllocWithData (sizeof (PCTSTR));
  228. if (InfFindFirstLine (g_Win95UpgInf, S_DIRRENAMESECT, NULL, &is)) {
  229. do {
  230. sfId = InfGetStringField (&is, 1);
  231. sfWhere = InfGetStringField (&is, 2);
  232. sfPath9x = InfGetStringField (&is, 3);
  233. sfPathNt = InfGetStringField (&is, 4);
  234. if (sfId && sfWhere && sfPath9x && sfPathNt) {
  235. tempPath1 = JoinPaths (sfId, sfWhere);
  236. tempPath2 = JoinPaths (tempPath1, sfPath9x);
  237. NtPath = PoolMemDuplicateString (g_SFPool, sfPathNt);
  238. HtAddStringAndData (g_DirRenameTable, tempPath2, &NtPath);
  239. FreePathString (tempPath2);
  240. FreePathString (tempPath1);
  241. }
  242. InfResetInfStruct (&is);
  243. } while (InfFindNextLine (&is));
  244. }
  245. InfCleanUpInfStruct (&is);
  246. return TRUE;
  247. }
  248. VOID
  249. pCreateSfMapWorker (
  250. IN DWORD MapFlags,
  251. IN OUT PMAPSTRUCT Map,
  252. IN PINFSTRUCT InfStruct,
  253. IN UINT ReplaceField
  254. )
  255. {
  256. PCTSTR searchStr;
  257. PCTSTR replaceStr;
  258. TCHAR expandedReplaceStr[MEMDB_MAX];
  259. TCHAR replaceRootPath[MAX_TCHAR_PATH];
  260. PCTSTR replaceRoot = NULL;
  261. PCTSTR fullReplaceStr = NULL;
  262. PCTSTR fieldData;
  263. DWORD offset1;
  264. DWORD offset2;
  265. if (MapFlags & MAP_ARG_MUST_BE_ZERO) {
  266. //
  267. // Screen out entries that don't have zero in ReplaceField + 1
  268. //
  269. fieldData = InfGetStringField (InfStruct, ReplaceField + 1);
  270. if (fieldData && _ttoi (fieldData)) {
  271. return;
  272. }
  273. }
  274. if (MapFlags & MAP_ARG_MUST_BE_ONE) {
  275. //
  276. // Screen out entries that don't have one in ReplaceField + 1
  277. //
  278. fieldData = InfGetStringField (InfStruct, ReplaceField + 1);
  279. if (!fieldData || (_ttoi (fieldData) != 1)) {
  280. return;
  281. }
  282. }
  283. if (MapFlags & MAP_PARENT_FIELD) {
  284. //
  285. // Check (replace field - 1) and if it is not empty, get the parent
  286. //
  287. MYASSERT (g_DefaultDirMap);
  288. replaceRoot = InfGetStringField (InfStruct, ReplaceField - 1);
  289. if (replaceRoot && *replaceRoot) {
  290. StringCopy (replaceRootPath, replaceRoot);
  291. replaceRoot = replaceRootPath;
  292. if (!MappingSearchAndReplace (Map, replaceRootPath, sizeof (replaceRootPath))) {
  293. if (!MappingSearchAndReplace (g_DefaultDirMap, replaceRootPath, sizeof (replaceRootPath))) {
  294. replaceRoot = NULL;
  295. }
  296. }
  297. } else {
  298. replaceRoot = NULL;
  299. }
  300. }
  301. //
  302. // Add search/replace string pair to map
  303. //
  304. searchStr = InfGetStringField (InfStruct, 0);
  305. replaceStr = InfGetStringField (InfStruct, ReplaceField);
  306. if (!replaceStr) {
  307. replaceStr = TEXT("");
  308. }
  309. if (MapFlags & MAP_FLAG_EXPAND) {
  310. if (ExpandNtEnvironmentVariables (replaceStr, expandedReplaceStr, sizeof (expandedReplaceStr))) {
  311. replaceStr = expandedReplaceStr;
  312. }
  313. }
  314. if (replaceRoot) {
  315. fullReplaceStr = JoinPaths (replaceRoot, replaceStr);
  316. replaceStr = fullReplaceStr;
  317. }
  318. if (MapFlags & MAP_REVERSE) {
  319. AddStringMappingPair (Map, replaceStr, searchStr);
  320. } else {
  321. AddStringMappingPair (Map, searchStr, replaceStr);
  322. }
  323. if (MapFlags & MAP_RECORD_IN_MEMDB) {
  324. MYASSERT (!(MapFlags & MAP_REVERSE));
  325. MemDbSetValueEx (
  326. MEMDB_CATEGORY_SF_COMMON,
  327. replaceStr,
  328. NULL,
  329. NULL,
  330. 0,
  331. &offset1
  332. );
  333. MemDbSetValueEx (
  334. MEMDB_CATEGORY_SF_PERUSER,
  335. searchStr,
  336. NULL,
  337. NULL,
  338. offset1,
  339. &offset2
  340. );
  341. // knowing implementation of memdb, this will not change offset1
  342. MemDbSetValueEx (
  343. MEMDB_CATEGORY_SF_COMMON,
  344. replaceStr,
  345. NULL,
  346. NULL,
  347. offset2,
  348. NULL
  349. );
  350. }
  351. FreePathString (fullReplaceStr);
  352. }
  353. PMAPSTRUCT
  354. pCreateSfMap (
  355. IN PCTSTR SectionName,
  356. IN UINT ReplaceField,
  357. IN DWORD MapFlags
  358. )
  359. {
  360. INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
  361. PMAPSTRUCT Map;
  362. TCHAR versionSectionName[256];
  363. Map = CreateStringMapping();
  364. if (!Map) {
  365. return NULL;
  366. }
  367. //
  368. // Load the entire INF section and add the mapping for each line
  369. //
  370. if (InfFindFirstLine (g_Win95UpgInf, SectionName, NULL, &is)) {
  371. do {
  372. pCreateSfMapWorker (MapFlags, Map, &is, ReplaceField);
  373. } while (InfFindNextLine (&is));
  374. }
  375. //
  376. // ... and for the specific Win9x Version
  377. //
  378. versionSectionName[0] = 0;
  379. if (ISWIN95_GOLDEN()) {
  380. wsprintf (versionSectionName, TEXT("%s.Win95"), SectionName);
  381. } else if (ISWIN95_OSR2()) {
  382. wsprintf (versionSectionName, TEXT("%s.Win95Osr2"), SectionName);
  383. } else if (ISMEMPHIS()) {
  384. wsprintf (versionSectionName, TEXT("%s.Win98"), SectionName);
  385. } else if (ISMILLENNIUM()) {
  386. wsprintf (versionSectionName, TEXT("%s.Me"), SectionName);
  387. }
  388. if (versionSectionName[0] &&
  389. InfFindFirstLine (g_Win95UpgInf, versionSectionName, NULL, &is)
  390. ) {
  391. do {
  392. pCreateSfMapWorker (MapFlags, Map, &is, ReplaceField);
  393. } while (InfFindNextLine (&is));
  394. }
  395. InfCleanUpInfStruct (&is);
  396. return Map;
  397. }
  398. VOID
  399. pDestroySfMap (
  400. IN PMAPSTRUCT Map
  401. )
  402. {
  403. DestroyStringMapping (Map);
  404. }
  405. VOID
  406. pCreatePriorityList (
  407. VOID
  408. )
  409. {
  410. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  411. PCTSTR data;
  412. if (InfFindFirstLine (g_Win95UpgInf, S_SHELL_FOLDER_PRIORITY, NULL, &is)) {
  413. do {
  414. data = InfGetStringField (&is, 1);
  415. if (data && *data) {
  416. GrowListAppendString (&g_CollisionPriorityList, data);
  417. }
  418. } while (InfFindNextLine (&is));
  419. }
  420. InfCleanUpInfStruct (&is);
  421. }
  422. BOOL
  423. pTestRule (
  424. IN PINFSTRUCT InfStruct,
  425. OUT PBOOL TestResult
  426. )
  427. {
  428. PCTSTR keyStr;
  429. PCTSTR valueName;
  430. PCTSTR test;
  431. PBYTE valueData = NULL;
  432. DWORD dataSize;
  433. DWORD dataType;
  434. HKEY key = NULL;
  435. BOOL not = FALSE;
  436. *TestResult = FALSE;
  437. //
  438. // Get instructions from INF
  439. //
  440. keyStr = InfGetStringField (InfStruct, 1);
  441. if (!keyStr || *keyStr == 0) {
  442. DEBUGMSG ((DBG_WHOOPS, "Missing Key field in ShellFolders.ConditionalPreserve"));
  443. return FALSE;
  444. }
  445. valueName = InfGetStringField (InfStruct, 2);
  446. if (!valueName) {
  447. DEBUGMSG ((DBG_WHOOPS, "Missing ValueName field in ShellFolders.ConditionalPreserve"));
  448. return FALSE;
  449. }
  450. test = InfGetStringField (InfStruct, 3);
  451. if (!test || *test == 0) {
  452. DEBUGMSG ((DBG_WHOOPS, "Missing Test field in ShellFolders.ConditionalPreserve"));
  453. return FALSE;
  454. }
  455. //
  456. // Execute instructions
  457. //
  458. __try {
  459. //
  460. // Process NOT arg
  461. //
  462. MYASSERT (test && *test);
  463. if (test[0] == TEXT('!')) {
  464. not = TRUE;
  465. test++;
  466. }
  467. //
  468. // Fetch registry data
  469. //
  470. MYASSERT (keyStr && *keyStr);
  471. key = OpenRegKeyStr (keyStr);
  472. if (!key) {
  473. DEBUGMSG ((DBG_VERBOSE, "%s does not exist", keyStr));
  474. __leave;
  475. }
  476. MYASSERT (valueName);
  477. if (!StringIMatch (test, TEXT("KEY"))) {
  478. valueData = GetRegValueData (key, valueName);
  479. if (!valueData) {
  480. DEBUGMSG ((DBG_VERBOSE, "%s [%s] does not exist", keyStr, valueName));
  481. __leave;
  482. }
  483. if (!GetRegValueTypeAndSize (key, valueName, &dataType, &dataSize)) {
  484. DEBUGMSG ((DBG_ERROR, "Failed to get type/size of %s [%s]", keyStr, valueName));
  485. __leave;
  486. }
  487. }
  488. //
  489. // Perform tests
  490. //
  491. if (StringIMatch (test, TEXT("KEY"))) {
  492. *TestResult = TRUE;
  493. } else if (StringIMatch (test, TEXT("VALUE"))) {
  494. if (valueData) {
  495. *TestResult = TRUE;
  496. }
  497. } else if (StringIMatch (test, TEXT("PATH"))) {
  498. if (valueData && (dataType == REG_SZ || dataType == REG_NONE)) {
  499. //
  500. // Registry wrapper apis make sure the string is nul terminated
  501. //
  502. *TestResult = DoesFileExist ((PCTSTR) valueData);
  503. }
  504. } else {
  505. DEBUGMSG ((DBG_WHOOPS, "Unexpected rule in ShellFolders.ConditionalPreserve: '%s'", test));
  506. }
  507. }
  508. __finally {
  509. if (key) {
  510. CloseRegKey (key);
  511. }
  512. if (valueData) {
  513. FreeMem (valueData);
  514. }
  515. }
  516. if (not) {
  517. *TestResult = !(*TestResult);
  518. }
  519. return TRUE;
  520. }
  521. BOOL
  522. pTestRuleOrSection (
  523. IN HINF Inf,
  524. IN PINFSTRUCT InfStruct,
  525. OUT PUINT ShellFolderField,
  526. OUT PBOOL TestResult
  527. )
  528. {
  529. INFSTRUCT sectionInfStruct = INITINFSTRUCT_POOLHANDLE;
  530. PCTSTR field1;
  531. PCTSTR decoratedSection;
  532. TCHAR number[32];
  533. UINT u;
  534. BOOL processed = FALSE;
  535. BOOL result = FALSE;
  536. BOOL foundSection;
  537. *TestResult = FALSE;
  538. *ShellFolderField = 2; // start with assumption line is in <section>,<shellfolder> format
  539. field1 = InfGetStringField (InfStruct, 1);
  540. if (!field1 || *field1 == 0) {
  541. DEBUGMSG ((DBG_WHOOPS, "Empty field 1 in ShellFolders.ConditionalPreserve"));
  542. return FALSE;
  543. }
  544. __try {
  545. //
  546. // Test this field for an AND section
  547. //
  548. for (u = 1, foundSection = TRUE ; foundSection ; u++) {
  549. wsprintf (number, TEXT(".%u"), u);
  550. decoratedSection = JoinText (field1, number);
  551. if (InfFindFirstLine (Inf, decoratedSection, NULL, &sectionInfStruct)) {
  552. //
  553. // flag that this is processed, and reset the test result for the
  554. // next section (which we just found)
  555. //
  556. processed = TRUE;
  557. *TestResult = FALSE;
  558. //
  559. // locate the first line that is true
  560. //
  561. do {
  562. if (!pTestRule (&sectionInfStruct, TestResult)) {
  563. __leave;
  564. }
  565. if (*TestResult) {
  566. break;
  567. }
  568. } while (InfFindNextLine (&sectionInfStruct));
  569. } else {
  570. foundSection = FALSE;
  571. }
  572. FreeText (decoratedSection);
  573. }
  574. //
  575. // Test this field for an OR section
  576. //
  577. if (!processed) {
  578. if (InfFindFirstLine (Inf, field1, NULL, &sectionInfStruct)) {
  579. processed = TRUE;
  580. //
  581. // locate the first line that is true
  582. //
  583. do {
  584. if (!pTestRule (&sectionInfStruct, TestResult)) {
  585. __leave;
  586. }
  587. if (*TestResult) {
  588. break;
  589. }
  590. } while (InfFindNextLine (&sectionInfStruct));
  591. }
  592. }
  593. //
  594. // Finally process the line if it is not an AND or OR section
  595. //
  596. if (!processed) {
  597. *ShellFolderField = 4; // line is in <key>,<value>,<test>,<shellfolder> format
  598. if (!pTestRule (&sectionInfStruct, TestResult)) {
  599. __leave;
  600. }
  601. }
  602. result = TRUE;
  603. }
  604. __finally {
  605. InfCleanUpInfStruct (&sectionInfStruct);
  606. }
  607. return result;
  608. }
  609. BOOL
  610. pPopulateConditionalPreserveTable (
  611. IN HASHTABLE Table
  612. )
  613. {
  614. INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
  615. UINT tagField;
  616. PCTSTR multiSz;
  617. MULTISZ_ENUM e;
  618. BOOL testResult;
  619. BOOL result = FALSE;
  620. __try {
  621. if (InfFindFirstLine (g_Win95UpgInf, TEXT("ShellFolders.ConditionalPreserve"), NULL, &is)) {
  622. do {
  623. if (!pTestRuleOrSection (g_Win95UpgInf, &is, &tagField, &testResult)) {
  624. __leave;
  625. }
  626. if (!testResult) {
  627. continue;
  628. }
  629. //
  630. // Add multi-sz of tags to preserve table
  631. //
  632. multiSz = InfGetMultiSzField (&is, tagField);
  633. if (!multiSz) {
  634. DEBUGMSG ((DBG_WHOOPS, "ShellFolders.ConditionalPreserve line is missing shell folder tags"));
  635. } else {
  636. if (EnumFirstMultiSz (&e, multiSz)) {
  637. do {
  638. DEBUGMSG ((DBG_VERBOSE, "Dynamically preserving %s", e.CurrentString));
  639. HtAddString (Table, e.CurrentString);
  640. } while (EnumNextMultiSz (&e));
  641. }
  642. }
  643. } while (InfFindNextLine (&is));
  644. }
  645. result = TRUE;
  646. }
  647. __finally {
  648. InfCleanUpInfStruct (&is);
  649. }
  650. return result;
  651. }
  652. BOOL
  653. pCreateSfTables (
  654. VOID
  655. )
  656. {
  657. g_SkippedTable = pCreateSfList (S_SHELL_FOLDERS_SKIPPED, FALSE);
  658. g_PreservedSfTable = pCreateSfList (S_SHELL_FOLDERS_PRESERVED, FALSE);
  659. g_MassiveDirTable = pCreateSfList (S_SHELL_FOLDERS_MASSIVE, TRUE);
  660. g_CollapseRestrictions = pCreateSfList (S_SHELL_FOLDERS_DONT_COLLAPSE, FALSE);
  661. if (!pPopulateConditionalPreserveTable (g_PreservedSfTable)) {
  662. LOG ((LOG_ERROR, "An INF syntax error in ShellFolders.ConditionalPreserve caused setup to fail"));
  663. return FALSE;
  664. }
  665. //
  666. // String maps are useful for in-place conversion of a string to another
  667. // string. We are about to establish maps for all INF-driven shell folder
  668. // data.
  669. //
  670. // The INF syntaxes defined for shell folders are the following:
  671. //
  672. // <search>=<replace>
  673. // <search>=<don't care>,<replace>
  674. // <search>=<parent>,<replace>,<arg>
  675. //
  676. // The second arg to pCreateSfMap specifies the <replace> string field.
  677. // The field before the replace string field is the <parent> field, if
  678. // MAP_PARENT_FIELD is specified. The field after the <replace> field is
  679. // the <arg> field, and is used with MAP_ARG_MUST_BE_ZERO or
  680. // MAP_ARG_MUST_BE_ONE.
  681. //
  682. // MAP_FLAG_NONE just creates a simple A to B string mapping.
  683. // MAP_FLAG_EXPAND causes NT environment expansion on the replace string.
  684. // MAP_ARG_MUST_BE_ZERO or MAP_ARG_MUST_BE_ONE restrict the map to entries
  685. // which have a 0 or 1 in the arg field, respectively.
  686. //
  687. g_ShortNameMap = pCreateSfMap (S_SHELL_FOLDERS_SHORT, 1, MAP_FLAG_NONE);
  688. g_SfRenameMap = pCreateSfMap (S_SHELL_FOLDERS_RENAMED, 1, MAP_FLAG_NONE);
  689. g_DefaultDirMap = pCreateSfMap (S_SHELL_FOLDERS_DEFAULT, 1, MAP_FLAG_EXPAND);
  690. g_AlternateCommonDirMap = pCreateSfMap (S_SHELL_FOLDERS_ALT_DEFAULT, 2, MAP_FLAG_EXPAND|MAP_PARENT_FIELD|MAP_ARG_MUST_BE_ZERO);
  691. g_AlternatePerUserDirMap = pCreateSfMap (S_SHELL_FOLDERS_ALT_DEFAULT, 2, MAP_FLAG_EXPAND|MAP_PARENT_FIELD|MAP_ARG_MUST_BE_ONE);
  692. g_DefaultShortDirMap = pCreateSfMap (S_SHELL_FOLDERS_DEFAULT, 2, MAP_FLAG_EXPAND);
  693. g_CommonFromPerUserMap = pCreateSfMap (S_SHELL_FOLDERS_PERUSER_TO_COMMON, 1, MAP_RECORD_IN_MEMDB);
  694. g_PerUserFromCommonMap = pCreateSfMap (S_SHELL_FOLDERS_PERUSER_TO_COMMON, 1, MAP_REVERSE);
  695. pCreatePriorityList ();
  696. return g_SkippedTable &&
  697. g_PreservedSfTable &&
  698. g_MassiveDirTable &&
  699. g_CollapseRestrictions &&
  700. g_ShortNameMap &&
  701. g_SfRenameMap &&
  702. g_DefaultDirMap &&
  703. g_AlternateCommonDirMap &&
  704. g_AlternatePerUserDirMap &&
  705. g_CommonFromPerUserMap &&
  706. g_PerUserFromCommonMap &&
  707. g_DefaultShortDirMap;
  708. }
  709. VOID
  710. pDestroySfTables (
  711. VOID
  712. )
  713. {
  714. pDestroySfList (g_SkippedTable);
  715. pDestroySfList (g_PreservedSfTable);
  716. pDestroySfList (g_MassiveDirTable);
  717. pDestroySfList (g_CollapseRestrictions);
  718. pDestroySfMap (g_ShortNameMap);
  719. pDestroySfMap (g_SfRenameMap);
  720. pDestroySfMap (g_DefaultDirMap);
  721. pDestroySfMap (g_AlternateCommonDirMap);
  722. pDestroySfMap (g_AlternatePerUserDirMap);
  723. pDestroySfMap (g_CommonFromPerUserMap);
  724. pDestroySfMap (g_PerUserFromCommonMap);
  725. pDestroySfMap (g_DefaultShortDirMap);
  726. FreeGrowList (&g_CollisionPriorityList);
  727. }
  728. BOOL
  729. pIsSkippedSf (
  730. IN PCTSTR ShellFolderName
  731. )
  732. /*++
  733. Routine Description:
  734. pIsSkippedSf returns if a shell folder needs to be skipped from processing.
  735. Arguments:
  736. ShellFolderName - Specifies the shell folder identifer to check
  737. Return Value:
  738. TRUE if the shell folder needs to be skipped, FALSE otherwise
  739. --*/
  740. {
  741. return HtFindString (g_SkippedTable, ShellFolderName) != 0;
  742. }
  743. BOOL
  744. pIsMassiveDir (
  745. IN PCTSTR ShellFolderPath
  746. )
  747. /*++
  748. Routine Description:
  749. pIsMassiveDir returns if a certain path should not be moved to a temporary
  750. location during migration. Usually this is the case for directories like
  751. %windir% or %windir%\system.
  752. Arguments:
  753. ShellFolderPath - Specifies the path to check
  754. Return Value:
  755. TRUE if the path should not be moved, FALSE otherwise
  756. --*/
  757. {
  758. return HtFindString (g_MassiveDirTable, ShellFolderPath) != 0;
  759. }
  760. BOOL
  761. pIsPreservedSf (
  762. IN PCTSTR ShellFolderName
  763. )
  764. /*++
  765. Routine Description:
  766. pIsPreservedSf returns if a shell folder location needs to be preserved.
  767. Arguments:
  768. ShellFolderName - Specifies the shell folder identifer to check
  769. Return Value:
  770. TRUE if the shell folder location needs to be preserved, FALSE otherwise
  771. --*/
  772. {
  773. return HtFindString (g_PreservedSfTable, ShellFolderName) != 0;
  774. }
  775. BOOL
  776. pShortFileNames (
  777. VOID
  778. )
  779. /*++
  780. Routine Description:
  781. pShortFileNames returns TRUE if we are on a system that does not allow long file names.
  782. Arguments:
  783. none
  784. Return Value:
  785. TRUE if the system does not allow long file names, FALSE otherwise
  786. --*/
  787. {
  788. //
  789. // It is unclear how the OS decides to use short filenames for
  790. // shell folders. Assuming this never is the case.
  791. //
  792. return FALSE;
  793. }
  794. PCTSTR
  795. pGetShellFolderLongName (
  796. IN PCTSTR ShortName
  797. )
  798. /*++
  799. Routine Description:
  800. pGetShellFolderLongName transforms a short format shell folder into
  801. the long format. The short format is only used on systems that don't
  802. allow long file names.
  803. Arguments:
  804. ShortName - Specifies the shell folder short name.
  805. Return Value:
  806. Shell folder long name. Caller must free via FreePathString.
  807. --*/
  808. {
  809. TCHAR longName [MEMDB_MAX];
  810. if (pShortFileNames()) {
  811. StringCopy (longName, ShortName);
  812. MappingSearchAndReplace (g_ShortNameMap, longName, sizeof (longName));
  813. } else {
  814. longName[0] = 0;
  815. }
  816. return DuplicatePathString (longName[0] ? longName : ShortName, 0);
  817. }
  818. UINT
  819. pGetShellFolderPriority (
  820. IN PCTSTR ShellFolderName
  821. )
  822. {
  823. PCTSTR *strArray;
  824. UINT arraySize;
  825. UINT u;
  826. strArray = GrowListGetStringPtrArray (&g_CollisionPriorityList);
  827. if (strArray) {
  828. arraySize = GrowListGetSize (&g_CollisionPriorityList);
  829. for (u = 0 ; u < arraySize ; u++) {
  830. if (StringIMatch (strArray[u], ShellFolderName)) {
  831. return u;
  832. }
  833. }
  834. }
  835. return 0xFFFFFFFF;
  836. }
  837. BOOL
  838. pGetDefaultLocation (
  839. IN PCTSTR ShellFolderName,
  840. OUT PCTSTR *LocalizedFolderName, OPTIONAL
  841. IN WHICHDEFAULT WhichDefault
  842. )
  843. /*++
  844. Routine Description:
  845. pGetDefaultLocation returns the default location for a certain shell
  846. folder.
  847. Arguments:
  848. ShellFolderName - specifies the shell folder identifer, which can be
  849. a long identifier or a short identifier, depending
  850. on the mode determined by pShortFileNames.
  851. LocalizedFolderName - receives the localized name of shell folder
  852. WhichDefault - specifies the default to return:
  853. DEFAULT_PER_USER or DEFAULT_COMMON - the standard default
  854. location, such as c:\windows\Start Menu
  855. DEFAULT_ALT_COMMON - the alternate common default, defined
  856. by [ShellFolders.AlternateDefault].
  857. DEFAULT_ALT_PER_USER - the alternate per-user default,
  858. defined by [ShellFolders.AlternateDefault].
  859. Return Value:
  860. The default location for the shell folder. LocalizedFolderName points to a
  861. subpath, rooted from %windir% or the user's profile root, or it points to a
  862. full path (second character is a colon). Caller must free via
  863. FreePathString.
  864. --*/
  865. {
  866. TCHAR defaultPath[MEMDB_MAX];
  867. BOOL Result;
  868. StringCopy (defaultPath, ShellFolderName);
  869. switch (WhichDefault) {
  870. case DEFAULT_ALT_COMMON:
  871. Result = MappingSearchAndReplace (g_AlternateCommonDirMap, defaultPath, sizeof (defaultPath));
  872. break;
  873. case DEFAULT_ALT_PER_USER:
  874. Result = MappingSearchAndReplace (g_AlternatePerUserDirMap, defaultPath, sizeof (defaultPath));
  875. break;
  876. default:
  877. if (pShortFileNames()) {
  878. Result = MappingSearchAndReplace (g_DefaultShortDirMap, defaultPath, sizeof (defaultPath));
  879. } else {
  880. Result = MappingSearchAndReplace (g_DefaultDirMap, defaultPath, sizeof (defaultPath));
  881. }
  882. break;
  883. }
  884. if (LocalizedFolderName) {
  885. if (Result) {
  886. *LocalizedFolderName = DuplicatePathString (defaultPath, 0);
  887. } else {
  888. *LocalizedFolderName = NULL;
  889. }
  890. }
  891. return Result;
  892. }
  893. BOOL
  894. pTestDefaultLocationWorker (
  895. IN PCTSTR UserName, OPTIONAL
  896. IN PCTSTR FullPathOrSubDir,
  897. IN PCTSTR PathToTest
  898. )
  899. {
  900. TCHAR tempPath[MEMDB_MAX];
  901. PCTSTR defaultPath;
  902. //
  903. // Compute the full path. It might be specified, or if not, it is either
  904. // a subdir of %windir% or a subdir of %windir%\profiles\<username>.
  905. //
  906. if (FullPathOrSubDir[0] && FullPathOrSubDir[1] == TEXT(':')) {
  907. defaultPath = FullPathOrSubDir;
  908. } else {
  909. if (UserName) {
  910. wsprintf (tempPath, TEXT("%s\\%s"), g_WinDir, S_PROFILES);
  911. if (StringIMatch (PathToTest, tempPath)) {
  912. return TRUE;
  913. }
  914. AppendWack (tempPath);
  915. return StringIPrefix (PathToTest, tempPath);
  916. } else {
  917. if (FullPathOrSubDir[0]) {
  918. wsprintf (tempPath, TEXT("%s\\%s"), g_WinDir, FullPathOrSubDir);
  919. } else {
  920. wsprintf (tempPath, TEXT("%s"), g_WinDir);
  921. }
  922. }
  923. defaultPath = tempPath;
  924. }
  925. return StringIMatch (defaultPath, PathToTest);
  926. }
  927. BOOL
  928. pIsTheDefaultLocation (
  929. IN PCTSTR ShellFolderName,
  930. IN PCTSTR ShellFolderPath,
  931. IN PCTSTR UserName, OPTIONAL
  932. IN WHICHDEFAULT WhichDefault
  933. )
  934. /*++
  935. Routine Description:
  936. pIsTheDefaultLocation returns if a shell folder points to the default location.
  937. Arguments:
  938. ShellFolderName - Specifies the shell folder identifier
  939. ShellFolderPath - Specifies the path to compare against the default
  940. UserName - Specifies the current user
  941. WhichDefault - Specifies the default location to test (typically
  942. ANY_COMMON or ANY_PER_USER).
  943. Return Value:
  944. TRUE if the shell folder points to the default location, FALSE otherwise.
  945. --*/
  946. {
  947. PCTSTR subDir = NULL;
  948. TCHAR fullPath[MEMDB_MAX];
  949. BOOL Result = FALSE;
  950. BOOL fullPathReturned;
  951. if (StringIMatch (ShellFolderName, S_SF_PROFILES) ||
  952. StringIMatch (ShellFolderName, S_SF_COMMON_PROFILES)
  953. ) {
  954. return TRUE;
  955. }
  956. __try {
  957. if (WhichDefault & DEFAULT_COMMON) {
  958. if (pGetDefaultLocation (ShellFolderName, &subDir, DEFAULT_COMMON)) {
  959. if (pTestDefaultLocationWorker (NULL, subDir, ShellFolderPath)) {
  960. Result = TRUE;
  961. __leave;
  962. }
  963. }
  964. }
  965. if (WhichDefault & DEFAULT_ALT_COMMON) {
  966. if (pGetDefaultLocation (ShellFolderName, &subDir, DEFAULT_ALT_COMMON)) {
  967. if (pTestDefaultLocationWorker (NULL, subDir, ShellFolderPath)) {
  968. Result = TRUE;
  969. __leave;
  970. }
  971. }
  972. }
  973. if (UserName) {
  974. if (WhichDefault & DEFAULT_PER_USER) {
  975. if (pGetDefaultLocation (ShellFolderName, &subDir, DEFAULT_PER_USER)) {
  976. if (pTestDefaultLocationWorker (UserName, subDir, ShellFolderPath)) {
  977. Result = TRUE;
  978. __leave;
  979. }
  980. }
  981. }
  982. if (WhichDefault & DEFAULT_ALT_PER_USER) {
  983. if (pGetDefaultLocation (ShellFolderName, &subDir, DEFAULT_ALT_PER_USER)) {
  984. if (pTestDefaultLocationWorker (UserName, subDir, ShellFolderPath)) {
  985. Result = TRUE;
  986. __leave;
  987. }
  988. }
  989. }
  990. }
  991. MYASSERT (!Result);
  992. }
  993. __finally {
  994. FreePathString (subDir);
  995. }
  996. #ifdef DEBUG
  997. if (!Result) {
  998. if (WhichDefault == ANY_DEFAULT) {
  999. DEBUGMSG ((DBG_USERLOOP, "%s (%s) is not in any default location", ShellFolderPath, ShellFolderName));
  1000. } else if (WhichDefault == ANY_PER_USER) {
  1001. DEBUGMSG ((DBG_USERLOOP, "%s (%s) is not in any per-user location", ShellFolderPath, ShellFolderName));
  1002. } else if (WhichDefault == ANY_COMMON) {
  1003. DEBUGMSG ((DBG_USERLOOP, "%s (%s) is not in any common location", ShellFolderPath, ShellFolderName));
  1004. } else {
  1005. if (WhichDefault & DEFAULT_COMMON) {
  1006. DEBUGMSG ((DBG_USERLOOP, "%s (%s) is not in default common location", ShellFolderPath, ShellFolderName));
  1007. }
  1008. if (WhichDefault & DEFAULT_ALT_COMMON) {
  1009. DEBUGMSG ((DBG_USERLOOP, "%s (%s) is not in default alternate common location", ShellFolderPath, ShellFolderName));
  1010. }
  1011. if (WhichDefault & DEFAULT_PER_USER) {
  1012. DEBUGMSG ((DBG_USERLOOP, "%s (%s) is not in default per-user location", ShellFolderPath, ShellFolderName));
  1013. }
  1014. if (WhichDefault & DEFAULT_ALT_PER_USER) {
  1015. DEBUGMSG ((DBG_USERLOOP, "%s (%s) is not in default alternate per-user location", ShellFolderPath, ShellFolderName));
  1016. }
  1017. }
  1018. }
  1019. #endif
  1020. return Result;
  1021. }
  1022. PCTSTR
  1023. pGetNtName (
  1024. IN PCTSTR ShellFolderName
  1025. )
  1026. /*++
  1027. Routine Description:
  1028. pGetNtName returns the name of the shell folder used on NT.
  1029. Arguments:
  1030. ShellFolderName - Specifies the Win9x shell folder identifier
  1031. Return Value:
  1032. A pointer to the NT identifer. The caller must free this value via
  1033. FreePathString.
  1034. --*/
  1035. {
  1036. TCHAR ntName[MEMDB_MAX];
  1037. StringCopy (ntName, ShellFolderName);
  1038. MappingSearchAndReplace (g_SfRenameMap, ntName, sizeof (ntName));
  1039. return DuplicatePathString (ntName, 0);
  1040. }
  1041. BOOL
  1042. pIsNtShellFolder (
  1043. IN PCTSTR ShellFolderName,
  1044. IN BOOL PerUser,
  1045. IN BOOL IsNtName
  1046. )
  1047. /*++
  1048. Routine Description:
  1049. pIsNtShellFolder returns if a shell folder is also installed by NT.
  1050. Arguments:
  1051. ShellFolderName - Specifies the NT shell folder identifier
  1052. PerUser - Specifies TRUE if the shell folder is per-user, FALSE if it is
  1053. common.
  1054. IsNtName - Specifies TRUE if ShellFolderName is an NT name, FALSE if it is a
  1055. Win9x name.
  1056. Return Value:
  1057. TRUE if the shell folder is installed by NT, FALSE otherwise.
  1058. --*/
  1059. {
  1060. INFCONTEXT context;
  1061. PCTSTR ntName;
  1062. BOOL result;
  1063. if (IsNtName) {
  1064. ntName = ShellFolderName;
  1065. } else {
  1066. ntName = pGetNtName (ShellFolderName);
  1067. }
  1068. result = SetupFindFirstLine (
  1069. g_Win95UpgInf,
  1070. PerUser?S_SHELL_FOLDERS_NTINSTALLED_USER:S_SHELL_FOLDERS_NTINSTALLED_COMMON,
  1071. ntName,
  1072. &context
  1073. );
  1074. if (ntName != ShellFolderName) {
  1075. FreePathString (ntName);
  1076. }
  1077. return result;
  1078. }
  1079. BOOL
  1080. pIsPerUserWanted (
  1081. IN PCTSTR ShellFolderTag
  1082. )
  1083. {
  1084. return HtFindString (g_CollapseRestrictions, ShellFolderTag) != 0;
  1085. }
  1086. BOOL
  1087. pGetNtShellFolderPath (
  1088. IN PCTSTR ShellFolderName,
  1089. IN PCTSTR FixedUserName,
  1090. OUT PTSTR Buffer,
  1091. IN DWORD BufferSize
  1092. )
  1093. /*++
  1094. Routine Description:
  1095. pGetNtShellFolderPath returns the path where the shell folder is installed.
  1096. Arguments:
  1097. ShellFolderName - Specifies the NT shell folder identifier
  1098. Buffer - Receives the NT shell folder path
  1099. BufferSize - Specifies the size of Buffer, in bytes
  1100. Return Value:
  1101. TRUE if the shell folder identifier maps to a path, or FALSE if not.
  1102. --*/
  1103. {
  1104. TCHAR node[MEMDB_MAX];
  1105. PCTSTR ntName;
  1106. BOOL result = FALSE;
  1107. ntName = pGetNtName (ShellFolderName);
  1108. __try {
  1109. if (!pIsNtShellFolder (ntName, FixedUserName?TRUE:FALSE, TRUE)) {
  1110. return FALSE;
  1111. }
  1112. wsprintf (node, TEXT("<%s>%s"), ntName, FixedUserName?FixedUserName:S_DOT_ALLUSERS);
  1113. ExpandNtEnvironmentVariables (node, node, MEMDB_MAX);
  1114. _tcssafecpy (Buffer, node, BufferSize / sizeof (TCHAR));
  1115. result = TRUE;
  1116. }
  1117. __finally {
  1118. FreePathString (ntName);
  1119. }
  1120. return result;
  1121. }
  1122. BOOL
  1123. pIsValidPath (
  1124. PCTSTR Path
  1125. )
  1126. {
  1127. PCTSTR currPtr;
  1128. if (!Path || !(*Path)) {
  1129. return FALSE;
  1130. }
  1131. currPtr = Path;
  1132. do {
  1133. if ((*currPtr == TEXT(',')) ||
  1134. (*currPtr == TEXT(';')) ||
  1135. (*currPtr == TEXT('<')) ||
  1136. (*currPtr == TEXT('>')) ||
  1137. (*currPtr == TEXT('|')) ||
  1138. (*currPtr == TEXT('?')) ||
  1139. (*currPtr == TEXT('*'))
  1140. ) {
  1141. return FALSE;
  1142. }
  1143. currPtr = _tcsinc (currPtr);
  1144. }
  1145. while (*currPtr);
  1146. return TRUE;
  1147. }
  1148. BOOL
  1149. pEnumProfileShellFolder (
  1150. IN OUT PSF_ENUM e
  1151. )
  1152. {
  1153. PCTSTR ProfilePath;
  1154. PCTSTR enumPath;
  1155. if (e->FirstCall) {
  1156. e->sfName = DuplicatePathString (
  1157. e->EnumPtr ? S_SF_PROFILES : S_SF_COMMON_PROFILES,
  1158. 0
  1159. );
  1160. e->FirstCall = FALSE;
  1161. ProfilePath = JoinPaths (g_WinDir, S_PROFILES);
  1162. if (e->EnumPtr) {
  1163. e->sfPath = JoinPaths (ProfilePath, e->EnumPtr->UserName);
  1164. FreePathString (ProfilePath);
  1165. } else {
  1166. e->sfPath = ProfilePath;
  1167. }
  1168. //
  1169. // if this folder exists, enumerate it, otherwise end the enum
  1170. //
  1171. if (((!e->EnumPtr) || (!e->EnumPtr->CommonProfilesEnabled)) && DoesFileExist (e->sfPath)) {
  1172. enumPath = PoolMemDuplicateString (g_SFPool, e->sfPath);
  1173. HtAddStringAndData (e->enumeratedSf, e->sfName, &enumPath);
  1174. return TRUE;
  1175. }
  1176. }
  1177. FreePathString (e->sfName);
  1178. e->sfName = NULL;
  1179. FreePathString (e->sfPath);
  1180. e->sfPath = NULL;
  1181. return FALSE;
  1182. }
  1183. BOOL
  1184. pEnumNextVirtualShellFolder (
  1185. IN OUT PSF_ENUM e
  1186. )
  1187. {
  1188. TCHAR SfName[MEMDB_MAX];
  1189. TCHAR SfParent[MEMDB_MAX];
  1190. TCHAR SfPath[MEMDB_MAX];
  1191. PCTSTR SfFullPath = NULL;
  1192. PCTSTR SfParentPath = NULL;
  1193. INT HasToExist;
  1194. INT PerUser;
  1195. PCTSTR argList[3]={"SystemDrive", g_WinDrive, NULL};
  1196. PCTSTR pathExp = NULL;
  1197. DWORD dontCare;
  1198. PCTSTR enumPath;
  1199. if (!e->ProfileSF) {
  1200. if (!e->FirstCall) {
  1201. FreePathString (e->sfPath);
  1202. e->sfPath = NULL;
  1203. FreePathString (e->sfName);
  1204. e->sfName = NULL;
  1205. }
  1206. while (e->FirstCall?SetupFindFirstLine (g_Win95UpgInf, S_VIRTUAL_SF, NULL, &e->Context):SetupFindNextLine (&e->Context, &e->Context)) {
  1207. e->FirstCall = FALSE;
  1208. if ((SetupGetStringField (&e->Context, 0, SfName, MEMDB_MAX, &dontCare)) &&
  1209. (SetupGetStringField (&e->Context, 1, SfParent, MEMDB_MAX, &dontCare)) &&
  1210. (SetupGetStringField (&e->Context, 2, SfPath, MEMDB_MAX, &dontCare))
  1211. ) {
  1212. if (!SfName[0] || HtFindStringAndData (e->enumeratedSf, SfName, NULL)) {
  1213. // this shell folder was already enumerated
  1214. continue;
  1215. }
  1216. if (!SetupGetIntField (&e->Context, 3, &PerUser)) {
  1217. PerUser = FALSE;
  1218. }
  1219. if (PerUser && (!e->EnumPtr)) {
  1220. continue;
  1221. }
  1222. if (!PerUser && (e->EnumPtr)) {
  1223. continue;
  1224. }
  1225. if (!SetupGetIntField (&e->Context, 4, &HasToExist)) {
  1226. HasToExist = FALSE;
  1227. }
  1228. pathExp = ExpandEnvironmentTextExA (SfPath, argList);
  1229. if (SfParent[0]) {
  1230. if (!HtFindStringAndData (e->enumeratedSf, SfParent, (PVOID) &SfParentPath)) {
  1231. DEBUGMSG ((DBG_WARNING, "Virtual SF parent not found: %s", SfParent));
  1232. FreeText (pathExp);
  1233. continue;
  1234. }
  1235. SfFullPath = JoinPaths (SfParentPath, pathExp);
  1236. } else if (pathExp[0] && pathExp[1] == TEXT(':')) {
  1237. SfFullPath = DuplicatePathString (pathExp, 0);
  1238. } else {
  1239. SfFullPath = JoinPaths (g_WinDir, pathExp);
  1240. }
  1241. FreeText (pathExp);
  1242. if (HasToExist && !DoesFileExist (SfFullPath)) {
  1243. // ISSUE: not sure what this code path does -- is it right?
  1244. // it is not used in the INF right now.
  1245. e->FirstCall = TRUE;
  1246. e->ProfileSF = TRUE;
  1247. FreePathString (SfFullPath);
  1248. return pEnumProfileShellFolder (e);
  1249. }
  1250. e->sfPath = SfFullPath;
  1251. e->sfName = DuplicatePathString (SfName, 0);
  1252. enumPath = PoolMemDuplicateString (g_SFPool, e->sfPath);
  1253. HtAddStringAndData (e->enumeratedSf, e->sfName, &enumPath);
  1254. return TRUE;
  1255. }
  1256. }
  1257. e->FirstCall = TRUE;
  1258. e->ProfileSF = TRUE;
  1259. }
  1260. return pEnumProfileShellFolder (e);
  1261. }
  1262. BOOL
  1263. pEnumFirstVirtualShellFolder (
  1264. IN OUT PSF_ENUM e
  1265. )
  1266. {
  1267. e->VirtualSF = TRUE;
  1268. MYASSERT (g_Win95UpgInf);
  1269. e->FirstCall = TRUE;
  1270. return pEnumNextVirtualShellFolder (e);
  1271. }
  1272. VOID
  1273. pAbortEnumVirtualShellFolder (
  1274. IN OUT PSF_ENUM e
  1275. )
  1276. {
  1277. HASHTABLE_ENUM h;
  1278. if (e->enumeratedSf) {
  1279. if (EnumFirstHashTableString (&h, e->enumeratedSf)) {
  1280. do {
  1281. PoolMemReleaseMemory (g_SFPool, *((PTSTR *)h.ExtraData));
  1282. } while (EnumNextHashTableString (&h));
  1283. }
  1284. HtFree (e->enumeratedSf);
  1285. e->enumeratedSf = NULL;
  1286. }
  1287. }
  1288. PVOID
  1289. pPathPoolAllocator (
  1290. IN DWORD Size
  1291. )
  1292. {
  1293. return (PVOID) AllocPathString (Size / sizeof (TCHAR));
  1294. }
  1295. VOID
  1296. pPathPoolDeAllocator (
  1297. IN PCVOID Mem
  1298. )
  1299. {
  1300. FreePathString ((PCTSTR) Mem);
  1301. }
  1302. PBYTE
  1303. pPathPoolGetRegValueDataOfType (
  1304. IN HKEY hKey,
  1305. IN PCSTR Value,
  1306. IN DWORD MustBeType
  1307. )
  1308. {
  1309. return GetRegValueDataOfType2 (
  1310. hKey,
  1311. Value,
  1312. MustBeType,
  1313. pPathPoolAllocator,
  1314. pPathPoolDeAllocator
  1315. );
  1316. }
  1317. #define PathPoolGetRegValueString(key,valuename) (PTSTR) pPathPoolGetRegValueDataOfType((key),(valuename),REG_SZ)
  1318. #define PathPoolGetRegValueBinary(key,valuename) (PBYTE) pPathPoolGetRegValueDataOfType((key),(valuename),REG_BINARY)
  1319. PCTSTR
  1320. pGetRegValuePath (
  1321. IN HKEY Key,
  1322. IN PCTSTR Value
  1323. )
  1324. {
  1325. PCTSTR data;
  1326. DWORD type;
  1327. DWORD size;
  1328. BOOL b = TRUE;
  1329. //
  1330. // the data may be stored as bytes (each byte a CHAR)
  1331. // if REG_BINARY value ending with 0, treat it as a string
  1332. //
  1333. if (!GetRegValueTypeAndSize (Key, Value, &type, &size)) {
  1334. return NULL;
  1335. }
  1336. switch (type) {
  1337. case REG_SZ:
  1338. data = PathPoolGetRegValueString (Key, Value);
  1339. break;
  1340. case REG_BINARY:
  1341. if (size) {
  1342. data = (PCTSTR)PathPoolGetRegValueBinary (Key, Value);
  1343. b = (data && data[(size / sizeof (TCHAR)) - 1] == 0);
  1344. } else {
  1345. b = FALSE;
  1346. }
  1347. break;
  1348. default:
  1349. data = NULL;
  1350. }
  1351. b = b && pIsValidPath (data);
  1352. if (!b) {
  1353. //
  1354. // invalid data
  1355. //
  1356. if (data) {
  1357. pPathPoolDeAllocator (data);
  1358. data = NULL;
  1359. }
  1360. }
  1361. return data;
  1362. }
  1363. /*++
  1364. Routine Description:
  1365. EnumFirstRegShellFolder and EnumNextRegShellFolder are enumeration routines that
  1366. enumerate all shell folders per system or for a particular user.
  1367. Arguments:
  1368. e - enumeration structure
  1369. EnumPtr - user enumeration structure
  1370. Return Value:
  1371. Both routines return TRUE if a new shell folder could be found, FALSE otherwise
  1372. --*/
  1373. BOOL
  1374. EnumNextRegShellFolder (
  1375. IN OUT PSF_ENUM e
  1376. );
  1377. BOOL
  1378. EnumFirstRegShellFolder (
  1379. IN OUT PSF_ENUM e,
  1380. IN PUSERENUM EnumPtr
  1381. )
  1382. {
  1383. BOOL b = FALSE;
  1384. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  1385. e->EnumPtr = EnumPtr;
  1386. e->VirtualSF = FALSE;
  1387. e->ProfileSF = FALSE;
  1388. e->FirstCall = FALSE;
  1389. e->sfCollapse = TRUE;
  1390. e->enumeratedSf = HtAllocWithData (sizeof (PCTSTR));
  1391. if (EnumPtr) {
  1392. e->SfKey = OpenRegKey (EnumPtr->UserRegKey, S_SHELL_FOLDERS_KEY_USER);
  1393. } else {
  1394. e->SfKey = OpenRegKeyStr (S_SHELL_FOLDERS_KEY_SYSTEM);
  1395. }
  1396. if (!e->SfKey) {
  1397. b = pEnumFirstVirtualShellFolder (e);
  1398. if (b) {
  1399. e->sfCollapse = !InfFindFirstLine (
  1400. g_Win95UpgInf,
  1401. S_ONE_USER_SHELL_FOLDERS,
  1402. e->sfName,
  1403. &is
  1404. );
  1405. InfCleanUpInfStruct (&is);
  1406. }
  1407. }
  1408. e->FirstCall = TRUE;
  1409. return EnumNextRegShellFolder (e);
  1410. }
  1411. BOOL
  1412. EnumNextRegShellFolder (
  1413. IN OUT PSF_ENUM e
  1414. )
  1415. {
  1416. HKEY UsfKey;
  1417. BOOL b = FALSE;
  1418. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  1419. PCTSTR enumPath;
  1420. if (!e->VirtualSF) {
  1421. if (!e->FirstCall) {
  1422. FreePathString (e->sfName);
  1423. e->sfName = NULL;
  1424. FreePathString (e->sfPath);
  1425. e->sfPath = NULL;
  1426. }
  1427. do {
  1428. if (e->FirstCall?EnumFirstRegValue (&e->SfKeyEnum, e->SfKey):EnumNextRegValue (&e->SfKeyEnum)) {
  1429. e->sfName = pGetShellFolderLongName (e->SfKeyEnum.ValueName);
  1430. e->sfPath = NULL;
  1431. if (e->EnumPtr) {
  1432. UsfKey = OpenRegKey (e->EnumPtr->UserRegKey, S_USHELL_FOLDERS_KEY_USER);
  1433. } else {
  1434. UsfKey = OpenRegKeyStr (S_USHELL_FOLDERS_KEY_SYSTEM);
  1435. }
  1436. if (UsfKey) {
  1437. e->sfPath = pGetRegValuePath (UsfKey, e->SfKeyEnum.ValueName);
  1438. CloseRegKey (UsfKey);
  1439. }
  1440. if (e->sfPath == NULL) {
  1441. e->sfPath = pGetRegValuePath (e->SfKey, e->SfKeyEnum.ValueName);
  1442. }
  1443. if (e->sfPath != NULL) {
  1444. b = TRUE;
  1445. enumPath = PoolMemDuplicateString (g_SFPool, e->sfPath);
  1446. HtAddStringAndData (e->enumeratedSf, e->sfName, &enumPath);
  1447. }
  1448. } else {
  1449. CloseRegKey (e->SfKey);
  1450. b = pEnumFirstVirtualShellFolder (e);
  1451. }
  1452. e->FirstCall = FALSE;
  1453. } while (e->sfPath == NULL && !e->VirtualSF);
  1454. } else {
  1455. b = pEnumNextVirtualShellFolder (e);
  1456. }
  1457. if (b) {
  1458. e->sfCollapse = !InfFindFirstLine (
  1459. g_Win95UpgInf,
  1460. S_ONE_USER_SHELL_FOLDERS,
  1461. e->sfName,
  1462. &is
  1463. );
  1464. InfCleanUpInfStruct (&is);
  1465. }
  1466. return b;
  1467. }
  1468. BOOL
  1469. EnumAbortRegShellFolder (
  1470. IN OUT PSF_ENUM e
  1471. )
  1472. {
  1473. pAbortEnumVirtualShellFolder (e);
  1474. return TRUE;
  1475. }
  1476. VOID
  1477. pRecordShellFolderInMemDb (
  1478. IN PSHELLFOLDER psf
  1479. )
  1480. {
  1481. TCHAR Node[MEMDB_MAX];
  1482. g_SfSequencer++;
  1483. wsprintf (
  1484. Node,
  1485. TEXT("%s\\%s\\%s\\\001%u"),
  1486. MEMDB_CATEGORY_SF_ORDER_NAME_SRC,
  1487. psf->Name,
  1488. psf->SrcPath,
  1489. g_SfSequencer
  1490. );
  1491. MemDbSetValue (Node, (DWORD) psf);
  1492. g_SfSequencer++;
  1493. wsprintf (
  1494. Node,
  1495. TEXT("%s\\%s\\\001%u"),
  1496. MEMDB_CATEGORY_SF_ORDER_SRC,
  1497. psf->SrcPath,
  1498. g_SfSequencer
  1499. );
  1500. MemDbSetValue (Node, (DWORD) psf);
  1501. }
  1502. VOID
  1503. pGatherCommonShellFoldersData (
  1504. VOID
  1505. )
  1506. /*++
  1507. Routine Description:
  1508. pGatherCommonShellFoldersData walks the system shell folders creating a
  1509. linked list with data that will be used later.
  1510. Arguments:
  1511. none
  1512. Return Value:
  1513. none
  1514. --*/
  1515. {
  1516. SF_ENUM e;
  1517. TCHAR Node[MEMDB_MAX];
  1518. PSHELLFOLDER psf;
  1519. PTSTR endStr;
  1520. if (EnumFirstRegShellFolder (&e, NULL)) {
  1521. do {
  1522. if (!pIsSkippedSf (e.sfName)) {
  1523. // if this is the startup group, store this
  1524. if (StringIMatch (e.sfName, S_SYSTEM_STARTUP)) {
  1525. MemDbSetValueEx (MEMDB_CATEGORY_SF_STARTUP, e.sfPath, TEXT("*"), NULL, 0, NULL);
  1526. }
  1527. psf = (PSHELLFOLDER) PoolMemGetMemory (g_SFPool, sizeof (SHELLFOLDER));
  1528. ZeroMemory (psf, sizeof (SHELLFOLDER));
  1529. //
  1530. // CanBeCollapsed does not make sense for common shell folders,
  1531. // since collapsing is the action of moving per-user folders
  1532. // into the common folder.
  1533. //
  1534. psf->CanBeCollapsed = TRUE;
  1535. psf->Next = g_ShellFolders;
  1536. g_ShellFolders = psf;
  1537. psf->Name = PoolMemDuplicateString (g_SFPool, e.sfName);
  1538. psf->SrcPath = PoolMemDuplicateString (g_SFPool, e.sfPath);
  1539. endStr = GetEndOfString (psf->SrcPath);
  1540. endStr = _tcsdec (psf->SrcPath, endStr);
  1541. if (endStr && (_tcsnextc (endStr) == TEXT('\\')) && ((endStr - psf->SrcPath) > 2) ) {
  1542. *endStr = 0;
  1543. }
  1544. //
  1545. // Determine destination, either the NT default location, or
  1546. // the current location.
  1547. //
  1548. if (!pIsMassiveDir (psf->SrcPath) &&
  1549. !pIsPreservedSf (psf->Name) &&
  1550. pIsTheDefaultLocation (psf->Name, psf->SrcPath, NULL, ANY_COMMON) &&
  1551. pIsNtShellFolder (psf->Name, FALSE, FALSE)
  1552. ) {
  1553. pGetNtShellFolderPath (psf->Name, NULL, Node, sizeof (Node));
  1554. psf->DestPath = PoolMemDuplicateString (g_SFPool, Node);
  1555. }
  1556. if (!psf->DestPath) {
  1557. psf->DestPath = PoolMemDuplicateString (g_SFPool, psf->SrcPath);
  1558. }
  1559. //
  1560. // Save references to memdb
  1561. //
  1562. pRecordShellFolderInMemDb (psf);
  1563. AddShellFolder (psf->Name, psf->SrcPath);
  1564. }
  1565. } while (EnumNextRegShellFolder (&e));
  1566. }
  1567. EnumAbortRegShellFolder (&e);
  1568. }
  1569. VOID
  1570. pGatherUserShellFoldersData (
  1571. IN PUSERENUM EnumPtr
  1572. )
  1573. /*++
  1574. Routine Description:
  1575. pGatherUserShellFoldersData walks the shell folders for a user, creating a
  1576. linked list with data that will be used later.
  1577. Arguments:
  1578. EnumPtr - User enumeration structure
  1579. Return Value:
  1580. None
  1581. --*/
  1582. {
  1583. SF_ENUM e;
  1584. TCHAR Node[MEMDB_MAX];
  1585. PSHELLFOLDER psf;
  1586. UINT driveClusterSize;
  1587. UINT infClusterSize;
  1588. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  1589. UINT fieldIndex;
  1590. UINT sfSize;
  1591. PTSTR endStr;
  1592. g_TotalUsers++;
  1593. if (EnumFirstRegShellFolder (&e, EnumPtr)) {
  1594. do {
  1595. if (!pIsSkippedSf (e.sfName)) {
  1596. // if this is the startup group, store this
  1597. if (StringIMatch (e.sfName, S_USER_STARTUP)) {
  1598. MemDbSetValueEx (MEMDB_CATEGORY_SF_STARTUP, e.sfPath, TEXT("*"), NULL, 0, NULL);
  1599. }
  1600. psf = (PSHELLFOLDER) PoolMemGetMemory (g_SFPool, sizeof (SHELLFOLDER));
  1601. ZeroMemory (psf, sizeof (SHELLFOLDER));
  1602. psf->CanBeCollapsed = e.sfCollapse;
  1603. psf->Next = g_ShellFolders;
  1604. g_ShellFolders = psf;
  1605. psf->Name = PoolMemDuplicateString (g_SFPool, e.sfName);
  1606. psf->FixedUserName = PoolMemDuplicateString (g_SFPool, EnumPtr->FixedUserName);
  1607. psf->UserName = PoolMemDuplicateString (g_SFPool, EnumPtr->UserName);
  1608. psf->SrcPath = PoolMemDuplicateString (g_SFPool, e.sfPath);
  1609. endStr = GetEndOfString (psf->SrcPath);
  1610. endStr = _tcsdec (psf->SrcPath, endStr);
  1611. if (endStr && (_tcsnextc (endStr) == TEXT('\\')) && ((endStr - psf->SrcPath) > 2) ) {
  1612. *endStr = 0;
  1613. }
  1614. //
  1615. // Determine destination, either the NT default location or
  1616. // the current Win9x location
  1617. //
  1618. if (!pIsMassiveDir (psf->Name) &&
  1619. !pIsPreservedSf (psf->Name) &&
  1620. pIsTheDefaultLocation (psf->Name, psf->SrcPath, EnumPtr->UserName, ANY_DEFAULT) &&
  1621. pIsNtShellFolder (psf->Name, TRUE, FALSE)
  1622. ) {
  1623. pGetNtShellFolderPath (psf->Name, EnumPtr->FixedUserName, Node, sizeof (Node));
  1624. psf->DestPath = PoolMemDuplicateString (g_SFPool, Node);
  1625. } else {
  1626. psf->DestPath = PoolMemDuplicateString (g_SFPool, psf->SrcPath);
  1627. //
  1628. // Now let's see if the preserved directory is on a different drive. If yes,
  1629. // we will need some additional space on that drive to copy files from the
  1630. // default NT shell folder
  1631. //
  1632. if ((pIsNtShellFolder (psf->Name, TRUE, FALSE)) &&
  1633. (_totupper (psf->SrcPath[0]) != _totupper (g_WinDir[0]))
  1634. ) {
  1635. driveClusterSize = QueryClusterSize (psf->SrcPath);
  1636. if (driveClusterSize) {
  1637. MYASSERT (g_Win95UpgInf);
  1638. if (InfFindFirstLine (g_Win95UpgInf, S_SHELL_FOLDERS_DISK_SPACE, psf->Name, &is)) {
  1639. infClusterSize = 256;
  1640. fieldIndex = 1;
  1641. sfSize = 0;
  1642. while (infClusterSize < driveClusterSize) {
  1643. if (!InfGetIntField (&is, fieldIndex, &sfSize)) {
  1644. break;
  1645. }
  1646. fieldIndex ++;
  1647. infClusterSize *= 2;
  1648. }
  1649. if (sfSize) {
  1650. UseSpace (psf->SrcPath, sfSize);
  1651. }
  1652. }
  1653. InfCleanUpInfStruct (&is);
  1654. }
  1655. }
  1656. }
  1657. //
  1658. // Save references to memdb
  1659. //
  1660. pRecordShellFolderInMemDb (psf);
  1661. AddShellFolder (psf->Name, psf->SrcPath);
  1662. }
  1663. } while (EnumNextRegShellFolder (&e));
  1664. }
  1665. EnumAbortRegShellFolder (&e);
  1666. }
  1667. BOOL
  1668. pPreserveAllShellFolders (
  1669. PCTSTR ShellFolderName,
  1670. PCTSTR ShellFolderPath
  1671. )
  1672. /*++
  1673. Routine Description:
  1674. pPreserveAllShellFolders walks the shell folders structures preserving
  1675. the location for all the shell folders that match the arguments.
  1676. Arguments:
  1677. ShellFolderName - Specifies the Win9x shell folder identifier to preserve
  1678. ShellFolderPath - Specifies the shell folder path to preserve
  1679. Return Value:
  1680. Always TRUE.
  1681. --*/
  1682. {
  1683. TCHAR Node[MEMDB_MAX];
  1684. PSHELLFOLDER psf;
  1685. MEMDB_ENUM enumSF;
  1686. MemDbBuildKey (
  1687. Node,
  1688. MEMDB_CATEGORY_SF_ORDER_NAME_SRC,
  1689. ShellFolderName,
  1690. ShellFolderPath,
  1691. TEXT("*")
  1692. );
  1693. if (MemDbEnumFirstValue (&enumSF, Node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  1694. do {
  1695. psf = SFSTRUCT(enumSF);
  1696. if (psf->FixedUserName) {
  1697. psf->DestPath = psf->SrcPath;
  1698. }
  1699. } while (MemDbEnumNextValue (&enumSF));
  1700. }
  1701. return TRUE;
  1702. }
  1703. BOOL
  1704. pCollapseAllShellFolders (
  1705. PCTSTR ShellFolderName,
  1706. PCTSTR ShellFolderCommonName,
  1707. PCTSTR ShellFolderPath,
  1708. PCTSTR UserName OPTIONAL // only if we have only one user
  1709. )
  1710. /*++
  1711. Routine Description:
  1712. pCollapseAllShellFolders collapses a number of user shell folders into
  1713. a system shell folder.
  1714. Arguments:
  1715. ShellFolderName - Specifies the Win9x shell folder identifier
  1716. ShellFolderCommonName - Specifies the Win9x shell folder identifier that
  1717. has "Common" in it
  1718. ShellFolderPath - Specifies the common shell folder source path.
  1719. Return Value:
  1720. TRUE if successfull, FALSE if not.
  1721. --*/
  1722. {
  1723. TCHAR Node[MEMDB_MAX];
  1724. PSHELLFOLDER psf;
  1725. MEMDB_ENUM enumSF;
  1726. //
  1727. // First step. Search the list of shell folders eliminating the
  1728. // ones that are per-user and contain the data matching the caller's
  1729. // arguments. Then we build a common structure.
  1730. //
  1731. psf = g_ShellFolders;
  1732. while (psf) {
  1733. if ((psf->Name) &&
  1734. (psf->FixedUserName) &&
  1735. (StringIMatch (psf->Name, ShellFolderName)) &&
  1736. (StringIMatch (psf->SrcPath, ShellFolderPath))
  1737. ) {
  1738. //
  1739. // Eliminate the folders that will actually become All Users
  1740. // on NT. We will delete the memdb index entries just before
  1741. // returning (see below).
  1742. //
  1743. psf->Name = NULL;
  1744. }
  1745. psf = psf->Next;
  1746. }
  1747. //
  1748. // Add the common shell folder to the list
  1749. //
  1750. psf = (PSHELLFOLDER) PoolMemGetMemory (g_SFPool, sizeof (SHELLFOLDER));
  1751. ZeroMemory (psf, sizeof (SHELLFOLDER));
  1752. psf->CanBeCollapsed = TRUE;
  1753. psf->Next = g_ShellFolders;
  1754. g_ShellFolders = psf;
  1755. psf->Name = PoolMemDuplicateString (g_SFPool, ShellFolderCommonName);
  1756. psf->SrcPath = PoolMemDuplicateString (g_SFPool, ShellFolderPath);
  1757. if (!pIsPreservedSf (ShellFolderCommonName) &&
  1758. pIsTheDefaultLocation (ShellFolderName, ShellFolderPath, UserName, ANY_COMMON)
  1759. ) {
  1760. pGetNtShellFolderPath (ShellFolderCommonName, NULL, Node, sizeof (Node));
  1761. psf->DestPath = PoolMemDuplicateString (g_SFPool, Node);
  1762. }
  1763. if (!psf->DestPath) {
  1764. psf->DestPath = PoolMemDuplicateString (g_SFPool, ShellFolderPath);
  1765. }
  1766. //
  1767. // Before adding the new node to the index in memdb, look if a common
  1768. // shell folder with this name already exists. If it does, we will
  1769. // take the destination path from there, otherwise, we will use the
  1770. // current one.
  1771. //
  1772. MemDbBuildKey (
  1773. Node,
  1774. MEMDB_CATEGORY_SF_ORDER_NAME_SRC,
  1775. ShellFolderCommonName,
  1776. TEXT("*"),
  1777. NULL
  1778. );
  1779. if (MemDbEnumFirstValue (&enumSF, Node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  1780. if (!StringIMatch (psf->DestPath, SFSTRUCT(enumSF)->DestPath)) {
  1781. //
  1782. // This case occurs when a common folder was previously preserved
  1783. //
  1784. psf->DestPath = PoolMemDuplicateString (
  1785. g_SFPool,
  1786. SFSTRUCT(enumSF)->DestPath
  1787. );
  1788. }
  1789. }
  1790. //
  1791. // Save references to memdb
  1792. //
  1793. pRecordShellFolderInMemDb (psf);
  1794. //
  1795. // Finally eliminate all MemDb entries for the deleted structures.
  1796. //
  1797. MemDbBuildKey (
  1798. Node,
  1799. MEMDB_CATEGORY_SF_ORDER_NAME_SRC,
  1800. ShellFolderName,
  1801. ShellFolderPath,
  1802. NULL
  1803. );
  1804. MemDbDeleteTree (Node);
  1805. return TRUE;
  1806. }
  1807. VOID
  1808. pComputeCommonName (
  1809. OUT PTSTR Buffer,
  1810. IN PCTSTR PerUserName
  1811. )
  1812. {
  1813. MYASSERT(g_CommonFromPerUserMap);
  1814. StringCopy (Buffer, PerUserName);
  1815. if (MappingSearchAndReplace (g_CommonFromPerUserMap, Buffer, MAX_SHELLFOLDER_NAME)) {
  1816. return;
  1817. }
  1818. wsprintf (Buffer, TEXT("%s %s"), TEXT("Common"), PerUserName);
  1819. return;
  1820. }
  1821. VOID
  1822. pComputePerUserName (
  1823. OUT PTSTR Buffer,
  1824. IN PCTSTR CommonName
  1825. )
  1826. {
  1827. MYASSERT(g_PerUserFromCommonMap);
  1828. StringCopy (Buffer, CommonName);
  1829. if (MappingSearchAndReplace (g_PerUserFromCommonMap, Buffer, MAX_SHELLFOLDER_NAME)) {
  1830. return;
  1831. }
  1832. if (StringIPrefix (CommonName, TEXT("Common"))) {
  1833. CommonName += 6;
  1834. if (_tcsnextc (CommonName) == TEXT(' ')) {
  1835. CommonName++;
  1836. }
  1837. StringCopy (Buffer, CommonName);
  1838. }
  1839. return;
  1840. }
  1841. BOOL
  1842. pIsPerUserSf (
  1843. IN PCTSTR TagName
  1844. )
  1845. {
  1846. TCHAR testBuf[MAX_SHELLFOLDER_NAME];
  1847. //
  1848. // Tag is common if it has a "Common" prefix or can be mapped from g_PerUserFromCommonMap
  1849. //
  1850. if (StringIPrefix (TagName, TEXT("Common"))) {
  1851. return FALSE;
  1852. }
  1853. StringCopy (testBuf, TagName);
  1854. if (MappingSearchAndReplace (g_PerUserFromCommonMap, testBuf, MAX_SHELLFOLDER_NAME)) {
  1855. return FALSE;
  1856. }
  1857. return TRUE;
  1858. }
  1859. VOID
  1860. pProcessShellFoldersInfo (
  1861. VOID
  1862. )
  1863. /*++
  1864. Routine Description:
  1865. pProcessShellFoldersInfo walks the shell folders structures, rearranging
  1866. the structures and/or modifying the destination paths. This function is
  1867. called after all shell folders have been identified. The purpose is to
  1868. move all common shell folders to All Users, and to preserve the locations
  1869. of shell folders that are non-standard.
  1870. Arguments:
  1871. None
  1872. Return Value:
  1873. None
  1874. --*/
  1875. {
  1876. TCHAR lastSfName[MAX_SHELLFOLDER_NAME];
  1877. TCHAR node[MEMDB_MAX];
  1878. TCHAR lastPath[MAX_TCHAR_PATH];
  1879. TCHAR commonName[MAX_SHELLFOLDER_NAME];
  1880. MEMDB_ENUM enumSF;
  1881. DWORD UsersWithIdenticalPaths;
  1882. DWORD UsersWithDefaultCommonPath;
  1883. DWORD UsersWithFolder;
  1884. PSHELLFOLDER lastSf;
  1885. PSHELLFOLDER testSf;
  1886. BOOL sentinel;
  1887. DWORD extraUsers = 0;
  1888. if (CANCELLED()) {
  1889. return;
  1890. }
  1891. //
  1892. // Enumerate the SHELLFOLDER structures, ordered by sorted shell folder name, and
  1893. // see if all users point to the same source path.
  1894. //
  1895. MemDbBuildKey (node, MEMDB_CATEGORY_SF_ORDER_NAME_SRC, TEXT("*"), NULL, NULL);
  1896. if (MemDbEnumFirstValue (&enumSF, node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  1897. lastSfName[0] = 0;
  1898. lastPath[0] = 0;
  1899. UsersWithIdenticalPaths = 0;
  1900. UsersWithDefaultCommonPath = 0;
  1901. UsersWithFolder = 0;
  1902. lastSf = NULL;
  1903. sentinel = FALSE;
  1904. extraUsers = 0;
  1905. for (;;) {
  1906. testSf = SFSTRUCT(enumSF);
  1907. if (sentinel || !StringIMatch (lastSfName, testSf->Name)) {
  1908. //
  1909. // This shell folder is not the same as the previous folder,
  1910. // or the sentinel triggered one final test.
  1911. //
  1912. DEBUGMSG ((DBG_USERLOOP, "%u users have shell folder %s", UsersWithFolder, lastSfName));
  1913. if (UsersWithDefaultCommonPath == (g_TotalUsers + extraUsers)) {
  1914. //
  1915. // If lastSf is non-NULL, then we know it is per-user and the previous
  1916. // shell folder maps to one or more users, and all users point to
  1917. // the same place.
  1918. //
  1919. if (lastSf) {
  1920. //
  1921. // If this shell folder is forced to be per-user, then
  1922. // don't collapse it into the common location if there
  1923. // is only one user. Otherwise, point all users to the
  1924. // NT All Users shell folder, and leave the per-user
  1925. // location empty.
  1926. //
  1927. if (UsersWithDefaultCommonPath > 1 || !pIsPerUserWanted (lastSf->Name)) {
  1928. pComputeCommonName (commonName, lastSf->Name);
  1929. if (pIsNtShellFolder (commonName, FALSE, FALSE)) {
  1930. //
  1931. // All users point to the same shell folder, and the shell
  1932. // folder is a default location. Therefore, make sure they
  1933. // all are mapped to NT's All Users location.
  1934. //
  1935. pCollapseAllShellFolders (
  1936. lastSf->Name,
  1937. commonName,
  1938. lastSf->SrcPath,
  1939. (UsersWithDefaultCommonPath == 1)?lastSf->UserName:NULL
  1940. );
  1941. }
  1942. }
  1943. }
  1944. } else {
  1945. //
  1946. // If 2 or more users point to this location, but not all users
  1947. // point here, then preserve all uses of this shell folder/source
  1948. // path pair.
  1949. //
  1950. if (UsersWithIdenticalPaths > 1) {
  1951. DEBUGMSG ((
  1952. DBG_USERLOOP,
  1953. "%u users point to %s for %s (common), and %u point to default location, but there are %u users %s",
  1954. UsersWithIdenticalPaths,
  1955. lastSf->SrcPath,
  1956. lastSf->Name,
  1957. UsersWithDefaultCommonPath,
  1958. g_TotalUsers,
  1959. extraUsers ? TEXT("plus <default>") : TEXT("")
  1960. ));
  1961. pPreserveAllShellFolders (lastSf->Name, lastSf->SrcPath);
  1962. }
  1963. }
  1964. //
  1965. // We have to break out now when sentinel is TRUE. This is our
  1966. // only exit condition for this loop.
  1967. //
  1968. if (sentinel) {
  1969. break;
  1970. }
  1971. //
  1972. // Keep track of the name of the shell folder (for comparison the
  1973. // next time through the loop)
  1974. //
  1975. StringCopy (lastSfName, testSf->Name);
  1976. StringCopy (lastPath, testSf->SrcPath);
  1977. UsersWithIdenticalPaths = 0;
  1978. UsersWithDefaultCommonPath = 0;
  1979. UsersWithFolder = 0;
  1980. extraUsers = 0;
  1981. lastSf = NULL; // works without this, but added for less tests
  1982. }
  1983. UsersWithFolder++;
  1984. //
  1985. // Is this a per-user shell folder?
  1986. //
  1987. if (testSf->FixedUserName) {
  1988. //
  1989. // Yes, compare its path against the previous path
  1990. //
  1991. if (StringIMatch (lastPath, testSf->SrcPath)) {
  1992. UsersWithIdenticalPaths++;
  1993. if (pIsTheDefaultLocation (testSf->Name, testSf->SrcPath, testSf->FixedUserName, ANY_COMMON)) {
  1994. if (testSf->CanBeCollapsed) {
  1995. UsersWithDefaultCommonPath++;
  1996. } else {
  1997. DEBUGMSG ((
  1998. DBG_USERLOOP,
  1999. "User %s uses the default common path for %s, but it can't be collapsed",
  2000. testSf->FixedUserName,
  2001. testSf->Name
  2002. ));
  2003. }
  2004. }
  2005. ELSE_DEBUGMSG ((
  2006. DBG_USERLOOP,
  2007. "User %s does not use the default common path for %s",
  2008. testSf->FixedUserName,
  2009. testSf->Name
  2010. ));
  2011. } else {
  2012. //
  2013. // At least two users have different paths. Were there 2 or
  2014. // more users using the same path? If so, preserve that path.
  2015. //
  2016. if (UsersWithIdenticalPaths > 1) {
  2017. DEBUGMSG ((
  2018. DBG_USERLOOP,
  2019. "%u users point to %s for %s (per-user), but there are %u users %s",
  2020. UsersWithIdenticalPaths,
  2021. lastSf->SrcPath,
  2022. lastSf->Name,
  2023. g_TotalUsers,
  2024. extraUsers ? TEXT("plus <default>") : TEXT("")
  2025. ));
  2026. pPreserveAllShellFolders (lastSf->Name, lastSf->SrcPath);
  2027. }
  2028. //
  2029. // Now we must compare against a different path
  2030. //
  2031. UsersWithIdenticalPaths = 1;
  2032. StringCopy (lastPath, testSf->SrcPath);
  2033. }
  2034. lastSf = testSf;
  2035. } else {
  2036. extraUsers = 1;
  2037. UsersWithIdenticalPaths = 1;
  2038. if (pIsTheDefaultLocation (testSf->Name, testSf->SrcPath, testSf->FixedUserName, ANY_COMMON)) {
  2039. UsersWithDefaultCommonPath++;
  2040. }
  2041. ELSE_DEBUGMSG ((
  2042. DBG_USERLOOP,
  2043. "User %s does not use the default common path for %s",
  2044. testSf->FixedUserName,
  2045. testSf->Name
  2046. ));
  2047. }
  2048. if (!MemDbEnumNextValue (&enumSF)) {
  2049. //
  2050. // We're just about done.
  2051. //
  2052. // This will cause us to test the last shell folder,
  2053. // and then break out of the loop:
  2054. //
  2055. sentinel = TRUE;
  2056. }
  2057. }
  2058. }
  2059. }
  2060. VOID
  2061. pIgnoreShellFolder (
  2062. PSHELLFOLDER DisableSf
  2063. )
  2064. {
  2065. TREE_ENUM treeEnum;
  2066. BOOL fileFound;
  2067. TCHAR buffer[MAX_TCHAR_PATH];
  2068. DWORD status;
  2069. if (DisableSf && DisableSf->DestPath) {
  2070. DEBUGMSG_IF ((
  2071. DisableSf->UserName != NULL,
  2072. DBG_USERLOOP,
  2073. "Disabling %s for %s because it should be empty",
  2074. DisableSf->Name,
  2075. DisableSf->UserName
  2076. ));
  2077. if (StringIMatch (DisableSf->Name, TEXT("Personal"))) {
  2078. //
  2079. // Check if source has at least one file that belongs
  2080. // to the user
  2081. //
  2082. if (EnumFirstFileInTreeEx (
  2083. &treeEnum,
  2084. DisableSf->SrcPath,
  2085. NULL,
  2086. FALSE,
  2087. FALSE,
  2088. FILE_ENUM_ALL_LEVELS
  2089. )) {
  2090. fileFound = FALSE;
  2091. do {
  2092. if (CANCELLED()) {
  2093. AbortEnumFileInTree (&treeEnum);
  2094. return;
  2095. }
  2096. if (!treeEnum.Directory) {
  2097. status = GetFileStatusOnNt (treeEnum.FullPath);
  2098. status &= FILESTATUS_DELETED|
  2099. FILESTATUS_NTINSTALLED|
  2100. FILESTATUS_REPLACED;
  2101. if (!status) {
  2102. fileFound = TRUE;
  2103. AbortEnumFileInTree (&treeEnum);
  2104. break;
  2105. }
  2106. }
  2107. } while (EnumNextFileInTree (&treeEnum));
  2108. if (fileFound &&
  2109. pGetNtShellFolderPath (
  2110. DisableSf->Name,
  2111. DisableSf->FixedUserName,
  2112. buffer,
  2113. sizeof (buffer)
  2114. )) {
  2115. //
  2116. // At least one file -- add warning text file
  2117. //
  2118. MemDbSetValueEx (
  2119. MEMDB_CATEGORY_MYDOCS_WARNING,
  2120. DisableSf->FixedUserName,
  2121. buffer,
  2122. NULL,
  2123. 0,
  2124. NULL
  2125. );
  2126. }
  2127. }
  2128. }
  2129. DisableSf->DestPath = NULL;
  2130. }
  2131. }
  2132. VOID
  2133. pResolveSourceCollisions (
  2134. VOID
  2135. )
  2136. /*++
  2137. Routine Description:
  2138. pResolveSourceCollisions walks the list of shell folders and redirects
  2139. destinations when source paths collide. If a source path of one shell
  2140. folder matches another, and one of the paths is status "merged," then the
  2141. other should be the same. By definition, given one source path, we cannot
  2142. have a merge to two separate locations.
  2143. NOTE: Merge simply means the source path is different from the dest path.
  2144. After resolving collisions, this function scans the SFs again, eliminating
  2145. common folders that are redirected to per-user locations, if that per-user
  2146. location is in its default location.
  2147. Arguments:
  2148. None.
  2149. Return Value:
  2150. None.
  2151. --*/
  2152. {
  2153. MEMDB_ENUM enumSF;
  2154. GROWBUFFER sfPtrList = GROWBUF_INIT;
  2155. PSHELLFOLDER thisSf;
  2156. PSHELLFOLDER compareSf;
  2157. PSHELLFOLDER disableSf;
  2158. PSHELLFOLDER commonSf;
  2159. PSHELLFOLDER perUserSf;
  2160. PSHELLFOLDER *listPtr;
  2161. INT i;
  2162. INT j;
  2163. INT count;
  2164. INT lookAhead;
  2165. TCHAR node[MEMDB_MAX];
  2166. BOOL thisMoved;
  2167. BOOL compareMoved;
  2168. UINT p1, p2;
  2169. TCHAR commonName[MAX_SHELLFOLDER_NAME];
  2170. TCHAR perUserName[MAX_SHELLFOLDER_NAME];
  2171. INT duplicates;
  2172. INT total;
  2173. if (CANCELLED()) {
  2174. return;
  2175. }
  2176. //
  2177. // Put the shell folder pointers into an array
  2178. //
  2179. MemDbBuildKey (node, MEMDB_CATEGORY_SF_ORDER_NAME_SRC, TEXT("*"), NULL, NULL);
  2180. if (MemDbEnumFirstValue (&enumSF, node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  2181. do {
  2182. listPtr = (PSHELLFOLDER *) GrowBuffer (&sfPtrList, sizeof (PSHELLFOLDER));
  2183. *listPtr = SFSTRUCT(enumSF);
  2184. } while (MemDbEnumNextValue (&enumSF));
  2185. }
  2186. count = (sfPtrList.End / sizeof (PSHELLFOLDER)) - 1;
  2187. listPtr = (PSHELLFOLDER *) sfPtrList.Buf;
  2188. for (i = 0 ; i <= count ; i++) {
  2189. thisSf = listPtr[i];
  2190. thisSf->SourceExists = DoesFileExist (thisSf->SrcPath);
  2191. }
  2192. //
  2193. // Eliminate shell folders that have per user shell folder names but
  2194. // don't have a user.
  2195. //
  2196. for (i = 0 ; i < count ; i++) {
  2197. perUserSf = listPtr[i];
  2198. if (pIsPerUserSf (perUserSf->Name) && perUserSf->UserName == NULL) {
  2199. pIgnoreShellFolder (perUserSf);
  2200. }
  2201. }
  2202. //
  2203. // Scan for a common shell folder that has the same source location of
  2204. // two or more per-user shell folders. When this case is found, discard
  2205. // the whole set, so they are preserved.
  2206. //
  2207. for (i = 0 ; i < count ; i++) {
  2208. commonSf = listPtr[i];
  2209. //
  2210. // Has this sf been processed already? Or is it just a logical folder?
  2211. // Or is it per-user?
  2212. //
  2213. if (commonSf->MergedIntoOtherShellFolder) {
  2214. continue;
  2215. }
  2216. if (!commonSf->SourceExists) {
  2217. continue;
  2218. }
  2219. if (!commonSf->DestPath) {
  2220. continue;
  2221. }
  2222. if (pIsPerUserSf (commonSf->Name)) {
  2223. continue;
  2224. }
  2225. pComputePerUserName (perUserName, commonSf->Name);
  2226. //
  2227. // Count all the per-user shell folders that have the same source path
  2228. // as the common. If it is just one, then we're going to use the
  2229. // per-user location instead of the common location. If it is more
  2230. // than one, but not all, then we're going to preserve the location
  2231. // for the path. If it is everyone, then we're going to use the common
  2232. // location.
  2233. //
  2234. duplicates = 0;
  2235. total = 0;
  2236. for (j = 0 ; j <= count ; j++) {
  2237. perUserSf = listPtr[j];
  2238. if (perUserSf == commonSf) {
  2239. continue;
  2240. }
  2241. if (!perUserSf->DestPath) {
  2242. continue;
  2243. }
  2244. if (!StringIMatch (perUserSf->Name, perUserName)) {
  2245. continue;
  2246. }
  2247. total++;
  2248. if (StringIMatch (commonSf->SrcPath, perUserSf->SrcPath)) {
  2249. duplicates++;
  2250. }
  2251. }
  2252. if (duplicates <= 1) {
  2253. //
  2254. // Do nothing (resolved later)
  2255. //
  2256. } else {
  2257. DEBUGMSG_IF ((
  2258. duplicates < total,
  2259. DBG_USERLOOP,
  2260. "Preserving all references to %s for shell folder %s, because some (but not all) users point to the same path",
  2261. commonSf->SrcPath,
  2262. perUserName
  2263. ));
  2264. DEBUGMSG_IF ((
  2265. duplicates == total,
  2266. DBG_USERLOOP,
  2267. "All users use common location %s",
  2268. commonSf->SrcPath
  2269. ));
  2270. for (j = 0 ; j <= count ; j++) {
  2271. perUserSf = listPtr[j];
  2272. if (perUserSf == commonSf) {
  2273. continue;
  2274. }
  2275. if (!perUserSf->DestPath) {
  2276. continue;
  2277. }
  2278. if (!StringIMatch (perUserSf->Name, perUserName)) {
  2279. continue;
  2280. }
  2281. if (duplicates < total) {
  2282. //
  2283. // Preserve the location to this source path
  2284. //
  2285. if (StringIMatch (commonSf->SrcPath, perUserSf->SrcPath)) {
  2286. perUserSf->DestPath = perUserSf->SrcPath;
  2287. }
  2288. } else {
  2289. //
  2290. // Everyone points to the common location; use it
  2291. // by disabling the per-user shell folder entry.
  2292. //
  2293. MYASSERT (StringIMatch (commonSf->SrcPath, perUserSf->SrcPath));
  2294. pIgnoreShellFolder (perUserSf);
  2295. }
  2296. }
  2297. if (duplicates < total) {
  2298. //
  2299. // Discard all migration to the common shell folder
  2300. //
  2301. pIgnoreShellFolder (commonSf);
  2302. }
  2303. }
  2304. }
  2305. //
  2306. // Walk the array. For each pair of shell folders that have the same
  2307. // source location, make the dest location the same if it is not the same
  2308. // as source.
  2309. //
  2310. for (i = 0 ; i < count ; i++) {
  2311. thisSf = listPtr[i];
  2312. //
  2313. // Has this sf been processed already? Or is it just a logical folder?
  2314. //
  2315. if (thisSf->MergedIntoOtherShellFolder) {
  2316. continue;
  2317. }
  2318. if (!thisSf->SourceExists) {
  2319. continue;
  2320. }
  2321. if (!thisSf->DestPath) {
  2322. continue;
  2323. }
  2324. //
  2325. // Look through all other SFs on the list. If a pair of SFs have the
  2326. // same source, then see if one is moved but the other is preserved.
  2327. // In that case, move them both.
  2328. //
  2329. for (lookAhead = i + 1 ; lookAhead <= count ; lookAhead++) {
  2330. compareSf = listPtr[lookAhead];
  2331. if (!compareSf->SourceExists) {
  2332. continue;
  2333. }
  2334. if (!compareSf->DestPath) {
  2335. continue;
  2336. }
  2337. if (StringIMatch (thisSf->SrcPath, compareSf->SrcPath)) {
  2338. DEBUGMSG ((
  2339. DBG_USERLOOP,
  2340. "%s for %s and %s for %s both point to %s",
  2341. thisSf->Name,
  2342. thisSf->UserName,
  2343. compareSf->Name,
  2344. compareSf->UserName,
  2345. thisSf->SrcPath
  2346. ));
  2347. thisMoved = !StringIMatch (thisSf->SrcPath, thisSf->DestPath);
  2348. compareMoved = !StringIMatch (compareSf->SrcPath, compareSf->DestPath);
  2349. if (thisMoved && compareMoved && !StringIMatch (thisSf->DestPath, compareSf->DestPath)) {
  2350. //
  2351. // Need to fix this dest contradiction with a prority table
  2352. //
  2353. p1 = pGetShellFolderPriority (thisSf->Name);
  2354. p2 = pGetShellFolderPriority (compareSf->Name);
  2355. if (p1 > p2) {
  2356. //
  2357. // Use compareSf over thisSf
  2358. //
  2359. DEBUGMSG ((DBG_USERLOOP, "Destination collision: %s overpowers %s", compareSf->Name, thisSf->Name));
  2360. thisSf->DestPath = compareSf->DestPath;
  2361. } else if (p1 < p2) {
  2362. //
  2363. // Use thisSf over compareSf
  2364. //
  2365. DEBUGMSG ((DBG_USERLOOP, "Destination collision: %s overpowers %s", thisSf->Name, compareSf->Name));
  2366. compareSf->DestPath = thisSf->DestPath;
  2367. } else {
  2368. DEBUGMSG ((DBG_WHOOPS, "Missing priority for both %s and %s", thisSf->Name, compareSf->Name));
  2369. }
  2370. } else if (thisMoved != compareMoved) {
  2371. DEBUGMSG ((
  2372. DBG_USERLOOP,
  2373. "%s for %s redirected from %s to %s",
  2374. thisMoved ? compareSf->Name : thisSf->Name,
  2375. thisMoved ? compareSf->UserName : thisSf->UserName,
  2376. thisMoved ? compareSf->DestPath : thisSf->DestPath,
  2377. thisMoved ? thisSf->DestPath : compareSf->DestPath
  2378. ));
  2379. disableSf = NULL;
  2380. if (thisMoved) {
  2381. //
  2382. // thisSf is merged but compareSf is preserved. If compareSf is a per-user
  2383. // shell folder, and thisSf is common, then ignore compareSf, because thisSf
  2384. // will take care of the move. We don't want two shell folders pointing to
  2385. // the same place.
  2386. //
  2387. if (pIsPerUserSf (compareSf->Name) && !pIsPerUserSf (thisSf->Name)) {
  2388. disableSf = compareSf;
  2389. } else {
  2390. compareSf->DestPath = thisSf->DestPath;
  2391. }
  2392. } else {
  2393. //
  2394. // inverse case of above
  2395. //
  2396. if (pIsPerUserSf (thisSf->Name) && !pIsPerUserSf (compareSf->Name)) {
  2397. disableSf = thisSf;
  2398. } else {
  2399. thisSf->DestPath = compareSf->DestPath;
  2400. }
  2401. }
  2402. thisSf->MergedIntoOtherShellFolder = TRUE;
  2403. compareSf->MergedIntoOtherShellFolder = TRUE;
  2404. pIgnoreShellFolder (disableSf);
  2405. }
  2406. }
  2407. }
  2408. }
  2409. //
  2410. // Now fix this problem: does a common shell folder point to a per-user
  2411. // destination, and the per-user destination points to its default
  2412. // location? If so, remove the common shell folder from migration.
  2413. //
  2414. for (i = 0 ; i <= count ; i++) {
  2415. thisSf = listPtr[i];
  2416. if (!thisSf->DestPath) {
  2417. continue;
  2418. }
  2419. //
  2420. // If source does not exist, force dest to NULL
  2421. //
  2422. if (thisSf->SourceExists == FALSE) {
  2423. if (thisSf->DestPath[0] != '\\' || thisSf->DestPath[1] != '\\') {
  2424. thisSf->DestPath = NULL;
  2425. }
  2426. continue;
  2427. }
  2428. if (thisSf->UserName == NULL) {
  2429. continue;
  2430. }
  2431. //
  2432. // We found a per-user shell folder. Is it merged? If so, find its
  2433. // common equivalent, and check if that is merged. If yes, delete the
  2434. // common sf.
  2435. //
  2436. if (StringIMatch (thisSf->SrcPath, thisSf->DestPath)) {
  2437. continue; // per-user folder is preserved; ignore it
  2438. }
  2439. DEBUGMSG ((DBG_USERLOOP, "Processing common/per-user collisions for %s", thisSf->Name));
  2440. pComputeCommonName (commonName, thisSf->Name);
  2441. for (j = 0 ; j <= count ; j++) {
  2442. if (j == i) {
  2443. continue;
  2444. }
  2445. compareSf = listPtr[j];
  2446. if (compareSf->UserName != NULL) {
  2447. continue;
  2448. }
  2449. if (compareSf->DestPath == NULL) {
  2450. continue;
  2451. }
  2452. if (!StringIMatch (compareSf->Name, commonName)) {
  2453. continue;
  2454. }
  2455. if (!StringIMatch (thisSf->SrcPath, compareSf->SrcPath)) {
  2456. //
  2457. // Different source paths -- don't touch
  2458. //
  2459. continue;
  2460. }
  2461. if (!StringIMatch (compareSf->SrcPath, compareSf->DestPath) &&
  2462. StringIMatch (thisSf->DestPath, compareSf->DestPath)
  2463. ) {
  2464. //
  2465. // The dest is the same, and common is not preserved, so
  2466. // remove the common folder
  2467. //
  2468. DEBUGMSG ((
  2469. DBG_USERLOOP,
  2470. "Common dest %s is the same as the per-user dest, deleting common",
  2471. compareSf->DestPath
  2472. ));
  2473. pIgnoreShellFolder (compareSf);
  2474. }
  2475. }
  2476. }
  2477. FreeGrowBuffer (&sfPtrList);
  2478. }
  2479. BOOL
  2480. pRecordUserShellFolder (
  2481. IN PCTSTR ShellFolderName,
  2482. IN PCTSTR FixedUserName,
  2483. IN PCTSTR ShellFolderSrc,
  2484. IN PCTSTR ShellFolderOriginalSrc,
  2485. IN PCTSTR ShellFolderDest
  2486. )
  2487. {
  2488. TCHAR node[MEMDB_MAX];
  2489. UINT sequencer;
  2490. MemDbBuildKey (node, FixedUserName?FixedUserName:S_DOT_ALLUSERS, ShellFolderName, NULL, NULL);
  2491. if (IsFileMarkedForOperation (node, OPERATION_SHELL_FOLDER)) {
  2492. sequencer = GetSequencerFromPath (node);
  2493. }
  2494. else {
  2495. sequencer = AddOperationToPath (node, OPERATION_SHELL_FOLDER);
  2496. AddPropertyToPathEx (sequencer, OPERATION_SHELL_FOLDER, ShellFolderDest, MEMDB_CATEGORY_SHELLFOLDERS_DEST);
  2497. }
  2498. AddPropertyToPathEx (sequencer, OPERATION_SHELL_FOLDER, ShellFolderOriginalSrc, MEMDB_CATEGORY_SHELLFOLDERS_ORIGINAL_SRC);
  2499. AddPropertyToPathEx (sequencer, OPERATION_SHELL_FOLDER, ShellFolderSrc, MEMDB_CATEGORY_SHELLFOLDERS_SRC);
  2500. return TRUE;
  2501. }
  2502. //
  2503. // These globals are used to record an empty directory. Every time we come across a directory we store
  2504. // the directory name and we set g_EmptyDir to true. When we come across a file we reset g_EmptyDir.
  2505. // If, when we come across another directory, g_EmptyDir is set it means that the previous directory was
  2506. // empty.
  2507. //
  2508. PTSTR g_EmptyDirName = NULL;
  2509. BOOL g_EmptyDir = FALSE;
  2510. BOOL
  2511. pIsParent (
  2512. IN PCTSTR Parent,
  2513. IN PCTSTR Son
  2514. )
  2515. {
  2516. UINT parentLen;
  2517. parentLen = ByteCount (Parent);
  2518. if (StringIMatchByteCount (Parent, Son, parentLen)) {
  2519. if (Son [parentLen] == '\\') {
  2520. return TRUE;
  2521. }
  2522. }
  2523. return FALSE;
  2524. }
  2525. BOOL
  2526. pCheckTemporaryInternetFiles (
  2527. IN PSHELLFOLDER ShellFolder
  2528. )
  2529. {
  2530. /*
  2531. TREE_ENUM e;
  2532. DWORD fileCount = 0;
  2533. DWORD startCount;
  2534. BOOL expired = FALSE;
  2535. */
  2536. //
  2537. // Don't migrate temporary internet files under any circumstances
  2538. //
  2539. return FALSE;
  2540. /*
  2541. MYASSERT (ShellFolder);
  2542. startCount = GetTickCount ();
  2543. if (EnumFirstFileInTreeEx (&e, ShellFolder->SrcPath, NULL, FALSE, FALSE, FILE_ENUM_ALL_LEVELS)) {
  2544. do {
  2545. if (GetTickCount () - startCount > 1000) {
  2546. AbortEnumFileInTree (&e);
  2547. expired = TRUE;
  2548. break;
  2549. }
  2550. fileCount++;
  2551. } while (EnumNextFileInTree (&e));
  2552. }
  2553. return !expired;
  2554. */
  2555. }
  2556. BOOL
  2557. pMoveShellFolder (
  2558. IN PSHELLFOLDER ShellFolder
  2559. )
  2560. {
  2561. TREE_ENUM e;
  2562. TCHAR Node[MEMDB_MAX];
  2563. UINT SrcBytes;
  2564. PCTSTR NewDest;
  2565. PCTSTR p;
  2566. PROFILE_MERGE_DATA Data;
  2567. PSHELL_FOLDER_FILTER Filter;
  2568. DWORD d;
  2569. //
  2570. // Don't scan shell folders on inaccessible drives
  2571. //
  2572. if (!IsDriveAccessible (ShellFolder->SrcPath)) {
  2573. return TRUE;
  2574. }
  2575. //
  2576. // Don't scan shell folders pointing to massive dirs
  2577. //
  2578. if (pIsMassiveDir (ShellFolder->SrcPath)) {
  2579. return TRUE;
  2580. }
  2581. //
  2582. // Test to ensure that Temporary Internet Files isn't too huge.
  2583. //
  2584. if (StringIMatch (ShellFolder->Name, TEXT("CACHE")) && !pCheckTemporaryInternetFiles (ShellFolder)) {
  2585. DEBUGMSG ((DBG_WARNING, "Temporary Internet Files will be removed during textmode."));
  2586. ExcludePath (g_ExclusionValue, ShellFolder->SrcPath);
  2587. MemDbSetValueEx (MEMDB_CATEGORY_FULL_DIR_DELETES, ShellFolder->SrcPath, NULL, NULL, 0, NULL);
  2588. return TRUE;
  2589. }
  2590. g_EmptyDir = FALSE;
  2591. g_EmptyDirName = AllocPathString (MEMDB_MAX);
  2592. if (!StringIMatch (ShellFolder->SrcPath, ShellFolder->DestPath)) {
  2593. MarkFileForShellFolderMove (ShellFolder->SrcPath, ShellFolder->DestPath);
  2594. }
  2595. //
  2596. // Init the filter data struct
  2597. //
  2598. ZeroMemory (&Data, sizeof (Data));
  2599. Data.FixedUserName = ShellFolder->FixedUserName;
  2600. Data.ShellFolderIdentifier = ShellFolder->Name;
  2601. Data.Context = PER_FOLDER_INITIALIZE;
  2602. _tcssafecpy (Data.TempSourcePath, ShellFolder->SrcPath, MEMDB_MAX);
  2603. _tcssafecpy (Data.DestinationPath, ShellFolder->DestPath, MEMDB_MAX);
  2604. Data.SrcRootPath = ShellFolder->SrcPath;
  2605. Data.DestRootPath = ShellFolder->DestPath;
  2606. //
  2607. // Call filters for init
  2608. //
  2609. for (Filter = g_Filters ; Filter->Fn ; Filter++) {
  2610. Data.State = 0;
  2611. Filter->Fn (&Data);
  2612. Filter->State = Data.State;
  2613. }
  2614. if (EnumFirstFileInTreeEx (&e, ShellFolder->SrcPath, NULL, FALSE, FALSE, FILE_ENUM_ALL_LEVELS)) {
  2615. do {
  2616. if (CANCELLED()) {
  2617. AbortEnumFileInTree (&e);
  2618. return FALSE;
  2619. }
  2620. if (e.Directory) {
  2621. MemDbBuildKey (Node, MEMDB_CATEGORY_SHELL_FOLDERS_MOVED, e.FullPath, NULL, NULL);
  2622. if (MemDbGetValue (Node, NULL)) {
  2623. DEBUGMSG ((DBG_USERLOOP, "%s already moved", e.FullPath));
  2624. AbortEnumCurrentDir (&e);
  2625. continue;
  2626. }
  2627. }
  2628. if (IsFileMarkedForOperation (e.FullPath, ALL_DELETE_OPERATIONS|ALL_MOVE_OPERATIONS)) {
  2629. //
  2630. // File is already going to be deleted or moved; ignore shell folder migration
  2631. //
  2632. continue;
  2633. }
  2634. //
  2635. // Generate the symbolic destination, and add an external file move
  2636. // operation. It also records the source from destination linkage.
  2637. //
  2638. SrcBytes = ByteCount (ShellFolder->SrcPath);
  2639. p = (PCTSTR) ((PBYTE) e.FullPath + SrcBytes);
  2640. if (*p == TEXT('\\')) {
  2641. p++;
  2642. }
  2643. NewDest = JoinPaths (ShellFolder->DestPath, p);
  2644. Data.Attributes = e.FindData->dwFileAttributes;
  2645. _tcssafecpy (Data.TempSourcePath, e.FullPath, MEMDB_MAX);
  2646. _tcssafecpy (Data.DestinationPath, NewDest, MEMDB_MAX);
  2647. Data.Context = PROCESS_PATH;
  2648. FreePathString (NewDest);
  2649. NewDest = NULL;
  2650. //
  2651. // Allow filters to change source or dest, or to skip copy
  2652. //
  2653. for (Filter = g_Filters ; Filter->Fn ; Filter++) {
  2654. Data.State = Filter->State;
  2655. d = Filter->Fn (&Data);
  2656. Filter->State = Data.State;
  2657. if (d == SHELLFILTER_SKIP_FILE) {
  2658. break;
  2659. }
  2660. if (d == SHELLFILTER_SKIP_DIRECTORY) {
  2661. AbortEnumCurrentDir (&e);
  2662. break;
  2663. }
  2664. }
  2665. if (!Filter->Fn) {
  2666. if (!StringIMatch (e.FullPath, Data.DestinationPath)) {
  2667. if (!IsFileMarkedForDelete (e.FullPath)) {
  2668. MarkFileForShellFolderMove (e.FullPath, Data.DestinationPath);
  2669. }
  2670. }
  2671. if (e.Directory) {
  2672. if (g_EmptyDir && (!pIsParent (g_EmptyDirName, Data.DestinationPath))) {
  2673. MarkDirectoryAsPreserved (g_EmptyDirName);
  2674. }
  2675. g_EmptyDir = TRUE;
  2676. _tcssafecpy (g_EmptyDirName, Data.DestinationPath, MEMDB_MAX);
  2677. }
  2678. } else {
  2679. //
  2680. // we don't need this file on NT side
  2681. // let's delete it in text mode setup.
  2682. //
  2683. MarkFileForDelete (e.FullPath);
  2684. }
  2685. if (!e.Directory) {
  2686. g_EmptyDir = FALSE;
  2687. }
  2688. } while (EnumNextFileInTree (&e));
  2689. }
  2690. //
  2691. // Call filters one last time
  2692. //
  2693. Data.Attributes = 0;
  2694. Data.TempSourcePath[0] = 0;
  2695. Data.DestinationPath[0] = 0;
  2696. Data.Context = PER_FOLDER_TERMINATE;
  2697. _tcssafecpy (Data.TempSourcePath, ShellFolder->SrcPath, MAX_TCHAR_PATH);
  2698. _tcssafecpy (Data.DestinationPath, ShellFolder->DestPath, MAX_TCHAR_PATH);
  2699. for (Filter = g_Filters ; Filter->Fn ; Filter++) {
  2700. Data.State = Filter->State;
  2701. Filter->Fn (&Data);
  2702. Filter->State = Data.State;
  2703. }
  2704. if (g_EmptyDir) {
  2705. MarkDirectoryAsPreserved (g_EmptyDirName);
  2706. }
  2707. FreePathString (g_EmptyDirName);
  2708. g_EmptyDirName = NULL;
  2709. return TRUE;
  2710. }
  2711. VOID
  2712. pLoadSFMigDirs (
  2713. VOID
  2714. )
  2715. {
  2716. INFCONTEXT ctx;
  2717. TCHAR SFName[MAX_SHELLFOLDER_NAME];
  2718. TCHAR SubDirBuffer[MAX_PATH];
  2719. PCTSTR SubDir;
  2720. INT Levels;
  2721. if (SetupFindFirstLine (g_Win95UpgInf, S_SHELLFOLDERSMIGRATIONDIRS, NULL, &ctx)) {
  2722. do {
  2723. if (SetupGetStringField (&ctx, 1, SFName, MAX_PATH, NULL) && SFName[0]) {
  2724. if (SetupGetStringField (&ctx, 2, SubDirBuffer, MAX_PATH, NULL) && SubDirBuffer[0]) {
  2725. SubDir = SubDirBuffer;
  2726. } else {
  2727. SubDir = NULL;
  2728. }
  2729. if (SetupGetIntField (&ctx, 3, &Levels) && Levels == 1) {
  2730. //
  2731. // the whole subtree
  2732. //
  2733. Levels = MAX_DEEP_LEVELS;
  2734. } else {
  2735. Levels = 0;
  2736. }
  2737. //
  2738. // add this info to memdb
  2739. //
  2740. MemDbSetValueEx (MEMDB_CATEGORY_SFMIGDIRS, SFName, SubDir, NULL, Levels, NULL);
  2741. }
  2742. } while (SetupFindNextLine (&ctx, &ctx));
  2743. }
  2744. }
  2745. VOID
  2746. pExecuteShellFoldersMove (
  2747. VOID
  2748. )
  2749. {
  2750. GROWBUFFER Pointers = GROWBUF_INIT;
  2751. INT Pos;
  2752. PSHELLFOLDER psf, oldPsf;
  2753. TCHAR Node[MEMDB_MAX];
  2754. UINT Sequencer = 0;
  2755. TCHAR TempPath[MEMDB_MAX];
  2756. MEMDB_ENUM enumSF;
  2757. PROFILE_MERGE_DATA Data;
  2758. PSHELL_FOLDER_FILTER Filter;
  2759. PCTSTR MigPath;
  2760. DWORD Levels;
  2761. HASHTABLE tagNames;
  2762. TCHAR uniqueTagName[MAX_SHELLFOLDER_NAME + MAX_USER_NAME + 5];
  2763. PTSTR numPtr;
  2764. UINT sequencer;
  2765. if (CANCELLED()) {
  2766. return;
  2767. }
  2768. //
  2769. // Prepare a list of pointers, so we can process them in
  2770. // reverse order
  2771. //
  2772. MemDbBuildKey (Node, MEMDB_CATEGORY_SF_ORDER_SRC, TEXT("*"), NULL, NULL);
  2773. if (MemDbEnumFirstValue (&enumSF, Node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  2774. do {
  2775. GrowBufAppendDword (&Pointers, enumSF.dwValue);
  2776. } while (MemDbEnumNextValue (&enumSF));
  2777. }
  2778. tagNames = HtAlloc();
  2779. //
  2780. // Call filters for global init
  2781. //
  2782. ZeroMemory (&Data, sizeof (Data));
  2783. Data.Context = GLOBAL_INITIALIZE;
  2784. for (Filter = g_Filters ; Filter->Fn ; Filter++) {
  2785. Data.State = 0;
  2786. Filter->Fn (&Data);
  2787. Filter->State = Data.State;
  2788. }
  2789. //
  2790. // Now loop through all the pointers starting at the end
  2791. //
  2792. for (Pos = (INT) Pointers.End - sizeof (DWORD) ; Pos >= 0 ; Pos -= sizeof (DWORD)) {
  2793. if (CANCELLED()) {
  2794. break;
  2795. }
  2796. psf = *((PSHELLFOLDER *) (Pointers.Buf + Pos));
  2797. //
  2798. // Now process the current shell folder
  2799. //
  2800. if ((psf->Name == NULL) || (psf->DestPath == NULL)) {
  2801. //
  2802. // this is an obsolete shell folder structure, leftover from
  2803. // collapsing or collision cleanup
  2804. //
  2805. continue;
  2806. }
  2807. //
  2808. // Was this already processed? If so, skip it.
  2809. //
  2810. MemDbBuildKey (
  2811. Node,
  2812. MEMDB_CATEGORY_SHELL_FOLDERS_MOVED,
  2813. psf->SrcPath,
  2814. NULL,
  2815. NULL
  2816. );
  2817. if (MemDbGetValue (Node, (PDWORD)(&oldPsf))) {
  2818. DEBUGMSG ((DBG_USERLOOP, "%s already moved", psf->SrcPath));
  2819. psf->TempPath = oldPsf->TempPath;
  2820. } else {
  2821. MemDbSetValueEx (
  2822. MEMDB_CATEGORY_SHELL_FOLDERS_MOVED,
  2823. psf->SrcPath,
  2824. NULL,
  2825. NULL,
  2826. (DWORD) psf,
  2827. NULL
  2828. );
  2829. //
  2830. // Now let's enumerate the shell folder content and see if we need
  2831. // to mark some file as moved. We do that if the shell folder
  2832. // is not preserved or if some of the filters modify the name of
  2833. // some files.
  2834. //
  2835. pMoveShellFolder (psf);
  2836. //
  2837. // Now see if this one is in WinNt way. If so, move it to a temporary location.
  2838. //
  2839. if ((!pIsMassiveDir (psf->SrcPath)) &&
  2840. (_totupper (psf->SrcPath[0]) == _totupper (g_WinDir[0]))
  2841. ) {
  2842. StringCopy (uniqueTagName, psf->FixedUserName ? psf->FixedUserName : TEXT(".system"));
  2843. StringCopy (AppendWack (uniqueTagName), psf->Name);
  2844. sequencer = 1;
  2845. numPtr = GetEndOfString (uniqueTagName);
  2846. while (HtFindString (tagNames, uniqueTagName)) {
  2847. sequencer++;
  2848. wsprintf (numPtr, TEXT(" %u"), sequencer);
  2849. }
  2850. HtAddString (tagNames, uniqueTagName);
  2851. ComputeTemporaryPathA (
  2852. psf->SrcPath,
  2853. psf->SrcPath,
  2854. uniqueTagName,
  2855. g_TempDir,
  2856. TempPath
  2857. );
  2858. DEBUGMSG ((DBG_USERLOOP, "Moving shell folder %s from %s to %s", psf->Name, psf->SrcPath, TempPath));
  2859. MarkShellFolderForMove (psf->SrcPath, TempPath);
  2860. psf->TempPath = PoolMemDuplicateString (g_SFPool, TempPath);
  2861. }
  2862. }
  2863. //
  2864. // Record the shell folder in all cases, so it can be filtered in GUI mode.
  2865. //
  2866. pRecordUserShellFolder (
  2867. psf->Name,
  2868. psf->FixedUserName,
  2869. psf->TempPath?psf->TempPath:psf->SrcPath,
  2870. psf->SrcPath,
  2871. psf->DestPath
  2872. );
  2873. //
  2874. // check if this SF (or a subdir) is a migration path
  2875. //
  2876. MemDbBuildKey (Node, MEMDB_CATEGORY_SFMIGDIRS, psf->Name, NULL, NULL);
  2877. if (MemDbGetValue (Node, &Levels)) {
  2878. AddMigrationPath (psf->SrcPath, Levels);
  2879. }
  2880. if (MemDbGetValueEx (&enumSF, MEMDB_CATEGORY_SFMIGDIRS, psf->Name, NULL)) {
  2881. do {
  2882. if (enumSF.szName[0]) {
  2883. MigPath = JoinPaths (psf->SrcPath, enumSF.szName);
  2884. } else {
  2885. MigPath = psf->SrcPath;
  2886. }
  2887. AddMigrationPath (MigPath, enumSF.dwValue);
  2888. if (enumSF.szName[0]) {
  2889. FreePathString (MigPath);
  2890. }
  2891. } while (MemDbEnumNextValue (&enumSF));
  2892. }
  2893. }
  2894. //
  2895. // Call filters for global terminate
  2896. //
  2897. Data.Context = GLOBAL_TERMINATE;
  2898. for (Filter = g_Filters ; Filter->Fn ; Filter++) {
  2899. Data.State = Filter->State;
  2900. Filter->Fn (&Data);
  2901. }
  2902. //
  2903. // Clean up
  2904. //
  2905. FreeGrowBuffer (&Pointers);
  2906. HtFree (tagNames);
  2907. }
  2908. VOID
  2909. pMoveUserHive (
  2910. IN PUSERENUM EnumPtr
  2911. )
  2912. {
  2913. //
  2914. // Save the user profile directory name
  2915. //
  2916. MemDbSetValueEx (
  2917. MEMDB_CATEGORY_USER_PROFILE_EXT,
  2918. EnumPtr->FixedUserName,
  2919. NULL,
  2920. EnumPtr->ProfileDirName,
  2921. 0,
  2922. NULL
  2923. );
  2924. //
  2925. // Tell winnt.sif to relocate this Win9x user's user.dat. And, if the directory
  2926. // containing user.dat is a per-user profile directory (i.e., if it is a subdir of
  2927. // %WinDir%\profiles, and specifically NOT %WinDir%), then also relocate any files
  2928. // at the same level as user.dat.
  2929. //
  2930. MarkHiveForTemporaryMove (
  2931. EnumPtr->UserDatPath,
  2932. g_TempDir,
  2933. EnumPtr->FixedUserName,
  2934. EnumPtr->DefaultUserHive,
  2935. EnumPtr->CreateAccountOnly
  2936. );
  2937. }
  2938. DWORD
  2939. MigrateShellFolders (
  2940. IN DWORD Request
  2941. )
  2942. {
  2943. USERENUM e;
  2944. DWORD Ticks;
  2945. TCHAR key [MEMDB_MAX];
  2946. PSHELLFOLDER psf;
  2947. MEMDB_ENUM enumSF;
  2948. if (!g_SFPool) {
  2949. g_SFPool = PoolMemInitNamedPool ("Shell Folders Pool");
  2950. PoolMemDisableTracking (g_SFPool);
  2951. }
  2952. if (Request == REQUEST_QUERYTICKS) {
  2953. Ticks = 0;
  2954. if (EnumFirstUser (&e, ENUMUSER_ENABLE_NAME_FIX)) {
  2955. do {
  2956. Ticks += TICKS_USERPROFILE_MIGRATION;
  2957. } while (EnumNextUser (&e));
  2958. }
  2959. return Ticks ? Ticks : 200;
  2960. } else if (Request != REQUEST_RUN) {
  2961. return ERROR_SUCCESS;
  2962. }
  2963. if (!pCreateSfTables ()) {
  2964. LOG ((LOG_ERROR, "Can't initialize shell folder table"));
  2965. return ERROR_OPEN_FAILED;
  2966. }
  2967. if (!pCreateDirRenameTable ()) {
  2968. LOG ((LOG_ERROR, "Can't create shell folder rename table"));
  2969. return ERROR_OPEN_FAILED;
  2970. }
  2971. pGatherCommonShellFoldersData ();
  2972. TickProgressBar ();
  2973. if (EnumFirstUser (&e, ENUMUSER_ENABLE_NAME_FIX)) {
  2974. do {
  2975. if (!(e.AccountType & INVALID_ACCOUNT)) {
  2976. InitNtUserEnvironment (&e);
  2977. if (e.AccountType & NAMED_USER) {
  2978. //
  2979. // Process the shell folders for this migrated user
  2980. //
  2981. pGatherUserShellFoldersData (&e);
  2982. } else if ((e.AccountType & DEFAULT_USER) &&
  2983. (e.AccountType & CURRENT_USER) &&
  2984. (e.AccountType & ADMINISTRATOR)
  2985. ) {
  2986. if (!e.RealAdminAccountExists) {
  2987. //
  2988. // Process the shell folders for the default user
  2989. // (there are no named users)
  2990. //
  2991. pGatherUserShellFoldersData (&e);
  2992. }
  2993. }
  2994. //
  2995. // Move the hive for all valid users
  2996. //
  2997. pMoveUserHive (&e);
  2998. TerminateNtUserEnvironment();
  2999. TickProgressBar ();
  3000. }
  3001. } while (!CANCELLED() && EnumNextUser (&e));
  3002. }
  3003. pProcessShellFoldersInfo();
  3004. pResolveSourceCollisions();
  3005. TickProgressBar ();
  3006. MemDbBuildKey (key, MEMDB_CATEGORY_SF_ORDER_NAME_SRC, TEXT("*"), NULL, NULL);
  3007. if (MemDbEnumFirstValue (&enumSF, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  3008. do {
  3009. psf = (PSHELLFOLDER) enumSF.dwValue;
  3010. if (psf->Name) {
  3011. LOG ((
  3012. LOG_INFORMATION,
  3013. "Shell folder: %s\n"
  3014. " Status: %s\n"
  3015. " User: %s\n"
  3016. " Source: %s %s\n"
  3017. " Destination: %s\n"
  3018. " Combined: %s",
  3019. psf->Name,
  3020. psf->DestPath ? (StringICompare (psf->SrcPath, psf->DestPath) == 0?TEXT("Preserved"):TEXT("Merged")) : TEXT("Ignored"),
  3021. psf->FixedUserName ? psf->FixedUserName : TEXT("(all)"),
  3022. psf->SrcPath,
  3023. psf->SourceExists ? TEXT("") : TEXT("[does not exist]"),
  3024. psf->DestPath,
  3025. psf->MergedIntoOtherShellFolder ? TEXT("YES") : TEXT("NO")
  3026. ));
  3027. }
  3028. } while (MemDbEnumNextValue (&enumSF));
  3029. }
  3030. //
  3031. // load SF Migration Dirs into memdb, in the temporary hive
  3032. //
  3033. pLoadSFMigDirs ();
  3034. pExecuteShellFoldersMove();
  3035. pDestroySfTables();
  3036. HtFree (g_DirRenameTable);
  3037. PoolMemDestroyPool (g_SFPool);
  3038. g_SFPool = NULL;
  3039. return CANCELLED() ? ERROR_CANCELLED : ERROR_SUCCESS;
  3040. }
  3041. DWORD
  3042. pSendToFilter (
  3043. IN OUT PPROFILE_MERGE_DATA Data
  3044. )
  3045. {
  3046. PCTSTR filePtr;
  3047. static shouldProcess = FALSE;
  3048. INFCONTEXT context;
  3049. switch (Data->Context) {
  3050. case PER_FOLDER_INITIALIZE:
  3051. if (StringIMatch (Data->ShellFolderIdentifier, S_SENDTO)) {
  3052. shouldProcess = TRUE;
  3053. } else {
  3054. shouldProcess = FALSE;
  3055. }
  3056. break;
  3057. case PROCESS_PATH:
  3058. if (shouldProcess) {
  3059. filePtr = GetFileNameFromPath (Data->TempSourcePath);
  3060. if (SetupFindFirstLine (g_Win95UpgInf, S_SENDTO_SUPPRESS, filePtr, &context)) {
  3061. return SHELLFILTER_SKIP_FILE;
  3062. }
  3063. }
  3064. break;
  3065. case PER_FOLDER_TERMINATE:
  3066. shouldProcess = FALSE;
  3067. break;
  3068. }
  3069. return SHELLFILTER_OK;
  3070. }
  3071. PSTR
  3072. pSkipPath (
  3073. IN PSTR SrcPath,
  3074. IN PCSTR RootPath
  3075. )
  3076. {
  3077. PSTR p;
  3078. PCSTR q;
  3079. p = SrcPath;
  3080. q = RootPath;
  3081. while (_mbsnextc (p) == _mbsnextc (q)) {
  3082. p = _mbsinc (p);
  3083. q = _mbsinc (q);
  3084. }
  3085. if (_mbsnextc (p) == '\\') {
  3086. p = _mbsinc (p);
  3087. }
  3088. return p;
  3089. }
  3090. DWORD
  3091. pDirRenameFilter (
  3092. IN OUT PPROFILE_MERGE_DATA Data
  3093. )
  3094. {
  3095. PSTR wackPtr, dirPtr, pathPtr, NtDir;
  3096. PCSTR searchStr;
  3097. CHAR NewDestPath [MEMDB_MAX] = "";
  3098. switch (Data->Context) {
  3099. case PER_FOLDER_INITIALIZE:
  3100. break;
  3101. case PROCESS_PATH:
  3102. pathPtr = pSkipPath (Data->DestinationPath, Data->DestRootPath);
  3103. StringCopy (NewDestPath, Data->DestRootPath);
  3104. dirPtr = pathPtr;
  3105. wackPtr = _mbschr (pathPtr, '\\');
  3106. if (wackPtr) {
  3107. *wackPtr = 0;
  3108. }
  3109. while (dirPtr) {
  3110. StringCat (NewDestPath, "\\");
  3111. searchStr = JoinPaths (Data->ShellFolderIdentifier, pathPtr);
  3112. if (HtFindStringAndData (g_DirRenameTable, searchStr, &NtDir)) {
  3113. StringCat (NewDestPath, NtDir);
  3114. } else {
  3115. StringCat (NewDestPath, dirPtr);
  3116. }
  3117. FreePathString (searchStr);
  3118. if (wackPtr) {
  3119. *wackPtr = '\\';
  3120. dirPtr = _mbsinc (wackPtr);
  3121. wackPtr = _mbschr (dirPtr, '\\');
  3122. if (wackPtr) {
  3123. *wackPtr = 0;
  3124. }
  3125. } else {
  3126. dirPtr = NULL;
  3127. }
  3128. }
  3129. _mbssafecpy (Data->DestinationPath, NewDestPath, MEMDB_MAX);
  3130. break;
  3131. case PER_FOLDER_TERMINATE:
  3132. break;
  3133. }
  3134. return SHELLFILTER_OK;
  3135. }
  3136. DWORD
  3137. pKatakanaFilter (
  3138. IN OUT PPROFILE_MERGE_DATA Data
  3139. )
  3140. {
  3141. PCTSTR newName;
  3142. PCTSTR filePtr;
  3143. switch (Data->Context) {
  3144. case PER_FOLDER_INITIALIZE:
  3145. break;
  3146. case PROCESS_PATH:
  3147. //
  3148. // On JPN systems we are going to convert paths from SB Katakana to DB Katakana,
  3149. // starting after the root path of the shell folder.
  3150. //
  3151. if (GetACP() == 932) { // this is only for JPN builds
  3152. //
  3153. // We only do the conversion for directories and for files that are not hidden
  3154. //
  3155. if ((Data->Attributes & FILE_ATTRIBUTE_DIRECTORY) ||
  3156. ((Data->Attributes & FILE_ATTRIBUTE_HIDDEN) == 0)
  3157. ) {
  3158. filePtr = NULL;
  3159. } else {
  3160. filePtr = GetFileNameFromPath (Data->DestinationPath);
  3161. }
  3162. newName = ConvertSBtoDB (Data->DestRootPath, Data->DestinationPath, filePtr);
  3163. _tcssafecpy (Data->DestinationPath, newName, MEMDB_MAX);
  3164. FreePathString (newName);
  3165. }
  3166. break;
  3167. case PER_FOLDER_TERMINATE:
  3168. break;
  3169. }
  3170. return SHELLFILTER_OK;
  3171. }
  3172. PCTSTR
  3173. GenerateNewFileName (
  3174. IN PCTSTR OldName,
  3175. IN OUT PWORD Sequencer,
  3176. IN BOOL CheckExistence
  3177. )
  3178. {
  3179. PCTSTR extPtr;
  3180. PTSTR newName;
  3181. PTSTR result;
  3182. extPtr = GetFileExtensionFromPath (OldName);
  3183. if (!extPtr) {
  3184. extPtr = GetEndOfString (OldName);
  3185. }
  3186. else {
  3187. extPtr = _tcsdec (OldName, extPtr);
  3188. }
  3189. newName = DuplicatePathString (OldName, 0);
  3190. result = DuplicatePathString (OldName, 10);
  3191. StringCopyAB (newName, OldName, extPtr);
  3192. do {
  3193. (*Sequencer) ++;
  3194. wsprintf (result, TEXT("%s (%u)%s"), newName, *Sequencer, extPtr);
  3195. } while ((CheckExistence) && (DoesFileExist (result)));
  3196. FreePathString (newName);
  3197. return result;
  3198. }
  3199. DWORD
  3200. pCollisionDetection (
  3201. IN OUT PPROFILE_MERGE_DATA Data
  3202. )
  3203. {
  3204. WORD Sequencer;
  3205. PCTSTR NewName;
  3206. PCTSTR OldName;
  3207. TCHAR key[MEMDB_MAX];
  3208. switch (Data->Context) {
  3209. case PER_FOLDER_INITIALIZE:
  3210. break;
  3211. case PROCESS_PATH:
  3212. Sequencer = 0;
  3213. NewName = DuplicatePathString (Data->DestinationPath, 0);
  3214. for (;;) {
  3215. MemDbBuildKey (key, MEMDB_CATEGORY_SF_FILES_DEST, NewName, NULL, NULL);
  3216. if (MemDbGetValue (key, NULL)) {
  3217. OldName = NewName;
  3218. NewName = GenerateNewFileName (OldName, &Sequencer, FALSE);
  3219. FreePathString (OldName);
  3220. }
  3221. else {
  3222. MemDbSetValue (key, 0);
  3223. break;
  3224. }
  3225. }
  3226. _tcssafecpy (Data->DestinationPath, NewName, MEMDB_MAX);
  3227. FreePathString (NewName);
  3228. break;
  3229. case PER_FOLDER_TERMINATE:
  3230. break;
  3231. }
  3232. return SHELLFILTER_OK;
  3233. }
  3234. DWORD
  3235. pRecordCacheFolders (
  3236. IN OUT PPROFILE_MERGE_DATA Data
  3237. )
  3238. {
  3239. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  3240. PTSTR path;
  3241. switch (Data->Context) {
  3242. case GLOBAL_INITIALIZE:
  3243. //
  3244. // Cleanup is done with a special function below
  3245. //
  3246. g_CacheShellFolders = CreateStringMapping();
  3247. break;
  3248. case PER_FOLDER_INITIALIZE:
  3249. //
  3250. // If this shell folder is in the HtmlCaches list, then add it to the
  3251. // string mapping (mapping it to a static string). The string mapping
  3252. // provides a fast and easy way to test a full file path against a
  3253. // list of directories.
  3254. //
  3255. if (InfFindFirstLine (
  3256. g_Win95UpgInf,
  3257. TEXT("ShellFolders.HtmlCaches"),
  3258. Data->ShellFolderIdentifier,
  3259. &is
  3260. )) {
  3261. path = DuplicatePathString (Data->SrcRootPath, 1);
  3262. AppendWack (path);
  3263. AddStringMappingPair (g_CacheShellFolders, path, TEXT(""));
  3264. DEBUGMSG ((DBG_NAUSEA, "%s is an HTML cache", path));
  3265. FreePathString (path);
  3266. }
  3267. break;
  3268. }
  3269. return SHELLFILTER_OK;
  3270. }
  3271. VOID
  3272. TerminateCacheFolderTracking (
  3273. VOID
  3274. )
  3275. {
  3276. if (g_CacheShellFolders) {
  3277. DestroyStringMapping (g_CacheShellFolders);
  3278. g_CacheShellFolders = NULL;
  3279. }
  3280. }
  3281. PCTSTR
  3282. ShellFolderGetPath (
  3283. IN PUSERENUM EnumPtr,
  3284. IN PCTSTR ShellFolderId
  3285. )
  3286. {
  3287. HKEY sfKey, sfUserKey;
  3288. SF_ENUM e;
  3289. PCTSTR path = NULL;
  3290. //
  3291. // first attempt to get the path from HKR\...\User Shell Folders
  3292. //
  3293. sfUserKey = OpenRegKey (EnumPtr->UserRegKey, S_USHELL_FOLDERS_KEY_USER);
  3294. if (sfUserKey) {
  3295. path = pGetRegValuePath (sfUserKey, ShellFolderId);
  3296. CloseRegKey (sfUserKey);
  3297. }
  3298. //
  3299. // if that fails, try to get it from HKR\...\Shell Folders
  3300. //
  3301. if (!path) {
  3302. sfKey = OpenRegKey (EnumPtr->UserRegKey, S_SHELL_FOLDERS_KEY_USER);
  3303. if (sfKey) {
  3304. path = pGetRegValuePath (sfKey, ShellFolderId);
  3305. CloseRegKey (sfKey);
  3306. }
  3307. }
  3308. //
  3309. // if that fails too, maybe it's a virtual SF
  3310. //
  3311. if (!path) {
  3312. if (!g_SFPool) {
  3313. g_SFPool = PoolMemInitNamedPool ("Shell Folders Pool");
  3314. PoolMemDisableTracking (g_SFPool);
  3315. }
  3316. ZeroMemory (&e, sizeof (e));
  3317. e.EnumPtr = EnumPtr;
  3318. e.enumeratedSf = HtAllocWithData (sizeof (PCTSTR));
  3319. if (pEnumFirstVirtualShellFolder (&e)) {
  3320. do {
  3321. if (StringIMatch (e.sfName, ShellFolderId)) {
  3322. //
  3323. // found it
  3324. //
  3325. path = DuplicatePathString (e.sfPath, 0);
  3326. pAbortEnumVirtualShellFolder (&e);
  3327. break;
  3328. }
  3329. } while (pEnumNextVirtualShellFolder (&e));
  3330. }
  3331. }
  3332. return path;
  3333. }