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.

1346 lines
30 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. LUA_RedirectFS_Cleanup.cpp
  5. Abstract:
  6. Delete the redirected copies in every user's directory.
  7. Created:
  8. 02/12/2001 maonis
  9. Modified:
  10. --*/
  11. #include "precomp.h"
  12. #include "secutils.h"
  13. #include "utils.h"
  14. #include <userenv.h>
  15. WCHAR g_wszUserProfile[MAX_PATH] = L"";
  16. DWORD g_cUserProfile = 0;
  17. WCHAR g_wszSystemRoot[MAX_PATH] = L"";
  18. DWORD g_cSystemRoot = 0;
  19. DWORD
  20. GetUserProfileDirW()
  21. {
  22. if (g_cUserProfile == 0)
  23. {
  24. HANDLE hToken;
  25. if (OpenProcessToken(
  26. GetCurrentProcess(),
  27. TOKEN_QUERY,
  28. &hToken))
  29. {
  30. WCHAR wszProfileDir[MAX_PATH] = L"";
  31. DWORD dwSize = MAX_PATH;
  32. if (GetUserProfileDirectoryW(hToken, wszProfileDir, &dwSize))
  33. {
  34. dwSize = GetLongPathNameW(wszProfileDir, g_wszUserProfile, MAX_PATH);
  35. if (dwSize <= MAX_PATH)
  36. {
  37. //
  38. // Only if we successfully got the path and it's not more
  39. // than MAX_PATH will we set the global values.
  40. //
  41. g_cUserProfile = dwSize;
  42. }
  43. else
  44. {
  45. g_wszUserProfile[0] = L'\0';
  46. }
  47. }
  48. CloseHandle(hToken);
  49. }
  50. }
  51. return g_cUserProfile;
  52. }
  53. BOOL
  54. IsUserDirectory(LPCWSTR pwszPath)
  55. {
  56. GetUserProfileDirW();
  57. if (g_cUserProfile)
  58. {
  59. return !_wcsnicmp(pwszPath, g_wszUserProfile, g_cUserProfile);
  60. }
  61. return FALSE;
  62. }
  63. DWORD
  64. GetSystemRootDirW()
  65. {
  66. if (g_cSystemRoot == 0)
  67. {
  68. if (g_cSystemRoot = GetSystemWindowsDirectoryW(g_wszSystemRoot, MAX_PATH))
  69. {
  70. //
  71. // Just to be cautious - if we really have a system directory that's
  72. // longer than MAX_PATH, most likely something suspicious is going on
  73. // here, so we bail out.
  74. //
  75. if (g_cSystemRoot >= MAX_PATH)
  76. {
  77. g_wszSystemRoot[0] = L'\0';
  78. g_cSystemRoot = 0;
  79. }
  80. else if (g_cSystemRoot > 3)
  81. {
  82. g_wszSystemRoot[g_cSystemRoot] = L'\\';
  83. g_wszSystemRoot[g_cSystemRoot + 1] = L'\0';
  84. ++g_cSystemRoot;
  85. }
  86. else
  87. {
  88. g_wszSystemRoot[g_cSystemRoot] = L'\0';
  89. }
  90. }
  91. }
  92. return g_cSystemRoot;
  93. }
  94. /*++
  95. Function Description:
  96. For the GetPrivateProfile* and WritePrivateProfile* APIs,
  97. if the app didn't specify the path, we append the windows dir
  98. in the front as that's where it'll be looking for and creating
  99. the file it doesn't already exist.
  100. Arguments:
  101. IN lpFileName - The file name specified by the profile API.
  102. IN/OUT pwszFullPath - Pointer to the buffer to receive the full path.
  103. This buffer is at least MAX_PATH WCHARs long.
  104. Return Value:
  105. TRUE - Successfully got the path.
  106. FALSE - We don't handle this filename, either because an error
  107. occured or the file name is longer than MAX_PATH.
  108. History:
  109. 05/16/2001 maonis Created
  110. 02/13/2002 maonis Modified to signal errors.
  111. --*/
  112. BOOL
  113. MakeFileNameForProfileAPIsW(
  114. IN LPCWSTR lpFileName,
  115. IN OUT LPWSTR pwszFullPath // at least MAX_PATH in length
  116. )
  117. {
  118. BOOL fIsSuccess = FALSE;
  119. if (lpFileName)
  120. {
  121. DWORD cFileNameLen = wcslen(lpFileName);
  122. if (wcschr(lpFileName, L'\\'))
  123. {
  124. if (cFileNameLen < MAX_PATH)
  125. {
  126. //
  127. // The filename already contains the path, just copy it over.
  128. //
  129. wcsncpy(pwszFullPath, lpFileName, cFileNameLen);
  130. fIsSuccess = TRUE;
  131. }
  132. }
  133. else if (GetSystemRootDirW() && g_cSystemRoot)
  134. {
  135. DWORD cLen = g_cSystemRoot + cFileNameLen;
  136. //
  137. // Only copy when we know the buffer is big enough.
  138. //
  139. if (cLen < MAX_PATH)
  140. {
  141. wcsncpy(pwszFullPath, g_wszSystemRoot, g_cSystemRoot);
  142. wcsncpy(pwszFullPath + g_cSystemRoot, lpFileName, cFileNameLen);
  143. pwszFullPath[cLen - 1] = L'\0';
  144. fIsSuccess = TRUE;
  145. }
  146. }
  147. }
  148. return fIsSuccess;
  149. }
  150. //
  151. // If the .exe name is *setup*, *install* or _INS*._MP, we consider
  152. // them a setup program and won't shim them.
  153. //
  154. BOOL IsSetup(
  155. )
  156. {
  157. WCHAR wszModuleName[MAX_PATH + 1];
  158. ZeroMemory(wszModuleName, (MAX_PATH + 1) * sizeof(WCHAR));
  159. GetModuleFileNameW(NULL, wszModuleName, MAX_PATH + 1);
  160. wszModuleName[MAX_PATH] = 0;
  161. _wcslwr(wszModuleName);
  162. if (wcsstr(wszModuleName, L"setup") || wcsstr(wszModuleName, L"install"))
  163. {
  164. return TRUE;
  165. }
  166. LPWSTR pwsz;
  167. if (pwsz = wcsstr(wszModuleName, L"_ins"))
  168. {
  169. if (wcsstr(pwsz + 4, L"_mp"))
  170. {
  171. return TRUE;
  172. }
  173. }
  174. return FALSE;
  175. }
  176. BOOL LuaShouldApplyShim(
  177. )
  178. {
  179. return (!IsSetup() && ShouldApplyShim());
  180. }
  181. #define REDIRECT_DIR L"\\Local Settings\\Application Data\\Redirected\\"
  182. // We look at HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList for the users.
  183. #define PROFILELIST_STR L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"
  184. #define CLASSES_HIVE_SUFFIX L"_Classes"
  185. #define CLASSES_HIVE_SUFFIX_LEN (sizeof(CLASSES_HIVE_SUFFIX) / sizeof(WCHAR) - 1)
  186. #define USER_HIVE_NAME L"\\NtUser.dat"
  187. #define USER_HIVE_NAME_LEN (sizeof(USER_HIVE_NAME) / sizeof(WCHAR) - 1)
  188. #define USER_CLASSES_HIVE_NAME L"\\Local Settings\\Application Data\\Microsoft\\Windows\\UsrClass.dat"
  189. #define USER_CLASSES_HIVE_NAME_LEN (sizeof(USER_CLASSES_HIVE_NAME) / sizeof(WCHAR) - 1)
  190. // Total number of users which is the number of subkeys of
  191. // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
  192. static DWORD g_cUsers = 0;
  193. // We need to keep a list of keys we had to load under HKEY_USERS and unload them
  194. // when the process exits.
  195. static WCHAR** g_wszLoadedKeys = NULL;
  196. static DWORD g_cLoadedKeys = 0;
  197. // The number of users is the number of subkeys under
  198. // HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
  199. LONG
  200. InitGetUsers(
  201. OUT DWORD* pcUsers,
  202. OUT HKEY* phKey
  203. )
  204. {
  205. LONG lRes;
  206. if ((lRes = RegOpenKeyExW(
  207. HKEY_LOCAL_MACHINE,
  208. PROFILELIST_STR,
  209. 0,
  210. KEY_READ,
  211. phKey)) == ERROR_SUCCESS)
  212. {
  213. lRes = RegQueryInfoKeyW(*phKey, NULL, NULL, NULL, pcUsers,
  214. NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  215. RegCloseKey(*phKey);
  216. }
  217. return lRes;
  218. }
  219. // In case of failure we need to clean up our array.
  220. VOID
  221. FreeUserDirectoryArray(
  222. REDIRECTED_USER_PATH* pRedirectUserPaths
  223. )
  224. {
  225. for (DWORD ui = 0; ui < g_cUsers; ++ui)
  226. {
  227. delete [] pRedirectUserPaths[ui].pwszPath;
  228. }
  229. delete [] pRedirectUserPaths;
  230. }
  231. BOOL
  232. IsDirectory(
  233. WCHAR* pwszName
  234. )
  235. {
  236. DWORD dwAttrib = GetFileAttributesW(pwszName);
  237. return (dwAttrib != -1 && dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
  238. }
  239. LONG GetProfilePath(
  240. HKEY hkProfileList,
  241. LPCWSTR pwszUserSID,
  242. LPWSTR pwszUserDirectory
  243. )
  244. {
  245. LONG lRes;
  246. HKEY hkUserSID;
  247. DWORD dwFlags;
  248. // Open the user SID key.
  249. if ((lRes = RegOpenKeyExW(
  250. hkProfileList,
  251. pwszUserSID,
  252. 0,
  253. KEY_QUERY_VALUE,
  254. &hkUserSID)) == ERROR_SUCCESS)
  255. {
  256. DWORD dwSize = sizeof(DWORD);
  257. if ((lRes = RegQueryValueExW(
  258. hkUserSID,
  259. L"Flags",
  260. NULL,
  261. NULL,
  262. (LPBYTE)&dwFlags,
  263. &dwSize)) == ERROR_SUCCESS)
  264. {
  265. // Check if the value of Flag is 0, if so it's the user we care about.
  266. if (dwFlags == 0)
  267. {
  268. DWORD cTemp = MAX_PATH;
  269. WCHAR wszTemp[MAX_PATH] = L"";
  270. if ((lRes = RegQueryValueExW(
  271. hkUserSID,
  272. L"ProfileImagePath",
  273. NULL,
  274. NULL,
  275. (LPBYTE)wszTemp,
  276. &cTemp)) == ERROR_SUCCESS)
  277. {
  278. DWORD cExpandLen = ExpandEnvironmentStringsW(wszTemp, pwszUserDirectory, MAX_PATH);
  279. if (cExpandLen > MAX_PATH)
  280. {
  281. lRes = ERROR_MORE_DATA;
  282. }
  283. }
  284. }
  285. else
  286. {
  287. lRes = ERROR_INVALID_HANDLE;
  288. }
  289. }
  290. RegCloseKey(hkUserSID);
  291. }
  292. return lRes;
  293. }
  294. BOOL
  295. GetUsersFS(
  296. REDIRECTED_USER_PATH** ppRedirectUserPaths,
  297. DWORD* pcUsers
  298. )
  299. {
  300. WCHAR wszRedirectDir[MAX_PATH] = L"";
  301. DWORD cUsers;
  302. HKEY hkProfileList;
  303. if (InitGetUsers(&cUsers, &hkProfileList) != ERROR_SUCCESS)
  304. {
  305. DPF("LUAUtils", eDbgLevelError, "[GetUsersFS] Error initializing");
  306. return FALSE;
  307. }
  308. *ppRedirectUserPaths = new REDIRECTED_USER_PATH [cUsers];
  309. if (!*ppRedirectUserPaths)
  310. {
  311. DPF("LUAUtils", eDbgLevelError, "[GetUsersFS] Error allocating memory");
  312. return FALSE;
  313. }
  314. REDIRECTED_USER_PATH* pRedirectUserPaths = *ppRedirectUserPaths;
  315. ZeroMemory((PVOID)pRedirectUserPaths, cUsers * sizeof(REDIRECTED_USER_PATH));
  316. WCHAR wszSubKey[MAX_PATH] = L"";
  317. DWORD cSubKey = 0;
  318. HKEY hkUserSID;
  319. LONG lRes;
  320. // The number of users we care about.
  321. DWORD cLUAUsers = 0;
  322. DWORD dwIndex = 0;
  323. while (TRUE)
  324. {
  325. cSubKey = MAX_PATH;
  326. lRes = RegEnumKeyExW(hkProfileList, dwIndex, wszSubKey, &cSubKey,
  327. NULL, NULL, NULL, NULL);
  328. if (lRes == ERROR_SUCCESS)
  329. {
  330. WCHAR wszUserDirectory[MAX_PATH] = L"";
  331. if ((lRes = GetProfilePath(hkProfileList, wszSubKey, wszUserDirectory))
  332. == ERROR_SUCCESS)
  333. {
  334. //
  335. // If the directory doesn't exist, it means either the user
  336. // never logged on, or there are no redirected files for that
  337. // user. We simply skip it.
  338. //
  339. if (IsDirectory(wszUserDirectory))
  340. {
  341. DWORD cPath = wcslen(wszUserDirectory) + 1;
  342. LPWSTR pwszPath = new WCHAR [cPath];
  343. if (pwszPath)
  344. {
  345. wcscpy(pwszPath, wszUserDirectory);
  346. pRedirectUserPaths[cLUAUsers].pwszPath = pwszPath;
  347. pRedirectUserPaths[cLUAUsers].cLen = cPath;
  348. ++cLUAUsers;
  349. }
  350. else
  351. {
  352. DPF("LUAUtils", eDbgLevelError,
  353. "[GetUsersFS] Error allocating memory");
  354. lRes = ERROR_NOT_ENOUGH_MEMORY;
  355. goto EXIT;
  356. }
  357. }
  358. }
  359. }
  360. else if (lRes == ERROR_NO_MORE_ITEMS)
  361. {
  362. *pcUsers = cLUAUsers;
  363. lRes = ERROR_SUCCESS;
  364. goto EXIT;
  365. }
  366. else
  367. {
  368. break;
  369. }
  370. ++dwIndex;
  371. }
  372. EXIT:
  373. RegCloseKey(hkProfileList);
  374. if (lRes == ERROR_SUCCESS)
  375. {
  376. return TRUE;
  377. }
  378. FreeUserDirectoryArray(pRedirectUserPaths);
  379. return FALSE;
  380. }
  381. VOID
  382. FreeUsersFS(
  383. REDIRECTED_USER_PATH* pRedirectUserPaths
  384. )
  385. {
  386. FreeUserDirectoryArray(pRedirectUserPaths);
  387. }
  388. LONG
  389. LoadHive(
  390. LPCWSTR pwszHiveName,
  391. LPCWSTR pwszHiveFile,
  392. HKEY* phKey
  393. )
  394. {
  395. LONG lRes;
  396. // If the hive is already loaded, we'll get a sharing violation so
  397. // check that as well.
  398. if ((lRes = RegLoadKeyW(HKEY_USERS, pwszHiveName, pwszHiveFile))
  399. == ERROR_SUCCESS || lRes == ERROR_SHARING_VIOLATION)
  400. {
  401. if (lRes == ERROR_SUCCESS)
  402. {
  403. DWORD cLen = wcslen(pwszHiveName) + 1;
  404. g_wszLoadedKeys[g_cLoadedKeys] = new WCHAR [cLen];
  405. if (!(g_wszLoadedKeys[g_cLoadedKeys]))
  406. {
  407. DPF("LUAUtils", eDbgLevelError,
  408. "[LoadHive] Error allocating %d WCHARs",
  409. cLen);
  410. return ERROR_NOT_ENOUGH_MEMORY;
  411. }
  412. // Store the hive name so later on we can unload this hive.
  413. wcscpy(g_wszLoadedKeys[g_cLoadedKeys++], pwszHiveName);
  414. }
  415. lRes = RegOpenKeyExW(
  416. HKEY_USERS,
  417. pwszHiveName,
  418. 0,
  419. KEY_ALL_ACCESS,
  420. phKey);
  421. }
  422. return lRes;
  423. }
  424. BOOL
  425. GetUsersReg(
  426. USER_HIVE_KEY** pphkUsers,
  427. DWORD* pcUsers
  428. )
  429. {
  430. // We have to enable the "Restore files and directories" privilege to
  431. // load each user's hive.
  432. if (!AdjustPrivilege(SE_RESTORE_NAME, TRUE))
  433. {
  434. DPF("LUAUtils", eDbgLevelError,
  435. "[GetUsersReg] Error enabling the SE_RESTORE_NAME privilege");
  436. return FALSE;
  437. }
  438. DWORD cUsers;
  439. HKEY hkProfileList;
  440. if (InitGetUsers(&cUsers, &hkProfileList) != ERROR_SUCCESS)
  441. {
  442. DPF("LUAUtils", eDbgLevelError, "[GetUsersReg] Error initializing");
  443. return FALSE;
  444. }
  445. *pphkUsers = new USER_HIVE_KEY [cUsers];
  446. if (!*pphkUsers)
  447. {
  448. DPF("LUAUtils", eDbgLevelError,
  449. "[GetUsersReg] Error allocating memory for %d USER_HIVE_KEYs",
  450. cUsers);
  451. return FALSE;
  452. }
  453. g_wszLoadedKeys = new WCHAR* [cUsers * 2];
  454. if (!g_wszLoadedKeys)
  455. {
  456. DPF("LUAUtils", eDbgLevelError,
  457. "[GetUsersReg] Error allocating memory for %d WCHARs",
  458. cUsers * 2);
  459. delete [] *pphkUsers;
  460. return FALSE;
  461. }
  462. USER_HIVE_KEY* phkUsers = *pphkUsers;
  463. ZeroMemory((PVOID)phkUsers, cUsers * sizeof(USER_HIVE_KEY));
  464. ZeroMemory((PVOID)g_wszLoadedKeys, cUsers * 2 * sizeof (WCHAR*));
  465. WCHAR wszSubKey[MAX_PATH] = L"";
  466. WCHAR wszUserHive[MAX_PATH] = L"";
  467. WCHAR wszUserClassesHive[MAX_PATH] = L"";
  468. DWORD cSubKey = 0;
  469. HKEY hkSubKey;
  470. LONG lRes;
  471. // The number of users we care about.
  472. DWORD cLUAUsers = 0;
  473. DWORD dwIndex = 0;
  474. DWORD cUserHive = 0;
  475. while (TRUE)
  476. {
  477. cSubKey = MAX_PATH;
  478. lRes = RegEnumKeyExW(hkProfileList, dwIndex, wszSubKey, &cSubKey,
  479. NULL, NULL, NULL, NULL);
  480. if (lRes == ERROR_SUCCESS)
  481. {
  482. if ((lRes = GetProfilePath(hkProfileList, wszSubKey, wszUserHive))
  483. == ERROR_SUCCESS)
  484. {
  485. //
  486. // Make sure we don't buffer overflow.
  487. //
  488. cUserHive = wcslen(wszUserHive);
  489. if ((cUserHive + USER_CLASSES_HIVE_NAME_LEN + 1) > MAX_PATH ||
  490. (cUserHive + USER_HIVE_NAME_LEN + 1) > MAX_PATH)
  491. {
  492. DPF("LUAUtils", eDbgLevelError,
  493. "[GetUsersReg] The hive key names are too long - we don't handle them");
  494. goto EXIT;
  495. }
  496. //
  497. // Construct the locations for the user hive and user classes data hive.
  498. //
  499. wcsncpy(wszUserClassesHive, wszUserHive, cUserHive);
  500. wcsncpy(
  501. wszUserClassesHive + cUserHive,
  502. USER_CLASSES_HIVE_NAME,
  503. USER_CLASSES_HIVE_NAME_LEN);
  504. wszUserClassesHive[cUserHive + USER_CLASSES_HIVE_NAME_LEN] = L'\0';
  505. wcsncpy(wszUserHive + cUserHive, USER_HIVE_NAME, USER_HIVE_NAME_LEN);
  506. wszUserHive[cUserHive + USER_HIVE_NAME_LEN] = L'\0';
  507. //
  508. // Load the HKCU for this user.
  509. //
  510. if ((lRes = LoadHive(
  511. wszSubKey,
  512. wszUserHive,
  513. &phkUsers[cLUAUsers].hkUser)) == ERROR_SUCCESS)
  514. {
  515. //
  516. // We can't necessarily load the HKCR for this user - it might
  517. // contain no data so we only attemp to load it.
  518. //
  519. if ((cSubKey + CLASSES_HIVE_SUFFIX_LEN + 1) > MAX_PATH)
  520. {
  521. DPF("LUAUtils", eDbgLevelError,
  522. "[GetUsersReg] The CR key name is too long - we don't handle it");
  523. goto EXIT;
  524. }
  525. wcsncpy(wszSubKey + cSubKey, CLASSES_HIVE_SUFFIX, CLASSES_HIVE_SUFFIX_LEN);
  526. wszSubKey[cSubKey + CLASSES_HIVE_SUFFIX_LEN] = L'\0';
  527. LoadHive(
  528. wszSubKey,
  529. wszUserClassesHive,
  530. &phkUsers[cLUAUsers].hkUserClasses);
  531. ++cLUAUsers;
  532. }
  533. }
  534. }
  535. else if (lRes == ERROR_NO_MORE_ITEMS)
  536. {
  537. *pcUsers = cLUAUsers;
  538. lRes = ERROR_SUCCESS;
  539. goto EXIT;
  540. }
  541. else
  542. {
  543. break;
  544. }
  545. ++dwIndex;
  546. }
  547. EXIT:
  548. RegCloseKey(hkProfileList);
  549. if (lRes == ERROR_SUCCESS)
  550. {
  551. return TRUE;
  552. }
  553. FreeUsersReg(phkUsers, cUsers);
  554. return FALSE;
  555. }
  556. VOID
  557. FreeUsersReg(
  558. USER_HIVE_KEY* phkUsers,
  559. DWORD cUsers
  560. )
  561. {
  562. DWORD dw;
  563. // Close all the open keys.
  564. for (dw = 0; dw < cUsers; ++dw)
  565. {
  566. RegCloseKey(phkUsers[dw].hkUser);
  567. RegCloseKey(phkUsers[dw].hkUserClasses);
  568. }
  569. delete [] phkUsers;
  570. for (dw = 0; dw < g_cLoadedKeys; ++dw)
  571. {
  572. // Unloaded the keys we had to load under HKEY_USERS.
  573. RegUnLoadKey(HKEY_USERS, g_wszLoadedKeys[dw]);
  574. delete [] g_wszLoadedKeys[dw];
  575. }
  576. delete [] g_wszLoadedKeys;
  577. // Disable the "Restore files and directories" privilege.
  578. AdjustPrivilege(SE_RESTORE_NAME, FALSE);
  579. }
  580. //
  581. // Registry utilies.
  582. //
  583. HKEY g_hkRedirectRoot = NULL;
  584. HKEY g_hkCurrentUserClasses = NULL;
  585. /*++
  586. Function Description:
  587. We only return TRUE if it's one of the predefined keys we are interested in.
  588. We don't redirect the HKEY_USERS and HKEY_PERFORMANCE_DATA keys.
  589. Arguments:
  590. IN hKey - the key handle.
  591. IN lpSubKey - subkey to check.
  592. Return Value:
  593. TRUE - It's one of our predefined keys.
  594. FALSE - It's either a non-predefined key or a predefined key that we are not
  595. interested in.
  596. History:
  597. 03/27/2001 maonis Created
  598. --*/
  599. BOOL
  600. IsPredefinedKey(
  601. IN HKEY hKey
  602. )
  603. {
  604. return (
  605. hKey == HKEY_CLASSES_ROOT ||
  606. hKey == HKEY_CURRENT_USER ||
  607. hKey == HKEY_LOCAL_MACHINE);
  608. }
  609. LONG
  610. GetRegRedirectKeys()
  611. {
  612. LONG lRet;
  613. if (lRet = RegCreateKeyExW(
  614. HKEY_CURRENT_USER,
  615. LUA_REG_REDIRECT_KEY,
  616. 0,
  617. 0,
  618. REG_OPTION_NON_VOLATILE,
  619. KEY_ALL_ACCESS,
  620. NULL,
  621. &g_hkRedirectRoot,
  622. NULL) == ERROR_SUCCESS)
  623. {
  624. lRet = RegCreateKeyExW(
  625. HKEY_CURRENT_USER,
  626. LUA_SOFTWARE_CLASSES,
  627. 0,
  628. 0,
  629. REG_OPTION_NON_VOLATILE,
  630. KEY_ALL_ACCESS,
  631. NULL,
  632. &g_hkCurrentUserClasses,
  633. NULL);
  634. }
  635. return lRet;
  636. }
  637. #define IS_END_OF_COMPONENT(x) (*x == L'\\' || *x == L'\0')
  638. /*++
  639. Function Description:
  640. Determines if 2 components match - one with wildcards and the other
  641. without.
  642. Note: this function is specialized for the LUA shims - the pattern
  643. is all lowercase. If the components match, we advance the string
  644. to the end of the component so when we do the whole path/file name
  645. matching we don't need to go through the string twice.
  646. Arguments:
  647. IN ppPattern - component with wildcards.
  648. IN ppString - component without wildcards.
  649. Return Value:
  650. TRUE - the components match.
  651. FALSE - the components don't match.
  652. History:
  653. 05/10/2001 maonis Created
  654. --*/
  655. BOOL
  656. DoComponentsMatch(
  657. LPCWSTR* ppwszPattern,
  658. LPCWSTR* ppwszString)
  659. {
  660. LPCWSTR pwszPattern = *ppwszPattern;
  661. LPCWSTR pwszString = *ppwszString;
  662. LPCWSTR pwszSearch = NULL;
  663. LPCWSTR pwszSearchPattern = NULL;
  664. BOOL fIsSuccess = TRUE;
  665. do
  666. {
  667. if (*pwszPattern == L'*')
  668. {
  669. while (*++pwszPattern == L'*');
  670. if (IS_END_OF_COMPONENT(pwszPattern))
  671. {
  672. // Advanced the string to the end.
  673. while (!IS_END_OF_COMPONENT(pwszString))
  674. {
  675. ++pwszString;
  676. }
  677. goto EXIT;
  678. }
  679. pwszSearch = pwszString;
  680. pwszSearchPattern = pwszPattern;
  681. }
  682. if (IS_END_OF_COMPONENT(pwszString))
  683. {
  684. break;
  685. }
  686. if ((*pwszPattern == L'?') ||
  687. (*pwszPattern == *pwszString))
  688. {
  689. pwszPattern++;
  690. }
  691. else if (pwszSearch == NULL)
  692. {
  693. return FALSE;
  694. }
  695. else
  696. {
  697. pwszString = pwszSearch++;
  698. pwszPattern = pwszSearchPattern;
  699. }
  700. ++pwszString;
  701. } while (!IS_END_OF_COMPONENT(pwszString));
  702. if (*pwszPattern == L'*')
  703. {
  704. fIsSuccess = TRUE;
  705. ++pwszPattern;
  706. }
  707. else
  708. {
  709. fIsSuccess = IS_END_OF_COMPONENT(pwszPattern);
  710. }
  711. EXIT:
  712. *ppwszPattern = pwszPattern;
  713. *ppwszString = pwszString;
  714. return fIsSuccess;
  715. }
  716. /*++
  717. Function Description:
  718. Determines if the item is in the redirect list.
  719. Arguments:
  720. IN pwszDirectory - All lowercase name.
  721. IN cDirectory - The length of the directory.
  722. IN pwszFile - The file name.
  723. Return Value:
  724. TRUE - the names match.
  725. FALSE - the names don't match.
  726. History:
  727. 11/30/2001 maonis Created
  728. --*/
  729. BOOL
  730. DoesItemMatchRedirect(
  731. LPCWSTR pwszItem,
  732. const RITEM* pItem,
  733. BOOL fIsDirectory
  734. )
  735. {
  736. LPCWSTR pwszName = &(pItem->wszName[0]);
  737. BOOL fMatchComponents;
  738. if (pItem->fHasWC)
  739. {
  740. while (*pwszItem && *pwszName)
  741. {
  742. if (!DoComponentsMatch(&pwszName, &pwszItem))
  743. {
  744. return FALSE;
  745. }
  746. if (fIsDirectory)
  747. {
  748. if (!*pwszName)
  749. {
  750. //
  751. // directory has exhausted. It's a match.
  752. //
  753. return TRUE;
  754. }
  755. if (!*pwszItem)
  756. {
  757. //
  758. // directory hasn't exhausted but item has, no match.
  759. //
  760. return FALSE;
  761. }
  762. }
  763. else
  764. {
  765. if (!*pwszItem)
  766. {
  767. //
  768. // item has exhausted. It's a match.
  769. //
  770. return TRUE;
  771. }
  772. if (!*pwszName)
  773. {
  774. //
  775. // item hasn't exhausted but file has, no match.
  776. //
  777. return FALSE;
  778. }
  779. }
  780. ++pwszName;
  781. ++pwszItem;
  782. }
  783. if (fIsDirectory)
  784. {
  785. return (!*pwszName);
  786. }
  787. else
  788. {
  789. return (!*pwszItem);
  790. }
  791. }
  792. else
  793. {
  794. while (*pwszItem && *pwszName && *pwszItem == *pwszName)
  795. {
  796. ++pwszItem;
  797. ++pwszName;
  798. }
  799. if (fIsDirectory)
  800. {
  801. return (!*pwszName && (!*pwszItem || *pwszItem == L'\\'));
  802. }
  803. else
  804. {
  805. return (!*pwszItem && (!*pwszName || *pwszName == L'\\'));
  806. }
  807. }
  808. }
  809. /*++
  810. Function Description:
  811. Parse the commandline argument for the LUA shims using ' ' as the delimiter.
  812. If a token has spaces, use double quotes around it. Use this function the
  813. same way you use wcstok except you don't have to specify the delimiter.
  814. Arguments:
  815. IN/OUT pwsz - the string to parse.
  816. Return Value:
  817. pointer to the next token.
  818. History:
  819. 05/17/2001 maonis Created
  820. --*/
  821. LPWSTR GetNextToken(
  822. LPWSTR pwsz
  823. )
  824. {
  825. static LPWSTR pwszToken;
  826. static LPWSTR pwszEndOfLastToken;
  827. if (!pwsz)
  828. {
  829. pwsz = pwszEndOfLastToken;
  830. }
  831. // Skip the white space.
  832. while (*pwsz && *pwsz == ' ')
  833. {
  834. ++pwsz;
  835. }
  836. pwszToken = pwsz;
  837. BOOL fInsideQuotes = 0;
  838. while (*pwsz)
  839. {
  840. switch(*pwsz)
  841. {
  842. case L'"':
  843. fInsideQuotes ^= 1;
  844. if (fInsideQuotes)
  845. {
  846. ++pwszToken;
  847. }
  848. case L' ':
  849. if (!fInsideQuotes)
  850. {
  851. goto EXIT;
  852. }
  853. default:
  854. ++pwsz;
  855. }
  856. }
  857. EXIT:
  858. if (*pwsz)
  859. {
  860. *pwsz = L'\0';
  861. pwszEndOfLastToken = ++pwsz;
  862. }
  863. else
  864. {
  865. pwszEndOfLastToken = pwsz;
  866. }
  867. return pwszToken;
  868. }
  869. /*++
  870. Function Description:
  871. Starting from the end going backward and find the first non whitespace
  872. char. Set the whitespace char after it to '\0'.
  873. Arguments:
  874. IN pwsz - Beginning pointer.
  875. Return Value:
  876. None.
  877. History:
  878. 06/27/2001 maonis Created
  879. --*/
  880. VOID TrimTrailingSpaces(
  881. LPWSTR pwsz
  882. )
  883. {
  884. if (pwsz)
  885. {
  886. DWORD cLen = wcslen(pwsz);
  887. LPWSTR pwszEnd = pwsz + cLen - 1;
  888. while (pwszEnd >= pwsz && (*pwszEnd == L' ' || *pwszEnd == L'\t'))
  889. {
  890. --pwszEnd;
  891. }
  892. *(++pwszEnd) = L'\0';
  893. }
  894. }
  895. /*++
  896. Function Description:
  897. If the directory doesn't exist, we create it.
  898. Arguments:
  899. IN pwszDir - The name of the directory to create. The directory should NOT
  900. start with \\?\ and it should haved a trailing slash.
  901. Return Value:
  902. TRUE - the directory was created.
  903. FALSE - otherwise.
  904. History:
  905. 05/17/2001 maonis Created
  906. --*/
  907. BOOL
  908. CreateDirectoryOnDemand(
  909. LPWSTR pwszDir
  910. )
  911. {
  912. if (!pwszDir || !*pwszDir)
  913. {
  914. DPF("LUAUtils", eDbgLevelSpew,
  915. "[CreateDirectoryOnDemand] Empty directory name - nothing to do");
  916. return TRUE;
  917. }
  918. WCHAR* pwszStartPath = pwszDir;
  919. WCHAR* pwszEndPath = pwszDir + wcslen(pwszDir);
  920. WCHAR* pwszStartNext = pwszStartPath;
  921. // Find the end of the next sub dir.
  922. WCHAR* pwszEndNext;
  923. DWORD dwAttrib;
  924. while (pwszStartNext < pwszEndPath)
  925. {
  926. pwszEndNext = wcschr(pwszStartNext, L'\\');
  927. if (pwszEndNext)
  928. {
  929. *pwszEndNext = L'\0';
  930. if ((dwAttrib = GetFileAttributesW(pwszStartPath)) != -1)
  931. {
  932. // If the directory already exists, we probe its sub directory.
  933. *pwszEndNext = L'\\';
  934. pwszStartNext = pwszEndNext + 1;
  935. continue;
  936. }
  937. if (!CreateDirectoryW(pwszStartPath, NULL))
  938. {
  939. DPF("LUAUtils", eDbgLevelError,
  940. "[CreateDirectoryOnDemand] CreateDirectory %S failed: %d",
  941. pwszStartPath,
  942. GetLastError());
  943. return FALSE;
  944. }
  945. *pwszEndNext = L'\\';
  946. pwszStartNext = pwszEndNext + 1;
  947. }
  948. else
  949. {
  950. DPF("LUAUtils", eDbgLevelError,
  951. "[CreateDirectoryOnDemand] Invalid directory name: %S", pwszStartPath);
  952. return FALSE;
  953. }
  954. }
  955. return TRUE;
  956. }
  957. /*++
  958. Function Description:
  959. Expand a string which might have enviorment variables embedded in it.
  960. It gives you options to
  961. 1) Add a trailing slash if there's not one;
  962. 2) Create the directory if it doesn't exist;
  963. 3) Add the \\?\ prefix;
  964. NOTE: The caller is responsible of free the memory using delete [].
  965. Arguments:
  966. IN pwszItem - string to expand.
  967. OUT pcItemExpand - number of characters in the resulting string.
  968. NOTE: this *includes* the terminating NULL.
  969. IN fEnsureTrailingSlash - option 1.
  970. IN fCreateDirectory - option 2.
  971. IN fAddPrefix - option 3.
  972. Return Value:
  973. The expanded string or NULL if error occured.
  974. History:
  975. 05/17/2001 maonis Created
  976. --*/
  977. LPWSTR
  978. ExpandItem(
  979. LPCWSTR pwszItem,
  980. DWORD* pcItemExpand,
  981. BOOL fEnsureTrailingSlash,
  982. BOOL fCreateDirectory,
  983. BOOL fAddPrefix
  984. )
  985. {
  986. BOOL fIsSuccess = FALSE;
  987. //
  988. // Get the required length.
  989. //
  990. DWORD cLenExpand = ExpandEnvironmentStringsW(pwszItem, NULL, 0);
  991. if (!cLenExpand)
  992. {
  993. DPF("LUAUtils", eDbgLevelError,
  994. "[ExpandItem] Failed to get the required buffer size "
  995. "when expanding %S: %d",
  996. pwszItem, GetLastError());
  997. return NULL;
  998. }
  999. if (fEnsureTrailingSlash)
  1000. {
  1001. ++cLenExpand;
  1002. }
  1003. if (fAddPrefix)
  1004. {
  1005. cLenExpand += FILE_NAME_PREFIX_LEN;
  1006. }
  1007. LPWSTR pwszItemExpand = new WCHAR [cLenExpand];
  1008. if (!pwszItemExpand)
  1009. {
  1010. DPF("LUAUtils", eDbgLevelError,
  1011. "[ExpandItem] Error allocating %d WCHARs", cLenExpand);
  1012. return NULL;
  1013. }
  1014. LPWSTR pwszTemp = pwszItemExpand;
  1015. DWORD cTemp = cLenExpand;
  1016. if (fAddPrefix)
  1017. {
  1018. wcscpy(pwszItemExpand, FILE_NAME_PREFIX);
  1019. pwszTemp += FILE_NAME_PREFIX_LEN;
  1020. cTemp -= FILE_NAME_PREFIX_LEN;
  1021. }
  1022. if (!ExpandEnvironmentStringsW(pwszItem, pwszTemp, cTemp))
  1023. {
  1024. DPF("LUAUtils", eDbgLevelError,
  1025. "[ExpandItem] Failed to expand %S: %d",
  1026. pwszItem, GetLastError());
  1027. goto Cleanup;
  1028. }
  1029. // Ensure the trailing slash.
  1030. if (fEnsureTrailingSlash)
  1031. {
  1032. if (pwszItemExpand[cLenExpand - 3] != L'\\')
  1033. {
  1034. pwszItemExpand[cLenExpand - 2] = L'\\';
  1035. pwszItemExpand[cLenExpand - 1] = L'\0';
  1036. }
  1037. else
  1038. {
  1039. --cLenExpand;
  1040. }
  1041. if (fCreateDirectory &&
  1042. !CreateDirectoryOnDemand(pwszItemExpand + (fAddPrefix ? 4 : 0)))
  1043. {
  1044. DPF("LUAUtils", eDbgLevelError,
  1045. "[ExpandItem] Failed to create %S",
  1046. pwszItemExpand);
  1047. goto Cleanup;
  1048. }
  1049. }
  1050. *pcItemExpand = cLenExpand;
  1051. fIsSuccess = TRUE;
  1052. Cleanup:
  1053. if (!fIsSuccess)
  1054. {
  1055. delete [] pwszItemExpand;
  1056. pwszItemExpand = NULL;
  1057. }
  1058. return pwszItemExpand;
  1059. }
  1060. /*++
  1061. Function Description:
  1062. Given a delimiter character, returns the number of items in the string.
  1063. Return Value:
  1064. Number of items in the string.
  1065. History:
  1066. 11/13/2001 maonis Created
  1067. --*/
  1068. DWORD
  1069. GetItemsCount(
  1070. LPCWSTR pwsz,
  1071. WCHAR chDelimiter
  1072. )
  1073. {
  1074. DWORD cItems = 0;
  1075. while (*pwsz) {
  1076. if (*pwsz == chDelimiter) {
  1077. ++cItems;
  1078. }
  1079. ++pwsz;
  1080. }
  1081. return (cItems + 1);
  1082. }