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.

3535 lines
91 KiB

  1. /*--
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. sdbinst.cpp
  5. Abstract:
  6. installs custom SDB files into AppPatch\Custom, and adds registry entries to point
  7. to them
  8. Author:
  9. dmunsil 12/29/2000
  10. Revision History:
  11. Many people contributed over time.
  12. (in alphabetical order: clupu, dmunsil, rparsons, vadimb)
  13. Notes:
  14. --*/
  15. #define _UNICODE
  16. #define WIN
  17. #define FLAT_32
  18. #define TRUE_IF_WIN32 1
  19. #include <nt.h>
  20. #include <ntrtl.h>
  21. #include <nturtl.h>
  22. #define _WINDOWS
  23. #include <windows.h>
  24. #include <shellapi.h>
  25. #include <stdio.h>
  26. #include <stddef.h>
  27. #include <stdlib.h>
  28. #include <assert.h>
  29. #include <tchar.h>
  30. #include <aclapi.h>
  31. #include "resource.h"
  32. extern "C" {
  33. #include "shimdb.h"
  34. }
  35. BOOL g_bQuiet;
  36. BOOL g_bWin2K;
  37. WCHAR g_wszCustom[MAX_PATH];
  38. BOOL g_bAllowPatches = FALSE;
  39. HINSTANCE g_hInst;
  40. HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
  41. typedef enum _INSTALL_MODE {
  42. MODE_INSTALL,
  43. MODE_UNINSTALL,
  44. MODE_CLEANUP,
  45. MODE_CONVERT_FORMAT_NEW,
  46. MODE_CONVERT_FORMAT_OLD
  47. } INSTALL_MODE;
  48. #define UNINSTALL_KEY_PATH L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"
  49. #define APPCOMPAT_KEY L"System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility"
  50. DWORD g_dwWow64Key = (DWORD)-1;
  51. void
  52. __cdecl
  53. vPrintError(
  54. UINT unRes,
  55. ...
  56. )
  57. {
  58. WCHAR szT[1024];
  59. WCHAR wszFormat[1024];
  60. WCHAR wszCaption[1024];
  61. va_list arglist;
  62. if (!g_bQuiet) {
  63. if (!LoadStringW(g_hInst, IDS_APP_ERROR_TITLE, wszCaption, 1024)) {
  64. return;
  65. }
  66. if (LoadStringW(g_hInst, unRes, wszFormat, 1024)) {
  67. va_start(arglist, unRes);
  68. _vsnwprintf(szT, 1023, wszFormat, arglist);
  69. szT[1022] = 0;
  70. va_end(arglist);
  71. MessageBoxW(NULL, szT, wszCaption, MB_OK | MB_ICONWARNING);
  72. }
  73. }
  74. }
  75. void
  76. __cdecl
  77. vPrintMessage(
  78. UINT unRes,
  79. ...
  80. )
  81. {
  82. WCHAR szT[1024];
  83. WCHAR wszFormat[1024];
  84. WCHAR wszCaption[1024];
  85. va_list arglist;
  86. if (!g_bQuiet) {
  87. if (!LoadStringW(g_hInst, IDS_APP_TITLE, wszCaption, 1024)) {
  88. return;
  89. }
  90. if (LoadStringW(g_hInst, unRes, wszFormat, 1024)) {
  91. va_start(arglist, unRes);
  92. _vsnwprintf(szT, 1023, wszFormat, arglist);
  93. szT[1022] = 0;
  94. va_end(arglist);
  95. MessageBoxW(NULL, szT, wszCaption, MB_OK | MB_ICONINFORMATION);
  96. }
  97. }
  98. }
  99. void
  100. __cdecl
  101. vLogMessage(
  102. LPCSTR pwszFormat,
  103. ...
  104. )
  105. {
  106. CHAR szT[1024];
  107. va_list arglist;
  108. int nLength;
  109. va_start(arglist, pwszFormat);
  110. nLength = _vsnprintf(szT, CHARCOUNT(szT), pwszFormat, arglist);
  111. if (nLength < 0) {
  112. szT[1023] = '\0';
  113. nLength = sizeof(szT);
  114. } else {
  115. nLength *= sizeof(szT[0]);
  116. }
  117. va_end(arglist);
  118. if (g_hLogFile != INVALID_HANDLE_VALUE) {
  119. DWORD dwWritten;
  120. WriteFile(g_hLogFile, (LPVOID)szT, (DWORD)nLength, &dwWritten, NULL);
  121. }
  122. OutputDebugStringA(szT);
  123. }
  124. DWORD
  125. GetWow64Flag(
  126. void
  127. )
  128. {
  129. if (g_dwWow64Key == (DWORD)-1) {
  130. if (g_bWin2K) {
  131. g_dwWow64Key = 0; // no flag since there is no wow64 on win2k
  132. } else {
  133. g_dwWow64Key = KEY_WOW64_64KEY;
  134. }
  135. }
  136. return g_dwWow64Key;
  137. }
  138. VOID
  139. OpenLogFile(
  140. VOID
  141. )
  142. {
  143. WCHAR wszLogFile[MAX_PATH];
  144. CHAR szBuffer[1024];
  145. GetSystemWindowsDirectoryW(wszLogFile, CHARCOUNT(wszLogFile));
  146. wcscat(wszLogFile, L"\\AppPatch\\SdbInst.Log");
  147. g_hLogFile = CreateFileW(wszLogFile,
  148. GENERIC_WRITE,
  149. FILE_SHARE_READ,
  150. NULL,
  151. CREATE_ALWAYS,
  152. FILE_ATTRIBUTE_NORMAL,
  153. NULL);
  154. }
  155. VOID
  156. CloseLogFile(
  157. VOID
  158. )
  159. {
  160. if (g_hLogFile != INVALID_HANDLE_VALUE) {
  161. CloseHandle(g_hLogFile);
  162. }
  163. g_hLogFile = INVALID_HANDLE_VALUE;
  164. }
  165. void
  166. vPrintHelp(
  167. WCHAR* szAppName
  168. )
  169. {
  170. vPrintMessage(IDS_HELP_TEXT, szAppName);
  171. }
  172. typedef void (CALLBACK *pfn_ShimFlushCache)(HWND, HINSTANCE, LPSTR, int);
  173. void
  174. vFlushCache(
  175. void
  176. )
  177. {
  178. HMODULE hAppHelp;
  179. pfn_ShimFlushCache pShimFlushCache;
  180. hAppHelp = LoadLibraryW(L"apphelp.dll");
  181. if (hAppHelp) {
  182. pShimFlushCache = (pfn_ShimFlushCache)GetProcAddress(hAppHelp, "ShimFlushCache");
  183. if (pShimFlushCache) {
  184. pShimFlushCache(NULL, NULL, NULL, 0);
  185. }
  186. }
  187. }
  188. BOOL
  189. bSearchGroupForSID(
  190. DWORD dwGroup,
  191. BOOL* pfIsMember
  192. )
  193. {
  194. PSID pSID = NULL;
  195. SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
  196. BOOL fRes = TRUE;
  197. if (!AllocateAndInitializeSid(&SIDAuth,
  198. 2,
  199. SECURITY_BUILTIN_DOMAIN_RID,
  200. dwGroup,
  201. 0,
  202. 0,
  203. 0,
  204. 0,
  205. 0,
  206. 0,
  207. &pSID)) {
  208. return FALSE;
  209. }
  210. if (!pSID) {
  211. return FALSE;
  212. }
  213. if (!CheckTokenMembership(NULL, pSID, pfIsMember)) {
  214. fRes = FALSE;
  215. }
  216. FreeSid(pSID);
  217. return fRes;
  218. }
  219. BOOL
  220. bCanRun(
  221. void
  222. )
  223. {
  224. BOOL fIsAdmin;
  225. if (!bSearchGroupForSID(DOMAIN_ALIAS_RID_ADMINS, &fIsAdmin))
  226. {
  227. return FALSE;
  228. }
  229. return fIsAdmin;
  230. }
  231. WCHAR*
  232. wszGetFileFromPath(
  233. WCHAR* wszPath
  234. )
  235. {
  236. WCHAR* szTemp = wcsrchr(wszPath, L'\\');
  237. if (szTemp) {
  238. return szTemp + 1;
  239. }
  240. return NULL;
  241. }
  242. BOOL
  243. bIsAlreadyInstalled(
  244. WCHAR* wszPath
  245. )
  246. {
  247. DWORD dwCustomLen;
  248. DWORD dwInputLen;
  249. DWORD dwPos;
  250. dwCustomLen = wcslen(g_wszCustom);
  251. dwInputLen = wcslen(wszPath);
  252. if (_wcsnicmp(wszPath, g_wszCustom, dwCustomLen) != 0) {
  253. //
  254. // it's not in the custom directory
  255. //
  256. return FALSE;
  257. }
  258. for (dwPos = dwCustomLen; dwPos < dwInputLen; ++dwPos) {
  259. if (wszPath[dwPos] == L'\\') {
  260. //
  261. // it's in a subdirectory of Custom,
  262. //
  263. return FALSE;
  264. }
  265. }
  266. return TRUE;
  267. }
  268. BOOL
  269. bGuidToPath(
  270. GUID* pGuid,
  271. WCHAR* wszPath
  272. )
  273. {
  274. UNICODE_STRING ustrGuid;
  275. if (!NT_SUCCESS(RtlStringFromGUID(*pGuid, &ustrGuid))) {
  276. return FALSE;
  277. }
  278. wcscpy(wszPath, g_wszCustom);
  279. wcscat(wszPath, ustrGuid.Buffer);
  280. wcscat(wszPath, L".sdb");
  281. RtlFreeUnicodeString(&ustrGuid);
  282. return TRUE;
  283. }
  284. BOOL
  285. bGetGuid(
  286. WCHAR* wszSDB,
  287. GUID* pGuid
  288. )
  289. {
  290. PDB pdb = NULL;
  291. TAGID tiDatabase;
  292. TAGID tiID;
  293. BOOL bRet = FALSE;
  294. pdb = SdbOpenDatabase(wszSDB, DOS_PATH);
  295. if (!pdb) {
  296. vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSDB);
  297. bRet = FALSE;
  298. goto out;
  299. }
  300. tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
  301. if (!tiDatabase) {
  302. vPrintError(IDS_NO_DB_TAG, wszSDB);
  303. bRet = FALSE;
  304. goto out;
  305. }
  306. ZeroMemory(pGuid, sizeof(GUID));
  307. tiID = SdbFindFirstTag(pdb, tiDatabase, TAG_DATABASE_ID);
  308. if (tiID) {
  309. if (SdbReadBinaryTag(pdb, tiID, (PBYTE)pGuid, sizeof(GUID))) {
  310. bRet = TRUE;
  311. }
  312. }
  313. if (!bRet) {
  314. vPrintError(IDS_NO_DB_ID, wszSDB);
  315. }
  316. out:
  317. if (pdb) {
  318. SdbCloseDatabase(pdb);
  319. pdb = NULL;
  320. }
  321. return bRet;
  322. }
  323. typedef enum _TIME_COMPARE {
  324. FILE_NEWER,
  325. FILE_SAME,
  326. FILE_OLDER
  327. } TIME_COMPARE;
  328. BOOL
  329. bOldSdbInstalled(
  330. WCHAR* wszPath,
  331. WCHAR* wszOldPath
  332. )
  333. {
  334. WIN32_FIND_DATAW FindData;
  335. GUID guidMain;
  336. BOOL bRet = FALSE;
  337. HANDLE hFind;
  338. //
  339. // get the guid from the DB we're installing
  340. //
  341. if (!bGetGuid(wszPath, &guidMain)) {
  342. //
  343. // there's no info in this DB, so no way to tell.
  344. //
  345. return FALSE;
  346. }
  347. //
  348. // get the path to the current file
  349. //
  350. if (!bGuidToPath(&guidMain, wszOldPath)) {
  351. //
  352. // couldn't convert to path
  353. //
  354. return FALSE;
  355. }
  356. //
  357. // check to see if the file exists
  358. //
  359. hFind = FindFirstFileW(wszOldPath, &FindData);
  360. if (hFind != INVALID_HANDLE_VALUE) {
  361. //
  362. // yup
  363. //
  364. bRet = TRUE;
  365. FindClose(hFind);
  366. }
  367. return bRet;
  368. }
  369. BOOL
  370. IsKnownDatabaseGUID(
  371. GUID* pGuid
  372. )
  373. {
  374. const GUID* rgpGUID[] = {
  375. &GUID_SYSMAIN_SDB,
  376. &GUID_APPHELP_SDB,
  377. &GUID_SYSTEST_SDB,
  378. &GUID_DRVMAIN_SDB,
  379. &GUID_MSIMAIN_SDB
  380. };
  381. int i;
  382. for (i = 0; i < ARRAYSIZE(rgpGUID); ++i) {
  383. if (*rgpGUID[i] == *pGuid) {
  384. return TRUE;
  385. }
  386. }
  387. return FALSE;
  388. }
  389. BOOL
  390. DatabaseContainsPatch(
  391. WCHAR* wszSDB
  392. )
  393. {
  394. PDB pdb = NULL;
  395. TAGID tiDatabase = TAGID_NULL;
  396. TAGID tiLibrary = TAGID_NULL;
  397. TAGID tiPatch = TAGID_NULL;
  398. BOOL bRet = FALSE;
  399. pdb = SdbOpenDatabase(wszSDB, DOS_PATH);
  400. if (!pdb) {
  401. vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSDB);
  402. bRet = FALSE;
  403. goto out;
  404. }
  405. tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
  406. if (!tiDatabase) {
  407. vPrintError(IDS_NO_DB_TAG, wszSDB);
  408. bRet = FALSE;
  409. goto out;
  410. }
  411. tiLibrary = SdbFindFirstTag(pdb, tiDatabase, TAG_LIBRARY);
  412. if (!tiLibrary) {
  413. //
  414. // this isn't an error -- no library just means no patches
  415. //
  416. bRet = FALSE;
  417. goto out;
  418. }
  419. tiPatch = SdbFindFirstTag(pdb, tiLibrary, TAG_PATCH);
  420. if (tiPatch) {
  421. bRet = TRUE;
  422. } else {
  423. bRet = FALSE;
  424. }
  425. out:
  426. if (pdb) {
  427. SdbCloseDatabase(pdb);
  428. pdb = NULL;
  429. }
  430. return bRet;
  431. }
  432. BOOL
  433. bGetInternalNameAndID(
  434. WCHAR* wszSDB,
  435. WCHAR* wszInternalName,
  436. GUID* pGuid
  437. )
  438. {
  439. PDB pdb = NULL;
  440. TAGID tiDatabase;
  441. TAGID tiName;
  442. TAGID tiID;
  443. BOOL bRet = FALSE;
  444. WCHAR* wszTemp;
  445. pdb = SdbOpenDatabase(wszSDB, DOS_PATH);
  446. if (!pdb) {
  447. vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSDB);
  448. bRet = FALSE;
  449. goto out;
  450. }
  451. tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
  452. if (!tiDatabase) {
  453. vPrintError(IDS_NO_DB_TAG, wszSDB);
  454. bRet = FALSE;
  455. goto out;
  456. }
  457. tiName = SdbFindFirstTag(pdb, tiDatabase, TAG_NAME);
  458. if (tiName) {
  459. wszTemp = SdbGetStringTagPtr(pdb, tiName);
  460. }
  461. if (wszTemp) {
  462. wcscpy(wszInternalName, wszTemp);
  463. } else {
  464. wszInternalName[0] = 0;
  465. }
  466. ZeroMemory(pGuid, sizeof(GUID));
  467. tiID = SdbFindFirstTag(pdb, tiDatabase, TAG_DATABASE_ID);
  468. if (!tiID) {
  469. bRet = FALSE;
  470. goto out;
  471. }
  472. if (!SdbReadBinaryTag(pdb, tiID, (PBYTE)pGuid, sizeof(GUID))) {
  473. bRet = FALSE;
  474. goto out;
  475. }
  476. bRet = TRUE;
  477. out:
  478. if (pdb) {
  479. SdbCloseDatabase(pdb);
  480. pdb = NULL;
  481. }
  482. return bRet;
  483. }
  484. BOOL
  485. bFriendlyNameToFile(
  486. WCHAR* wszFriendlyName,
  487. WCHAR* wszFile,
  488. WCHAR* wszPath
  489. )
  490. {
  491. WCHAR wszSearchPath[MAX_PATH];
  492. WIN32_FIND_DATAW FindData;
  493. BOOL bRet = FALSE;
  494. WCHAR wszInternalTemp[256];
  495. WCHAR wszFileTemp[MAX_PATH];
  496. GUID guidTemp;
  497. HANDLE hFind;
  498. wcscpy(wszSearchPath, g_wszCustom);
  499. wcscat(wszSearchPath, L"*.sdb");
  500. hFind = FindFirstFileW(wszSearchPath, &FindData);
  501. if (hFind == INVALID_HANDLE_VALUE) {
  502. return FALSE;
  503. }
  504. while (hFind != INVALID_HANDLE_VALUE) {
  505. wcscpy(wszFileTemp, g_wszCustom);
  506. wcscat(wszFileTemp, FindData.cFileName);
  507. if (!bGetInternalNameAndID(wszFileTemp, wszInternalTemp, &guidTemp)) {
  508. goto nextFile;
  509. }
  510. if (_wcsicmp(wszInternalTemp, wszFriendlyName) == 0) {
  511. bRet = TRUE;
  512. wcscpy(wszFile, FindData.cFileName);
  513. wcscpy(wszPath, wszFileTemp);
  514. FindClose(hFind);
  515. break;
  516. }
  517. nextFile:
  518. if (!FindNextFileW(hFind, &FindData)) {
  519. FindClose(hFind);
  520. hFind = INVALID_HANDLE_VALUE;
  521. }
  522. }
  523. return bRet;
  524. }
  525. BOOL
  526. bFindInstallName(
  527. WCHAR* wszPath,
  528. WCHAR* wszInstallPath
  529. )
  530. {
  531. GUID guidMain;
  532. //
  533. // get the guid from the DB we're installing
  534. //
  535. if (!bGetGuid(wszPath, &guidMain)) {
  536. //
  537. // there's no info in this DB, so no way to tell.
  538. //
  539. return FALSE;
  540. }
  541. //
  542. // get the path to the current file
  543. //
  544. if (!bGuidToPath(&guidMain, wszInstallPath)) {
  545. //
  546. // couldn't convert to path
  547. //
  548. return FALSE;
  549. }
  550. return TRUE;
  551. }
  552. //
  553. // this function is necessary because RegDeleteKey doesn't work right with
  554. // a 32-bit app deleting 64-bit reg keys
  555. //
  556. LONG
  557. LocalRegDeleteKeyW (
  558. IN HKEY hKey,
  559. IN LPCWSTR lpSubKey
  560. )
  561. {
  562. LONG lRes;
  563. HKEY hSubKey = NULL;
  564. lRes = RegOpenKeyExW(hKey,
  565. lpSubKey,
  566. 0,
  567. KEY_ALL_ACCESS|GetWow64Flag(),
  568. &hSubKey);
  569. if (lRes != ERROR_SUCCESS) {
  570. return lRes;
  571. }
  572. lRes = NtDeleteKey(hSubKey);
  573. RegCloseKey(hSubKey);
  574. return lRes;
  575. }
  576. VOID
  577. InstallW2KData(
  578. WCHAR* pszEntryName,
  579. LPCWSTR pszGuidDB
  580. )
  581. {
  582. HKEY hKey;
  583. WCHAR wszRegPath[MAX_PATH];
  584. DWORD dwDisposition, cbData;
  585. LONG lResult = 0;
  586. BYTE data[16] = {0x0c, 0, 0, 0, 0, 0, 0, 0,
  587. 0x06, 0, 0, 0, 0, 0, 0, 0};
  588. //
  589. // This is Windows 2000 - attempt to add custom SDB specific data.
  590. //
  591. wsprintfW(wszRegPath, L"%s\\%s", APPCOMPAT_KEY, pszEntryName);
  592. lResult = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
  593. wszRegPath,
  594. 0,
  595. NULL,
  596. 0,
  597. KEY_SET_VALUE,
  598. NULL,
  599. &hKey,
  600. &dwDisposition);
  601. if (ERROR_SUCCESS != lResult) {
  602. if (ERROR_ACCESS_DENIED == lResult) {
  603. vPrintError(IDS_NEED_INSTALL_PERMISSION);
  604. return;
  605. } else {
  606. vPrintError(IDS_CANT_CREATE_REG_KEY, pszEntryName);
  607. return;
  608. }
  609. }
  610. //
  611. // Set the registry values.
  612. //
  613. lResult = RegSetValueExW(hKey,
  614. pszGuidDB,
  615. 0,
  616. REG_BINARY,
  617. data,
  618. sizeof(data));
  619. if (ERROR_SUCCESS != lResult) {
  620. RegCloseKey(hKey);
  621. if (ERROR_ACCESS_DENIED == lResult) {
  622. vPrintError(IDS_NEED_INSTALL_PERMISSION);
  623. } else {
  624. //
  625. // djm - can't use new strings for XP SP1
  626. //
  627. //vPrintError(IDS_CANT_SET_REG_VALUE, pszEntryName);
  628. vPrintError(IDS_CANT_CREATE_VALUE, pszEntryName);
  629. }
  630. return;
  631. }
  632. data[0] = 0;
  633. wsprintfW(wszRegPath, L"DllPatch-%s", pszGuidDB);
  634. lResult = RegSetValueExW(hKey,
  635. wszRegPath,
  636. 0,
  637. REG_SZ,
  638. data,
  639. 2 * sizeof(WCHAR));
  640. if (ERROR_SUCCESS != lResult) {
  641. RegCloseKey(hKey);
  642. if (ERROR_ACCESS_DENIED == lResult) {
  643. vPrintError(IDS_NEED_INSTALL_PERMISSION);
  644. } else {
  645. //
  646. // djm - can't use new strings for XP SP1
  647. //
  648. //vPrintError(IDS_CANT_SET_REG_VALUE, pszEntryName);
  649. vPrintError(IDS_CANT_CREATE_VALUE, pszEntryName);
  650. }
  651. return;
  652. }
  653. RegCloseKey(hKey);
  654. }
  655. VOID
  656. RemoveW2KData(
  657. WCHAR* pszEntryName,
  658. LPCWSTR pszGuidDB
  659. )
  660. {
  661. HKEY hKey;
  662. WCHAR wszRegPath[MAX_PATH];
  663. LONG lResult = 0;
  664. DWORD dwValues;
  665. //
  666. // This is Windows 2000 - attempt to remove custom SDB specific data.
  667. //
  668. wsprintfW(wszRegPath, L"%s\\%s", APPCOMPAT_KEY, pszEntryName);
  669. lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  670. wszRegPath,
  671. 0,
  672. KEY_ALL_ACCESS|GetWow64Flag(),
  673. &hKey);
  674. if (ERROR_SUCCESS != lResult) {
  675. if (ERROR_ACCESS_DENIED == lResult) {
  676. vPrintError(IDS_NEED_INSTALL_PERMISSION);
  677. return;
  678. } else {
  679. vPrintError(IDS_CANT_OPEN_REG_KEY, wszRegPath);
  680. return;
  681. }
  682. }
  683. RegDeleteValueW(hKey, pszGuidDB);
  684. wsprintfW(wszRegPath, L"DllPatch-%s", pszGuidDB);
  685. RegDeleteValueW(hKey, wszRegPath);
  686. //
  687. // Figure out if we should delete the key, if there aren't any more values left
  688. //
  689. lResult = RegQueryInfoKey(hKey,
  690. NULL,
  691. NULL,
  692. NULL,
  693. NULL,
  694. NULL,
  695. NULL,
  696. &dwValues,
  697. NULL,
  698. NULL,
  699. NULL,
  700. NULL);
  701. RegCloseKey(hKey);
  702. hKey = NULL;
  703. if (dwValues != 0) {
  704. return;
  705. }
  706. lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  707. APPCOMPAT_KEY,
  708. 0,
  709. KEY_ALL_ACCESS|GetWow64Flag(),
  710. &hKey);
  711. if (ERROR_SUCCESS == lResult) {
  712. lResult = LocalRegDeleteKeyW(hKey, pszEntryName);
  713. }
  714. if (lResult != ERROR_SUCCESS) {
  715. vPrintError(IDS_CANT_DELETE_REG_KEY, pszEntryName, APPCOMPAT_KEY);
  716. }
  717. RegCloseKey(hKey);
  718. }
  719. // Caller is responsible for freeing the memory using delete [].
  720. LPWSTR
  721. ExpandItem(
  722. LPCWSTR pwszItem
  723. )
  724. {
  725. // Get the required length.
  726. DWORD cLenExpand = ExpandEnvironmentStringsW(pwszItem, NULL, 0);
  727. if (!cLenExpand)
  728. {
  729. return NULL;
  730. }
  731. //
  732. // Make room for "\\?\"
  733. //
  734. cLenExpand += 4;
  735. LPWSTR pwszItemExpand = new WCHAR [cLenExpand];
  736. if (!pwszItemExpand)
  737. {
  738. return NULL;
  739. }
  740. LPWSTR pwszTemp = pwszItemExpand;
  741. DWORD cTemp = cLenExpand;
  742. wcscpy(pwszItemExpand, L"\\\\?\\");
  743. pwszTemp += 4;
  744. cTemp -= 4;
  745. if (!ExpandEnvironmentStringsW(pwszItem, pwszTemp, cTemp))
  746. {
  747. return NULL;
  748. }
  749. return pwszItemExpand;
  750. }
  751. DWORD
  752. GiveUsersWriteAccess(
  753. LPWSTR pwszDir
  754. )
  755. {
  756. DWORD dwRes;
  757. PACL pOldDACL;
  758. PACL pNewDACL = NULL;
  759. SECURITY_DESCRIPTOR sd;
  760. PSECURITY_DESCRIPTOR pSD = &sd;
  761. GUID guidChildObjectType; // GUID of object to control creation of
  762. PSID pTrusteeSID; // trustee for new ACE
  763. EXPLICIT_ACCESS ea;
  764. SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
  765. PSID pUsersSID = NULL;
  766. if ((dwRes = GetNamedSecurityInfoW(
  767. pwszDir,
  768. SE_FILE_OBJECT,
  769. DACL_SECURITY_INFORMATION,
  770. NULL,
  771. NULL,
  772. &pOldDACL,
  773. NULL,
  774. &pSD)) != ERROR_SUCCESS) {
  775. goto Cleanup;
  776. }
  777. if(!AllocateAndInitializeSid(
  778. &SIDAuth,
  779. 2,
  780. SECURITY_BUILTIN_DOMAIN_RID,
  781. DOMAIN_ALIAS_RID_USERS,
  782. 0,
  783. 0,
  784. 0,
  785. 0,
  786. 0,
  787. 0,
  788. &pUsersSID)) {
  789. dwRes = ERROR_NOT_ENOUGH_MEMORY;
  790. goto Cleanup;
  791. }
  792. //
  793. // Initialize an EXPLICIT_ACCESS structure for the new ACE.
  794. //
  795. ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
  796. ea.grfAccessPermissions = FILE_ALL_ACCESS;
  797. ea.grfAccessMode = GRANT_ACCESS;
  798. ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  799. ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
  800. ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
  801. ea.Trustee.ptstrName = (LPTSTR) pUsersSID;
  802. //
  803. // Create a new ACL that merges the new ACE
  804. // into the existing DACL.
  805. //
  806. if ((dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL)) != ERROR_SUCCESS) {
  807. goto Cleanup;
  808. }
  809. dwRes = SetNamedSecurityInfoW(
  810. pwszDir,
  811. SE_FILE_OBJECT,
  812. DACL_SECURITY_INFORMATION,
  813. NULL,
  814. NULL,
  815. pNewDACL,
  816. NULL);
  817. Cleanup:
  818. if (pUsersSID) {
  819. FreeSid(pUsersSID);
  820. }
  821. return dwRes;
  822. }
  823. BOOL
  824. SetupLUAAllUserDir(
  825. LPCWSTR pwszAllUserDir
  826. )
  827. {
  828. BOOL bRes = FALSE;
  829. LPWSTR pwszExpandedDir = ExpandItem(pwszAllUserDir);
  830. if (!pwszExpandedDir) {
  831. //
  832. // djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
  833. //
  834. //vPrintError(IDS_CANT_EXPAND_DIR, pwszAllUserDir);
  835. vPrintError(IDS_APP_ERROR_TITLE);
  836. return FALSE;
  837. }
  838. //
  839. // Create the directory if it doesn't already exist.
  840. //
  841. DWORD dwAttributes = GetFileAttributesW(pwszExpandedDir);
  842. if (dwAttributes != -1) {
  843. if (!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  844. //
  845. // djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
  846. //
  847. //vPrintError(IDS_OBJECT_ALREADY_EXISTS, pwszExpandedDir);
  848. vPrintError(IDS_APP_ERROR_TITLE);
  849. goto Cleanup;
  850. }
  851. } else {
  852. if (!CreateDirectoryW(pwszExpandedDir, NULL)) {
  853. //
  854. // djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
  855. //
  856. //vPrintError(IDS_CANT_CREATE_DIRECTORY, pwszExpandedDir, GetLastError());
  857. vPrintError(IDS_APP_ERROR_TITLE);
  858. goto Cleanup;
  859. }
  860. }
  861. //
  862. // Give the Users group full control access (power users can already modify
  863. // files in this directory).
  864. //
  865. if (GiveUsersWriteAccess((LPWSTR)pwszExpandedDir) != ERROR_SUCCESS) {
  866. //
  867. // djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
  868. //
  869. //vPrintError(IDS_CANT_SET_ACLS, pwszExpandedDir);
  870. vPrintError(IDS_APP_ERROR_TITLE);
  871. goto Cleanup;
  872. }
  873. bRes = TRUE;
  874. Cleanup:
  875. delete [] pwszExpandedDir;
  876. return bRes;
  877. }
  878. // buffer size is in characters (unicode)
  879. BOOL
  880. InstallSdbEntry(
  881. WCHAR* szEntryName, // entry name (foo.exe or layer name)
  882. LPCWSTR pszGuidDB, // guid database id in string format
  883. PDB pdb,
  884. TAGID tiAction, // the ACTION node.
  885. ULONGLONG ullSdbTimeStamp, // representation of a timestamp
  886. BOOL bLayer // true if layer name
  887. )
  888. {
  889. LONG lRes;
  890. WCHAR szRegPath[MAX_PATH];
  891. WCHAR szDBName[MAX_PATH]; // this is used in older (win2k) versions
  892. INT nch;
  893. BOOL bReturn = FALSE;
  894. HKEY hKey = NULL;
  895. wcsncpy(szDBName, pszGuidDB, MAX_PATH);
  896. szDBName[MAX_PATH - 1] = 0;
  897. wcsncat(szDBName, L".sdb", MAX_PATH);
  898. szDBName[MAX_PATH - 1] = 0;
  899. pszGuidDB = szDBName;
  900. //
  901. // If this is Win2K, add data to the AppCompatibility key.
  902. //
  903. if (g_bWin2K) {
  904. InstallW2KData(szEntryName, pszGuidDB);
  905. }
  906. // else we have a string
  907. nch = _snwprintf(szRegPath,
  908. sizeof(szRegPath)/sizeof(szRegPath[0]),
  909. (bLayer ? L"%s\\Layers\\%s": L"%s\\%s"),
  910. APPCOMPAT_KEY_PATH_CUSTOM_W,
  911. szEntryName);
  912. if (nch < 0) {
  913. // error
  914. vPrintError(IDS_BUFFER_TOO_SMALL);
  915. goto HandleError;
  916. }
  917. lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
  918. szRegPath,
  919. 0,
  920. NULL,
  921. REG_OPTION_NON_VOLATILE,
  922. KEY_ALL_ACCESS|GetWow64Flag(),
  923. NULL,
  924. &hKey,
  925. NULL);
  926. //
  927. // on install, we want to quit if we hit an error.
  928. // BUGBUG - should we undo whatever we've already completed?
  929. //
  930. if (lRes != ERROR_SUCCESS) {
  931. vPrintError(IDS_CANT_CREATE_REG_KEY, szRegPath);
  932. goto HandleError;
  933. }
  934. lRes = RegSetValueExW(hKey,
  935. pszGuidDB,
  936. 0,
  937. REG_QWORD,
  938. (PBYTE)&ullSdbTimeStamp,
  939. sizeof(ullSdbTimeStamp));
  940. if (lRes != ERROR_SUCCESS) {
  941. vPrintError(IDS_CANT_CREATE_VALUE, szRegPath);
  942. goto HandleError;
  943. }
  944. LPWSTR szAllUserDir = NULL;
  945. if (tiAction) {
  946. //
  947. // the ACTION node in the EXE shimmed with LUA looks like this:
  948. //
  949. // <ACTION NAME="REDIRECT" TYPE="ChangeACLs">
  950. // <DATA NAME="AllUserDir" VALUETYPE="STRING"
  951. // VALUE="%ALLUSERSPROFILE%\Application Data\Fireworks 3"/>
  952. // </ACTION>
  953. //
  954. TAGID tiName, tiType, tiData, tiValue;
  955. LPWSTR szName, szType, szData;
  956. if ((tiName = SdbFindFirstTag(pdb, tiAction, TAG_NAME)) &&
  957. (szName = SdbGetStringTagPtr(pdb, tiName))) {
  958. if (!wcscmp(szName, L"REDIRECT")) {
  959. if ((tiType = SdbFindFirstTag(pdb, tiAction, TAG_ACTION_TYPE)) &&
  960. (szType = SdbGetStringTagPtr(pdb, tiType))) {
  961. if (!wcscmp(szType, L"ChangeACLs")) {
  962. if ((tiData = SdbFindFirstTag(pdb, tiAction, TAG_DATA)) &&
  963. (tiValue = SdbFindFirstTag(pdb, tiData, TAG_DATA_STRING)) &&
  964. (szAllUserDir = SdbGetStringTagPtr(pdb, tiValue))) {
  965. if (!SetupLUAAllUserDir(szAllUserDir)) {
  966. goto HandleError;
  967. }
  968. }
  969. }
  970. }
  971. }
  972. }
  973. }
  974. bReturn = TRUE;
  975. HandleError:
  976. if (hKey != NULL) {
  977. RegCloseKey(hKey);
  978. }
  979. return bReturn;
  980. }
  981. BOOL
  982. UninstallSdbEntry(
  983. WCHAR* szEntryName, // foo.exe or layer name
  984. LPCWSTR pszGuidDB, // guid (database id) in string format
  985. ULONGLONG ullSdbTimeStamp, // 1ABFCA7ADC99FC timestamp
  986. BOOL bLayer // true is layer
  987. )
  988. {
  989. LONG lRes;
  990. WCHAR szRegPath[MAX_PATH];
  991. WCHAR szDBName[MAX_PATH];
  992. INT nch;
  993. BOOL bReturn = FALSE;
  994. HKEY hKey = NULL;
  995. DWORD dwValues;
  996. WCHAR szOldInstallName[MAX_PATH];
  997. wcsncpy(szDBName, pszGuidDB, MAX_PATH);
  998. szDBName[MAX_PATH - 1] = 0;
  999. wcsncat(szDBName, L".sdb", MAX_PATH);
  1000. szDBName[MAX_PATH - 1] = 0;
  1001. pszGuidDB = szDBName;
  1002. if (g_bWin2K) {
  1003. RemoveW2KData(szEntryName, pszGuidDB);
  1004. }
  1005. nch = _snwprintf(szRegPath,
  1006. sizeof(szRegPath)/sizeof(szRegPath[0]),
  1007. (bLayer ? L"%s\\Layers\\%s": L"%s\\%s"),
  1008. APPCOMPAT_KEY_PATH_CUSTOM_W,
  1009. szEntryName);
  1010. if (nch < 0) {
  1011. // error
  1012. vPrintError(IDS_BUFFER_TOO_SMALL);
  1013. goto Out;
  1014. }
  1015. lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  1016. szRegPath,
  1017. 0,
  1018. KEY_ALL_ACCESS|GetWow64Flag(),
  1019. &hKey);
  1020. //
  1021. // if we fail to open a key on uninstall, keep going, so
  1022. // hopefully we can get as much uninstalled as possible.
  1023. //
  1024. if (lRes != ERROR_SUCCESS) {
  1025. if (lRes == ERROR_ACCESS_DENIED) {
  1026. vPrintError(IDS_NEED_UNINSTALL_PERMISSION);
  1027. goto HandleError;
  1028. } else {
  1029. //
  1030. // DO NOT report an error - this key might have been cleaned up during the
  1031. // previous path, such as when identical exe names appear in the same db
  1032. // for instance, two setup.exe's -- the first pass will clean up the key,
  1033. // second path will fail to open them right here
  1034. //
  1035. // vPrintError(IDS_CANT_OPEN_REG_KEY, szRegPath);
  1036. goto Out;
  1037. }
  1038. }
  1039. lRes = RegDeleteValueW(hKey, pszGuidDB);
  1040. if (lRes != ERROR_SUCCESS) {
  1041. if (lRes == ERROR_ACCESS_DENIED) {
  1042. vPrintError(IDS_NEED_UNINSTALL_PERMISSION);
  1043. goto HandleError; // fatal error
  1044. } else {
  1045. //
  1046. // bugbug - pszSdbInstallName
  1047. //
  1048. if (lRes == ERROR_FILE_NOT_FOUND) {
  1049. WCHAR wszOldFormat[MAX_PATH];
  1050. //
  1051. // aha, value's not there, try old format
  1052. //
  1053. wcscpy(wszOldFormat, pszGuidDB);
  1054. wcscat(wszOldFormat, L".sdb");
  1055. lRes = RegDeleteValueW(hKey, wszOldFormat);
  1056. }
  1057. if (lRes != ERROR_SUCCESS) {
  1058. vPrintError(IDS_CANT_DELETE_REG_VALUE, pszGuidDB, szRegPath);
  1059. }
  1060. }
  1061. }
  1062. //
  1063. // figure out if we should delete the key, if there aren't any more values left
  1064. //
  1065. lRes = RegQueryInfoKey(hKey,
  1066. NULL,
  1067. NULL,
  1068. NULL,
  1069. NULL,
  1070. NULL,
  1071. NULL,
  1072. &dwValues,
  1073. NULL,
  1074. NULL,
  1075. NULL,
  1076. NULL);
  1077. if (dwValues == 0) {
  1078. RegCloseKey(hKey);
  1079. hKey = NULL;
  1080. nch = _snwprintf(szRegPath,
  1081. sizeof(szRegPath)/sizeof(szRegPath[0]),
  1082. (bLayer ? L"%s\\Layers": L"%s"),
  1083. APPCOMPAT_KEY_PATH_CUSTOM_W);
  1084. if (nch < 0) {
  1085. // error
  1086. vPrintError(IDS_BUFFER_TOO_SMALL);
  1087. goto Out;
  1088. }
  1089. lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegPath, 0, KEY_WRITE|GetWow64Flag(), &hKey);
  1090. if (lRes != ERROR_SUCCESS) {
  1091. vPrintError(IDS_CANT_OPEN_REG_KEY, szRegPath);
  1092. goto Out;
  1093. }
  1094. lRes = LocalRegDeleteKeyW(hKey, szEntryName);
  1095. if (lRes != ERROR_SUCCESS) {
  1096. vPrintError(IDS_CANT_DELETE_REG_KEY, szEntryName, szRegPath);
  1097. }
  1098. }
  1099. Out:
  1100. bReturn = TRUE;
  1101. HandleError:
  1102. if (hKey != NULL) {
  1103. RegCloseKey(hKey);
  1104. }
  1105. return bReturn;
  1106. UNREFERENCED_PARAMETER(ullSdbTimeStamp);
  1107. }
  1108. /*++
  1109. GetTimeStampByGuid
  1110. This function recovers installed database timestamp.
  1111. If the value for the database timestamp (DatabaseInstallTimeStamp) does not exist -
  1112. it is created using the last write time on the key associated with a particular installed
  1113. database
  1114. [out] pTimeStamp - receives the timestamp of a database
  1115. [in] pGuidDB - guid id of a database
  1116. returns TRUE if successful
  1117. --*/
  1118. BOOL
  1119. GetTimeStampByGuid(
  1120. PULONGLONG pTimeStamp,
  1121. GUID* pGuidDB
  1122. )
  1123. {
  1124. NTSTATUS Status;
  1125. WCHAR szKeyPath[MAX_PATH];
  1126. UNICODE_STRING ustrGuid = { 0 };
  1127. LONG lResult;
  1128. HKEY hKey = NULL;
  1129. BOOL bSuccess = FALSE;
  1130. DWORD dwType;
  1131. DWORD dwBufferSize;
  1132. Status = RtlStringFromGUID(*pGuidDB, &ustrGuid);
  1133. if (!NT_SUCCESS(Status)) {
  1134. // no way to tell what's up with this guid, perhaps out of memory --
  1135. // or we're
  1136. return FALSE;
  1137. }
  1138. //
  1139. // construct the key
  1140. //
  1141. wcscpy (szKeyPath, APPCOMPAT_KEY_PATH_INSTALLEDSDB_W);
  1142. wcscat (szKeyPath, L"\\");
  1143. wcsncat(szKeyPath, ustrGuid.Buffer, ustrGuid.Length / sizeof(WCHAR));
  1144. lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  1145. szKeyPath,
  1146. 0,
  1147. KEY_READ|GetWow64Flag(),
  1148. &hKey);
  1149. //
  1150. // query for value with timestamp
  1151. //
  1152. if (lResult != ERROR_SUCCESS) {
  1153. //
  1154. // oops -- can't open the key -- error condition
  1155. //
  1156. goto cleanup;
  1157. }
  1158. dwBufferSize = sizeof(*pTimeStamp);
  1159. lResult = RegQueryValueExW(hKey,
  1160. L"DatabaseInstallTimeStamp",
  1161. NULL,
  1162. &dwType,
  1163. (LPBYTE)pTimeStamp,
  1164. &dwBufferSize);
  1165. if (lResult != ERROR_SUCCESS || dwType != REG_BINARY) {
  1166. //
  1167. // we can try exhaustive search using values at this point
  1168. // there (apparently) is no install timestamp
  1169. //
  1170. if (lResult == ERROR_FILE_NOT_FOUND) {
  1171. FILETIME ftTimeStamp;
  1172. ULARGE_INTEGER liTimeStamp;
  1173. lResult = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  1174. NULL, NULL, &ftTimeStamp);
  1175. if (lResult == STATUS_SUCCESS) {
  1176. liTimeStamp.LowPart = ftTimeStamp.dwLowDateTime;
  1177. liTimeStamp.HighPart = ftTimeStamp.dwHighDateTime;
  1178. *pTimeStamp = liTimeStamp.QuadPart;
  1179. }
  1180. } else if (lResult == ERROR_SUCCESS && dwType != REG_BINARY) {
  1181. lResult = ERROR_INVALID_DATA;
  1182. }
  1183. if (lResult != ERROR_SUCCESS) {
  1184. goto cleanup;
  1185. }
  1186. }
  1187. //
  1188. // success
  1189. //
  1190. bSuccess = TRUE;
  1191. cleanup:
  1192. if (ustrGuid.Buffer != NULL) {
  1193. RtlFreeUnicodeString(&ustrGuid);
  1194. }
  1195. if (hKey != NULL) {
  1196. RegCloseKey(hKey);
  1197. }
  1198. return bSuccess;
  1199. }
  1200. NTSTATUS
  1201. SDBAPI
  1202. FindCharInUnicodeString(
  1203. ULONG Flags,
  1204. PCUNICODE_STRING StringToSearch,
  1205. PCUNICODE_STRING CharSet,
  1206. USHORT* NonInclusivePrefixLength
  1207. )
  1208. {
  1209. LPCWSTR pch;
  1210. //
  1211. // implement only the case when we move backward
  1212. //
  1213. if (Flags != RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END) {
  1214. return STATUS_NOT_IMPLEMENTED;
  1215. }
  1216. pch = StringToSearch->Buffer + StringToSearch->Length / sizeof(WCHAR);
  1217. while (pch >= StringToSearch->Buffer) {
  1218. if (_tcschr(CharSet->Buffer, *pch)) {
  1219. //
  1220. // got the char
  1221. //
  1222. if (NonInclusivePrefixLength) {
  1223. *NonInclusivePrefixLength = (USHORT)(pch - StringToSearch->Buffer) * sizeof(WCHAR);
  1224. }
  1225. return STATUS_SUCCESS;
  1226. }
  1227. pch--;
  1228. }
  1229. //
  1230. // We haven't found it. Return failure.
  1231. //
  1232. return STATUS_NOT_FOUND;
  1233. }
  1234. //
  1235. // Database list entry
  1236. // Used to represent a particular installed database
  1237. //
  1238. typedef struct tagSDBLISTENTRY {
  1239. LIST_ENTRY ListEntry; // link list stuff
  1240. ULONGLONG ullTimeStamp; // database install timestamp
  1241. GUID guidDB; // database guid
  1242. WCHAR szTimeStamp[32]; // time stamp in string form
  1243. WCHAR szGuidDB[64]; // guid in string form
  1244. WCHAR szDatabasePath[1]; // database path - we store only the name
  1245. } SDBLISTENTRY, *PSDBLISTENTRY;
  1246. /*++
  1247. AddSdbListEntry
  1248. Adds a particular database to the list of installed sdbs (maintained internally)
  1249. parses database path to retrieve database name
  1250. [in out] pHeadList - pointer to the associated list head for the installed sdbs
  1251. [in] guidDB - database guid
  1252. [in] TimeStamp - database time stamp
  1253. [in] pszDatabasePath - final database path
  1254. returns true if success
  1255. --*/
  1256. BOOL
  1257. AddSdbListEntry(
  1258. PLIST_ENTRY pHeadList,
  1259. GUID& guidDB,
  1260. ULONGLONG& TimeStamp,
  1261. LPCWSTR pszDatabasePath
  1262. )
  1263. {
  1264. //
  1265. // out of database path, recover the database name
  1266. //
  1267. UNICODE_STRING ustrPath = { 0 };
  1268. USHORT uPrefix;
  1269. UNICODE_STRING ustrPathSep = RTL_CONSTANT_STRING(L"\\/");
  1270. NTSTATUS Status;
  1271. UNICODE_STRING ustrGUID = { 0 };
  1272. if (pszDatabasePath != NULL) {
  1273. RtlInitUnicodeString(&ustrPath, pszDatabasePath);
  1274. Status = FindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
  1275. &ustrPath,
  1276. &ustrPathSep,
  1277. &uPrefix);
  1278. if (NT_SUCCESS(Status) && (uPrefix + sizeof(WCHAR)) < ustrPath.Length) {
  1279. //
  1280. // uPrefix is number of character preceding the one we found not including it
  1281. //
  1282. ustrPath.Buffer += uPrefix / sizeof(WCHAR) + 1;
  1283. ustrPath.Length -= (uPrefix + sizeof(WCHAR));
  1284. ustrPath.MaximumLength -= (uPrefix + sizeof(WCHAR));
  1285. }
  1286. //
  1287. // at this point ustrPath has just the filename -- this is what we shall use
  1288. //
  1289. }
  1290. PBYTE Buffer = new BYTE[sizeof(SDBLISTENTRY) + ustrPath.Length];
  1291. if (Buffer == NULL) {
  1292. vLogMessage("[AddSdbListEntry] Failed to allocate 0x%lx bytes\n",
  1293. sizeof(SDBLISTENTRY) + ustrPath.Length);
  1294. return FALSE;
  1295. }
  1296. PSDBLISTENTRY pSdbEntry = (PSDBLISTENTRY)Buffer;
  1297. pSdbEntry->guidDB = guidDB;
  1298. pSdbEntry->ullTimeStamp = TimeStamp;
  1299. Status = RtlStringFromGUID(guidDB, &ustrGUID);
  1300. if (!NT_SUCCESS(Status)) {
  1301. //
  1302. // we can't convert guid to string? memory allocation failure
  1303. //
  1304. vLogMessage("[AddSdbListEntry] Failed to convert guid to string Status 0x%lx\n",
  1305. Status);
  1306. delete[] Buffer;
  1307. return FALSE;
  1308. }
  1309. RtlCopyMemory(&pSdbEntry->szGuidDB[0], &ustrGUID.Buffer[0], ustrGUID.Length);
  1310. pSdbEntry->szGuidDB[ustrGUID.Length/sizeof(WCHAR)] = L'\0';
  1311. RtlFreeUnicodeString(&ustrGUID);
  1312. wsprintfW(pSdbEntry->szTimeStamp, L"%.16I64X", TimeStamp);
  1313. RtlCopyMemory(&pSdbEntry->szDatabasePath[0], &ustrPath.Buffer[0], ustrPath.Length);
  1314. pSdbEntry->szDatabasePath[ustrPath.Length / sizeof(WCHAR)] = L'\0';
  1315. InsertHeadList(pHeadList, &pSdbEntry->ListEntry);
  1316. return TRUE;
  1317. }
  1318. //
  1319. // only pGuidDB OR pwszGuid is allowed
  1320. //
  1321. /*++
  1322. FindSdbListEntry
  1323. Finds and returns an sdb list entry given a guid (in string or binary form)
  1324. Whenever possible pwszGuid is used (if it's supplied). If pwszGuid happens to be
  1325. an arbitrary filename -- it is assumed that it's the name of an installed sdb file
  1326. as registered.
  1327. [in] pHeadList - list of the installed sdbs
  1328. [in] pwszGuid - guid or guid.sdb
  1329. [out] ppSdbListEntry - if found, this receives a pointer to sdb list entry
  1330. [in] pGuidDB - guid in binary form
  1331. returns true if matching database has been located in the list
  1332. --*/
  1333. BOOL
  1334. FindSdbListEntry(
  1335. PLIST_ENTRY pHeadList,
  1336. LPCWSTR pwszGuid, // guid, possibly with trailing '.sdb'
  1337. PSDBLISTENTRY* ppSdbListEntry,
  1338. GUID* pGuidDB // guid
  1339. )
  1340. {
  1341. UNICODE_STRING ustrDot = RTL_CONSTANT_STRING(L".");
  1342. UNICODE_STRING ustrPath;
  1343. USHORT uPrefix;
  1344. NTSTATUS Status;
  1345. PLIST_ENTRY pEntry;
  1346. PSDBLISTENTRY pSdbEntry;
  1347. GUID guidDB;
  1348. BOOL bGuidSearch = TRUE;
  1349. BOOL bFound = FALSE;
  1350. LPCWSTR pch;
  1351. if (pGuidDB == NULL) {
  1352. RtlInitUnicodeString(&ustrPath, pwszGuid);
  1353. Status = FindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
  1354. &ustrPath,
  1355. &ustrDot,
  1356. &uPrefix);
  1357. if (NT_SUCCESS(Status)) {
  1358. //
  1359. // uPrefix is number of character preceding the one we found not including it
  1360. //
  1361. ustrPath.Length = uPrefix;
  1362. }
  1363. //
  1364. // convert to guid, but check first
  1365. //
  1366. pch = pwszGuid + wcsspn(pwszGuid, L" \t");
  1367. if (*pch != L'{') { // not a guid, why convert ?
  1368. bGuidSearch = FALSE;
  1369. } else {
  1370. Status = RtlGUIDFromString(&ustrPath, &guidDB);
  1371. if (!NT_SUCCESS(Status)) {
  1372. //
  1373. // failed, so use database path instead
  1374. //
  1375. bGuidSearch = FALSE;
  1376. }
  1377. }
  1378. } else {
  1379. guidDB = *pGuidDB; // guid search only
  1380. }
  1381. pEntry = pHeadList->Flink;
  1382. while (pEntry != pHeadList && !bFound) {
  1383. //
  1384. // convert entry by subtracting the offset of the list entry
  1385. //
  1386. pSdbEntry = (PSDBLISTENTRY)((PBYTE)pEntry - OFFSETOF(SDBLISTENTRY, ListEntry));
  1387. //
  1388. // compare db guids or paths
  1389. //
  1390. if (bGuidSearch) {
  1391. bFound = RtlEqualMemory(&pSdbEntry->guidDB, &guidDB, sizeof(GUID));
  1392. } else {
  1393. bFound = !_wcsicmp(pSdbEntry->szDatabasePath, pwszGuid);
  1394. }
  1395. pEntry = pEntry->Flink;
  1396. }
  1397. //
  1398. // we have found an entry ? return it -- note that pEntry would have advanced while pSdbEntry
  1399. // still points to the entry we have found
  1400. //
  1401. if (bFound) {
  1402. *ppSdbListEntry = pSdbEntry;
  1403. }
  1404. return bFound;
  1405. }
  1406. /*++
  1407. CleanupSdbList
  1408. Performs cleanup for the installed sdb list
  1409. returns nothing
  1410. --*/
  1411. VOID
  1412. CleanupSdbList(
  1413. PLIST_ENTRY pSdbListHead
  1414. )
  1415. {
  1416. PLIST_ENTRY pEntry;
  1417. PSDBLISTENTRY pSdbEntry;
  1418. PBYTE Buffer;
  1419. pEntry = pSdbListHead->Flink;
  1420. if (pEntry == NULL) {
  1421. return;
  1422. }
  1423. while (pEntry != pSdbListHead) {
  1424. pSdbEntry = (PSDBLISTENTRY)((PBYTE)pEntry - OFFSETOF(SDBLISTENTRY, ListEntry));
  1425. pEntry = pEntry->Flink;
  1426. Buffer = (PBYTE)pSdbEntry;
  1427. delete[] Buffer;
  1428. }
  1429. }
  1430. /*++
  1431. ConvertInstalledSdbsToNewFormat
  1432. Converts installed sdbs to new format, which involves storing (or verifying) the
  1433. timestamp for each installed sdb file. This function also builds a list of sdbs
  1434. used elsewhere
  1435. [in] hKey - a key handle for hklm/..../InstalledSdb
  1436. [in out] pSdbListHead - list head for the installed sdbs
  1437. returns true if successful
  1438. --*/
  1439. BOOL
  1440. ConvertInstalledSdbsToNewFormat(
  1441. HKEY hKey, // hklm/.../InstalledSdb
  1442. PLIST_ENTRY pSdbListHead // we fill this list with our sdbs for later
  1443. )
  1444. {
  1445. DWORD dwIndex = 0;
  1446. WCHAR szSubKeyName[MAX_PATH];
  1447. PWCHAR pwszKeyName;
  1448. DWORD dwBufferSize;
  1449. FILETIME ftLastWriteTime;
  1450. HKEY hKeyEntry = NULL;
  1451. LONG lResult;
  1452. ULARGE_INTEGER liTimeStamp;
  1453. UNICODE_STRING ustrGuid;
  1454. GUID guidDB;
  1455. NTSTATUS Status;
  1456. WCHAR szDatabasePath[MAX_PATH];
  1457. PWCHAR pszDatabasePath;
  1458. DWORD dwType;
  1459. BOOL bSuccess = TRUE;
  1460. while (TRUE) {
  1461. dwBufferSize = sizeof(szSubKeyName)/sizeof(szSubKeyName[0]);
  1462. lResult = RegEnumKeyExW(hKey,
  1463. dwIndex,
  1464. szSubKeyName,
  1465. &dwBufferSize,
  1466. NULL, NULL, NULL,
  1467. &ftLastWriteTime);
  1468. ++dwIndex;
  1469. if (lResult != ERROR_SUCCESS) {
  1470. //
  1471. // done if no more keys, else some sort of error
  1472. // bugbug
  1473. //
  1474. if (lResult == ERROR_NO_MORE_ITEMS) {
  1475. //
  1476. // we are done, clean
  1477. //
  1478. break;
  1479. }
  1480. //
  1481. // this is unexpected
  1482. //
  1483. vLogMessage("[ConvertInstalledSdbsToNewFormat] RegEnumKeyExW for index 0x%lx returned unexpected error 0x%lx\n",
  1484. dwIndex, lResult);
  1485. break;
  1486. }
  1487. RtlInitUnicodeString(&ustrGuid, szSubKeyName);
  1488. Status = RtlGUIDFromString(&ustrGuid, &guidDB);
  1489. if (!NT_SUCCESS(Status)) {
  1490. //
  1491. // BUGBUG - failed to convert the guid (subkey name!)
  1492. // extraneous entry, log warning
  1493. //
  1494. vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to convert string to guid for \"%ls\" status 0x%lx\n",
  1495. szSubKeyName, Status);
  1496. continue;
  1497. }
  1498. //
  1499. // for this db entry we have to set the timestamp
  1500. //
  1501. lResult = RegOpenKeyExW(hKey,
  1502. szSubKeyName,
  1503. 0,
  1504. KEY_READ|KEY_WRITE|GetWow64Flag(),
  1505. &hKeyEntry);
  1506. if (lResult != ERROR_SUCCESS) {
  1507. //
  1508. // bad error ?
  1509. // BUGBUG
  1510. vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to open subkey \"%ls\" error 0x%lx\n",
  1511. szSubKeyName, lResult);
  1512. continue;
  1513. }
  1514. //
  1515. // now check the value
  1516. //
  1517. dwBufferSize = sizeof(liTimeStamp.QuadPart);
  1518. lResult = RegQueryValueExW(hKeyEntry,
  1519. L"DatabaseInstallTimeStamp",
  1520. NULL,
  1521. &dwType,
  1522. (PBYTE)&liTimeStamp.QuadPart,
  1523. &dwBufferSize);
  1524. if (lResult != ERROR_SUCCESS || dwType != REG_BINARY) {
  1525. //
  1526. // we may either have this value already -- if not, set it up now
  1527. //
  1528. liTimeStamp.LowPart = ftLastWriteTime.dwLowDateTime;
  1529. liTimeStamp.HighPart = ftLastWriteTime.dwHighDateTime;
  1530. vLogMessage("[Info] Database \"%ls\" receives timestamp \"%.16I64X\"\n",
  1531. szSubKeyName, liTimeStamp.QuadPart);
  1532. lResult = RegSetValueExW(hKeyEntry,
  1533. L"DatabaseInstallTimeStamp",
  1534. 0,
  1535. REG_BINARY,
  1536. (PBYTE)&liTimeStamp.QuadPart,
  1537. sizeof(liTimeStamp.QuadPart));
  1538. if (lResult != ERROR_SUCCESS) {
  1539. //
  1540. // error, ignore for now
  1541. //
  1542. vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to set timestamp value for database \"%ls\" value \"%.16I64X\" error 0x%lx\n",
  1543. szSubKeyName, liTimeStamp.QuadPart, lResult);
  1544. }
  1545. }
  1546. //
  1547. // at this point we have :
  1548. // sdb guid (in szSubKeyName)
  1549. // time stamp in liTimeStamp
  1550. //
  1551. //
  1552. // query also database path
  1553. //
  1554. pszDatabasePath = &szDatabasePath[0];
  1555. dwBufferSize = sizeof(szDatabasePath);
  1556. lResult = RegQueryValueExW(hKeyEntry,
  1557. L"DatabasePath",
  1558. NULL,
  1559. &dwType,
  1560. (PBYTE)pszDatabasePath,
  1561. &dwBufferSize);
  1562. if (lResult != ERROR_SUCCESS || dwType != REG_SZ) {
  1563. //
  1564. // no database path
  1565. // warn basically corrupt database path
  1566. //
  1567. vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to query database path for \"%s\" error 0x%lx\n", szSubKeyName, lResult);
  1568. pszDatabasePath = NULL;
  1569. }
  1570. //
  1571. // optional check: we can check here whether the sdb file does exist
  1572. //
  1573. //
  1574. // add this sdb to our cache
  1575. //
  1576. if (!AddSdbListEntry(pSdbListHead, guidDB, liTimeStamp.QuadPart, pszDatabasePath)) {
  1577. //
  1578. // failed to add list entry - we cannot continue
  1579. //
  1580. bSuccess = FALSE;
  1581. break;
  1582. }
  1583. RegCloseKey(hKeyEntry);
  1584. hKeyEntry = NULL;
  1585. }
  1586. if (hKeyEntry != NULL) {
  1587. RegCloseKey(hKeyEntry);
  1588. }
  1589. //
  1590. // we are done converting entries -- and we have also collected cache of sdb info
  1591. //
  1592. return bSuccess;
  1593. }
  1594. //
  1595. // this stucture is used to cache values associated with any particular entry (exe)
  1596. //
  1597. typedef struct tagSDBVALUEENTRY {
  1598. LIST_ENTRY ListEntry; // link
  1599. PSDBLISTENTRY pSdbEntry; // this entry belongs to this database
  1600. WCHAR szValueName[1]; // value name as we got it from registry
  1601. } SDBVALUEENTRY, *PSDBVALUEENTRY;
  1602. /*++
  1603. AddValueEntry
  1604. Adds an new link list element to the list of values
  1605. [in out] pValueListHead - link list of values
  1606. [in] pSdbEntry - pointer to a cached entry from sdb list
  1607. [in] pwszValueName - value name as we got it from the db (something like {guid} or {guid}.sdb)
  1608. returns true if successful
  1609. --*/
  1610. BOOL
  1611. AddValueEntry(
  1612. PLIST_ENTRY pValueListHead,
  1613. PSDBLISTENTRY pSdbEntry,
  1614. LPCWSTR pwszValueName
  1615. )
  1616. {
  1617. PSDBVALUEENTRY pValueEntry;
  1618. PBYTE Buffer;
  1619. DWORD dwSize;
  1620. dwSize = sizeof(SDBVALUEENTRY) + wcslen(pwszValueName) * sizeof(WCHAR);
  1621. Buffer = new BYTE[dwSize];
  1622. if (Buffer == NULL) {
  1623. //
  1624. // out of memory
  1625. //
  1626. vLogMessage("[AddValueEntry] Failed to allocate buffer for %ls 0x%lx bytes\n",
  1627. pwszValueName, dwSize);
  1628. return FALSE;
  1629. }
  1630. pValueEntry = (PSDBVALUEENTRY)Buffer;
  1631. pValueEntry->pSdbEntry = pSdbEntry;
  1632. wcscpy(pValueEntry->szValueName, pwszValueName);
  1633. InsertHeadList(pValueListHead, &pValueEntry->ListEntry);
  1634. return TRUE;
  1635. }
  1636. /*++
  1637. WriteEntryValue
  1638. Writes value for a particular entry (exe or layer name), deletes old value associated with
  1639. this particular database for this exe (or layer)
  1640. [in] hKey - handle for an entry (for instance
  1641. hklm/Software/Microsoft/Windows NT/CurrentVersion/AppcompatFlags/Custom/Notepad.exe)
  1642. [in] pValueEntry - pointer to a value entry element from the value list
  1643. [in] bWriteNewFormat - whether we are asked to write new or old format
  1644. returns true if successful
  1645. --*/
  1646. BOOL
  1647. WriteEntryValue(
  1648. HKEY hKey,
  1649. PSDBVALUEENTRY pValueEntry,
  1650. BOOL bWriteNewFormat // if true -- write new format else old format
  1651. )
  1652. {
  1653. LONG lResult;
  1654. BOOL bSuccess = FALSE;
  1655. LPCWSTR pValueName;
  1656. if (bWriteNewFormat) {
  1657. pValueName = pValueEntry->pSdbEntry->szGuidDB;
  1658. lResult = RegSetValueExW(hKey,
  1659. pValueName,
  1660. 0,
  1661. REG_QWORD,
  1662. (PBYTE)&pValueEntry->pSdbEntry->ullTimeStamp,
  1663. sizeof(pValueEntry->pSdbEntry->ullTimeStamp));
  1664. if (lResult != ERROR_SUCCESS) {
  1665. //
  1666. // we can't do this entry ?
  1667. //
  1668. vLogMessage("[WriteEntryValue] Failed to write qword value \"%ls\"=\"%.16I64X\" error 0x%lx\n",
  1669. pValueEntry->pSdbEntry->szGuidDB, pValueEntry->pSdbEntry->ullTimeStamp, lResult);
  1670. goto cleanup;
  1671. }
  1672. //
  1673. // nuke old entry
  1674. //
  1675. } else {
  1676. //
  1677. // old style format please
  1678. //
  1679. pValueName = pValueEntry->pSdbEntry->szDatabasePath;
  1680. lResult = RegSetValueExW(hKey,
  1681. pValueName,
  1682. 0,
  1683. REG_SZ,
  1684. (PBYTE)L"",
  1685. sizeof(WCHAR));
  1686. if (lResult != ERROR_SUCCESS) {
  1687. //
  1688. // trouble -- error
  1689. //
  1690. vLogMessage("[WriteEntryValue] Failed to write string value \"%ls\" error 0x%lx\n",
  1691. pValueEntry->pSdbEntry->szDatabasePath, lResult);
  1692. goto cleanup;
  1693. }
  1694. }
  1695. //
  1696. // if we are here -- success, check to see if we can delete the old value
  1697. //
  1698. if (_wcsicmp(pValueEntry->szValueName, pValueName) != 0) {
  1699. lResult = RegDeleteValueW(hKey, pValueEntry->szValueName);
  1700. if (lResult != ERROR_SUCCESS) {
  1701. vLogMessage("[WriteEntryValue] Failed to delete value \"%ls\" error 0x%lx\n",
  1702. pValueEntry->szValueName, lResult);
  1703. }
  1704. }
  1705. bSuccess = TRUE;
  1706. cleanup:
  1707. return bSuccess;
  1708. }
  1709. /*++
  1710. ConvertEntryToNewFormat
  1711. Converts a particular entry (layer or exe)
  1712. [in] hKeyParent - key handle for a parent key (for instance
  1713. hklm/Software/Microsoft/Windows NT/CurrentVersion/AppcompatFlags/Custom when
  1714. pwszEntryName == "Notepad.exe" or
  1715. hklm/Software/Microsoft/Windows NT/CurrentVersion/AppcompatFlags/Custom/Layers when
  1716. pwszEntryName == "RunLayer"
  1717. [in] pwszEntryName - Either exe name or layer name
  1718. [in] pSdbListHead - cached list of installed databases
  1719. [in] bNewFormat - whether to use new or old format
  1720. returns true if successful
  1721. --*/
  1722. BOOL
  1723. ConvertEntryToNewFormat(
  1724. HKEY hKeyParent,
  1725. LPCWSTR pwszEntryName,
  1726. PLIST_ENTRY pSdbListHead,
  1727. BOOL bConvertToNewFormat // true if converting to new format, false if reverting
  1728. )
  1729. {
  1730. LONG lResult;
  1731. DWORD dwValues;
  1732. DWORD dwMaxValueNameLen;
  1733. DWORD dwMaxValueLen;
  1734. DWORD dwType;
  1735. DWORD dwValueNameSize;
  1736. DWORD dwValueSize;
  1737. LPWSTR pwszValueName = NULL;
  1738. LPBYTE pValue = NULL;
  1739. PSDBLISTENTRY pSdbEntry;
  1740. DWORD dwIndex;
  1741. LIST_ENTRY ValueList = { 0 };
  1742. PSDBVALUEENTRY pValueEntry;
  1743. PLIST_ENTRY pValueList;
  1744. PBYTE Buffer;
  1745. BOOL bSuccess = FALSE;
  1746. HKEY hKey = NULL;
  1747. //
  1748. // loop through values, for each value - find sdb and write out new entry
  1749. // then delete old entry
  1750. //
  1751. lResult = RegOpenKeyExW(hKeyParent,
  1752. pwszEntryName,
  1753. 0,
  1754. KEY_READ|KEY_WRITE|GetWow64Flag(),
  1755. &hKey);
  1756. if (lResult != ERROR_SUCCESS) {
  1757. vLogMessage("[ConvertEntryToNewFormat] Failed to open key \"%ls\" error 0x%lx\n",
  1758. pwszEntryName, lResult);
  1759. goto cleanup;
  1760. }
  1761. lResult = RegQueryInfoKeyW(hKey,
  1762. NULL, NULL, // class/class buffer
  1763. NULL, // reserved
  1764. NULL, NULL, // subkeys/max subkey length
  1765. NULL, // max class len
  1766. &dwValues, // value count
  1767. &dwMaxValueNameLen,
  1768. &dwMaxValueLen,
  1769. NULL, NULL);
  1770. if (lResult != ERROR_SUCCESS) {
  1771. //
  1772. // failed to query the key, very bad
  1773. // bugbug
  1774. vLogMessage("[ConvertEntryToNewFormat] Failed to query key information \"%ls\" error 0x%lx\n",
  1775. pwszEntryName, lResult);
  1776. goto cleanup;
  1777. }
  1778. //
  1779. // allocate buffers
  1780. //
  1781. pwszValueName = new WCHAR[dwMaxValueNameLen + 1];
  1782. pValue = new BYTE[dwMaxValueLen];
  1783. if (pValue == NULL || pwszValueName == NULL) {
  1784. //
  1785. // bugbug
  1786. //
  1787. vLogMessage("[ConvertEntryToNewFormat] Failed to allocate memory buffer entry \"%ls\" (0x%lx, 0x%lx)\n",
  1788. pwszEntryName, dwMaxValueNameLen, dwMaxValueLen);
  1789. goto cleanup;
  1790. }
  1791. InitializeListHead(&ValueList);
  1792. //
  1793. // we have dwValues -- the count of values
  1794. //
  1795. for (dwIndex = 0; dwIndex < dwValues; ++dwIndex) {
  1796. dwValueNameSize = dwMaxValueNameLen + 1;
  1797. dwValueSize = dwMaxValueLen;
  1798. lResult = RegEnumValueW(hKey,
  1799. dwIndex,
  1800. pwszValueName,
  1801. &dwValueNameSize,
  1802. NULL,
  1803. &dwType,
  1804. (PBYTE)pValue,
  1805. &dwValueSize);
  1806. //
  1807. // check if we are successful
  1808. //
  1809. if (lResult != ERROR_SUCCESS) {
  1810. if (lResult == ERROR_NO_MORE_ITEMS) {
  1811. //
  1812. // oops -- we ran out of values!!! Unexpected, but ok
  1813. //
  1814. vLogMessage("[ConvertEntryToNewFormat] RegEnumValue unexpectedly reports no more items for \"%ls\" index 0x%lx\n",
  1815. pwszEntryName, dwIndex);
  1816. break;
  1817. }
  1818. //
  1819. // log error and continue
  1820. //
  1821. vLogMessage("[ConvertEntryToNewFormat] RegEnumValue failed for \"%ls\" index 0x%lx error 0x%lx\n",
  1822. pwszEntryName, dwIndex, lResult);
  1823. continue;
  1824. }
  1825. if (bConvertToNewFormat) {
  1826. if (dwType != REG_SZ) {
  1827. //
  1828. // bad entry for sure -- this could be a new entry
  1829. // log warning
  1830. //
  1831. if (dwType == REG_QWORD || (dwType == REG_BINARY && dwValueSize == sizeof(ULONGLONG))) {
  1832. //
  1833. // new style entry ?
  1834. //
  1835. if (wcsrchr(pwszValueName, L'.') == NULL &&
  1836. *pwszValueName == L'{' &&
  1837. *(pwszValueName + wcslen(pwszValueName) - 1) == L'}') {
  1838. vLogMessage("[Info] Entry \"%ls\" value \"%ls\" already in new format.\n",
  1839. pwszEntryName, pwszValueName);
  1840. continue;
  1841. }
  1842. }
  1843. //
  1844. // very likely - some entry we do not understand
  1845. //
  1846. vLogMessage("[ConvertEntryToNewFormat] Bad value type (0x%lx) for entry \"%ls\" value \"%ls\" index 0x%lx\n",
  1847. dwType, pwszEntryName, pwszValueName, dwIndex);
  1848. continue;
  1849. }
  1850. //
  1851. // search by pwszValueName (which happens to be the GUID.sdb)
  1852. // this may be any kind of a string -- not nec. guid
  1853. //
  1854. if (!FindSdbListEntry(pSdbListHead, pwszValueName, &pSdbEntry, NULL)) {
  1855. //
  1856. // error - sdb not found!
  1857. //
  1858. vLogMessage("[ConvertEntryToNewFormat] Failed to find database \"%ls\" for entry \"%ls\" index 0x%lx\n",
  1859. pwszValueName, pwszEntryName, dwIndex);
  1860. continue;
  1861. }
  1862. } else {
  1863. //
  1864. // check the type first, if this is a new style entry - this will be bin
  1865. //
  1866. if (dwType == REG_SZ &&
  1867. wcsrchr(pwszValueName, L'.') != NULL &&
  1868. *(LPCWSTR)pValue == L'\0') {
  1869. vLogMessage("[Info] Entry \"%ls\" value \"%ls\" is already in required (old) format.\n",
  1870. pwszEntryName, pwszValueName);
  1871. continue;
  1872. }
  1873. if (dwType != REG_QWORD &&
  1874. (dwType != REG_BINARY || dwValueSize < sizeof(ULONGLONG))) {
  1875. //
  1876. // error -- we don't know what this entry is, go to the next one
  1877. // print warning actually
  1878. //
  1879. vLogMessage("[ConvertEntryToNewFormat] Bad value type (0x%lx) or size (0x%lx) for entry \"%ls\" value \"%ls\" index 0x%lx\n",
  1880. dwType, dwValueSize, pwszEntryName, pwszValueName, dwIndex);
  1881. continue;
  1882. }
  1883. if (!FindSdbListEntry(pSdbListHead, pwszValueName, &pSdbEntry, NULL)) {
  1884. //
  1885. // we're in trouble -- an entry has no registered database
  1886. //
  1887. vLogMessage("[ConvertEntryToNewFormat] Failed to find database for value \"%ls\" for entry \"%ls\" index 0x%lx\n",
  1888. pwszValueName, pwszEntryName, dwIndex);
  1889. continue;
  1890. }
  1891. }
  1892. //
  1893. // we have found entry and we're ready to write it out, queue it up
  1894. //
  1895. if (!AddValueEntry(&ValueList, pSdbEntry, pwszValueName)) {
  1896. //
  1897. // bugbug can't add value entry
  1898. //
  1899. vLogMessage("[ConvertEntryToNewFormat] Failed to add value \"%ls\" for entry \"%ls\" index 0x%lx\n",
  1900. pwszValueName, pwszEntryName, dwIndex);
  1901. goto cleanup;
  1902. }
  1903. }
  1904. //
  1905. // we have gone through all the values, write loop
  1906. //
  1907. bSuccess = TRUE;
  1908. pValueList = ValueList.Flink;
  1909. while (pValueList != &ValueList) {
  1910. pValueEntry = (PSDBVALUEENTRY)((PBYTE)pValueList - OFFSETOF(SDBVALUEENTRY, ListEntry));
  1911. //
  1912. // we can point to the next entry now
  1913. //
  1914. if (!WriteEntryValue(hKey, pValueEntry, bConvertToNewFormat)) {
  1915. //
  1916. // error, can't convert entry
  1917. // continue though so that we cleanout the list
  1918. vLogMessage("[ConvertEntryToNewFormat] Failed to write value for entry \"%ls\"\n",
  1919. pwszEntryName);
  1920. }
  1921. pValueList = pValueList->Flink;
  1922. }
  1923. cleanup:
  1924. if (ValueList.Flink) {
  1925. pValueList = ValueList.Flink;
  1926. while (pValueList != &ValueList) {
  1927. Buffer = (PBYTE)pValueList - OFFSETOF(SDBVALUEENTRY, ListEntry);
  1928. pValueList = pValueList->Flink;
  1929. delete[] Buffer;
  1930. }
  1931. }
  1932. if (hKey != NULL) {
  1933. RegCloseKey(hKey);
  1934. }
  1935. if (pwszValueName != NULL) {
  1936. delete[] pwszValueName;
  1937. }
  1938. if (pValue != NULL) {
  1939. delete[] pValue;
  1940. }
  1941. return bSuccess;
  1942. }
  1943. /*++
  1944. ConvertFormat
  1945. This function handles format conversions
  1946. [in] bConvertToNewFormat - true if conversion old->new, false otherwise
  1947. returns true if success
  1948. --*/
  1949. BOOL
  1950. ConvertFormat(
  1951. BOOL bConvertToNewFormat
  1952. )
  1953. {
  1954. LIST_ENTRY SdbList = { 0 }; // installed sdbs cache
  1955. HKEY hKey;
  1956. LONG lResult;
  1957. DWORD dwIndex;
  1958. WCHAR szSubKeyName[MAX_PATH];
  1959. DWORD dwBufferSize;
  1960. WCHAR szKeyPath[MAX_PATH];
  1961. BOOL bSuccess = FALSE;
  1962. //
  1963. // first convert installed sdbs
  1964. // open installed sdb key
  1965. //
  1966. lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  1967. APPCOMPAT_KEY_PATH_INSTALLEDSDB_W, // path to InstalledSDB
  1968. 0,
  1969. KEY_READ|KEY_WRITE|GetWow64Flag(),
  1970. &hKey);
  1971. if (lResult != ERROR_SUCCESS) {
  1972. //
  1973. // perhaps no dbs are installed ?
  1974. //
  1975. if (lResult == ERROR_FILE_NOT_FOUND) {
  1976. //
  1977. // no installed sdbs -- no problem
  1978. //
  1979. vLogMessage("[ConvertFormat] No Installed sdbs found\n");
  1980. return TRUE;
  1981. }
  1982. //
  1983. // some sort of error has occured
  1984. //
  1985. vLogMessage("[ConvertFormat] Failed to open key \"%ls\" Error 0x%lx\n",
  1986. APPCOMPAT_KEY_PATH_INSTALLEDSDB_W, lResult);
  1987. return FALSE;
  1988. }
  1989. //
  1990. // note that ConvertInstalledSdbsToNewFormat works properly for both install and uninstall cases
  1991. //
  1992. InitializeListHead(&SdbList);
  1993. if (!ConvertInstalledSdbsToNewFormat(hKey, &SdbList)) {
  1994. goto cleanup;
  1995. }
  1996. // done with Installed sdbs
  1997. RegCloseKey(hKey);
  1998. hKey = NULL;
  1999. //
  2000. // next up is entry conversion -- first enum exes, then layers
  2001. //
  2002. lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  2003. APPCOMPAT_KEY_PATH_CUSTOM_W,
  2004. 0,
  2005. KEY_READ|KEY_WRITE|GetWow64Flag(),
  2006. &hKey);
  2007. if (lResult != ERROR_SUCCESS) {
  2008. //
  2009. // what is this?
  2010. //
  2011. if (lResult == ERROR_FILE_NOT_FOUND && !IsListEmpty(&SdbList)) {
  2012. vLogMessage("[ConvertFormat] Failed to open \"%ls\" - check consistency\n",
  2013. APPCOMPAT_KEY_PATH_CUSTOM_W);
  2014. } else {
  2015. vLogMessage("[ConvertFormat] Failed to open \"%ls\" error 0x%lx\n",
  2016. APPCOMPAT_KEY_PATH_CUSTOM_W, lResult);
  2017. }
  2018. goto cleanup;
  2019. }
  2020. dwIndex = 0;
  2021. while (TRUE) {
  2022. dwBufferSize = sizeof(szSubKeyName)/sizeof(szSubKeyName[0]);
  2023. lResult = RegEnumKeyExW(hKey,
  2024. dwIndex,
  2025. szSubKeyName,
  2026. &dwBufferSize,
  2027. NULL, NULL, NULL,
  2028. NULL);
  2029. ++dwIndex;
  2030. if (lResult != ERROR_SUCCESS) {
  2031. if (lResult == ERROR_NO_MORE_ITEMS) {
  2032. break;
  2033. }
  2034. //
  2035. // some sort of error, log and continue
  2036. //
  2037. vLogMessage("[ConvertFormat] RegEnumKey (entries) returned error for index 0x%lx error 0x%lx\n",
  2038. dwIndex, lResult);
  2039. break;
  2040. }
  2041. //
  2042. // skip layers for now
  2043. //
  2044. if (!_wcsicmp(szSubKeyName, L"Layers")) {
  2045. continue;
  2046. }
  2047. // for each of these -- call fixup function
  2048. if (!ConvertEntryToNewFormat(hKey, szSubKeyName, &SdbList, bConvertToNewFormat)) {
  2049. vLogMessage("[ConvertFormat] Failed to convert entry \"%ls\"\n", szSubKeyName);
  2050. }
  2051. }
  2052. RegCloseKey(hKey);
  2053. hKey = NULL;
  2054. //
  2055. // next up - layers
  2056. //
  2057. wcscpy(szKeyPath, APPCOMPAT_KEY_PATH_CUSTOM_W);
  2058. wcscat(szKeyPath, L"\\Layers");
  2059. //
  2060. // open and enum layers
  2061. //
  2062. lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  2063. szKeyPath,
  2064. 0,
  2065. KEY_READ|KEY_WRITE|GetWow64Flag(),
  2066. &hKey);
  2067. if (lResult != ERROR_SUCCESS) {
  2068. // maybe dead ?
  2069. if (lResult == ERROR_FILE_NOT_FOUND) {
  2070. //
  2071. // it's ok, maybe we have none of those ?
  2072. //
  2073. vLogMessage("[ConvertFormat] No layers found\n");
  2074. goto ConvertComplete;
  2075. }
  2076. vLogMessage("[ConvertFormat] Failed to open \"%ls\" error 0x%lx\n", szKeyPath, lResult);
  2077. goto cleanup;
  2078. }
  2079. dwIndex = 0;
  2080. while (TRUE) {
  2081. dwBufferSize = sizeof(szSubKeyName)/sizeof(szSubKeyName[0]);
  2082. lResult = RegEnumKeyExW(hKey,
  2083. dwIndex,
  2084. szSubKeyName,
  2085. &dwBufferSize,
  2086. NULL, NULL, NULL,
  2087. NULL);
  2088. ++dwIndex;
  2089. if (lResult != ERROR_SUCCESS) {
  2090. // check if this was the last entry
  2091. if (lResult == ERROR_NO_MORE_ITEMS) {
  2092. // clean break
  2093. break;
  2094. }
  2095. // some sort of error, log and continue
  2096. vLogMessage("[ConvertFormat] RegEnumKey (layers) returned error for index 0x%lx error 0x%lx\n",
  2097. dwIndex, lResult);
  2098. break;
  2099. }
  2100. // for each of these -- call fixup function
  2101. if (!ConvertEntryToNewFormat(hKey, szSubKeyName, &SdbList, bConvertToNewFormat)) {
  2102. vLogMessage("[ConvertFormat] Failed to convert entry \"%ls\"\n", szSubKeyName);
  2103. }
  2104. }
  2105. RegCloseKey(hKey);
  2106. hKey = NULL;
  2107. ConvertComplete:
  2108. bSuccess = TRUE;
  2109. cleanup:
  2110. if (hKey != NULL) {
  2111. RegCloseKey(hKey);
  2112. }
  2113. //
  2114. // free SdbList
  2115. //
  2116. CleanupSdbList(&SdbList);
  2117. return bSuccess;
  2118. }
  2119. BOOL
  2120. ProcessMSIPackages(
  2121. PDB pdb,
  2122. TAGID tiDatabase,
  2123. LPCWSTR pszGuidDB,
  2124. ULONGLONG ullSdbTimeStamp,
  2125. INSTALL_MODE eMode)
  2126. {
  2127. TAGID tiMsiPackage;
  2128. TAGID tiMsiPackageID;
  2129. GUID* pGuidID;
  2130. WCHAR szRegPath[MAX_PATH];
  2131. BOOL bReturn = TRUE;
  2132. WCHAR wszGuid[64];
  2133. UNICODE_STRING ustrGuid = { 0 };
  2134. tiMsiPackage = SdbFindFirstTag(pdb, tiDatabase, TAG_MSI_PACKAGE);
  2135. while (tiMsiPackage && bReturn) {
  2136. //
  2137. // we have a package, extract/find TAG_MSI_PACKAGE_ID
  2138. //
  2139. tiMsiPackageID = SdbFindFirstTag(pdb, tiMsiPackage, TAG_MSI_PACKAGE_ID);
  2140. if (!tiMsiPackageID) {
  2141. vPrintError(IDS_MISSING_PACKAGE_ID);
  2142. if (eMode == MODE_CLEANUP) {
  2143. goto NextPackage;
  2144. } else {
  2145. bReturn = FALSE;
  2146. break;
  2147. }
  2148. }
  2149. pGuidID = (GUID*)SdbGetBinaryTagData(pdb, tiMsiPackageID);
  2150. if (pGuidID == NULL) {
  2151. vPrintError(IDS_MISSING_PACKAGE_ID);
  2152. if (eMode == MODE_CLEANUP) {
  2153. goto NextPackage;
  2154. } else {
  2155. bReturn = FALSE;
  2156. break;
  2157. }
  2158. }
  2159. if (!NT_SUCCESS(RtlStringFromGUID(*pGuidID, &ustrGuid))) {
  2160. vPrintError(IDS_GUID_BAD_FORMAT);
  2161. bReturn = FALSE;
  2162. break;
  2163. }
  2164. RtlCopyMemory(wszGuid, ustrGuid.Buffer, ustrGuid.Length);
  2165. wszGuid[ustrGuid.Length / sizeof(WCHAR)] = TEXT('\0');
  2166. if (eMode == MODE_INSTALL) {
  2167. bReturn = InstallSdbEntry(wszGuid, pszGuidDB, 0, 0, ullSdbTimeStamp, FALSE);
  2168. } else {
  2169. bReturn = UninstallSdbEntry(wszGuid, pszGuidDB, ullSdbTimeStamp, FALSE);
  2170. }
  2171. RtlFreeUnicodeString(&ustrGuid);
  2172. NextPackage:
  2173. tiMsiPackage = SdbFindNextTag(pdb, tiDatabase, tiMsiPackage);
  2174. }
  2175. return bReturn;
  2176. }
  2177. #define MAX_FRIENDLY_NAME_LEN 256
  2178. BOOL
  2179. bHandleInstall(
  2180. WCHAR* wszSdbPath,
  2181. INSTALL_MODE eMode,
  2182. WCHAR* wszSdbInstallPath
  2183. )
  2184. {
  2185. PDB pdb = NULL;
  2186. int i;
  2187. WCHAR wszSdbName[MAX_PATH];
  2188. WCHAR wszSdbInstallName[MAX_PATH];
  2189. HKEY hKey = NULL;
  2190. LONG lRes;
  2191. TAGID tiDatabase, tiExe, tiLayer;
  2192. TAGID tiDBName = TAGID_NULL;
  2193. WCHAR* pszDBName = NULL;
  2194. WCHAR wszFriendlyName[MAX_FRIENDLY_NAME_LEN];
  2195. WCHAR* wszTemp;
  2196. GUID guidDB;
  2197. NTSTATUS Status;
  2198. FILETIME SystemTime;
  2199. BOOL bRet = TRUE;
  2200. UNICODE_STRING ustrGUID;
  2201. ULARGE_INTEGER TimeStamp = { 0 };
  2202. //
  2203. // determine the timestamp (for the install case)
  2204. //
  2205. if (eMode == MODE_INSTALL) {
  2206. GetSystemTimeAsFileTime(&SystemTime);
  2207. TimeStamp.LowPart = SystemTime.dwLowDateTime;
  2208. TimeStamp.HighPart = SystemTime.dwHighDateTime;
  2209. }
  2210. assert(wszSdbPath && wszSdbInstallPath);
  2211. if (!wszSdbPath || !wszSdbInstallPath) {
  2212. bRet = FALSE;
  2213. goto quickOut;
  2214. }
  2215. ZeroMemory(wszFriendlyName, sizeof(wszFriendlyName));
  2216. //
  2217. // get the full path from the file name
  2218. //
  2219. wszTemp = wszGetFileFromPath(wszSdbPath);
  2220. if (!wszTemp) {
  2221. vPrintMessage(IDS_UNABLE_TO_GET_FILE);
  2222. bRet = FALSE;
  2223. goto quickOut;
  2224. }
  2225. wcscpy(wszSdbName, wszTemp);
  2226. if (wcscmp(wszSdbName, L"sysmain.sdb") == 0) {
  2227. vPrintError(IDS_CANT_INSTALL_SYS);
  2228. bRet = FALSE;
  2229. goto quickOut;
  2230. }
  2231. if (GetFileAttributesW(wszSdbPath) != -1 && bIsAlreadyInstalled(wszSdbPath)) {
  2232. if (eMode == MODE_INSTALL) {
  2233. //
  2234. // they asked us to install, it's installed, so we're done
  2235. //
  2236. vPrintMessage(IDS_ALREADY_INSTALLED, wszSdbPath);
  2237. goto quickOut;
  2238. }
  2239. } else {
  2240. if (eMode == MODE_UNINSTALL) {
  2241. //
  2242. // they asked us to uninstall, it's not installed, so we're done
  2243. //
  2244. vPrintMessage(IDS_NOT_INSTALLED, wszSdbPath);
  2245. goto quickOut;
  2246. }
  2247. }
  2248. if (eMode == MODE_INSTALL) {
  2249. //
  2250. // find out what file name we're going to use for installing
  2251. //
  2252. if (!bFindInstallName(wszSdbPath, wszSdbInstallPath)) {
  2253. bRet = FALSE;
  2254. goto quickOut;
  2255. }
  2256. } else if (eMode == MODE_CLEANUP) {
  2257. //
  2258. // we're cleaning up a bad install, so we need to get the install name from the
  2259. // install path
  2260. //
  2261. wszTemp = wszGetFileFromPath(wszSdbInstallPath);
  2262. if (!wszTemp) {
  2263. vPrintMessage(IDS_UNABLE_TO_GET_FILE);
  2264. bRet = FALSE;
  2265. goto quickOut;
  2266. }
  2267. } else {
  2268. //
  2269. // we're uninstalling, so the install name is the given name
  2270. // and the install path is the given path
  2271. //
  2272. wcscpy(wszSdbInstallPath, wszSdbPath);
  2273. }
  2274. //
  2275. // try to get the guid for later
  2276. //
  2277. if (!bGetGuid(wszSdbPath, &guidDB)) {
  2278. bRet = FALSE;
  2279. goto out;
  2280. }
  2281. //
  2282. // check whether the guid is coopted from one of the known databases
  2283. //
  2284. if (IsKnownDatabaseGUID(&guidDB)) {
  2285. vPrintError(IDS_CANT_INSTALL_SYS);
  2286. bRet = FALSE;
  2287. goto quickOut;
  2288. }
  2289. //
  2290. // in all cases, install name is the db GUID
  2291. //
  2292. Status = RtlStringFromGUID(guidDB, &ustrGUID);
  2293. if (!NT_SUCCESS(Status)) {
  2294. bRet = FALSE;
  2295. goto out;
  2296. }
  2297. RtlCopyMemory(wszSdbInstallName, ustrGUID.Buffer, ustrGUID.Length);
  2298. wszSdbInstallName[ustrGUID.Length/sizeof(WCHAR)] = L'\0';
  2299. RtlFreeUnicodeString(&ustrGUID);
  2300. //
  2301. // if we're installing, make sure the root tags are in place
  2302. //
  2303. if (eMode == MODE_INSTALL) {
  2304. lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
  2305. APPCOMPAT_KEY_PATH_W,
  2306. 0,
  2307. NULL,
  2308. 0,
  2309. KEY_ALL_ACCESS|GetWow64Flag(),
  2310. NULL,
  2311. &hKey,
  2312. NULL);
  2313. if (lRes != ERROR_SUCCESS) {
  2314. if (lRes == ERROR_ACCESS_DENIED) {
  2315. vPrintError(IDS_NEED_INSTALL_PERMISSION);
  2316. } else {
  2317. vPrintError(IDS_CANT_CREATE_REG_KEY, APPCOMPAT_KEY_PATH_W);
  2318. }
  2319. bRet = FALSE;
  2320. goto out;
  2321. }
  2322. RegCloseKey(hKey);
  2323. hKey = NULL;
  2324. lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
  2325. APPCOMPAT_KEY_PATH_CUSTOM_W,
  2326. 0,
  2327. NULL,
  2328. 0,
  2329. KEY_ALL_ACCESS|GetWow64Flag(),
  2330. NULL,
  2331. &hKey,
  2332. NULL);
  2333. if (lRes != ERROR_SUCCESS) {
  2334. vPrintError(IDS_CANT_CREATE_REG_KEY, APPCOMPAT_KEY_PATH_CUSTOM_W);
  2335. bRet = FALSE;
  2336. goto out;
  2337. }
  2338. RegCloseKey(hKey);
  2339. hKey = NULL;
  2340. }
  2341. // Open the DB.
  2342. pdb = SdbOpenDatabase(wszSdbPath, DOS_PATH);
  2343. if (pdb == NULL) {
  2344. vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSdbPath);
  2345. bRet = FALSE;
  2346. goto out;
  2347. }
  2348. tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
  2349. if (!tiDatabase) {
  2350. vPrintError(IDS_NO_DB_TAG, wszSdbPath);
  2351. bRet = FALSE;
  2352. goto out;
  2353. }
  2354. //
  2355. // get the friendly name of the database
  2356. //
  2357. tiDBName = SdbFindFirstTag(pdb, tiDatabase, TAG_NAME);
  2358. if (tiDBName) {
  2359. pszDBName = SdbGetStringTagPtr(pdb, tiDBName);
  2360. }
  2361. //
  2362. // if we don't find a friendly name, use the SDB file name
  2363. //
  2364. if (pszDBName) {
  2365. wcsncpy(wszFriendlyName, pszDBName, MAX_FRIENDLY_NAME_LEN);
  2366. wszFriendlyName[MAX_FRIENDLY_NAME_LEN - 1] = 0;
  2367. } else {
  2368. wcsncpy(wszFriendlyName, wszSdbName, MAX_FRIENDLY_NAME_LEN);
  2369. wszFriendlyName[MAX_FRIENDLY_NAME_LEN - 1] = 0;
  2370. }
  2371. tiExe = SdbFindFirstTag(pdb, tiDatabase, TAG_EXE);
  2372. while (tiExe) {
  2373. WCHAR szRegPath[MAX_PATH];
  2374. TAGID tiName, tiAction;
  2375. WCHAR *szName;
  2376. tiName = SdbFindFirstTag(pdb, tiExe, TAG_NAME);
  2377. if (!tiName) {
  2378. vPrintError(IDS_NO_EXE_NAME);
  2379. bRet = FALSE;
  2380. if (eMode == MODE_CLEANUP) {
  2381. goto nextExe;
  2382. } else {
  2383. goto quickOut;
  2384. }
  2385. }
  2386. szName = SdbGetStringTagPtr(pdb, tiName);
  2387. if (!szName) {
  2388. vPrintError(IDS_NO_EXE_NAME_PTR);
  2389. bRet = FALSE;
  2390. if (eMode == MODE_CLEANUP) {
  2391. goto nextExe;
  2392. } else {
  2393. goto quickOut;
  2394. }
  2395. }
  2396. //
  2397. // See if this EXE has an ACTION node. Currently only EXEs shimmed with the LUA
  2398. // shims have ACTION nodes.
  2399. //
  2400. tiAction = SdbFindFirstTag(pdb, tiExe, TAG_ACTION);
  2401. if (eMode == MODE_INSTALL) {
  2402. if (!InstallSdbEntry(szName, wszSdbInstallName, pdb, tiAction, TimeStamp.QuadPart, FALSE)) {
  2403. bRet = FALSE;
  2404. goto out;
  2405. }
  2406. } else {
  2407. if (!UninstallSdbEntry(szName, wszSdbInstallName, TimeStamp.QuadPart, FALSE)) {
  2408. goto quickOut;
  2409. }
  2410. }
  2411. nextExe:
  2412. tiExe = SdbFindNextTag(pdb, tiDatabase, tiExe);
  2413. }
  2414. //
  2415. // Loop through the published layers
  2416. //
  2417. tiLayer = SdbFindFirstTag(pdb, tiDatabase, TAG_LAYER);
  2418. while (tiLayer) {
  2419. WCHAR szRegPath[MAX_PATH];
  2420. TAGID tiName;
  2421. WCHAR* szName;
  2422. tiName = SdbFindFirstTag(pdb, tiLayer, TAG_NAME);
  2423. if (!tiName) {
  2424. vPrintError(IDS_NO_EXE_NAME);
  2425. bRet = FALSE;
  2426. if (eMode == MODE_CLEANUP) {
  2427. goto nextLayer;
  2428. } else {
  2429. goto quickOut;
  2430. }
  2431. }
  2432. szName = SdbGetStringTagPtr(pdb, tiName);
  2433. if (!szName) {
  2434. vPrintError(IDS_NO_EXE_NAME_PTR);
  2435. bRet = FALSE;
  2436. if (eMode == MODE_CLEANUP) {
  2437. goto nextLayer;
  2438. } else {
  2439. goto quickOut;
  2440. }
  2441. }
  2442. if (eMode == MODE_INSTALL) {
  2443. if (!InstallSdbEntry(szName, wszSdbInstallName, 0, 0, TimeStamp.QuadPart, TRUE)) {
  2444. bRet = FALSE;
  2445. goto out;
  2446. }
  2447. } else {
  2448. if (!UninstallSdbEntry(szName, wszSdbInstallName, TimeStamp.QuadPart, TRUE)) {
  2449. goto quickOut;
  2450. }
  2451. }
  2452. nextLayer:
  2453. tiLayer = SdbFindNextTag(pdb, tiDatabase, tiLayer);
  2454. }
  2455. if (!ProcessMSIPackages(pdb, tiDatabase, wszSdbInstallName, TimeStamp.QuadPart, eMode)) {
  2456. bRet = FALSE;
  2457. goto quickOut;
  2458. }
  2459. if (pdb) {
  2460. SdbCloseDatabase(pdb);
  2461. pdb = NULL;
  2462. }
  2463. //
  2464. // now that we've handled the registry keys, copy the file
  2465. //
  2466. if (eMode == MODE_INSTALL) {
  2467. //
  2468. // ensure the directory exists
  2469. //
  2470. CreateDirectoryW(g_wszCustom, NULL);
  2471. if (!CopyFileW(wszSdbPath, wszSdbInstallPath, TRUE)) {
  2472. vPrintError(IDS_CANT_COPY_FILE, wszSdbInstallPath);
  2473. bRet = FALSE;
  2474. goto out;
  2475. }
  2476. } else {
  2477. //
  2478. // ensure that we don't fail because of read-only files
  2479. //
  2480. SetFileAttributesW(wszSdbInstallPath, FILE_ATTRIBUTE_NORMAL);
  2481. if (!DeleteFileW(wszSdbInstallPath)) {
  2482. vPrintError(IDS_CANT_DELETE_FILE, wszSdbInstallPath);
  2483. bRet = FALSE;
  2484. }
  2485. }
  2486. //
  2487. // set up or delete the uninstall registry keys
  2488. //
  2489. if (eMode == MODE_INSTALL) {
  2490. WCHAR wszSDBInstPath[MAX_PATH];
  2491. WCHAR wszUninstallPath[MAX_PATH];
  2492. WCHAR wszUninstallString[MAX_PATH];
  2493. //
  2494. // goofball hack required because of crazy redirection strategy on IA64
  2495. //
  2496. wszSDBInstPath[0] = 0;
  2497. GetSystemWindowsDirectoryW(wszSDBInstPath, MAX_PATH);
  2498. wcscat(wszSDBInstPath, L"\\SysWow64\\sdbinst.exe");
  2499. if (GetFileAttributesW(wszSDBInstPath) == -1) {
  2500. //
  2501. // there's no SysWow64 directory, so we'll just use system32
  2502. //
  2503. wszSDBInstPath[0] = 0;
  2504. GetSystemWindowsDirectoryW(wszSDBInstPath, MAX_PATH);
  2505. wcscat(wszSDBInstPath, L"\\system32\\sdbinst.exe");
  2506. }
  2507. wcscpy(wszUninstallPath, UNINSTALL_KEY_PATH);
  2508. wcscat(wszUninstallPath, wszSdbInstallName);
  2509. wcscat(wszUninstallPath, L".sdb");
  2510. lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
  2511. wszUninstallPath,
  2512. 0,
  2513. NULL,
  2514. REG_OPTION_NON_VOLATILE,
  2515. KEY_ALL_ACCESS|GetWow64Flag(),
  2516. NULL,
  2517. &hKey,
  2518. NULL);
  2519. if (lRes != ERROR_SUCCESS) {
  2520. vPrintError(IDS_CANT_CREATE_REG_KEY, wszUninstallPath);
  2521. bRet = FALSE;
  2522. goto out;
  2523. }
  2524. lRes = RegSetValueExW(hKey,
  2525. L"DisplayName",
  2526. 0,
  2527. REG_SZ,
  2528. (PBYTE)wszFriendlyName,
  2529. (wcslen(wszFriendlyName) + 1) * sizeof(WCHAR));
  2530. if (lRes != ERROR_SUCCESS) {
  2531. vPrintError(IDS_CANT_CREATE_VALUE, wszUninstallPath);
  2532. bRet = FALSE;
  2533. goto out;
  2534. }
  2535. swprintf(wszUninstallString, L"%s -u \"%s\"", wszSDBInstPath, wszSdbInstallPath);
  2536. lRes = RegSetValueExW(hKey, L"UninstallString", 0, REG_SZ,
  2537. (PBYTE)wszUninstallString, (wcslen(wszUninstallString) + 1) * sizeof(WCHAR));
  2538. if (lRes != ERROR_SUCCESS) {
  2539. vPrintError(IDS_CANT_CREATE_VALUE, wszUninstallPath);
  2540. bRet = FALSE;
  2541. goto out;
  2542. }
  2543. RegCloseKey(hKey);
  2544. hKey = NULL;
  2545. } else {
  2546. WCHAR wszUninstallPath[MAX_PATH];
  2547. lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, UNINSTALL_KEY_PATH, 0, KEY_ALL_ACCESS|GetWow64Flag(), &hKey);
  2548. if (lRes != ERROR_SUCCESS) {
  2549. vPrintError(IDS_CANT_OPEN_REG_KEY, UNINSTALL_KEY_PATH);
  2550. bRet = FALSE;
  2551. goto out;
  2552. }
  2553. //
  2554. // create sdb path name
  2555. //
  2556. wcscpy(wszUninstallPath, wszSdbInstallName);
  2557. wcscat(wszUninstallPath, L".sdb");
  2558. lRes = LocalRegDeleteKeyW(hKey, wszUninstallPath);
  2559. if (lRes != ERROR_SUCCESS) {
  2560. vPrintError(IDS_CANT_DELETE_REG_KEY, wszSdbInstallName, UNINSTALL_KEY_PATH);
  2561. }
  2562. RegCloseKey(hKey);
  2563. hKey = NULL;
  2564. }
  2565. //
  2566. // register or unregister the DB
  2567. //
  2568. if (eMode == MODE_INSTALL) {
  2569. if (!SdbRegisterDatabaseEx(wszSdbInstallPath, SDB_DATABASE_SHIM, &TimeStamp.QuadPart)) {
  2570. vPrintError(IDS_CANT_REGISTER_DB, wszFriendlyName);
  2571. bRet = FALSE;
  2572. goto out;
  2573. }
  2574. } else {
  2575. if (!SdbUnregisterDatabase(&guidDB)) {
  2576. vPrintError(IDS_CANT_UNREGISTER_DB, wszFriendlyName);
  2577. }
  2578. }
  2579. if (eMode == MODE_INSTALL) {
  2580. vPrintMessage(IDS_INSTALL_COMPLETE, wszFriendlyName);
  2581. } else {
  2582. vPrintMessage(IDS_UNINSTALL_COMPLETE, wszFriendlyName);
  2583. }
  2584. out:
  2585. //
  2586. // always silently delete the file on uninstall, whether we failed to remove the
  2587. // registry entries or not.
  2588. //
  2589. if (eMode != MODE_INSTALL) {
  2590. //
  2591. // need to make sure the pdb is closed before deleting it.
  2592. //
  2593. if (pdb) {
  2594. SdbCloseDatabase(pdb);
  2595. pdb = NULL;
  2596. }
  2597. //
  2598. // ensure that we don't fail because of read-only files
  2599. //
  2600. SetFileAttributesW(wszSdbInstallPath, FILE_ATTRIBUTE_NORMAL);
  2601. DeleteFileW(wszSdbInstallPath);
  2602. }
  2603. quickOut:
  2604. //
  2605. // these cleanup steps are not strictly necessary, as they'll be cleaned up
  2606. // on exit anyway. But what the heck.
  2607. //
  2608. if (pdb) {
  2609. SdbCloseDatabase(pdb);
  2610. pdb = NULL;
  2611. }
  2612. if (hKey) {
  2613. RegCloseKey(hKey);
  2614. hKey = NULL;
  2615. }
  2616. return bRet;
  2617. }
  2618. extern "C" int APIENTRY
  2619. wWinMain(
  2620. HINSTANCE hInstance,
  2621. HINSTANCE hPrevInstance,
  2622. LPWSTR lpCmdLine,
  2623. int nCmdShow
  2624. )
  2625. {
  2626. int i;
  2627. int nReturn = 0;
  2628. WCHAR wszSdbName[MAX_PATH];
  2629. WCHAR wszSdbPath[MAX_PATH];
  2630. WCHAR wszSdbInstallPath[MAX_PATH];
  2631. WCHAR wszOldSdbPath[MAX_PATH];
  2632. TAGID tiDBName = TAGID_NULL;
  2633. WCHAR* pszDBName = NULL;
  2634. WCHAR wszFriendlyName[256];
  2635. WCHAR wszGuid[100];
  2636. OSVERSIONINFO osvi;
  2637. LPWSTR szCommandLine;
  2638. LPWSTR* argv;
  2639. int argc;
  2640. INSTALL_MODE eMode;
  2641. g_hInst = hInstance;
  2642. //
  2643. // init custom directory
  2644. //
  2645. g_wszCustom[0] = 0;
  2646. GetSystemWindowsDirectoryW(g_wszCustom, MAX_PATH);
  2647. #if defined(_WIN64)
  2648. wcscat(g_wszCustom, L"\\AppPatch\\Custom\\IA64\\");
  2649. #else
  2650. wcscat(g_wszCustom, L"\\AppPatch\\Custom\\");
  2651. #endif // _WIN64
  2652. RtlZeroMemory(&osvi, sizeof(OSVERSIONINFO));
  2653. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  2654. GetVersionEx(&osvi);
  2655. if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
  2656. g_bWin2K = TRUE;
  2657. }
  2658. //
  2659. // Note that this memory isn't freed because it will automatically
  2660. // be freed on exit anyway, and there are a lot of exit cases from this application
  2661. //
  2662. szCommandLine = GetCommandLineW();
  2663. argv = CommandLineToArgvW(szCommandLine, &argc);
  2664. if (!argv) {
  2665. vPrintError(IDS_CANT_GET_ARGS);
  2666. return 1;
  2667. }
  2668. if (argc < 2) {
  2669. vPrintHelp(argv[0]);
  2670. return 0;
  2671. }
  2672. g_bQuiet = FALSE;
  2673. eMode = MODE_INSTALL;
  2674. wszSdbName[0] = 0;
  2675. wszGuid[0] = 0;
  2676. wszFriendlyName[0] = 0;
  2677. for (i = 1; i < argc; ++i) {
  2678. if (argv[i][0] == L'-' || argv[i][0] == L'/') {
  2679. switch (tolower(argv[i][1])) {
  2680. case L'?':
  2681. vPrintHelp(argv[0]);
  2682. return 0;
  2683. break;
  2684. case L'c':
  2685. //
  2686. // convert entries to new format
  2687. //
  2688. eMode = MODE_CONVERT_FORMAT_NEW;
  2689. break;
  2690. case L'g':
  2691. i++;
  2692. if (i >= argc) {
  2693. vPrintError(IDS_NEED_ARG, argv[i-1]);
  2694. vPrintHelp(argv[0]);
  2695. return 1;
  2696. }
  2697. eMode = MODE_UNINSTALL;
  2698. wcscpy(wszGuid, argv[i]);
  2699. break;
  2700. case L'n':
  2701. i++;
  2702. if (i >= argc) {
  2703. vPrintError(IDS_NEED_ARG, argv[i-1]);
  2704. vPrintHelp(argv[0]);
  2705. return 1;
  2706. }
  2707. eMode = MODE_UNINSTALL;
  2708. wcscpy(wszFriendlyName, argv[i]);
  2709. break;
  2710. case L'p':
  2711. g_bAllowPatches = TRUE;
  2712. break;
  2713. case L'r':
  2714. //
  2715. // revert to old format
  2716. //
  2717. eMode = MODE_CONVERT_FORMAT_OLD;
  2718. break;
  2719. case L'q':
  2720. g_bQuiet = TRUE;
  2721. break;
  2722. case L'u':
  2723. eMode = MODE_UNINSTALL;
  2724. break;
  2725. default:
  2726. vPrintError(IDS_INVALID_SWITCH, argv[i]);
  2727. vPrintHelp(argv[0]);
  2728. return 1;
  2729. }
  2730. } else {
  2731. if (wszSdbName[0]) {
  2732. vPrintError(IDS_TOO_MANY_ARGS);
  2733. vPrintHelp(argv[0]);
  2734. return 1;
  2735. }
  2736. wcscpy(wszSdbName, argv[i]);
  2737. }
  2738. }
  2739. //
  2740. // check to make sure the user is an administrator
  2741. //
  2742. if (!bCanRun()) {
  2743. if (eMode == MODE_INSTALL) {
  2744. vPrintError(IDS_NEED_INSTALL_PERMISSION);
  2745. } else {
  2746. vPrintError(IDS_NEED_UNINSTALL_PERMISSION);
  2747. }
  2748. return 1;
  2749. }
  2750. //
  2751. // check if we are running in a special 'setup' mode (converting or reverting the entries)
  2752. //
  2753. if (eMode == MODE_CONVERT_FORMAT_NEW || eMode == MODE_CONVERT_FORMAT_OLD) {
  2754. OpenLogFile();
  2755. if (!ConvertFormat(eMode == MODE_CONVERT_FORMAT_NEW)) {
  2756. nReturn = 1;
  2757. }
  2758. CloseLogFile();
  2759. return nReturn;
  2760. }
  2761. if (eMode == MODE_INSTALL && !wszSdbName[0]) {
  2762. vPrintError(IDS_MUST_SPECIFY_SDB);
  2763. vPrintHelp(argv[0]);
  2764. return 1;
  2765. }
  2766. if (eMode == MODE_UNINSTALL && !wszSdbName[0] && !wszGuid[0] && !wszFriendlyName[0]) {
  2767. vPrintError(IDS_MUST_SPECIFY_SDB);
  2768. vPrintHelp(argv[0]);
  2769. return 1;
  2770. }
  2771. if (wszSdbName[0]) {
  2772. if (wszSdbName[1] == L':' || wszSdbName[1] == L'\\') {
  2773. //
  2774. // this is a full path name, so just copy it
  2775. //
  2776. wcscpy(wszSdbPath, wszSdbName);
  2777. } else {
  2778. DWORD dwRet;
  2779. //
  2780. // this is a relative path name, so get the full one
  2781. //
  2782. if (!_wfullpath(wszSdbPath, wszSdbName, MAX_PATH)) {
  2783. vPrintError(IDS_CANT_GET_FULL_PATH);
  2784. return 1;
  2785. }
  2786. }
  2787. }
  2788. //
  2789. // First, get the real file name from other params, if necessary
  2790. //
  2791. if (eMode == MODE_UNINSTALL) {
  2792. if (wszGuid[0]) {
  2793. DWORD dwLen = wcslen(wszGuid);
  2794. if (dwLen != 38 || wszGuid[0] != L'{' || wszGuid[dwLen - 1] != L'}' ||
  2795. wszGuid[9] != L'-' || wszGuid[14] != L'-' || wszGuid[19] != L'-' ||
  2796. wszGuid[24] != L'-') {
  2797. vPrintError(IDS_GUID_BAD_FORMAT);
  2798. return 1;
  2799. }
  2800. wcscpy(wszSdbName, wszGuid);
  2801. wcscat(wszSdbName, L".sdb");
  2802. wcscpy(wszSdbPath, g_wszCustom);
  2803. wcscat(wszSdbPath, wszSdbName);
  2804. } else if (wszFriendlyName[0]) {
  2805. if (!bFriendlyNameToFile(wszFriendlyName, wszSdbName, wszSdbPath)) {
  2806. vPrintError(IDS_NO_FRIENDLY_NAME, wszFriendlyName);
  2807. return 1;
  2808. }
  2809. } else {
  2810. if (!bIsAlreadyInstalled(wszSdbPath)) {
  2811. WCHAR wszSdbPathTemp[MAX_PATH];
  2812. //
  2813. // they're not giving us an installed file, so get the GUID and convert to a file
  2814. //
  2815. if (!bFindInstallName(wszSdbPath, wszSdbPathTemp)) {
  2816. return 1;
  2817. }
  2818. wcscpy(wszSdbName, wszSdbPathTemp); // name and path are the same
  2819. wcscpy(wszSdbPath, wszSdbPathTemp);
  2820. }
  2821. }
  2822. }
  2823. if (eMode == MODE_INSTALL &&
  2824. GetFileAttributesW(wszSdbPath) != -1 &&
  2825. bIsAlreadyInstalled(wszSdbPath)) {
  2826. //
  2827. // they asked us to install, it's installed, so we're done
  2828. //
  2829. vPrintMessage(IDS_ALREADY_INSTALLED, wszSdbPath);
  2830. goto quickOut;
  2831. }
  2832. if (eMode == MODE_UNINSTALL && GetFileAttributesW(wszSdbPath) == -1) {
  2833. //
  2834. // they asked us to uninstall, it's not installed, so we're done
  2835. //
  2836. vPrintMessage(IDS_NOT_INSTALLED, wszSdbName);
  2837. goto quickOut;
  2838. }
  2839. if (eMode == MODE_INSTALL && DatabaseContainsPatch(wszSdbPath) && !g_bAllowPatches) {
  2840. //
  2841. // we can't install because the SDB contains a patch and the user hasn't authorized it.
  2842. //
  2843. // djm - can't use new text in XP SP1, so we'll print a generic error
  2844. //
  2845. //vPrintMessage(IDS_NO_PATCHES_ALLOWED);
  2846. vPrintMessage(IDS_APP_ERROR_TITLE);
  2847. goto quickOut;
  2848. }
  2849. if (eMode == MODE_INSTALL && bOldSdbInstalled(wszSdbPath, wszOldSdbPath)) {
  2850. //
  2851. // we should ask if we're going to uninstall the old one,
  2852. // unless we're in quiet mode.
  2853. //
  2854. int nRet;
  2855. WCHAR wszCaption[1024];
  2856. WCHAR wszText[1024];
  2857. if (g_bQuiet) {
  2858. nRet = IDYES;
  2859. } else {
  2860. if (!LoadStringW(g_hInst, IDS_APP_TITLE, wszCaption, 1024)) {
  2861. return 1;
  2862. }
  2863. if (!LoadStringW(g_hInst, IDS_FOUND_SAME_ID, wszText, 1024)) {
  2864. return 1;
  2865. }
  2866. nRet = MessageBoxW(NULL,
  2867. wszText,
  2868. wszCaption,
  2869. MB_YESNO | MB_ICONQUESTION);
  2870. }
  2871. if (nRet == IDNO) {
  2872. return 0;
  2873. } else if (nRet == IDYES) {
  2874. if (!bHandleInstall(wszOldSdbPath, MODE_UNINSTALL, wszSdbInstallPath)) {
  2875. vPrintError(IDS_FAILED_UNINSTALL);
  2876. return 1;
  2877. }
  2878. }
  2879. }
  2880. wszSdbInstallPath[0] = 0;
  2881. if (!bHandleInstall(wszSdbPath, eMode, wszSdbInstallPath)) {
  2882. if (eMode == MODE_INSTALL) {
  2883. //
  2884. // we need to clean up; the install failed.
  2885. //
  2886. g_bQuiet = TRUE;
  2887. bHandleInstall(wszSdbPath, MODE_CLEANUP, wszSdbInstallPath);
  2888. }
  2889. nReturn = 1;
  2890. }
  2891. //
  2892. // no matter what happens, flush the cache
  2893. //
  2894. vFlushCache();
  2895. quickOut:
  2896. return nReturn;
  2897. }