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

3256 lines
95 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. shell.c
  5. Abstract:
  6. Contains code that implements shell folder migration. Shell folders
  7. are moved into new NT locations whenever possible. Also, a set of
  8. filters alter the content of the shell folders.
  9. Author:
  10. Jim Schmidt (jimschm) 24-Aug-1998
  11. Revision History:
  12. Calin Negreanu (calinn) 09-Sep-1998 Obsolete links, fixes and other changes
  13. --*/
  14. #include "pch.h"
  15. #include <linkpif.h>
  16. #include "migmainp.h"
  17. #define DBG_SHELL "Shell"
  18. #define SHELL_FOLDER_FILTERS_9X_NT \
  19. DEFMAC(pObsoleteLinksFilter) \
  20. DEFMAC(pStartupDisableFilter) \
  21. DEFMAC(pFontNameFilter) \
  22. DEFMAC(pCollisionDetection9xNt) \
  23. #define SHELL_FOLDER_FILTERS_NT_9X \
  24. DEFMAC(pDetectOtherShellFolder) \
  25. DEFMAC(pCollisionDetectionNt9x) \
  26. typedef enum {
  27. INITIALIZE,
  28. PROCESS_PATH,
  29. TERMINATE
  30. } CALL_CONTEXT;
  31. #define SHELLFILTER_OK 0
  32. #define SHELLFILTER_SKIP_FILE 1
  33. #define SHELLFILTER_SKIP_DIRECTORY 2
  34. #define SHELLFILTER_ERROR 3
  35. #define SHELLFILTER_FORCE_CHANGE 4
  36. typedef struct {
  37. IN PCWSTR Win9xUser; OPTIONAL
  38. IN PCWSTR FixedUserName; OPTIONAL
  39. IN HKEY UserHiveRoot; // HKLM or the Default User hive
  40. IN PCWSTR ShellFolderIdentifier; // i.e., Fonts, Programs, etc...
  41. IN OUT WCHAR TempSourcePath[MEMDB_MAX]; // full path, a child of SrcRootPath
  42. IN OUT WCHAR DestinationPath[MEMDB_MAX];
  43. IN PCWSTR DefaultShellFolder; OPTIONAL
  44. IN PCWSTR UserDefaultLocation;
  45. IN PCWSTR SrcRootPath; // the temp root dir
  46. IN PCWSTR DestRootPath; // the dest root dir
  47. IN PCWSTR OrigRootPath; // the Win9x root dir
  48. IN OUT DWORD Attributes;
  49. IN DWORD UserFlags;
  50. IN OUT DWORD State;
  51. IN PMIGRATE_USER_ENUM EnumPtr;
  52. IN CALL_CONTEXT Context;
  53. } PROFILE_MERGE_DATA, *PPROFILE_MERGE_DATA;
  54. typedef DWORD(PROFILEMERGEFILTER_PROTOTYPE)(IN OUT PPROFILE_MERGE_DATA Data);
  55. typedef PROFILEMERGEFILTER_PROTOTYPE * PROFILEMERGEFILTER;
  56. typedef struct {
  57. PROFILEMERGEFILTER Fn;
  58. PCSTR Name;
  59. DWORD State;
  60. } SHELL_FOLDER_FILTER, *PSHELL_FOLDER_FILTER;
  61. #define DEFMAC(fn) PROFILEMERGEFILTER_PROTOTYPE fn;
  62. SHELL_FOLDER_FILTERS_9X_NT
  63. SHELL_FOLDER_FILTERS_NT_9X
  64. #undef DEFMAC
  65. #define DEFMAC(fn) {fn, #fn},
  66. static SHELL_FOLDER_FILTER g_Filters_9xNt[] = {
  67. SHELL_FOLDER_FILTERS_9X_NT /* , */
  68. {NULL}
  69. };
  70. static SHELL_FOLDER_FILTER g_Filters_Nt9x[] = {
  71. SHELL_FOLDER_FILTERS_NT_9X /* , */
  72. {NULL}
  73. };
  74. #undef DEFMAC
  75. GROWLIST g_SfQueueSrc;
  76. GROWLIST g_SfQueueDest;
  77. PVOID g_SystemSfList;
  78. PVOID g_UserSfList;
  79. typedef struct {
  80. PCTSTR sfName;
  81. PCTSTR sfPath;
  82. HKEY SfKey;
  83. REGVALUE_ENUM SfKeyEnum;
  84. BOOL UserSf;
  85. } SF_ENUM, *PSF_ENUM;
  86. #define MAX_SHELL_TAG 64
  87. typedef struct {
  88. INT CsidlValue;
  89. PCTSTR Tag;
  90. } CSIDLMAP, *PCSIDLMAP;
  91. CSIDLMAP g_CsidlMap[] = {
  92. CSIDL_ADMINTOOLS, TEXT("Administrative Tools"),
  93. CSIDL_ALTSTARTUP, TEXT("AltStartup"),
  94. CSIDL_APPDATA, TEXT("AppData"),
  95. CSIDL_BITBUCKET, TEXT("RecycleBinFolder"),
  96. CSIDL_CONNECTIONS, TEXT("ConnectionsFolder"),
  97. CSIDL_CONTROLS, TEXT("ControlPanelFolder"),
  98. CSIDL_COOKIES, TEXT("Cookies"),
  99. CSIDL_DESKTOP, TEXT("Desktop"),
  100. CSIDL_DRIVES, TEXT("DriveFolder"),
  101. CSIDL_FAVORITES, TEXT("Favorites"),
  102. CSIDL_FONTS, TEXT("Fonts"),
  103. CSIDL_HISTORY, TEXT("History"),
  104. CSIDL_INTERNET, TEXT("InternetFolder"),
  105. CSIDL_INTERNET_CACHE, TEXT("Cache"),
  106. CSIDL_LOCAL_APPDATA, TEXT("Local AppData"),
  107. CSIDL_MYDOCUMENTS, TEXT("My Documents"),
  108. CSIDL_MYMUSIC, TEXT("My Music"),
  109. CSIDL_MYPICTURES, TEXT("My Pictures"),
  110. CSIDL_MYVIDEO, TEXT("My Video"),
  111. CSIDL_NETHOOD, TEXT("NetHood"),
  112. CSIDL_NETWORK, TEXT("NetworkFolder"),
  113. CSIDL_PERSONAL, TEXT("Personal"),
  114. CSIDL_PROGRAMS, TEXT("Programs"),
  115. CSIDL_RECENT, TEXT("Recent"),
  116. CSIDL_SENDTO, TEXT("SendTo"),
  117. CSIDL_STARTMENU, TEXT("Start Menu"),
  118. CSIDL_STARTUP, TEXT("Startup"),
  119. CSIDL_TEMPLATES, TEXT("Templates"),
  120. CSIDL_COMMON_ADMINTOOLS, TEXT("Common Administrative Tools"),
  121. CSIDL_COMMON_ALTSTARTUP, TEXT("Common AltStartup"),
  122. CSIDL_COMMON_APPDATA, TEXT("Common AppData"),
  123. CSIDL_COMMON_DESKTOPDIRECTORY, TEXT("Common Desktop"),
  124. CSIDL_COMMON_DOCUMENTS, TEXT("Common Documents"),
  125. CSIDL_COMMON_FAVORITES, TEXT("Common Favorites"),
  126. CSIDL_COMMON_PROGRAMS, TEXT("Common Programs"),
  127. CSIDL_COMMON_STARTMENU, TEXT("Common Start Menu"),
  128. CSIDL_COMMON_STARTUP, TEXT("Common Startup"),
  129. CSIDL_COMMON_TEMPLATES, TEXT("Common Templates"),
  130. CSIDL_COMMON_DOCUMENTS, TEXT("Common Personal"),
  131. CSIDL_COMMON_MUSIC, TEXT("CommonMusic"),
  132. CSIDL_COMMON_PICTURES, TEXT("CommonPictures"),
  133. CSIDL_COMMON_VIDEO, TEXT("CommonVideo"),
  134. 0, NULL
  135. };
  136. VOID
  137. pConvertCommonSfToPerUser (
  138. IN PCTSTR CommonSf,
  139. OUT PTSTR PerUserSf // must hold MAX_SHELL_TAG chars
  140. );
  141. BOOL
  142. pIsCommonSf (
  143. IN PCTSTR ShellFolderTag
  144. );
  145. VOID
  146. pConvertPerUserSfToCommon (
  147. IN PCTSTR PerUserSf,
  148. OUT PTSTR CommonSf // must hold MAX_SHELL_TAG chars
  149. );
  150. /*++
  151. Routine Description:
  152. EnumFirstRegShellFolder and EnumNextRegShellFolder are enumeration routines that
  153. enumerate all shell folders per system or for a particular user.
  154. Arguments:
  155. e - enumeration structure
  156. EnumPtr - user enumeration structure
  157. Return Value:
  158. Both routines return TRUE if a new shell folder could be found, FALSE otherwise
  159. --*/
  160. BOOL
  161. EnumFirstRegShellFolder (
  162. IN OUT PSF_ENUM e,
  163. IN BOOL UserSf
  164. )
  165. {
  166. HKEY UsfKey;
  167. e->UserSf = UserSf;
  168. e->sfPath = NULL;
  169. if (UserSf) {
  170. e->SfKey = OpenRegKey (HKEY_CURRENT_USER, S_SHELL_FOLDERS_KEY_USER);
  171. } else {
  172. e->SfKey = OpenRegKeyStr (S_SHELL_FOLDERS_KEY_SYSTEM);
  173. }
  174. if (!e->SfKey) {
  175. return FALSE;
  176. }
  177. if (EnumFirstRegValue (&e->SfKeyEnum, e->SfKey)) {
  178. e->sfName = e->SfKeyEnum.ValueName;
  179. e->sfPath = NULL;
  180. if (UserSf) {
  181. UsfKey = OpenRegKey (HKEY_CURRENT_USER, S_USHELL_FOLDERS_KEY_USER);
  182. } else {
  183. UsfKey = OpenRegKeyStr (S_USHELL_FOLDERS_KEY_SYSTEM);
  184. }
  185. if (UsfKey) {
  186. e->sfPath = GetRegValueString (UsfKey, e->SfKeyEnum.ValueName);
  187. CloseRegKey (UsfKey);
  188. }
  189. if (e->sfPath == NULL) {
  190. e->sfPath = GetRegValueString (e->SfKey, e->SfKeyEnum.ValueName);
  191. }
  192. return TRUE;
  193. }
  194. CloseRegKey (e->SfKey);
  195. return FALSE;
  196. }
  197. BOOL
  198. EnumNextRegShellFolder (
  199. IN OUT PSF_ENUM e
  200. )
  201. {
  202. HKEY UsfKey;
  203. if (e->sfPath) {
  204. MemFree (g_hHeap, 0, e->sfPath);
  205. e->sfPath = NULL;
  206. }
  207. if (EnumNextRegValue (&e->SfKeyEnum)) {
  208. e->sfName = e->SfKeyEnum.ValueName;
  209. e->sfPath = NULL;
  210. if (e->UserSf) {
  211. UsfKey = OpenRegKey (HKEY_CURRENT_USER, S_USHELL_FOLDERS_KEY_USER);
  212. } else {
  213. UsfKey = OpenRegKeyStr (S_USHELL_FOLDERS_KEY_SYSTEM);
  214. }
  215. if (UsfKey) {
  216. e->sfPath = GetRegValueString (UsfKey, e->SfKeyEnum.ValueName);
  217. CloseRegKey (UsfKey);
  218. }
  219. if (e->sfPath == NULL) {
  220. e->sfPath = GetRegValueString (e->SfKey, e->SfKeyEnum.ValueName);
  221. }
  222. return TRUE;
  223. }
  224. CloseRegKey (e->SfKey);
  225. return FALSE;
  226. }
  227. VOID
  228. AbortEnumRegShellFolder (
  229. IN OUT PSF_ENUM e
  230. )
  231. {
  232. if (e->sfPath) {
  233. MemFree (g_hHeap, 0, e->sfPath);
  234. e->sfPath = NULL;
  235. }
  236. }
  237. VOID
  238. pPrepareSfRestartability(
  239. VOID
  240. )
  241. {
  242. PTSTR userProfilePath = NULL;
  243. DWORD Size;
  244. MIGRATE_USER_ENUM e;
  245. if (EnumFirstUserToMigrate (&e, ENUM_NO_FLAGS)) {
  246. do {
  247. if (!e.CreateOnly &&
  248. (e.AccountType != DEFAULT_USER_ACCOUNT) &&
  249. (e.AccountType != LOGON_USER_SETTINGS)
  250. ) {
  251. if (GetUserProfilePath (e.FixedUserName, &userProfilePath)) {
  252. RenameOnRestartOfGuiMode (userProfilePath, NULL);
  253. FreePathString (userProfilePath);
  254. }
  255. }
  256. } while (EnumNextUserToMigrate (&e));
  257. }
  258. }
  259. VOID
  260. pFlushSfQueue (
  261. VOID
  262. )
  263. {
  264. UINT u;
  265. UINT count;
  266. PCTSTR source;
  267. PCTSTR dest;
  268. //
  269. // For files that need to be copied, do that now before writing to the journal
  270. //
  271. count = GrowListGetSize (&g_SfQueueSrc);
  272. if (!count) {
  273. return;
  274. }
  275. for (u = 0 ; u < count ; u++) {
  276. dest = GrowListGetString (&g_SfQueueDest, u);
  277. if (!dest) {
  278. continue;
  279. }
  280. if (DoesFileExist (dest)) {
  281. source = GrowListGetString (&g_SfQueueSrc, u);
  282. MYASSERT (source);
  283. if (!OurCopyFileW (source, dest)) {
  284. LOG ((LOG_WARNING, (PCSTR)MSG_COULD_NOT_MOVE_FILE_LOG, dest, GetLastError ()));
  285. g_BlowAwayTempShellFolders = FALSE;
  286. }
  287. //
  288. // Make the string pointers NULL for this item
  289. //
  290. GrowListResetItem (&g_SfQueueSrc, u);
  291. GrowListResetItem (&g_SfQueueDest, u);
  292. }
  293. }
  294. //
  295. // Now record the remaining items in the journal (before the move
  296. // happens). Ignore journal failures. Since we are undoing the move,
  297. // source and dest must be flipped.
  298. //
  299. RenameListOnRestartOfGuiMode (&g_SfQueueDest, &g_SfQueueSrc);
  300. //
  301. // Do the move
  302. //
  303. for (u = 0 ; u < count ; u++) {
  304. source = GrowListGetString (&g_SfQueueSrc, u);
  305. dest = GrowListGetString (&g_SfQueueDest, u);
  306. if (!source || !dest) {
  307. continue;
  308. }
  309. if (!OurMoveFileEx (source, dest, MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
  310. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  311. DEBUGMSG ((DBG_WARNING, "%s already exists", dest));
  312. } else {
  313. LOG ((LOG_WARNING, (PCSTR)MSG_COULD_NOT_MOVE_FILE_LOG, dest, GetLastError ()));
  314. g_BlowAwayTempShellFolders = FALSE;
  315. }
  316. }
  317. }
  318. //
  319. // Clean up -- grow lists are ready for reuse after FreeGrowList
  320. //
  321. FreeGrowList (&g_SfQueueSrc);
  322. FreeGrowList (&g_SfQueueDest);
  323. }
  324. VOID
  325. pQueueSfMove (
  326. IN PCTSTR Source,
  327. IN PCTSTR Destination
  328. )
  329. {
  330. UINT count;
  331. MYASSERT (Source && Destination);
  332. count = GrowListGetSize (&g_SfQueueSrc);
  333. if (count == 1000) {
  334. //
  335. // Do 1,000 moves at once
  336. //
  337. pFlushSfQueue();
  338. }
  339. GrowListAppendString (&g_SfQueueSrc, Source);
  340. GrowListAppendString (&g_SfQueueDest, Destination);
  341. }
  342. PVOID
  343. pCreateSystemSfList (
  344. )
  345. {
  346. PCTSTR expandedPath;
  347. PVOID Table;
  348. SF_ENUM e;
  349. Table = pSetupStringTableInitialize();
  350. if (!Table) {
  351. return NULL;
  352. }
  353. //
  354. // Load all the System shell folders into this table
  355. //
  356. if (EnumFirstRegShellFolder (&e, FALSE)) {
  357. do {
  358. expandedPath = ExpandEnvironmentText (e.sfPath);
  359. pSetupStringTableAddString (Table, (PVOID) expandedPath, STRTAB_CASE_INSENSITIVE);
  360. FreeText (expandedPath);
  361. } while (EnumNextRegShellFolder (&e));
  362. }
  363. return Table;
  364. }
  365. PVOID
  366. pCreateUserSfList (
  367. IN PPROFILE_MERGE_DATA Data
  368. )
  369. {
  370. PTSTR CurrentUserProfilePath = NULL;
  371. TCHAR DefaultUserProfilePath[MAX_TCHAR_PATH];
  372. DWORD Size;
  373. PCTSTR expandedPath;
  374. PCTSTR tempExpand;
  375. PVOID Table;
  376. SF_ENUM e;
  377. if (Data && Data->FixedUserName) {
  378. if (!GetUserProfilePath (Data->FixedUserName, &CurrentUserProfilePath)) {
  379. return NULL;
  380. }
  381. }
  382. else {
  383. Size = sizeof (DefaultUserProfilePath);
  384. if (!GetDefaultUserProfileDirectory (DefaultUserProfilePath, &Size)) {
  385. return NULL;
  386. }
  387. }
  388. Table = pSetupStringTableInitialize();
  389. if (!Table) {
  390. return NULL;
  391. }
  392. //
  393. // Load all the System shell folders into this table
  394. //
  395. if (EnumFirstRegShellFolder (&e, TRUE)) {
  396. do {
  397. tempExpand = StringSearchAndReplace (
  398. e.sfPath,
  399. S_USERPROFILE_ENV,
  400. CurrentUserProfilePath?CurrentUserProfilePath:DefaultUserProfilePath
  401. );
  402. if (!tempExpand) {
  403. tempExpand = DuplicatePathString (e.sfPath, 0);
  404. }
  405. expandedPath = ExpandEnvironmentText (tempExpand);
  406. FreePathString (tempExpand);
  407. pSetupStringTableAddString (Table, (PVOID) expandedPath, STRTAB_CASE_INSENSITIVE);
  408. FreeText (expandedPath);
  409. } while (EnumNextRegShellFolder (&e));
  410. }
  411. if (CurrentUserProfilePath) {
  412. FreePathString (CurrentUserProfilePath);
  413. CurrentUserProfilePath = NULL;
  414. }
  415. return Table;
  416. }
  417. VOID
  418. pDestroySfList (
  419. IN PVOID Table
  420. )
  421. {
  422. if (Table) {
  423. pSetupStringTableDestroy (Table);
  424. }
  425. }
  426. PVOID g_LinkDataPool = NULL;
  427. typedef struct _LINK_DATA {
  428. PCTSTR Target;
  429. PCTSTR Arguments;
  430. PCTSTR ShellFolderName;
  431. struct _LINK_DATA *Next;
  432. } LINK_DATA, *PLINK_DATA;
  433. PVOID g_FoldersTable;
  434. PVOID g_Merged9xFolders;
  435. typedef struct _LINK_RENAME_DATA {
  436. PCTSTR OldTarget;
  437. PCTSTR NewTarget;
  438. PCTSTR OldArguments;
  439. PCTSTR NewArguments;
  440. PCTSTR ShellFolderName;
  441. struct _LINK_RENAME_DATA *Next;
  442. } LINK_RENAME_DATA, *PLINK_RENAME_DATA;
  443. PLINK_RENAME_DATA g_LinkRenameData;
  444. VOID
  445. pAddAllLinksToList (
  446. PTSTR AllocBuffer, // MEMDB_MAX * 4, caller-owned for less allocs
  447. PCTSTR ShellFolderName,
  448. PCTSTR RootPath,
  449. IShellLink *ShellLink,
  450. IPersistFile *PersistFile
  451. )
  452. {
  453. TREE_ENUM e;
  454. PTSTR ShortcutTarget;
  455. PTSTR ShortcutArgs;
  456. PTSTR ShortcutWorkDir;
  457. PTSTR ShortcutIconPath;
  458. INT ShortcutIcon;
  459. WORD ShortcutHotKey;
  460. BOOL dosApp;
  461. BOOL msDosMode;
  462. PLINK_DATA linkData;
  463. LONG stringId;
  464. ShortcutTarget = AllocBuffer + MEMDB_MAX;
  465. ShortcutArgs = ShortcutTarget + MEMDB_MAX;
  466. ShortcutWorkDir = ShortcutArgs + MEMDB_MAX;
  467. ShortcutIconPath = ShortcutWorkDir + MEMDB_MAX;
  468. if (EnumFirstFileInTree (&e, RootPath, NULL, FALSE)) {
  469. do {
  470. if (e.Directory) {
  471. if (((g_SystemSfList) && (pSetupStringTableLookUpString (g_SystemSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1)) ||
  472. ((g_UserSfList) && (pSetupStringTableLookUpString (g_UserSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1))
  473. ) {
  474. AbortEnumCurrentDir (&e);
  475. }
  476. continue;
  477. }
  478. DEBUGMSG ((DBG_SHELL, "Extracting shortcut info for enumerated file %s", e.FullPath));
  479. if (ExtractShortcutInfo (
  480. ShortcutTarget,
  481. ShortcutArgs,
  482. ShortcutWorkDir,
  483. ShortcutIconPath,
  484. &ShortcutIcon,
  485. &ShortcutHotKey,
  486. &dosApp,
  487. &msDosMode,
  488. NULL,
  489. NULL,
  490. e.FullPath,
  491. ShellLink,
  492. PersistFile
  493. )) {
  494. linkData = (PLINK_DATA) (PoolMemGetMemory (g_LinkDataPool, sizeof (LINK_DATA)));
  495. ZeroMemory (linkData, sizeof (LINK_DATA));
  496. linkData->Target = PoolMemDuplicateString (g_LinkDataPool, ShortcutTarget);
  497. linkData->Arguments = PoolMemDuplicateString (g_LinkDataPool, ShortcutArgs);
  498. linkData->ShellFolderName = PoolMemDuplicateString (g_LinkDataPool, ShellFolderName);
  499. linkData->Next = NULL;
  500. DEBUGMSG ((DBG_SHELL, "Recording NT default shortcut: %s in %s", e.FullPath, ShellFolderName));
  501. stringId = pSetupStringTableLookUpString (g_FoldersTable, (PTSTR)ShellFolderName, 0);
  502. if (stringId != -1) {
  503. pSetupStringTableGetExtraData (g_FoldersTable, stringId, &linkData->Next, sizeof (PLINK_DATA));
  504. pSetupStringTableSetExtraData (g_FoldersTable, stringId, &linkData, sizeof (PLINK_DATA));
  505. }
  506. else {
  507. pSetupStringTableAddStringEx (
  508. g_FoldersTable,
  509. (PTSTR)ShellFolderName,
  510. STRTAB_CASE_INSENSITIVE,
  511. &linkData,
  512. sizeof (PLINK_DATA)
  513. );
  514. }
  515. }
  516. } while (EnumNextFileInTree (&e));
  517. }
  518. }
  519. VOID
  520. pAddKnownLinks (
  521. VOID
  522. )
  523. {
  524. INFCONTEXT context;
  525. TCHAR field[MEMDB_MAX];
  526. BOOL result = FALSE;
  527. PLINK_DATA linkData;
  528. PCTSTR pathExp;
  529. LONG stringId;
  530. PCTSTR ArgList [4] = {TEXT("ProgramFiles"), g_ProgramFiles, NULL, NULL};
  531. MYASSERT (g_WkstaMigInf);
  532. if (SetupFindFirstLine (g_WkstaMigInf, S_KNOWN_NT_LINKS, NULL, &context)) {
  533. do {
  534. linkData = (PLINK_DATA) (PoolMemGetMemory (g_LinkDataPool, sizeof (LINK_DATA)));
  535. ZeroMemory (linkData, sizeof (LINK_DATA));
  536. result = FALSE;
  537. __try {
  538. if (!SetupGetStringField (&context, 1, field, MEMDB_MAX, NULL)) {
  539. __leave;
  540. }
  541. pathExp = ExpandEnvironmentTextEx (field, ArgList);
  542. linkData->Target = PoolMemDuplicateString (g_LinkDataPool, pathExp);
  543. FreeText (pathExp);
  544. if (!SetupGetStringField (&context, 2, field, MEMDB_MAX, NULL)) {
  545. __leave;
  546. }
  547. pathExp = ExpandEnvironmentTextEx (field, ArgList);
  548. linkData->Arguments = PoolMemDuplicateString (g_LinkDataPool, pathExp);
  549. FreeText (pathExp);
  550. if (!SetupGetStringField (&context, 3, field, MEMDB_MAX, NULL)) {
  551. __leave;
  552. }
  553. linkData->ShellFolderName = PoolMemDuplicateString (g_LinkDataPool, field);
  554. linkData->Next = NULL;
  555. result = TRUE;
  556. }
  557. __finally {
  558. if (result) {
  559. DEBUGMSG ((DBG_SHELL, "Recording known link: %s in %s", linkData->Target, linkData->ShellFolderName));
  560. stringId = pSetupStringTableLookUpString (g_FoldersTable, (PTSTR)linkData->ShellFolderName, 0);
  561. if (stringId != -1) {
  562. pSetupStringTableGetExtraData (g_FoldersTable, stringId, &linkData->Next, sizeof (PLINK_DATA));
  563. pSetupStringTableSetExtraData (g_FoldersTable, stringId, &linkData, sizeof (PLINK_DATA));
  564. }
  565. else {
  566. pSetupStringTableAddStringEx (
  567. g_FoldersTable,
  568. (PTSTR)linkData->ShellFolderName,
  569. STRTAB_CASE_INSENSITIVE,
  570. &linkData,
  571. sizeof (PLINK_DATA)
  572. );
  573. }
  574. }
  575. else {
  576. if (linkData->Target) {
  577. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->Target);
  578. }
  579. if (linkData->Arguments) {
  580. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->Arguments);
  581. }
  582. if (linkData->ShellFolderName) {
  583. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->ShellFolderName);
  584. }
  585. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData);
  586. linkData = NULL;
  587. }
  588. }
  589. } while (SetupFindNextLine (&context, &context));
  590. }
  591. }
  592. VOID
  593. pCreateLinksList (
  594. VOID
  595. )
  596. {
  597. SF_ENUM e;
  598. PCTSTR expandedPath;
  599. UINT commonLen;
  600. DWORD Size;
  601. PCTSTR tempExpand;
  602. PTSTR DefaultUserProfilePath;
  603. IShellLink *shellLink;
  604. IPersistFile *persistFile;
  605. PTSTR perUserName;
  606. PTSTR bigBuf = NULL;
  607. __try {
  608. bigBuf = (PTSTR) MemAllocUninit ((MEMDB_MAX * 4 + MAX_TCHAR_PATH + MAX_SHELL_TAG) * sizeof (TCHAR));
  609. if (!bigBuf) {
  610. __leave;
  611. }
  612. DefaultUserProfilePath = bigBuf + MEMDB_MAX * 4;
  613. perUserName = DefaultUserProfilePath + MAX_TCHAR_PATH;
  614. g_LinkDataPool = PoolMemInitNamedPool ("LinkData Pool");
  615. g_FoldersTable = pSetupStringTableInitializeEx (sizeof (PLINK_DATA), 0);
  616. if (!g_FoldersTable) {
  617. DEBUGMSG((DBG_ERROR, "Cannot initialize Shell Folders table."));
  618. __leave;
  619. }
  620. //
  621. // First thing: Load links from the INF files. These are links that we know NT is going to install
  622. //
  623. pAddKnownLinks ();
  624. if (InitCOMLink (&shellLink, &persistFile)) {
  625. //
  626. // Go through all system shell folders and list the links
  627. //
  628. if (EnumFirstRegShellFolder (&e, FALSE)) {
  629. do {
  630. if (*e.sfPath) {
  631. expandedPath = ExpandEnvironmentText (e.sfPath);
  632. pConvertCommonSfToPerUser (e.sfName, perUserName);
  633. pAddAllLinksToList (bigBuf, perUserName, expandedPath, shellLink, persistFile);
  634. FreeText (expandedPath);
  635. }
  636. ELSE_DEBUGMSG ((DBG_WARNING, "Shell Folder <%s> data is empty!", e.sfName));
  637. } while (EnumNextRegShellFolder (&e));
  638. }
  639. Size = MAX_TCHAR_PATH;
  640. if (!GetDefaultUserProfileDirectory (DefaultUserProfilePath, &Size)) {
  641. __leave;
  642. }
  643. //
  644. // Go through all user shell folders and list the links from the default user dirs
  645. //
  646. if (EnumFirstRegShellFolder (&e, TRUE)) {
  647. do {
  648. if (*e.sfPath) {
  649. tempExpand = StringSearchAndReplace (
  650. e.sfPath,
  651. S_USERPROFILE_ENV,
  652. DefaultUserProfilePath
  653. );
  654. if (!tempExpand) {
  655. tempExpand = DuplicatePathString (e.sfPath, 0);
  656. }
  657. expandedPath = ExpandEnvironmentText (tempExpand);
  658. FreePathString (tempExpand);
  659. pAddAllLinksToList (bigBuf, e.sfName, expandedPath, shellLink, persistFile);
  660. FreeText (expandedPath);
  661. }
  662. ELSE_DEBUGMSG ((DBG_WARNING, "Shell Folder <%s> data is empty!", e.sfName));
  663. } while (EnumNextRegShellFolder (&e));
  664. }
  665. FreeCOMLink (&shellLink, &persistFile);
  666. }
  667. else {
  668. DEBUGMSG((DBG_ERROR, "Cannot initialize COM. Obsolete links filter will not work."));
  669. }
  670. }
  671. __finally {
  672. if (bigBuf) {
  673. FreeMem (bigBuf);
  674. }
  675. }
  676. }
  677. VOID
  678. pCreateLinksRenameList (
  679. VOID
  680. )
  681. {
  682. INFCONTEXT context;
  683. TCHAR field[MEMDB_MAX];
  684. BOOL result = FALSE;
  685. PLINK_RENAME_DATA linkData;
  686. PCTSTR pathExp;
  687. PCTSTR ArgList [4] = {TEXT("ProgramFiles"), g_ProgramFiles, NULL, NULL};
  688. MYASSERT (g_WkstaMigInf);
  689. if (SetupFindFirstLine (g_WkstaMigInf, S_OBSOLETE_LINKS, NULL, &context)) {
  690. do {
  691. linkData = (PLINK_RENAME_DATA) (PoolMemGetMemory (g_LinkDataPool, sizeof (LINK_RENAME_DATA)));
  692. ZeroMemory (linkData, sizeof (LINK_RENAME_DATA));
  693. result = FALSE;
  694. __try {
  695. if (!SetupGetStringField (&context, 1, field, MEMDB_MAX, NULL)) {
  696. __leave;
  697. }
  698. pathExp = ExpandEnvironmentTextEx (field, ArgList);
  699. linkData->OldTarget = PoolMemDuplicateString (g_LinkDataPool, pathExp);
  700. FreeText (pathExp);
  701. if (!SetupGetStringField (&context, 2, field, MEMDB_MAX, NULL)) {
  702. __leave;
  703. }
  704. pathExp = ExpandEnvironmentTextEx (field, ArgList);
  705. linkData->OldArguments = PoolMemDuplicateString (g_LinkDataPool, pathExp);
  706. FreeText (pathExp);
  707. if (!SetupGetStringField (&context, 3, field, MEMDB_MAX, NULL)) {
  708. __leave;
  709. }
  710. pathExp = ExpandEnvironmentTextEx (field, ArgList);
  711. linkData->NewTarget = PoolMemDuplicateString (g_LinkDataPool, pathExp);
  712. FreeText (pathExp);
  713. if (!SetupGetStringField (&context, 4, field, MEMDB_MAX, NULL)) {
  714. __leave;
  715. }
  716. pathExp = ExpandEnvironmentTextEx (field, ArgList);
  717. linkData->NewArguments = PoolMemDuplicateString (g_LinkDataPool, pathExp);
  718. FreeText (pathExp);
  719. if (!SetupGetStringField (&context, 5, field, MEMDB_MAX, NULL)) {
  720. __leave;
  721. }
  722. linkData->ShellFolderName = PoolMemDuplicateString (g_LinkDataPool, field);
  723. result = TRUE;
  724. }
  725. __finally {
  726. if (result) {
  727. linkData->Next = g_LinkRenameData;
  728. g_LinkRenameData = linkData;
  729. }
  730. else {
  731. if (linkData->OldTarget) {
  732. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->OldTarget);
  733. }
  734. if (linkData->NewTarget) {
  735. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->NewTarget);
  736. }
  737. if (linkData->OldArguments) {
  738. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->OldArguments);
  739. }
  740. if (linkData->NewArguments) {
  741. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->NewArguments);
  742. }
  743. if (linkData->ShellFolderName) {
  744. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->ShellFolderName);
  745. }
  746. PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData);
  747. linkData = NULL;
  748. }
  749. }
  750. } while (SetupFindNextLine (&context, &context));
  751. }
  752. }
  753. VOID
  754. pDestroyLinksData (
  755. VOID
  756. )
  757. {
  758. if (g_LinkDataPool != NULL) {
  759. PoolMemDestroyPool (g_LinkDataPool);
  760. g_LinkDataPool = NULL;
  761. }
  762. if (g_FoldersTable != NULL) {
  763. pSetupStringTableDestroy (g_FoldersTable);
  764. }
  765. g_LinkRenameData = NULL;
  766. }
  767. BOOL
  768. pMigrateShellFolder (
  769. IN PCTSTR Win9xUser, OPTIONAL
  770. IN PCTSTR FixedUserName, OPTIONAL
  771. IN BOOL SystemShellFolder,
  772. IN PCTSTR ShellFolderIdentifier,
  773. IN PCTSTR SourcePath,
  774. IN PCTSTR DestinationPath,
  775. IN PCTSTR OrigSourcePath,
  776. IN DWORD UserFlags,
  777. IN PMIGRATE_USER_ENUM EnumPtr
  778. );
  779. TCHAR g_DefaultHivePath[MAX_TCHAR_PATH];
  780. HKEY g_DefaultHiveRoot;
  781. INT g_DefaultHiveMapped;
  782. VOID
  783. pMigrateSystemShellFolders (
  784. VOID
  785. )
  786. {
  787. FILEOP_ENUM eOp;
  788. FILEOP_PROP_ENUM eOpProp;
  789. PTSTR NewDest;
  790. PTSTR OrigSrc;
  791. if (EnumFirstPathInOperation (&eOp, OPERATION_SHELL_FOLDER)) {
  792. do {
  793. if (IsPatternMatch (S_DOT_ALLUSERS TEXT("\\*"), eOp.Path)) {
  794. NewDest = NULL;
  795. OrigSrc = NULL;
  796. if (EnumFirstFileOpProperty (&eOpProp, eOp.Sequencer, OPERATION_SHELL_FOLDER)) {
  797. do {
  798. if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_DEST)) {
  799. NewDest = DuplicatePathString (eOpProp.Property, 0);
  800. }
  801. if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_ORIGINAL_SRC)) {
  802. OrigSrc = DuplicatePathString (eOpProp.Property, 0);
  803. }
  804. if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_SRC)) {
  805. MYASSERT (NewDest);
  806. MYASSERT (OrigSrc);
  807. DEBUGMSG ((DBG_NAUSEA, "System SourcePath: %s", eOpProp.Property));
  808. pMigrateShellFolder (
  809. NULL,
  810. NULL,
  811. TRUE,
  812. _tcsinc(_tcschr (eOp.Path, '\\')),
  813. eOpProp.Property,
  814. NewDest,
  815. OrigSrc,
  816. 0,
  817. NULL
  818. );
  819. }
  820. } while (EnumNextFileOpProperty (&eOpProp));
  821. }
  822. if (NewDest) {
  823. FreePathString (NewDest);
  824. NewDest = NULL;
  825. }
  826. if (OrigSrc) {
  827. FreePathString (OrigSrc);
  828. OrigSrc = NULL;
  829. }
  830. }
  831. } while (EnumNextPathInOperation (&eOp));
  832. }
  833. }
  834. VOID
  835. pWriteMyDocsHelpFile (
  836. IN PCTSTR SubDir
  837. )
  838. /*++
  839. Routine Description:
  840. pWriteMyDocsHelpFile outputs a text file to the given path. This assists
  841. the user in locating their documents, when the My Documents shell folder
  842. goes to Shared Documents.
  843. Arguments:
  844. SubDir - Specifies the path to the subdir where the file should be written
  845. Return Value:
  846. None.
  847. --*/
  848. {
  849. HANDLE file;
  850. PCTSTR fileName;
  851. PCTSTR msg;
  852. DWORD bytesWritten;
  853. PCTSTR path;
  854. fileName = GetStringResource (MSG_EMPTY_MYDOCS_TITLE);
  855. msg = GetStringResource (MSG_EMPTY_MYDOCS_TEXT);
  856. path = JoinPaths (SubDir, fileName);
  857. if (fileName && msg && path) {
  858. //
  859. // For uninstall, mark the file as create. Because of a bug, we have
  860. // to treat this file as an OS file. What we really want to do is
  861. // call:
  862. //
  863. // MarkFileForCreation (path);
  864. //
  865. // but this does not work. So we call MarkFileAsOsFile.
  866. //
  867. MarkFileAsOsFile (path); // allows uninstall to work properly
  868. file = CreateFile (
  869. path,
  870. GENERIC_WRITE,
  871. 0,
  872. NULL,
  873. CREATE_ALWAYS,
  874. FILE_ATTRIBUTE_NORMAL,
  875. NULL
  876. );
  877. if (file != INVALID_HANDLE_VALUE) {
  878. #ifdef UNICODE
  879. WriteFile (file, "\xff\xfe", 2, &bytesWritten, NULL);
  880. #endif
  881. WriteFile (file, msg, SizeOfString (msg), &bytesWritten, NULL);
  882. CloseHandle (file);
  883. }
  884. }
  885. FreeStringResource (msg);
  886. FreeStringResource (fileName);
  887. FreePathString (path);
  888. }
  889. VOID
  890. pMigrateUserShellFolders (
  891. IN PMIGRATE_USER_ENUM EnumPtr
  892. )
  893. {
  894. FILEOP_ENUM eOp;
  895. FILEOP_PROP_ENUM eOpProp;
  896. PTSTR NewDest;
  897. PTSTR OrigSrc;
  898. TCHAR node[MEMDB_MAX];
  899. MEMDB_ENUM e;
  900. if (EnumFirstPathInOperation (&eOp, OPERATION_SHELL_FOLDER)) {
  901. do {
  902. MemDbBuildKey (node, EnumPtr->FixedUserName, TEXT("*"), NULL, NULL);
  903. if (IsPatternMatch (node, eOp.Path)) {
  904. NewDest = NULL;
  905. OrigSrc = NULL;
  906. if (EnumFirstFileOpProperty (&eOpProp, eOp.Sequencer, OPERATION_SHELL_FOLDER)) {
  907. do {
  908. if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_DEST)) {
  909. NewDest = DuplicatePathString (eOpProp.Property, 0);
  910. }
  911. if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_ORIGINAL_SRC)) {
  912. OrigSrc = DuplicatePathString (eOpProp.Property, 0);
  913. }
  914. if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_SRC)) {
  915. MYASSERT (NewDest);
  916. MYASSERT (OrigSrc);
  917. DEBUGMSG ((DBG_NAUSEA, "Per-User SourcePath: %s", eOpProp.Property));
  918. pMigrateShellFolder (
  919. EnumPtr->Win9xUserName,
  920. EnumPtr->FixedUserName,
  921. FALSE,
  922. _tcsinc(_tcschr (eOp.Path, '\\')),
  923. eOpProp.Property,
  924. NewDest,
  925. OrigSrc,
  926. 0,
  927. NULL
  928. );
  929. }
  930. } while (EnumNextFileOpProperty (&eOpProp));
  931. }
  932. if (NewDest) {
  933. FreePathString (NewDest);
  934. NewDest = NULL;
  935. }
  936. if (OrigSrc) {
  937. FreePathString (OrigSrc);
  938. OrigSrc = NULL;
  939. }
  940. }
  941. } while (EnumNextPathInOperation (&eOp));
  942. }
  943. if (EnumPtr->FixedUserName) {
  944. MemDbBuildKey (
  945. node,
  946. MEMDB_CATEGORY_MYDOCS_WARNING,
  947. EnumPtr->FixedUserName,
  948. TEXT("*"),
  949. NULL
  950. );
  951. if (MemDbEnumFirstValue (&e, node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  952. do {
  953. DEBUGMSG ((DBG_SHELL, "Creating mydocs help file %s", e.szName));
  954. pWriteMyDocsHelpFile (e.szName);
  955. } while (MemDbEnumNextValue (&e));
  956. }
  957. }
  958. }
  959. BOOL
  960. pCleanupDir (
  961. IN PCTSTR Path,
  962. IN BOOL CleanUpRoot
  963. )
  964. {
  965. TREE_ENUM e;
  966. DWORD oldAttributes;
  967. if (EnumFirstFileInTreeEx (&e, Path, NULL, TRUE, TRUE, FILE_ENUM_ALL_LEVELS)) {
  968. do {
  969. if (e.Directory) {
  970. //
  971. // This is a dir. Let's see if we enter another shell folder
  972. //
  973. if (((g_SystemSfList) && (pSetupStringTableLookUpString (g_SystemSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1)) ||
  974. ((g_UserSfList) && (pSetupStringTableLookUpString (g_UserSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1)) ||
  975. (IsDirectoryMarkedAsEmpty (e.FullPath))
  976. ) {
  977. //
  978. // we are just getting into another shell folder. Let's skip it
  979. //
  980. AbortEnumCurrentDir (&e);
  981. }
  982. else {
  983. SetLongPathAttributes (e.FullPath, FILE_ATTRIBUTE_NORMAL);
  984. if (!RemoveLongDirectoryPath (e.FullPath)) {
  985. SetLongPathAttributes (e.FullPath, e.FindData->dwFileAttributes);
  986. }
  987. }
  988. }
  989. } while (EnumNextFileInTree (&e));
  990. }
  991. AbortEnumFileInTree (&e);
  992. if (CleanUpRoot) {
  993. oldAttributes = GetLongPathAttributes (Path);
  994. SetLongPathAttributes (Path, FILE_ATTRIBUTE_NORMAL);
  995. if (!RemoveLongDirectoryPath (Path)) {
  996. SetLongPathAttributes (Path, oldAttributes);
  997. }
  998. }
  999. return TRUE;
  1000. }
  1001. INT
  1002. pGetCsidlFromTag (
  1003. IN PCTSTR ShellFolderIdentifier
  1004. )
  1005. {
  1006. PCSIDLMAP map;
  1007. for (map = g_CsidlMap ; map->Tag ; map++) {
  1008. if (StringIMatch (map->Tag, ShellFolderIdentifier)) {
  1009. return map->CsidlValue;
  1010. }
  1011. }
  1012. return -1;
  1013. }
  1014. INT
  1015. CALLBACK
  1016. pSfCopyCallback (
  1017. PCTSTR FullFileSpec,
  1018. PCTSTR DestSpec,
  1019. WIN32_FIND_DATA *FindData,
  1020. DWORD EnumTreeID,
  1021. PVOID Param,
  1022. PDWORD CurrentDirData
  1023. )
  1024. {
  1025. //
  1026. // Put this file in the cleanout category, so that it gets removed unless
  1027. // it has been backed up.
  1028. //
  1029. MemDbSetValueEx (
  1030. MEMDB_CATEGORY_CLEAN_OUT,
  1031. DestSpec,
  1032. NULL,
  1033. NULL,
  1034. BACKUP_FILE,
  1035. NULL
  1036. );
  1037. return CALLBACK_CONTINUE;
  1038. }
  1039. BOOL
  1040. pCreateSfWithApi (
  1041. IN PCTSTR ShellFolderIdentifier,
  1042. IN PCTSTR FolderToCreate
  1043. )
  1044. {
  1045. HRESULT hr;
  1046. INT csidl;
  1047. TCHAR folderPath[MAX_PATH];
  1048. BOOL destroy = FALSE;
  1049. BOOL result = TRUE;
  1050. DWORD attribs;
  1051. //
  1052. // Convert the tag to a CSIDL constant
  1053. //
  1054. csidl = pGetCsidlFromTag (ShellFolderIdentifier);
  1055. if (csidl < 0) {
  1056. DEBUGMSG ((DBG_VERBOSE, "CSIDL ID for %s not known", ShellFolderIdentifier));
  1057. return FALSE;
  1058. }
  1059. //
  1060. // Query the shell for an existing shell folder
  1061. //
  1062. hr = SHGetFolderPath (NULL, csidl, NULL, SHGFP_TYPE_CURRENT, folderPath);
  1063. if (hr != S_OK && hr != S_FALSE) {
  1064. DEBUGMSG ((DBG_WARNING, "Can't get shell folder path for ID %s", ShellFolderIdentifier));
  1065. return FALSE;
  1066. }
  1067. //
  1068. // Get the attributes of the existing shell folder
  1069. //
  1070. if (hr == S_OK) {
  1071. DEBUGMSG ((DBG_VERBOSE, "Shell folder %s already exists at %s", ShellFolderIdentifier, folderPath));
  1072. attribs = GetLongPathAttributes (folderPath);
  1073. } else {
  1074. attribs = INVALID_ATTRIBUTES;
  1075. }
  1076. //
  1077. // If existing shell folder is not present, create it temporarily
  1078. //
  1079. if (attribs == INVALID_ATTRIBUTES) {
  1080. DEBUGMSG ((DBG_VERBOSE, "Shell folder %s needs to be created", ShellFolderIdentifier));
  1081. destroy = TRUE;
  1082. hr = SHGetFolderPath (
  1083. NULL,
  1084. csidl | CSIDL_FLAG_CREATE,
  1085. NULL,
  1086. SHGFP_TYPE_CURRENT,
  1087. folderPath
  1088. );
  1089. if (hr != S_OK) {
  1090. LOG ((LOG_ERROR, "Can't create shell folder path for ID %s", ShellFolderIdentifier));
  1091. return FALSE;
  1092. }
  1093. attribs = GetLongPathAttributes (folderPath);
  1094. if (attribs == INVALID_ATTRIBUTES) {
  1095. LOG ((LOG_ERROR, "Can't get attributes of %s for ID %s", folderPath, ShellFolderIdentifier));
  1096. result = FALSE;
  1097. }
  1098. }
  1099. //
  1100. // On success (either existing sf or we created it), make a copy of the whole folder
  1101. //
  1102. if (result) {
  1103. MakeSurePathExists (FolderToCreate, TRUE);
  1104. attribs = GetLongPathAttributes (folderPath);
  1105. if (attribs != INVALID_ATTRIBUTES) {
  1106. SetLongPathAttributes (FolderToCreate, attribs);
  1107. }
  1108. CopyTree (
  1109. folderPath,
  1110. FolderToCreate,
  1111. 0, // no EnumTree ID
  1112. COPYTREE_DOCOPY | COPYTREE_NOOVERWRITE,
  1113. ENUM_ALL_LEVELS,
  1114. FILTER_ALL,
  1115. NULL, // no exclude.inf struct
  1116. pSfCopyCallback,
  1117. NULL // no error callback
  1118. );
  1119. }
  1120. //
  1121. // If we created the sf, we must destroy it to return the system back
  1122. // to its original state. We punt the case where power goes out and
  1123. // GUI mode restarts.
  1124. //
  1125. if (destroy) {
  1126. RemoveCompleteDirectory (folderPath);
  1127. }
  1128. return result;
  1129. }
  1130. BOOL
  1131. pMigrateShellFolder (
  1132. IN PCTSTR Win9xUser, OPTIONAL
  1133. IN PCTSTR FixedUserName, OPTIONAL
  1134. IN BOOL SystemShellFolder,
  1135. IN PCTSTR ShellFolderIdentifier,
  1136. IN PCTSTR SourcePath,
  1137. IN PCTSTR OrgDestinationPath,
  1138. IN PCTSTR OrigSourcePath,
  1139. IN DWORD UserFlags,
  1140. IN PMIGRATE_USER_ENUM EnumPtr
  1141. )
  1142. {
  1143. TREE_ENUM e;
  1144. PSHELL_FOLDER_FILTER Filter;
  1145. TCHAR DefaultShellFolder[MAX_TCHAR_PATH];
  1146. PCTSTR DestPath = NULL;
  1147. PROFILE_MERGE_DATA Data;
  1148. BOOL Result = FALSE;
  1149. TCHAR UserRoot[MAX_TCHAR_PATH];
  1150. PCTSTR NtDefaultLocation = NULL;
  1151. PCTSTR DefaultUserLocation = NULL;
  1152. PCTSTR tempExpand = NULL;
  1153. PCTSTR nextExpand;
  1154. TCHAR ShellFolderPath[MAX_TCHAR_PATH];
  1155. DWORD Offset;
  1156. DWORD Size;
  1157. HKEY Key;
  1158. DWORD Attributes;
  1159. PCTSTR ValData = NULL;
  1160. PTSTR p;
  1161. DWORD d;
  1162. HKEY UserHiveRoot;
  1163. LONG rc;
  1164. PCTSTR EncodedKey;
  1165. PCTSTR NewDestPath;
  1166. BOOL AlreadyMoved;
  1167. PCTSTR OrigFullPath;
  1168. BOOL regFolder;
  1169. PCTSTR freeMe;
  1170. TCHAR driveLetter[] = TEXT("?:");
  1171. BOOL allUsers;
  1172. BOOL keep;
  1173. PCWSTR OrigRootPath, DestRootPath;
  1174. PBYTE bufferRoot;
  1175. PTSTR destPathBuffer;
  1176. DWORD fileStatus;
  1177. __try {
  1178. bufferRoot = MemAllocUninit (MEMDB_MAX * sizeof (TCHAR));
  1179. if (!bufferRoot) {
  1180. __leave;
  1181. }
  1182. destPathBuffer = (PTSTR) bufferRoot;
  1183. DEBUGMSG ((DBG_SHELL, "Entering shell folder %s", ShellFolderIdentifier));
  1184. regFolder = TRUE;
  1185. if (StringIMatch (ShellFolderIdentifier, S_SF_PROFILES)) {
  1186. regFolder = FALSE;
  1187. }
  1188. if (StringIMatch (ShellFolderIdentifier, S_SF_COMMON_PROFILES)) {
  1189. regFolder = FALSE;
  1190. }
  1191. //
  1192. // Get root default folder
  1193. //
  1194. Size = sizeof (DefaultShellFolder);
  1195. if (!GetDefaultUserProfileDirectory (DefaultShellFolder, &Size)) {
  1196. MYASSERT (FALSE);
  1197. __leave;
  1198. }
  1199. if (regFolder) {
  1200. //
  1201. // Get ShellFolderPath (with environment variables in it)
  1202. //
  1203. if (SystemShellFolder) {
  1204. UserHiveRoot = HKEY_LOCAL_MACHINE;
  1205. } else {
  1206. UserHiveRoot = g_DefaultHiveRoot;
  1207. }
  1208. Key = OpenRegKey (UserHiveRoot, S_USER_SHELL_FOLDERS_KEY);
  1209. if (Key) {
  1210. ValData = GetRegValueString (Key, ShellFolderIdentifier);
  1211. DEBUGMSG_IF ((!ValData, DBG_WARNING, "Can't get NT default for %s from registry", ShellFolderIdentifier));
  1212. CloseRegKey (Key);
  1213. }
  1214. ELSE_DEBUGMSG ((DBG_ERROR, "Can't open %s", S_USER_SHELL_FOLDERS_KEY));
  1215. if (ValData) {
  1216. StringCopy (ShellFolderPath, ValData);
  1217. MemFree (g_hHeap, 0, ValData);
  1218. ValData = NULL;
  1219. } else {
  1220. wsprintf (ShellFolderPath, TEXT("%s\\%s"), S_USERPROFILE_ENV, ShellFolderIdentifier);
  1221. }
  1222. }
  1223. //
  1224. // Get the user's profile root
  1225. //
  1226. if (FixedUserName) {
  1227. if (!GetUserProfilePath (FixedUserName, &p)) {
  1228. MYASSERT (FALSE);
  1229. __leave;
  1230. }
  1231. StringCopy (UserRoot, p);
  1232. allUsers = FALSE;
  1233. FreePathString (p);
  1234. } else {
  1235. Size = sizeof (UserRoot);
  1236. if (regFolder) {
  1237. if (!GetAllUsersProfileDirectory (UserRoot, &Size)) {
  1238. MYASSERT (FALSE);
  1239. __leave;
  1240. }
  1241. allUsers = TRUE;
  1242. } else {
  1243. if (!GetProfilesDirectory (UserRoot, &Size)) {
  1244. MYASSERT (FALSE);
  1245. __leave;
  1246. }
  1247. allUsers = FALSE;
  1248. }
  1249. }
  1250. if (regFolder) {
  1251. //
  1252. // Compute the default NT location and the Default User location
  1253. //
  1254. tempExpand = StringSearchAndReplace (
  1255. ShellFolderPath,
  1256. S_USERPROFILE_ENV,
  1257. UserRoot
  1258. );
  1259. if (!tempExpand) {
  1260. tempExpand = DuplicatePathString (ShellFolderPath, 0);
  1261. }
  1262. } else {
  1263. tempExpand = DuplicatePathString (UserRoot, 0);
  1264. }
  1265. NtDefaultLocation = ExpandEnvironmentText (tempExpand);
  1266. FreePathString (tempExpand);
  1267. if (regFolder) {
  1268. tempExpand = StringSearchAndReplace (
  1269. ShellFolderPath,
  1270. S_USERPROFILE_ENV,
  1271. DefaultShellFolder
  1272. );
  1273. if (!tempExpand) {
  1274. tempExpand = DuplicatePathString (ShellFolderPath, 0);
  1275. }
  1276. } else {
  1277. tempExpand = StringSearchAndReplace (
  1278. UserRoot,
  1279. S_USERPROFILE_ENV,
  1280. DefaultShellFolder
  1281. );
  1282. if (!tempExpand) {
  1283. tempExpand = DuplicatePathString (UserRoot, 0);
  1284. }
  1285. }
  1286. DefaultUserLocation = ExpandEnvironmentText (tempExpand);
  1287. FreePathString (tempExpand);
  1288. //
  1289. // Init the filter data struct
  1290. //
  1291. ZeroMemory (&Data, sizeof (Data));
  1292. Data.Win9xUser = Win9xUser;
  1293. Data.FixedUserName = FixedUserName;
  1294. Data.UserHiveRoot = UserHiveRoot;
  1295. Data.ShellFolderIdentifier = ShellFolderIdentifier;
  1296. Data.DefaultShellFolder = DefaultUserLocation;
  1297. Data.UserDefaultLocation = NtDefaultLocation;
  1298. Data.UserFlags = UserFlags;
  1299. Data.Context = INITIALIZE;
  1300. StringCopyByteCount (Data.TempSourcePath, SourcePath, sizeof (Data.TempSourcePath));
  1301. StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath));
  1302. Data.SrcRootPath = SourcePath;
  1303. Data.DestRootPath = OrgDestinationPath;
  1304. Data.OrigRootPath = OrigSourcePath;
  1305. Data.EnumPtr = EnumPtr;
  1306. Data.Attributes = GetLongPathAttributes (OrgDestinationPath);
  1307. //
  1308. // Establish the shell folder using the shell APIs
  1309. //
  1310. if (pCreateSfWithApi (
  1311. ShellFolderIdentifier,
  1312. OrgDestinationPath
  1313. )) {
  1314. DEBUGMSG ((
  1315. DBG_VERBOSE,
  1316. "Using API defaults for shell folder %s",
  1317. ShellFolderIdentifier
  1318. ));
  1319. Data.Attributes = GetLongPathAttributes (OrgDestinationPath);
  1320. }
  1321. if (Data.Attributes == INVALID_ATTRIBUTES) {
  1322. //
  1323. // We don't care about this shell folder's desktop.ini or
  1324. // attributes -- use the NT default attributes, or the
  1325. // Win9x attributes if there is no default.
  1326. //
  1327. Data.Attributes = GetLongPathAttributes (NtDefaultLocation);
  1328. if (Data.Attributes == INVALID_ATTRIBUTES) {
  1329. Data.Attributes = GetLongPathAttributes (Data.TempSourcePath);
  1330. }
  1331. if (Data.Attributes == INVALID_ATTRIBUTES) {
  1332. //
  1333. // This happens for shell folders like My Music & My Video
  1334. // which don't exist on Win9x
  1335. //
  1336. Data.Attributes = FILE_ATTRIBUTE_READONLY;
  1337. }
  1338. MakeSureLongPathExists (OrgDestinationPath, TRUE);
  1339. SetLongPathAttributes (OrgDestinationPath, Data.Attributes);
  1340. DEBUGMSG ((
  1341. DBG_VERBOSE,
  1342. "Using previous OS desktop.ini for shell folder %s, attribs=%08X",
  1343. ShellFolderIdentifier,
  1344. Data.Attributes
  1345. ));
  1346. }
  1347. //
  1348. // Now add string mappings for this shell folder. The reason for doing
  1349. // this is that we want to catch the case of paths to non-existent files
  1350. // within shell stored in the registry.
  1351. //
  1352. OrigRootPath = JoinPaths (Data.OrigRootPath, TEXT(""));
  1353. DestRootPath = JoinPaths (Data.DestRootPath, TEXT(""));
  1354. AddStringMappingPair (g_SubStringMap, OrigRootPath, DestRootPath);
  1355. FreePathString (DestRootPath);
  1356. FreePathString (OrigRootPath);
  1357. //
  1358. // PHASE ONE - move the files from 9x shell folder to their NT locations
  1359. //
  1360. //
  1361. // Call filters for init
  1362. //
  1363. for (Filter = g_Filters_9xNt ; Filter->Fn ; Filter++) {
  1364. //DEBUGMSGA ((DBG_SHELL, "9X->NT: INIT: %s (enter)", Filter->Name));
  1365. Data.State = 0;
  1366. Filter->Fn (&Data);
  1367. Filter->State = Data.State;
  1368. //DEBUGMSGA ((DBG_SHELL, "9X->NT: INIT: %s (done)", Filter->Name));
  1369. }
  1370. //
  1371. // Enumerate the shell folder and move it to the destination
  1372. //
  1373. DEBUGMSG ((DBG_SHELL, "9X->NT: Enumerating %s", SourcePath));
  1374. if (EnumFirstFileInTree (&e, SourcePath, NULL, FALSE)) {
  1375. do {
  1376. //
  1377. // Update the filter data struct
  1378. //
  1379. OrigFullPath = JoinPaths (OrigSourcePath, e.SubPath);
  1380. fileStatus = GetFileInfoOnNt (OrigFullPath, destPathBuffer, MEMDB_MAX);
  1381. DestPath = destPathBuffer;
  1382. if (fileStatus == FILESTATUS_UNCHANGED) {
  1383. //
  1384. // No reason not to move this file too
  1385. //
  1386. MYASSERT (StringIMatch (destPathBuffer, OrigFullPath));
  1387. DestPath = JoinPaths (Data.DestRootPath, e.SubPath);
  1388. if (!StringIMatch (OrigFullPath, DestPath)) {
  1389. MarkFileForMoveExternal (OrigFullPath, DestPath);
  1390. }
  1391. }
  1392. Data.Attributes = e.FindData->dwFileAttributes;
  1393. StringCopyByteCount (Data.TempSourcePath, e.FullPath, sizeof (Data.TempSourcePath));
  1394. StringCopyByteCount (Data.DestinationPath, DestPath, sizeof (Data.DestinationPath));
  1395. Data.Context = PROCESS_PATH;
  1396. DEBUGMSG ((DBG_SHELL, "9X->NT: Original temp source path: %s", Data.TempSourcePath));
  1397. //
  1398. // Allow filters to change source or dest, or to skip copy
  1399. //
  1400. keep = TRUE;
  1401. for (Filter = g_Filters_9xNt ; Filter->Fn ; Filter++) {
  1402. //DEBUGMSGA ((DBG_SHELL, "9X->NT: FILTER: %s (enter)", Filter->Name));
  1403. Data.State = Filter->State;
  1404. d = Filter->Fn (&Data);
  1405. Filter->State = Data.State;
  1406. //DEBUGMSGA ((DBG_SHELL, "9X->NT: FILTER: %s (result=%u)", Filter->Name, d));
  1407. // ignore SHELLFILTER_ERROR & try to complete processing
  1408. if (d == SHELLFILTER_FORCE_CHANGE) {
  1409. DEBUGMSG ((DBG_SHELL, "9X->NT: Skipping additional filters because shell folder filter %hs said so", Filter->Name));
  1410. break;
  1411. }
  1412. if (d == SHELLFILTER_SKIP_FILE) {
  1413. DEBUGMSG ((DBG_SHELL, "9X->NT:Skipping %s because shell folder filter %hs said so", DestPath, Filter->Name));
  1414. keep = FALSE;
  1415. break;
  1416. }
  1417. if (d == SHELLFILTER_SKIP_DIRECTORY) {
  1418. AbortEnumCurrentDir (&e);
  1419. keep = FALSE;
  1420. break;
  1421. }
  1422. }
  1423. if (keep && !(Data.Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1424. //
  1425. // Is source different from the dest?
  1426. //
  1427. if (!StringIMatch (Data.TempSourcePath, Data.DestinationPath)) {
  1428. //
  1429. // Make sure dest exists
  1430. //
  1431. MakeSureLongPathExists (Data.DestinationPath, FALSE); // FALSE == not path only
  1432. //
  1433. // Move or copy the file.
  1434. //
  1435. pQueueSfMove (Data.TempSourcePath, Data.DestinationPath);
  1436. }
  1437. } else if (keep) {
  1438. MakeSureLongPathExists (Data.DestinationPath, TRUE); // TRUE == path only
  1439. SetLongPathAttributes (Data.DestinationPath, Data.Attributes);
  1440. } else if (d == SHELLFILTER_SKIP_FILE) {
  1441. //
  1442. // Mark this file for deletion if it won't be moved from temp to dest
  1443. //
  1444. if (!StringIMatch (Data.TempSourcePath, Data.DestinationPath)) {
  1445. DEBUGMSG ((DBG_SHELL, "Deleting shell folder file %s", e.FullPath));
  1446. ForceOperationOnPath (e.FullPath, OPERATION_CLEANUP);
  1447. }
  1448. }
  1449. if (DestPath && DestPath != destPathBuffer) {
  1450. FreePathString (DestPath);
  1451. }
  1452. DestPath = NULL;
  1453. FreePathString (OrigFullPath);
  1454. OrigFullPath = NULL;
  1455. } while (EnumNextFileInTree (&e));
  1456. }
  1457. pFlushSfQueue();
  1458. //
  1459. // Call filters one last time
  1460. //
  1461. Data.Attributes = 0;
  1462. Data.Context = TERMINATE;
  1463. StringCopyByteCount (Data.TempSourcePath, SourcePath, sizeof (Data.TempSourcePath));
  1464. StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath));
  1465. for (Filter = g_Filters_9xNt ; Filter->Fn ; Filter++) {
  1466. //DEBUGMSGA ((DBG_SHELL, "9X->NT: TERMINATE: %s (enter)", Filter->Name));
  1467. Data.State = Filter->State;
  1468. Filter->Fn (&Data);
  1469. Filter->State = Data.State;
  1470. //DEBUGMSGA ((DBG_SHELL, "9X->NT: TERMINATE: %s (done)", Filter->Name));
  1471. }
  1472. //
  1473. // Now cleanup this directory for all empty dirs (excluding the root)
  1474. // Do not cleanup non reg folders!!
  1475. //
  1476. if (regFolder) {
  1477. DEBUGMSG ((DBG_NAUSEA, "Cleaning up %s", Data.DestinationPath));
  1478. pCleanupDir (Data.DestinationPath, FALSE);
  1479. }
  1480. //
  1481. // PHASE TWO - if necessary, merge files from NT default shell folder
  1482. // to the new location and update the registry
  1483. //
  1484. if (regFolder) {
  1485. //
  1486. // Encode string with %USERPROFILE%/%ALLUSERSPROFILE%, %SYSTEMROOT%
  1487. // or %SYSTEMDRIVE% if possible
  1488. //
  1489. // %USERPROFILE% or %ALLUSERSPROFILE%
  1490. tempExpand = OrgDestinationPath;
  1491. if (allUsers) {
  1492. nextExpand = StringSearchAndReplace (
  1493. tempExpand,
  1494. UserRoot,
  1495. S_ALLUSERSPROFILE_ENV
  1496. );
  1497. } else {
  1498. nextExpand = StringSearchAndReplace (
  1499. tempExpand,
  1500. UserRoot,
  1501. S_USERPROFILE_ENV
  1502. );
  1503. }
  1504. if (nextExpand) {
  1505. tempExpand = nextExpand;
  1506. }
  1507. // %SYSTEMROOT%
  1508. nextExpand = StringSearchAndReplace (
  1509. tempExpand,
  1510. g_WinDir,
  1511. S_SYSTEMROOT_ENV
  1512. );
  1513. if (nextExpand) {
  1514. if (tempExpand != OrgDestinationPath) {
  1515. FreePathString (tempExpand);
  1516. }
  1517. tempExpand = nextExpand;
  1518. }
  1519. // %SYSTEMDRIVE%
  1520. driveLetter[0] = g_WinDir[0];
  1521. nextExpand = StringSearchAndReplace (
  1522. tempExpand,
  1523. driveLetter,
  1524. S_SYSTEMDRIVE_ENV
  1525. );
  1526. if (nextExpand) {
  1527. if (tempExpand != OrgDestinationPath) {
  1528. FreePathString (tempExpand);
  1529. }
  1530. tempExpand = nextExpand;
  1531. }
  1532. // tempExpand points to OrgDestinationPath or a expanded path from the path pool
  1533. MYASSERT (tempExpand);
  1534. //
  1535. // Now store it. If HKLM, put it in the registry. Otherwise, put it
  1536. // in memdb, which will later be put in the user's hive.
  1537. //
  1538. if (Data.UserHiveRoot == HKEY_LOCAL_MACHINE) {
  1539. //
  1540. // Update the registry, User Shell Folder must point to original
  1541. // location
  1542. //
  1543. Key = OpenRegKey (Data.UserHiveRoot, S_USER_SHELL_FOLDERS_KEY);
  1544. if (Key) {
  1545. rc = RegSetValueEx (
  1546. Key,
  1547. Data.ShellFolderIdentifier,
  1548. 0,
  1549. REG_EXPAND_SZ,
  1550. (PBYTE) tempExpand,
  1551. SizeOfString (tempExpand)
  1552. );
  1553. DEBUGMSG_IF ((
  1554. rc != ERROR_SUCCESS,
  1555. DBG_ERROR,
  1556. "Can't save %s for %s",
  1557. tempExpand,
  1558. Data.ShellFolderIdentifier
  1559. ));
  1560. DEBUGMSG_IF ((
  1561. rc == ERROR_SUCCESS,
  1562. DBG_SHELL,
  1563. "Win9x shell location preserved: %s (%s)",
  1564. tempExpand,
  1565. Data.ShellFolderIdentifier
  1566. ));
  1567. CloseRegKey (Key);
  1568. }
  1569. ELSE_DEBUGMSG ((DBG_ERROR, "Can't open %s", S_USER_SHELL_FOLDERS_KEY));
  1570. } else {
  1571. EncodedKey = CreateEncodedRegistryStringEx (
  1572. S_USER_SHELL_FOLDERS_KEY,
  1573. Data.ShellFolderIdentifier,
  1574. FALSE
  1575. );
  1576. MemDbSetValueEx (
  1577. MEMDB_CATEGORY_USER_REGISTRY_VALUE,
  1578. tempExpand,
  1579. NULL,
  1580. NULL,
  1581. REG_EXPAND_SZ,
  1582. &Offset
  1583. );
  1584. MemDbSetValueEx (
  1585. MEMDB_CATEGORY_SET_USER_REGISTRY,
  1586. Data.FixedUserName,
  1587. EncodedKey,
  1588. NULL,
  1589. Offset,
  1590. NULL
  1591. );
  1592. FreeEncodedRegistryString (EncodedKey);
  1593. }
  1594. if (tempExpand != OrgDestinationPath) {
  1595. FreePathString (tempExpand);
  1596. }
  1597. }
  1598. if (!StringIMatch (OrgDestinationPath, NtDefaultLocation)) {
  1599. //
  1600. // Now move from the NT default location into the preserved location
  1601. //
  1602. //
  1603. // Fix the Data structure
  1604. //
  1605. Data.UserFlags = UserFlags;
  1606. Data.Context = INITIALIZE;
  1607. StringCopyByteCount (Data.TempSourcePath, NtDefaultLocation, sizeof (Data.TempSourcePath));
  1608. StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath));
  1609. Data.SrcRootPath = NtDefaultLocation;
  1610. Data.DestRootPath = OrgDestinationPath;
  1611. Data.OrigRootPath = OrigSourcePath;
  1612. //
  1613. // Now check to see if we already moved something into the preserved directory.
  1614. // If we did, we will not make the move (we will only delete the default files).
  1615. //
  1616. if (g_Merged9xFolders && (pSetupStringTableLookUpString (g_Merged9xFolders, (PTSTR)Data.DestRootPath, 0) != -1)) {
  1617. AlreadyMoved = TRUE;
  1618. }
  1619. else {
  1620. AlreadyMoved = FALSE;
  1621. pSetupStringTableAddString (g_Merged9xFolders, (PVOID) Data.DestRootPath, STRTAB_CASE_INSENSITIVE);
  1622. }
  1623. //
  1624. // Call filters for init
  1625. //
  1626. for (Filter = g_Filters_Nt9x ; Filter->Fn ; Filter++) {
  1627. //DEBUGMSGA ((DBG_SHELL, "NT->9X: INIT: %s (enter)", Filter->Name));
  1628. Data.State = 0;
  1629. Filter->Fn (&Data);
  1630. Filter->State = Data.State;
  1631. //DEBUGMSGA ((DBG_SHELL, "NT->9X: INIT: %s (done)", Filter->Name));
  1632. }
  1633. DEBUGMSG ((DBG_SHELL, "NT->9X: Enumerating %s", Data.TempSourcePath));
  1634. MYASSERT (Data.TempSourcePath && *Data.TempSourcePath);
  1635. if (EnumFirstFileInTree (&e, Data.TempSourcePath, NULL, FALSE)) {
  1636. do {
  1637. //
  1638. // This is only needed for user shell folders but does not hurt.
  1639. //
  1640. if (StringIMatch (TEXT("ntuser.dat"), e.Name)) {
  1641. continue;
  1642. }
  1643. //
  1644. // start with the assumption that the dest file is under the original
  1645. // destination path
  1646. //
  1647. NewDestPath = JoinPaths (OrgDestinationPath, e.SubPath);
  1648. //
  1649. // If this is desktop.ini, merge it with the existing one
  1650. //
  1651. if (StringIMatch (TEXT("desktop.ini"), e.Name)) {
  1652. DEBUGMSG ((
  1653. DBG_VERBOSE,
  1654. "Merging clean install %s with the one in Default User",
  1655. e.FullPath
  1656. ));
  1657. MergeIniFile (NewDestPath, e.FullPath, FALSE);
  1658. continue;
  1659. }
  1660. //
  1661. // Not the root shell folder desktop.ini -- continue processing
  1662. //
  1663. Data.Attributes = e.FindData->dwFileAttributes;
  1664. StringCopyByteCount (Data.TempSourcePath, e.FullPath, sizeof (Data.TempSourcePath));
  1665. StringCopyByteCount (Data.DestinationPath, NewDestPath, sizeof (Data.DestinationPath));
  1666. Data.Context = PROCESS_PATH;
  1667. DEBUGMSG ((DBG_SHELL, "NT->9X: Original temp source path: %s", Data.TempSourcePath));
  1668. //
  1669. // if we only need to delete the default files, skip the filters
  1670. //
  1671. if (AlreadyMoved) {
  1672. SetLongPathAttributes (Data.TempSourcePath, FILE_ATTRIBUTE_NORMAL);
  1673. if (!DeleteLongPath (Data.TempSourcePath)) {
  1674. SetLongPathAttributes (Data.TempSourcePath, Data.Attributes);
  1675. DEBUGMSG ((DBG_WARNING, "%s could not be removed.", Data.TempSourcePath));
  1676. }
  1677. }
  1678. else {
  1679. //
  1680. // Allow filters to change source or dest, or to skip copy
  1681. //
  1682. keep = TRUE;
  1683. for (Filter = g_Filters_Nt9x ; Filter->Fn ; Filter++) {
  1684. //DEBUGMSGA ((DBG_SHELL, "NT->9X: FILTER: %s (enter)", Filter->Name));
  1685. Data.State = Filter->State;
  1686. d = Filter->Fn (&Data);
  1687. Filter->State = Data.State;
  1688. //DEBUGMSGA ((DBG_SHELL, "NT->9X: FILTER: %s (result=%u)", Filter->Name, d));
  1689. if (d == SHELLFILTER_FORCE_CHANGE) {
  1690. break;
  1691. }
  1692. if (d == SHELLFILTER_SKIP_FILE) {
  1693. keep = FALSE;
  1694. break;
  1695. }
  1696. if (d == SHELLFILTER_SKIP_DIRECTORY) {
  1697. AbortEnumCurrentDir (&e);
  1698. keep = FALSE;
  1699. break;
  1700. }
  1701. }
  1702. if (keep) {
  1703. if (!(e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1704. pQueueSfMove (Data.TempSourcePath, Data.DestinationPath);
  1705. }
  1706. else {
  1707. MakeSureLongPathExists (Data.DestinationPath, TRUE); // TRUE == path only
  1708. SetLongPathAttributes (Data.DestinationPath, Data.Attributes);
  1709. }
  1710. }
  1711. }
  1712. FreePathString (NewDestPath);
  1713. } while (EnumNextFileInTree (&e));
  1714. }
  1715. pFlushSfQueue();
  1716. //
  1717. // Call filters one last time
  1718. //
  1719. Data.Attributes = 0;
  1720. Data.Context = TERMINATE;
  1721. StringCopyByteCount (Data.TempSourcePath, NtDefaultLocation, sizeof (Data.TempSourcePath));
  1722. StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath));
  1723. for (Filter = g_Filters_Nt9x ; Filter->Fn ; Filter++) {
  1724. //DEBUGMSGA ((DBG_SHELL, "NT->9X: TERMINATE: %s (enter)", Filter->Name));
  1725. Data.State = Filter->State;
  1726. Filter->Fn (&Data);
  1727. Filter->State = Data.State;
  1728. //DEBUGMSGA ((DBG_SHELL, "NT->9X: TERMINATE: %s (done)", Filter->Name));
  1729. }
  1730. //
  1731. // Now cleanup this directory for all empty dirs (including the root)
  1732. // Do not cleanup non reg folders!!
  1733. //
  1734. if (regFolder) {
  1735. DEBUGMSG ((DBG_NAUSEA, "Cleaning up %s (including root)", Data.TempSourcePath));
  1736. pCleanupDir (Data.TempSourcePath, TRUE);
  1737. }
  1738. }
  1739. //
  1740. // Loop through the whole tree and add desktop.ini to cleanup
  1741. //
  1742. if (EnumFirstFileInTree (&e, OrgDestinationPath, NULL, FALSE)) {
  1743. do {
  1744. if (!e.Directory) {
  1745. continue;
  1746. }
  1747. MemDbSetValueEx (
  1748. MEMDB_CATEGORY_CLEAN_OUT,
  1749. e.FullPath,
  1750. TEXT("desktop.ini"),
  1751. NULL,
  1752. BACKUP_FILE,
  1753. NULL
  1754. );
  1755. } while (EnumNextFileInTree (&e));
  1756. }
  1757. Result = TRUE;
  1758. }
  1759. __finally {
  1760. PushError();
  1761. AbortEnumFileInTree (&e);
  1762. FreeText (NtDefaultLocation);
  1763. FreeText (DefaultUserLocation);
  1764. if (bufferRoot) {
  1765. FreeMem (bufferRoot);
  1766. }
  1767. PopError();
  1768. }
  1769. DEBUGMSG ((
  1770. DBG_SHELL,
  1771. "Leaving shell folder %s with result %s",
  1772. ShellFolderIdentifier,
  1773. Result ? TEXT("TRUE") : TEXT("FALSE")
  1774. ));
  1775. return Result;
  1776. }
  1777. HKEY
  1778. pLoadDefaultUserHive (
  1779. VOID
  1780. )
  1781. {
  1782. DWORD Size;
  1783. BOOL b;
  1784. LONG rc;
  1785. if (!g_DefaultHiveMapped) {
  1786. if (!g_DefaultHivePath[0]) {
  1787. Size = sizeof (g_DefaultHivePath);
  1788. b = GetDefaultUserProfileDirectory (g_DefaultHivePath, &Size);
  1789. MYASSERT (b);
  1790. if (!b) {
  1791. wsprintf (g_DefaultHivePath, TEXT("%s\\profiles\\default user"), g_WinDir);
  1792. }
  1793. StringCopy (AppendWack (g_DefaultHivePath), TEXT("ntuser.dat"));
  1794. }
  1795. rc = RegLoadKey (HKEY_USERS, S_DEFAULT_USER, g_DefaultHivePath);
  1796. if (rc != ERROR_SUCCESS) {
  1797. DEBUGMSG ((DBG_ERROR, "Can't load default user hive from %s", g_DefaultHivePath));
  1798. g_DefaultHiveRoot = NULL;
  1799. return NULL;
  1800. }
  1801. g_DefaultHiveRoot = OpenRegKey (HKEY_USERS, S_DEFAULT_USER);
  1802. if (!g_DefaultHiveRoot) {
  1803. DEBUGMSG ((DBG_WHOOPS, "Loaded hive %s but could not open it", g_DefaultHivePath));
  1804. }
  1805. }
  1806. g_DefaultHiveMapped++;
  1807. return g_DefaultHiveRoot;
  1808. }
  1809. VOID
  1810. pUnloadDefaultUserHive (
  1811. VOID
  1812. )
  1813. {
  1814. if (!g_DefaultHiveMapped) {
  1815. return;
  1816. }
  1817. g_DefaultHiveMapped--;
  1818. if (!g_DefaultHiveMapped) {
  1819. CloseRegKey (g_DefaultHiveRoot);
  1820. RegUnLoadKey (HKEY_USERS, S_DEFAULT_USER);
  1821. }
  1822. }
  1823. VOID
  1824. pLoadIgnoredCollisions (
  1825. VOID
  1826. )
  1827. {
  1828. INFCONTEXT context;
  1829. TCHAR sfId[MEMDB_MAX];
  1830. TCHAR file[MEMDB_MAX];
  1831. INT value;
  1832. MYASSERT (g_WkstaMigInf);
  1833. if (SetupFindFirstLine (g_WkstaMigInf, S_IGNORED_COLLISIONS, NULL, &context)) {
  1834. do {
  1835. if (SetupGetStringField (&context, 1, sfId, MEMDB_MAX, NULL) &&
  1836. SetupGetStringField (&context, 2, file, MEMDB_MAX, NULL) &&
  1837. SetupGetIntField (&context, 3, &value)
  1838. ) {
  1839. MemDbSetValueEx (MEMDB_CATEGORY_IGNORED_COLLISIONS, sfId, file, NULL, value, NULL);
  1840. }
  1841. } while (SetupFindNextLine (&context, &context));
  1842. }
  1843. }
  1844. DWORD
  1845. MigrateShellFolders (
  1846. IN DWORD Request
  1847. )
  1848. {
  1849. MIGRATE_USER_ENUM e;
  1850. if (Request == REQUEST_QUERYTICKS) {
  1851. return TICKS_SYSTEM_SHELL_MIGRATION;
  1852. } else if (Request != REQUEST_RUN) {
  1853. return ERROR_SUCCESS;
  1854. }
  1855. pPrepareSfRestartability();
  1856. pLoadIgnoredCollisions ();
  1857. g_SystemSfList = pCreateSystemSfList ();
  1858. g_UserSfList = pCreateUserSfList (NULL);
  1859. pCreateLinksList ();
  1860. pCreateLinksRenameList ();
  1861. pDestroySfList (g_UserSfList);
  1862. pLoadDefaultUserHive();
  1863. g_Merged9xFolders = pSetupStringTableInitialize();
  1864. pMigrateSystemShellFolders();
  1865. if (EnumFirstUserToMigrate (&e, ENUM_NO_FLAGS)) {
  1866. do {
  1867. if (!e.CreateOnly && e.AccountType != DEFAULT_USER_ACCOUNT) {
  1868. pMigrateUserShellFolders (&e);
  1869. }
  1870. } while (EnumNextUserToMigrate (&e));
  1871. }
  1872. if (g_Merged9xFolders) {
  1873. pSetupStringTableDestroy (g_Merged9xFolders);
  1874. }
  1875. pFlushSfQueue();
  1876. pUnloadDefaultUserHive();
  1877. pDestroyLinksData ();
  1878. pDestroySfList (g_SystemSfList);
  1879. return ERROR_SUCCESS;
  1880. }
  1881. PCTSTR
  1882. GenerateNewFileName (
  1883. IN PCTSTR OldName,
  1884. IN WORD Sequencer,
  1885. IN BOOL CheckExistence
  1886. )
  1887. {
  1888. PCTSTR extPtr;
  1889. PTSTR newName;
  1890. PTSTR result;
  1891. extPtr = GetFileExtensionFromPath (OldName);
  1892. if (!extPtr) {
  1893. extPtr = GetEndOfString (OldName);
  1894. }
  1895. else {
  1896. extPtr = _tcsdec (OldName, extPtr);
  1897. }
  1898. newName = DuplicatePathString (OldName, 0);
  1899. result = DuplicatePathString (OldName, 10);
  1900. StringCopyAB (newName, OldName, extPtr);
  1901. do {
  1902. Sequencer ++;
  1903. wsprintf (result, TEXT("%s (%u)%s"), newName, Sequencer, extPtr);
  1904. } while ((CheckExistence) && (DoesFileExist (result)));
  1905. FreePathString (newName);
  1906. return result;
  1907. }
  1908. BOOL
  1909. pIgnoredCollisions (
  1910. IN PPROFILE_MERGE_DATA Data
  1911. )
  1912. {
  1913. TCHAR key[MEMDB_MAX];
  1914. DWORD value;
  1915. MemDbBuildKey (
  1916. key,
  1917. MEMDB_CATEGORY_IGNORED_COLLISIONS,
  1918. Data->ShellFolderIdentifier,
  1919. GetFileNameFromPath (Data->DestinationPath),
  1920. NULL);
  1921. if (MemDbGetPatternValue (key, &value)) {
  1922. return value;
  1923. } else {
  1924. return 0;
  1925. }
  1926. }
  1927. //
  1928. // Filters 9X -> NT
  1929. //
  1930. DWORD
  1931. pCollisionDetection9xNt (
  1932. IN OUT PPROFILE_MERGE_DATA Data
  1933. )
  1934. {
  1935. //
  1936. // this filter will detect name collision while copying win9x shell folders files.
  1937. // If we have a name collision, it means that NT already installed a file with the
  1938. // same name. In this case, we want the new file to be survive even with a different
  1939. // name. We will build a new file name starting with filename.ext. The new file will
  1940. // look something like filename001.ext. In all cases we want to keep the extension,
  1941. // since there might be some shell extensions active for this file.
  1942. // Important: we do not care about directory collisions.
  1943. //
  1944. PCTSTR newName;
  1945. PCTSTR OriginalSource;
  1946. PCTSTR extPtr;
  1947. DWORD value;
  1948. switch (Data->Context) {
  1949. case INITIALIZE:
  1950. break;
  1951. case PROCESS_PATH:
  1952. if ((!(Data->Attributes & FILE_ATTRIBUTE_DIRECTORY)) &&
  1953. (!StringIMatch (Data->SrcRootPath, Data->DestRootPath)) &&
  1954. (DoesFileExist (Data->DestinationPath))
  1955. ) {
  1956. value = pIgnoredCollisions (Data);
  1957. if (value) {
  1958. if (value == 1) {
  1959. // we should keep the NT file
  1960. // By returning SHELLFILTER_SKIP_FILE we are instructing the copy routine
  1961. // not to copy this file. As a result the already installed NT file will
  1962. // survive
  1963. return SHELLFILTER_SKIP_FILE;
  1964. } else {
  1965. // we should keep the 9x file
  1966. // We want to delete the NT file installed here to make room for the 9x
  1967. // file that should be copied when we return from this filter
  1968. SetLongPathAttributes (Data->DestinationPath, FILE_ATTRIBUTE_NORMAL);
  1969. DeleteLongPath (Data->DestinationPath);
  1970. }
  1971. } else {
  1972. newName = GenerateNewFileName (Data->DestinationPath, 0, TRUE); //TRUE - check unique
  1973. StringCopyByteCount (Data->DestinationPath, newName, sizeof (Data->DestinationPath));
  1974. FreePathString (newName);
  1975. //
  1976. // now if this was a link we need to fix the destination of the move external operation
  1977. // We have two reasons to do this. One is that the LinkEdit code needs the actual destination
  1978. // to be able to edit the link, and secondly we need this new target for the uninstall programs
  1979. // to work properly. If this file is not a LNK or a PIF, we don't care, we want everybody to
  1980. // use the NT installed file. BTW, there is a collision here only because NT installed a file
  1981. // with the same name in this location.
  1982. //
  1983. extPtr = GetFileExtensionFromPath (Data->DestinationPath);
  1984. if ((extPtr) &&
  1985. ((StringIMatch (extPtr, TEXT("LNK"))) ||
  1986. (StringIMatch (extPtr, TEXT("PIF")))
  1987. )
  1988. ) {
  1989. //
  1990. // Get the original source for this file
  1991. //
  1992. OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath);
  1993. MYASSERT (OriginalSource);
  1994. if (IsFileMarkedForOperation (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER)) {
  1995. RemoveOperationsFromPath (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER);
  1996. MarkFileForShellFolderMove (OriginalSource, Data->DestinationPath);
  1997. }
  1998. FreePathString (OriginalSource);
  1999. }
  2000. }
  2001. }
  2002. break;
  2003. case TERMINATE:
  2004. break;
  2005. }
  2006. return SHELLFILTER_OK;
  2007. }
  2008. DWORD
  2009. pFontNameFilter (
  2010. IN OUT PPROFILE_MERGE_DATA Data
  2011. )
  2012. {
  2013. static HASHTABLE HashTable;
  2014. HKEY FontKey;
  2015. REGVALUE_ENUM e;
  2016. PCTSTR Font;
  2017. switch (Data->Context) {
  2018. case INITIALIZE:
  2019. //
  2020. // Preload a hash table with all the font names
  2021. //
  2022. HashTable = HtAlloc();
  2023. FontKey = OpenRegKeyStr (TEXT("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"));
  2024. if (FontKey) {
  2025. if (EnumFirstRegValue (&e, FontKey)) {
  2026. do {
  2027. Font = GetRegValueString (FontKey, e.ValueName);
  2028. if (Font) {
  2029. HtAddString (HashTable, Font);
  2030. MemFree (g_hHeap, 0, Font);
  2031. }
  2032. ELSE_DEBUGMSG ((DBG_ERROR, "Can't get value data for %s in fonts key", e.ValueName));
  2033. } while (EnumNextRegValue (&e));
  2034. }
  2035. CloseRegKey (FontKey);
  2036. }
  2037. ELSE_LOG ((LOG_ERROR, "Can't open Fonts registry key. There may be duplicate font files."));
  2038. break;
  2039. case PROCESS_PATH:
  2040. //
  2041. // If the shell folder is Fonts, and the font is already
  2042. // registered, skip the Win9x copy.
  2043. //
  2044. if (StringIMatch (Data->ShellFolderIdentifier, TEXT("Fonts"))) {
  2045. if (!(Data->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
  2046. if (DoesFileExist (Data->DestinationPath)) {
  2047. //
  2048. // NT already installed this file. We won't overwrite this
  2049. // with the 9x copy.
  2050. //
  2051. DEBUGMSG ((
  2052. DBG_SHELL,
  2053. "Skipping copy of already existent font file: %s",
  2054. Data->DestinationPath
  2055. ));
  2056. return SHELLFILTER_SKIP_FILE;
  2057. }
  2058. if (HtFindString (HashTable, GetFileNameFromPath (Data->DestinationPath))) {
  2059. DEBUGMSG ((
  2060. DBG_SHELL,
  2061. "Skipping copy of already registered font file: %s",
  2062. Data->DestinationPath
  2063. ));
  2064. return SHELLFILTER_SKIP_FILE;
  2065. }
  2066. }
  2067. }
  2068. break;
  2069. case TERMINATE:
  2070. HtFree (HashTable);
  2071. HashTable = NULL;
  2072. break;
  2073. }
  2074. return SHELLFILTER_OK;
  2075. }
  2076. BOOL
  2077. pIsCommonSf (
  2078. IN PCTSTR ShellFolderTag
  2079. )
  2080. {
  2081. TCHAR memdbKey[MAX_SHELL_TAG + 32];
  2082. if (StringIPrefix (ShellFolderTag, TEXT("Common"))) {
  2083. return TRUE;
  2084. }
  2085. MemDbBuildKey (memdbKey, MEMDB_CATEGORY_SF_COMMON, ShellFolderTag, NULL, NULL);
  2086. return MemDbGetValue (memdbKey, NULL);
  2087. }
  2088. VOID
  2089. pConvertPerUserSfToCommon (
  2090. IN PCTSTR PerUserSf,
  2091. OUT PTSTR CommonSf // must hold MAX_SHELL_TAG chars
  2092. )
  2093. {
  2094. TCHAR memdbKey[MAX_SHELL_TAG + 32];
  2095. DWORD offset;
  2096. BOOL useDefault = TRUE;
  2097. MemDbBuildKey (memdbKey, MEMDB_CATEGORY_SF_PERUSER, PerUserSf, NULL, NULL);
  2098. if (MemDbGetValue (memdbKey, &offset)) {
  2099. if (MemDbBuildKeyFromOffset (offset, CommonSf, 1, NULL)) {
  2100. useDefault = FALSE;
  2101. }
  2102. }
  2103. if (useDefault) {
  2104. wsprintf (CommonSf, TEXT("Common %s"), PerUserSf);
  2105. }
  2106. }
  2107. VOID
  2108. pConvertCommonSfToPerUser (
  2109. IN PCTSTR CommonSf,
  2110. OUT PTSTR PerUserSf // must hold MAX_SHELL_TAG chars
  2111. )
  2112. {
  2113. TCHAR memdbKey[MAX_SHELL_TAG + 32];
  2114. DWORD offset;
  2115. BOOL useDefault = TRUE;
  2116. MemDbBuildKey (memdbKey, MEMDB_CATEGORY_SF_COMMON, CommonSf, NULL, NULL);
  2117. if (MemDbGetValue (memdbKey, &offset)) {
  2118. if (MemDbBuildKeyFromOffset (offset, PerUserSf, 1, NULL)) {
  2119. useDefault = FALSE;
  2120. }
  2121. }
  2122. if (useDefault) {
  2123. if (StringIPrefix (CommonSf, TEXT("Common"))) {
  2124. CommonSf += 6;
  2125. if (_tcsnextc (CommonSf) == TEXT(' ')) {
  2126. CommonSf++;
  2127. }
  2128. }
  2129. StringCopy (PerUserSf, CommonSf);
  2130. }
  2131. }
  2132. BOOL
  2133. pIsObsoleteLink (
  2134. IN PCTSTR ShortcutName,
  2135. IN PCTSTR ShortcutTarget,
  2136. IN PCTSTR ShortcutArgs,
  2137. IN PCTSTR CurrentShellFolder,
  2138. IN PCTSTR CurrentShellFolderPath
  2139. )
  2140. {
  2141. PLINK_DATA linkData = NULL;
  2142. PLINK_RENAME_DATA linkRenameData = NULL;
  2143. LONG stringId;
  2144. TCHAR perUserName[MAX_SHELL_TAG];
  2145. DEBUGMSG ((
  2146. DBG_SHELL,
  2147. "pIsObsoleteLink: Checking %s\n"
  2148. " Input Target: %s\n"
  2149. " Input Args: %s\n"
  2150. " Current Shell Folder: %s\n"
  2151. " Current Shell Folder Path: %s",
  2152. ShortcutName,
  2153. ShortcutTarget,
  2154. ShortcutArgs,
  2155. CurrentShellFolder,
  2156. CurrentShellFolderPath
  2157. ));
  2158. pConvertCommonSfToPerUser (CurrentShellFolder, perUserName);
  2159. stringId = pSetupStringTableLookUpString (g_FoldersTable, perUserName, 0);
  2160. if (stringId != -1) {
  2161. pSetupStringTableGetExtraData (g_FoldersTable, stringId, &linkData, sizeof (PLINK_DATA));
  2162. while (linkData) {
  2163. #if 0
  2164. DEBUGMSG ((
  2165. DBG_SHELL,
  2166. "Checking NT-installed LNK:\n"
  2167. " Target: %s\n"
  2168. " Args: %s",
  2169. linkData->Target,
  2170. linkData->Arguments
  2171. ));
  2172. #endif
  2173. if ((IsPatternMatch (linkData->Target, ShortcutTarget)) &&
  2174. (IsPatternMatch (linkData->Arguments, ShortcutArgs))
  2175. ) {
  2176. DEBUGMSG ((
  2177. DBG_SHELL,
  2178. "Obsolete link:\n"
  2179. " \"%s\" matched \"%s\"\n"
  2180. " \"%s\" matched \"%s\"",
  2181. linkData->Target,
  2182. ShortcutTarget,
  2183. linkData->Arguments,
  2184. ShortcutArgs
  2185. ));
  2186. return TRUE;
  2187. }
  2188. linkRenameData = g_LinkRenameData;
  2189. while (linkRenameData) {
  2190. #if 0
  2191. DEBUGMSG ((
  2192. DBG_SHELL,
  2193. "Checking NT rename data:\n"
  2194. " Old Target: %s\n"
  2195. " New Target: %s\n"
  2196. " Old Args: %s\n"
  2197. " New Args: %s",
  2198. linkRenameData->OldTarget,
  2199. linkRenameData->NewTarget,
  2200. linkRenameData->OldArguments,
  2201. linkRenameData->NewArguments
  2202. ));
  2203. #endif
  2204. if (StringIMatch (linkRenameData->ShellFolderName, perUserName)) {
  2205. if ((IsPatternMatch (linkRenameData->OldTarget, ShortcutTarget)) &&
  2206. (IsPatternMatch (linkRenameData->NewTarget, linkData->Target)) &&
  2207. (IsPatternMatch (linkRenameData->OldArguments, ShortcutArgs)) &&
  2208. (IsPatternMatch (linkRenameData->NewArguments, linkData->Arguments))
  2209. ) {
  2210. DEBUGMSG ((
  2211. DBG_SHELL,
  2212. "Obsolete link:\n"
  2213. " \"%s\" matched \"%s\"\n"
  2214. " \"%s\" matched \"%s\"\n"
  2215. " \"%s\" matched \"%s\"\n"
  2216. " \"%s\" matched \"%s\"\n",
  2217. linkRenameData->OldTarget, ShortcutTarget,
  2218. linkRenameData->NewTarget, linkData->Target,
  2219. linkRenameData->OldArguments, ShortcutArgs,
  2220. linkRenameData->NewArguments, linkData->Arguments
  2221. ));
  2222. return TRUE;
  2223. }
  2224. }
  2225. linkRenameData = linkRenameData->Next;
  2226. }
  2227. linkData = linkData->Next;
  2228. }
  2229. }
  2230. ELSE_DEBUGMSG ((DBG_SHELL, "Nothing in shell folder %s is obsolete", perUserName));
  2231. return FALSE;
  2232. }
  2233. DWORD
  2234. pStartupDisableFilter (
  2235. IN OUT PPROFILE_MERGE_DATA Data
  2236. )
  2237. {
  2238. DWORD status;
  2239. PCTSTR originalSource;
  2240. PCTSTR newSource;
  2241. PCTSTR path;
  2242. TCHAR disablePath[MAX_TCHAR_PATH];
  2243. PTSTR dontCare;
  2244. DWORD result = SHELLFILTER_OK;
  2245. switch (Data->Context) {
  2246. case INITIALIZE:
  2247. break;
  2248. case PROCESS_PATH:
  2249. DEBUGMSG ((
  2250. DBG_SHELL,
  2251. __FUNCTION__ ": Processing %s in %s",
  2252. Data->TempSourcePath,
  2253. Data->ShellFolderIdentifier
  2254. ));
  2255. if (!StringIMatch (Data->ShellFolderIdentifier, TEXT("startup")) &&
  2256. !StringIMatch (Data->ShellFolderIdentifier, TEXT("common startup"))
  2257. ) {
  2258. DEBUGMSG ((
  2259. DBG_SHELL,
  2260. "Shell folder ID %s does not match startup or common startup",
  2261. Data->ShellFolderIdentifier
  2262. ));
  2263. break;
  2264. }
  2265. if (Data->DestRootPath[0] == 0 ||
  2266. Data->DestRootPath[1] == 0 ||
  2267. Data->DestRootPath[2] == 0 ||
  2268. Data->DestRootPath[3] == 0
  2269. ) {
  2270. DEBUGMSG ((
  2271. DBG_SHELL,
  2272. "Skipping disable of startup item %s because its dest is a root directory",
  2273. Data->DestinationPath
  2274. ));
  2275. break;
  2276. }
  2277. originalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath);
  2278. MYASSERT (originalSource);
  2279. DEBUGMSG ((DBG_SHELL, "Checking if %s is disabled", originalSource));
  2280. if (IsFileDisabled (originalSource)) {
  2281. //
  2282. // Redirect disabled startup items to ..\Disabled Startup
  2283. //
  2284. path = JoinPaths (Data->DestRootPath, TEXT("..\\Disabled Startup"));
  2285. MakeSureLongPathExists (path, TRUE); // TRUE == path only
  2286. GetFullPathName (path, ARRAYSIZE(disablePath), disablePath, &dontCare);
  2287. FreePathString (path);
  2288. DEBUGMSG ((DBG_SHELL, "Disabled startup dest is %s", disablePath));
  2289. SetLongPathAttributes (disablePath, FILE_ATTRIBUTE_HIDDEN);
  2290. newSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, disablePath);
  2291. StringCopy (Data->DestinationPath, newSource);
  2292. FreePathString (newSource);
  2293. DEBUGMSG ((DBG_SHELL, "Startup item moved to %s", Data->DestinationPath));
  2294. RemoveOperationsFromPath (originalSource, OPERATION_FILE_DISABLED);
  2295. if (IsFileMarkedForOperation (originalSource, OPERATION_FILE_MOVE_SHELL_FOLDER)) {
  2296. RemoveOperationsFromPath (originalSource, OPERATION_FILE_MOVE_SHELL_FOLDER);
  2297. MarkFileForShellFolderMove (originalSource, Data->DestinationPath);
  2298. }
  2299. //
  2300. // By returning SHELLFILTER_FORCE_CHANGE, we are instructing the
  2301. // shell folder algorithm to use our destination and not call anyone
  2302. // else.
  2303. //
  2304. result = SHELLFILTER_FORCE_CHANGE;
  2305. }
  2306. FreePathString (originalSource);
  2307. break;
  2308. case TERMINATE:
  2309. break;
  2310. }
  2311. return result;
  2312. }
  2313. DWORD
  2314. pObsoleteLinksFilter (
  2315. IN OUT PPROFILE_MERGE_DATA Data
  2316. )
  2317. {
  2318. static IShellLink *shellLink = NULL;
  2319. static IPersistFile *persistFile = NULL;
  2320. static PTSTR bigBuf;
  2321. static PTSTR ShortcutTarget;
  2322. static PTSTR ShortcutArgs;
  2323. static PTSTR ShortcutWorkDir;
  2324. static PTSTR ShortcutIconPath;
  2325. INT ShortcutIcon;
  2326. WORD ShortcutHotKey;
  2327. BOOL result = FALSE;
  2328. BOOL dosApp;
  2329. BOOL msDosMode;
  2330. PCTSTR extPtr;
  2331. FILEOP_PROP_ENUM eOpProp;
  2332. PTSTR NewTarget;
  2333. PCTSTR OriginalSource;
  2334. switch (Data->Context) {
  2335. case INITIALIZE:
  2336. if (!InitCOMLink (&shellLink, &persistFile)) {
  2337. DEBUGMSG ((DBG_ERROR, "Cannot initialize COM. Obsolete links filter will not work."));
  2338. return SHELLFILTER_ERROR;
  2339. }
  2340. bigBuf = (PTSTR) MemAllocUninit ((MEMDB_MAX * 4) * sizeof (TCHAR));
  2341. if (!bigBuf) {
  2342. return SHELLFILTER_ERROR;
  2343. }
  2344. ShortcutTarget = bigBuf;
  2345. ShortcutArgs = ShortcutTarget + MEMDB_MAX;
  2346. ShortcutWorkDir = ShortcutArgs + MEMDB_MAX;
  2347. ShortcutIconPath = ShortcutWorkDir + MEMDB_MAX;
  2348. break;
  2349. case PROCESS_PATH:
  2350. extPtr = GetFileExtensionFromPath (Data->DestinationPath);
  2351. if (!extPtr) {
  2352. return SHELLFILTER_OK;
  2353. }
  2354. if ((!StringIMatch (extPtr, TEXT("LNK"))) &&
  2355. (!StringIMatch (extPtr, TEXT("PIF")))
  2356. ) {
  2357. return SHELLFILTER_OK;
  2358. }
  2359. DEBUGMSG ((DBG_SHELL, "Extracting shortcut info for temp file %s", Data->TempSourcePath));
  2360. if ((shellLink) &&
  2361. (persistFile) &&
  2362. (ExtractShortcutInfo (
  2363. ShortcutTarget,
  2364. ShortcutArgs,
  2365. ShortcutWorkDir,
  2366. ShortcutIconPath,
  2367. &ShortcutIcon,
  2368. &ShortcutHotKey,
  2369. &dosApp,
  2370. &msDosMode,
  2371. NULL,
  2372. NULL,
  2373. Data->TempSourcePath,
  2374. shellLink,
  2375. persistFile
  2376. ))) {
  2377. // get the new destination if this shortcut is to be edited
  2378. NewTarget = NULL;
  2379. //
  2380. // Get the original source for this file
  2381. //
  2382. OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath);
  2383. MYASSERT (OriginalSource);
  2384. DEBUGMSG ((DBG_SHELL, "OriginalSource for shortcut is %s", OriginalSource));
  2385. if (IsFileMarkedForOperation (OriginalSource, OPERATION_LINK_EDIT)) {
  2386. DEBUGMSG ((DBG_SHELL, "OriginalSource is marked for file edit"));
  2387. if (EnumFirstFileOpProperty (&eOpProp, GetSequencerFromPath (OriginalSource), OPERATION_LINK_EDIT)) {
  2388. do {
  2389. if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_TARGET)) {
  2390. NewTarget = DuplicatePathString (eOpProp.Property, 0);
  2391. break;
  2392. }
  2393. } while (EnumNextFileOpProperty (&eOpProp));
  2394. }
  2395. }
  2396. FreePathString (OriginalSource);
  2397. if (!NewTarget) {
  2398. NewTarget = DuplicatePathString (ShortcutTarget, 0);
  2399. }
  2400. result = pIsObsoleteLink (Data->DestinationPath, NewTarget, ShortcutArgs, Data->ShellFolderIdentifier, Data->DestRootPath);
  2401. DEBUGMSG_IF ((result, DBG_SHELL, "%s is obsolete", Data->DestinationPath));
  2402. DEBUGMSG_IF ((!result, DBG_SHELL, "%s is not obsolete", Data->DestinationPath));
  2403. FreePathString (NewTarget);
  2404. }
  2405. if (result) {
  2406. //
  2407. // If this link is to be edited by the LinkEdit code we should remove this
  2408. // operation because the file will not be available.
  2409. //
  2410. DEBUGMSG ((DBG_SHELL, "File %s will not be available for LinkEdit", Data->TempSourcePath));
  2411. //
  2412. // Get the original source for this file
  2413. //
  2414. OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath);
  2415. MYASSERT (OriginalSource);
  2416. if (IsFileMarkedForOperation (OriginalSource, OPERATION_LINK_EDIT)) {
  2417. RemoveOperationsFromPath (OriginalSource, OPERATION_LINK_EDIT);
  2418. }
  2419. FreePathString (OriginalSource);
  2420. //
  2421. // Now remove the source file. We cannot keep this file to be restored by the UNDO code.
  2422. // The reason for this is that we might have some other
  2423. // shell folder pointing to the same source and destination. In this case, obsolete links
  2424. // filter will not work since we just removed the file from OPERATION_LINK_EDIT.
  2425. //
  2426. MYASSERT ((Data->Attributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
  2427. SetLongPathAttributes (Data->TempSourcePath, FILE_ATTRIBUTE_NORMAL);
  2428. if (!DeleteLongPath (Data->TempSourcePath)) {
  2429. DEBUGMSG ((DBG_ERROR, "Cannot remove file %s", Data->TempSourcePath));
  2430. SetLongPathAttributes (Data->TempSourcePath, Data->Attributes);
  2431. }
  2432. return SHELLFILTER_SKIP_FILE;
  2433. }
  2434. return SHELLFILTER_OK;
  2435. case TERMINATE:
  2436. if (bigBuf) {
  2437. FreeMem (bigBuf);
  2438. }
  2439. FreeCOMLink (&shellLink, &persistFile);
  2440. break;
  2441. }
  2442. return SHELLFILTER_OK;
  2443. }
  2444. //
  2445. // Filters NT -> 9X
  2446. //
  2447. DWORD
  2448. pCollisionDetectionNt9x (
  2449. IN OUT PPROFILE_MERGE_DATA Data
  2450. )
  2451. {
  2452. //
  2453. // this filter will detect name collision while copying files from NT shell folders
  2454. // or default user to migrated 9x shell folder.
  2455. // If we have a name collision, we want to keep the NT file original name and to rename
  2456. // the migrated Win9x file. We will build a new file name starting with filename.ext.
  2457. // The new file will look something like filename001.ext. In all cases we want to keep
  2458. // the extension, since there might be some shell extensions active for this file.
  2459. // Important: we do not care about directory collisions.
  2460. //
  2461. PCTSTR newName;
  2462. PCTSTR extPtr;
  2463. PCTSTR OriginalSource;
  2464. DWORD value;
  2465. switch (Data->Context) {
  2466. case INITIALIZE:
  2467. break;
  2468. case PROCESS_PATH:
  2469. if ((!(Data->Attributes & FILE_ATTRIBUTE_DIRECTORY)) &&
  2470. (!StringIMatch (Data->SrcRootPath, Data->DestRootPath)) &&
  2471. (DoesFileExist (Data->DestinationPath))
  2472. ) {
  2473. value = pIgnoredCollisions (Data);
  2474. if (value) {
  2475. if (value == 1) {
  2476. // we should keep the 9x file
  2477. // By returning SHELLFILTER_SKIP_FILE we are instructing the copy routine
  2478. // not to copy this file. As a result the already installed 9x file will
  2479. // survive
  2480. return SHELLFILTER_SKIP_FILE;
  2481. } else {
  2482. // we should keep the NT file
  2483. // We want to delete the 9x file installed here to make room for the NT
  2484. // file that should be copied when we return from this filter
  2485. SetLongPathAttributes (Data->DestinationPath, FILE_ATTRIBUTE_NORMAL);
  2486. DeleteLongPath (Data->DestinationPath);
  2487. }
  2488. } else {
  2489. newName = GenerateNewFileName (Data->DestinationPath, 0, TRUE); //TRUE - check unique
  2490. DEBUGMSG ((
  2491. DBG_SHELL,
  2492. "9x file collides with NT file -- renaming 9x file from %s to %s",
  2493. Data->DestinationPath,
  2494. newName
  2495. ));
  2496. pQueueSfMove (Data->DestinationPath, newName);
  2497. //
  2498. // now if this was a link we need to fix the destination of the move external operation
  2499. // We have two reasons to do this. One is that the LinkEdit code needs the actual destination
  2500. // to be able to edit the link, and secondly we need this new target for the uninstall programs
  2501. // to work properly. If this file is not a LNK or a PIF, we don't care, we want everybody to
  2502. // use the NT installed file. BTW, there is a collision here only because NT installed a file
  2503. // with the same name in this location.
  2504. //
  2505. extPtr = GetFileExtensionFromPath (Data->DestinationPath);
  2506. if ((extPtr) &&
  2507. ((StringIMatch (extPtr, TEXT("LNK"))) ||
  2508. (StringIMatch (extPtr, TEXT("PIF")))
  2509. )
  2510. ) {
  2511. //
  2512. // Get the original source for this file
  2513. //
  2514. OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath);
  2515. MYASSERT (OriginalSource);
  2516. if (IsFileMarkedForOperation (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER)) {
  2517. DEBUGMSG ((
  2518. DBG_SHELL,
  2519. "Removing shell move op from %s",
  2520. OriginalSource
  2521. ));
  2522. RemoveOperationsFromPath (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER);
  2523. MarkFileForShellFolderMove (OriginalSource, newName);
  2524. }
  2525. FreePathString (OriginalSource);
  2526. }
  2527. FreePathString (newName);
  2528. }
  2529. }
  2530. break;
  2531. case TERMINATE:
  2532. break;
  2533. }
  2534. return SHELLFILTER_OK;
  2535. }
  2536. DWORD
  2537. pDetectOtherShellFolder (
  2538. IN OUT PPROFILE_MERGE_DATA Data
  2539. )
  2540. {
  2541. switch (Data->Context) {
  2542. case INITIALIZE:
  2543. g_UserSfList = pCreateUserSfList (Data);
  2544. break;
  2545. case PROCESS_PATH:
  2546. if (Data->Attributes & FILE_ATTRIBUTE_DIRECTORY) {
  2547. //
  2548. // This is a dir. Let's see if we enter another shell folder
  2549. //
  2550. if (((g_SystemSfList) && (pSetupStringTableLookUpString (g_SystemSfList, (PVOID) Data->TempSourcePath, STRTAB_CASE_INSENSITIVE) != -1)) ||
  2551. ((g_UserSfList) && (pSetupStringTableLookUpString (g_UserSfList, (PVOID) Data->TempSourcePath, STRTAB_CASE_INSENSITIVE) != -1))
  2552. ) {
  2553. //
  2554. // we are just getting into another shell folder. Let's skip it
  2555. //
  2556. return SHELLFILTER_SKIP_DIRECTORY;
  2557. }
  2558. }
  2559. break;
  2560. case TERMINATE:
  2561. pDestroySfList (g_UserSfList);
  2562. break;
  2563. }
  2564. return SHELLFILTER_OK;
  2565. }