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.

702 lines
19 KiB

  1. #---------------------------------------------------------------------
  2. package ReadSetupFiles;
  3. #
  4. # Copyright (c) Microsoft Corporation. All rights reserved.
  5. #
  6. # Used to find and parse common setup files like
  7. # dosnet.inf, excdosnt.inf and drvindex.inf
  8. #
  9. #---------------------------------------------------------------------
  10. use strict;
  11. # Local include path
  12. use lib $ENV{ "RazzleToolPath" };
  13. use lib $ENV{ "RazzleToolPath" } . "\\PostBuildScripts";
  14. # Local includes
  15. use Logmsg;
  16. #
  17. # Function:
  18. # GetSetupDir
  19. #
  20. # Arguments:
  21. #
  22. # Sku (scalar) - sku to return setup dir for
  23. #
  24. # Purpose:
  25. # Returns the relative setup dir associated with the sku
  26. # per = perinf, pro=., bla=blainf, sbs=sbsinf, srv=srvinf, ads=entinf, dtc=dtcinf
  27. #
  28. # Returns:
  29. # String representing setup dir
  30. # UNDEF on failure
  31. sub GetSetupDir
  32. {
  33. my $Sku = lc$_[0];
  34. # Pro (wks) setup info is found at the root
  35. if ( $Sku eq 'pro' ) {
  36. return ".";
  37. } elsif ( $Sku eq 'ads' ) {
  38. # ADS used to be called ENT and the files are
  39. # still found under the old name
  40. return "entinf";
  41. } elsif ( $Sku eq 'per' ||
  42. $Sku eq 'bla' ||
  43. $Sku eq 'sbs' ||
  44. $Sku eq 'srv' ||
  45. $Sku eq 'dtc' ) {
  46. return $Sku . "inf";
  47. } else {
  48. # Unrecognized SKU
  49. return;
  50. }
  51. }
  52. #
  53. # Function:
  54. # ReadDosnet
  55. #
  56. # Arguments:
  57. #
  58. # RootPath (scalar) - path to the flat binaries\release share
  59. #
  60. # Sku (scalar) - per, pro, bla, sbs, srv, ads, dtc
  61. #
  62. # Architecture (scalar) - x86, amd64, ia64
  63. #
  64. # Files (array by ref) - Fills array in with files referenced from
  65. # primary location
  66. #
  67. # Secondaryfiles (array by ref) - Fills array in with files referenced from
  68. # secondary location (Win64 WOW files/x86 TabletPC files)
  69. #
  70. # Purpose:
  71. # Reads in a dosnet.inf file, returning the standard files
  72. #
  73. # Returns:
  74. # 1 if successful, undef otherwise
  75. #
  76. sub ReadDosnet
  77. {
  78. my ($RootPath, $Sku, $Architecture, $Files, $SecondaryFiles) = @_;
  79. my ($DosnetPath, $RelativeSetupDir);
  80. my (@FullDosnetLines);
  81. $RelativeSetupDir = GetSetupDir( $Sku );
  82. if ( ! defined $RelativeSetupDir ) {
  83. errmsg( "Unrecognized SKU \"$Sku\"." );
  84. return ();
  85. }
  86. $RootPath =~ s/\\$//;
  87. $DosnetPath = $RootPath . "\\" . $RelativeSetupDir . "\\dosnet.inf";
  88. # Attempt to open the dosnet file
  89. unless ( open DOSNET, $DosnetPath ) {
  90. errmsg( "Failed to open $DosnetPath for reading." );
  91. return ();
  92. }
  93. @FullDosnetLines = <DOSNET>;
  94. close DOSNET;
  95. return ParseDosnetFile( \@FullDosnetLines, $Architecture, $Files, $SecondaryFiles );
  96. }
  97. #
  98. # Function:
  99. # ParseDosnetFile
  100. #
  101. # Arguments:
  102. # DosnetLines (array by ref) - ref to array containing contents
  103. # of a dosnet-style file
  104. #
  105. # Architecture (scalar) - x86, amd64, ia64
  106. #
  107. # Files (array by ref) - ref to array of files referenced from
  108. # the primary location will be stored
  109. #
  110. # SecondaryFiles (array by ref) - ref to array of files referenced from
  111. # secondary location (WOW files for win64/
  112. # TabletPC files for x86)
  113. #
  114. # Purpose:
  115. # Contains the logic to parse a dosnet file
  116. #
  117. # Returns:
  118. # 1 if successful, undef otherwise
  119. #
  120. sub ParseDosnetFile
  121. {
  122. my ( $DosnetLines, $Architecture, $Files, $SecondaryFiles) = @_;
  123. my ($ReadFlag, $Line, $fIsSecondaryFile);
  124. my (%CheckForRedundantFiles, %CheckForRedundantSecondaryFiles);
  125. undef $ReadFlag;
  126. foreach $Line ( @$DosnetLines ) {
  127. chomp( $Line );
  128. next if ( ( length( $Line ) == 0 ) ||
  129. ( substr( $Line, 0, 1 ) eq ";" ) ||
  130. ( substr( $Line, 0, 1 ) eq "#" ) ||
  131. ( substr( $Line, 0, 2 ) eq "@*" ) );
  132. if ( $Line =~ /^\[/ ) { undef $ReadFlag; }
  133. if ( ( $Line =~ /^\[Files\]/ ) ||
  134. ( $Line =~ /^\[SourceDisksFiles\]/ ) ) {
  135. $ReadFlag = 1;
  136. } elsif ( $Line =~ /^\[SourceDisksFiles\.$Architecture\]/i ) {
  137. $ReadFlag = 1;
  138. } elsif ( $ReadFlag ) {
  139. my ($MyName, $SrcDir, $Junk);
  140. # Determine if this is a secondary (d2) file
  141. # and strip the source directory
  142. undef $fIsSecondaryFile;
  143. if ( $Line =~ s/^d2\,// ) {
  144. $fIsSecondaryFile = 1;
  145. }
  146. else {
  147. $Line =~ s/^d\d+\,//;
  148. }
  149. ( $MyName, $SrcDir ) = split( /\,/, $Line );
  150. ( $MyName, $Junk ) = split( /\s/, $MyName );
  151. if ( $fIsSecondaryFile ) {
  152. if ( ($CheckForRedundantSecondaryFiles{ lc $MyName }++) > 1 ) {
  153. logmsg( "WARNING: redundant file $MyName found." );
  154. } else {
  155. push @$SecondaryFiles, $MyName;
  156. }
  157. }
  158. else {
  159. if ( ($CheckForRedundantFiles{ lc $MyName }++) > 1 ) {
  160. logmsg( "WARNING: redundant file $MyName found." );
  161. } else {
  162. push @$Files, $MyName;
  163. }
  164. }
  165. }
  166. }
  167. return 1;
  168. }
  169. #
  170. # Function:
  171. # ReadExcDosnet
  172. #
  173. # Arguments:
  174. #
  175. # RootPath (scalar) - path to the flat binaries\release share
  176. #
  177. # Sku (scalar) - per, pro, bla, sbs, srv, ads, dtc
  178. #
  179. # Files (array by ref) - Fills array in with files listed in excdosnt.inf
  180. #
  181. # Purpose:
  182. # Reads in an excdosnt.inf file, returning the standard files
  183. #
  184. # Returns:
  185. # 1 if successful, undef otherwise
  186. #
  187. sub ReadExcDosnet
  188. {
  189. my ( $RootPath, $Sku, $Files ) = @_;
  190. my ($ExcDosnetPath, $RelativeSetupDir);
  191. my (@FullExcDosnetLines);
  192. $RelativeSetupDir = GetSetupDir( $Sku );
  193. if ( ! defined $RelativeSetupDir ) {
  194. errmsg( "Unrecognized SKU \"$Sku\"." );
  195. return ();
  196. }
  197. $RootPath =~ s/\\$//;
  198. $ExcDosnetPath = $RootPath . "\\" . $RelativeSetupDir . "\\excdosnt.inf";
  199. # Attempt to open the excdosnt file
  200. unless ( open EXCDOSNET, $ExcDosnetPath ) {
  201. errmsg( "Failed to open $ExcDosnetPath for reading." );
  202. return ();
  203. }
  204. @FullExcDosnetLines = <EXCDOSNET>;
  205. close EXCDOSNET;
  206. # Seems to work OK to parse excdosnt file as a dosnet file
  207. return ParseExcDosnetFile( \@FullExcDosnetLines, $Files );
  208. }
  209. #
  210. # Function:
  211. # ParseExcDosnetFile
  212. #
  213. # Arguments:
  214. # ExcDosnetLines (array by ref) - ref to array containing contents
  215. # of an excdosnt-style file
  216. #
  217. # Files (array by ref) - ref to array of files listed in excdosnt.inf
  218. #
  219. # Purpose:
  220. # Contains the logic to parse an excdosnt file
  221. #
  222. # Returns:
  223. # 1 if successful, undef otherwise
  224. #
  225. sub ParseExcDosnetFile
  226. {
  227. my ( $ExcDosnetLines, $Files ) = @_;
  228. my ($ReadFlag, $Line);
  229. my %CheckForRedundantFiles;
  230. undef $ReadFlag;
  231. foreach $Line ( @$ExcDosnetLines ) {
  232. chomp( $Line );
  233. if ( $Line =~ /^\[Files\]/ ) {
  234. $ReadFlag = 1;
  235. } elsif ( $ReadFlag ) {
  236. if ( ($CheckForRedundantFiles{ lc $Line }++) > 1 ) {
  237. logmsg( "WARNING: redundant file $Line found." );
  238. } else {
  239. push @$Files, $Line;
  240. }
  241. }
  242. }
  243. return 1;
  244. }
  245. #
  246. # Function:
  247. # ReadDrvIndex
  248. #
  249. # Arguments:
  250. #
  251. # RootPath (scalar) - path to the flat binaries\release share
  252. #
  253. # Sku (scalar) - per, pro, bla, sbs, srv, ads, dtc
  254. #
  255. # Files (array by ref) - Fills array in with files listed in drvindex.inf
  256. #
  257. # Purpose:
  258. # Reads in a drvindex.inf file, returning the files
  259. #
  260. # Returns:
  261. # 1 if successful, undef otherwise
  262. #
  263. sub ReadDrvIndex
  264. {
  265. my ( $RootPath, $Sku, $Files ) = @_;
  266. my ($DrvIndexPath, $RelativeSetupDir);
  267. my (@FullDrvIndexLines);
  268. $RelativeSetupDir = GetSetupDir( $Sku );
  269. if ( ! defined $RelativeSetupDir ) {
  270. errmsg( "Unrecognized SKU \"$Sku\"." );
  271. return ();
  272. }
  273. $RootPath =~ s/\\$//;
  274. $DrvIndexPath = $RootPath . "\\" . $RelativeSetupDir . "\\drvindex.inf";
  275. # Attempt to open the drvindex file
  276. unless ( open DRVINDEX, $DrvIndexPath ) {
  277. errmsg( "Failed to open $DrvIndexPath for reading." );
  278. return ();
  279. }
  280. @FullDrvIndexLines = <DRVINDEX>;
  281. close DRVINDEX;
  282. # Seems to work OK to parse excdosnt file as a dosnet file
  283. return ParseDrvIndexFile( \@FullDrvIndexLines, $Files );
  284. }
  285. #
  286. # Function:
  287. # ParseDrvIndexFile
  288. #
  289. # Arguments:
  290. # DrvIndexLines (array by ref) - ref to array containing contents
  291. # of a drvindex-style file
  292. #
  293. # Files (array by ref) - ref to array of files listed in drvindex.inf
  294. #
  295. # Purpose:
  296. # Contains the logic to parse a drvindex file
  297. #
  298. # Returns:
  299. # 1 if successful, undef otherwise
  300. #
  301. sub ParseDrvIndexFile
  302. {
  303. my ( $DrvIndexLines, $Files ) = @_;
  304. my ($ReadFlag, $Line);
  305. my %CheckForRedundantFiles;
  306. undef $ReadFlag;
  307. foreach $Line ( @$DrvIndexLines ) {
  308. chomp( $Line );
  309. next if ( 0 == length($Line) || $Line =~ /^\;/ );
  310. if ( $Line =~ /\[driver\]/ ) {
  311. $ReadFlag = 1;
  312. } elsif ( $Line =~ /\[/ ) {
  313. undef $ReadFlag;
  314. } elsif ( $ReadFlag ) {
  315. if ( ($CheckForRedundantFiles{ lc $Line }++) > 1 ) {
  316. logmsg( "WARNING: redundant file $Line found." );
  317. } else {
  318. push @$Files, $Line;
  319. }
  320. }
  321. }
  322. return 1;
  323. }
  324. #
  325. # Function:
  326. # ReadLayout
  327. #
  328. # Arguments:
  329. #
  330. # RootPath (scalar) - path to the flat binaries\release share
  331. #
  332. # Sku (scalar) - per, pro, bla, sbs, srv, ads, dtc
  333. #
  334. # Architecture (scalar) - x86, amd64, ia64
  335. #
  336. # FileHash (hash by ref) - adds files as keys and a hash of attributes as the value
  337. # Known Attributes:
  338. # DiskID
  339. # SubDir
  340. # Size
  341. # Checksum
  342. # Spare1
  343. # Spare2
  344. # BootMediaOrd
  345. # TargetDirectory
  346. # UpgradeDisposition
  347. # TextModeDisposition
  348. # TargetName
  349. # MiniNTSourceDirectory
  350. # MiniNTTargetDirectory
  351. #
  352. # Purpose:
  353. # Reads in a layout.inf file, returning information about the files
  354. #
  355. # Returns:
  356. # 1 if successful, undef otherwise
  357. #
  358. sub ReadLayout
  359. {
  360. my ($RootPath, $Sku, $Architecture, $FileHash) = @_;
  361. my ($LayoutPath, $RelativeSetupDir);
  362. my (@FullLayoutLines);
  363. $RelativeSetupDir = GetSetupDir( $Sku );
  364. if ( ! defined $RelativeSetupDir ) {
  365. errmsg( "Unrecognized SKU \"$Sku\"." );
  366. return ();
  367. }
  368. $RootPath =~ s/\\$//;
  369. $LayoutPath = $RootPath . "\\" . $RelativeSetupDir . "\\layout.inf";
  370. # Attempt to open the layout file
  371. unless ( open LAYOUT, $LayoutPath ) {
  372. errmsg( "Failed to open $LayoutPath for reading ($!)." );
  373. return;
  374. }
  375. @FullLayoutLines = <LAYOUT>;
  376. close LAYOUT;
  377. return ParseLayoutFile( \@FullLayoutLines, $Architecture, $FileHash );
  378. }
  379. #
  380. # Function:
  381. # ParseLayoutFile
  382. #
  383. # Arguments:
  384. # LayoutLines (array by ref) - ref to array containing contents
  385. # of a layout-style file
  386. #
  387. # Architecture (scalar) - x86, amd64, ia64
  388. #
  389. # FileHash (hash by ref) - ref to hash which will get files as keys
  390. # and a hash of attributes as values
  391. # Known Attributes:
  392. # DiskID
  393. # SubDir
  394. # Size
  395. # Checksum
  396. # Spare1
  397. # Spare2
  398. # BootMediaOrd
  399. # TargetDirectory
  400. # UpgradeDisposition
  401. # TextModeDisposition
  402. # TargetName
  403. # MiniNTSourceDirectory
  404. # MiniNTTargetDirectory
  405. #
  406. # Purpose:
  407. # Contains the logic to parse a layout file
  408. #
  409. # Returns:
  410. # 1 if successful, undef otherwise
  411. #
  412. sub ParseLayoutFile
  413. {
  414. my ( $LayoutLines, $Architecture, $FileHash) = @_;
  415. my ($ReadFlag, $Line);
  416. my (%CheckForRedundantFiles);
  417. # Return a compressed, comment-free, variable substituted form of layout
  418. my @processedLayout = ProcessLayoutLines($LayoutLines);
  419. my %strings;
  420. undef $ReadFlag;
  421. foreach $Line ( @processedLayout ) {
  422. if ( $Line =~ /^\[/ ) { undef $ReadFlag; }
  423. if ( ( $Line =~ /^\[Files\]/ ) ||
  424. ( $Line =~ /^\[SourceDisksFiles\]/ ) ) {
  425. $ReadFlag = 1;
  426. } elsif ( $Line =~ /^\[SourceDisksFiles\.$Architecture\]/i ) {
  427. $ReadFlag = 1;
  428. } elsif ( $ReadFlag ) {
  429. if ( $Line =~ /^([^\s]+)\s*=\s*(.*[^\s])\s*$/ ) {
  430. # Put a newline at the end so that split will work like we want it to
  431. my $field_info = "$2\n";
  432. my @fields = split ',', $field_info;
  433. if ( @fields < 9 || @fields > 13 ) {
  434. wrnmsg( "Unrecognized line: $Line" );
  435. next;
  436. }
  437. # remove the newline we added
  438. chomp $fields[$#fields];
  439. my %fileInfo = ( DiskID => $fields[0],
  440. SubDir => $fields[1],
  441. Size => $fields[2],
  442. Checksum => $fields[3],
  443. Spare1 => $fields[4],
  444. Spare2 => $fields[5],
  445. BootMediaOrd => $fields[6],
  446. TargetDirectory => $fields[7],
  447. UpgradeDisposition => $fields[8],
  448. TextModeDisposition => $fields[9],
  449. TargetName => $fields[10]?$fields[10]:'',
  450. MiniNTSourceDirectory => $fields[11]?$fields[11]:'',
  451. MiniNTTargetDirectory => $fields[12]?$fields[12]:'' );
  452. if ( exists $FileHash->{$1} ) {
  453. wrnmsg( "Multiple entries for file $1 found." );
  454. }
  455. $FileHash->{$1} = \%fileInfo;
  456. }
  457. else {
  458. wrnmsg( "Unrecognized line: $Line" );
  459. }
  460. }
  461. }
  462. return 1;
  463. }
  464. sub ProcessLayoutLines
  465. {
  466. my $lines = shift;
  467. my @tokenizedLines;
  468. my @processedLines;
  469. my $nextLine = '';
  470. my @string_section_offsets;
  471. my %var_lookup;
  472. my ($i, $j);
  473. foreach ( @$lines )
  474. {
  475. # pre-processor comment
  476. if ( /^@\*/ ) { next; }
  477. # line is continued
  478. if ( s/\\\s*$/ / ) {
  479. $nextLine .= $_;
  480. next;
  481. }
  482. # non-continued line (or last line of a continue line)
  483. else {
  484. $nextLine .= $_;
  485. }
  486. # remove leading space / trailing space
  487. $nextLine =~ s/^\s*//;
  488. $nextLine =~ s/\s*$//;
  489. # Skip empty lines
  490. if ( !$nextLine ) { next }
  491. # Specifically want to end the lines with a
  492. # newline so split will work how we want it to
  493. $nextLine .= "\n";
  494. my @tokenized;
  495. my @quote_split = split '"', $nextLine;
  496. #
  497. # Token types:
  498. # 0 - standard text
  499. # 1 - quoted text
  500. # 2 - variable (in %%)
  501. # 3 - quoted variable (%*% inside quotes)
  502. #
  503. for ( $i = 0; $i < @quote_split; $i++ ) {
  504. # empty quotes aren't going to contain any %'s
  505. if ( $i % 2 && !$quote_split[$i] ) {
  506. push @tokenized, ['', 1];
  507. }
  508. my @percent_split = split '%', $quote_split[$i];
  509. for ( $j = 0; $j < @percent_split; $j++ ) {
  510. if ( $j % 2 ) {
  511. # Variable string (in %%)
  512. push @tokenized, [$percent_split[$j], ($i % 2 ? 3:2)];
  513. }
  514. else {
  515. # Not in %%
  516. push @tokenized, [$percent_split[$j], ($i % 2 ? 1:0)];
  517. }
  518. }
  519. }
  520. # comment check (;)
  521. # ";" and %;% are not comments
  522. for ( $i = 0; $i < @tokenized; $i++ ) {
  523. if ( $tokenized[$i]->[1] == 0 &&
  524. $tokenized[$i]->[0] =~ s/;.*// ) {
  525. splice @tokenized, $i + 1;
  526. last;
  527. }
  528. }
  529. if ( @tokenized > 1 ||
  530. length($tokenized[0]->[0]) > 1 ) {
  531. push @tokenizedLines, \@tokenized;
  532. # look for [Strings] as we will need it later
  533. if ( $nextLine =~ /^\[Strings\]/i ) {
  534. push @string_section_offsets, scalar(@tokenizedLines);
  535. }
  536. }
  537. # get the next line
  538. $nextLine = '';
  539. }
  540. # Now build a list of variables and their values from the [Strings] section
  541. foreach ( @string_section_offsets ) {
  542. my $i;
  543. for ( $i = $_ + 1; $i < @tokenizedLines; $i++ ) {
  544. my $tokens = $tokenizedLines[$i];
  545. # hit another section -- done
  546. if ( $tokens->[1] == 0 &&
  547. $tokens->[0] =~ /^\[/ ) {
  548. last;
  549. }
  550. my $entry = BuildLayoutLineFromTokens( $tokens, {} );
  551. my ($key, $value) = $entry =~ /\s*([^\s=]+)\s*=\s*(.*)/;
  552. if ( $key && $value ) {
  553. $var_lookup{lc$key} = $value;
  554. }
  555. }
  556. }
  557. # Rebuild the lines, replacing variables if we know the values
  558. foreach ( @tokenizedLines ) {
  559. my $new_entry = BuildLayoutLineFromTokens( $_, \%var_lookup );
  560. # chomp the lines here as we added a newline
  561. chomp $new_entry;
  562. push @processedLines, $new_entry;
  563. }
  564. return @processedLines;
  565. }
  566. sub BuildLayoutLineFromTokens
  567. {
  568. my ($token_list, $var_lookup) = @_;
  569. my $line = '';
  570. my $fInQuote;
  571. my $fInVar;
  572. foreach ( @$token_list ) {
  573. if ( 0 == $_->[1] ) {
  574. if ( $fInQuote ) {
  575. $line .= '"';
  576. undef $fInQuote;
  577. }
  578. elsif ( $fInVar ) {
  579. $line .= '%';
  580. undef $fInVar;
  581. }
  582. $line .= $_->[0];
  583. }
  584. elsif ( 1 == $_->[1] ) {
  585. if ( $fInVar ) {
  586. $line .= '%';
  587. undef $fInVar;
  588. }
  589. $line .= '"';
  590. $fInQuote = 1;
  591. $line .= $_->[0];
  592. }
  593. elsif ( 2 == $_->[1] ) {
  594. if ( $fInQuote ) {
  595. $line .= '"';
  596. undef $fInQuote;
  597. }
  598. if ( exists $var_lookup->{lc$_->[0]} ) {
  599. $line .= $var_lookup->{lc$_->[0]};
  600. }
  601. else {
  602. $line .= '%';
  603. $fInVar = 1;
  604. $line .= $_->[0];
  605. }
  606. }
  607. elsif ( 3 == $_->[1] ) {
  608. if ( exists $var_lookup->{lc$_->[0]} ) {
  609. $line .= $var_lookup->{lc$_->[0]};
  610. }
  611. else {
  612. $line .= '%';
  613. $fInVar = 1;
  614. $line .= $_->[0];
  615. }
  616. }
  617. else {
  618. wrnmsg( "Internal error parsing layout lines (token-type = $_->[1])" );
  619. next;
  620. }
  621. }
  622. return $line;
  623. }
  624. 1;