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

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