Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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