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.

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