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

499 lines
19 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2002 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // BuildCab.cpp
  7. //
  8. // Description:
  9. // Prepares files to generate the CAB file, which will be streamed
  10. // into sasetup.msi.
  11. //
  12. // The directory structure for files to be searched is:
  13. // <SAKBuild> Directory where all files are binplaced, passed from the command line
  14. // ie. C:\binaries.x86fre\sacomponents
  15. // <SAKBuild>\sasetup.msi MSI in which the Files table is searched
  16. // <SAKBuild>\tmpCab Temp directory where files are placed
  17. //
  18. // History:
  19. // travisn 28-FEB-2002 Created
  20. //
  21. //////////////////////////////////////////////////////////////////////////////
  22. #include <crtdbg.h>
  23. #include <atlbase.h>
  24. #include <string>
  25. #include <msi.h>
  26. #include <msiquery.h>
  27. #include <search.h>
  28. #include <shellapi.h>
  29. #include <stdio.h>
  30. LPCWSTR MSI_FILENAME = L"sasetup.msi";
  31. LPCWSTR FILENAME_QUERY = L"SELECT File FROM File";
  32. const DWORD MAX_FILES = 500;
  33. //
  34. // Indexes to the array representing indexes to the data string
  35. // DO NOT CHANGE THESE SINCE THE BINARY SEARCH RELIES ON THEM IN THIS ORDER
  36. //
  37. const DWORD ELEMENTS_IN_SORT_ARRAY = 3;
  38. const DWORD SHORT_FILENAME = 0;
  39. const DWORD LONG_FILENAME = 1;
  40. const DWORD FOUND_FILE = 2;
  41. using namespace std;
  42. //Global list of filenames read from the MSI
  43. wstring g_wsAllFilenames;
  44. //////////////////////////////////////////////////////////////////////////////
  45. //++
  46. // CompareFilenames
  47. //
  48. // Description:
  49. // Function to sort the filenames found in the File table of sasetup.msi.
  50. // It ignores the file key that was prepended by sasetup.msi.
  51. // For example, it compares appmgr.exe instead of F12345_appmgr.exe
  52. //
  53. // Parameters:
  54. // e1 Pointer to a WCHAR that the search function is seeking
  55. // e2 Pointer to an offset in g_wsAllFilenames
  56. //--
  57. //////////////////////////////////////////////////////////////////////////////
  58. int __cdecl CompareFilenames(const void *e1, const void *e2)
  59. {
  60. DWORD *dwElement1 = (DWORD*)e1;
  61. DWORD *dwElement2 = (DWORD*)e2;
  62. //Get the short filename for the first string
  63. wstring wstr1(g_wsAllFilenames.substr(
  64. *dwElement1,
  65. g_wsAllFilenames.find_first_of(L",", *dwElement1) - *dwElement1));
  66. //Get the short filename for the second string
  67. wstring wstr2(g_wsAllFilenames.substr(
  68. *dwElement2,
  69. g_wsAllFilenames.find_first_of(L",", *dwElement2) - *dwElement2));
  70. return _wcsicmp(wstr1.data(), wstr2.data());
  71. }
  72. //////////////////////////////////////////////////////////////////////////////
  73. //++
  74. // CompareStringToFilename
  75. //
  76. // Description:
  77. // Function to search for a string that is equal to the given key
  78. //
  79. // Parameters:
  80. // pKey Pointer to a WCHAR that the search function is seeking
  81. // pFilenameIndex Pointer to an offset in g_wsAllFilenames
  82. //--
  83. //////////////////////////////////////////////////////////////////////////////
  84. int __cdecl CompareStringToFilename(const void *pKey, const void *pFilenameIndex)
  85. {
  86. WCHAR *wszKey = (WCHAR*)pKey;
  87. DWORD *dwElement2 = (DWORD*)pFilenameIndex;
  88. //Create a wstring out of the key
  89. wstring wstr1(wszKey);
  90. //Create a wstring out of the short filename to compare to
  91. wstring wstr2(g_wsAllFilenames.substr(
  92. *dwElement2,
  93. g_wsAllFilenames.find_first_of(L",", *dwElement2) - *dwElement2));
  94. return _wcsicmp(wstr1.data(), wstr2.data());
  95. }
  96. //////////////////////////////////////////////////////////////////////////////
  97. //++
  98. // RenameFilesFromMSI
  99. //
  100. // Description:
  101. // Recursively traverses a directory to look for files that also are listed
  102. // in sasetup.msi and copy them to a temp directory for CAB generation.
  103. // Files are renamed in the temp directory to the file key name in the msi.
  104. // For example, appmgr.exe might be renamed to F12345_appmgr.exe.
  105. // Files not listed in the MSI are ignored. If a file is listed in the msi,
  106. // but not found on the system, a fatal error occurs after this function exits.
  107. //
  108. // Parameters:
  109. // pwsSourceDir [in] The source directory to search for files and recursively call
  110. // this function with its subdirectories (no trailing backslash)
  111. // pwsDestDir [in] The temp directory where the files are copied to for CAB creation
  112. // adwLongAndShortFilenameOffsets [in] Array of pointers to the offsets in
  113. // g_wsAllFilenames where the filenames can be found.
  114. // The long filename might be: F12345_appmgr.exe
  115. // The short filename might be: appmgr.exe (simply an offset to the
  116. // first _ in the long filename)
  117. // dwNumRecords [in] The total number of files in the File table of sasetup.msi
  118. // dwNumFilesCopied [in, out] The running total of the number of files copied to the
  119. // temp directory
  120. // dwLevel [in] The level of recursion, used for debug printing
  121. //--
  122. //////////////////////////////////////////////////////////////////////////////
  123. HRESULT RenameFilesFromMSI(const LPCWSTR pwsSourceDir,
  124. const LPCWSTR pwsDestDir,
  125. const DWORD **adwLongAndShortFilenameOffsets,
  126. const DWORD dwNumRecords,
  127. DWORD &dwNumFilesCopied,
  128. const DWORD dwLevel)
  129. {
  130. HRESULT hr = S_OK;
  131. wstring wsCurrentDir(pwsSourceDir);
  132. wsCurrentDir += L"\\";
  133. wstring wsSearch(pwsSourceDir);
  134. wsSearch += L"\\*.*";
  135. WIN32_FIND_DATA FindFileData;
  136. HANDLE hFind = FindFirstFile(wsSearch.data(), &FindFileData);
  137. if (hFind == INVALID_HANDLE_VALUE)
  138. {
  139. //printf("%d: Invalid Handle Value: %ws", dwLevel, pwsSourceDir);
  140. }
  141. else
  142. {
  143. //printf("%d: Directory: %ws", dwLevel, pwsSourceDir);
  144. BOOL bValidFile = TRUE;
  145. while (bValidFile)
  146. {
  147. if (wcscmp(FindFileData.cFileName, L".") != 0 && wcscmp(FindFileData.cFileName, L"..") != 0)
  148. {
  149. //
  150. // If it's a directory, recurse down the directory structure.
  151. // If it's a file, rename it if it's found in the MSI.
  152. //
  153. if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  154. {
  155. wstring wsChildDir(pwsSourceDir);
  156. wsChildDir += L"\\";
  157. wsChildDir += FindFileData.cFileName;
  158. hr = RenameFilesFromMSI(wsChildDir.data(),
  159. pwsDestDir,
  160. adwLongAndShortFilenameOffsets,
  161. dwNumRecords,
  162. dwNumFilesCopied,
  163. dwLevel + 1);
  164. if (FAILED(hr))
  165. {
  166. break;
  167. }
  168. }
  169. else
  170. {
  171. //printf("%d: File: %ws", dwLevel, FindFileData.cFileName);
  172. DWORD *pFilenameIndex = (DWORD*) bsearch(FindFileData.cFileName,
  173. (void*)adwLongAndShortFilenameOffsets,
  174. dwNumRecords,
  175. sizeof(DWORD)*ELEMENTS_IN_SORT_ARRAY,
  176. CompareStringToFilename);
  177. //
  178. // Rename the file to the name in the MSI
  179. //
  180. if (pFilenameIndex != NULL)
  181. {
  182. //Get the pointer to the long filename instead of the short filename
  183. pFilenameIndex += 1;
  184. //Create the path and filename for the file for the CAB
  185. wstring wsNewFilename(pwsDestDir);
  186. wsNewFilename += g_wsAllFilenames.substr(*pFilenameIndex,
  187. g_wsAllFilenames.find_first_of(L",", *pFilenameIndex) - *pFilenameIndex);
  188. //Create the path and filename for the original file
  189. wstring wsOldFilename(wsCurrentDir);
  190. wsOldFilename += FindFileData.cFileName;
  191. if (CopyFile(wsOldFilename.data(), wsNewFilename.data(), TRUE))
  192. {
  193. //printf(" Successfully copied file to temp CAB folder");
  194. fwprintf(stdout, L".");
  195. dwNumFilesCopied++;
  196. }
  197. else
  198. {
  199. fwprintf(stdout, L"\nYOU HAVE A DUPLICATE FILE. FAILED copying file: %ws, to temp CAB folder: %ws", wsOldFilename.data(), wsNewFilename.data());
  200. hr = E_FAIL;
  201. break;
  202. }
  203. pFilenameIndex += 1;
  204. if (*pFilenameIndex != 0)
  205. fwprintf(stdout, L"\nERROR: pFilenameIndex = %d", (*pFilenameIndex));
  206. else
  207. (*pFilenameIndex) = 1;//Mark this string as found
  208. }
  209. //else
  210. // printf(" Did NOT find %ws in MSI", FindFileData.cFileName);
  211. }
  212. }
  213. if (!FindNextFile(hFind, &FindFileData))
  214. {
  215. bValidFile = FALSE;
  216. }
  217. }
  218. FindClose(hFind);
  219. }
  220. return hr;
  221. }
  222. //////////////////////////////////////////////////////////////////////////////
  223. //++
  224. // ReadMSIFilenamesAndRenameFiles
  225. //
  226. // Description:
  227. // Opens sasetup.msi in the given source directory, reads all the filenames
  228. // in the File table, sorts the filenames, then calls RenameFilesFromMSI
  229. // to copy and rename the files that will be placed in the CAB and streamed
  230. // into the MSI.
  231. //
  232. // Parameters:
  233. // pwsSakSourceDir [in] The source directory where sasetup.msi is found, with
  234. // no trailing backslash
  235. // ie. C:\binaries.x86fre\sakit
  236. // pwsTempCabDir [in] The directory where all files should be copied to that
  237. // will be packaged in the CAB
  238. //--
  239. //////////////////////////////////////////////////////////////////////////////
  240. HRESULT ReadMSIFilenamesAndRenameFiles(LPCWSTR pwsSakSourceDir, LPCWSTR pwsTempCabDir)
  241. {
  242. wstring wsMsiFilename(pwsSakSourceDir);
  243. wsMsiFilename += L"\\";
  244. wsMsiFilename += MSI_FILENAME;
  245. HRESULT hr = E_FAIL;
  246. fwprintf(stdout, L"\nOpen MSI file: %ws\n", wsMsiFilename.data());
  247. MSIHANDLE hMSI = NULL;
  248. MSIHANDLE hView = NULL;
  249. do
  250. {
  251. //
  252. // Open sasetup.msi
  253. //
  254. UINT rVal = MsiOpenDatabase(wsMsiFilename.data(), MSIDBOPEN_READONLY, &hMSI);
  255. if (rVal != ERROR_SUCCESS)
  256. {
  257. fwprintf(stdout, L"\nFailed opening MSI");
  258. break;
  259. }
  260. //printf("Successfully opened MSI");
  261. //
  262. // Query the msi for the filenames
  263. //
  264. rVal = MsiDatabaseOpenView(hMSI, FILENAME_QUERY, &hView);
  265. if (rVal != ERROR_SUCCESS)
  266. {
  267. fwprintf(stdout, L"\nFailed query to retrieve filenames");
  268. break;
  269. }
  270. //printf("Successfully queried the filenames");
  271. //
  272. // Finalize the query for the filenames
  273. //
  274. rVal = MsiViewExecute(hView, 0);
  275. if (rVal != ERROR_SUCCESS)
  276. {
  277. fwprintf(stdout, L"\nFailed query to finalize the query");
  278. break;
  279. }
  280. //printf("Successfully finalized the query");
  281. //
  282. // Extract the filenames from the MSI one by one
  283. //
  284. DWORD adwLongAndShortFilenameOffsets[MAX_FILES][ELEMENTS_IN_SORT_ARRAY];
  285. MSIHANDLE hRecord;
  286. rVal = MsiViewFetch(hView, &hRecord);
  287. DWORD dwRecord = 0;
  288. DWORD dwOffset = 0;
  289. hr = S_OK;
  290. while (rVal == ERROR_SUCCESS)
  291. {
  292. if (dwRecord >= MAX_FILES)
  293. {
  294. fwprintf(stdout, L"\nExceeded maximum number of files");
  295. hr = E_FAIL;
  296. break;
  297. }
  298. WCHAR wszFilename[MAX_PATH];
  299. DWORD dwLength = MAX_PATH;
  300. rVal = MsiRecordGetString(hRecord, 1, wszFilename, &dwLength);
  301. if (rVal != ERROR_SUCCESS)
  302. {
  303. fwprintf(stdout, L"\nCOULD NOT fetch record %d", dwRecord);
  304. hr = E_FAIL;
  305. break;
  306. }
  307. wstring wsFilename(wszFilename);
  308. wstring wsNewFilename;
  309. int nUnderscore = wsFilename.find_first_of(L"_");
  310. if (nUnderscore == -1)
  311. {
  312. fwprintf(stdout, L"\nCOULD NOT find underscore in %ws", wsFilename.data());
  313. hr = E_FAIL;
  314. break;
  315. }
  316. adwLongAndShortFilenameOffsets[dwRecord][LONG_FILENAME] = dwOffset;
  317. adwLongAndShortFilenameOffsets[dwRecord][SHORT_FILENAME]= dwOffset + nUnderscore + 1;//Add 1 to skip the _
  318. adwLongAndShortFilenameOffsets[dwRecord][FOUND_FILE]= 0;
  319. //wsNewFilename = wsFilename.substr(nUnderscore+1);
  320. g_wsAllFilenames += wsFilename + L",";
  321. MsiCloseHandle(hRecord);
  322. rVal = MsiViewFetch(hView, &hRecord);
  323. dwRecord++;
  324. dwOffset += dwLength + 1;//Add 1 for the comma
  325. }
  326. //If the While loop failed, break out of the function
  327. if (FAILED(hr))
  328. {
  329. break;
  330. }
  331. //
  332. // Sort by the short filename
  333. //
  334. qsort(&adwLongAndShortFilenameOffsets[0][0], dwRecord, sizeof(DWORD)*ELEMENTS_IN_SORT_ARRAY, CompareFilenames);
  335. //Print out the sorted list of filenames
  336. //for (int i=0; i < dwRecord; i++)
  337. //{
  338. // //Get the substrings and print them to make sure they were restored correctly
  339. // DWORD dwLongIndex = adwLongAndShortFilenameOffsets[i][LONG_FILENAME];
  340. // DWORD dwShortIndex = adwLongAndShortFilenameOffsets[i][SHORT_FILENAME];
  341. // wstring wsLongName(g_wsAllFilenames.substr(dwLongIndex, g_wsAllFilenames.find_first_of(L",", dwLongIndex) - dwLongIndex));
  342. // wstring wsShortName(g_wsAllFilenames.substr(dwShortIndex, g_wsAllFilenames.find_first_of(L",", dwShortIndex) - dwShortIndex));
  343. // //printf("%d: %ws -> %ws", i, wsLongName.data(), wsShortName.data());
  344. //}
  345. // Create the directory used to search for files to put in the CAB
  346. wstring wsSourceDir(pwsSakSourceDir);
  347. //Create the directory used to copy and rename the files temporarily for CAB creation
  348. wstring wsTmpCabDir(pwsTempCabDir);
  349. wsTmpCabDir += L"\\";
  350. if (!CreateDirectory(wsTmpCabDir.data(), NULL)
  351. && GetLastError() != ERROR_ALREADY_EXISTS)
  352. {
  353. fwprintf(stdout, L"\nCOULD NOT create directory %ws", wsTmpCabDir.data());
  354. hr = E_FAIL;
  355. break;
  356. }
  357. DWORD dwNumFilesForCAB = 0;
  358. //Copy and rename files for the CAB file
  359. hr = RenameFilesFromMSI(wsSourceDir.data(),
  360. wsTmpCabDir.data(),
  361. (const DWORD**)adwLongAndShortFilenameOffsets,
  362. dwRecord,
  363. dwNumFilesForCAB,
  364. 1);
  365. if (FAILED(hr))
  366. {
  367. break;
  368. }
  369. //
  370. // Check to make sure there are the same number of files copied for CAB creation
  371. // as there are in the MSI table
  372. //
  373. if (dwNumFilesForCAB != dwRecord)
  374. {
  375. fwprintf(stdout, L"\nERROR: %d files listed in the MSI were not found in the source directory", dwRecord - dwNumFilesForCAB);
  376. for (int i=0; i < dwRecord; i++)
  377. {
  378. if (adwLongAndShortFilenameOffsets[i][FOUND_FILE] != 1)
  379. {
  380. DWORD dwIndex = adwLongAndShortFilenameOffsets[i][LONG_FILENAME];
  381. DWORD dwFirst = g_wsAllFilenames.find_first_of(L",", dwIndex);
  382. fwprintf(stdout, L"\n Missing file: %ws",
  383. g_wsAllFilenames.substr(dwIndex, dwFirst - dwIndex).data());
  384. }
  385. }
  386. hr = E_FAIL;
  387. break;
  388. }
  389. } while (false);
  390. if (hMSI)
  391. {
  392. MsiCloseHandle(hMSI);
  393. }
  394. if (hView)
  395. {
  396. MsiCloseHandle(hView);
  397. }
  398. return hr;
  399. }
  400. //////////////////////////////////////////////////////////////////////////////
  401. //++
  402. // WinMain
  403. //
  404. // Description:
  405. // Main entry point to prepare the files for cab generation
  406. // One command line argument is expected, which is the base directory
  407. // where everything is located.
  408. //--
  409. //////////////////////////////////////////////////////////////////////////////
  410. int APIENTRY WinMain(HINSTANCE hInstance,
  411. HINSTANCE hPrevInstance,
  412. LPSTR lpCmdLine,
  413. int nCmdShow)
  414. {
  415. HRESULT hr = E_FAIL;
  416. LPWSTR *argvCommandLine = NULL;
  417. do
  418. {
  419. int nArgs;
  420. argvCommandLine = CommandLineToArgvW(
  421. GetCommandLine(),// pointer to a command-line string
  422. &nArgs); // receives the argument count
  423. if (argvCommandLine == NULL || nArgs < 3)
  424. {
  425. fwprintf(stdout, L"\nCommand line syntax: PrepareCabFiles.exe <sakitBuildDir> <TempCabDir>\n");
  426. break;
  427. }
  428. fwprintf(stdout, L"\nSAK build directory: %ws", argvCommandLine[1]);
  429. fwprintf(stdout, L"\nTemp Cab Directory: %ws", argvCommandLine[2]);
  430. hr = ReadMSIFilenamesAndRenameFiles(argvCommandLine[1], argvCommandLine[2]);
  431. if (FAILED(hr))
  432. {
  433. fwprintf(stdout, L"\nThere was a FAILURE\n\n");
  434. }
  435. else
  436. {
  437. fwprintf(stdout, L"\nSUCCESS!!\n\n");
  438. }
  439. } while (false);
  440. GlobalFree(argvCommandLine);
  441. if (FAILED(hr))
  442. return 1;//The build script expects a 1 if an error occurred.
  443. return 0;
  444. }