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.

6146 lines
200 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. infload.c
  5. Abstract:
  6. Routines to load and parse INF files, and manipulate data in them.
  7. Author:
  8. Ted Miller (tedm) 13-Jan-1995
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #include <ntverp.h>
  14. //
  15. // Values used when initializing and growing the section, line, and value blocks.
  16. //
  17. #define INITIAL_SECTION_BLOCK_SIZE 50
  18. #define INITIAL_LINE_BLOCK_SIZE 350
  19. #define INITIAL_VALUE_BLOCK_SIZE 1000
  20. #define SECTION_BLOCK_GROWTH 10
  21. #define LINE_BLOCK_GROWTH 100
  22. #define VALUE_BLOCK_GROWTH 500
  23. //
  24. // Define unresolved substitution values for return by ParseValueString and
  25. // ProcessForSubstitutions
  26. //
  27. #define UNRESOLVED_SUBST_NONE (0)
  28. #define UNRESOLVED_SUBST_USER_DIRID (1)
  29. #define UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID (2)
  30. //
  31. // Macros used to quadword-align PNF blocks.
  32. //
  33. #define PNF_ALIGNMENT ((DWORD)8)
  34. #define PNF_ALIGN_MASK (~(DWORD)(PNF_ALIGNMENT - 1))
  35. #define PNF_ALIGN_BLOCK(x) ((x & PNF_ALIGN_MASK) + ((x & ~PNF_ALIGN_MASK) ? PNF_ALIGNMENT : 0))
  36. //
  37. // Structure containing parameters relating to a [strings] section of an INF
  38. // file (used during parsing).
  39. //
  40. typedef struct _STRINGSEC_PARAMS {
  41. PCTSTR Start;
  42. PCTSTR End;
  43. UINT StartLineNumber;
  44. UINT EndLineNumber;
  45. } STRINGSEC_PARAMS, *PSTRINGSEC_PARAMS;
  46. //
  47. // Parse context, used by inf load/parse routines to pass
  48. // state around.
  49. //
  50. typedef struct _PARSE_CONTEXT {
  51. //
  52. // Pointer to the end of the buffer.
  53. //
  54. PCTSTR BufferEnd;
  55. //
  56. // Current line number in the file
  57. //
  58. UINT CurrentLineNumber;
  59. //
  60. // section, line, and value block buffer sizes and current locations.
  61. //
  62. UINT LineBlockUseCount;
  63. UINT ValueBlockUseCount;
  64. UINT SectionBlockSize;
  65. UINT LineBlockSize;
  66. UINT ValueBlockSize;
  67. //
  68. // Value indicating whether we are within a section.
  69. // We always within a section unless the first non-comment line
  70. // of the inf is not section line, and that is an error case.
  71. //
  72. BOOL GotOneSection;
  73. //
  74. // Pointer to the actual inf descriptor
  75. //
  76. PLOADED_INF Inf;
  77. //
  78. // The following field is used solely for the purposes of calling
  79. // ProcessForSubstitutions() after an INF has already been loaded. This is
  80. // necessary when applying user-defined or volatile system DIRIDs to
  81. // unresolved string substitutions. If this flag is TRUE, then the
  82. // aforementioned routine will call pSetupVolatileDirIdToPath for %<x>%
  83. // substrings, instead of its normal (i.e., load-time) processing.
  84. //
  85. BOOL DoVolatileDirIds;
  86. //
  87. // Specifies the directory where this INF is located (if it's an OEM location).
  88. //
  89. PCTSTR InfSourcePath; // may be NULL.
  90. //
  91. // Specifies the drive/directory where the OsLoader is located.
  92. //
  93. PCTSTR OsLoaderPath;
  94. //
  95. // Buffer used during parsing.
  96. //
  97. TCHAR TemporaryString[MAX_INF_STRING_LENGTH+1];
  98. } PARSE_CONTEXT, *PPARSE_CONTEXT;
  99. //
  100. // Declare global string variables used throughout the inf loaders.
  101. //
  102. // These strings are defined in infstr.h:
  103. //
  104. CONST TCHAR pszSignature[] = INFSTR_KEY_SIGNATURE,
  105. pszVersion[] = INFSTR_SECT_VERSION,
  106. pszClass[] = INFSTR_KEY_HARDWARE_CLASS,
  107. pszClassGuid[] = INFSTR_KEY_HARDWARE_CLASSGUID,
  108. pszProvider[] = INFSTR_KEY_PROVIDER,
  109. pszStrings[] = SZ_KEY_STRINGS,
  110. pszLayoutFile[] = SZ_KEY_LAYOUT_FILE,
  111. pszManufacturer[] = INFSTR_SECT_MFG,
  112. pszControlFlags[] = INFSTR_CONTROLFLAGS_SECTION,
  113. pszReboot[] = INFSTR_REBOOT,
  114. pszRestart[] = INFSTR_RESTART,
  115. pszClassInstall32[] = INFSTR_SECT_CLASS_INSTALL_32,
  116. pszAddInterface[] = SZ_KEY_ADDINTERFACE,
  117. pszInterfaceInstall32[] = INFSTR_SECT_INTERFACE_INSTALL_32,
  118. pszAddService[] = SZ_KEY_ADDSERVICE,
  119. pszDelService[] = SZ_KEY_DELSERVICE,
  120. pszCatalogFile[] = INFSTR_KEY_CATALOGFILE;
  121. //
  122. // Other misc. global strings:
  123. //
  124. // Be sure to keep these strings in sync with the strings used
  125. // in inf.h to compute the array size. This is done so that
  126. // we can determine string length by doing a sizeof() instead
  127. // of having to do lstrlen().
  128. //
  129. CONST TCHAR pszDrvDescFormat[] = DISTR_INF_DRVDESCFMT,
  130. pszHwSectionFormat[] = DISTR_INF_HWSECTIONFMT,
  131. pszChicagoSig[] = DISTR_INF_CHICAGOSIG,
  132. pszWindowsNTSig[] = DISTR_INF_WINNTSIG,
  133. pszWindows95Sig[] = DISTR_INF_WIN95SIG,
  134. pszWinSuffix[] = DISTR_INF_WIN_SUFFIX,
  135. pszNtSuffix[] = DISTR_INF_NT_SUFFIX,
  136. pszNtAlphaSuffix[] = DISTR_INF_NTALPHA_SUFFIX,
  137. pszNtX86Suffix[] = DISTR_INF_NTX86_SUFFIX,
  138. pszNtIA64Suffix[] = DISTR_INF_NTIA64_SUFFIX,
  139. pszNtAXP64Suffix[] = DISTR_INF_NTAXP64_SUFFIX,
  140. pszNtAMD64Suffix[] = DISTR_INF_NTAMD64_SUFFIX,
  141. pszPnfSuffix[] = DISTR_INF_PNF_SUFFIX,
  142. pszInfSuffix[] = DISTR_INF_INF_SUFFIX,
  143. pszCatSuffix[] = DISTR_INF_CAT_SUFFIX,
  144. pszServicesSectionSuffix[] = DISTR_INF_SERVICES_SUFFIX,
  145. pszInterfacesSectionSuffix[] = DISTR_INF_INTERFACES_SUFFIX,
  146. pszCoInstallersSectionSuffix[] = DISTR_INF_COINSTALLERS_SUFFIX,
  147. pszLogConfigOverrideSectionSuffix[] = DISTR_INF_LOGCONFIGOVERRIDE_SUFFIX,
  148. pszAlphaSrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_ALPHA,
  149. pszX86SrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_X86,
  150. pszIa64SrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_IA64,
  151. pszAxp64SrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_AXP64,
  152. pszAmd64SrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_AMD64;
  153. DWORD
  154. CreateInfVersionNode(
  155. IN PLOADED_INF Inf,
  156. IN PCTSTR Filename,
  157. IN PFILETIME LastWriteTime
  158. );
  159. BOOL
  160. LoadPrecompiledInf(
  161. IN PCTSTR Filename,
  162. IN PFILETIME LastWriteTime,
  163. IN PCTSTR OsLoaderPath, OPTIONAL
  164. IN DWORD LanguageId,
  165. IN DWORD Flags,
  166. IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
  167. OUT PLOADED_INF *Inf,
  168. OUT PTSTR *InfSourcePathToMigrate, OPTIONAL
  169. OUT PDWORD InfSourcePathToMigrateMediaType, OPTIONAL
  170. OUT PTSTR *InfOriginalNameToMigrate OPTIONAL
  171. );
  172. DWORD
  173. SavePnf(
  174. IN PCTSTR Filename,
  175. IN PLOADED_INF Inf
  176. );
  177. PLOADED_INF
  178. DuplicateLoadedInfDescriptor(
  179. IN PLOADED_INF Inf
  180. );
  181. BOOL
  182. AddUnresolvedSubstToList(
  183. IN PLOADED_INF Inf,
  184. IN UINT ValueOffset,
  185. IN BOOL CaseSensitive
  186. );
  187. BOOL
  188. AlignForNextBlock(
  189. IN HANDLE hFile,
  190. IN DWORD ByteCount
  191. );
  192. BOOL
  193. IsWhitespace(
  194. IN PCTSTR pc
  195. )
  196. /*++
  197. Routine Description:
  198. Determine whether a character is whitespace. Whitespace refers to the ctype
  199. definition.
  200. Arguments:
  201. pc - points to character to be examined.
  202. Return Value:
  203. TRUE if the character is whitespace. FALSE if not.
  204. Note that the nul chracter is not whitespace.
  205. --*/
  206. {
  207. WORD Type;
  208. return(GetStringTypeEx(LOCALE_SYSTEM_DEFAULT,CT_CTYPE1,pc,1,&Type) && (Type & C1_SPACE));
  209. }
  210. VOID
  211. SkipWhitespace(
  212. IN OUT PCTSTR *Location,
  213. IN PCTSTR BufferEnd
  214. )
  215. /*++
  216. Routine Description:
  217. Skip whitespace characters in the input stream. For the purposes of this
  218. routine, newline characters are NOT considered whitespace.
  219. Note that the end-of-stream marker ('\0') IS considered whitespace.
  220. Arguments:
  221. Location - on input, supplies the current location in the input stream.
  222. On output, receives the location in the input stream of the first
  223. non-whitespace character. Note that this may be equal to BufferEnd,
  224. if no whitespace was found, in which case the pointer may be
  225. invalid.
  226. BufferEnd - specifies the address of the end of the buffer (i.e., the
  227. memory address immediately following the buffer's memory range).
  228. Return Value:
  229. None.
  230. --*/
  231. {
  232. while((*Location < BufferEnd) &&
  233. (**Location != TEXT('\n')) &&
  234. (!(**Location) || IsWhitespace(*Location))) {
  235. (*Location)++;
  236. }
  237. }
  238. VOID
  239. SkipLine(
  240. IN OUT PPARSE_CONTEXT Context,
  241. IN OUT PCTSTR *Location
  242. )
  243. /*++
  244. Routine Description:
  245. Skip all remaining characters in the current line, and positions the
  246. input pointer to the first character on the next line.
  247. No whitespace is skipped automatically -- the input pointer may
  248. very well point to whitespace or the end-of-stream marker on exit.
  249. Arguments:
  250. Context - supplies the parse context
  251. Location - on input, supplies the current location in the input stream.
  252. On output, receives the location in the input stream of the first
  253. character on the next line.
  254. Return Value:
  255. None.
  256. --*/
  257. {
  258. PCTSTR BufferEnd = Context->BufferEnd;
  259. while((*Location < BufferEnd) && (**Location != TEXT('\n'))) {
  260. (*Location)++;
  261. }
  262. //
  263. // *Location points at either the newline or end-of-buffer.
  264. // Skip the newline if necessary.
  265. //
  266. if(*Location < BufferEnd) {
  267. Context->CurrentLineNumber++;
  268. (*Location)++;
  269. }
  270. }
  271. BOOL
  272. MergeDuplicateSection(
  273. IN PPARSE_CONTEXT Context
  274. )
  275. {
  276. PLOADED_INF Inf;
  277. PINF_SECTION NewestSection;
  278. PINF_SECTION Section;
  279. UINT Size;
  280. UINT MoveSize;
  281. PVOID TempBuffer;
  282. Inf = Context->Inf;
  283. //
  284. // Nothing to merge if only one section
  285. //
  286. if(Inf->SectionCount < 2) {
  287. return(TRUE);
  288. }
  289. NewestSection = Inf->SectionBlock + Inf->SectionCount - 1;
  290. //
  291. // See whether the final section duplicates any existing sections.
  292. //
  293. for(Section=Inf->SectionBlock; Section<NewestSection; Section++) {
  294. if(Section->SectionName == NewestSection->SectionName) {
  295. break;
  296. }
  297. }
  298. if(Section == NewestSection) {
  299. //
  300. // No duplication; return success
  301. //
  302. return(TRUE);
  303. }
  304. //
  305. // Got a duplicate.
  306. //
  307. //
  308. // We need to move the new section's lines (at the end of the line block)
  309. // to be just after the existing section's lines.
  310. //
  311. // First, we'll save off the new section's lines in a temporary buffer.
  312. //
  313. Size = NewestSection->LineCount * sizeof(INF_LINE);
  314. TempBuffer = MyMalloc(Size);
  315. if(!TempBuffer) {
  316. return(FALSE);
  317. }
  318. CopyMemory(TempBuffer,&Inf->LineBlock[NewestSection->Lines],Size);
  319. //
  320. // Next, we'll move up the affected existing lines, to make room for
  321. // the section's new lines
  322. //
  323. MoveSize = Context->LineBlockUseCount - (Section->Lines + Section->LineCount);
  324. MoveSize *= sizeof(INF_LINE);
  325. MoveSize -= Size;
  326. MoveMemory(
  327. &Inf->LineBlock[Section->Lines + Section->LineCount + NewestSection->LineCount],
  328. &Inf->LineBlock[Section->Lines + Section->LineCount],
  329. MoveSize
  330. );
  331. //
  332. // Now put the new lines in the hole we just opened up
  333. //
  334. CopyMemory(
  335. &Inf->LineBlock[Section->Lines + Section->LineCount],
  336. TempBuffer,
  337. Size
  338. );
  339. MyFree(TempBuffer);
  340. //
  341. // Adjust the existing section's limits to account for the new lines.
  342. //
  343. Section->LineCount += NewestSection->LineCount;
  344. //
  345. // Adjust all subsequent sections' starting line value
  346. //
  347. for(Section=Section+1; Section<NewestSection; Section++) {
  348. Section->Lines += NewestSection->LineCount;
  349. }
  350. //
  351. // Remove the newest section.
  352. //
  353. Inf->SectionCount--;
  354. return(TRUE);
  355. }
  356. PTCHAR
  357. LocateStringSubstitute(
  358. IN PPARSE_CONTEXT Context,
  359. IN PTSTR String
  360. )
  361. /*++
  362. Routine Description:
  363. This routine attempts to find a string substitution in an INF's
  364. [strings] section for the specified key.
  365. THIS ROUTINE OPERATES UNDER THE ASSUMPTION THAT IT IS ONLY INVOKED FROM
  366. WITHIN LOADINF. IT DOESN'T HANDLE MULTIPLE INFS, NOR DOES IT DO ANY
  367. INF LOCKING.
  368. Arguments:
  369. Context - current INF parse context
  370. String - string to be substituted
  371. Return Value:
  372. If substitution is a success, the function returns a pointer to the string,
  373. either in the string table or in the workspace. If failure, NULL is returned.
  374. --*/
  375. {
  376. UINT Zero = 0;
  377. PINF_LINE Line;
  378. MYASSERT(Context->Inf->SectionCount > 1);
  379. MYASSERT(Context->Inf->HasStrings);
  380. //
  381. // The strings section is always first to be parsed.
  382. // (See PreprocessInf()).
  383. //
  384. // Look for a line in [strings] with key of String.
  385. //
  386. if(InfLocateLine(Context->Inf,
  387. Context->Inf->SectionBlock,
  388. String,
  389. &Zero,
  390. &Line)) {
  391. //
  392. // Get and return value #1.
  393. //
  394. return(InfGetField(Context->Inf,Line,1,NULL));
  395. }
  396. //
  397. // No valid substitution exists.
  398. //
  399. return NULL;
  400. }
  401. VOID
  402. ProcessForSubstitutions(
  403. IN OUT PPARSE_CONTEXT Context,
  404. IN PCTSTR String,
  405. OUT PDWORD UnresolvedSubst
  406. )
  407. {
  408. PCTSTR In, q;
  409. PTCHAR Out, p;
  410. TCHAR Str[MAX_STRING_LENGTH];
  411. ULONG Len, i;
  412. PTCHAR End;
  413. TCHAR DirId[MAX_PATH];
  414. BOOL HasStrings = Context->Inf->HasStrings;
  415. UINT DirIdUsed;
  416. BOOL HasVolatileSysDirId;
  417. In = String;
  418. Out = Context->TemporaryString;
  419. End = Out + SIZECHARS(Context->TemporaryString);
  420. *UnresolvedSubst = UNRESOLVED_SUBST_NONE;
  421. while(*In) {
  422. if(*In == TEXT('%')) {
  423. //
  424. // Double % in input ==> single % in output
  425. //
  426. if(*(++In) == TEXT('%')) {
  427. if(Out < End) {
  428. *Out++ = TEXT('%');
  429. }
  430. In++;
  431. } else {
  432. //
  433. // Look for terminating %.
  434. //
  435. if(p = _tcschr(In,TEXT('%'))) {
  436. HasVolatileSysDirId = FALSE;
  437. //
  438. // Get value to substitute. If we can't find the value,
  439. // put the whole string like %abc% in there.
  440. //
  441. Len = (ULONG)(p - In);
  442. if(Len > CSTRLEN(Str)) {
  443. //
  444. // We can't handle substitutions for tokens this long.
  445. // We'll just bail in this case, and copy over the token as-is.
  446. //
  447. q = NULL;
  448. } else {
  449. lstrcpyn(Str,In,Len+1);
  450. if(Context->DoVolatileDirIds) {
  451. if(q = pSetupVolatileDirIdToPath(Str, 0, NULL, Context->Inf)) {
  452. lstrcpyn(DirId, q, SIZECHARS(DirId));
  453. MyFree(q);
  454. q = DirId;
  455. //
  456. // If the next character following this string substitution
  457. // is a backslash, then we need to make sure that the path we
  458. // just retrieved doesn't have a backslash (i.e., we want to
  459. // make sure we have a well-formed path).
  460. //
  461. if(*(p + 1) == TEXT('\\')) {
  462. i = lstrlen(DirId);
  463. if(i > 0 && (*CharPrev(DirId,DirId+i) == TEXT('\\'))) {
  464. DirId[i-1] = TEXT('\0');
  465. }
  466. }
  467. }
  468. } else {
  469. if(HasStrings) {
  470. q = LocateStringSubstitute(Context, Str);
  471. } else {
  472. q = NULL;
  473. }
  474. if(!q) {
  475. //
  476. // Maybe we have a standard DIRID here...
  477. //
  478. if(q = pSetupDirectoryIdToPathEx(Str,
  479. &DirIdUsed,
  480. NULL,
  481. Context->InfSourcePath,
  482. &(Context->OsLoaderPath),
  483. &HasVolatileSysDirId)) {
  484. lstrcpyn(DirId, q, SIZECHARS(DirId));
  485. MyFree(q);
  486. q = DirId;
  487. //
  488. // If the next character following this string substitution
  489. // is a backslash, then we need to make sure that the path we
  490. // just retrieved doesn't have a backslash (i.e., we want to
  491. // make sure we have a well-formed path).
  492. //
  493. if(*(p + 1) == TEXT('\\')) {
  494. i = lstrlen(DirId);
  495. if(i > 0 && (*CharPrev(DirId,DirId+i) == TEXT('\\'))) {
  496. DirId[i-1] = TEXT('\0');
  497. }
  498. }
  499. if((DirIdUsed == DIRID_BOOT) || (DirIdUsed == DIRID_LOADER)) {
  500. //
  501. // Then this INF contains string substititutions that
  502. // reference system partition DIRIDs. Store the OsLoaderPath
  503. // contained in the parse context structure in the INF itself.
  504. //
  505. Context->Inf->OsLoaderPath = Context->OsLoaderPath;
  506. }
  507. }
  508. }
  509. }
  510. }
  511. if(q) {
  512. Len = lstrlen(q);
  513. for(i=0; i<Len; i++) {
  514. if(Out < End) {
  515. *Out++ = q[i];
  516. }
  517. }
  518. In = p+1;
  519. } else {
  520. //
  521. // Len is the length of the internal part (the abc in %abc%).
  522. //
  523. if(Out < End) {
  524. *Out++ = TEXT('%');
  525. }
  526. for(i=0; i<=Len; i++, In++) {
  527. if(Out < End) {
  528. *Out++ = *In;
  529. }
  530. }
  531. //
  532. // When we encounter a substitution for which there is
  533. // no corresponding string, we set the UnresolvedSubst
  534. // output parameter so that the caller knows to track
  535. // this value for later resolution (e.g., for volatile
  536. // and user-defined DIRIDs).
  537. //
  538. // (NOTE: Don't set this if we bailed because the token
  539. // was too long!)
  540. //
  541. if(Len <= CSTRLEN(Str)) {
  542. *UnresolvedSubst = HasVolatileSysDirId
  543. ? UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID
  544. : UNRESOLVED_SUBST_USER_DIRID;
  545. }
  546. }
  547. } else {
  548. //
  549. // No terminating %. So we have something like %abc.
  550. // Want to put %abc in the output. Put the % in here
  551. // manually and then just let subsequent passes
  552. // through the loop copy the rest of the chars.
  553. //
  554. if(Out < End) {
  555. *Out++ = TEXT('%');
  556. }
  557. }
  558. }
  559. } else {
  560. //
  561. // Plain char.
  562. //
  563. if(Out < End) {
  564. *Out++ = *In;
  565. }
  566. In++;
  567. }
  568. }
  569. *Out = 0;
  570. }
  571. VOID
  572. ParseValueString(
  573. IN OUT PPARSE_CONTEXT Context,
  574. IN OUT PCTSTR *Location,
  575. IN BOOL ForKey,
  576. OUT PDWORD UnresolvedSubst
  577. )
  578. /*++
  579. Routine Description:
  580. Extract a string starting at the current location in the input stream.
  581. The string starts at the current location, and is terminated by
  582. comma, newline, comment, or end-of-buffer. If the string is potentially
  583. a line key, it may also terminate with an =.
  584. The string may also be continued across multiple lines by using a
  585. continuation character "\". The pieces are appended together to form
  586. a single string that is returned to the caller. E.g.,
  587. "this is a "\
  588. "string used to" \
  589. " test line continuation"
  590. becomes:
  591. "this is a string used to test line continuation"
  592. Arguments:
  593. Context - supplies parse context.
  594. Location - on input, supplies a pointer to the current location in the
  595. input stream. On outut, recevies a pointer to the location in the
  596. input stream of the character that terminated the string (may be
  597. a pointer to the end of the buffer, in which case the pointer must
  598. not be dereferenced!)
  599. ForKey - indicates whether = is a valid string terminator. If this value
  600. is FALSE, = is just another character with no special semantics.
  601. UnresolvedSubst - receives a value indicating whether or not this value
  602. contained any unresolved string substitutions (and as such, should be
  603. tracked for user-defined DIRID replacement, etc.). May be one of the
  604. following 3 values:
  605. UNRESOLVED_SUBST_NONE (0)
  606. UNRESOLVED_SUBST_USER_DIRID (1)
  607. UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID (2)
  608. Return Value:
  609. None.
  610. --*/
  611. {
  612. DWORD Count;
  613. PTCHAR Out;
  614. BOOL InQuotes;
  615. BOOL Done;
  616. PCTSTR location = *Location;
  617. PCTSTR BufferEnd = Context->BufferEnd;
  618. TCHAR TempString[MAX_STRING_LENGTH+1];
  619. PTSTR LastBackslashChar, LastNonWhitespaceChar;
  620. //
  621. // Prepare to get the string
  622. //
  623. Count = 0;
  624. Out = TempString;
  625. Done = FALSE;
  626. InQuotes = FALSE;
  627. LastBackslashChar = NULL;
  628. //
  629. // Set the last non-whitespace pointer to be the character immediately preceding
  630. // the output buffer. We always reference the value of this pointer + 1, so there's
  631. // no danger of a bad memory reference.
  632. //
  633. LastNonWhitespaceChar = Out - 1;
  634. //
  635. // The first string can terminate with an =
  636. // as well as the usual comma, newline, comment, or end-of-input.
  637. //
  638. while(!Done && (location < BufferEnd)) {
  639. switch(*location) {
  640. case TEXT('\r'):
  641. //
  642. // Ignore these.
  643. //
  644. location++;
  645. break;
  646. case TEXT('\\'):
  647. //
  648. // If we're not inside quotes, this could be a continuation character.
  649. //
  650. if(!InQuotes) {
  651. LastBackslashChar = Out;
  652. }
  653. //
  654. // We always store this character, we just may have to remove it later if
  655. // it turns out to be the continuation character.
  656. //
  657. goto store;
  658. case TEXT('\"'):
  659. location++;
  660. if(InQuotes) {
  661. if((location < BufferEnd) && *location == TEXT('\"')) {
  662. goto store;
  663. } else {
  664. InQuotes = FALSE;
  665. }
  666. } else {
  667. InQuotes = TRUE;
  668. }
  669. break;
  670. case TEXT(','):
  671. if(InQuotes) {
  672. goto store;
  673. } else {
  674. Done = TRUE;
  675. break;
  676. }
  677. case TEXT(';'):
  678. if(InQuotes) {
  679. goto store;
  680. }
  681. //
  682. // This character terminates the value, so let fall through to processing
  683. // of end-of-line. (We treat ';' and '\n' differently than ',' because the
  684. // former chars can possibly require a line continuation.)
  685. //
  686. case TEXT('\n'):
  687. //
  688. // OK, we've hit the end of the data on the line. If we found a backslash
  689. // character, and its value is greater than that of the last non-whitespace
  690. // character we encountered, then that means that we need to continue this
  691. // value on the next line.
  692. //
  693. if(LastBackslashChar && (LastBackslashChar > LastNonWhitespaceChar)) {
  694. //
  695. // Trim any trailing whitespace from our current string (this includes
  696. // getting rid of the backslash character itself).
  697. //
  698. Out = LastNonWhitespaceChar + 1;
  699. //
  700. // Skip to the beginning of the next line.
  701. //
  702. SkipLine(Context, &location);
  703. //
  704. // Skip any preceding whitespace on this new line.
  705. //
  706. SkipWhitespace(&location, BufferEnd);
  707. //
  708. // Clear the last-backslash pointer--we're on a new line now.
  709. //
  710. LastBackslashChar = NULL;
  711. break;
  712. }
  713. Done = TRUE;
  714. break;
  715. case TEXT('='):
  716. if(InQuotes) {
  717. goto store;
  718. }
  719. if(ForKey) {
  720. //
  721. // We've got a key.
  722. //
  723. Done = TRUE;
  724. break;
  725. }
  726. //
  727. // Else just fall through for default handling.
  728. //
  729. default:
  730. store:
  731. //
  732. // Strings longer then the maximum length are silently truncated.
  733. // NULL characters are converted to spaces.
  734. //
  735. if(Count < CSTRLEN(TempString)) {
  736. *Out = *location ? *location : TEXT(' ');
  737. if(InQuotes || ((*Out != TEXT('\\')) && !IsWhitespace(Out))) {
  738. //
  739. // Update our pointer that keeps track of the last non-whitespace
  740. // character we've encountered.
  741. //
  742. LastNonWhitespaceChar = Out;
  743. }
  744. Out++;
  745. Count++;
  746. }
  747. location++;
  748. break;
  749. }
  750. }
  751. //
  752. // Terminate the string in the buffer after the last non-whitespace character encountered.
  753. //
  754. *(LastNonWhitespaceChar + 1) = TEXT('\0');
  755. //
  756. // Store the new current buffer location in the caller's variable.
  757. //
  758. *Location = location;
  759. //
  760. // Substitute localized strings from the strings section.
  761. // The strings section (if it exists) is always first
  762. // (see PreprocessInf()).
  763. //
  764. // (tedm) Ignore whether or not the value was in quotes.
  765. // Win95 infs do stuff like "%Description%\foo" and expect the
  766. // substitution to work.
  767. //
  768. // (lonnym) We have to do this regardless of whether the INF has
  769. // a [strings] section, since this routine tells us whether we have
  770. // unresolved substitutions (e.g., for later replacement by user-defined
  771. // DIRIDs).
  772. //
  773. if((Context->Inf->SectionCount > 1) || !(Context->Inf->HasStrings)) {
  774. ProcessForSubstitutions(Context, TempString, UnresolvedSubst);
  775. } else {
  776. //
  777. // Don't process values in the [strings] section for substitution!
  778. //
  779. lstrcpy(Context->TemporaryString, TempString);
  780. *UnresolvedSubst = UNRESOLVED_SUBST_NONE;
  781. }
  782. }
  783. DWORD
  784. ParseValuesLine(
  785. IN OUT PPARSE_CONTEXT Context,
  786. IN OUT PCTSTR *Location
  787. )
  788. /*++
  789. Routine Description:
  790. Parse a line of input that is not a section name and not a line
  791. with only a comment on it.
  792. Such lines are in the format
  793. [<key> = ] <value>,<value>,<value>,...
  794. The key is optional. Unquoted whitespace between non-whitespace characters
  795. within a value is significant and considered part of the value.
  796. Thus
  797. a, b cd ef ,ghi
  798. is the 3 values "a" "b cd ef" and "ghi"
  799. Unquoted commas separate values. Two double quotes in a row within a quoted
  800. string result in a single double quote character in the resulting string.
  801. A logical line may be extended across several physical lines by use of the line
  802. continuation character "\". E.g.,
  803. a = b, c, \
  804. d, e
  805. becomes "a = b, c, d, e"
  806. If it is desired to have a string that ends with a backslash at the end of a line,
  807. the string must be enclosed in quotes. E.g.,
  808. a = "C:\"
  809. Arguments:
  810. Context - supplies the parse context
  811. Location - on input, supplies the current location in the input stream.
  812. This must point to the left bracket.
  813. On output, receives the location in the input stream of the first
  814. character on the next line. This may be the end of input marker.
  815. Return Value:
  816. Result indicating outcome.
  817. --*/
  818. {
  819. BOOL HaveKey = FALSE, RepeatSingleVal = FALSE;
  820. BOOL Done;
  821. DWORD Size;
  822. PVOID p;
  823. LONG StringId;
  824. PCTSTR BufferEnd = Context->BufferEnd;
  825. PWORD pValueCount;
  826. DWORD UnresolvedSubst;
  827. BOOL CaseSensitive;
  828. //
  829. // Parse out the first string.
  830. // The first string can terminate with an = or whitespace
  831. // as well as the usual comma, newline, comment, or end-of-buffer
  832. // (or line continuation character "\").
  833. //
  834. ParseValueString(Context, Location, TRUE, &UnresolvedSubst);
  835. //
  836. // If it terminated with an = then it's a key.
  837. //
  838. if(*Location < BufferEnd) {
  839. HaveKey = (**Location == TEXT('='));
  840. }
  841. //
  842. // Set up the current line
  843. //
  844. MYASSERT(Context->Inf->SectionCount);
  845. Context->Inf->SectionBlock[Context->Inf->SectionCount-1].LineCount++;
  846. if(Context->LineBlockUseCount == Context->LineBlockSize) {
  847. Size = (Context->LineBlockSize + LINE_BLOCK_GROWTH) * sizeof(INF_LINE);
  848. p = MyRealloc(Context->Inf->LineBlock,Size);
  849. if(p) {
  850. Context->Inf->LineBlock = p;
  851. Context->LineBlockSize += LINE_BLOCK_GROWTH;
  852. } else {
  853. return(ERROR_NOT_ENOUGH_MEMORY);
  854. }
  855. }
  856. Context->Inf->LineBlock[Context->LineBlockUseCount].Values = Context->ValueBlockUseCount;
  857. *(pValueCount = &(Context->Inf->LineBlock[Context->LineBlockUseCount].ValueCount)) = 0;
  858. Context->Inf->LineBlock[Context->LineBlockUseCount].Flags = HaveKey
  859. ? (INF_LINE_HASKEY | INF_LINE_SEARCHABLE)
  860. : 0;
  861. for(Done=FALSE; !Done; ) {
  862. //
  863. // Save away the value in the value block. If it's a key, then
  864. // store it twice--once case-insensitively for lookup, and a second
  865. // time case-sensitively for display. Store everything else
  866. // case-sensitively.
  867. //
  868. // We also want to treat a single value with no key as if it were a key (i.e., store
  869. // it twice). This is for Win95 compatibility.
  870. //
  871. do {
  872. do {
  873. //
  874. // To keep from having to allocate a buffer for the case-insensitive key addition (which
  875. // must be value 0), we do the case-sensitive addition first, then insert the case-
  876. // insensitive version in front of it on the second pass of this inner loop.
  877. //
  878. CaseSensitive = ((*pValueCount != 1) || !HaveKey);
  879. StringId = pStringTableAddString(Context->Inf->StringTable,
  880. Context->TemporaryString,
  881. STRTAB_BUFFER_WRITEABLE | (CaseSensitive ? STRTAB_CASE_SENSITIVE
  882. : STRTAB_CASE_INSENSITIVE),
  883. NULL,0
  884. );
  885. if(StringId == -1) {
  886. return(ERROR_NOT_ENOUGH_MEMORY);
  887. }
  888. if(Context->ValueBlockUseCount == Context->ValueBlockSize) {
  889. Size = (Context->ValueBlockSize + VALUE_BLOCK_GROWTH) * sizeof(LONG);
  890. p = MyRealloc(Context->Inf->ValueBlock,Size);
  891. if(p) {
  892. Context->Inf->ValueBlock = p;
  893. Context->ValueBlockSize += VALUE_BLOCK_GROWTH;
  894. } else {
  895. return(ERROR_NOT_ENOUGH_MEMORY);
  896. }
  897. }
  898. if((*pValueCount == 1) && HaveKey) {
  899. //
  900. // Shift over the case-sensitive version, and insert the case-insensitive one.
  901. //
  902. Context->Inf->ValueBlock[Context->ValueBlockUseCount] =
  903. Context->Inf->ValueBlock[Context->ValueBlockUseCount - 1];
  904. Context->Inf->ValueBlock[Context->ValueBlockUseCount - 1] = StringId;
  905. if(UnresolvedSubst) {
  906. if(!AddUnresolvedSubstToList(Context->Inf,
  907. Context->ValueBlockUseCount - 1,
  908. CaseSensitive)) {
  909. return ERROR_NOT_ENOUGH_MEMORY;
  910. }
  911. if(UnresolvedSubst == UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID) {
  912. Context->Inf->Flags |= LIF_HAS_VOLATILE_DIRIDS;
  913. }
  914. }
  915. //
  916. // Reset the 'RepeatSingleVal' flag, in case we were faking the key behavior.
  917. //
  918. RepeatSingleVal = FALSE;
  919. } else {
  920. Context->Inf->ValueBlock[Context->ValueBlockUseCount] = StringId;
  921. if(UnresolvedSubst) {
  922. if(!AddUnresolvedSubstToList(Context->Inf,
  923. Context->ValueBlockUseCount,
  924. CaseSensitive)) {
  925. return ERROR_NOT_ENOUGH_MEMORY;
  926. }
  927. if(UnresolvedSubst == UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID) {
  928. Context->Inf->Flags |= LIF_HAS_VOLATILE_DIRIDS;
  929. }
  930. }
  931. }
  932. Context->ValueBlockUseCount++;
  933. (*pValueCount)++;
  934. } while(HaveKey && (*pValueCount < 2));
  935. //
  936. // Check to see if this was the last value on the line.
  937. //
  938. if((*Location == BufferEnd) ||
  939. (**Location == TEXT('\n')) ||
  940. (**Location == TEXT(';'))) {
  941. Done = TRUE;
  942. //
  943. // If this was the _only_ value on the line (i.e., no key), then treat this value
  944. // as a key, and add it in again, case-insensitively.
  945. //
  946. if(*pValueCount == 1) {
  947. MYASSERT(!HaveKey);
  948. HaveKey = TRUE;
  949. Context->Inf->LineBlock[Context->LineBlockUseCount].Flags = INF_LINE_SEARCHABLE;
  950. RepeatSingleVal = TRUE;
  951. }
  952. }
  953. } while (RepeatSingleVal);
  954. if(!Done) {
  955. //
  956. // Skip terminator and whitespace.
  957. //
  958. (*Location)++;
  959. SkipWhitespace(Location, BufferEnd);
  960. //
  961. // Get the next string.
  962. //
  963. ParseValueString(Context, Location, FALSE, &UnresolvedSubst);
  964. }
  965. }
  966. Context->LineBlockUseCount++;
  967. //
  968. // Skip to next line
  969. //
  970. SkipLine(Context,Location);
  971. return(NO_ERROR);
  972. }
  973. DWORD
  974. ParseSectionLine(
  975. IN OUT PPARSE_CONTEXT Context,
  976. IN OUT PCTSTR *Location
  977. )
  978. /*++
  979. Routine Description:
  980. Parse a line of input that is known to be a section name line.
  981. Such lines are in the format
  982. '[' <arbitrary chars> ']'
  983. All charcters between the brackets are considered part of the section
  984. name, with no special casing of quotes, whitespace, etc. The remainder
  985. of the line is ignored.
  986. Arguments:
  987. Context - supplies the parse context
  988. Location - on input, supplies the current location in the input stream.
  989. This must point to the left bracket.
  990. On output, receives the location in the input stream of the first
  991. character on the next line. This may be the end of input marker.
  992. Return Value:
  993. Result indicating outcome.
  994. --*/
  995. {
  996. DWORD Count;
  997. PTCHAR Out;
  998. BOOL Done;
  999. DWORD Result;
  1000. PVOID p;
  1001. DWORD Size;
  1002. DWORD Index;
  1003. LONG SectionNameId;
  1004. PCTSTR BufferEnd = Context->BufferEnd;
  1005. //
  1006. // Skip the left bracket.
  1007. //
  1008. MYASSERT(**Location == TEXT('['));
  1009. (*Location)++;
  1010. //
  1011. // Prepare for section name
  1012. //
  1013. Out = Context->TemporaryString;
  1014. Count = 0;
  1015. //
  1016. // This is implemeted according to the win95 code in setup\setupx\inf2.c.
  1017. // All characters between the 2 brackets are considered part of the
  1018. // section name with no further processing (like for double quotes, etc).
  1019. //
  1020. // Win95 also seems to allow [] as a section name.
  1021. //
  1022. for(Done=FALSE,Result=NO_ERROR; !Done; (*Location)++) {
  1023. if((*Location == BufferEnd) || (**Location == TEXT('\n'))) {
  1024. //
  1025. // Syntax error
  1026. //
  1027. Result = ERROR_BAD_SECTION_NAME_LINE;
  1028. Done = TRUE;
  1029. } else {
  1030. switch(**Location) {
  1031. case TEXT(']'):
  1032. Done = TRUE;
  1033. *Out = 0;
  1034. break;
  1035. default:
  1036. if(Count < MAX_SECT_NAME_LEN) {
  1037. //
  1038. // Convert NULL characters to spaces.
  1039. //
  1040. *Out++ = **Location ? **Location : TEXT(' ');
  1041. Count++;
  1042. } else {
  1043. Result = ERROR_SECTION_NAME_TOO_LONG;
  1044. Done = TRUE;
  1045. }
  1046. break;
  1047. }
  1048. }
  1049. }
  1050. Index = Context->Inf->SectionCount;
  1051. if(Result == NO_ERROR) {
  1052. //
  1053. // Ignore the rest of the line
  1054. //
  1055. SkipLine(Context,Location);
  1056. //
  1057. // See if we have enough room in the section block
  1058. // for this section. If not, grow the block.
  1059. //
  1060. if(Index == Context->SectionBlockSize) {
  1061. //
  1062. // Calculate the new section block size.
  1063. //
  1064. Size = (Index + SECTION_BLOCK_GROWTH) * sizeof(INF_SECTION);
  1065. if(p = MyRealloc(Context->Inf->SectionBlock,Size)) {
  1066. Context->SectionBlockSize += SECTION_BLOCK_GROWTH;
  1067. Context->Inf->SectionBlock = p;
  1068. } else {
  1069. Result = ERROR_NOT_ENOUGH_MEMORY;
  1070. }
  1071. }
  1072. }
  1073. if(Result == NO_ERROR) {
  1074. Context->Inf->SectionBlock[Index].LineCount = 0;
  1075. Context->Inf->SectionBlock[Index].Lines = Context->LineBlockUseCount;
  1076. SectionNameId = pStringTableAddString(Context->Inf->StringTable,
  1077. Context->TemporaryString,
  1078. STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE,
  1079. NULL,0
  1080. );
  1081. if(SectionNameId == -1) {
  1082. Result = ERROR_NOT_ENOUGH_MEMORY;
  1083. } else {
  1084. Context->Inf->SectionBlock[Index].SectionName = SectionNameId;
  1085. Context->Inf->SectionCount++;
  1086. Context->GotOneSection = TRUE;
  1087. }
  1088. }
  1089. return(Result);
  1090. }
  1091. DWORD
  1092. ParseGenericLine(
  1093. IN OUT PPARSE_CONTEXT Context,
  1094. IN OUT PCTSTR *Location,
  1095. OUT PBOOL Done
  1096. )
  1097. /*++
  1098. Routine Description:
  1099. Parse a single line of input. The line may be a comment line, a section name,
  1100. or a values line.
  1101. Handling is passed off to line-specific parsing routines depending on the
  1102. line type.
  1103. Arguments:
  1104. Context - supplies the parse context
  1105. Location - on input, supplies the current location in the input stream.
  1106. On output, receives the location in the input stream of the first
  1107. character on the next line.
  1108. Done - receives boolean value indicating whether we are done
  1109. parsing the buffer. If this is TRUE on output the caller can stop
  1110. calling this routine.
  1111. Return Value:
  1112. Result indicating outcome.
  1113. --*/
  1114. {
  1115. DWORD ParseResult;
  1116. *Done = FALSE;
  1117. //
  1118. // Skip over leading whitespace on the line.
  1119. //
  1120. SkipWhitespace(Location, Context->BufferEnd);
  1121. //
  1122. // Further processing depends on the first important character on the line.
  1123. //
  1124. if(*Location == Context->BufferEnd) {
  1125. //
  1126. // End of input, empty line. Terminate current section.
  1127. //
  1128. *Done = TRUE;
  1129. ParseResult = MergeDuplicateSection(Context) ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY;
  1130. } else {
  1131. switch(**Location) {
  1132. case TEXT('\n'):
  1133. //
  1134. // Empty line.
  1135. //
  1136. SkipLine(Context,Location);
  1137. ParseResult = NO_ERROR;
  1138. break;
  1139. case TEXT('['):
  1140. //
  1141. // Potentially got a new section.
  1142. // First terminate the current section.
  1143. //
  1144. if(MergeDuplicateSection(Context)) {
  1145. ParseResult = ParseSectionLine(Context,Location);
  1146. } else {
  1147. ParseResult = ERROR_NOT_ENOUGH_MEMORY;
  1148. }
  1149. break;
  1150. case TEXT(';'):
  1151. //
  1152. // Comment line; ignore it.
  1153. //
  1154. SkipLine(Context,Location);
  1155. ParseResult = NO_ERROR;
  1156. break;
  1157. default:
  1158. //
  1159. // Ordinary values line. Disallow unless we are within a section.
  1160. //
  1161. ParseResult = Context->GotOneSection
  1162. ? ParseValuesLine(Context,Location)
  1163. : ERROR_EXPECTED_SECTION_NAME;
  1164. break;
  1165. }
  1166. }
  1167. return(ParseResult);
  1168. }
  1169. PLOADED_INF
  1170. AllocateLoadedInfDescriptor(
  1171. IN DWORD SectionBlockSize,
  1172. IN DWORD LineBlockSize,
  1173. IN DWORD ValueBlockSize,
  1174. IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
  1175. )
  1176. {
  1177. PLOADED_INF p;
  1178. if(p = MyTaggedMalloc(sizeof(LOADED_INF),MEMTAG_INF)) {
  1179. ZeroMemory(p,sizeof(LOADED_INF));
  1180. if(p->SectionBlock = MyMalloc(SectionBlockSize*sizeof(INF_SECTION))) {
  1181. if(p->LineBlock = MyMalloc(LineBlockSize*sizeof(INF_LINE))) {
  1182. if(p->ValueBlock = MyMalloc(ValueBlockSize*sizeof(LONG))) {
  1183. if(p->StringTable = pStringTableInitialize(0)) {
  1184. p->LogContext = NULL;
  1185. if(InheritLogContext(LogContext, &p->LogContext) == NO_ERROR) {
  1186. //
  1187. // success
  1188. //
  1189. if(InitializeSynchronizedAccess(&p->Lock)) {
  1190. p->Signature = LOADED_INF_SIG;
  1191. p->FileHandle = p->MappingHandle = INVALID_HANDLE_VALUE;
  1192. return(p);
  1193. }
  1194. DeleteLogContext(p->LogContext);
  1195. }
  1196. pStringTableDestroy(p->StringTable);
  1197. }
  1198. MyFree(p->ValueBlock);
  1199. }
  1200. MyFree(p->LineBlock);
  1201. }
  1202. MyFree(p->SectionBlock);
  1203. }
  1204. MyTaggedFree(p,MEMTAG_INF);
  1205. }
  1206. return(NULL);
  1207. }
  1208. PLOADED_INF
  1209. DuplicateLoadedInfDescriptor(
  1210. IN PLOADED_INF Inf
  1211. )
  1212. /*++
  1213. Routine Description:
  1214. This routine duplicates an existing INF descriptor. The duplicate returned
  1215. is a totally independent copy, except that it has the lock handles (MYLOCK
  1216. array) and Prev and Next pointers of the original. This is useful for
  1217. transferring a memory-mapped PNF into read-write memory if modification is
  1218. required.
  1219. THIS ROUTINE DOESN'T DO LOCKING OF ANY FORM ON THE INF--THE CALLER MUST
  1220. HANDLE IT.
  1221. Arguments:
  1222. Inf - supplies the address of the INF descriptor to be duplicated. This
  1223. pointer refers to a single LOADED_INF structure, so any additional INFs
  1224. linked up via the 'Next' pointer are ignored.
  1225. Return Value:
  1226. If successful, the return value is the address of the newly-created duplicate.
  1227. If out-of-memory or inpage error, the return value is NULL.
  1228. --*/
  1229. {
  1230. PLOADED_INF NewInf;
  1231. BOOL Success;
  1232. if(NewInf = MyTaggedMalloc(sizeof(LOADED_INF),MEMTAG_INF)) {
  1233. CopyMemory(NewInf, Inf, sizeof(LOADED_INF));
  1234. NewInf->Signature = 0;
  1235. NewInf->SectionBlock = NULL;
  1236. NewInf->LineBlock = NULL;
  1237. NewInf->ValueBlock = NULL;
  1238. NewInf->StringTable = NULL;
  1239. NewInf->VersionBlock.DataBlock = NULL;
  1240. NewInf->UserDirIdList.UserDirIds = NULL;
  1241. NewInf->SubstValueList = NULL;
  1242. NewInf->OsLoaderPath = NULL;
  1243. NewInf->InfSourcePath = NULL;
  1244. NewInf->OriginalInfName = NULL;
  1245. } else {
  1246. return NULL;
  1247. }
  1248. Success = FALSE;
  1249. try {
  1250. NewInf->SectionBlock = MyMalloc(Inf->SectionBlockSizeBytes);
  1251. if(NewInf->SectionBlock) {
  1252. CopyMemory(NewInf->SectionBlock, Inf->SectionBlock, Inf->SectionBlockSizeBytes);
  1253. NewInf->LineBlock = MyMalloc(Inf->LineBlockSizeBytes);
  1254. if(NewInf->LineBlock) {
  1255. CopyMemory(NewInf->LineBlock, Inf->LineBlock, Inf->LineBlockSizeBytes);
  1256. NewInf->ValueBlock = MyMalloc(Inf->ValueBlockSizeBytes);
  1257. if(NewInf->ValueBlock) {
  1258. CopyMemory(NewInf->ValueBlock, Inf->ValueBlock, Inf->ValueBlockSizeBytes);
  1259. NewInf->StringTable = pStringTableDuplicate(Inf->StringTable);
  1260. if(NewInf->StringTable) {
  1261. NewInf->VersionBlock.DataBlock = MyTaggedMalloc(Inf->VersionBlock.DataSize,MEMTAG_VBDATA);
  1262. if(NewInf->VersionBlock.DataBlock) {
  1263. CopyMemory((PVOID)(NewInf->VersionBlock.DataBlock),
  1264. Inf->VersionBlock.DataBlock,
  1265. Inf->VersionBlock.DataSize
  1266. );
  1267. if(Inf->SubstValueCount) {
  1268. NewInf->SubstValueList =
  1269. MyMalloc(Inf->SubstValueCount * sizeof(STRINGSUBST_NODE));
  1270. if(!(NewInf->SubstValueList)) {
  1271. goto clean0;
  1272. }
  1273. CopyMemory((PVOID)NewInf->SubstValueList,
  1274. Inf->SubstValueList,
  1275. Inf->SubstValueCount * sizeof(STRINGSUBST_NODE)
  1276. );
  1277. }
  1278. if(Inf->UserDirIdList.UserDirIdCount) {
  1279. NewInf->UserDirIdList.UserDirIds =
  1280. MyMalloc(Inf->UserDirIdList.UserDirIdCount * sizeof(USERDIRID));
  1281. if(!(NewInf->UserDirIdList.UserDirIds)) {
  1282. goto clean0;
  1283. }
  1284. CopyMemory((PVOID)NewInf->UserDirIdList.UserDirIds,
  1285. Inf->UserDirIdList.UserDirIds,
  1286. Inf->UserDirIdList.UserDirIdCount * sizeof(USERDIRID)
  1287. );
  1288. }
  1289. if(Inf->OsLoaderPath) {
  1290. NewInf->OsLoaderPath = DuplicateString(Inf->OsLoaderPath);
  1291. if(!NewInf->OsLoaderPath) {
  1292. goto clean0;
  1293. }
  1294. }
  1295. if(Inf->InfSourcePath) {
  1296. NewInf->InfSourcePath = DuplicateString(Inf->InfSourcePath);
  1297. if(!NewInf->InfSourcePath) {
  1298. goto clean0;
  1299. }
  1300. }
  1301. if(Inf->OriginalInfName) {
  1302. NewInf->OriginalInfName = DuplicateString(Inf->OriginalInfName);
  1303. if(!NewInf->OriginalInfName) {
  1304. goto clean0;
  1305. }
  1306. }
  1307. //
  1308. // Reset the PNF fields because this backed-up INF is completely
  1309. // in-memory.
  1310. //
  1311. NewInf->FileHandle = NewInf->MappingHandle = INVALID_HANDLE_VALUE;
  1312. NewInf->ViewAddress = NULL;
  1313. NewInf->Signature = LOADED_INF_SIG;
  1314. Success = TRUE;
  1315. }
  1316. }
  1317. }
  1318. }
  1319. }
  1320. clean0: ; // nothing to do
  1321. } except(EXCEPTION_EXECUTE_HANDLER) {
  1322. //
  1323. // Access the following variables here in the except clause, so that the compiler will respect
  1324. // our statement ordering w.r.t. these variables.
  1325. //
  1326. Success = FALSE;
  1327. NewInf->OriginalInfName = NewInf->OriginalInfName;
  1328. NewInf->InfSourcePath = NewInf->InfSourcePath;
  1329. NewInf->OsLoaderPath = NewInf->OsLoaderPath;
  1330. NewInf->SubstValueList = NewInf->SubstValueList;
  1331. NewInf->UserDirIdList.UserDirIds = NewInf->UserDirIdList.UserDirIds;
  1332. NewInf->VersionBlock.DataBlock = NewInf->VersionBlock.DataBlock;
  1333. NewInf->StringTable = NewInf->StringTable;
  1334. NewInf->ValueBlock = NewInf->ValueBlock;
  1335. NewInf->LineBlock = NewInf->LineBlock;
  1336. NewInf->SectionBlock = NewInf->SectionBlock;
  1337. }
  1338. if(!Success) {
  1339. //
  1340. // Either we ran out of memory, or we got an inpage error trying to copy data
  1341. // from a memory-mapped PNF image. Free any memory we allocated above.
  1342. //
  1343. if(NewInf->OriginalInfName) {
  1344. MyFree(NewInf->OriginalInfName);
  1345. }
  1346. if(NewInf->InfSourcePath) {
  1347. MyFree(NewInf->InfSourcePath);
  1348. }
  1349. if(NewInf->OsLoaderPath) {
  1350. MyFree(NewInf->OsLoaderPath);
  1351. }
  1352. if(NewInf->SubstValueList) {
  1353. MyFree(NewInf->SubstValueList);
  1354. }
  1355. if(NewInf->UserDirIdList.UserDirIds) {
  1356. MyFree(NewInf->UserDirIdList.UserDirIds);
  1357. }
  1358. if(NewInf->VersionBlock.DataBlock) {
  1359. MyTaggedFree(NewInf->VersionBlock.DataBlock,MEMTAG_VBDATA);
  1360. }
  1361. if(NewInf->StringTable) {
  1362. pStringTableDestroy(NewInf->StringTable);
  1363. }
  1364. if(NewInf->ValueBlock) {
  1365. MyFree(NewInf->ValueBlock);
  1366. }
  1367. if(NewInf->LineBlock) {
  1368. MyFree(NewInf->LineBlock);
  1369. }
  1370. if(NewInf->SectionBlock) {
  1371. MyFree(NewInf->SectionBlock);
  1372. }
  1373. MyTaggedFree(NewInf,MEMTAG_INF);
  1374. NewInf = NULL;
  1375. } else {
  1376. //
  1377. // The copy was successful, but it made a copy of the pointer to the
  1378. // log context, so we must addref if
  1379. //
  1380. RefLogContext(NewInf->LogContext);
  1381. }
  1382. return NewInf;
  1383. }
  1384. VOID
  1385. ReplaceLoadedInfDescriptor(
  1386. IN PLOADED_INF InfToReplace,
  1387. IN PLOADED_INF NewInf
  1388. )
  1389. /*++
  1390. Routine Description:
  1391. Replace the specified INF with a new INF descriptor.
  1392. Note that this routine also frees the NewInf descriptor, when done.
  1393. Arguments:
  1394. InfToReplace - supplies a pointer to the inf descriptor to be replaced.
  1395. NewInf - supplies a pointer to the new INF descriptor that is to replace
  1396. the existing one.
  1397. Return Value:
  1398. None.
  1399. --*/
  1400. {
  1401. FreeInfOrPnfStructures(InfToReplace);
  1402. //
  1403. // Copy backup to inf
  1404. //
  1405. CopyMemory(InfToReplace, NewInf, sizeof(LOADED_INF));
  1406. //
  1407. // Just free the NewInf descriptor itself.
  1408. //
  1409. MyTaggedFree(NewInf,MEMTAG_INF);
  1410. }
  1411. VOID
  1412. FreeInfOrPnfStructures(
  1413. IN PLOADED_INF Inf
  1414. )
  1415. /*++
  1416. Routine Description:
  1417. If the specified INF was loaded from a textfile (non-PNF), then this routine
  1418. frees the memory associated with the various blocks it contains. If, instead,
  1419. the Inf is a PNF, then the PNF file is unmapped from memory and the handle is
  1420. closed.
  1421. THIS ROUTINE DOES NOT FREE THE LOADED_INF STRUCTURE ITSELF!
  1422. Arguments:
  1423. Inf - supplies a pointer to the inf descriptor for the loaded inf file.
  1424. Return Value:
  1425. None.
  1426. --*/
  1427. {
  1428. //
  1429. // If this INF has a vald FileHandle, then we must unmap and close its PNF,
  1430. // otherwise, we simply need to free the associated memory blocks.
  1431. //
  1432. if(Inf->FileHandle != INVALID_HANDLE_VALUE) {
  1433. pSetupUnmapAndCloseFile(Inf->FileHandle, Inf->MappingHandle, Inf->ViewAddress);
  1434. pStringTableDestroy(Inf->StringTable);
  1435. } else {
  1436. MyFree(Inf->ValueBlock);
  1437. MyFree(Inf->LineBlock);
  1438. MyFree(Inf->SectionBlock);
  1439. pStringTableDestroy(Inf->StringTable);
  1440. if(Inf->VersionBlock.DataBlock) {
  1441. MyTaggedFree(Inf->VersionBlock.DataBlock,MEMTAG_VBDATA);
  1442. }
  1443. if(Inf->SubstValueList) {
  1444. MyFree(Inf->SubstValueList);
  1445. Inf->SubstValueList = NULL;
  1446. }
  1447. if(Inf->OsLoaderPath) {
  1448. MyFree(Inf->OsLoaderPath);
  1449. }
  1450. if(Inf->InfSourcePath) {
  1451. MyFree(Inf->InfSourcePath);
  1452. }
  1453. if(Inf->OriginalInfName) {
  1454. MyFree(Inf->OriginalInfName);
  1455. }
  1456. }
  1457. //
  1458. // For both INFs and PNFs, we must free the user-defined DIRID list (if there is one).
  1459. //
  1460. if(Inf->UserDirIdList.UserDirIds) {
  1461. MyFree(Inf->UserDirIdList.UserDirIds);
  1462. }
  1463. //
  1464. // Delete the log context if there is one
  1465. //
  1466. DeleteLogContext(Inf->LogContext);
  1467. Inf->LogContext = NULL;
  1468. //
  1469. // Finally, mark the INF as no longer valid.
  1470. //
  1471. Inf->Signature = 0;
  1472. }
  1473. DWORD
  1474. ParseNewInf(
  1475. IN PCTSTR FileImage,
  1476. IN DWORD FileImageSize,
  1477. IN PCTSTR InfSourcePath, OPTIONAL
  1478. IN PCTSTR OsLoaderPath, OPTIONAL
  1479. IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
  1480. OUT PLOADED_INF *Inf,
  1481. OUT UINT *ErrorLineNumber,
  1482. IN PSTRINGSEC_PARAMS StringsSectionParams
  1483. )
  1484. /*++
  1485. Routine Description:
  1486. Parse an inf file from an in-memory image.
  1487. Arguments:
  1488. FileImage - supplies a pointer to the unicode in-memory image
  1489. of the file.
  1490. FileImageSize - supplies the size of the in memory image.
  1491. InfSourcePath - optionally, supplies the directory path from which
  1492. the Inf is being loaded.
  1493. OsLoaderPath - optionally, supplies the full path to the OsLoader
  1494. (e.g., "C:\os\winnt40"). If it is discovered that this INF
  1495. references system partition DIRIDs, then a copy of this string
  1496. will be stored in the LOADED_INF structure. If this parameter
  1497. is not specified, then it will be retrieved from the registry,
  1498. if needed.
  1499. LogContext - optionally supplies the log context we should inherit from
  1500. Inf - receives a pointer to the descriptor for the inf we loaded.
  1501. ErrorLineNumber - receives the line number of a syntax error,
  1502. if parsing was not successful for other than an out of memory
  1503. condition.
  1504. StringsSectionParams - Supplies information about the location of a
  1505. [strings] section (if there is one) in this INF.
  1506. Return Value:
  1507. Result indicating outcome. If the result is not ERROR_ERROR,
  1508. ErrorLineNumber is filled in.
  1509. --*/
  1510. {
  1511. PPARSE_CONTEXT ParseContext;
  1512. PCTSTR Location;
  1513. DWORD Result, OsLoaderPathLength;
  1514. PVOID p;
  1515. BOOL Done;
  1516. PINF_SECTION DestDirsSection;
  1517. PINF_LINE DestDirsLine;
  1518. PCTSTR DirId;
  1519. PTCHAR End;
  1520. PCTSTR FileImageEnd;
  1521. UINT NumPieces, i, DirIdInt;
  1522. PCTSTR PieceList[3][2]; // 3 pieces, each with a start & end address
  1523. UINT StartLineNumber[3]; // keep track of the starting line number for
  1524. // each piece.
  1525. *ErrorLineNumber = 0;
  1526. ParseContext = MyMalloc(sizeof(PARSE_CONTEXT));
  1527. if(!ParseContext) {
  1528. return ERROR_NOT_ENOUGH_MEMORY;
  1529. }
  1530. ZeroMemory(ParseContext,sizeof(PARSE_CONTEXT));
  1531. ParseContext->Inf = AllocateLoadedInfDescriptor(
  1532. INITIAL_SECTION_BLOCK_SIZE,
  1533. INITIAL_LINE_BLOCK_SIZE,
  1534. INITIAL_VALUE_BLOCK_SIZE,
  1535. LogContext
  1536. );
  1537. if(ParseContext->Inf) {
  1538. ParseContext->SectionBlockSize = INITIAL_SECTION_BLOCK_SIZE;
  1539. ParseContext->LineBlockSize = INITIAL_LINE_BLOCK_SIZE;
  1540. ParseContext->ValueBlockSize = INITIAL_VALUE_BLOCK_SIZE;
  1541. ParseContext->Inf->HasStrings = (StringsSectionParams->Start != NULL);
  1542. ParseContext->InfSourcePath = InfSourcePath;
  1543. if(OsLoaderPath) {
  1544. if(!(ParseContext->OsLoaderPath = DuplicateString(OsLoaderPath))) {
  1545. FreeLoadedInfDescriptor(ParseContext->Inf);
  1546. ParseContext->Inf = NULL;
  1547. }
  1548. }
  1549. }
  1550. if(ParseContext->Inf) {
  1551. ParseContext->Inf->Style = INF_STYLE_WIN4;
  1552. //
  1553. // We want to process the [strings] section first, if present,
  1554. // so we split the file up into (up to) 3 pieces--string section,
  1555. // what comes before it, and what comes after it.
  1556. //
  1557. FileImageEnd = FileImage + FileImageSize;
  1558. if(StringsSectionParams->Start) {
  1559. //
  1560. // Figure out whether we have 1, 2, or 3 pieces.
  1561. //
  1562. PieceList[0][0] = StringsSectionParams->Start;
  1563. PieceList[0][1] = StringsSectionParams->End;
  1564. StartLineNumber[0] = StringsSectionParams->StartLineNumber;
  1565. NumPieces = 1;
  1566. if(StringsSectionParams->Start > FileImage) {
  1567. PieceList[1][0] = FileImage;
  1568. PieceList[1][1] = StringsSectionParams->Start;
  1569. StartLineNumber[1] = 1;
  1570. NumPieces++;
  1571. }
  1572. if(StringsSectionParams->End < FileImageEnd) {
  1573. PieceList[NumPieces][0] = StringsSectionParams->End;
  1574. PieceList[NumPieces][1] = FileImageEnd;
  1575. StartLineNumber[NumPieces] = StringsSectionParams->EndLineNumber;
  1576. NumPieces++;
  1577. }
  1578. } else {
  1579. //
  1580. // No [strings] section, just one big piece.
  1581. //
  1582. PieceList[0][0] = FileImage;
  1583. PieceList[0][1] = FileImageEnd;
  1584. StartLineNumber[0] = 1;
  1585. NumPieces = 1;
  1586. }
  1587. //
  1588. // Surround the parsing loop with try/except in case we get an inpage error.
  1589. //
  1590. Result = NO_ERROR;
  1591. try {
  1592. for(i = 0; ((Result == NO_ERROR) && (i < NumPieces)); i++) {
  1593. //
  1594. // Parse every line in this piece.
  1595. //
  1596. Location = PieceList[i][0];
  1597. ParseContext->BufferEnd = PieceList[i][1];
  1598. ParseContext->CurrentLineNumber = StartLineNumber[i];
  1599. do {
  1600. Result = ParseGenericLine(ParseContext,&Location,&Done);
  1601. if(Result != NO_ERROR) {
  1602. break;
  1603. }
  1604. } while(!Done);
  1605. }
  1606. } except(EXCEPTION_EXECUTE_HANDLER) {
  1607. Result = ERROR_READ_FAULT;
  1608. }
  1609. if(Result != NO_ERROR) {
  1610. *ErrorLineNumber = ParseContext->CurrentLineNumber;
  1611. FreeLoadedInfDescriptor(ParseContext->Inf);
  1612. MyFree(ParseContext);
  1613. return(Result);
  1614. }
  1615. //
  1616. // We've successfully loaded the file. Trim down the section,
  1617. // line, and value blocks. Since these guys are shrinking or
  1618. // staying the same size the reallocs really ought not to fail.
  1619. // If a realloc fails we'll just continue to use the original block.
  1620. //
  1621. ParseContext->Inf->SectionBlockSizeBytes = ParseContext->Inf->SectionCount * sizeof(INF_SECTION);
  1622. p = MyRealloc(
  1623. ParseContext->Inf->SectionBlock,
  1624. ParseContext->Inf->SectionBlockSizeBytes
  1625. );
  1626. if(p) {
  1627. ParseContext->Inf->SectionBlock = p;
  1628. }
  1629. ParseContext->Inf->LineBlockSizeBytes = ParseContext->LineBlockUseCount * sizeof(INF_LINE);
  1630. p = MyRealloc(
  1631. ParseContext->Inf->LineBlock,
  1632. ParseContext->LineBlockUseCount * sizeof(INF_LINE)
  1633. );
  1634. if(p) {
  1635. ParseContext->Inf->LineBlock = p;
  1636. }
  1637. ParseContext->Inf->ValueBlockSizeBytes = ParseContext->ValueBlockUseCount * sizeof(LONG);
  1638. p = MyRealloc(
  1639. ParseContext->Inf->ValueBlock,
  1640. ParseContext->ValueBlockUseCount * sizeof(LONG)
  1641. );
  1642. if(p) {
  1643. ParseContext->Inf->ValueBlock = p;
  1644. }
  1645. pStringTableTrim(ParseContext->Inf->StringTable);
  1646. //
  1647. // Even if we didn't find any string substitutions referencing system partition DIRIDs,
  1648. // we still might have a reference in a [DestinationDirs] section--check for that now.
  1649. // this will allow us to have these values ready for if/when they are referenced
  1650. //
  1651. if(!ParseContext->Inf->OsLoaderPath &&
  1652. (DestDirsSection = InfLocateSection(ParseContext->Inf, pszDestinationDirs, NULL))) {
  1653. for(i = 0;
  1654. InfLocateLine(ParseContext->Inf, DestDirsSection, NULL, &i, &DestDirsLine);
  1655. i++) {
  1656. if(DirId = InfGetField(ParseContext->Inf, DestDirsLine, 1, NULL)) {
  1657. DirIdInt = _tcstoul(DirId, &End, 10);
  1658. if((DirIdInt == DIRID_BOOT) || (DirIdInt == DIRID_LOADER)) {
  1659. //
  1660. // We've found a reference to a system partition DIRID. Store a copy
  1661. // of the system partition path we're using into the INF, and abort the
  1662. // search.
  1663. //
  1664. if(!ParseContext->OsLoaderPath) {
  1665. //
  1666. // We haven't yet retrieved the OsLoaderPath--do so now.
  1667. // (Re-use the parse context's TemporaryString buffer to get this.)
  1668. //
  1669. Result = pSetupGetOsLoaderDriveAndPath(FALSE,
  1670. ParseContext->TemporaryString,
  1671. SIZECHARS(ParseContext->TemporaryString),
  1672. &OsLoaderPathLength
  1673. );
  1674. if(Result) {
  1675. FreeLoadedInfDescriptor(ParseContext->Inf);
  1676. MyFree(ParseContext);
  1677. return Result;
  1678. }
  1679. OsLoaderPathLength *= sizeof(TCHAR); // want # bytes--not chars
  1680. if(!(ParseContext->OsLoaderPath = MyMalloc(OsLoaderPathLength))) {
  1681. FreeLoadedInfDescriptor(ParseContext->Inf);
  1682. MyFree(ParseContext);
  1683. return ERROR_NOT_ENOUGH_MEMORY;
  1684. }
  1685. CopyMemory((PVOID)ParseContext->OsLoaderPath,
  1686. ParseContext->TemporaryString,
  1687. OsLoaderPathLength
  1688. );
  1689. }
  1690. ParseContext->Inf->OsLoaderPath = ParseContext->OsLoaderPath;
  1691. break;
  1692. }
  1693. }
  1694. }
  1695. }
  1696. //
  1697. // If there is no OsLoaderPath stored in the INF, then that means that it contains no
  1698. // references to system partition DIRIDs. We can free the OsLoaderPath character buffer
  1699. // contained in the parse context structure.
  1700. //
  1701. if(!ParseContext->Inf->OsLoaderPath && ParseContext->OsLoaderPath) {
  1702. MyFree(ParseContext->OsLoaderPath);
  1703. }
  1704. *Inf = ParseContext->Inf;
  1705. MyFree(ParseContext);
  1706. return(NO_ERROR);
  1707. }
  1708. MyFree(ParseContext);
  1709. return(ERROR_NOT_ENOUGH_MEMORY);
  1710. }
  1711. DWORD
  1712. PreprocessInf(
  1713. IN PCTSTR FileImage,
  1714. IN OUT PDWORD FileImageSize,
  1715. IN BOOL MatchClassGuid,
  1716. IN PCTSTR ClassGuidString, OPTIONAL
  1717. IN DWORD LanguageId, OPTIONAL
  1718. IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
  1719. IN PCTSTR FileName, OPTIONAL
  1720. OUT PBOOL Win95Inf,
  1721. OUT PSTRINGSEC_PARAMS StringsSectionParams OPTIONAL
  1722. )
  1723. {
  1724. PCTSTR FileImageEnd;
  1725. PCTSTR VerAndStringsCheckUB, DecoratedStringsCheckUB, SigAndClassGuidCheckUB;
  1726. PCTSTR p;
  1727. PTSTR endp;
  1728. UINT CurLineNumber, InStringsSection;
  1729. PCTSTR StrSecStart[5], StrSecEnd[5]; // 1-based, 0th entry unused.
  1730. UINT StrSecStartLine[5], StrSecEndLine[5]; // ""
  1731. BOOL InVersionSection;
  1732. BOOL IsWin95Inf;
  1733. DWORD rc = NO_ERROR;
  1734. DWORD StrSecLangId, PrimaryLanguageId, NearLanguageId;
  1735. BOOL LocalizedInf = FALSE;
  1736. //
  1737. // We make some assumptions about the relative lengths of certain
  1738. // strings during the preprocessing phase for optimization reasons.
  1739. // The following asserts verify that our assumptions remain correct.
  1740. //
  1741. MYASSERT(CSTRLEN(pszVersion) == CSTRLEN(pszStrings));
  1742. MYASSERT(CSTRLEN(pszClassGuid) == CSTRLEN(pszSignature));
  1743. MYASSERT(CSTRLEN(pszChicagoSig) <= CSTRLEN(pszWindowsNTSig));
  1744. MYASSERT(CSTRLEN(pszWindowsNTSig) == CSTRLEN(pszWindows95Sig));
  1745. FileImageEnd = FileImage + *FileImageSize;
  1746. SigAndClassGuidCheckUB = FileImageEnd;
  1747. //
  1748. // I have to cast these two arrays to silence a bogus compiler warning about
  1749. // different 'const' qualifiers.
  1750. //
  1751. ZeroMemory((PVOID)StrSecStart, sizeof(StrSecStart));
  1752. ZeroMemory((PVOID)StrSecEnd, sizeof(StrSecEnd));
  1753. InStringsSection = 0;
  1754. PrimaryLanguageId = (DWORD)PRIMARYLANGID(LanguageId);
  1755. NearLanguageId = 0;
  1756. InVersionSection = IsWin95Inf = FALSE;
  1757. CurLineNumber = 1;
  1758. //
  1759. // Pre-compute upper-bound for section name string comparison that we
  1760. // make multiple times, so that we don't have to compute it each
  1761. // time.
  1762. //
  1763. VerAndStringsCheckUB = FileImageEnd - CSTRLEN(pszVersion);
  1764. DecoratedStringsCheckUB = VerAndStringsCheckUB - 5; // "strings" + ".xxxx"
  1765. //
  1766. // Define a macro that lets us know we're at the end of the file
  1767. // if either:
  1768. // (a) we reach the end of the image, or
  1769. // (b) we hit a CTL-Z
  1770. //
  1771. #define AT_EOF ((p >= FileImageEnd) || (*p == (TCHAR)26))
  1772. //
  1773. // Guard the pre-processing pass through the file with a try/except, in
  1774. // case we get an inpage error.
  1775. //
  1776. try {
  1777. for(p=FileImage; !AT_EOF; ) {
  1778. //
  1779. // Skip whitespace and newlines.
  1780. //
  1781. while(TRUE) {
  1782. if(*p == TEXT('\n')) {
  1783. CurLineNumber++;
  1784. } else if(!IsWhitespace(p)) {
  1785. break;
  1786. }
  1787. p++;
  1788. if(AT_EOF) {
  1789. break;
  1790. }
  1791. }
  1792. if(AT_EOF) {
  1793. //
  1794. // We're through processing the buffer.
  1795. //
  1796. break;
  1797. }
  1798. //
  1799. // See if it's a section title.
  1800. //
  1801. if(*p == TEXT('[')) {
  1802. //
  1803. // If the section we were just in was a [Strings] section, then
  1804. // remember where the strings section ended.
  1805. //
  1806. if(InStringsSection) {
  1807. StrSecEnd[InStringsSection] = p;
  1808. StrSecEndLine[InStringsSection] = CurLineNumber;
  1809. InStringsSection = 0;
  1810. }
  1811. p++;
  1812. InVersionSection = FALSE;
  1813. //
  1814. // See if it's one of the ones we care about.
  1815. //
  1816. // (Be careful here--we check the closing bracket position
  1817. // _before_ the string compare as an optimization. It just
  1818. // so happens that both strings are the same length, so this
  1819. // acts as a quick filter to eliminate string compares.)
  1820. //
  1821. if((p < VerAndStringsCheckUB) &&
  1822. (*(p + CSTRLEN(pszVersion)) == TEXT(']'))) {
  1823. //
  1824. // Then we may have either a [Version] or a [Strings] section.
  1825. // Check for these in turn.
  1826. //
  1827. if(!_tcsnicmp(p, pszVersion, CSTRLEN(pszVersion))) {
  1828. InVersionSection = TRUE;
  1829. p += (CSTRLEN(pszVersion) + 1);
  1830. //
  1831. // Pre-compute an upper bound to speed up string comparisons
  1832. // when checking for signature and class GUID entries.
  1833. //
  1834. SigAndClassGuidCheckUB = FileImageEnd - CSTRLEN(pszSignature);
  1835. } else {
  1836. if(!StrSecStart[4] && !_tcsnicmp(p, pszStrings, CSTRLEN(pszStrings))) {
  1837. //
  1838. // We matched on the undecorated string section--this is the lowest
  1839. // priority match.
  1840. //
  1841. InStringsSection = 4;
  1842. StrSecStart[4] = p-1;
  1843. StrSecStartLine[4] = CurLineNumber;
  1844. p += (CSTRLEN(pszStrings) + 1);
  1845. }
  1846. }
  1847. } else if(LanguageId && !StrSecStart[1]) {
  1848. //
  1849. // We don't have a [strings] nor a [version] section. However, we need to
  1850. // check to see if we have a language-specific strings section, for example,
  1851. //
  1852. // [strings.0409]
  1853. //
  1854. if((p < DecoratedStringsCheckUB) &&
  1855. (*(p + CSTRLEN(pszVersion) + 5) == TEXT(']'))) {
  1856. //
  1857. // The section name is of the right length. Now verify that the name
  1858. // begins with "strings."
  1859. //
  1860. if((*(p + CSTRLEN(pszVersion)) == TEXT('.')) &&
  1861. !_tcsnicmp(p, pszStrings, CSTRLEN(pszStrings))) {
  1862. //
  1863. // OK, we've found a language-specific strings section--retrieve
  1864. // the 4-digit (hex) language ID.
  1865. //
  1866. StrSecLangId = _tcstoul((p + CSTRLEN(pszVersion) + 1), &endp, 16);
  1867. if(endp == (p + CSTRLEN(pszVersion) + 5)) {
  1868. //
  1869. // The language ID was of the proper form - this
  1870. // is a localized INF
  1871. //
  1872. LocalizedInf = TRUE;
  1873. //
  1874. // now see if it matches the language we're
  1875. // supposed to be using when loading this INF.
  1876. //
  1877. if(StrSecLangId == LanguageId) {
  1878. //
  1879. // we have an exact match
  1880. //
  1881. InStringsSection = 1;
  1882. NearLanguageId = LanguageId;
  1883. } else if(StrSecLangId == PrimaryLanguageId) {
  1884. //
  1885. // we have a match on primary language (sublanguage is not
  1886. // included in the strings section's name--thus permitting
  1887. // a 'wildcard' match).
  1888. //
  1889. if(!StrSecStart[2]) {
  1890. InStringsSection = 2;
  1891. }
  1892. if(!StrSecStart[1]) {
  1893. NearLanguageId = PrimaryLanguageId;
  1894. }
  1895. } else if((DWORD)PRIMARYLANGID(StrSecLangId) == PrimaryLanguageId) {
  1896. //
  1897. // we have a match on primary language (sublanguage is a
  1898. // mismatch, but it's better than falling back to the default).
  1899. //
  1900. if(!StrSecStart[3]) {
  1901. InStringsSection = 3;
  1902. if(!StrSecStart[1] && !StrSecStart[2]) {
  1903. NearLanguageId = StrSecLangId;
  1904. }
  1905. }
  1906. }
  1907. if(InStringsSection) {
  1908. StrSecStart[InStringsSection] = p-1;
  1909. StrSecStartLine[InStringsSection] = CurLineNumber;
  1910. }
  1911. p += (CSTRLEN(pszStrings) + 6);
  1912. }
  1913. }
  1914. }
  1915. }
  1916. } else {
  1917. if(InVersionSection && (p < SigAndClassGuidCheckUB)) {
  1918. //
  1919. // See if this is the signature line indicating a Win95-style
  1920. // Device INF. (signature=$Chicago$ or "$Windows NT$")
  1921. //
  1922. if(!IsWin95Inf && !_tcsnicmp(p, pszSignature, CSTRLEN(pszSignature))) {
  1923. PCTSTR ChicagoCheckUB = FileImageEnd - CSTRLEN(pszChicagoSig);
  1924. //
  1925. // Skip over Signature, and look for "$Chicago$" or
  1926. // "$Windows NT$" anywhere on the rest of the line
  1927. //
  1928. p += CSTRLEN(pszSignature);
  1929. while((p <= ChicagoCheckUB) &&
  1930. (*p != (TCHAR)26) && (*p != TEXT('\n'))) {
  1931. if(*(p++) == TEXT('$')) {
  1932. //
  1933. // Check for signatures (check in order of
  1934. // increasing signature length, so that we can
  1935. // eliminate checks if we happen to be near the
  1936. // end of the file).
  1937. //
  1938. // Check for "$Chicago$"
  1939. //
  1940. if(!_tcsnicmp(p,
  1941. pszChicagoSig + 1,
  1942. CSTRLEN(pszChicagoSig) - 1)) {
  1943. IsWin95Inf = TRUE;
  1944. p += (CSTRLEN(pszChicagoSig) - 1);
  1945. } else if((p + (CSTRLEN(pszWindowsNTSig) - 1)) <= FileImageEnd) {
  1946. //
  1947. // Check for "Windows NT$" and "Windows 95$" (we already checked
  1948. // for the preceding '$').
  1949. //
  1950. if(!_tcsnicmp(p, pszWindowsNTSig + 1, CSTRLEN(pszWindowsNTSig) - 1) ||
  1951. !_tcsnicmp(p, pszWindows95Sig + 1, CSTRLEN(pszWindows95Sig) - 1)) {
  1952. IsWin95Inf = TRUE;
  1953. p += (CSTRLEN(pszWindowsNTSig) - 1);
  1954. }
  1955. }
  1956. break;
  1957. }
  1958. }
  1959. } else if(MatchClassGuid && !_tcsnicmp(p, pszClassGuid, CSTRLEN(pszClassGuid))) {
  1960. PCTSTR GuidStringCheckUB = FileImageEnd - (GUID_STRING_LEN - 1);
  1961. //
  1962. // We have found a ClassGUID line--see if it matches the
  1963. // class GUID specified by the caller.
  1964. //
  1965. p += CSTRLEN(pszClassGuid);
  1966. //
  1967. // If a class GUID string wasn't specified, then use GUID_NULL.
  1968. //
  1969. if(!ClassGuidString) {
  1970. ClassGuidString = pszGuidNull;
  1971. }
  1972. while((p <= GuidStringCheckUB) &&
  1973. (*p != (TCHAR)26) && (*p != TEXT('\n'))) {
  1974. if(*(p++) == TEXT('{')) {
  1975. if((*(p + (GUID_STRING_LEN - 3)) != TEXT('}')) ||
  1976. _tcsnicmp(p, ClassGuidString + 1, GUID_STRING_LEN - 3)) {
  1977. //
  1978. // The GUIDs don't match. If ClassGuid was NULL, then
  1979. // this means we should continue, because we were matching
  1980. // against GUID_NULL, which we want to disallow.
  1981. //
  1982. if(ClassGuidString == pszGuidNull) {
  1983. //
  1984. // We don't need to keep looking for ClassGUIDs.
  1985. //
  1986. MatchClassGuid = FALSE;
  1987. }
  1988. } else {
  1989. //
  1990. // The GUIDs match. If ClassGuid was not NULL, then this
  1991. // means that we should continue.
  1992. //
  1993. if(ClassGuidString != pszGuidNull) {
  1994. //
  1995. // We don't need to keep looking for ClassGUIDs.
  1996. //
  1997. MatchClassGuid = FALSE;
  1998. }
  1999. }
  2000. //
  2001. // Skip over the GUID string.
  2002. //
  2003. p += (GUID_STRING_LEN - 2);
  2004. break;
  2005. }
  2006. }
  2007. //
  2008. // If we get here, and MatchClassGuid hasn't been reset,
  2009. // then we know that this ClassGUID entry didn't match.
  2010. //
  2011. if(MatchClassGuid) {
  2012. rc = ERROR_CLASS_MISMATCH;
  2013. goto clean0;
  2014. }
  2015. }
  2016. }
  2017. }
  2018. //
  2019. // Skip to the newline or end of file.
  2020. //
  2021. while(!AT_EOF && (*p != TEXT('\n'))) {
  2022. p++;
  2023. }
  2024. }
  2025. clean0: ; // Nothing to do.
  2026. } except(EXCEPTION_EXECUTE_HANDLER) {
  2027. rc = ERROR_READ_FAULT;
  2028. }
  2029. if(rc == NO_ERROR) {
  2030. MYASSERT(p <= FileImageEnd);
  2031. if(p < FileImageEnd) {
  2032. //
  2033. // Then we hit a CTL-Z during processing, so update the
  2034. // FileImageSize output parameter with the new size.
  2035. //
  2036. *FileImageSize = (DWORD)(p - FileImage);
  2037. }
  2038. if(StringsSectionParams) {
  2039. //
  2040. // If a strings section happens to be the last section in the INF,
  2041. // then we need to remember the end of the INF as being the end of
  2042. // the string section also.
  2043. //
  2044. if(InStringsSection) {
  2045. StrSecEnd[InStringsSection] = p;
  2046. StrSecEndLine[InStringsSection] = CurLineNumber;
  2047. }
  2048. //
  2049. // Now search through our array of strings sections (highest priority to lowest),
  2050. // looking for the best match.
  2051. //
  2052. for(InStringsSection = 1; InStringsSection < 5; InStringsSection++) {
  2053. if(StrSecStart[InStringsSection]) {
  2054. break;
  2055. }
  2056. }
  2057. //
  2058. // if the INF appears to be partially localized, and we didn't
  2059. // pick an apropriate localized string section
  2060. // log it
  2061. //
  2062. if(LogContext && IsWin95Inf) {
  2063. if(InStringsSection >= 5) {
  2064. //
  2065. // it's quite valid to have an INF with no strings section
  2066. // so log it verbose here, we'll catch it later
  2067. //
  2068. WriteLogEntry(LogContext,
  2069. SETUP_LOG_VERBOSE,
  2070. MSG_LOG_NO_STRINGS,
  2071. NULL,
  2072. LanguageId,
  2073. PrimaryLanguageId,
  2074. NearLanguageId,
  2075. FileName
  2076. );
  2077. } else if(LocalizedInf && InStringsSection > 2) {
  2078. //
  2079. // INF has localized string sections
  2080. // but none were reasonable match for locale
  2081. //
  2082. WriteLogEntry(LogContext,
  2083. SETUP_LOG_WARNING,
  2084. (InStringsSection> 3 ? MSG_LOG_DEF_STRINGS :
  2085. MSG_LOG_NEAR_STRINGS),
  2086. NULL,
  2087. LanguageId,
  2088. PrimaryLanguageId,
  2089. NearLanguageId,
  2090. FileName
  2091. );
  2092. }
  2093. }
  2094. if(IsWin95Inf && (InStringsSection < 5)) {
  2095. //
  2096. // If we found a [strings] section in a Win95-style INF,
  2097. // then store the beginning and ending positions, and the
  2098. // beginning and ending line numbers, in the output parameter
  2099. // structure
  2100. //
  2101. StringsSectionParams->Start = StrSecStart[InStringsSection];
  2102. StringsSectionParams->End = StrSecEnd[InStringsSection];
  2103. StringsSectionParams->StartLineNumber = StrSecStartLine[InStringsSection];
  2104. StringsSectionParams->EndLineNumber = StrSecEndLine[InStringsSection];
  2105. } else {
  2106. ZeroMemory(StringsSectionParams, sizeof(STRINGSEC_PARAMS));
  2107. }
  2108. }
  2109. *Win95Inf = IsWin95Inf;
  2110. }
  2111. return rc;
  2112. }
  2113. DWORD
  2114. DetermineInfStyle(
  2115. IN PCTSTR Filename,
  2116. IN LPWIN32_FIND_DATA FindData
  2117. )
  2118. /*++
  2119. Routine Description:
  2120. Open an inf file, determine its style, and close the file, without
  2121. keeping it around.
  2122. Arguments:
  2123. Filename - supplies the fully-qualified pathname of the inf file to be checked
  2124. Return Value:
  2125. INF_STYLE_NONE - style could not be determined
  2126. INF_STYLE_WIN4 - win95-style inf file
  2127. INF_STYLE_OLDNT - winnt3.5-style inf file
  2128. --*/
  2129. {
  2130. HANDLE TextFileHandle;
  2131. TEXTFILE_READ_BUFFER ReadBuffer;
  2132. DWORD Style;
  2133. BOOL Win95Inf;
  2134. PLOADED_INF Pnf;
  2135. //
  2136. // First, determine whether a precompiled form of this INF exists, and if so, then
  2137. // use it to determine the INF's style.
  2138. //
  2139. if(LoadPrecompiledInf(Filename,
  2140. &(FindData->ftLastWriteTime),
  2141. NULL,
  2142. 0,
  2143. LDINF_FLAG_IGNORE_VOLATILE_DIRIDS | LDINF_FLAG_IGNORE_LANGUAGE,
  2144. NULL,
  2145. &Pnf,
  2146. NULL,
  2147. NULL,
  2148. NULL)) {
  2149. //
  2150. // Now we can simply access the Style field of the INF.
  2151. //
  2152. try {
  2153. Style = (DWORD)Pnf->Style;
  2154. } except(EXCEPTION_EXECUTE_HANDLER) {
  2155. Style = INF_STYLE_NONE;
  2156. }
  2157. //
  2158. // Now close the PNF.
  2159. //
  2160. FreeInfFile(Pnf);
  2161. } else {
  2162. //
  2163. // No PNF--Open and preprocess the text version of the INF to find out its style.
  2164. //
  2165. if((TextFileHandle = CreateFile(Filename,
  2166. GENERIC_READ,
  2167. FILE_SHARE_READ,
  2168. NULL,
  2169. OPEN_EXISTING,
  2170. 0,
  2171. NULL)) == INVALID_HANDLE_VALUE) {
  2172. return INF_STYLE_NONE;
  2173. } else {
  2174. //
  2175. // We're ready to make the determination--initially assume 'no-style'
  2176. //
  2177. Style = INF_STYLE_NONE;
  2178. }
  2179. if(ReadAsciiOrUnicodeTextFile(TextFileHandle, &ReadBuffer,NULL) == NO_ERROR) {
  2180. if(PreprocessInf(ReadBuffer.TextBuffer,
  2181. &(ReadBuffer.TextBufferSize),
  2182. FALSE,
  2183. NULL,
  2184. 0,
  2185. NULL,
  2186. NULL,
  2187. &Win95Inf,
  2188. NULL) == NO_ERROR) {
  2189. Style = Win95Inf ? INF_STYLE_WIN4 : INF_STYLE_OLDNT;
  2190. }
  2191. DestroyTextFileReadBuffer(&ReadBuffer);
  2192. }
  2193. //
  2194. // No need to close the textfile handle--it's taken care of in the above routines.
  2195. //
  2196. }
  2197. return Style;
  2198. }
  2199. DWORD
  2200. LoadInfFile(
  2201. IN PCTSTR Filename,
  2202. IN LPWIN32_FIND_DATA FileData,
  2203. IN DWORD Style,
  2204. IN DWORD Flags,
  2205. IN PCTSTR ClassGuidString, OPTIONAL
  2206. IN PCTSTR InfSourcePath, OPTIONAL
  2207. IN PCTSTR OriginalInfName, OPTIONAL
  2208. IN PLOADED_INF AppendInf, OPTIONAL
  2209. IN PSETUP_LOG_CONTEXT pLogContext, OPTIONAL
  2210. OUT PLOADED_INF *LoadedInf,
  2211. OUT UINT *ErrorLineNumber,
  2212. OUT BOOL *PnfWasUsed OPTIONAL
  2213. )
  2214. /*++
  2215. Routine Description:
  2216. Top level routine to load an inf file. Both win95-style and winnt3.x-style
  2217. device infs are supported.
  2218. Arguments:
  2219. Filename - supplies the fully-qualified pathname of the inf file to be loaded
  2220. FileData - supplies data returned from FindFirstFile/FindNextFile for this INF.
  2221. Style - supplies a type of inf file to be loaded. May be a combination of
  2222. INF_STYLE_WIN4 - fail to load the given inf file if it is not a win95
  2223. inf file.
  2224. INF_STYLE_OLDNT - fail to load the given inf file if it is not an old
  2225. style inf file.
  2226. If a load fails because of the type, the return code is
  2227. ERROR_WRONG_INF_STYLE.
  2228. Flags - Specifies certain behaviors to use when loading the INF. May be a
  2229. combination of the following values:
  2230. LDINF_FLAG_MATCH_CLASS_GUID - Check the INF to make sure it matches the GUID
  2231. specified by the ClassGuid parameter (see discussion below).
  2232. LDINF_FLAG_ALWAYS_TRY_PNF - If specified, then we will always attempt to
  2233. generate a PNF file, if a valid one does not exist.
  2234. LDINF_FLAG_IGNORE_VOLATILE_DIRIDS - If specified, then no validation
  2235. will be done on the stored OsLoaderPath present in the PNF. Since
  2236. dynamically retrieving the current path is time consuming, this
  2237. flag should be specified as an optimization if it is known that the
  2238. relevant DIRIDs are not going to be needed (e.g., driver searching).
  2239. This flag also suppresses substitution of volatile system DIRIDs.
  2240. (Note: this flag should not be specified when append-loading an INF)
  2241. LDINF_FLAG_REGENERATE_PNF - If specified, then the existing PNF (if
  2242. present) is considered invalid, and is not even checked for. This
  2243. flag causes us to always generate a new PNF, and if we're unable to
  2244. do so, the routine will fail. This flag must always be specified in
  2245. conjunction with LDINF_FLAG_ALWAYS_TRY_PNF.
  2246. LDINF_FLAG_SRCPATH_IS_URL - If specified, then the InfSourcePath passed in is
  2247. not a file path, but rather a URL. If this flag is specified, InfSourcePath
  2248. may still be NULL, which indicates that the origin of this INF was the default
  2249. Code Download Manager site.
  2250. ClassGuidString - Optionally, supplies the address of a class GUID string that
  2251. the INF should match in order to be opened. If the LDINF_FLAG_MATCH_CLASS_GUID
  2252. bit is set in the Flags parameter, this GUID is matched against the ClassGUID
  2253. entry in the [version] section of the INF. If the two GUIDs are different, the
  2254. load will fail with ERROR_CLASS_MISMATCH. If the INF has no ClassGUID entry,
  2255. then this check is not made, and the file is always opened. If ClassGUID matching
  2256. is requested, but ClassGuidString is NULL, then the INF load will succeed for all
  2257. INFs except those with a ClassGUID of GUID_NULL.
  2258. InfSourcePath - Optionally, supplies a path to be used as the INF's source path. If
  2259. LDINF_FLAG_SRCPATH_IS_URL is specified, this is a URL (see above), otherwise, this
  2260. is a directory path. This information is stored in the PNF file if this INF gets
  2261. precompiled.
  2262. If LDINF_FLAG_SRCPATH_IS_URL is specified, then "A:\" is used as the directory string
  2263. substitution for DIRID_SRCPATH.
  2264. OriginalInfName - Optionally, supplies the original name of the INF (no path)
  2265. to be stored in the PNF, if generated. If this parameter is not supplied,
  2266. then the INF's present name is assumed to be its original name.
  2267. AppendInf - if supplied, specifies an already-loaded inf to which
  2268. the inf is to be load-appended. THIS INF MUST HAVE BEEN ALREADY LOCKED BY THE
  2269. CALLER!!!
  2270. pLogContext - if supplied, specifies a LogContext that should be inherited
  2271. as opposed to creating one
  2272. LoadedInf - If AppendInf is not specified, receives a pointer to
  2273. the descriptor for the inf. If AppendInf is specified, receives AppendInf.
  2274. ErrorLineNumber - receives the line number of the error if there is
  2275. a syntax error in the file (see below)
  2276. PnfWasUsed - optionally, receives a boolean value upon successful return
  2277. indicating whether or not a precompiled INF was used/generated in
  2278. loading this INF. NOTE, this flag should not be specified if an
  2279. append-load is requested.
  2280. Return Value:
  2281. Win32 error code (with inf extensions) for result.
  2282. If result is not NO_ERROR, ErrorLineNumber is filled in.
  2283. --*/
  2284. {
  2285. TEXTFILE_READ_BUFFER ReadBuffer;
  2286. DWORD rc;
  2287. PLOADED_INF Inf, InfListTail;
  2288. BOOL Win95Inf;
  2289. STRINGSEC_PARAMS StringsSectionParams;
  2290. HANDLE TextFileHandle;
  2291. PCTSTR OsLoaderPath = NULL;
  2292. DWORD LanguageId;
  2293. PTSTR InfSourcePathToMigrate, InfOriginalNameToMigrate;
  2294. DWORD InfSourcePathToMigrateMediaType = SPOST_NONE;
  2295. BOOL PnfUsed = FALSE; // this allows us to log the flag if PnfWasUsed=NULL
  2296. BOOL PnfSaved = FALSE; // allows us to log the fact that we saved PNF
  2297. PSETUP_LOG_CONTEXT LogContext = NULL;
  2298. MYASSERT(!(AppendInf && PnfWasUsed));
  2299. MYASSERT(!(AppendInf && (Flags & LDINF_FLAG_IGNORE_VOLATILE_DIRIDS)));
  2300. *ErrorLineNumber = 0;
  2301. if(PnfWasUsed) {
  2302. *PnfWasUsed = FALSE;
  2303. }
  2304. //
  2305. // Since we're now storing zero-length INF files in %windir%\Inf as
  2306. // placeholders for the corresponding catalog files, add a quick check to
  2307. // make sure we haven't been handed a zero-length INF. If so, we can return
  2308. // immediately and short ciruit some code. (While we're at it, also make
  2309. // sure that the high DWORD doesn't have any bits set, as we can't handle
  2310. // files greater than 2^32).
  2311. //
  2312. if(FileData->nFileSizeHigh || !FileData->nFileSizeLow) {
  2313. return ERROR_GENERAL_SYNTAX;
  2314. }
  2315. //
  2316. // If append-loading, then traverse the existing list of loaded INFs, to see
  2317. // if we've already loaded this one.
  2318. //
  2319. if(AppendInf) {
  2320. //
  2321. // Only allow appending with win95 infs
  2322. //
  2323. if(AppendInf->Style & INF_STYLE_OLDNT) {
  2324. return ERROR_WRONG_INF_STYLE;
  2325. }
  2326. for(Inf = AppendInf; Inf; Inf = Inf->Next) {
  2327. if(!lstrcmpi(Inf->VersionBlock.Filename, Filename)) {
  2328. //
  2329. // We've already loaded this INF--we can return success.
  2330. //
  2331. *LoadedInf = AppendInf;
  2332. return NO_ERROR;
  2333. }
  2334. //
  2335. // Check to see if the INF we're currently examining references the
  2336. // system partition/OsLoader path. If so, then remember this path
  2337. // so that we will use the same one later when append-loading our
  2338. // new INF.
  2339. //
  2340. if(Inf->OsLoaderPath) {
  2341. if(OsLoaderPath) {
  2342. //
  2343. // We'd better be using the same value for OsLoadPath for
  2344. // all our append-loaded INFs!
  2345. //
  2346. MYASSERT(!lstrcmpi(Inf->OsLoaderPath, OsLoaderPath));
  2347. } else {
  2348. OsLoaderPath = Inf->OsLoaderPath;
  2349. }
  2350. }
  2351. //
  2352. // Remember this node, in case it's the tail. We do this so we don't
  2353. // have to hunt for the tail again later.
  2354. //
  2355. InfListTail = Inf;
  2356. }
  2357. //
  2358. // We want to append-load the INF based on the locale of the already-
  2359. // loaded INF(s)
  2360. //
  2361. LanguageId = AppendInf->LanguageId;
  2362. } else {
  2363. //
  2364. // We want to load the INF based on the current locale set for this thread.
  2365. //
  2366. LanguageId = (DWORD)LANGIDFROMLCID(GetThreadLocale());
  2367. }
  2368. InheritLogContext(pLogContext,&LogContext);
  2369. //
  2370. // Now determine whether a precompiled form of this INF exists, and if so,
  2371. // then use it instead.
  2372. //
  2373. if(!(Flags & LDINF_FLAG_REGENERATE_PNF)) {
  2374. if (!InfSourcePath && !(Flags & LDINF_FLAG_SRCPATH_IS_URL)) {
  2375. //
  2376. // if no source information provided, then always use that provided in the PNF
  2377. // even if it might be wrong
  2378. // typically when we're replacing such an INF, we'll explicitly say
  2379. // not to load existing PNF
  2380. //
  2381. Flags |= LDINF_FLAG_ALWAYS_GET_SRCPATH;
  2382. }
  2383. if (LoadPrecompiledInf(Filename,
  2384. &(FileData->ftLastWriteTime),
  2385. OsLoaderPath,
  2386. LanguageId,
  2387. Flags,
  2388. LogContext,
  2389. &Inf,
  2390. &InfSourcePathToMigrate,
  2391. &InfSourcePathToMigrateMediaType,
  2392. &InfOriginalNameToMigrate)) {
  2393. //
  2394. // Make sure that the PNF is of the specified style.
  2395. //
  2396. if(!(Style & (DWORD)Inf->Style)) {
  2397. FreeInfFile(Inf);
  2398. DeleteLogContext(LogContext);
  2399. return ERROR_WRONG_INF_STYLE;
  2400. }
  2401. if(AppendInf) {
  2402. Inf->Prev = InfListTail;
  2403. InfListTail->Next = Inf;
  2404. }
  2405. PnfUsed = TRUE;
  2406. if(PnfWasUsed) {
  2407. *PnfWasUsed = TRUE;
  2408. }
  2409. rc = NO_ERROR;
  2410. goto clean0;
  2411. }
  2412. }
  2413. //
  2414. // If we tried to load the PNF and it failed, then check to see if we were
  2415. // returned any INF source path information to migrate to the new PNF. If
  2416. // so, then this overrides the InfSourcePath information passed into this
  2417. // routine.
  2418. //
  2419. if(InfSourcePathToMigrateMediaType != SPOST_NONE) {
  2420. //
  2421. // Discard the arguments the caller passed in and use what we retrieved
  2422. // from the old PNF instead.
  2423. //
  2424. InfSourcePath = InfSourcePathToMigrate;
  2425. if(InfSourcePathToMigrateMediaType == SPOST_PATH) {
  2426. //
  2427. // Make sure the "sourcepath is URL" bit is not set.
  2428. //
  2429. Flags &= ~LDINF_FLAG_SRCPATH_IS_URL;
  2430. } else {
  2431. //
  2432. // This is a URL path--make sure the "sourcepath is URL" bit is set.
  2433. //
  2434. Flags |= LDINF_FLAG_SRCPATH_IS_URL;
  2435. }
  2436. //
  2437. // If we're migrating source path information from the PNF, then we need
  2438. // to use the PNF-specified original INF name, as well, instead of what
  2439. // the caller may have specified.
  2440. //
  2441. OriginalInfName = InfOriginalNameToMigrate;
  2442. }
  2443. //
  2444. // We can't use a precompiled INF, so resort to reading in the textfile INF.
  2445. //
  2446. if((TextFileHandle = CreateFile(Filename,
  2447. GENERIC_READ,
  2448. FILE_SHARE_READ,
  2449. NULL,
  2450. OPEN_EXISTING,
  2451. 0,
  2452. NULL)) == INVALID_HANDLE_VALUE) {
  2453. if(InfSourcePathToMigrateMediaType != SPOST_NONE) {
  2454. if(InfSourcePathToMigrate) {
  2455. MyFree(InfSourcePathToMigrate);
  2456. }
  2457. if(InfOriginalNameToMigrate) {
  2458. MyFree(InfOriginalNameToMigrate);
  2459. }
  2460. }
  2461. DeleteLogContext(LogContext);
  2462. return GetLastError();
  2463. }
  2464. //
  2465. // Note: We don't have to worry about closing TextFileHandle from this point
  2466. // on, because the following routine will either close it for us, or copy it
  2467. // into the ReadBuffer structure, to be later closed via DestroyTextFileReadBuffer().
  2468. //
  2469. if((rc = ReadAsciiOrUnicodeTextFile(TextFileHandle, &ReadBuffer,LogContext)) == NO_ERROR) {
  2470. //
  2471. // Make sure the style (and class) matched what the caller is asking
  2472. // for and go parse the inf file in a style-specific manner.
  2473. //
  2474. Inf = NULL;
  2475. if((rc = PreprocessInf(ReadBuffer.TextBuffer,
  2476. &(ReadBuffer.TextBufferSize),
  2477. (Flags & LDINF_FLAG_MATCH_CLASS_GUID),
  2478. ClassGuidString,
  2479. LanguageId,
  2480. LogContext,
  2481. Filename,
  2482. &Win95Inf,
  2483. &StringsSectionParams)) == NO_ERROR) {
  2484. rc = ERROR_WRONG_INF_STYLE;
  2485. if(Win95Inf) {
  2486. if(Style & INF_STYLE_WIN4) {
  2487. //
  2488. // If we're dealing with a URL, then we don't have a real
  2489. // directory that we can do substitions on for DIRID_SRCPATH.
  2490. // "A:\" is about the best we can do.
  2491. //
  2492. rc = ParseNewInf(ReadBuffer.TextBuffer,
  2493. ReadBuffer.TextBufferSize,
  2494. (Flags & LDINF_FLAG_SRCPATH_IS_URL) ? pszOemInfDefaultPath
  2495. : InfSourcePath,
  2496. OsLoaderPath,
  2497. LogContext,
  2498. &Inf,
  2499. ErrorLineNumber,
  2500. &StringsSectionParams
  2501. );
  2502. }
  2503. } else {
  2504. //
  2505. // Can't append old-style file.
  2506. //
  2507. if(!AppendInf && (Style & INF_STYLE_OLDNT)) {
  2508. rc = ParseOldInf(ReadBuffer.TextBuffer,
  2509. ReadBuffer.TextBufferSize,
  2510. LogContext,
  2511. &Inf,
  2512. ErrorLineNumber
  2513. );
  2514. }
  2515. }
  2516. }
  2517. //
  2518. // Free the in-memory image of the file.
  2519. //
  2520. DestroyTextFileReadBuffer(&ReadBuffer);
  2521. if(rc == NO_ERROR) {
  2522. //
  2523. // If we get here then we've parsed the file successfully.
  2524. // Set up version block for this file.
  2525. //
  2526. *ErrorLineNumber = 0;
  2527. rc = CreateInfVersionNode(Inf, Filename, &(FileData->ftLastWriteTime));
  2528. if(rc == NO_ERROR) {
  2529. Inf->InfSourceMediaType = (Flags & LDINF_FLAG_SRCPATH_IS_URL) ? SPOST_URL
  2530. : SPOST_PATH;
  2531. if(InfSourcePath) {
  2532. //
  2533. // If the caller specified a source path (or we're migrating
  2534. // one from a previously-existing PNF), then duplicate the
  2535. // string, and store a pointer to it in our INF structure.
  2536. //
  2537. if(!(Inf->InfSourcePath = DuplicateString(InfSourcePath))) {
  2538. rc = ERROR_NOT_ENOUGH_MEMORY;
  2539. }
  2540. }
  2541. if((rc == NO_ERROR) && OriginalInfName) {
  2542. //
  2543. // If the caller specified the INF's original filename (or
  2544. // we're migrating one from a previously-existing PNF), then
  2545. // duplicate the string, and store a pointer to it in our
  2546. // INF structure.
  2547. //
  2548. // We shouldn't be storing this name if it's the same as the
  2549. // INF's present name.
  2550. //
  2551. MYASSERT(lstrcmpi(OriginalInfName, pSetupGetFileTitle(Filename)));
  2552. if(!(Inf->OriginalInfName = DuplicateString(OriginalInfName))) {
  2553. rc = ERROR_NOT_ENOUGH_MEMORY;
  2554. }
  2555. }
  2556. }
  2557. if(rc == NO_ERROR) {
  2558. //
  2559. // Store the language ID used to load this INF into the LOADED_INF structure.
  2560. //
  2561. Inf->LanguageId = LanguageId;
  2562. if (Flags & LDINF_FLAG_OEM_F6_INF) {
  2563. Inf->Flags = LIF_OEM_F6_INF;
  2564. }
  2565. //
  2566. // If we get here then we've parsed the file successfully and
  2567. // successfully created the version block. If we're allowed
  2568. // to write out a PNF file for this loaded INF, then do that
  2569. // now.
  2570. //
  2571. if(Flags & LDINF_FLAG_ALWAYS_TRY_PNF) {
  2572. rc = SavePnf(Filename, Inf);
  2573. if(rc == NO_ERROR) {
  2574. PnfSaved = TRUE;
  2575. if(PnfWasUsed) {
  2576. *PnfWasUsed = TRUE;
  2577. }
  2578. } else if(((rc == ERROR_SHARING_VIOLATION)
  2579. || ((rc == ERROR_LOCK_VIOLATION)))
  2580. && (Flags & LDINF_FLAG_ALLOW_PNF_SHARING_LOCK)) {
  2581. //
  2582. // A sharing-type error occurred
  2583. // so a PNF exists and is in USE
  2584. // and we are flagged to indicate that it's non-fatal.
  2585. //
  2586. rc = NO_ERROR;
  2587. } else if(!(Flags & LDINF_FLAG_REGENERATE_PNF)) {
  2588. //
  2589. // We weren't explicitly asked to generate a PNF, thus
  2590. // our failure to do so shouldn't be considered fatal.
  2591. //
  2592. rc = NO_ERROR;
  2593. }
  2594. }
  2595. }
  2596. if(rc == NO_ERROR) {
  2597. if(AppendInf) {
  2598. Inf->Prev = InfListTail;
  2599. InfListTail->Next = Inf;
  2600. }
  2601. } else {
  2602. FreeInfFile(Inf);
  2603. }
  2604. }
  2605. }
  2606. clean0:
  2607. if(AppendInf) {
  2608. //
  2609. // If the newly-loaded INF has any volatile system DIRIDs, or if the
  2610. // INF we're appending to has any user-defined DIRIDs, then apply those
  2611. // to the newly-appended INF now.
  2612. //
  2613. if((rc == NO_ERROR) &&
  2614. (AppendInf->UserDirIdList.UserDirIdCount || Inf->Flags & LIF_HAS_VOLATILE_DIRIDS)) {
  2615. if((rc = ApplyNewVolatileDirIdsToInfs(AppendInf, Inf)) != NO_ERROR) {
  2616. //
  2617. // So near, and yet, so far! Yank the new INF out of the linked
  2618. // list, and free it.
  2619. //
  2620. MYASSERT(Inf->Prev);
  2621. Inf->Prev->Next = Inf->Next;
  2622. FreeInfFile(Inf);
  2623. }
  2624. }
  2625. if(rc == NO_ERROR) {
  2626. *LoadedInf = AppendInf;
  2627. }
  2628. } else if(rc == NO_ERROR) {
  2629. //
  2630. // We're not append-loading the INF, thus there's no user-defined
  2631. // DIRID substitutions to worry about. However, if the INF has volatile
  2632. // system DIRIDs, then we still need to apply paths to those DIRIDs now
  2633. // (unless the caller said to skip it).
  2634. //
  2635. if((Inf->Flags & LIF_HAS_VOLATILE_DIRIDS) &&
  2636. !(Flags & LDINF_FLAG_IGNORE_VOLATILE_DIRIDS)) {
  2637. rc = ApplyNewVolatileDirIdsToInfs(Inf, NULL);
  2638. }
  2639. if(rc == NO_ERROR) {
  2640. *LoadedInf = Inf;
  2641. } else {
  2642. FreeInfFile(Inf);
  2643. }
  2644. }
  2645. if (rc == NO_ERROR) {
  2646. //
  2647. // log that the INF was loaded
  2648. //
  2649. WriteLogEntry(
  2650. LogContext,
  2651. SETUP_LOG_VVERBOSE,
  2652. (PnfUsed ? MSG_LOG_OPENED_PNF
  2653. : (PnfSaved ? MSG_LOG_SAVED_PNF : MSG_LOG_OPENED_INF)),
  2654. NULL,
  2655. Filename,
  2656. LanguageId);
  2657. }
  2658. if(InfSourcePathToMigrateMediaType != SPOST_NONE) {
  2659. if(InfSourcePathToMigrate) {
  2660. MyFree(InfSourcePathToMigrate);
  2661. }
  2662. if(InfOriginalNameToMigrate) {
  2663. MyFree(InfOriginalNameToMigrate);
  2664. }
  2665. }
  2666. DeleteLogContext(LogContext);
  2667. return rc;
  2668. }
  2669. VOID
  2670. FreeInfFile(
  2671. IN PLOADED_INF LoadedInf
  2672. )
  2673. /*++
  2674. Routine Description:
  2675. Unload an inf file, freeing all resources used by its internal
  2676. representation.
  2677. Arguments:
  2678. Inf - supplies a pointer to the inf descriptor for the loaded inf file.
  2679. Return Value:
  2680. None.
  2681. --*/
  2682. {
  2683. if(LockInf(LoadedInf)) {
  2684. DestroySynchronizedAccess(&LoadedInf->Lock);
  2685. FreeLoadedInfDescriptor(LoadedInf);
  2686. }
  2687. }
  2688. BOOL
  2689. AddDatumToVersionBlock(
  2690. IN OUT PINF_VERSION_NODE VersionNode,
  2691. IN PCTSTR DatumName,
  2692. IN PCTSTR DatumValue
  2693. )
  2694. /*++
  2695. Routine Description:
  2696. Append an inf version datum to the version node.
  2697. Arguments:
  2698. VersionNode - supplies pointer to the version node.
  2699. DatumName - supplies name of the datum.
  2700. DatumValue - supplies datum's value.
  2701. Return Value:
  2702. FALSE if OOM.
  2703. TRUE if datum added successfully. Various fields in the VersionNode
  2704. will have been updated.
  2705. --*/
  2706. {
  2707. UINT RequiredSpace;
  2708. UINT NameLength, ValueLength;
  2709. PTSTR NewDataBlock;
  2710. NameLength = lstrlen(DatumName) + 1;
  2711. ValueLength = lstrlen(DatumValue) + 1;
  2712. //
  2713. // The space needed to store the datum is the existing space plus
  2714. // the length of the 2 strings and their nul bytes.
  2715. //
  2716. RequiredSpace = VersionNode->DataSize + ((NameLength + ValueLength) * sizeof(TCHAR));
  2717. if(VersionNode->DataBlock) {
  2718. NewDataBlock = MyTaggedRealloc((PVOID)(VersionNode->DataBlock), RequiredSpace, MEMTAG_VBDATA);
  2719. } else {
  2720. NewDataBlock = MyTaggedMalloc(RequiredSpace, MEMTAG_VBDATA);
  2721. }
  2722. if(!NewDataBlock) {
  2723. return FALSE;
  2724. }
  2725. //
  2726. // Place the datum name in the version block.
  2727. //
  2728. lstrcpy((PTSTR)((PUCHAR)NewDataBlock + VersionNode->DataSize), DatumName);
  2729. VersionNode->DataSize += NameLength * sizeof(TCHAR);
  2730. //
  2731. // Place the datum value in the version block.
  2732. //
  2733. lstrcpy((PTSTR)((PUCHAR)NewDataBlock + VersionNode->DataSize), DatumValue);
  2734. VersionNode->DataSize += ValueLength * sizeof(TCHAR);
  2735. VersionNode->DatumCount++;
  2736. VersionNode->DataBlock = NewDataBlock;
  2737. return TRUE;
  2738. }
  2739. DWORD
  2740. ProcessNewInfVersionBlock(
  2741. IN PLOADED_INF Inf
  2742. )
  2743. /*++
  2744. Routine Description:
  2745. Set up a version node for a new-style inf file. The version node is
  2746. simply a mirror of the [Version] section in the file.
  2747. Since this routine is only called at INF load time, no locking is done.
  2748. Also, since we are guaranteed that this will operate on a single INF
  2749. only, we don't have to worry about traversing a linked list of INFs.
  2750. Arguments:
  2751. Inf - supplies a pointer to the inf descriptor for the file.
  2752. Return Value:
  2753. Win32 error code (with inf extensions) indicating outcome.
  2754. --*/
  2755. {
  2756. PINF_SECTION Section;
  2757. PINF_LINE Line;
  2758. UINT u;
  2759. BOOL b;
  2760. //
  2761. // Locate the [Version] section.
  2762. //
  2763. if(Section = InfLocateSection(Inf, pszVersion, NULL)) {
  2764. //
  2765. // Iterate each line in the section. If the line has a key and at least one
  2766. // other value, then it counts as a version datum. Otherwise ignore it.
  2767. //
  2768. for(u = 0, Line = &Inf->LineBlock[Section->Lines];
  2769. u < Section->LineCount;
  2770. u++, Line++)
  2771. {
  2772. if(HASKEY(Line)) {
  2773. MYASSERT(Line->ValueCount > 2);
  2774. //
  2775. // Use the case-sensitive key name.
  2776. //
  2777. b = AddDatumToVersionBlock(
  2778. &(Inf->VersionBlock),
  2779. pStringTableStringFromId(Inf->StringTable, Inf->ValueBlock[Line->Values+1]),
  2780. pStringTableStringFromId(Inf->StringTable, Inf->ValueBlock[Line->Values+2])
  2781. );
  2782. if(!b) {
  2783. return ERROR_NOT_ENOUGH_MEMORY;
  2784. }
  2785. }
  2786. }
  2787. }
  2788. return NO_ERROR;
  2789. }
  2790. DWORD
  2791. CreateInfVersionNode(
  2792. IN PLOADED_INF Inf,
  2793. IN PCTSTR Filename,
  2794. IN PFILETIME LastWriteTime
  2795. )
  2796. /*++
  2797. Routine Description:
  2798. Set up a version node for an inf file, and link it into the list of INFs for
  2799. the specified LOADED_INF structure.
  2800. THIS ROUTINE ASSUMES THAT THE VERSION BLOCK STRUCTURE IN THE INF HAS BEEN
  2801. ZEROED OUT.
  2802. Arguments:
  2803. Inf - supplies pointer to descriptor for loaded inf file.
  2804. Filename - supplies (fully-qualified) filename used to load inf file.
  2805. LastWriteTime - supplies a pointer to a FILETIME structure specifying
  2806. the time that the INF was last written to.
  2807. Return Value:
  2808. Win32 error code (with inf extensions) indicating outcome.
  2809. --*/
  2810. {
  2811. MYASSERT(!(Inf->VersionBlock.DataBlock));
  2812. MYASSERT(!(Inf->VersionBlock.DataSize));
  2813. MYASSERT(!(Inf->VersionBlock.DatumCount));
  2814. //
  2815. // Fill in the filename and other fields in the version descriptor.
  2816. //
  2817. Inf->VersionBlock.LastWriteTime = *LastWriteTime;
  2818. Inf->VersionBlock.FilenameSize = (lstrlen(Filename) + 1) * sizeof(TCHAR);
  2819. CopyMemory(Inf->VersionBlock.Filename, Filename, Inf->VersionBlock.FilenameSize);
  2820. //
  2821. // Style-specific processing.
  2822. //
  2823. return((Inf->Style == INF_STYLE_WIN4) ? ProcessNewInfVersionBlock(Inf)
  2824. : ProcessOldInfVersionBlock(Inf));
  2825. }
  2826. /////////////////////////////////////////////
  2827. //
  2828. // Inf data access functions
  2829. //
  2830. /////////////////////////////////////////////
  2831. #ifdef UNICODE
  2832. BOOL
  2833. WINAPI
  2834. SetupEnumInfSectionsA (
  2835. IN HINF InfHandle,
  2836. IN UINT Index,
  2837. OUT PSTR Buffer, OPTIONAL
  2838. IN UINT Size, OPTIONAL
  2839. OUT UINT *SizeNeeded OPTIONAL
  2840. )
  2841. /*++
  2842. Routine Description:
  2843. See SetupEnumInfSections
  2844. Ansi Wrapper
  2845. --*/
  2846. {
  2847. UINT UniSize;
  2848. UINT AnsiSize;
  2849. BOOL f;
  2850. PWSTR UniBuffer;
  2851. PSTR AnsiBuffer;
  2852. DWORD rc;
  2853. f = SetupEnumInfSectionsW(InfHandle,Index,NULL,0,&UniSize);
  2854. if(!f) {
  2855. return FALSE;
  2856. }
  2857. UniBuffer = (PWSTR)MyMalloc(UniSize*sizeof(WCHAR));
  2858. if(!UniBuffer) {
  2859. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  2860. return FALSE;
  2861. }
  2862. f = SetupEnumInfSectionsW(InfHandle,Index,UniBuffer,UniSize,NULL);
  2863. if(!f) {
  2864. rc = GetLastError();
  2865. MYASSERT(f);
  2866. MyFree(UniBuffer);
  2867. SetLastError(rc);
  2868. return FALSE;
  2869. }
  2870. AnsiBuffer = pSetupUnicodeToAnsi(UniBuffer);
  2871. MyFree(UniBuffer);
  2872. if(!AnsiBuffer) {
  2873. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  2874. return FALSE;
  2875. }
  2876. AnsiSize = strlen(AnsiBuffer)+1;
  2877. try {
  2878. if(SizeNeeded) {
  2879. *SizeNeeded = AnsiSize;
  2880. }
  2881. if (Buffer) {
  2882. if(Size<AnsiSize) {
  2883. rc = ERROR_INSUFFICIENT_BUFFER;
  2884. } else {
  2885. strcpy(Buffer,AnsiBuffer);
  2886. rc = NO_ERROR;
  2887. }
  2888. } else if(Size) {
  2889. rc = ERROR_INVALID_USER_BUFFER;
  2890. } else {
  2891. rc = NO_ERROR;
  2892. }
  2893. } except(EXCEPTION_EXECUTE_HANDLER) {
  2894. //
  2895. // Assume InfHandle was bad pointer
  2896. //
  2897. rc = ERROR_INVALID_DATA;
  2898. }
  2899. MyFree(AnsiBuffer);
  2900. SetLastError(rc);
  2901. return (rc == NO_ERROR);
  2902. }
  2903. #else
  2904. BOOL
  2905. WINAPI
  2906. SetupEnumInfSectionsW (
  2907. IN HINF InfHandle,
  2908. IN UINT Index,
  2909. OUT PWSTR Buffer, OPTIONAL
  2910. IN UINT Size, OPTIONAL
  2911. OUT UINT *SizeNeeded OPTIONAL
  2912. )
  2913. /*++
  2914. Routine Description:
  2915. See SetupEnumInfSections
  2916. Unicode Stub for ANSI SetupAPI
  2917. --*/
  2918. {
  2919. UNREFERENCED_PARAMETER(InfHandle);
  2920. UNREFERENCED_PARAMETER(Buffer);
  2921. UNREFERENCED_PARAMETER(Size);
  2922. UNREFERENCED_PARAMETER(SizeNeeded);
  2923. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  2924. return(FALSE);
  2925. }
  2926. #endif
  2927. BOOL
  2928. WINAPI
  2929. SetupEnumInfSections (
  2930. IN HINF InfHandle,
  2931. IN UINT Index,
  2932. OUT PTSTR Buffer, OPTIONAL
  2933. IN UINT Size, OPTIONAL
  2934. OUT UINT *SizeNeeded OPTIONAL
  2935. )
  2936. /*++
  2937. Routine Description:
  2938. Enumerate Sections of a single INF (ignoring any attached INF's)
  2939. Start with Index==0 and keep incrementing Index until ERROR_NO_MORE_ITEMS
  2940. is returned.
  2941. section name is copied into Buffer.
  2942. Arguments:
  2943. InfHandle - Specifies the handle to an open INF file
  2944. Index - enumeration index, not related to order sections are in INF
  2945. Buffer - Receives a single section name
  2946. Size - Specifies the size of Buffer, in characters
  2947. SizeNeeded - Receives the size of Buffer needed, in characters
  2948. Return Value:
  2949. TRUE if the function succeeds, or FALSE if not.
  2950. --*/
  2951. {
  2952. DWORD rc = NO_ERROR;
  2953. LPTSTR section;
  2954. UINT actsz;
  2955. PLOADED_INF pInf = (PLOADED_INF)InfHandle;
  2956. try {
  2957. if(!LockInf(pInf)) {
  2958. rc = ERROR_INVALID_HANDLE;
  2959. }
  2960. } except(EXCEPTION_EXECUTE_HANDLER) {
  2961. //
  2962. // Assume InfHandle was bad pointer
  2963. //
  2964. rc = ERROR_INVALID_HANDLE;
  2965. }
  2966. if (rc != NO_ERROR) {
  2967. SetLastError (rc);
  2968. return FALSE;
  2969. }
  2970. try {
  2971. if(Index >= pInf->SectionCount) {
  2972. rc = ERROR_NO_MORE_ITEMS;
  2973. leave;
  2974. }
  2975. section = pStringTableStringFromId(pInf->StringTable, pInf->SectionBlock[Index].SectionName);
  2976. if(section == NULL) {
  2977. MYASSERT(section);
  2978. rc = ERROR_INVALID_DATA;
  2979. leave;
  2980. }
  2981. actsz = lstrlen(section)+1;
  2982. if(SizeNeeded) {
  2983. *SizeNeeded = actsz;
  2984. }
  2985. if (Buffer) {
  2986. if(Size<actsz) {
  2987. rc = ERROR_INSUFFICIENT_BUFFER;
  2988. } else {
  2989. _tcscpy(Buffer,section);
  2990. rc = NO_ERROR;
  2991. }
  2992. } else if(Size) {
  2993. rc = ERROR_INVALID_USER_BUFFER;
  2994. } else {
  2995. rc = NO_ERROR;
  2996. }
  2997. } except(EXCEPTION_EXECUTE_HANDLER) {
  2998. //
  2999. // Assume InfHandle was bad pointer
  3000. //
  3001. rc = ERROR_INVALID_DATA;
  3002. }
  3003. UnlockInf(pInf);
  3004. SetLastError(rc);
  3005. return (rc == NO_ERROR);
  3006. }
  3007. //
  3008. // NTRAID#207847-JamieHun-2000/10/19 Fix users of (p)SetupGetInfSections
  3009. //
  3010. // pSectionEnumWorker and pSetupGetInfSections are busted implementations
  3011. // of obtaining a list of INF sections
  3012. //
  3013. // we have to leave them in until all internal tools get updated
  3014. //
  3015. VOID
  3016. pSectionEnumWorker (
  3017. IN PCTSTR String,
  3018. IN OUT PSECTION_ENUM_PARAMS Params
  3019. )
  3020. /*++
  3021. Routine Description:
  3022. Callback that receives each section name. It copies the string
  3023. to a supplied buffer (if available), and also keeps track of the
  3024. total size regardless if a buffer was supplied.
  3025. Arguments:
  3026. String - Specifies the section name
  3027. Params - Specifies a pointer to a SECTION_ENUM_PARAMS structure.
  3028. Receives the section appended to the supplied buffer (if
  3029. necessary) and an updated total buffer size.
  3030. Return Value:
  3031. Always TRUE.
  3032. --*/
  3033. {
  3034. UINT Size;
  3035. if (!String) {
  3036. MYASSERT(FALSE);
  3037. return;
  3038. }
  3039. Size = (UINT)((PBYTE) _tcschr (String, 0) - (PBYTE) String) + sizeof(TCHAR);
  3040. Params->SizeNeeded += Size;
  3041. if (Params->Size > Params->SizeNeeded) {
  3042. if (Params->Buffer) {
  3043. _tcscpy (Params->End, String);
  3044. Params->End = _tcschr (Params->End, 0);
  3045. Params->End++;
  3046. }
  3047. }
  3048. }
  3049. BOOL
  3050. pSetupGetInfSections (
  3051. IN HINF InfHandle,
  3052. OUT PTSTR Buffer, OPTIONAL
  3053. IN UINT Size, OPTIONAL
  3054. OUT UINT *SizeNeeded OPTIONAL
  3055. )
  3056. /*++
  3057. Routine Description:
  3058. Make a multi-sz list of section names by enumerating the section
  3059. string table and copying them into a caller-supplied buffer.
  3060. Caller can also request the size needed without supplying a
  3061. buffer.
  3062. This function was implemented for the Win9x upgrade and is NOT
  3063. exposed as a public API nor an ANSI version.
  3064. Arguments:
  3065. Inf - Specifies the handle to an open INF file
  3066. Buffer - Receives a multi-sz list of section names
  3067. Size - Specifies the size of Buffer, in bytes
  3068. SizeNeeded - Receives the size of Buffer needed, in bytes
  3069. Return Value:
  3070. TRUE if the function succeeds, or FALSE if not.
  3071. --*/
  3072. {
  3073. PLOADED_INF Inf;
  3074. DWORD rc = NO_ERROR;
  3075. SECTION_ENUM_PARAMS Params;
  3076. PBYTE p;
  3077. PINF_SECTION Section;
  3078. UINT u;
  3079. //
  3080. // Init the enum worker params
  3081. //
  3082. Params.Buffer = Buffer;
  3083. Params.Size = Buffer ? Size : 0;
  3084. Params.SizeNeeded = 0;
  3085. Params.End = Buffer;
  3086. //
  3087. // Validate buffer arg
  3088. //
  3089. try {
  3090. if (Buffer) {
  3091. p = (PBYTE) Buffer;
  3092. p[0] = 0;
  3093. p[Size - 1] = 0;
  3094. }
  3095. } except(EXCEPTION_EXECUTE_HANDLER) {
  3096. rc = ERROR_INVALID_PARAMETER;
  3097. }
  3098. if (rc != NO_ERROR) {
  3099. SetLastError (rc);
  3100. return FALSE;
  3101. }
  3102. //
  3103. // Lock the INF
  3104. //
  3105. try {
  3106. if(!LockInf((PLOADED_INF)InfHandle)) {
  3107. rc = ERROR_INVALID_HANDLE;
  3108. }
  3109. } except(EXCEPTION_EXECUTE_HANDLER) {
  3110. //
  3111. // Assume InfHandle was bad pointer
  3112. //
  3113. rc = ERROR_INVALID_HANDLE;
  3114. }
  3115. if (rc != NO_ERROR) {
  3116. SetLastError (rc);
  3117. return FALSE;
  3118. }
  3119. //
  3120. // Traverse the linked list of loaded INFs, enumerating each INF's
  3121. // sections.
  3122. //
  3123. try {
  3124. for(Inf = (PLOADED_INF)InfHandle; Inf; Inf = Inf->Next) {
  3125. //
  3126. // Enumerate the sections
  3127. //
  3128. for(u=0,Section=Inf->SectionBlock; u<Inf->SectionCount; u++,Section++) {
  3129. pSectionEnumWorker (
  3130. pStringTableStringFromId (Inf->StringTable, Section->SectionName),
  3131. &Params
  3132. );
  3133. }
  3134. }
  3135. } except(EXCEPTION_EXECUTE_HANDLER) {
  3136. rc = ERROR_INVALID_PARAMETER;
  3137. }
  3138. //
  3139. // Update the structure and OUT params for the last time
  3140. //
  3141. try {
  3142. if (rc == NO_ERROR) {
  3143. Params.SizeNeeded += sizeof(TCHAR);
  3144. if (SizeNeeded) {
  3145. *SizeNeeded = Params.SizeNeeded;
  3146. }
  3147. if (Params.Buffer && Params.Size >= Params.SizeNeeded) {
  3148. *Params.End = 0;
  3149. } else if (Params.Buffer) {
  3150. rc = ERROR_INSUFFICIENT_BUFFER;
  3151. }
  3152. }
  3153. } except (EXCEPTION_EXECUTE_HANDLER) {
  3154. rc = ERROR_INVALID_PARAMETER;
  3155. }
  3156. //
  3157. // Unlock the INF
  3158. //
  3159. UnlockInf((PLOADED_INF)InfHandle);
  3160. return rc == NO_ERROR;
  3161. }
  3162. PINF_SECTION
  3163. InfLocateSection(
  3164. IN PLOADED_INF Inf,
  3165. IN PCTSTR SectionName,
  3166. OUT PUINT SectionNumber OPTIONAL
  3167. )
  3168. /*++
  3169. Routine Description:
  3170. Locate a section within an inf file. This routine DOES NOT traverse a
  3171. linked list of INFs, looking for the section in each.
  3172. THIS ROUTINE DOES NOT LOCK THE INF--THE CALLER MUST HANDLE IT!!!
  3173. Arguments:
  3174. Inf - supplies a pointer to the inf descriptor for the loaded inf file.
  3175. SectionName - Supplies the name of the section to be located.
  3176. SectionNumber - if specified, receives the ordinal number of
  3177. the section.
  3178. Return Value:
  3179. Pointer to the section descriptor, or NULL if the section
  3180. does not exist.
  3181. --*/
  3182. {
  3183. LONG StringId;
  3184. PINF_SECTION Section;
  3185. UINT u;
  3186. DWORD StringLength;
  3187. TCHAR TempString[MAX_SECT_NAME_LEN];
  3188. //
  3189. // Make a copy of the SectionName into a modifiable buffer to speed
  3190. // the lookup.
  3191. //
  3192. lstrcpyn(TempString, SectionName, SIZECHARS(TempString));
  3193. //
  3194. // Start from the beginning.
  3195. //
  3196. StringId = pStringTableLookUpString(Inf->StringTable,
  3197. TempString,
  3198. &StringLength,
  3199. NULL,
  3200. NULL,
  3201. STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE,
  3202. NULL,0
  3203. );
  3204. if(StringId == -1) {
  3205. return(NULL);
  3206. }
  3207. for(u=0,Section=Inf->SectionBlock; u<Inf->SectionCount; u++,Section++) {
  3208. if(Section->SectionName == StringId) {
  3209. if(SectionNumber) {
  3210. *SectionNumber = u;
  3211. }
  3212. return(Section);
  3213. }
  3214. }
  3215. return(NULL);
  3216. }
  3217. BOOL
  3218. InfLocateLine(
  3219. IN PLOADED_INF Inf,
  3220. IN PINF_SECTION Section,
  3221. IN PCTSTR Key, OPTIONAL
  3222. IN OUT PUINT LineNumber,
  3223. OUT PINF_LINE *Line
  3224. )
  3225. /*++
  3226. Routine Description:
  3227. Locate a line within a section. This routine DOES NOT traverse a
  3228. linked list of INFs, looking for the section in each.
  3229. THIS ROUTINE DOES NOT LOCK THE INF--THE CALLER MUST HANDLE IT!!!
  3230. Arguments:
  3231. Inf - supplies a pointer to the inf descriptor for the loaded inf file.
  3232. SectionName - Supplies a pointer to the section descriptor for the section
  3233. to be searched.
  3234. Key - if specified, supplies the key of the line to look for.
  3235. LineNumber - on input, supplies the line number of the line where the
  3236. search is to begin. On output, receives the line number of the
  3237. line where the match was found
  3238. Line - receives a pointer to the line descriptor for the line
  3239. where the match was found.
  3240. Return Value:
  3241. TRUE if line is found, FALSE otherwise.
  3242. --*/
  3243. {
  3244. PINF_LINE line;
  3245. UINT u;
  3246. LONG StringId;
  3247. DWORD StringLength;
  3248. TCHAR TempString[MAX_STRING_LENGTH];
  3249. if(Key) {
  3250. //
  3251. // Copy the key name into a modifiable buffer to speed up the string table API.
  3252. //
  3253. lstrcpyn(TempString, Key, SIZECHARS(TempString));
  3254. StringId = pStringTableLookUpString(Inf->StringTable,
  3255. TempString,
  3256. &StringLength,
  3257. NULL,
  3258. NULL,
  3259. STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE,
  3260. NULL,0
  3261. );
  3262. if(StringId == -1) {
  3263. return FALSE;
  3264. }
  3265. for(u = *LineNumber, line = &Inf->LineBlock[Section->Lines + (*LineNumber)];
  3266. u < Section->LineCount;
  3267. u++, line++)
  3268. {
  3269. if(ISSEARCHABLE(line) && (Inf->ValueBlock[line->Values] == StringId)) {
  3270. *Line = line;
  3271. *LineNumber = u;
  3272. return TRUE;
  3273. }
  3274. }
  3275. } else {
  3276. if(*LineNumber < Section->LineCount) {
  3277. *Line = &Inf->LineBlock[Section->Lines + (*LineNumber)];
  3278. return TRUE;
  3279. }
  3280. }
  3281. return FALSE;
  3282. }
  3283. PTSTR
  3284. InfGetField(
  3285. IN PLOADED_INF Inf,
  3286. IN PINF_LINE InfLine,
  3287. IN UINT ValueNumber,
  3288. OUT PLONG StringId OPTIONAL
  3289. )
  3290. /*++
  3291. Routine Description:
  3292. Retrieve the key or a value from a specified line in an inf file.
  3293. THIS ROUTINE DOES NOT DO LOCKING!!!
  3294. Arguments:
  3295. Inf - supplies a pointer to the inf descriptor for the loaded inf file.
  3296. InfLine - supplies a pointer to the line descriptor for the line
  3297. from which the value is to be fetched. THIS LINE MUST BE CONTAINED
  3298. WITHIN THE SPECIFIED INF!!
  3299. ValueNumber - supplies the index for the value to retreive. If a line has a key,
  3300. the key is value #0 and other values start at 1. If a line does not have a
  3301. key, values start at 1. For Win95 INF compatibility, if there's only a single
  3302. value on the line (i.e., no '=' to denote it as a key), we'll consider it to
  3303. be both a key and the first value (either 0 or 1 will work).
  3304. StringId - if specified, receives the string table id of the value.
  3305. Return Value:
  3306. Pointer to the value, or NULL if not found. The caller must not write into
  3307. or otherwise alter this string.
  3308. --*/
  3309. {
  3310. LONG stringId;
  3311. PTSTR ret = NULL;
  3312. //
  3313. // Adjust the value number.
  3314. //
  3315. if(HASKEY(InfLine)) {
  3316. //
  3317. // All field references are shifted up by one, to account for the two
  3318. // copies of the key (first is case insensative)
  3319. //
  3320. ValueNumber++;
  3321. if(ValueNumber==0) {
  3322. //
  3323. // wrap
  3324. //
  3325. return NULL;
  3326. }
  3327. } else {
  3328. if(ISSEARCHABLE(InfLine)) {
  3329. //
  3330. // lines that consist of one value "VaLue" are treated like "value=VaLue"
  3331. // this is such a line, and is recognized because HASKEY is FALSE but
  3332. // ISSEARCHABLE is TRUE
  3333. //
  3334. // We want to return the second of the two, since it's the one that was
  3335. // stored case-sensitively.
  3336. //
  3337. if(ValueNumber > 1) {
  3338. return NULL;
  3339. } else {
  3340. ValueNumber = 1;
  3341. }
  3342. } else {
  3343. //
  3344. // This line is not searchable, so asking for value #0 is an error.
  3345. //
  3346. if(ValueNumber) {
  3347. ValueNumber--;
  3348. } else {
  3349. return NULL;
  3350. }
  3351. }
  3352. }
  3353. //
  3354. // Get the value.
  3355. //
  3356. if(ValueNumber < InfLine->ValueCount) {
  3357. stringId = Inf->ValueBlock[InfLine->Values+ValueNumber];
  3358. if(StringId) {
  3359. *StringId = stringId;
  3360. }
  3361. return pStringTableStringFromId(Inf->StringTable, stringId);
  3362. }
  3363. return NULL;
  3364. }
  3365. PTSTR
  3366. InfGetKeyOrValue(
  3367. IN PLOADED_INF Inf,
  3368. IN PCTSTR SectionName,
  3369. IN PCTSTR LineKey, OPTIONAL
  3370. IN UINT LineNumber, OPTIONAL
  3371. IN UINT ValueNumber,
  3372. OUT PLONG StringId OPTIONAL
  3373. )
  3374. /*++
  3375. Routine Description:
  3376. Retrieve the key or a value from a specified line in an inf file.
  3377. Arguments:
  3378. Inf - supplies a pointer to the inf descriptor for the loaded inf file.
  3379. SectionName - supplies the name of the section where the value is located.
  3380. LineKey - if specified, supplies the key name for the line where the
  3381. value is located. If not specified, LineNumber is used instead.
  3382. LineNumber - if LineKey is not specified, supplies the 0-based line number
  3383. within the section where the value is located.
  3384. ValueNumber - supplies the index for the value to retreive. If a line has a key,
  3385. the key is value #0 and other values start at 1. If a line does not have a
  3386. key, values start at 1.
  3387. StringId - if specified, receives the string table id of the value.
  3388. Return Value:
  3389. Pointer to the value, or NULL if not found. The caller must not write into
  3390. or otherwise alter this string.
  3391. --*/
  3392. {
  3393. INFCONTEXT InfContext;
  3394. PINF_LINE Line;
  3395. PTSTR String;
  3396. if(LineKey) {
  3397. if(!SetupFindFirstLine((HINF)Inf, SectionName, LineKey, &InfContext)) {
  3398. return NULL;
  3399. }
  3400. } else {
  3401. if(!SetupGetLineByIndex((HINF)Inf, SectionName, LineNumber, &InfContext)) {
  3402. return NULL;
  3403. }
  3404. }
  3405. Line = InfLineFromContext(&InfContext);
  3406. //
  3407. // The above routines do their own locking. The following routine, however, does
  3408. // not, so we must lock the INF before preceding.
  3409. //
  3410. if(LockInf(Inf)) {
  3411. String = InfGetField(Inf, Line, ValueNumber, StringId);
  3412. UnlockInf(Inf);
  3413. } else {
  3414. String = NULL;
  3415. }
  3416. return String;
  3417. }
  3418. PVOID
  3419. InitializeStringTableFromPNF(
  3420. IN PPNF_HEADER PnfHeader,
  3421. IN LCID Locale
  3422. )
  3423. {
  3424. PVOID StringTable = NULL;
  3425. try {
  3426. StringTable = InitializeStringTableFromMemoryMappedFile(
  3427. (PUCHAR)PnfHeader + PnfHeader->StringTableBlockOffset,
  3428. PnfHeader->StringTableBlockSize,
  3429. Locale,
  3430. 0
  3431. );
  3432. } except(EXCEPTION_EXECUTE_HANDLER) {
  3433. }
  3434. return StringTable;
  3435. }
  3436. BOOL
  3437. LoadPrecompiledInf(
  3438. IN PCTSTR Filename,
  3439. IN PFILETIME LastWriteTime,
  3440. IN PCTSTR OsLoaderPath, OPTIONAL
  3441. IN DWORD LanguageId,
  3442. IN DWORD Flags,
  3443. IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
  3444. OUT PLOADED_INF *Inf,
  3445. OUT PTSTR *InfSourcePathToMigrate, OPTIONAL
  3446. OUT PDWORD InfSourcePathToMigrateMediaType, OPTIONAL
  3447. OUT PTSTR *InfOriginalNameToMigrate OPTIONAL
  3448. )
  3449. /*++
  3450. Routine Description:
  3451. This routine attempts to find a .PNF (Precompiled iNF) file corresponding to
  3452. the specified .INF name. If located, the .PNF is mapped into memory as a
  3453. LOADED_INF. To ensure that the INF hasn't changed since being compiled, the
  3454. INF's LastWriteTime, as stored in the .PNF's version block, is checked against
  3455. the LastWriteTime passed into this routine. If the two are different, then the
  3456. .PNF is out-of-sync, and is discarded from memory and deleted from the disk.
  3457. Arguments:
  3458. Filename - supplies the name of the INF file whose precompiled form is to be loaded.
  3459. This should be a fully qualified path (i.e., as returned by GetFullPathName).
  3460. LastWriteTime - supplies the last-write time for the INF.
  3461. OsLoaderPath - optionally, supplies path of the current OsLoader directory
  3462. (e.g., "C:\os\winnt40"). If the specified PNF contains references to
  3463. the system partition, then its stored OsLoaderPath must match this path
  3464. in order for the PNF to be valid. If this parameter is not specified,
  3465. the OsLoader path is dynamically retrieved for comparison (unless the
  3466. LDINF_FLAG_IGNORE_VOLATILE_DIRIDS flag is specified).
  3467. LanguageId - supplies the language ID that must match the language ID stored in the
  3468. PNF in order for the PNF to be used (ignored if LDINF_FLAG_IGNORE_LANGUAGE is
  3469. specified).
  3470. Flags - supplies flags that modify the behavior of this routine. The following
  3471. flags are currently recognized:
  3472. LDINF_FLAG_IGNORE_VOLATILE_DIRIDS - If specified, then no validation
  3473. will be done on the stored OsLoaderPath present in the PNF. Since
  3474. dynamically retrieving the current path is time consuming, this
  3475. flag should be specified as an optimization if it is known that the
  3476. relevant DIRIDs are not going to be needed.
  3477. LDINF_FLAG_IGNORE_LANGUAGE - If specified, then no validation will be done on
  3478. the language ID stored in the PNF. This flag should only be used if no data
  3479. is to be retrieved from the INF (e.g., if we're just interested in finding
  3480. out if this is an old- or new-style INF).
  3481. LogContext - if supplied, is a log context to be inherited
  3482. Inf - supplies the address of the variable that receives the LOADED_INF pointer,
  3483. if a valid .PNF is located.
  3484. InfSourcePathToMigrate - Optionally, supplies the address of a string pointer
  3485. that receives the address of a newly-allocated string buffer containing
  3486. the source path associated with the INF's PNF that, while valid, was
  3487. discarded because of a change in one of the stored system parameters
  3488. (e.g., OS loader path, windir path, language ID). This parameter will
  3489. only be filled in upon unsuccessful return. The type of path returned
  3490. is dependent upon the value received by the InfSourcePathToMigrateMediaType
  3491. argument, described below. ** THE CALLER MUST FREE THIS STRING **
  3492. InfSourcePathToMigrateMediaType - Optionally, supplies the address of a
  3493. variable that will be set whenever InfSourcePathToMigrate is returned.
  3494. This value indicates the type of source path we're talking about. It
  3495. can be one of the following values:
  3496. SPOST_PATH - InfSourcePathToMigrate is a pointer to a standard file path
  3497. SPOST_URL - If InfSourcePathToMigrate is NULL, then this INF came from
  3498. the Windows Update (aka, Code Download Manager) website. Otherwise,
  3499. InfSourcePathToMigrate indicates the URL where the INF came from.
  3500. InfOriginalNameToMigrate - Optionally, supplies the address of a string pointer
  3501. that receives the address of a newly-allocated string buffer containing
  3502. the original name of the associated INF (sans path). Like the
  3503. InfSourcePathToMigrate and InfSourcePathToMigrateMediaType arguments
  3504. described above, this argument is only filled in upon unsuccessful return
  3505. for a PNF that, while structurally sound, was invalid because of a system
  3506. parameter mismatch. ** THE CALLER MUST FREE THIS STRING **
  3507. Return Value:
  3508. If the PNF was successfully loaded, the return value is TRUE, otherwise, it
  3509. is FALSE.
  3510. --*/
  3511. {
  3512. TCHAR CharBuffer[MAX_PATH];
  3513. PTSTR PnfFileName, PnfFileExt;
  3514. DWORD FileSize;
  3515. HANDLE FileHandle, MappingHandle;
  3516. PVOID BaseAddress;
  3517. BOOL IsPnfFile = FALSE;
  3518. BOOL TimeDateMatch = FALSE;
  3519. PPNF_HEADER PnfHeader;
  3520. PLOADED_INF NewInf;
  3521. BOOL NeedToDestroyLock, MinorVer1FieldsAvailable;
  3522. PBYTE PnfImageEnd;
  3523. DWORD TempStringLen;
  3524. DWORD err;
  3525. //
  3526. // Either InfSourcePathToMigrate, InfSourcePathToMigrateMediaType, and
  3527. // InfOriginalNameToMigrate must all be specified, or none of them may be
  3528. // specified.
  3529. //
  3530. MYASSERT((InfSourcePathToMigrate && InfSourcePathToMigrateMediaType && InfOriginalNameToMigrate) ||
  3531. !(InfSourcePathToMigrate || InfSourcePathToMigrateMediaType || InfOriginalNameToMigrate));
  3532. if(InfSourcePathToMigrate) {
  3533. *InfSourcePathToMigrate = NULL;
  3534. *InfSourcePathToMigrateMediaType = SPOST_NONE;
  3535. *InfOriginalNameToMigrate = NULL;
  3536. }
  3537. lstrcpyn(CharBuffer, Filename, SIZECHARS(CharBuffer));
  3538. //
  3539. // Find the start of the filename component of the path, and then find the last
  3540. // period (if one exists) in that filename.
  3541. //
  3542. PnfFileName = (PTSTR)pSetupGetFileTitle(CharBuffer);
  3543. if(!(PnfFileExt = _tcsrchr(PnfFileName, TEXT('.')))) {
  3544. PnfFileExt = CharBuffer + lstrlen(CharBuffer);
  3545. }
  3546. //
  3547. // Now create a corresponding filename with the extension '.PNF'
  3548. //
  3549. lstrcpyn(PnfFileExt, pszPnfSuffix, SIZECHARS(CharBuffer) - (int)(PnfFileExt - CharBuffer));
  3550. //
  3551. // Attempt to open and map the file into memory.
  3552. //
  3553. if(pSetupOpenAndMapFileForRead(CharBuffer,
  3554. &FileSize,
  3555. &FileHandle,
  3556. &MappingHandle,
  3557. &BaseAddress) != NO_ERROR) {
  3558. //
  3559. // Couldn't open a .PNF file--bail now.
  3560. //
  3561. return FALSE;
  3562. }
  3563. NewInf = NULL;
  3564. NeedToDestroyLock = FALSE;
  3565. MinorVer1FieldsAvailable = TRUE;
  3566. PnfImageEnd = (PBYTE)BaseAddress + FileSize;
  3567. try {
  3568. //
  3569. // Now verify that this really is a precompiled INF (and that it's one we can use).
  3570. // Then see if the LastWriteTime field in its version block agrees with the filetime
  3571. // we were passed in.
  3572. //
  3573. PnfHeader = (PPNF_HEADER)BaseAddress;
  3574. //
  3575. // If we ever rev the major version, the logic below will need to change,
  3576. // as we'll need to migrate the INF source path information over, thus
  3577. // we can't bail so quickly.
  3578. //
  3579. MYASSERT(PNF_MAJOR_VERSION == 1);
  3580. if(HIBYTE(PnfHeader->Version) != PNF_MAJOR_VERSION) {
  3581. //
  3582. // A major version mismatch means the PNF is unusable (see note above
  3583. // about the need to migrate INF source path info in the future).
  3584. //
  3585. if(LogContext) {
  3586. WriteLogEntry(LogContext,
  3587. SETUP_LOG_WARNING,
  3588. MSG_LOG_PNF_VERSION_MAJOR_MISMATCH,
  3589. NULL,
  3590. PnfFileName,
  3591. PNF_MAJOR_VERSION,
  3592. HIBYTE(PnfHeader->Version)
  3593. );
  3594. }
  3595. goto clean0;
  3596. }
  3597. if(LOBYTE(PnfHeader->Version) != PNF_MINOR_VERSION) {
  3598. if(LogContext) {
  3599. WriteLogEntry(LogContext,
  3600. SETUP_LOG_WARNING,
  3601. MSG_LOG_PNF_VERSION_MINOR_MISMATCH,
  3602. NULL,
  3603. PnfFileName,
  3604. PNF_MINOR_VERSION,
  3605. LOBYTE(PnfHeader->Version)
  3606. );
  3607. }
  3608. if(LOBYTE(PnfHeader->Version) < PNF_MINOR_VERSION) {
  3609. //
  3610. // We're currently at minor version 1. PNFs having a minor
  3611. // version of 1 differ from those having a minor version of 0
  3612. // in the following ways:
  3613. //
  3614. // 1. Minor version 1 PNFs store the LanguageId in which the
  3615. // INF was precompiled. For Minor version 0 INFs, this field
  3616. // was initialized to zero. This will cause our check for
  3617. // LanguageId match to fail, thus we'll consider the PNF
  3618. // invalid.
  3619. //
  3620. // 2. Minor version 1 PNFs contain additional fields for
  3621. // InfSourcePathOffset and OriginalInfNameOffset. This means
  3622. // that the PNF_HEADER struct got longer, thus we can only
  3623. // use these fields for minor version 1 or greater PNFs.
  3624. //
  3625. MinorVer1FieldsAvailable = FALSE;
  3626. }
  3627. //
  3628. // (If the minor version of the PNF we're looking at is _greater_ than
  3629. // the version we currently support, then we should attempt to use
  3630. // this PNF, since all the fields that we care about should be right
  3631. // where we expect them to be.)
  3632. //
  3633. }
  3634. //
  3635. // The version information checks out--now check the last-write times.
  3636. // note that if we add any other consistancy checks to determine that this PNF
  3637. // is associated with the INF
  3638. // we must also modify simular tests for INF cache
  3639. //
  3640. TimeDateMatch = CompareFileTime(LastWriteTime, &(PnfHeader->InfVersionLastWriteTime))?FALSE:TRUE;
  3641. if (!TimeDateMatch && !(Flags&LDINF_FLAG_ALWAYS_GET_SRCPATH)) {
  3642. //
  3643. // Time&Date don't match, and we're not interested in always getting source path
  3644. //
  3645. WriteLogEntry(LogContext,
  3646. SETUP_LOG_WARNING,
  3647. MSG_LOG_PNF_TIMEDATE_MISMATCH,
  3648. NULL,
  3649. PnfFileName
  3650. );
  3651. goto clean0;
  3652. }
  3653. #ifdef UNICODE
  3654. if(!(PnfHeader->Flags & PNF_FLAG_IS_UNICODE))
  3655. #else
  3656. if(PnfHeader->Flags & PNF_FLAG_IS_UNICODE)
  3657. #endif
  3658. {
  3659. WriteLogEntry(LogContext,
  3660. SETUP_LOG_WARNING,
  3661. MSG_LOG_PNF_REBUILD_NATIVE,
  3662. NULL,
  3663. PnfFileName
  3664. );
  3665. //
  3666. // The APIs are Unicode while the PNF is ANSI, or vice versa. We
  3667. // still want to migrate the source path and original filename
  3668. // information, if present, so that we preserve this information
  3669. // across an upgrade from Win9x to NT, for example.
  3670. //
  3671. if(MinorVer1FieldsAvailable && InfSourcePathToMigrate) {
  3672. //
  3673. // First, retrieve the original INF name
  3674. //
  3675. if(PnfHeader->OriginalInfNameOffset) {
  3676. //
  3677. // Use strlen/wcslen so if an exception occurs it won't get
  3678. // swallowed...
  3679. //
  3680. #ifdef UNICODE
  3681. TempStringLen = strlen((PCSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset)) + 1;
  3682. TempStringLen *= sizeof(CHAR);
  3683. #else
  3684. TempStringLen = wcslen((PCWSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset)) + 1;
  3685. TempStringLen *= sizeof(WCHAR);
  3686. #endif
  3687. if(PnfImageEnd <
  3688. ((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset + TempStringLen))
  3689. {
  3690. goto clean0;
  3691. }
  3692. //
  3693. // Looks like we have a good original INF name string. Now
  3694. // convert it to the native character width.
  3695. //
  3696. #ifdef UNICODE
  3697. *InfOriginalNameToMigrate =
  3698. pSetupMultiByteToUnicode((PCSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset),
  3699. CP_ACP
  3700. );
  3701. #else
  3702. *InfOriginalNameToMigrate =
  3703. pSetupUnicodeToMultiByte((PCWSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset),
  3704. CP_ACP
  3705. );
  3706. #endif
  3707. if(!*InfOriginalNameToMigrate) {
  3708. goto clean0;
  3709. }
  3710. }
  3711. //
  3712. // Next, retrieve the source path information
  3713. //
  3714. if(PnfHeader->InfSourcePathOffset) {
  3715. #ifdef UNICODE
  3716. TempStringLen = strlen((PCSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset)) + 1;
  3717. TempStringLen *= sizeof(CHAR);
  3718. #else
  3719. TempStringLen = wcslen((PCWSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset)) + 1;
  3720. TempStringLen *= sizeof(WCHAR);
  3721. #endif
  3722. if(PnfImageEnd <
  3723. ((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset + TempStringLen))
  3724. {
  3725. goto clean0;
  3726. }
  3727. //
  3728. // Looks like we have a good source path string. Now convert
  3729. // it to the native character width.
  3730. //
  3731. #ifdef UNICODE
  3732. *InfSourcePathToMigrate =
  3733. pSetupMultiByteToUnicode((PCSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset),
  3734. CP_ACP
  3735. );
  3736. #else
  3737. *InfSourcePathToMigrate =
  3738. pSetupUnicodeToMultiByte((PCWSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset),
  3739. CP_ACP
  3740. );
  3741. #endif
  3742. if(!*InfSourcePathToMigrate) {
  3743. goto clean0;
  3744. }
  3745. if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
  3746. *InfSourcePathToMigrateMediaType = SPOST_URL;
  3747. } else {
  3748. *InfSourcePathToMigrateMediaType = SPOST_PATH;
  3749. }
  3750. } else if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
  3751. //
  3752. // No source path stored in the PNF, but the flag says it's
  3753. // a URL, thus it came from Windows Update.
  3754. //
  3755. *InfSourcePathToMigrateMediaType = SPOST_URL;
  3756. }
  3757. }
  3758. goto clean0;
  3759. }
  3760. //
  3761. // Make sure that the last data block is still within the file. This
  3762. // prevents us from opening up a corrupted (truncated) PNF, and thinking
  3763. // it's valid until later when we actually try to access data at an
  3764. // offset that's past the end of the file's mapped image.
  3765. //
  3766. if(PnfHeader->InfSubstValueCount) {
  3767. if(PnfImageEnd <
  3768. ((PBYTE)BaseAddress + PnfHeader->InfSubstValueListOffset + (PnfHeader->InfSubstValueCount * sizeof(STRINGSUBST_NODE))))
  3769. {
  3770. WriteLogEntry(LogContext,
  3771. SETUP_LOG_ERROR,
  3772. MSG_LOG_PNF_CORRUPTED,
  3773. NULL,
  3774. PnfFileName
  3775. );
  3776. goto clean0;
  3777. }
  3778. } else if(MinorVer1FieldsAvailable && (PnfHeader->OriginalInfNameOffset)) {
  3779. //
  3780. // Use _tcslen so if an exception occurs it won't get swallowed...
  3781. //
  3782. TempStringLen = _tcslen((PCTSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset)) + 1;
  3783. if(PnfImageEnd <
  3784. ((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset + (TempStringLen * sizeof(TCHAR))))
  3785. {
  3786. WriteLogEntry(LogContext,
  3787. SETUP_LOG_ERROR,
  3788. MSG_LOG_PNF_CORRUPTED,
  3789. NULL,
  3790. PnfFileName
  3791. );
  3792. goto clean0;
  3793. }
  3794. } else if(MinorVer1FieldsAvailable && (PnfHeader->InfSourcePathOffset)) {
  3795. //
  3796. // Use _tcslen so if an exception occurs it won't get swallowed...
  3797. //
  3798. TempStringLen = _tcslen((PCTSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset)) + 1;
  3799. if(PnfImageEnd <
  3800. ((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset + (TempStringLen * sizeof(TCHAR))))
  3801. {
  3802. WriteLogEntry(LogContext,
  3803. SETUP_LOG_ERROR,
  3804. MSG_LOG_PNF_CORRUPTED,
  3805. NULL,
  3806. PnfFileName
  3807. );
  3808. goto clean0;
  3809. }
  3810. } else {
  3811. //
  3812. // Well, we didn't have a substitution block or a source path block,
  3813. // so the last block in the PNF is the value block.
  3814. //
  3815. if(PnfImageEnd <
  3816. ((PBYTE)BaseAddress + PnfHeader->InfValueBlockOffset + PnfHeader->InfValueBlockSize))
  3817. {
  3818. WriteLogEntry(LogContext,
  3819. SETUP_LOG_ERROR,
  3820. MSG_LOG_PNF_CORRUPTED,
  3821. NULL,
  3822. PnfFileName
  3823. );
  3824. goto clean0;
  3825. }
  3826. }
  3827. //
  3828. // From this point forward, we appear to have a structurally sound PNF
  3829. // of the appropriate version and character width. Any failures
  3830. // encountered should cause us to return the INF source path information
  3831. // to the caller (if requested).
  3832. //
  3833. if (!TimeDateMatch) {
  3834. MYASSERT(Flags&LDINF_FLAG_ALWAYS_GET_SRCPATH);
  3835. //
  3836. // Time&Date don't match, but we've recovered old media
  3837. // we have to do this since on FAT/FAT32 the UT reported for a file
  3838. // will change every time system TZ is changed.
  3839. //
  3840. WriteLogEntry(LogContext,
  3841. SETUP_LOG_INFO,
  3842. MSG_LOG_PNF_REBUILD_TIMEDATE_MISMATCH,
  3843. NULL,
  3844. PnfFileName
  3845. );
  3846. goto clean1;
  3847. }
  3848. //
  3849. // Make sure that the language ID that this PNF was compiled for matches
  3850. // that of the current thread.
  3851. //
  3852. if(!(Flags & LDINF_FLAG_IGNORE_LANGUAGE) && ((DWORD)(PnfHeader->LanguageId) != LanguageId)) {
  3853. WriteLogEntry(LogContext,
  3854. SETUP_LOG_WARNING,
  3855. MSG_LOG_PNF_REBUILD_LANGUAGE_MISMATCH,
  3856. NULL,
  3857. PnfFileName,
  3858. LanguageId,
  3859. PnfHeader->LanguageId
  3860. );
  3861. goto clean1;
  3862. }
  3863. //
  3864. // Now verify that the Windows (and, optionally, OsLoader) directories
  3865. // for this PNF match the current state of the world.
  3866. //
  3867. if(lstrcmpi((PCTSTR)((PBYTE)BaseAddress + PnfHeader->WinDirPathOffset), WindowsDirectory)) {
  3868. //
  3869. // This PNF doesn't match the current WindowsDirectory path, so don't
  3870. // use it.
  3871. //
  3872. WriteLogEntry(LogContext,
  3873. SETUP_LOG_WARNING,
  3874. MSG_LOG_PNF_REBUILD_WINDIR_MISMATCH,
  3875. NULL,
  3876. PnfFileName,
  3877. WindowsDirectory,
  3878. (PCTSTR)((PBYTE)BaseAddress + PnfHeader->WinDirPathOffset)
  3879. );
  3880. goto clean1;
  3881. }
  3882. if((PnfHeader->OsLoaderPathOffset) && !(Flags & LDINF_FLAG_IGNORE_VOLATILE_DIRIDS)) {
  3883. //
  3884. // This INF contains references to the system partition. Verify that the path
  3885. // used during precompilation is the one we're currently using.
  3886. //
  3887. if(!OsLoaderPath) {
  3888. //
  3889. // The caller didn't specify an OsLoaderPath, so we must dynamically retrieve this
  3890. // value from the registry.
  3891. //
  3892. err = pSetupGetOsLoaderDriveAndPath(FALSE, CharBuffer, SIZECHARS(CharBuffer), NULL);
  3893. if(err) {
  3894. WriteLogEntry(LogContext,
  3895. SETUP_LOG_WARNING,
  3896. MSG_LOG_PNF_REBUILD_OSLOADER_MISMATCH,
  3897. NULL,
  3898. PnfFileName,
  3899. TEXT("?"),
  3900. (PCTSTR)((PBYTE)BaseAddress + PnfHeader->OsLoaderPathOffset)
  3901. );
  3902. goto clean1;
  3903. }
  3904. OsLoaderPath = CharBuffer;
  3905. }
  3906. if(lstrcmpi((PCTSTR)((PBYTE)BaseAddress + PnfHeader->OsLoaderPathOffset), OsLoaderPath)) {
  3907. WriteLogEntry(LogContext,
  3908. SETUP_LOG_WARNING,
  3909. MSG_LOG_PNF_REBUILD_OSLOADER_MISMATCH,
  3910. NULL,
  3911. PnfFileName,
  3912. OsLoaderPath,
  3913. (PCTSTR)((PBYTE)BaseAddress + PnfHeader->OsLoaderPathOffset)
  3914. );
  3915. goto clean1;
  3916. }
  3917. }
  3918. //
  3919. // Make sure that we have verified whether this INF is digitally signed or not
  3920. //
  3921. if (!(PnfHeader->Flags & PNF_FLAG_INF_VERIFIED)) {
  3922. WriteLogEntry(LogContext,
  3923. SETUP_LOG_INFO,
  3924. MSG_LOG_PNF_REBUILD_UNVERIFIED,
  3925. NULL,
  3926. PnfFileName
  3927. );
  3928. goto clean1;
  3929. }
  3930. //
  3931. // Verify that the product suite flags match
  3932. // this causes us to refresh the PNF's if there's any change in product
  3933. // suite
  3934. // if on NT, PNF_FLAG_16BIT_SUITE must be set and upper 16 bits contains
  3935. // product suite
  3936. // if not on NT, PNF_FLAG_16BIT_SUITE must NOT be set.
  3937. //
  3938. if(((OSVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) &&
  3939. (PnfHeader->Flags & PNF_FLAG_16BIT_SUITE)) ||
  3940. ((OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
  3941. (((PnfHeader->Flags & PNF_FLAG_16BIT_SUITE) == 0) ||
  3942. (((PnfHeader->Flags >> 16) & 0xffff) != OSVersionInfo.wSuiteMask)))) {
  3943. WriteLogEntry(LogContext,
  3944. SETUP_LOG_INFO,
  3945. MSG_LOG_PNF_REBUILD_SUITE,
  3946. NULL,
  3947. PnfFileName
  3948. );
  3949. goto clean1;
  3950. }
  3951. //
  3952. // One final check--make sure that the number of hash buckets used when precompiling
  3953. // this INF matches what we expect. (This wasn't rolled into the version check, since
  3954. // this is something that is subject to lots of modification, and we didn't want to
  3955. // rev the major version number each time.)
  3956. //
  3957. if(PnfHeader->StringTableHashBucketCount != HASH_BUCKET_COUNT) {
  3958. WriteLogEntry(LogContext,
  3959. SETUP_LOG_WARNING,
  3960. MSG_LOG_PNF_REBUILD_HASH_MISMATCH,
  3961. NULL,
  3962. PnfFileName,
  3963. HASH_BUCKET_COUNT,
  3964. PnfHeader->StringTableHashBucketCount
  3965. );
  3966. goto clean1;
  3967. }
  3968. //
  3969. // We can use the file--now set up our top level structures.
  3970. //
  3971. if(NewInf = MyTaggedMalloc(sizeof(LOADED_INF),MEMTAG_INF)) {
  3972. ZeroMemory(NewInf, sizeof(LOADED_INF));
  3973. if(NewInf->StringTable = InitializeStringTableFromPNF(PnfHeader, (LCID)LanguageId)) {
  3974. NewInf->LogContext = NULL;
  3975. if(InheritLogContext(LogContext, &(NewInf->LogContext)) == NO_ERROR) {
  3976. if(InitializeSynchronizedAccess(&(NewInf->Lock))) {
  3977. NeedToDestroyLock = TRUE;
  3978. //
  3979. // All necessary resources were successfully allocated--now
  3980. // fill in the LOADED_INF fields
  3981. //
  3982. NewInf->Signature = LOADED_INF_SIG;
  3983. NewInf->FileHandle = FileHandle;
  3984. NewInf->MappingHandle = MappingHandle;
  3985. NewInf->ViewAddress = BaseAddress;
  3986. NewInf->SectionCount = PnfHeader->InfSectionCount;
  3987. NewInf->SectionBlockSizeBytes = PnfHeader->InfSectionBlockSize;
  3988. NewInf->SectionBlock = (PINF_SECTION)((PBYTE)BaseAddress +
  3989. PnfHeader->InfSectionBlockOffset);
  3990. NewInf->LineBlockSizeBytes = PnfHeader->InfLineBlockSize;
  3991. NewInf->LineBlock = (PINF_LINE)((PBYTE)BaseAddress +
  3992. PnfHeader->InfLineBlockOffset);
  3993. NewInf->ValueBlockSizeBytes = PnfHeader->InfValueBlockSize;
  3994. NewInf->ValueBlock = (PLONG)((PBYTE)BaseAddress +
  3995. PnfHeader->InfValueBlockOffset);
  3996. NewInf->Style = PnfHeader->InfStyle;
  3997. NewInf->HasStrings = (PnfHeader->Flags & PNF_FLAG_HAS_STRINGS);
  3998. if(PnfHeader->Flags & PNF_FLAG_HAS_VOLATILE_DIRIDS) {
  3999. NewInf->Flags |= LIF_HAS_VOLATILE_DIRIDS;
  4000. }
  4001. if (PnfHeader->Flags & PNF_FLAG_INF_DIGITALLY_SIGNED) {
  4002. NewInf->Flags |= LIF_INF_DIGITALLY_SIGNED;
  4003. }
  4004. if (PnfHeader->Flags & PNF_FLAG_OEM_F6_INF) {
  4005. NewInf->Flags |= LIF_OEM_F6_INF;
  4006. }
  4007. NewInf->LanguageId = (DWORD)(PnfHeader->LanguageId);
  4008. //
  4009. // Next, fill in the VersionBlock fields.
  4010. //
  4011. NewInf->VersionBlock.LastWriteTime = *LastWriteTime;
  4012. NewInf->VersionBlock.DatumCount = PnfHeader->InfVersionDatumCount;
  4013. NewInf->VersionBlock.DataSize = PnfHeader->InfVersionDataSize;
  4014. NewInf->VersionBlock.DataBlock = (PCTSTR)((PBYTE)BaseAddress +
  4015. PnfHeader->InfVersionDataOffset);
  4016. NewInf->VersionBlock.FilenameSize = (lstrlen(Filename) + 1) * sizeof(TCHAR);
  4017. CopyMemory(NewInf->VersionBlock.Filename,
  4018. Filename,
  4019. NewInf->VersionBlock.FilenameSize
  4020. );
  4021. //
  4022. // Fill in the OsLoaderPath field, if present in the PNF.
  4023. //
  4024. if(PnfHeader->OsLoaderPathOffset) {
  4025. NewInf->OsLoaderPath = (PCTSTR)((PBYTE)BaseAddress +
  4026. PnfHeader->OsLoaderPathOffset);
  4027. }
  4028. //
  4029. // If the INF's SourcePath is available, then use it (default
  4030. // to assuming local (i.e., non-internet) source location).
  4031. //
  4032. // At this point, we should only be dealing with minor version
  4033. // 1 or later PNFs.
  4034. //
  4035. MYASSERT(MinorVer1FieldsAvailable);
  4036. NewInf->InfSourceMediaType = SPOST_PATH;
  4037. if(PnfHeader->InfSourcePathOffset) {
  4038. NewInf->InfSourcePath = (PCTSTR)((PBYTE)BaseAddress +
  4039. PnfHeader->InfSourcePathOffset);
  4040. }
  4041. if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
  4042. NewInf->InfSourceMediaType = SPOST_URL;
  4043. }
  4044. //
  4045. // Now retrieve the INF's original filename, if present. If
  4046. // this field isn't present, then the INF's current filename
  4047. // is assumed to be the same as its original filename (e.g.,
  4048. // a system-supplied INF).
  4049. //
  4050. if(PnfHeader->OriginalInfNameOffset) {
  4051. NewInf->OriginalInfName = (PCTSTR)((PBYTE)BaseAddress +
  4052. PnfHeader->OriginalInfNameOffset);
  4053. }
  4054. //
  4055. // Finally, fill in the string substitution list (if there is one).
  4056. //
  4057. if(PnfHeader->InfSubstValueCount) {
  4058. NewInf->SubstValueCount = PnfHeader->InfSubstValueCount;
  4059. NewInf->SubstValueList = (PSTRINGSUBST_NODE)((PBYTE)BaseAddress +
  4060. PnfHeader->InfSubstValueListOffset);
  4061. }
  4062. //
  4063. // We have successfully loaded the PNF.
  4064. //
  4065. IsPnfFile = TRUE;
  4066. }
  4067. }
  4068. }
  4069. }
  4070. clean1:
  4071. if(!IsPnfFile && InfSourcePathToMigrate && MinorVer1FieldsAvailable) {
  4072. //
  4073. // Actually, this is a good PNF, just one that we can't use. The
  4074. // caller has requested that we return the original INF source path
  4075. // location and original INF filename, so that this information can
  4076. // be migrated to the new PNF that will be built to replace this
  4077. // one.
  4078. //
  4079. #ifndef ANSI_SETUPAPI
  4080. #ifdef _X86_
  4081. MYASSERT(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
  4082. if((Flags&LDINF_FLAG_ALWAYS_GET_SRCPATH) &&
  4083. ((PnfHeader->Flags & PNF_FLAG_16BIT_SUITE) == 0) &&
  4084. (PnfHeader->OriginalInfNameOffset == 0) &&
  4085. (PnfHeader->InfSourcePathOffset == 0) &&
  4086. !pSetupInfIsFromOemLocation(Filename,TRUE)) {
  4087. PCTSTR title;
  4088. PCTSTR p;
  4089. PTSTR catname = NULL;
  4090. PSP_ALTPLATFORM_INFO_V2 pPlatform = NULL;
  4091. DWORD FixErr;
  4092. //
  4093. // if we're here
  4094. // we may need to work around a Win2k-Gold bug
  4095. //
  4096. // the bug is that if the timezone is changed
  4097. // Win2k looses OriginalInfNameOffset/InfSourcePathOffset
  4098. // which causes the INF to appear as unsigned
  4099. // when it's really signed
  4100. //
  4101. //
  4102. // get file title, of form:
  4103. // xxxx.INF
  4104. //
  4105. title = pSetupGetFileTitle(Filename);
  4106. //
  4107. // see if it's of form OEMxxxx.INF
  4108. //
  4109. p = title;
  4110. if(_wcsnicmp(p,TEXT("OEM"),3)!=0) {
  4111. goto clean0;
  4112. }
  4113. p+=3;
  4114. if(p[0] == TEXT('.')) {
  4115. //
  4116. // OEM.xxx (we're expecting a number before '.')
  4117. //
  4118. goto clean0;
  4119. }
  4120. while(p[0]>=TEXT('0')&&p[0]<=TEXT('9')) {
  4121. p++;
  4122. }
  4123. if((p-title) > 7) {
  4124. //
  4125. // we're expecting no more than 4 digits
  4126. //
  4127. goto clean0;
  4128. }
  4129. if(_wcsicmp(p,TEXT(".INF"))!=0) {
  4130. //
  4131. // not OEMnnnn.INF
  4132. //
  4133. goto clean0;
  4134. }
  4135. //
  4136. // see if there's a catalog that shadows this INF
  4137. //
  4138. WriteLogEntry(LogContext,
  4139. SETUP_LOG_INFO,
  4140. MSG_LOG_PNF_WIN2KBUG,
  4141. NULL,
  4142. PnfFileName
  4143. );
  4144. //
  4145. // see if the INF has a catalog that validates it
  4146. //
  4147. if(!pSetupApplyExtension(title,pszCatSuffix,&catname)) {
  4148. //
  4149. // validate against any catalog
  4150. // this is safe since the INF will get checked
  4151. // again when saving as PNF
  4152. //
  4153. catname = NULL;
  4154. }
  4155. pPlatform = MyMalloc(sizeof(SP_ALTPLATFORM_INFO_V2));
  4156. //
  4157. // if pPlatform is NULL, we'll probably fail the other bits
  4158. // too so bail.
  4159. //
  4160. if(!pPlatform) {
  4161. goto clean0;
  4162. }
  4163. ZeroMemory(pPlatform, sizeof(SP_ALTPLATFORM_INFO_V2));
  4164. pPlatform->cbSize = sizeof(SP_ALTPLATFORM_INFO_V2);
  4165. pPlatform->Platform = VER_PLATFORM_WIN32_NT;
  4166. pPlatform->Flags = SP_ALTPLATFORM_FLAGS_VERSION_RANGE;
  4167. pPlatform->MajorVersion = VER_PRODUCTMAJORVERSION;
  4168. pPlatform->MinorVersion = VER_PRODUCTMINORVERSION;
  4169. pPlatform->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
  4170. pPlatform->FirstValidatedMajorVersion = 0;
  4171. pPlatform->FirstValidatedMinorVersion = 0;
  4172. FixErr = _VerifyFile(
  4173. LogContext,
  4174. NULL, // no hCatAdmin to pass in
  4175. NULL, // no hSDBDrvMain to pass in
  4176. catname, // eg "OEMx.CAT"
  4177. NULL,0, // we're not verifying against another catalog image
  4178. title, // eg "mydisk.inf"
  4179. Filename, // eg "....\OEMx.INF"
  4180. NULL, // return: problem info
  4181. NULL, // return: problem file
  4182. FALSE, // has to be FALSE because we don't have full path
  4183. pPlatform, // alt platform info
  4184. (VERIFY_FILE_IGNORE_SELFSIGNED
  4185. | VERIFY_FILE_USE_OEM_CATALOGS
  4186. | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
  4187. NULL, // return: catalog file, full path
  4188. NULL, // return: number of catalogs considered
  4189. NULL,
  4190. NULL
  4191. );
  4192. if(catname) {
  4193. MyFree(catname);
  4194. }
  4195. if(pPlatform) {
  4196. MyFree(pPlatform);
  4197. }
  4198. if(FixErr != NO_ERROR) {
  4199. //
  4200. // failed, don't fake any information
  4201. //
  4202. goto clean0;
  4203. }
  4204. //
  4205. // at this point, pretend original name was "OEM.INF"
  4206. // and that files are located in A:\
  4207. // we'll see at the time the inf is parsed
  4208. // if it's signed or not
  4209. //
  4210. *InfSourcePathToMigrate = DuplicateString(TEXT("A:\\"));
  4211. if(!*InfSourcePathToMigrate) {
  4212. goto clean0;
  4213. }
  4214. *InfOriginalNameToMigrate = DuplicateString(TEXT("OEM.INF"));
  4215. if(!*InfOriginalNameToMigrate) {
  4216. MyFree(*InfSourcePathToMigrate);
  4217. *InfSourcePathToMigrate = NULL;
  4218. goto clean0;
  4219. }
  4220. *InfSourcePathToMigrateMediaType = SPOST_PATH;
  4221. WriteLogEntry(LogContext,
  4222. SETUP_LOG_WARNING,
  4223. MSG_LOG_PNF_WIN2KBUGFIX,
  4224. NULL,
  4225. PnfFileName
  4226. );
  4227. goto clean0;
  4228. }
  4229. #endif
  4230. #endif
  4231. if(PnfHeader->OriginalInfNameOffset) {
  4232. *InfOriginalNameToMigrate =
  4233. DuplicateString((PCTSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset));
  4234. if(!*InfOriginalNameToMigrate) {
  4235. goto clean0;
  4236. }
  4237. }
  4238. if(PnfHeader->InfSourcePathOffset) {
  4239. *InfSourcePathToMigrate =
  4240. DuplicateString((PCTSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset));
  4241. if(!*InfSourcePathToMigrate) {
  4242. goto clean0;
  4243. }
  4244. if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
  4245. *InfSourcePathToMigrateMediaType = SPOST_URL;
  4246. } else {
  4247. *InfSourcePathToMigrateMediaType = SPOST_PATH;
  4248. }
  4249. } else if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
  4250. //
  4251. // No source path stored in the PNF, but the flag says it's
  4252. // a URL, thus it came from Windows Update.
  4253. //
  4254. *InfSourcePathToMigrateMediaType = SPOST_URL;
  4255. }
  4256. }
  4257. clean0: ; // nothing to do
  4258. } except(EXCEPTION_EXECUTE_HANDLER) {
  4259. //
  4260. // Reference the NeedToDestroyLock flag here in the except clause, so that the
  4261. // compiler won't try to re-order the code in such a way that the flag is unreliable.
  4262. //
  4263. NeedToDestroyLock = NeedToDestroyLock;
  4264. }
  4265. if(IsPnfFile) {
  4266. *Inf = NewInf;
  4267. } else {
  4268. if(NewInf) {
  4269. if(NeedToDestroyLock && LockInf(NewInf)) {
  4270. DestroySynchronizedAccess(&(NewInf->Lock));
  4271. }
  4272. if(NewInf->StringTable) {
  4273. pStringTableDestroy(NewInf->StringTable);
  4274. }
  4275. if(NewInf->LogContext) {
  4276. DeleteLogContext(NewInf->LogContext);
  4277. }
  4278. MyTaggedFree(NewInf,MEMTAG_INF);
  4279. }
  4280. pSetupUnmapAndCloseFile(FileHandle, MappingHandle, BaseAddress);
  4281. }
  4282. return IsPnfFile;
  4283. }
  4284. DWORD
  4285. SavePnf(
  4286. IN PCTSTR Filename,
  4287. IN PLOADED_INF Inf
  4288. )
  4289. /*++
  4290. Routine Description:
  4291. This routine attempts to write to disk a precompiled form (.PNF file) of the
  4292. specified loaded INF descriptor (from a .INF file).
  4293. Arguments:
  4294. Filename - specifies the fully-qualified path to the .INF textfile from which
  4295. this INF descriptor was loaded. A corresponding file with a .PNF extension
  4296. will be created to store the precompiled INF into.
  4297. Inf - supplies the address of the loaded INF descriptor to be written to disk
  4298. as a precompiled INF file.
  4299. Return Value:
  4300. If successful, the return value is NO_ERROR.
  4301. If failure, the return value is a Win32 error code indicating the reason for
  4302. failure.
  4303. --*/
  4304. {
  4305. TCHAR PnfFilePath[MAX_PATH];
  4306. PTSTR PnfFileName, PnfFileExt;
  4307. HANDLE hFile;
  4308. PNF_HEADER PnfHeader;
  4309. DWORD Offset, BytesWritten, WinDirPathLen, SourcePathLen, OsLoaderPathLen;
  4310. DWORD OriginalInfNameLen;
  4311. PVOID StringTableDataBlock;
  4312. DWORD Err;
  4313. PSP_ALTPLATFORM_INFO_V2 ValidationPlatform;
  4314. if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
  4315. //
  4316. // To minimize our footprint in certain embedded scenarios, we refrain
  4317. // from generating PNFs. We also assume the INF is valid...
  4318. //
  4319. Inf->Flags |= LIF_INF_DIGITALLY_SIGNED;
  4320. return NO_ERROR;
  4321. }
  4322. lstrcpyn(PnfFilePath, Filename,SIZECHARS(PnfFilePath));
  4323. //
  4324. // Find the start of the filename component of the path, and then find the last
  4325. // period (if one exists) in that filename.
  4326. //
  4327. PnfFileName = (PTSTR)pSetupGetFileTitle(PnfFilePath);
  4328. if(!(PnfFileExt = _tcsrchr(PnfFileName, TEXT('.')))) {
  4329. PnfFileExt = PnfFilePath + lstrlen(PnfFilePath);
  4330. }
  4331. //
  4332. // Now create a corresponding filename with the extension '.PNF'
  4333. //
  4334. lstrcpyn(PnfFileExt, pszPnfSuffix, SIZECHARS(PnfFilePath) - (int)(PnfFileExt - PnfFilePath));
  4335. //
  4336. // NOTE: If there's already a PNF for this INF, we're going to blow it away.
  4337. // If we encounter a failure after successfully creating the file, we're going
  4338. // to delete the partial PNF, and there'll be no rollback to restore the old
  4339. // PNF. This is OK because if CreateFile succeeds, then we know we're going
  4340. // to be able to write out the PNF barring out-of-disk-space problems. For
  4341. // out-of-disk-space problems, there could be one of two causes:
  4342. //
  4343. // 1. The INF associated with the old PNF has gotten bigger, hence the PNF
  4344. // has gotten bigger. In this case, it's desirable that we blow away
  4345. // the old PNF because it's invalid for the INF anyway.
  4346. //
  4347. // 2. The INF is the same, but something else has changed that caused us to
  4348. // need to regenerate the PNF (e.g., code page changed). Given the
  4349. // present information stored in PNFs, such a change would not result in
  4350. // a significant size difference between the old and new PNFs. Thus, if
  4351. // the old PNF fit in the available disk space, then so would the new
  4352. // one. If this changes in the future (e.g., storing out a new PNF can
  4353. // result in substantially increasing its size), then we'll need to be
  4354. // careful about backing up the old PNF before attempting to write out
  4355. // the new one, in case we need to rollback.
  4356. //
  4357. hFile = CreateFile(PnfFilePath,
  4358. GENERIC_WRITE,
  4359. 0,
  4360. NULL,
  4361. CREATE_ALWAYS,
  4362. FILE_ATTRIBUTE_NORMAL,
  4363. NULL
  4364. );
  4365. if(hFile == INVALID_HANDLE_VALUE) {
  4366. return GetLastError();
  4367. }
  4368. //
  4369. // Enclose the rest of the function in try/except, in case we hit an error while
  4370. // writing to the file.
  4371. //
  4372. Err = NO_ERROR;
  4373. ValidationPlatform = NULL;
  4374. try {
  4375. //
  4376. // Initialize a PNF header structure to be written to the beginning of the file.
  4377. //
  4378. ZeroMemory(&PnfHeader, sizeof(PNF_HEADER));
  4379. PnfHeader.InfStyle = Inf->Style;
  4380. #ifdef UNICODE
  4381. PnfHeader.Flags = PNF_FLAG_IS_UNICODE;
  4382. #else
  4383. PnfHeader.Flags = 0;
  4384. #endif
  4385. if(Inf->HasStrings) {
  4386. PnfHeader.Flags |= PNF_FLAG_HAS_STRINGS;
  4387. }
  4388. if(Inf->InfSourceMediaType == SPOST_URL) {
  4389. PnfHeader.Flags |= PNF_FLAG_SRCPATH_IS_URL;
  4390. }
  4391. if(Inf->Flags & LIF_HAS_VOLATILE_DIRIDS) {
  4392. PnfHeader.Flags |= PNF_FLAG_HAS_VOLATILE_DIRIDS;
  4393. }
  4394. if (Inf->Flags & LIF_OEM_F6_INF) {
  4395. PnfHeader.Flags |= PNF_FLAG_OEM_F6_INF;
  4396. }
  4397. //
  4398. // if this is NT, save product suite
  4399. // this helps us, eg, catch migration from PER to PRO
  4400. // so that we can refresh PNF's
  4401. //
  4402. if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
  4403. PnfHeader.Flags |= (((DWORD)OSVersionInfo.wSuiteMask)<<16) | PNF_FLAG_16BIT_SUITE;
  4404. }
  4405. //
  4406. // We can only verify the digital signature of an INF file
  4407. // after the crypto DLLs have been registered.
  4408. //
  4409. if(!(GlobalSetupFlags & PSPGF_NO_VERIFY_INF)) {
  4410. TCHAR CatalogName[MAX_PATH];
  4411. TCHAR FullCatalogPath[MAX_PATH];
  4412. PTSTR p;
  4413. FullCatalogPath[0] = TEXT('\0');
  4414. //
  4415. // If this INF does not live in %windir%\inf, or specifies a
  4416. // CatalogFile= entry, then we don't want to do global validataion.
  4417. // In these cases, we want to validate against the CatalogFile=
  4418. // catalog.
  4419. //
  4420. // Note that if there is no CatalogFile= then FullCatalogPath[0]
  4421. // will still be set to TEXT('\0') which will cause us to do global
  4422. // validataion.
  4423. //
  4424. if(pSetupGetCatalogFileValue(&(Inf->VersionBlock),
  4425. CatalogName,
  4426. SIZECHARS(CatalogName),
  4427. NULL) &&
  4428. (CatalogName[0] != TEXT('\0'))) {
  4429. //
  4430. // The INF specified a CatalogFile= entry. If the INF is in
  4431. // a 3rd-party location (i.e., not in %windir%\Inf, then we'll
  4432. // use the full path to the catalog (it must be located in the
  4433. // same directory as the INF). If the INF is in %windir%\Inf,
  4434. // then we will look for an installed catalog having the same
  4435. // primary filename as the INF, with an extension of ".CAT".
  4436. //
  4437. if(pSetupInfIsFromOemLocation(Filename, TRUE)) {
  4438. //
  4439. // Construct full path to the catalog based on the location
  4440. // of the INF.
  4441. //
  4442. lstrcpyn(FullCatalogPath, Filename, SIZECHARS(FullCatalogPath));
  4443. p = (PTSTR)pSetupGetFileTitle(FullCatalogPath);
  4444. lstrcpyn(p,
  4445. CatalogName,
  4446. (int)(SIZECHARS(FullCatalogPath) - (p - FullCatalogPath))
  4447. );
  4448. } else {
  4449. //
  4450. // Construct simple filename of catalog based on INF's name
  4451. // (with .CAT extension)
  4452. //
  4453. lstrcpyn(FullCatalogPath,
  4454. pSetupGetFileTitle(Filename),
  4455. SIZECHARS(FullCatalogPath)
  4456. );
  4457. p = _tcsrchr(FullCatalogPath, TEXT('.'));
  4458. if(!p) {
  4459. //
  4460. // Should never happen, but if our INF file has no
  4461. // extension, simply append ".CAT".
  4462. //
  4463. p = FullCatalogPath + lstrlen(FullCatalogPath);
  4464. }
  4465. lstrcpyn(p,
  4466. pszCatSuffix,
  4467. (int)(SIZECHARS(FullCatalogPath) - (p - FullCatalogPath))
  4468. );
  4469. }
  4470. }
  4471. //
  4472. // Check if the INF digitally signed
  4473. //
  4474. IsInfForDeviceInstall(NULL,
  4475. NULL,
  4476. Inf,
  4477. NULL,
  4478. &ValidationPlatform,
  4479. NULL,
  4480. NULL
  4481. );
  4482. if(NO_ERROR == _VerifyFile(
  4483. NULL,
  4484. NULL,
  4485. NULL,
  4486. (*FullCatalogPath ? FullCatalogPath : NULL),
  4487. NULL,
  4488. 0,
  4489. (Inf->OriginalInfName
  4490. ? Inf->OriginalInfName
  4491. : pSetupGetFileTitle(Filename)),
  4492. Filename,
  4493. NULL,
  4494. NULL,
  4495. FALSE,
  4496. ValidationPlatform,
  4497. (VERIFY_FILE_IGNORE_SELFSIGNED
  4498. | VERIFY_FILE_USE_OEM_CATALOGS
  4499. | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
  4500. NULL,
  4501. NULL,
  4502. NULL,
  4503. NULL)) {
  4504. PnfHeader.Flags |= PNF_FLAG_INF_DIGITALLY_SIGNED;
  4505. Inf->Flags |= LIF_INF_DIGITALLY_SIGNED;
  4506. }
  4507. PnfHeader.Flags |= PNF_FLAG_INF_VERIFIED;
  4508. }
  4509. PnfHeader.Version = MAKEWORD(PNF_MINOR_VERSION, PNF_MAJOR_VERSION);
  4510. PnfHeader.StringTableHashBucketCount = HASH_BUCKET_COUNT;
  4511. PnfHeader.LanguageId = (WORD)(Inf->LanguageId);
  4512. //
  4513. // The Windows directory path is the first data block after the header.
  4514. //
  4515. Offset = PNF_ALIGN_BLOCK(sizeof(PNF_HEADER));
  4516. PnfHeader.WinDirPathOffset = Offset;
  4517. WinDirPathLen = (lstrlen(WindowsDirectory) + 1) * sizeof(TCHAR);
  4518. //
  4519. // The (optional) OsLoader directory path is the second data block.
  4520. //
  4521. Offset += PNF_ALIGN_BLOCK(WinDirPathLen);
  4522. if(Inf->OsLoaderPath) {
  4523. PnfHeader.OsLoaderPathOffset = Offset;
  4524. OsLoaderPathLen = (lstrlen(Inf->OsLoaderPath) + 1) * sizeof(TCHAR);
  4525. } else {
  4526. OsLoaderPathLen = 0;
  4527. }
  4528. //
  4529. // The string table is the third data block...
  4530. //
  4531. Offset += PNF_ALIGN_BLOCK(OsLoaderPathLen);
  4532. PnfHeader.StringTableBlockOffset = Offset;
  4533. PnfHeader.StringTableBlockSize = pStringTableGetDataBlock(Inf->StringTable, &StringTableDataBlock);
  4534. //
  4535. // Next comes the version block...
  4536. //
  4537. Offset += PNF_ALIGN_BLOCK(PnfHeader.StringTableBlockSize);
  4538. PnfHeader.InfVersionDataOffset = Offset;
  4539. PnfHeader.InfVersionDatumCount = Inf->VersionBlock.DatumCount;
  4540. PnfHeader.InfVersionDataSize = Inf->VersionBlock.DataSize;
  4541. PnfHeader.InfVersionLastWriteTime = Inf->VersionBlock.LastWriteTime;
  4542. //
  4543. // then, the section block...
  4544. //
  4545. Offset += PNF_ALIGN_BLOCK(PnfHeader.InfVersionDataSize);
  4546. PnfHeader.InfSectionBlockOffset = Offset;
  4547. PnfHeader.InfSectionCount = Inf->SectionCount;
  4548. PnfHeader.InfSectionBlockSize = Inf->SectionBlockSizeBytes;
  4549. //
  4550. // followed by the line block...
  4551. //
  4552. Offset += PNF_ALIGN_BLOCK(PnfHeader.InfSectionBlockSize);
  4553. PnfHeader.InfLineBlockOffset = Offset;
  4554. PnfHeader.InfLineBlockSize = Inf->LineBlockSizeBytes;
  4555. //
  4556. // and the value block...
  4557. //
  4558. Offset += PNF_ALIGN_BLOCK(PnfHeader.InfLineBlockSize);
  4559. PnfHeader.InfValueBlockOffset = Offset;
  4560. PnfHeader.InfValueBlockSize = Inf->ValueBlockSizeBytes;
  4561. //
  4562. // then the INF source path (if there is one)...
  4563. //
  4564. Offset += PNF_ALIGN_BLOCK(PnfHeader.InfValueBlockSize);
  4565. if(Inf->InfSourcePath) {
  4566. PnfHeader.InfSourcePathOffset = Offset;
  4567. SourcePathLen = (lstrlen(Inf->InfSourcePath) + 1) * sizeof(TCHAR);
  4568. Offset += PNF_ALIGN_BLOCK(SourcePathLen);
  4569. } else {
  4570. PnfHeader.InfSourcePathOffset = 0;
  4571. }
  4572. //
  4573. // followed by the original INF's filename (if supplied, this indicates
  4574. // the INF originally had a different name prior to being copied into
  4575. // the current location)...
  4576. //
  4577. if(Inf->OriginalInfName) {
  4578. PnfHeader.OriginalInfNameOffset = Offset;
  4579. OriginalInfNameLen = (lstrlen(Inf->OriginalInfName) + 1) * sizeof(TCHAR);
  4580. Offset += PNF_ALIGN_BLOCK(OriginalInfNameLen);
  4581. } else {
  4582. PnfHeader.OriginalInfNameOffset = 0;
  4583. }
  4584. //
  4585. // and finally, the string substitution block (if there is one).
  4586. //
  4587. if(PnfHeader.InfSubstValueCount = Inf->SubstValueCount) {
  4588. PnfHeader.InfSubstValueListOffset = Offset;
  4589. } else {
  4590. PnfHeader.InfSubstValueListOffset = 0;
  4591. }
  4592. //
  4593. // Now write out all the blocks.
  4594. //
  4595. Offset = 0;
  4596. if(!WriteFile(hFile, &PnfHeader, sizeof(PnfHeader), &BytesWritten, NULL)) {
  4597. Err = GetLastError();
  4598. goto clean0;
  4599. }
  4600. MYASSERT(BytesWritten == sizeof(PnfHeader));
  4601. Offset += BytesWritten;
  4602. if(AlignForNextBlock(hFile, PnfHeader.WinDirPathOffset - Offset)) {
  4603. Offset = PnfHeader.WinDirPathOffset;
  4604. } else {
  4605. Err = GetLastError();
  4606. goto clean0;
  4607. }
  4608. if(!WriteFile(hFile, WindowsDirectory, WinDirPathLen, &BytesWritten, NULL)) {
  4609. Err = GetLastError();
  4610. goto clean0;
  4611. }
  4612. MYASSERT(BytesWritten == WinDirPathLen);
  4613. Offset += BytesWritten;
  4614. if(Inf->OsLoaderPath) {
  4615. if(AlignForNextBlock(hFile, PnfHeader.OsLoaderPathOffset - Offset)) {
  4616. Offset = PnfHeader.OsLoaderPathOffset;
  4617. } else {
  4618. Err = GetLastError();
  4619. goto clean0;
  4620. }
  4621. if(!WriteFile(hFile, Inf->OsLoaderPath, OsLoaderPathLen, &BytesWritten, NULL)) {
  4622. Err = GetLastError();
  4623. goto clean0;
  4624. }
  4625. MYASSERT(BytesWritten == OsLoaderPathLen);
  4626. Offset += BytesWritten;
  4627. }
  4628. if(AlignForNextBlock(hFile, PnfHeader.StringTableBlockOffset - Offset)) {
  4629. Offset = PnfHeader.StringTableBlockOffset;
  4630. } else {
  4631. Err = GetLastError();
  4632. goto clean0;
  4633. }
  4634. if(!WriteFile(hFile, StringTableDataBlock, PnfHeader.StringTableBlockSize, &BytesWritten, NULL)) {
  4635. Err = GetLastError();
  4636. goto clean0;
  4637. }
  4638. MYASSERT(BytesWritten == PnfHeader.StringTableBlockSize);
  4639. Offset += BytesWritten;
  4640. if(AlignForNextBlock(hFile, PnfHeader.InfVersionDataOffset - Offset)) {
  4641. Offset = PnfHeader.InfVersionDataOffset;
  4642. } else {
  4643. Err = GetLastError();
  4644. goto clean0;
  4645. }
  4646. if(!WriteFile(hFile, Inf->VersionBlock.DataBlock, PnfHeader.InfVersionDataSize, &BytesWritten, NULL)) {
  4647. Err = GetLastError();
  4648. goto clean0;
  4649. }
  4650. MYASSERT(BytesWritten == PnfHeader.InfVersionDataSize);
  4651. Offset += BytesWritten;
  4652. if(AlignForNextBlock(hFile, PnfHeader.InfSectionBlockOffset - Offset)) {
  4653. Offset = PnfHeader.InfSectionBlockOffset;
  4654. } else {
  4655. Err = GetLastError();
  4656. goto clean0;
  4657. }
  4658. if(!WriteFile(hFile, Inf->SectionBlock, PnfHeader.InfSectionBlockSize, &BytesWritten, NULL)) {
  4659. Err = GetLastError();
  4660. goto clean0;
  4661. }
  4662. MYASSERT(BytesWritten == PnfHeader.InfSectionBlockSize);
  4663. Offset += BytesWritten;
  4664. if(AlignForNextBlock(hFile, PnfHeader.InfLineBlockOffset - Offset)) {
  4665. Offset = PnfHeader.InfLineBlockOffset;
  4666. } else {
  4667. Err = GetLastError();
  4668. goto clean0;
  4669. }
  4670. if(!WriteFile(hFile, Inf->LineBlock, PnfHeader.InfLineBlockSize, &BytesWritten, NULL)) {
  4671. Err = GetLastError();
  4672. goto clean0;
  4673. }
  4674. MYASSERT(BytesWritten == PnfHeader.InfLineBlockSize);
  4675. Offset += BytesWritten;
  4676. if(AlignForNextBlock(hFile, PnfHeader.InfValueBlockOffset - Offset)) {
  4677. Offset = PnfHeader.InfValueBlockOffset;
  4678. } else {
  4679. Err = GetLastError();
  4680. goto clean0;
  4681. }
  4682. if(!WriteFile(hFile, Inf->ValueBlock, PnfHeader.InfValueBlockSize, &BytesWritten, NULL)) {
  4683. Err = GetLastError();
  4684. goto clean0;
  4685. }
  4686. MYASSERT(BytesWritten == PnfHeader.InfValueBlockSize);
  4687. Offset += BytesWritten;
  4688. if(Inf->InfSourcePath) {
  4689. if(AlignForNextBlock(hFile, PnfHeader.InfSourcePathOffset - Offset)) {
  4690. Offset = PnfHeader.InfSourcePathOffset;
  4691. } else {
  4692. Err = GetLastError();
  4693. goto clean0;
  4694. }
  4695. if(!WriteFile(hFile, Inf->InfSourcePath, SourcePathLen, &BytesWritten, NULL)) {
  4696. Err = GetLastError();
  4697. goto clean0;
  4698. }
  4699. MYASSERT(BytesWritten == SourcePathLen);
  4700. Offset += BytesWritten;
  4701. }
  4702. if(Inf->OriginalInfName) {
  4703. if(AlignForNextBlock(hFile, PnfHeader.OriginalInfNameOffset - Offset)) {
  4704. Offset = PnfHeader.OriginalInfNameOffset;
  4705. } else {
  4706. Err = GetLastError();
  4707. goto clean0;
  4708. }
  4709. if(!WriteFile(hFile, Inf->OriginalInfName, OriginalInfNameLen, &BytesWritten, NULL)) {
  4710. Err = GetLastError();
  4711. goto clean0;
  4712. }
  4713. MYASSERT(BytesWritten == OriginalInfNameLen);
  4714. Offset += BytesWritten;
  4715. }
  4716. if(PnfHeader.InfSubstValueCount) {
  4717. if(!AlignForNextBlock(hFile, PnfHeader.InfSubstValueListOffset - Offset)) {
  4718. Err = GetLastError();
  4719. goto clean0;
  4720. }
  4721. if(!WriteFile(hFile,
  4722. Inf->SubstValueList,
  4723. PnfHeader.InfSubstValueCount * sizeof(STRINGSUBST_NODE),
  4724. &BytesWritten,
  4725. NULL)) {
  4726. Err = GetLastError();
  4727. goto clean0;
  4728. }
  4729. MYASSERT(BytesWritten == PnfHeader.InfSubstValueCount * sizeof(STRINGSUBST_NODE));
  4730. }
  4731. clean0: ; // nothing to do
  4732. } except(EXCEPTION_EXECUTE_HANDLER) {
  4733. Err = ERROR_INVALID_DATA;
  4734. }
  4735. CloseHandle(hFile);
  4736. if(ValidationPlatform) {
  4737. MyFree(ValidationPlatform);
  4738. }
  4739. if(Err != NO_ERROR) {
  4740. //
  4741. // Something went wrong--get rid of the file.
  4742. //
  4743. DeleteFile(PnfFilePath);
  4744. }
  4745. return Err;
  4746. }
  4747. BOOL
  4748. AddUnresolvedSubstToList(
  4749. IN PLOADED_INF Inf,
  4750. IN UINT ValueOffset,
  4751. IN BOOL CaseSensitive
  4752. )
  4753. /*++
  4754. Routine Description:
  4755. This routine adds a new STRINGSUBST_NODE to the array stored in the specified INF.
  4756. The entries in this array are used later to quickly locate all values that have
  4757. unresolved string substitutions in them (i.e., for subsequent user-defined DIRID
  4758. replacement).
  4759. Arguments:
  4760. Inf - Specifies the INF containing the string value to be added to the unresolved
  4761. substitutions list.
  4762. ValueOffset - Specifies the offset within the INF's value block of the unresolved
  4763. string value.
  4764. Return Value:
  4765. If the new element was successfully added to the array, the return value is TRUE.
  4766. If the routine failed (due to an out-of-memory error), the return value is FALSE.
  4767. --*/
  4768. {
  4769. PSTRINGSUBST_NODE p;
  4770. //
  4771. // Grow the array to accommodate the new element.
  4772. //
  4773. if(Inf->SubstValueList) {
  4774. p = MyRealloc(Inf->SubstValueList, (Inf->SubstValueCount + 1) * sizeof(STRINGSUBST_NODE));
  4775. } else {
  4776. MYASSERT(!(Inf->SubstValueCount));
  4777. p = MyMalloc(sizeof(STRINGSUBST_NODE));
  4778. }
  4779. if(!p) {
  4780. return FALSE;
  4781. }
  4782. //
  4783. // Now, we must check to see if the ValueOffset currently being inserted is the same
  4784. // as the entry on the end of the list. This will be the case if we're dealing with
  4785. // a line key, or a single-value line, since we first add the value case-sensitively,
  4786. // then add the value again case-insensitively for look-up, and insert it in front
  4787. // of the case-sensitive form.
  4788. //
  4789. if(Inf->SubstValueCount &&
  4790. (ValueOffset == p[Inf->SubstValueCount - 1].ValueOffset)) {
  4791. //
  4792. // The value offsets are the same. Increment the value offset for the value
  4793. // currently at the end of the list, before adding the new value.
  4794. //
  4795. p[Inf->SubstValueCount - 1].ValueOffset++;
  4796. }
  4797. p[Inf->SubstValueCount].ValueOffset = ValueOffset;
  4798. p[Inf->SubstValueCount].TemplateStringId = Inf->ValueBlock[ValueOffset];
  4799. p[Inf->SubstValueCount].CaseSensitive = CaseSensitive;
  4800. //
  4801. // Store the new array size and pointer back in the INF, and return success.
  4802. //
  4803. Inf->SubstValueList = p;
  4804. Inf->SubstValueCount++;
  4805. return TRUE;
  4806. }
  4807. DWORD
  4808. ApplyNewVolatileDirIdsToInfs(
  4809. IN PLOADED_INF MasterInf,
  4810. IN PLOADED_INF Inf OPTIONAL
  4811. )
  4812. /*++
  4813. Routine Description:
  4814. This routine processes either a single INF, or each loaded INF in the
  4815. linked list, applying volatile system or user-defined DIRID mappings to each
  4816. value containing unresolved string substitutions.
  4817. THIS ROUTINE DOES NOT DO INF LOCKING--CALLER MUST DO IT!
  4818. Arguments:
  4819. MasterInf - Supplies a pointer to the head of a linked list of loaded inf
  4820. structures. This 'master' node contains the user-defined DIRID
  4821. mappings for this set of INFs. If the 'Inf' parameter is not specified,
  4822. then each INF in this linked list is processed.
  4823. Inf - Optionally, supplies a pointer to a single INF within the MasterInf list
  4824. to be processed. If this parameter is not specified, then all INFs in
  4825. the list are processed.
  4826. Return Value:
  4827. If success, the return value is NO_ERROR.
  4828. If failure, the return value is a Win32 error code.
  4829. --*/
  4830. {
  4831. PLOADED_INF CurInf, WriteableInf;
  4832. UINT UserDirIdCount;
  4833. PUSERDIRID UserDirIds;
  4834. DWORD i;
  4835. PCTSTR TemplateString;
  4836. PPARSE_CONTEXT ParseContext = NULL;
  4837. DWORD UnresolvedSubst;
  4838. LONG NewStringId;
  4839. UserDirIdCount = MasterInf->UserDirIdList.UserDirIdCount;
  4840. UserDirIds = MasterInf->UserDirIdList.UserDirIds;
  4841. for(CurInf = Inf ? Inf : MasterInf;
  4842. CurInf;
  4843. CurInf = Inf ? NULL : CurInf->Next) {
  4844. //
  4845. // Nothing to do if there are no unresolved string substitutions.
  4846. //
  4847. if(!(CurInf->SubstValueCount)) {
  4848. continue;
  4849. }
  4850. //
  4851. // If this is a PNF, then we must move it into writeable memory before
  4852. // we do the string substitutions.
  4853. //
  4854. if(CurInf->FileHandle != INVALID_HANDLE_VALUE) {
  4855. if(!(WriteableInf = DuplicateLoadedInfDescriptor(CurInf))) {
  4856. if(ParseContext) {
  4857. MyFree(ParseContext);
  4858. }
  4859. return ERROR_NOT_ENOUGH_MEMORY;
  4860. }
  4861. //
  4862. // Replace the contents of the PNF in the linked list with that of our
  4863. // new writeable INF.
  4864. //
  4865. ReplaceLoadedInfDescriptor(CurInf, WriteableInf);
  4866. }
  4867. //
  4868. // There are one or more unresolved string substitutions in this INF.
  4869. // Process each one.
  4870. //
  4871. for(i = 0; i < CurInf->SubstValueCount; i++) {
  4872. //
  4873. // Retrieve the original (template) string for this value.
  4874. //
  4875. TemplateString = pStringTableStringFromId(CurInf->StringTable,
  4876. CurInf->SubstValueList[i].TemplateStringId
  4877. );
  4878. MYASSERT(TemplateString);
  4879. //
  4880. // Build a partial parse context structure to pass into ProcessForSubstitutions().
  4881. //
  4882. if(!ParseContext) {
  4883. ParseContext = MyMalloc(sizeof(PARSE_CONTEXT));
  4884. if(!ParseContext) {
  4885. return ERROR_NOT_ENOUGH_MEMORY;
  4886. }
  4887. ZeroMemory(ParseContext,sizeof(PARSE_CONTEXT));
  4888. }
  4889. ParseContext->DoVolatileDirIds = TRUE;
  4890. ParseContext->Inf = MasterInf;
  4891. //
  4892. // None of the other fields are used in this case--don't bother initializing them.
  4893. //
  4894. ProcessForSubstitutions(ParseContext, TemplateString, &UnresolvedSubst);
  4895. NewStringId = pStringTableAddString(CurInf->StringTable,
  4896. ParseContext->TemporaryString,
  4897. STRTAB_BUFFER_WRITEABLE | (CurInf->SubstValueList[i].CaseSensitive
  4898. ? STRTAB_CASE_SENSITIVE
  4899. : STRTAB_CASE_INSENSITIVE),
  4900. NULL,0
  4901. );
  4902. if(NewStringId == -1) {
  4903. //
  4904. // We failed because of an out-of-memory condition. Aborting now means that the
  4905. // INF may have some of its unresolved strings fixed up, while others haven't yet
  4906. // been processed. Oh well...
  4907. //
  4908. MyFree(ParseContext);
  4909. return ERROR_NOT_ENOUGH_MEMORY;
  4910. }
  4911. //
  4912. // Replace the string ID at the value offset with the new one we just computed.
  4913. //
  4914. CurInf->ValueBlock[CurInf->SubstValueList[i].ValueOffset] = NewStringId;
  4915. }
  4916. }
  4917. if(ParseContext) {
  4918. MyFree(ParseContext);
  4919. }
  4920. return NO_ERROR;
  4921. }
  4922. BOOL
  4923. AlignForNextBlock(
  4924. IN HANDLE hFile,
  4925. IN DWORD ByteCount
  4926. )
  4927. /*++
  4928. Routine Description:
  4929. This routine writes out the requested number of zero bytes into the specified
  4930. file.
  4931. Arguments:
  4932. hFile - Supplies a handle to the file where the zero-valued bytes are to be
  4933. written.
  4934. ByteCount - Specifies the number of zero-valued bytes to write to the file.
  4935. Return Value:
  4936. If success, the return value is TRUE.
  4937. If failure, the return value is FALSE. Call GetLastError() to retrieve a
  4938. Win32 error code indicating the cause of the failure.
  4939. --*/
  4940. {
  4941. DWORD i, BytesWritten;
  4942. BYTE byte = 0;
  4943. MYASSERT(ByteCount < PNF_ALIGNMENT);
  4944. for(i = 0; i < ByteCount; i++) {
  4945. if(!WriteFile(hFile, &byte, sizeof(byte), &BytesWritten, NULL)) {
  4946. //
  4947. // LastError already set.
  4948. //
  4949. return FALSE;
  4950. }
  4951. MYASSERT(BytesWritten == sizeof(byte));
  4952. }
  4953. return TRUE;
  4954. }
  4955. DWORD
  4956. pSetupGetOsLoaderDriveAndPath(
  4957. IN BOOL RootOnly,
  4958. OUT PTSTR CallerBuffer,
  4959. IN DWORD CallerBufferSize,
  4960. OUT PDWORD RequiredSize OPTIONAL
  4961. )
  4962. /*++
  4963. Routine Description:
  4964. This routine retrieves the current path for the system partition root/OsLoader directory
  4965. (from the registry).
  4966. Arguments:
  4967. RootOnly - if TRUE, then only the system partition root is returned (e.g., "C:\")
  4968. CallerBuffer - supplies a character buffer that receives the requested path
  4969. CallerBufferSize - supplies the size, in characters of the CallerBuffer
  4970. RequiredSize - optionally, supplies the address of a variable that receives the
  4971. number of characters required to store the requested path string (including
  4972. terminating NULL).
  4973. Return Value:
  4974. If success, the return value is NO_ERROR.
  4975. If failure, the return value is ERROR_INSUFFICIENT_BUFFER.
  4976. --*/
  4977. {
  4978. HKEY hKey;
  4979. TCHAR CharBuffer[MAX_PATH];
  4980. PTSTR Buffer = NULL;
  4981. DWORD DataLen;
  4982. DWORD Type;
  4983. LONG Err;
  4984. CopyMemory(CharBuffer,
  4985. pszPathSetup,
  4986. sizeof(pszPathSetup) - sizeof(TCHAR)
  4987. );
  4988. CopyMemory((PBYTE)CharBuffer + (sizeof(pszPathSetup) - sizeof(TCHAR)),
  4989. pszKeySetup,
  4990. sizeof(pszKeySetup)
  4991. );
  4992. if((Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  4993. CharBuffer,
  4994. 0,
  4995. KEY_READ,
  4996. &hKey)) == ERROR_SUCCESS) {
  4997. Err = QueryRegistryValue(hKey,pszBootDir,&Buffer,&Type,&DataLen);
  4998. if(Err == NO_ERROR) {
  4999. lstrcpyn(CharBuffer,Buffer,SIZECHARS(CharBuffer));
  5000. MyFree(Buffer);
  5001. }
  5002. RegCloseKey(hKey);
  5003. }
  5004. if(Err != ERROR_SUCCESS) {
  5005. #ifdef UNICODE
  5006. //
  5007. // If we couldn't retrieve the 'BootDir' value, resort to using the
  5008. // OsSystemPartitionRoot
  5009. //
  5010. // root path is \\?\GLOBALROOT\<SystemPartition> not <BootDir>
  5011. // can't make assumption about BootDir
  5012. // so fail if we don't have that information
  5013. //
  5014. if(!OsSystemPartitionRoot) {
  5015. //
  5016. // if this is NULL at this point, we can't support this call
  5017. // most likely due to out of memory condition, so report as such
  5018. //
  5019. return ERROR_OUTOFMEMORY;
  5020. }
  5021. lstrcpyn(CharBuffer,OsSystemPartitionRoot,SIZECHARS(CharBuffer));
  5022. #else
  5023. //
  5024. // If we couldn't retrieve the 'BootDir' value, drop back to default of "C:\".
  5025. //
  5026. lstrcpyn(CharBuffer,pszDefaultSystemPartition,SIZECHARS(CharBuffer));
  5027. #endif
  5028. Err = NO_ERROR;
  5029. }
  5030. //
  5031. // If there is an OsLoader relative path, then concatenate it to our root path.
  5032. //
  5033. if(!RootOnly && OsLoaderRelativePath) {
  5034. pSetupConcatenatePaths(CharBuffer, OsLoaderRelativePath, SIZECHARS(CharBuffer), &DataLen);
  5035. } else {
  5036. DataLen = lstrlen(CharBuffer)+1;
  5037. }
  5038. if(RequiredSize) {
  5039. *RequiredSize = DataLen;
  5040. }
  5041. if(CallerBufferSize < DataLen) {
  5042. return ERROR_INSUFFICIENT_BUFFER;
  5043. }
  5044. CopyMemory(CallerBuffer, CharBuffer, DataLen * sizeof(TCHAR));
  5045. return NO_ERROR;
  5046. }