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.

791 lines
22 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. ntsetup\winnt32\dll\sxs.h
  5. Abstract:
  6. SideBySide support in the winnt32 phase of ntsetup.
  7. Author:
  8. Jay Krell (JayKrell) March 2001
  9. Revision History:
  10. Environment:
  11. winnt32 -- Win9x ANSI (down to Win95gold) or NT Unicode
  12. libcmt statically linked in, _tcs* ok
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. #include <stdarg.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include "tchar.h"
  20. #include "sxs.h"
  21. #include "msg.h"
  22. #define NUMBER_OF(x) (sizeof(x)/sizeof((x)[0]))
  23. #define CHECK_FOR_MINIMUM_ASSEMBLIES 0
  24. #define CHECK_FOR_OBSOLETE_ASSEMBLIES 1
  25. #define EMPTY_LEAF_DIRECTORIES_ARE_OK 1
  26. const static TCHAR rgchPathSeperators[] = TEXT("\\/");
  27. #define PRESERVE_LAST_ERROR(code) \
  28. do { DWORD PreserveLastError = Success ? NO_ERROR : GetLastError(); \
  29. do { code ; } while(0); \
  30. if (!Success) SetLastError(PreserveLastError); \
  31. } while(0)
  32. #define StringLength _tcslen
  33. #define StringCopy _tcscpy
  34. #define StringAppend _tcscat
  35. #define FindLastChar _tcsrchr
  36. BOOL
  37. SxspIsPathSeperator(
  38. TCHAR ch
  39. )
  40. {
  41. return (_tcschr(rgchPathSeperators, ch) != NULL);
  42. }
  43. VOID
  44. __cdecl
  45. SxspDebugOut(
  46. LPCTSTR Format,
  47. ...
  48. )
  49. {
  50. //
  51. // DebugLog directly doesn't quite work out, because
  52. // it wants %1 formatting, where we have GetLastError().
  53. // Unless we duplicate all of our messages..
  54. //
  55. TCHAR Buffer[2000];
  56. va_list args;
  57. const BOOL Success = TRUE; // PRESERVE_LAST_ERROR
  58. Buffer[0] = 0;
  59. va_start(args, Format);
  60. _vsntprintf(Buffer, NUMBER_OF(Buffer), Format, args);
  61. va_end(args);
  62. if (Buffer[0] != 0)
  63. {
  64. LPTSTR End;
  65. SIZE_T Length;
  66. Buffer[NUMBER_OF(Buffer) - 1] = 0;
  67. PRESERVE_LAST_ERROR(OutputDebugString(Buffer));
  68. Length = StringLength(Buffer);
  69. End = Buffer + Length - 1;
  70. while (*End == ' ' || *End == '\t' || *End == '\n' || *End == '\r')
  71. *End-- = 0;
  72. DebugLog(Winnt32LogError, TEXT("%1"), 0, Buffer);
  73. }
  74. }
  75. VOID
  76. SxspRemoveTrailingPathSeperators(
  77. LPTSTR s
  78. )
  79. {
  80. if (s != NULL && s[0] != 0)
  81. {
  82. LPTSTR t;
  83. //
  84. // This is inefficient, in order to be mbcs correct,
  85. // but there aren't likely to be more than one or two.
  86. //
  87. while ((t = _tcsrchr(s, rgchPathSeperators[0])) != NULL && *(t + 1) == 0)
  88. {
  89. *t = 0;
  90. }
  91. }
  92. }
  93. VOID
  94. SxspGetPathBaseName(
  95. LPCTSTR Path,
  96. LPTSTR Base
  97. )
  98. {
  99. LPCTSTR Dot = FindLastChar(Path, '.');
  100. LPCTSTR Slash = FindLastChar(Path, rgchPathSeperators[0]);
  101. //
  102. // beware \foo.txt\bar
  103. // beware \bar
  104. // beware bar
  105. // beware .bar
  106. // beware \.bar
  107. //
  108. *Base = 0;
  109. if (Slash == NULL)
  110. Slash = Path;
  111. else
  112. Slash += 1;
  113. if (Dot == NULL || Dot < Slash)
  114. Dot = Path + StringLength(Path);
  115. CopyMemory(Base, Slash, (Dot - Slash) * sizeof(*Base));
  116. Base[Dot - Slash] = 0;
  117. }
  118. BOOL
  119. SxspIsDotOrDotDot(
  120. PCTSTR s
  121. )
  122. {
  123. return (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)));
  124. }
  125. const static LPCTSTR DotManifestExtensions[] =
  126. { TEXT(".Man"), TEXT(".Dll"), TEXT(".Manifest"), TEXT(".Policy") };
  127. const static LPCTSTR DotCatalogExtensions[] = { TEXT(".Cat"), TEXT(".Ca_") };
  128. BOOL
  129. SxspGetSameNamedFileWithExtensionFromList(
  130. PSXS_CHECK_LOCAL_SOURCE Context,
  131. LPCTSTR Directory,
  132. CONST LPCTSTR Extensions[],
  133. SIZE_T NumberOfExtensions,
  134. LPTSTR File
  135. )
  136. {
  137. const static TCHAR T_FUNCTION[] = TEXT("SxspGetSameNamedFileWithExtensionFromList");
  138. LPTSTR FileEnd = NULL;
  139. PTSTR Base = NULL;
  140. DWORD FileAttributes = 0;
  141. SIZE_T i = 0;
  142. BOOL Success = FALSE;
  143. File[0] = 0;
  144. StringCopy(File, Directory);
  145. SxspRemoveTrailingPathSeperators(File);
  146. Base = File + StringLength(File) + 1;
  147. SxspGetPathBaseName(Directory, Base);
  148. Base[-1] = rgchPathSeperators[0];
  149. FileEnd = Base + StringLength(Base);
  150. for (i = 0 ; i != NumberOfExtensions ; ++i)
  151. {
  152. StringCopy(FileEnd, Extensions[i]);
  153. FileAttributes = GetFileAttributes(File);
  154. if (FileAttributes != INVALID_FILE_ATTRIBUTES)
  155. {
  156. if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  157. {
  158. return TRUE;
  159. }
  160. }
  161. else
  162. {
  163. const DWORD LastError = GetLastError();
  164. if (LastError != ERROR_FILE_NOT_FOUND
  165. && LastError != ERROR_PATH_NOT_FOUND
  166. )
  167. {
  168. SxspDebugOut(
  169. TEXT("SXS: %s(%s):GetFileAttributes(%s):%lu\n"),
  170. T_FUNCTION,
  171. Directory,
  172. File,
  173. LastError
  174. );
  175. MessageBoxFromMessage(
  176. Context->ParentWindow,
  177. LastError,
  178. TRUE,
  179. AppTitleStringId,
  180. MB_OK | MB_ICONERROR | MB_TASKMODAL
  181. );
  182. File[0] = 0;
  183. Success = FALSE;
  184. goto Exit;
  185. }
  186. }
  187. }
  188. File[0] = 0;
  189. Success = TRUE;
  190. Exit:
  191. return Success;
  192. }
  193. BOOL
  194. SxspCheckFile(
  195. PSXS_CHECK_LOCAL_SOURCE Context,
  196. LPCTSTR File
  197. )
  198. {
  199. BYTE Buffer[512];
  200. static BYTE Zeroes[sizeof(Buffer)];
  201. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  202. DWORD BytesRead = 0;
  203. BOOL Success = FALSE;
  204. FileHandle = CreateFile(
  205. File,
  206. GENERIC_READ,
  207. FILE_SHARE_READ,
  208. NULL,
  209. OPEN_EXISTING,
  210. FILE_ATTRIBUTE_NORMAL,
  211. NULL
  212. );
  213. if (FileHandle == INVALID_HANDLE_VALUE)
  214. {
  215. CONST DWORD LastError = GetLastError();
  216. SxspDebugOut(TEXT("SXS: unable to open file %s, error %lu\n"), File, LastError);
  217. MessageBoxFromMessageAndSystemError(
  218. Context->ParentWindow,
  219. MSG_SXS_ERROR_FILE_OPEN_FAILED,
  220. GetLastError(),
  221. AppTitleStringId,
  222. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  223. File
  224. );
  225. Success = FALSE;
  226. goto Exit;
  227. }
  228. if (!ReadFile(FileHandle, Buffer, sizeof(Buffer), &BytesRead, NULL))
  229. {
  230. CONST DWORD LastError = GetLastError();
  231. SxspDebugOut(TEXT("SXS: ReadFile(%s) failed %lu\n"), File, LastError);
  232. MessageBoxFromMessageAndSystemError(
  233. Context->ParentWindow,
  234. MSG_SXS_ERROR_FILE_READ_FAILED,
  235. LastError,
  236. AppTitleStringId,
  237. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  238. File
  239. );
  240. Success = FALSE;
  241. goto Exit;
  242. }
  243. if (memcmp(Buffer, Zeroes, BytesRead) == 0)
  244. {
  245. SxspDebugOut(TEXT("SXS: File %s is all zeroes\n"), File);
  246. MessageBoxFromMessage(
  247. Context->ParentWindow,
  248. MSG_SXS_ERROR_FILE_IS_ALL_ZEROES,
  249. FALSE,
  250. AppTitleStringId,
  251. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  252. File
  253. );
  254. Success = FALSE;
  255. goto Exit;
  256. }
  257. Success = TRUE;
  258. Exit:
  259. if (FileHandle != INVALID_HANDLE_VALUE)
  260. CloseHandle(FileHandle);
  261. return Success;
  262. }
  263. BOOL
  264. SxspCheckLeafDirectory(
  265. PSXS_CHECK_LOCAL_SOURCE Context,
  266. LPCTSTR Directory
  267. )
  268. {
  269. TCHAR File[MAX_PATH];
  270. BOOL Success = TRUE; // NOTE backwardness
  271. const static struct {
  272. const LPCTSTR* Extensions;
  273. SIZE_T NumberOfExtensions;
  274. ULONG Error;
  275. } x[] = {
  276. {
  277. DotManifestExtensions,
  278. NUMBER_OF(DotManifestExtensions),
  279. MSG_SXS_ERROR_DIRECTORY_IS_MISSING_MANIFEST
  280. },
  281. {
  282. DotCatalogExtensions,
  283. NUMBER_OF(DotCatalogExtensions),
  284. MSG_SXS_ERROR_DIRECTORY_IS_MISSING_CATALOG
  285. }
  286. };
  287. SIZE_T i;
  288. for (i = 0 ; i != NUMBER_OF(x) ; ++i)
  289. {
  290. if (SxspGetSameNamedFileWithExtensionFromList(Context, Directory, x[i].Extensions, x[i].NumberOfExtensions, File))
  291. {
  292. if (File[0] == 0)
  293. {
  294. TCHAR Base[MAX_PATH];
  295. SxspGetPathBaseName(Directory, Base);
  296. SxspDebugOut(TEXT("SXS: Missing manifest or catalog in %s\n"), Directory);
  297. MessageBoxFromMessage(
  298. Context->ParentWindow,
  299. x[i].Error,
  300. FALSE,
  301. AppTitleStringId,
  302. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  303. Directory,
  304. Base
  305. );
  306. Success = FALSE;
  307. //goto Exit;
  308. // keep looping, to possibly report more errors (manifest and catalog)
  309. }
  310. else
  311. {
  312. if (!SxspCheckFile(Context, File))
  313. Success = FALSE;
  314. // keep looping, to possibly report more errors
  315. }
  316. }
  317. }
  318. // NOTE don't set Success = TRUE here.
  319. //Exit:
  320. return Success;
  321. }
  322. BOOL
  323. SxspFindAndCheckLeaves(
  324. PSXS_CHECK_LOCAL_SOURCE Context,
  325. LPTSTR Directory,
  326. SIZE_T DirectoryLength,
  327. LPWIN32_FIND_DATA FindData
  328. )
  329. {
  330. const static TCHAR T_FUNCTION[] = TEXT("SxspFindAndCheckLeaves");
  331. HANDLE FindHandle = INVALID_HANDLE_VALUE;
  332. BOOL ChildrenDirectories = FALSE;
  333. BOOL ChildrenFiles = FALSE;
  334. BOOL Success = TRUE;
  335. //
  336. // first enumerate looking for any directories
  337. // recurse on each one
  338. // if none found, check it as a leaf
  339. //
  340. ConcatenatePaths(Directory, TEXT("*"), MAX_PATH);
  341. FindHandle = FindFirstFile(Directory, FindData);
  342. if (FindHandle == INVALID_HANDLE_VALUE)
  343. {
  344. CONST DWORD LastError = GetLastError();
  345. //
  346. // we already did a successful GetFileAttributes on this and
  347. // found it was a directory, so no error is expected here
  348. //
  349. SxspDebugOut(
  350. TEXT("SXS: %s(%s),FindFirstFile:%d\n"),
  351. T_FUNCTION, Directory, LastError
  352. );
  353. MessageBoxFromMessage(
  354. Context->ParentWindow,
  355. LastError,
  356. TRUE,
  357. AppTitleStringId,
  358. MB_OK | MB_ICONERROR | MB_TASKMODAL
  359. );
  360. Success = FALSE;
  361. goto Exit;
  362. }
  363. else
  364. {
  365. do
  366. {
  367. if (SxspIsDotOrDotDot(FindData->cFileName))
  368. continue;
  369. if (FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  370. {
  371. ChildrenDirectories = TRUE;
  372. Directory[DirectoryLength] = 0;
  373. ConcatenatePaths(Directory, FindData->cFileName, MAX_PATH);
  374. if (!SxspFindAndCheckLeaves(
  375. Context,
  376. Directory,
  377. StringLength(Directory),
  378. FindData
  379. ))
  380. {
  381. Success = FALSE;
  382. // keep looping, in order to possibly report more errors
  383. }
  384. }
  385. else
  386. {
  387. ChildrenFiles = TRUE;
  388. }
  389. }
  390. while (FindNextFile(FindHandle, FindData));
  391. FindClose(FindHandle);
  392. }
  393. if (!ChildrenDirectories
  394. #if EMPTY_LEAF_DIRECTORIES_ARE_OK /* currently yes */
  395. && ChildrenFiles
  396. #endif
  397. )
  398. {
  399. Directory[DirectoryLength] = 0;
  400. if (!SxspCheckLeafDirectory(Context, Directory))
  401. Success = FALSE;
  402. }
  403. #if !EMPTY_LEAF_DIRECTORIES_ARE_OK /* currently no */
  404. if (!ChildrenDirectories && !ChildrenFiles)
  405. {
  406. // report an error
  407. }
  408. #endif
  409. // NOTE do not set Success = TRUE here
  410. Exit:
  411. return Success;
  412. }
  413. #if CHECK_FOR_MINIMUM_ASSEMBLIES /* 0 */
  414. //
  415. // This data is very specific to Windows 5.1.
  416. //
  417. // All of these should be under all roots, assuming
  418. // corporate deployers do not add roots to dosnet.inf.
  419. //
  420. const static LPCTSTR MinimumAssemblies[] =
  421. {
  422. TEXT("6000\\Msft\\Windows\\Common\\Controls"),
  423. TEXT("1000\\Msft\\Windows\\GdiPlus"),
  424. TEXT("5100\\Msft\\Windows\\System\\Default")
  425. };
  426. #endif
  427. #if CHECK_FOR_OBSOLETE_ASSEMBLIES
  428. //
  429. // This data is specific to Windows 5.1.
  430. //
  431. // None of these should be under any root, assuming
  432. // corporate deployers don't use these names.
  433. //
  434. // People internally end up with obsolete assemblies because they
  435. // copy newer drops on top of older drops, without deleting what is
  436. // no longer in the drop.
  437. //
  438. const static LPCTSTR ObsoleteAssemblies[] =
  439. {
  440. // This assembly was reversioned very early in its life, from 1.0.0.0 to 5.1.0.0.
  441. TEXT("1000\\Msft\\Windows\\System\\Default")
  442. };
  443. #endif
  444. BOOL
  445. SxspCheckRoot(
  446. PSXS_CHECK_LOCAL_SOURCE Context,
  447. LPCTSTR Root
  448. )
  449. {
  450. const static TCHAR T_FUNCTION[] = TEXT("SxspCheckRoot");
  451. DWORD FileAttributes = 0;
  452. DWORD LastError = 0;
  453. HANDLE FindHandle = INVALID_HANDLE_VALUE;
  454. WIN32_FIND_DATA FindData;
  455. TCHAR RootStar[MAX_PATH];
  456. SIZE_T RootLength = 0;
  457. BOOL Empty = TRUE;
  458. BOOL Success = TRUE; // NOTE the backwardness
  459. SIZE_T i = 0;
  460. StringCopy(RootStar, Root);
  461. RootLength = StringLength(Root);
  462. //
  463. // check that the root exists
  464. //
  465. FileAttributes = GetFileAttributes(Root);
  466. if (FileAttributes == INVALID_FILE_ATTRIBUTES)
  467. {
  468. Success = FALSE;
  469. LastError = GetLastError();
  470. SxspDebugOut(
  471. TEXT("SXS: %s(%s),GetFileAttributes:%d\n"),
  472. T_FUNCTION, Root, LastError
  473. );
  474. //if (LastError == ERROR_FILE_NOT_FOUND || LastError == ERROR_PATH_NOT_FOUND)
  475. {
  476. MessageBoxFromMessageAndSystemError(
  477. Context->ParentWindow,
  478. MSG_SXS_ERROR_REQUIRED_DIRECTORY_MISSING,
  479. LastError,
  480. AppTitleStringId,
  481. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  482. Root
  483. );
  484. goto Exit; // abort, otherwise we get many cascades, guaranteed
  485. }
  486. //else
  487. {
  488. /*
  489. MessageBoxFromMessage(
  490. Context->ParentWindow,
  491. LastError,
  492. TRUE,
  493. AppTitleStringId,
  494. MB_OK | MB_ICONERROR | MB_TASKMODAL
  495. );
  496. goto Exit;
  497. */
  498. }
  499. }
  500. //
  501. // check that the root is a directory
  502. //
  503. if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  504. {
  505. SxspDebugOut(TEXT("SXS: %s is file instead of directory\n"), Root);
  506. MessageBoxFromMessage(
  507. Context->ParentWindow,
  508. MSG_SXS_ERROR_FILE_INSTEAD_OF_DIRECTORY,
  509. FALSE,
  510. AppTitleStringId,
  511. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  512. Root
  513. );
  514. Success = FALSE;
  515. goto Exit;
  516. }
  517. #if CHECK_FOR_MINIMUM_ASSEMBLIES /* We do NOT this, it is buggy wrt asms/wasms. */
  518. //
  519. // ensure all the mandatory assemblies exist
  520. // NOTE this check is only partial, but a more complete
  521. // check will be done when we enumerate and recurse
  522. //
  523. for (i = 0 ; i != NUMBER_OF(MinimumAssemblies) ; ++i)
  524. {
  525. RootStar[RootLength] = 0;
  526. ConcatenatePaths(RootStar, MinimumAssemblies[i], MAX_PATH);
  527. FileAttributes = GetFileAttributes(RootStar);
  528. if (FileAttributes == INVALID_FILE_ATTRIBUTES)
  529. {
  530. const DWORD LastError = GetLastError();
  531. SxspDebugOut(TEXT("SXS: required directory %s missing, or error %lu.\n"), RootStar, LastError);
  532. MessageBoxFromMessageAndSystemError(
  533. Context->ParentWindow,
  534. MSG_SXS_ERROR_REQUIRED_DIRECTORY_MISSING,
  535. LastError,
  536. AppTitleStringId,
  537. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  538. RootStar
  539. );
  540. Success = FALSE;
  541. // keep running, look for more errors
  542. }
  543. if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  544. {
  545. SxspDebugOut(TEXT("SXS: %s is file instead of directory\n"), RootStar);
  546. MessageBoxFromMessage(
  547. Context->ParentWindow,
  548. MSG_SXS_ERROR_FILE_INSTEAD_OF_DIRECTORY,
  549. FALSE,
  550. AppTitleStringId,
  551. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  552. RootStar
  553. );
  554. Success = FALSE;
  555. }
  556. }
  557. #endif
  558. #if CHECK_FOR_OBSOLETE_ASSEMBLIES /* We do this; it somewhat against longstanding principle. */
  559. //
  560. // ensure none of the obsolete assemblies exist
  561. //
  562. for (i = 0 ; i != NUMBER_OF(ObsoleteAssemblies) ; ++i)
  563. {
  564. RootStar[RootLength] = 0;
  565. ConcatenatePaths(RootStar, ObsoleteAssemblies[i], MAX_PATH);
  566. FileAttributes = GetFileAttributes(RootStar);
  567. if (FileAttributes != INVALID_FILE_ATTRIBUTES)
  568. {
  569. //
  570. // We don't care if it's a file or directory or what
  571. // the directory contains. It's a fatal error no matter what.
  572. //
  573. SxspDebugOut(TEXT("SXS: obsolete %s present\n"), RootStar);
  574. MessageBoxFromMessage(
  575. Context->ParentWindow,
  576. MSG_SXS_ERROR_OBSOLETE_DIRECTORY_PRESENT,
  577. FALSE,
  578. AppTitleStringId,
  579. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  580. RootStar
  581. );
  582. Success = FALSE;
  583. // keep running, look for more errors
  584. }
  585. }
  586. #endif
  587. //
  588. // enumerate and recurse
  589. //
  590. RootStar[RootLength] = 0;
  591. StringCopy(RootStar, Root);
  592. ConcatenatePaths(RootStar, TEXT("*"), MAX_PATH);
  593. FindHandle = FindFirstFile(RootStar, &FindData);
  594. if (FindHandle == INVALID_HANDLE_VALUE)
  595. {
  596. //
  597. // An error here is unexplainable.
  598. //
  599. CONST DWORD LastError = GetLastError();
  600. SxspDebugOut(
  601. TEXT("SXS: %s(%s), FindFirstFile(%s):%d\n"),
  602. T_FUNCTION, Root, RootStar, LastError
  603. );
  604. MessageBoxFromMessage(
  605. Context->ParentWindow,
  606. LastError,
  607. TRUE,
  608. AppTitleStringId,
  609. MB_OK | MB_ICONERROR | MB_TASKMODAL
  610. );
  611. Success = FALSE;
  612. goto Exit;
  613. }
  614. do
  615. {
  616. if (SxspIsDotOrDotDot(FindData.cFileName))
  617. continue;
  618. //
  619. // REVIEW
  620. // I think this is too strict.
  621. // Corporate deployers might drop a readme.txt here.
  622. //
  623. if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  624. {
  625. //RootStar[RootLength] = 0;
  626. //Context->ReportErrorMessage(Context, MSG_SXS_ERROR_NON_LEAF_DIRECTORY_CONTAINS_FILE, RootStar, FindData.cFileName);
  627. }
  628. else
  629. {
  630. //
  631. // now enumerate recursively, checking each leaf
  632. // munge the recursion slightly to save a function and stack space
  633. // (usually we'd start at the root, instead of its first generation children)
  634. //
  635. Empty = FALSE;
  636. RootStar[RootLength] = 0;
  637. ConcatenatePaths(RootStar, FindData.cFileName, MAX_PATH);
  638. if (!SxspFindAndCheckLeaves(Context, RootStar, StringLength(RootStar), &FindData))
  639. Success = FALSE;
  640. // keep looping, to possibly report more errors
  641. }
  642. } while(FindNextFile(FindHandle, &FindData));
  643. FindClose(FindHandle);
  644. if (Empty)
  645. {
  646. SxspDebugOut(TEXT("SXS: directory %s empty\n"), Root);
  647. MessageBoxFromMessage(
  648. Context->ParentWindow,
  649. MSG_SXS_ERROR_DIRECTORY_EMPTY,
  650. FALSE,
  651. AppTitleStringId,
  652. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  653. Root
  654. );
  655. Success = FALSE;
  656. goto Exit;
  657. }
  658. Exit:
  659. return Success;
  660. }
  661. BOOL
  662. SxsCheckLocalSource(
  663. PSXS_CHECK_LOCAL_SOURCE Parameters
  664. )
  665. /*
  666. Late in winnt32
  667. enumerate ~ls\...\asms
  668. ensure asms is a directory
  669. ensure that everything one level down in asms is a directory (I didn't do this, seems too strict).
  670. enumerate asms recursively
  671. ensure every leaf directory has a .cat with the same base name as the directory
  672. ensure every leaf directory has a .man or .manifest with the same base name as the directory
  673. Read the first 512 bytes of every .cat/.man/.manifest.
  674. Ensure that they are not all zero.
  675. REVIEW also that required exist and obsolete assemblies do not
  676. */
  677. {
  678. ULONG i;
  679. TCHAR FullPath[MAX_PATH];
  680. BOOL Success = TRUE;
  681. TCHAR LocalSourceDrive;
  682. //
  683. // ensure LocalSource is present/valid
  684. //
  685. if (!MakeLocalSource)
  686. return TRUE;
  687. LocalSourceDrive = (TCHAR)towupper(LocalSourceDirectory[0]);
  688. if (LocalSourceDrive != towupper(LocalSourceWithPlatform[0]))
  689. return TRUE;
  690. if (LocalSourceDrive < 'C' || LocalSourceDrive > 'Z')
  691. return TRUE;
  692. //
  693. // flush LocalSource where the Win32 api is simple (NT, not Win9x)
  694. //
  695. if (ISNT())
  696. {
  697. CONST TCHAR LocalSourceDrivePath[] = { '\\', '\\', '.', '\\', LocalSourceDrive, ':', 0 };
  698. CONST HANDLE LocalSourceDriveHandle =
  699. CreateFile(
  700. LocalSourceDrivePath,
  701. GENERIC_READ | GENERIC_WRITE,
  702. FILE_SHARE_READ | FILE_SHARE_WRITE,
  703. NULL,
  704. OPEN_EXISTING,
  705. FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
  706. NULL
  707. );
  708. if (LocalSourceDriveHandle != INVALID_HANDLE_VALUE)
  709. {
  710. FlushFileBuffers(LocalSourceDriveHandle);
  711. CloseHandle(LocalSourceDriveHandle);
  712. }
  713. }
  714. for(i = 0; i != OptionalDirectoryCount; ++i)
  715. {
  716. if ((OptionalDirectoryFlags[i] & OPTDIR_SIDE_BY_SIDE) != 0)
  717. {
  718. MYASSERT(
  719. (OptionalDirectoryFlags[i] & OPTDIR_PLATFORM_INDEP)
  720. ^ (OptionalDirectoryFlags[i] & OPTDIR_ADDSRCARCH)
  721. );
  722. switch (OptionalDirectoryFlags[i] & (OPTDIR_PLATFORM_INDEP | OPTDIR_ADDSRCARCH))
  723. {
  724. case OPTDIR_ADDSRCARCH:
  725. StringCopy(FullPath, LocalSourceWithPlatform);
  726. break;
  727. case OPTDIR_PLATFORM_INDEP:
  728. StringCopy(FullPath, LocalSourceDirectory);
  729. break;
  730. }
  731. ConcatenatePaths(FullPath, OptionalDirectories[i], MAX_PATH);
  732. if (!SxspCheckRoot(Parameters, FullPath))
  733. Success = FALSE;
  734. // keep looping, to possibly report more errors
  735. }
  736. }
  737. return Success;
  738. }