Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4218 lines
127 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
infload.c
Abstract:
Routines to load and parse INF files, and manipulate data in them.
Author:
Ted Miller (tedm) 13-Jan-1995
Revision History:
--*/
#include "setupntp.h"
#pragma hdrstop
//
// Values used when initializing and growing the section, line, and value blocks.
//
#define INITIAL_SECTION_BLOCK_SIZE 50
#define INITIAL_LINE_BLOCK_SIZE 350
#define INITIAL_VALUE_BLOCK_SIZE 1000
#define SECTION_BLOCK_GROWTH 10
#define LINE_BLOCK_GROWTH 100
#define VALUE_BLOCK_GROWTH 500
//
// Macros used to quadword-align PNF blocks.
//
#define PNF_ALIGNMENT ((DWORD)8)
#define PNF_ALIGN_MASK (~(DWORD)(PNF_ALIGNMENT - 1))
#define PNF_ALIGN_BLOCK(x) ((x & PNF_ALIGN_MASK) + ((x & ~PNF_ALIGN_MASK) ? PNF_ALIGNMENT : 0))
//
// Structure containing parameters relating to a [strings] section of an INF
// file (used during parsing).
//
typedef struct _STRINGSEC_PARAMS {
PCTSTR Start;
PCTSTR End;
UINT StartLineNumber;
UINT EndLineNumber;
} STRINGSEC_PARAMS, *PSTRINGSEC_PARAMS;
//
// Parse context, used by inf load/parse routines to pass
// state around.
//
typedef struct _PARSE_CONTEXT {
//
// Pointer to the end of the buffer.
//
PCTSTR BufferEnd;
//
// Current line number in the file
//
UINT CurrentLineNumber;
//
// section, line, and value block buffer sizes and current locations.
//
UINT LineBlockUseCount;
UINT ValueBlockUseCount;
UINT SectionBlockSize;
UINT LineBlockSize;
UINT ValueBlockSize;
//
// Value indicating whether we are within a section.
// We always within a section unless the first non-comment line
// of the inf is not section line, and that is an error case.
//
BOOL GotOneSection;
//
// Pointer to the actual inf descriptor
//
PLOADED_INF Inf;
//
// The following field is used solely for the purposes of calling
// ProcessForSubstitutions() after an INF has already been loaded.
// This is necessary when applying user-defined DIRIDs to unresolved
// string substitutions. If this flag is TRUE, then the aforementioned
// routine will call pSetupUserDirIdToPath for %<x>% substrings, instead
// of its normal (i.e., load-time) processing.
//
BOOL DoUserDirIds;
//
// Specifies the directory where this INF is located (if it's an OEM location).
//
PCTSTR InfSourcePath; // may be NULL.
//
// Specifies the drive/directory where the OsLoader is located.
//
PCTSTR OsLoaderPath;
//
// Buffer used during parsing.
//
TCHAR TemporaryString[MAX_INF_STRING_LENGTH+1];
} PARSE_CONTEXT, *PPARSE_CONTEXT;
//
// Declare global string variables used throughout the inf loaders.
//
// These strings are defined in infstr.h:
//
CONST TCHAR pszSignature[] = INFSTR_KEY_SIGNATURE,
pszVersion[] = INFSTR_SECT_VERSION,
pszClass[] = INFSTR_KEY_HARDWARE_CLASS,
pszClassGuid[] = INFSTR_KEY_HARDWARE_CLASSGUID,
pszProvider[] = INFSTR_KEY_PROVIDER,
pszStrings[] = SZ_KEY_STRINGS,
pszLayoutFile[] = SZ_KEY_LAYOUT_FILE,
pszManufacturer[] = INFSTR_SECT_MFG,
pszControlFlags[] = INFSTR_CONTROLFLAGS_SECTION,
pszReboot[] = INFSTR_REBOOT,
pszRestart[] = INFSTR_RESTART,
pszClassInstall32[] = INFSTR_SECT_CLASS_INSTALL_32;
//
// Other misc. global strings:
//
// Be sure to keep these strings in sync with the strings used
// in inf.h to compute the array size. This is done so that
// we can determine string length by doing a sizeof() instead
// of having to do lstrlen().
//
CONST TCHAR pszDrvDescFormat[] = DISTR_INF_DRVDESCFMT,
pszHwSectionFormat[] = DISTR_INF_HWSECTIONFMT,
pszChicagoSig[] = DISTR_INF_CHICAGOSIG,
pszWindowsNTSig[] = DISTR_INF_WINNTSIG,
pszWindows95Sig[] = DISTR_INF_WIN95SIG,
pszWinSuffix[] = DISTR_INF_WIN_SUFFIX,
pszNtSuffix[] = DISTR_INF_NT_SUFFIX,
pszNtPlatformSuffix[] = DISTR_INF_NTPLATFORM_SUFFIX,
pszPnfSuffix[] = DISTR_INF_PNF_SUFFIX,
pszServicesSectionSuffix[] = DISTR_INF_SERVICES_SUFFIX;
DWORD
CreateInfVersionNode(
IN PLOADED_INF Inf,
IN PCTSTR Filename,
IN PFILETIME LastWriteTime
);
BOOL
LoadPrecompiledInf(
IN PCTSTR Filename,
IN PFILETIME LastWriteTime,
IN PCTSTR OsLoaderPath, OPTIONAL
IN DWORD Flags,
OUT PLOADED_INF *Inf
);
VOID
SavePnf(
IN PCTSTR Filename,
IN PLOADED_INF Inf
);
PLOADED_INF
DuplicateLoadedInfDescriptor(
IN PLOADED_INF Inf
);
BOOL
AddUnresolvedSubstToList(
IN PLOADED_INF Inf,
IN UINT ValueOffset,
IN BOOL CaseSensitive
);
BOOL
AlignForNextBlock(
IN HANDLE hFile,
IN DWORD ByteCount
);
BOOL
IsWhitespace(
IN PCTSTR pc
)
/*++
Routine Description:
Determine whether a character is whitespace. Whitespace refers to the ctype
definition.
Arguments:
pc - points to character to be examined.
Return Value:
TRUE if the character is whitespace. FALSE if not.
Note that the nul chracter is not whitespace.
--*/
{
WORD Type;
return(GetStringTypeEx(LOCALE_SYSTEM_DEFAULT,CT_CTYPE1,pc,1,&Type) && (Type & C1_SPACE));
}
VOID
SkipWhitespace(
IN OUT PCTSTR *Location,
IN PCTSTR BufferEnd
)
/*++
Routine Description:
Skip whitespace characters in the input stream. For the purposes of this
routine, newline characters are NOT considered whitespace.
Note that the end-of-stream marker ('\0') IS considered whitespace.
Arguments:
Location - on input, supplies the current location in the input stream.
On output, receives the location in the input stream of the first
non-whitespace character. Note that this may be equal to BufferEnd,
if no whitespace was found, in which case the pointer may be
invalid.
BufferEnd - specifies the address of the end of the buffer (i.e., the
memory address immediately following the buffer's memory range).
Return Value:
None.
--*/
{
while((*Location < BufferEnd) &&
(**Location != TEXT('\n')) &&
(!(**Location) || IsWhitespace(*Location))) {
(*Location)++;
}
}
VOID
SkipLine(
IN OUT PPARSE_CONTEXT Context,
IN OUT PCTSTR *Location
)
/*++
Routine Description:
Skip all remaining characters in the current line, and positions the
input pointer to the first character on the next line.
No whitespace is skipped automatically -- the input pointer may
very well point to whitespace or the end-of-stream marker on exit.
Arguments:
Context - supplies the parse context
Location - on input, supplies the current location in the input stream.
On output, receives the location in the input stream of the first
character on the next line.
Return Value:
None.
--*/
{
PCTSTR BufferEnd = Context->BufferEnd;
while((*Location < BufferEnd) && (**Location != TEXT('\n'))) {
(*Location)++;
}
//
// *Location points at either the newline or end-of-buffer.
// Skip the newline if necessary.
//
if(*Location < BufferEnd) {
Context->CurrentLineNumber++;
(*Location)++;
}
}
BOOL
MergeDuplicateSection(
IN PPARSE_CONTEXT Context
)
{
PLOADED_INF Inf;
PINF_SECTION NewestSection;
PINF_SECTION Section;
UINT Size;
UINT MoveSize;
PVOID TempBuffer;
Inf = Context->Inf;
//
// Nothing to merge if only one section
//
if(Inf->SectionCount < 2) {
return(TRUE);
}
NewestSection = Inf->SectionBlock + Inf->SectionCount - 1;
//
// See whether the final section duplicates any existing sections.
//
for(Section=Inf->SectionBlock; Section<NewestSection; Section++) {
if(Section->SectionName == NewestSection->SectionName) {
break;
}
}
if(Section == NewestSection) {
//
// No duplication; return success
//
return(TRUE);
}
//
// Got a duplicate.
//
//
// We need to move the new section's lines (at the end of the line block)
// to be just after the existing section's lines.
//
// First, we'll save off the new section's lines in a temporary buffer.
//
Size = NewestSection->LineCount * sizeof(INF_LINE);
TempBuffer = MyMalloc(Size);
if(!TempBuffer) {
return(FALSE);
}
CopyMemory(TempBuffer,&Inf->LineBlock[NewestSection->Lines],Size);
//
// Next, we'll move up the affected existing lines, to make room for
// the section's new lines
//
MoveSize = Context->LineBlockUseCount - (Section->Lines + Section->LineCount);
MoveSize *= sizeof(INF_LINE);
MoveSize -= Size;
MoveMemory(
&Inf->LineBlock[Section->Lines + Section->LineCount + NewestSection->LineCount],
&Inf->LineBlock[Section->Lines + Section->LineCount],
MoveSize
);
//
// Now put the new lines in the hole we just opened up
//
CopyMemory(
&Inf->LineBlock[Section->Lines + Section->LineCount],
TempBuffer,
Size
);
MyFree(TempBuffer);
//
// Adjust the existing section's limits to account for the new lines.
//
Section->LineCount += NewestSection->LineCount;
//
// Adjust all subsequent sections' starting line value
//
for(Section=Section+1; Section<NewestSection; Section++) {
Section->Lines += NewestSection->LineCount;
}
//
// Remove the newest section.
//
Inf->SectionCount--;
return(TRUE);
}
PTCHAR
LocateStringSubstitute(
IN PPARSE_CONTEXT Context,
IN PTSTR String
)
/*++
Routine Description:
This routine attempts to find a string substitution in an INF's
[strings] section for the specified key.
THIS ROUTINE OPERATES UNDER THE ASSUMPTION THAT IT IS ONLY INVOKED FROM
WITHIN LOADINF. IT DOESN'T HANDLE MULTIPLE INFS, NOR DOES IT DO ANY
INF LOCKING.
Arguments:
Context - current INF parse context
String - string to be substituted
Return Value:
If substitution is a success, the function returns a pointer to the string,
either in the string table or in the workspace. If failure, NULL is returned.
--*/
{
UINT Zero = 0;
PINF_LINE Line;
PCTSTR p;
MYASSERT(Context->Inf->SectionCount > 1);
MYASSERT(Context->Inf->HasStrings);
//
// The strings section is always first to be parsed.
// (See PreprocessInf()).
//
// Look for a line in [strings] with key of String.
//
if(InfLocateLine(Context->Inf,
Context->Inf->SectionBlock,
String,
&Zero,
&Line)) {
//
// Get and return value #1.
//
return(InfGetField(Context->Inf,Line,1,NULL));
}
//
// No valid substitution exists.
//
return NULL;
}
VOID
ProcessForSubstitutions(
IN OUT PPARSE_CONTEXT Context,
IN PCTSTR String,
OUT PBOOL UnresolvedSubst
)
{
PCTSTR In, q;
PTCHAR Out, p;
TCHAR Str[MAX_STRING_LENGTH];
UINT Len, i;
PTCHAR End;
TCHAR DirId[MAX_PATH];
BOOL HasStrings = Context->Inf->HasStrings;
UINT DirIdUsed;
In = String;
Out = Context->TemporaryString;
End = Out + SIZECHARS(Context->TemporaryString);
*UnresolvedSubst = FALSE;
while(*In) {
if(*In == TEXT('%')) {
//
// Double % in input ==> single % in output
//
if(*(++In) == TEXT('%')) {
if(Out < End) {
*Out++ = TEXT('%');
}
In++;
} else {
//
// Look for terminating %.
//
if(p = _tcschr(In,TEXT('%'))) {
//
// Get value to substitute. If we can't find the value,
// put the whole string like %abc% in there.
//
Len = p - In;
if(Len > CSTRLEN(Str)) {
//
// We can't handle substitutions for tokens this long.
// We'll just bail in this case, and copy over the token as-is.
//
q = NULL;
} else {
lstrcpyn(Str,In,Len+1);
if(Context->DoUserDirIds) {
if(q = pSetupUserDirIdToPath(Str, 0, NULL, Context->Inf)) {
lstrcpyn(DirId, q, SIZECHARS(DirId));
MyFree(q);
q = DirId;
//
// If the next character following this string substitution
// is a backslash, then we need to make sure that the path we
// just retrieved doesn't have a backslash (i.e., we want to
// make sure we have a well-formed path).
//
if(*(p + 1) == TEXT('\\')) {
if(DirId[i = lstrlen(DirId) - 1] == TEXT('\\')) {
DirId[i] = TEXT('\0');
}
}
}
} else {
if(HasStrings) {
q = LocateStringSubstitute(Context, Str);
} else {
q = NULL;
}
if(!q) {
//
// Maybe we have a standard DIRID here...
//
if(q = pSetupDirectoryIdToPath(Str,
&DirIdUsed,
NULL,
Context->InfSourcePath,
&(Context->OsLoaderPath))) {
lstrcpyn(DirId, q, SIZECHARS(DirId));
MyFree(q);
q = DirId;
//
// If the next character following this string substitution
// is a backslash, then we need to make sure that the path we
// just retrieved doesn't have a backslash (i.e., we want to
// make sure we have a well-formed path).
//
if(*(p + 1) == TEXT('\\')) {
if(DirId[i = lstrlen(DirId) - 1] == TEXT('\\')) {
DirId[i] = TEXT('\0');
}
}
if((DirIdUsed == DIRID_BOOT) || (DirIdUsed == DIRID_LOADER)) {
//
// Then this INF contains string substititutions that
// reference system partition DIRIDs. Store the OsLoaderPath
// contained in the parse context structure in the INF itself.
//
Context->Inf->OsLoaderPath = Context->OsLoaderPath;
}
}
}
}
}
if(q) {
Len = lstrlen(q);
for(i=0; i<Len; i++) {
if(Out < End) {
*Out++ = q[i];
}
}
In = p+1;
} else {
//
// Len is the length of the internal part (the abc in %abc%).
//
if(Out < End) {
*Out++ = TEXT('%');
}
for(i=0; i<=Len; i++, In++) {
if(Out < End) {
*Out++ = *In;
}
}
//
// When we encounter a substitution for which there is no
// corresponding string, we set the UnresolvedSubst output
// parameter so that the caller knows to track this value
// for later resolution (e.g., for user-defined DIRIDs).
//
// (NOTE: Don't set this if we bailed because the token was too long!)
//
if(Len <= CSTRLEN(Str)) {
*UnresolvedSubst = TRUE;
}
}
} else {
//
// No terminating %. So we have something like %abc.
// Want to put %abc in the output. Put the % in here
// manually and then just let subsequent passes
// through the loop copy the rest of the chars.
//
if(Out < End) {
*Out++ = TEXT('%');
}
}
}
} else {
//
// Plain char.
//
if(Out < End) {
*Out++ = *In;
}
In++;
}
}
*Out = 0;
}
VOID
ParseValueString(
IN OUT PPARSE_CONTEXT Context,
IN OUT PCTSTR *Location,
IN BOOL ForKey,
OUT PBOOL UnresolvedSubst
)
/*++
Routine Description:
Extract a string starting at the current location in the input stream.
The string starts at the current location, and is terminated by
comma, newline, comment, or end-of-buffer. If the string is potentially
a line key, it may also terminate with an =.
The string may also be continued across multiple lines by using a
continuation character "\". The pieces are appended together to form
a single string that is returned to the caller. E.g.,
"this is a "\
"string used to" \
" test line continuation"
becomes:
"this is a string used to test line continuation"
Arguments:
Context - supplies parse context.
Location - on input, supplies a pointer to the current location in the
input stream. On outut, recevies a pointer to the location in the
input stream of the character that terminated the string (may be
a pointer to the end of the buffer, in which case the pointer must
not be dereferenced!)
ForKey - indicates whether = is a valid string terminator. If this value
is FALSE, = is just another character with no special semantics.
UnresolvedSubst - receives a BOOLEAN indicating whether or not this value
contained any unresolved string substitutions (and as such, should be
tracked for user-defined DIRID replacement, etc.).
Return Value:
None.
--*/
{
DWORD Count;
PTCHAR Out;
BOOL InQuotes;
BOOL Done;
PCTSTR location = *Location;
PCTSTR BufferEnd = Context->BufferEnd;
TCHAR TempString[MAX_STRING_LENGTH+1];
PTSTR LastBackslashChar, LastNonWhitespaceChar;
//
// Prepare to get the string
//
Count = 0;
Out = TempString;
Done = FALSE;
InQuotes = FALSE;
LastBackslashChar = NULL;
//
// Set the last non-whitespace pointer to be the character immediately preceding
// the output buffer. We always reference the value of this pointer + 1, so there's
// no danger of a bad memory reference.
//
LastNonWhitespaceChar = Out - 1;
//
// The first string can terminate with an =
// as well as the usual comma, newline, comment, or end-of-input.
//
while(!Done && (location < BufferEnd)) {
switch(*location) {
case TEXT('\r'):
//
// Ignore these.
//
location++;
break;
case TEXT('\\'):
//
// If we're not inside quotes, this could be a continuation character.
//
if(!InQuotes) {
LastBackslashChar = Out;
}
//
// We always store this character, we just may have to remove it later if
// it turns out to be the continuation character.
//
goto store;
case TEXT('\"'):
location++;
if(InQuotes) {
if((location < BufferEnd) && *location == TEXT('\"')) {
goto store;
} else {
InQuotes = FALSE;
}
} else {
InQuotes = TRUE;
}
break;
case TEXT(','):
if(InQuotes) {
goto store;
} else {
Done = TRUE;
break;
}
case TEXT(';'):
if(InQuotes) {
goto store;
}
//
// This character terminates the value, so let fall through to processing
// of end-of-line. (We treat ';' and '\n' differently than ',' because the
// former chars can possibly require a line continuation.)
//
case TEXT('\n'):
//
// OK, we've hit the end of the data on the line. If we found a backslash
// character, and its value is greater than that of the last non-whitespace
// character we encountered, then that means that we need to continue this
// value on the next line.
//
if(LastBackslashChar && (LastBackslashChar > LastNonWhitespaceChar)) {
//
// Trim any trailing whitespace from our current string (this includes
// getting rid of the backslash character itself).
//
Out = LastNonWhitespaceChar + 1;
//
// Skip to the beginning of the next line.
//
SkipLine(Context, &location);
//
// Skip any preceding whitespace on this new line.
//
SkipWhitespace(&location, BufferEnd);
//
// Clear the last-backslash pointer--we're on a new line now.
//
LastBackslashChar = NULL;
break;
}
Done = TRUE;
break;
case TEXT('='):
if(InQuotes) {
goto store;
}
if(ForKey) {
//
// We've got a key.
//
Done = TRUE;
break;
}
//
// Else just fall through for default handling.
//
default:
store:
//
// Strings longer then the maximum length are silently truncated.
// NULL characters are converted to spaces.
//
if(Count < CSTRLEN(TempString)) {
*Out = *location ? *location : TEXT(' ');
if(InQuotes || ((*Out != TEXT('\\')) && !IsWhitespace(Out))) {
//
// Update our pointer that keeps track of the last non-whitespace
// character we've encountered.
//
LastNonWhitespaceChar = Out;
}
Out++;
Count++;
}
location++;
break;
}
}
//
// Terminate the string in the buffer after the last non-whitespace character encountered.
//
*(LastNonWhitespaceChar + 1) = TEXT('\0');
//
// Store the new current buffer location in the caller's variable.
//
*Location = location;
//
// Substitute localized strings from the strings section.
// The strings section (if it exists) is always first
// (see PreprocessInf()).
//
// (tedm) Ignore whether or not the value was in quotes.
// Win95 infs do stuff like "%Description%\foo" and expect the
// substitution to work.
//
// (lonnym) We have to do this regardless of whether the INF has
// a [strings] section, since this routine tells us whether we have
// unresolved substitutions (e.g., for later replacement by user-defined
// DIRIDs).
//
if((Context->Inf->SectionCount > 1) || !(Context->Inf->HasStrings)) {
ProcessForSubstitutions(Context, TempString, UnresolvedSubst);
} else {
//
// Don't process values in the [strings] section for substitution!
//
lstrcpy(Context->TemporaryString, TempString);
*UnresolvedSubst = FALSE;
}
}
DWORD
ParseValuesLine(
IN OUT PPARSE_CONTEXT Context,
IN OUT PCTSTR *Location
)
/*++
Routine Description:
Parse a line of input that is not a section name and not a line
with only a comment on it.
Such lines are in the format
[<key> = ] <value>,<value>,<value>,...
The key is optional. Unquoted whitespace between non-whitespace characters
within a value is significant and considered part of the value.
Thus
a, b cd ef ,ghi
is the 3 values "a" "b cd ef" and "ghi"
Unquoted commas separate values. Two double quotes in a row within a quoted
string result in a single double quote character in the resulting string.
A logical line may be extended across several physical lines by use of the line
continuation character "\". E.g.,
a = b, c, \
d, e
becomes "a = b, c, d, e"
If it is desired to have a string that ends with a backslash at the end of a line,
the string must be enclosed in quotes. E.g.,
a = "C:\"
Arguments:
Context - supplies the parse context
Location - on input, supplies the current location in the input stream.
This must point to the left bracket.
On output, receives the location in the input stream of the first
character on the next line. This may be the end of input marker.
Return Value:
Result indicating outcome.
--*/
{
BOOL HaveKey = FALSE, RepeatSingleVal = FALSE;
BOOL Done;
DWORD Size;
PINF_LINE Line;
PVOID p;
LONG StringId;
PCTSTR BufferEnd = Context->BufferEnd;
PWORD pValueCount;
BOOL UnresolvedSubst;
BOOL CaseSensitive;
//
// Parse out the first string.
// The first string can terminate with an = or whitespace
// as well as the usual comma, newline, comment, or end-of-buffer
// (or line continuation character "\").
//
ParseValueString(Context, Location, TRUE, &UnresolvedSubst);
//
// If it terminated with an = then it's a key.
//
if(*Location < BufferEnd) {
HaveKey = (**Location == TEXT('='));
}
//
// Set up the current line
//
MYASSERT(Context->Inf->SectionCount);
Context->Inf->SectionBlock[Context->Inf->SectionCount-1].LineCount++;
if(Context->LineBlockUseCount == Context->LineBlockSize) {
Size = (Context->LineBlockSize + LINE_BLOCK_GROWTH) * sizeof(INF_LINE);
p = MyRealloc(Context->Inf->LineBlock,Size);
if(p) {
Context->Inf->LineBlock = p;
Context->LineBlockSize += LINE_BLOCK_GROWTH;
} else {
return(ERROR_NOT_ENOUGH_MEMORY);
}
}
Context->Inf->LineBlock[Context->LineBlockUseCount].Values = Context->ValueBlockUseCount;
*(pValueCount = &(Context->Inf->LineBlock[Context->LineBlockUseCount].ValueCount)) = 0;
Context->Inf->LineBlock[Context->LineBlockUseCount].Flags = HaveKey
? (INF_LINE_HASKEY | INF_LINE_SEARCHABLE)
: 0;
for(Done=FALSE; !Done; ) {
//
// Save away the value in the value block. If it's a key, then
// store it twice--once case-insensitively for lookup, and a second
// time case-sensitively for display. Store everything else
// case-sensitively.
//
// We also want to treat a single value with no key as if it were a key (i.e., store
// it twice). This is for Win95 compatibility.
//
do {
do {
//
// To keep from having to allocate a buffer for the case-insensitive key addition (which
// must be value 0), we do the case-sensitive addition first, then insert the case-
// insensitive version in front of it on the second pass of this inner loop.
//
CaseSensitive = ((*pValueCount != 1) || !HaveKey);
StringId = pStringTableAddString(Context->Inf->StringTable,
Context->TemporaryString,
STRTAB_BUFFER_WRITEABLE | (CaseSensitive ? STRTAB_CASE_SENSITIVE
: STRTAB_CASE_INSENSITIVE)
);
if(StringId == -1) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
if(Context->ValueBlockUseCount == Context->ValueBlockSize) {
Size = (Context->ValueBlockSize + VALUE_BLOCK_GROWTH) * sizeof(LONG);
p = MyRealloc(Context->Inf->ValueBlock,Size);
if(p) {
Context->Inf->ValueBlock = p;
Context->ValueBlockSize += VALUE_BLOCK_GROWTH;
} else {
return(ERROR_NOT_ENOUGH_MEMORY);
}
}
if((*pValueCount == 1) && HaveKey) {
//
// Shift over the case-sensitive version, and insert the case-insensitive one.
//
Context->Inf->ValueBlock[Context->ValueBlockUseCount] =
Context->Inf->ValueBlock[Context->ValueBlockUseCount - 1];
Context->Inf->ValueBlock[Context->ValueBlockUseCount - 1] = StringId;
if(UnresolvedSubst &&
!AddUnresolvedSubstToList(Context->Inf,
Context->ValueBlockUseCount - 1,
CaseSensitive)) {
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Reset the 'RepeatSingleVal' flag, in case we were faking the key behavior.
//
RepeatSingleVal = FALSE;
} else {
Context->Inf->ValueBlock[Context->ValueBlockUseCount] = StringId;
if(UnresolvedSubst &&
!AddUnresolvedSubstToList(Context->Inf,
Context->ValueBlockUseCount,
CaseSensitive)) {
return ERROR_NOT_ENOUGH_MEMORY;
}
}
Context->ValueBlockUseCount++;
(*pValueCount)++;
} while(HaveKey && (*pValueCount < 2));
//
// Check to see if this was the last value on the line.
//
if((*Location == BufferEnd) ||
(**Location == TEXT('\n')) ||
(**Location == TEXT(';'))) {
Done = TRUE;
//
// If this was the _only_ value on the line (i.e., no key), then treat this value
// as a key, and add it in again, case-insensitively.
//
if(*pValueCount == 1) {
MYASSERT(!HaveKey);
HaveKey = TRUE;
Context->Inf->LineBlock[Context->LineBlockUseCount].Flags = INF_LINE_SEARCHABLE;
RepeatSingleVal = TRUE;
}
}
} while (RepeatSingleVal);
if(!Done) {
//
// Skip terminator and whitespace.
//
(*Location)++;
SkipWhitespace(Location, BufferEnd);
//
// Get the next string.
//
ParseValueString(Context, Location, FALSE, &UnresolvedSubst);
}
}
Context->LineBlockUseCount++;
//
// Skip to next line
//
SkipLine(Context,Location);
return(NO_ERROR);
}
DWORD
ParseSectionLine(
IN OUT PPARSE_CONTEXT Context,
IN OUT PCTSTR *Location
)
/*++
Routine Description:
Parse a line of input that is known to be a section name line.
Such lines are in the format
'[' <arbitrary chars> ']'
All charcters between the brackets are considered part of the section
name, with no special casing of quotes, whitespace, etc. The remainder
of the line is ignored.
Arguments:
Context - supplies the parse context
Location - on input, supplies the current location in the input stream.
This must point to the left bracket.
On output, receives the location in the input stream of the first
character on the next line. This may be the end of input marker.
Return Value:
Result indicating outcome.
--*/
{
DWORD Count;
PTCHAR Out;
BOOL Done;
DWORD Result;
PVOID p;
DWORD Size;
DWORD Index;
LONG SectionNameId;
PCTSTR BufferEnd = Context->BufferEnd;
//
// Skip the left bracket.
//
MYASSERT(**Location == TEXT('['));
(*Location)++;
//
// Prepare for section name
//
Out = Context->TemporaryString;
Count = 0;
//
// This is implemeted according to the win95 code in setup\setupx\inf2.c.
// All characters between the 2 brackets are considered part of the
// section name with no further processing (like for double quotes, etc).
//
// Win95 also seems to allow [] as a section name.
//
for(Done=FALSE,Result=NO_ERROR; !Done; (*Location)++) {
if((*Location == BufferEnd) || (**Location == TEXT('\n'))) {
//
// Syntax error
//
Result = ERROR_BAD_SECTION_NAME_LINE;
Done = TRUE;
} else {
switch(**Location) {
case TEXT(']'):
Done = TRUE;
*Out = 0;
break;
default:
if(Count < MAX_SECT_NAME_LEN) {
//
// Convert NULL characters to spaces.
//
*Out++ = **Location ? **Location : TEXT(' ');
Count++;
} else {
Result = ERROR_SECTION_NAME_TOO_LONG;
Done = TRUE;
}
break;
}
}
}
Index = Context->Inf->SectionCount;
if(Result == NO_ERROR) {
//
// Ignore the rest of the line
//
SkipLine(Context,Location);
//
// See if we have enough room in the section block
// for this section. If not, grow the block.
//
if(Index == Context->SectionBlockSize) {
//
// Calculate the new section block size.
//
Size = (Index + SECTION_BLOCK_GROWTH) * sizeof(INF_SECTION);
if(p = MyRealloc(Context->Inf->SectionBlock,Size)) {
Context->SectionBlockSize += SECTION_BLOCK_GROWTH;
Context->Inf->SectionBlock = p;
} else {
Result = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
if(Result == NO_ERROR) {
Context->Inf->SectionBlock[Index].LineCount = 0;
Context->Inf->SectionBlock[Index].Lines = Context->LineBlockUseCount;
SectionNameId = pStringTableAddString(Context->Inf->StringTable,
Context->TemporaryString,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(SectionNameId == -1) {
Result = ERROR_NOT_ENOUGH_MEMORY;
} else {
Context->Inf->SectionBlock[Index].SectionName = SectionNameId;
Context->Inf->SectionCount++;
Context->GotOneSection = TRUE;
}
}
return(Result);
}
DWORD
ParseGenericLine(
IN OUT PPARSE_CONTEXT Context,
IN OUT PCTSTR *Location,
OUT PBOOL Done
)
/*++
Routine Description:
Parse a single line of input. The line may be a comment line, a section name,
or a values line.
Handling is passed off to line-specific parsing routines depending on the
line type.
Arguments:
Context - supplies the parse context
Location - on input, supplies the current location in the input stream.
On output, receives the location in the input stream of the first
character on the next line.
Done - receives boolean value indicating whether we are done
parsing the buffer. If this is TRUE on output the caller can stop
calling this routine.
Return Value:
Result indicating outcome.
--*/
{
DWORD ParseResult;
*Done = FALSE;
//
// Skip over leading whitespace on the line.
//
SkipWhitespace(Location, Context->BufferEnd);
//
// Further processing depends on the first important character on the line.
//
if(*Location == Context->BufferEnd) {
//
// End of input, empty line. Terminate current section.
//
*Done = TRUE;
ParseResult = MergeDuplicateSection(Context) ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY;
} else {
switch(**Location) {
case TEXT('\n'):
//
// Empty line.
//
SkipLine(Context,Location);
ParseResult = NO_ERROR;
break;
case TEXT('['):
//
// Potentially got a new section.
// First terminate the current section.
//
if(MergeDuplicateSection(Context)) {
ParseResult = ParseSectionLine(Context,Location);
} else {
ParseResult = ERROR_NOT_ENOUGH_MEMORY;
}
break;
case TEXT(';'):
//
// Comment line; ignore it.
//
SkipLine(Context,Location);
ParseResult = NO_ERROR;
break;
default:
//
// Ordinary values line. Disallow unless we are within a section.
//
ParseResult = Context->GotOneSection
? ParseValuesLine(Context,Location)
: ERROR_EXPECTED_SECTION_NAME;
break;
}
}
return(ParseResult);
}
PLOADED_INF
AllocateLoadedInfDescriptor(
IN DWORD SectionBlockSize,
IN DWORD LineBlockSize,
IN DWORD ValueBlockSize
)
{
PLOADED_INF p;
if(p = MyMalloc(sizeof(LOADED_INF))) {
ZeroMemory(p,sizeof(LOADED_INF));
if(p->SectionBlock = MyMalloc(SectionBlockSize*sizeof(INF_SECTION))) {
if(p->LineBlock = MyMalloc(LineBlockSize*sizeof(INF_LINE))) {
if(p->ValueBlock = MyMalloc(ValueBlockSize*sizeof(LONG))) {
if(p->StringTable = pStringTableInitialize()) {
if(InitializeSynchronizedAccess(&p->Lock)) {
p->Signature = LOADED_INF_SIG;
p->FileHandle = p->MappingHandle = INVALID_HANDLE_VALUE;
return(p);
}
pStringTableDestroy(p->StringTable);
}
MyFree(p->ValueBlock);
}
MyFree(p->LineBlock);
}
MyFree(p->SectionBlock);
}
MyFree(p);
}
return(NULL);
}
PLOADED_INF
DuplicateLoadedInfDescriptor(
IN PLOADED_INF Inf
)
/*++
Routine Description:
This routine duplicates an existing INF descriptor. The duplicate returned
is a totally independent copy, except that it has the lock handles (MYLOCK array)
and Prev and Next pointers of the original. This is useful for transferring a
memory-mapped PNF into read-write memory if modification is required.
THIS ROUTINE DOESN'T DO LOCKING OF ANY FORM ON THE INF--THE CALLER MUST HANDLE IT.
Arguments:
Inf - supplies the address of the INF descriptor to be duplicated. This pointer
refers to a single LOADED_INF structure, so any additional INFs linked up via
the 'Next' pointer are ignored.
Return Value:
If successful, the return value is the address of the newly-created duplicate.
If out-of-memory or inpage error, the return value is NULL.
--*/
{
PLOADED_INF NewInf;
BOOL Success;
if(NewInf = MyMalloc(sizeof(LOADED_INF))) {
CopyMemory(NewInf, Inf, sizeof(LOADED_INF));
NewInf->Signature = 0;
NewInf->SectionBlock = NULL;
NewInf->LineBlock = NULL;
NewInf->ValueBlock = NULL;
NewInf->StringTable = NULL;
NewInf->VersionBlock.DataBlock = NULL;
NewInf->UserDirIdList.UserDirIds = NULL;
NewInf->SubstValueList = NULL;
} else {
return NULL;
}
Success = FALSE;
try {
NewInf->SectionBlock = MyMalloc(Inf->SectionBlockSizeBytes);
if(NewInf->SectionBlock) {
CopyMemory(NewInf->SectionBlock, Inf->SectionBlock, Inf->SectionBlockSizeBytes);
NewInf->LineBlock = MyMalloc(Inf->LineBlockSizeBytes);
if(NewInf->LineBlock) {
CopyMemory(NewInf->LineBlock, Inf->LineBlock, Inf->LineBlockSizeBytes);
NewInf->ValueBlock = MyMalloc(Inf->ValueBlockSizeBytes);
if(NewInf->ValueBlock) {
CopyMemory(NewInf->ValueBlock, Inf->ValueBlock, Inf->ValueBlockSizeBytes);
NewInf->StringTable = pStringTableDuplicate(Inf->StringTable);
if(NewInf->StringTable) {
NewInf->VersionBlock.DataBlock = MyMalloc(Inf->VersionBlock.DataSize);
if(NewInf->VersionBlock.DataBlock) {
CopyMemory((PVOID)(NewInf->VersionBlock.DataBlock),
Inf->VersionBlock.DataBlock,
Inf->VersionBlock.DataSize
);
if(Inf->SubstValueCount) {
NewInf->SubstValueList =
MyMalloc(Inf->SubstValueCount * sizeof(STRINGSUBST_NODE));
if(!(NewInf->SubstValueList)) {
goto clean0;
}
CopyMemory((PVOID)NewInf->SubstValueList,
Inf->SubstValueList,
Inf->SubstValueCount * sizeof(STRINGSUBST_NODE)
);
}
if(Inf->UserDirIdList.UserDirIdCount) {
NewInf->UserDirIdList.UserDirIds =
MyMalloc(Inf->UserDirIdList.UserDirIdCount * sizeof(USERDIRID));
if(!(NewInf->UserDirIdList.UserDirIds)) {
goto clean0;
}
CopyMemory((PVOID)NewInf->UserDirIdList.UserDirIds,
Inf->UserDirIdList.UserDirIds,
Inf->UserDirIdList.UserDirIdCount * sizeof(USERDIRID)
);
}
//
// Reset the PNF fields because this backed-up INF is completely
// in-memory.
//
NewInf->FileHandle = NewInf->MappingHandle = INVALID_HANDLE_VALUE;
NewInf->ViewAddress = NULL;
NewInf->Signature = LOADED_INF_SIG;
Success = TRUE;
}
}
}
}
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Access the following variables here in the except clause, so that the compiler will respect
// our statement ordering w.r.t. these variables.
//
Success = FALSE;
NewInf->SubstValueList = NewInf->SubstValueList;
NewInf->UserDirIdList.UserDirIds = NewInf->UserDirIdList.UserDirIds;
NewInf->VersionBlock.DataBlock = NewInf->VersionBlock.DataBlock;
NewInf->StringTable = NewInf->StringTable;
NewInf->ValueBlock = NewInf->ValueBlock;
NewInf->LineBlock = NewInf->LineBlock;
NewInf->SectionBlock = NewInf->SectionBlock;
}
if(!Success) {
//
// Either we ran out of memory, or we got an inpage error trying to copy data
// from a memory-mapped PNF image. Free any memory we allocated above.
//
if(NewInf->SubstValueList) {
MyFree(NewInf->SubstValueList);
}
if(NewInf->UserDirIdList.UserDirIds) {
MyFree(NewInf->UserDirIdList.UserDirIds);
}
if(NewInf->VersionBlock.DataBlock) {
MyFree(NewInf->VersionBlock.DataBlock);
}
if(NewInf->StringTable) {
pStringTableDestroy(NewInf->StringTable);
}
if(NewInf->ValueBlock) {
MyFree(NewInf->ValueBlock);
}
if(NewInf->LineBlock) {
MyFree(NewInf->LineBlock);
}
if(NewInf->SectionBlock) {
MyFree(NewInf->SectionBlock);
}
MyFree(NewInf);
NewInf = NULL;
}
return NewInf;
}
VOID
ReplaceLoadedInfDescriptor(
IN PLOADED_INF InfToReplace,
IN PLOADED_INF NewInf
)
/*++
Routine Description:
Replace the specified INF with a new INF descriptor.
Note that this routine also frees the NewInf descriptor, when done.
Arguments:
InfToReplace - supplies a pointer to the inf descriptor to be replaced.
NewInf - supplies a pointer to the new INF descriptor that is to replace
the existing one.
Return Value:
None.
--*/
{
FreeInfOrPnfStructures(InfToReplace);
//
// Copy backup to inf
//
CopyMemory(InfToReplace, NewInf, sizeof(LOADED_INF));
//
// Just free the NewInf descriptor itself.
//
MyFree(NewInf);
}
VOID
FreeInfOrPnfStructures(
IN PLOADED_INF Inf
)
/*++
Routine Description:
If the specified INF was loaded from a textfile (non-PNF), then this routine
frees the memory associated with the various blocks it contains. If, instead,
the Inf is a PNF, then the PNF file is unmapped from memory and the handle is
closed.
THIS ROUTINE DOES NOT FREE THE LOADED_INF STRUCTURE ITSELF!
Arguments:
Inf - supplies a pointer to the inf descriptor for the loaded inf file.
Return Value:
None.
--*/
{
PINF_VERSION_NODE p, n;
//
// If this INF has a vald FileHandle, then we must unmap and close its PNF,
// otherwise, we simply need to free the associated memory blocks.
//
if(Inf->FileHandle != INVALID_HANDLE_VALUE) {
UnmapAndCloseFile(Inf->FileHandle, Inf->MappingHandle, Inf->ViewAddress);
MyFree(Inf->StringTable);
} else {
MyFree(Inf->ValueBlock);
MyFree(Inf->LineBlock);
MyFree(Inf->SectionBlock);
pStringTableDestroy(Inf->StringTable);
if(Inf->VersionBlock.DataBlock) {
MyFree(Inf->VersionBlock.DataBlock);
}
if(Inf->SubstValueList) {
MyFree(Inf->SubstValueList);
}
if(Inf->OsLoaderPath) {
MyFree(Inf->OsLoaderPath);
}
}
//
// For both INFs and PNFs, we must free the user-defined DIRID list (if there is one).
//
if(Inf->UserDirIdList.UserDirIds) {
MyFree(Inf->UserDirIdList.UserDirIds);
}
//
// Finally, mark the INF as no longer valid.
//
Inf->Signature = 0;
}
DWORD
ParseNewInf(
IN PCTSTR FileImage,
IN DWORD FileImageSize,
IN PCTSTR InfSourcePath, OPTIONAL
IN PCTSTR OsLoaderPath, OPTIONAL
OUT PLOADED_INF *Inf,
OUT UINT *ErrorLineNumber,
IN PSTRINGSEC_PARAMS StringsSectionParams
)
/*++
Routine Description:
Parse an inf file from an in-memory image.
Arguments:
FileImage - supplies a pointer to the unicode in-memory image
of the file.
FileImageSize - supplies the size of the in memory image.
InfSourcePath - optionally, supplies the directory path from which
the Inf is being loaded.
OsLoaderPath - optionally, supplies the full path to the OsLoader
(e.g., "C:\os\winnt40"). If it is discovered that this INF
references system partition DIRIDs, then a copy of this string
will be stored in the LOADED_INF structure. If this parameter
is not specified, then it will be retrieved from the registry,
if needed.
Inf - receives a pointer to the descriptor for the inf we loaded.
ErrorLineNumber - receives the line number of a syntax error,
if parsing was not successful for other than an out of memory
condition.
StringsSectionParams - Supplies information about the location of a
[strings] section (if there is one) in this INF.
Return Value:
Result indicating outcome. If the result is not ERROR_ERROR,
ErrorLineNumber is filled in.
--*/
{
PARSE_CONTEXT ParseContext;
PCTSTR Location;
DWORD Result, OsLoaderPathLength;
PVOID p;
BOOL Done;
PINF_SECTION DestDirsSection;
PINF_LINE DestDirsLine;
PCTSTR DirId;
PTCHAR End;
PCTSTR FileImageEnd;
UINT NumPieces, i, DirIdInt;
PCTSTR PieceList[3][2]; // 3 pieces, each with a start & end address
UINT StartLineNumber[3]; // keep track of the starting line number for
// each piece.
ZeroMemory(&ParseContext,sizeof(PARSE_CONTEXT));
*ErrorLineNumber = 0;
ParseContext.Inf = AllocateLoadedInfDescriptor(
INITIAL_SECTION_BLOCK_SIZE,
INITIAL_LINE_BLOCK_SIZE,
INITIAL_VALUE_BLOCK_SIZE
);
if(ParseContext.Inf) {
ParseContext.SectionBlockSize = INITIAL_SECTION_BLOCK_SIZE;
ParseContext.LineBlockSize = INITIAL_LINE_BLOCK_SIZE;
ParseContext.ValueBlockSize = INITIAL_VALUE_BLOCK_SIZE;
ParseContext.Inf->HasStrings = (StringsSectionParams->Start != NULL);
ParseContext.InfSourcePath = InfSourcePath;
if(OsLoaderPath) {
if(!(ParseContext.OsLoaderPath = DuplicateString(OsLoaderPath))) {
FreeLoadedInfDescriptor(ParseContext.Inf);
ParseContext.Inf = NULL;
}
}
}
if(ParseContext.Inf) {
ParseContext.Inf->Style = INF_STYLE_WIN4;
//
// We want to process the [strings] section first, if present,
// so we split the file up into (up to) 3 pieces--string section,
// what comes before it, and what comes after it.
//
FileImageEnd = FileImage + FileImageSize;
if(StringsSectionParams->Start) {
//
// Figure out whether we have 1, 2, or 3 pieces.
//
PieceList[0][0] = StringsSectionParams->Start;
PieceList[0][1] = StringsSectionParams->End;
StartLineNumber[0] = StringsSectionParams->StartLineNumber;
NumPieces = 1;
if(StringsSectionParams->Start > FileImage) {
PieceList[1][0] = FileImage;
PieceList[1][1] = StringsSectionParams->Start;
StartLineNumber[1] = 1;
NumPieces++;
}
if(StringsSectionParams->End < FileImageEnd) {
PieceList[NumPieces][0] = StringsSectionParams->End;
PieceList[NumPieces][1] = FileImageEnd;
StartLineNumber[NumPieces] = StringsSectionParams->EndLineNumber;
NumPieces++;
}
} else {
//
// No [strings] section, just one big piece.
//
PieceList[0][0] = FileImage;
PieceList[0][1] = FileImageEnd;
StartLineNumber[0] = 1;
NumPieces = 1;
}
//
// Surround the parsing loop with try/except in case we get an inpage error.
//
Result = NO_ERROR;
try {
for(i = 0; ((Result == NO_ERROR) && (i < NumPieces)); i++) {
//
// Parse every line in this piece.
//
Location = PieceList[i][0];
ParseContext.BufferEnd = PieceList[i][1];
ParseContext.CurrentLineNumber = StartLineNumber[i];
do {
Result = ParseGenericLine(&ParseContext,&Location,&Done);
if(Result != NO_ERROR) {
break;
}
} while(!Done);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Result = ERROR_READ_FAULT;
}
if(Result != NO_ERROR) {
*ErrorLineNumber = ParseContext.CurrentLineNumber;
FreeLoadedInfDescriptor(ParseContext.Inf);
return(Result);
}
//
// We've successfully loaded the file. Trim down the section,
// line, and value blocks. Since these guys are shrinking or
// staying the same size the reallocs really ought not to fail.
// If a realloc fails we'll just continue to use the original block.
//
ParseContext.Inf->SectionBlockSizeBytes = ParseContext.Inf->SectionCount * sizeof(INF_SECTION);
p = MyRealloc(
ParseContext.Inf->SectionBlock,
ParseContext.Inf->SectionBlockSizeBytes
);
if(p) {
ParseContext.Inf->SectionBlock = p;
}
ParseContext.Inf->LineBlockSizeBytes = ParseContext.LineBlockUseCount * sizeof(INF_LINE);
p = MyRealloc(
ParseContext.Inf->LineBlock,
ParseContext.LineBlockUseCount * sizeof(INF_LINE)
);
if(p) {
ParseContext.Inf->LineBlock = p;
}
ParseContext.Inf->ValueBlockSizeBytes = ParseContext.ValueBlockUseCount * sizeof(LONG);
p = MyRealloc(
ParseContext.Inf->ValueBlock,
ParseContext.ValueBlockUseCount * sizeof(LONG)
);
if(p) {
ParseContext.Inf->ValueBlock = p;
}
pStringTableTrim(ParseContext.Inf->StringTable);
//
// Even if we didn't find any string substitutions referencing system partition DIRIDs,
// we still might have a reference in a [DestinationDirs] section--check for that now.
//
if(!ParseContext.Inf->OsLoaderPath &&
(DestDirsSection = InfLocateSection(ParseContext.Inf, pszDestinationDirs, NULL))) {
for(i = 0;
InfLocateLine(ParseContext.Inf, DestDirsSection, NULL, &i, &DestDirsLine);
i++) {
if(DirId = InfGetField(ParseContext.Inf, DestDirsLine, 1, NULL)) {
DirIdInt = _tcstoul(DirId, &End, 10);
if((DirIdInt == DIRID_BOOT) || (DirIdInt == DIRID_LOADER)) {
//
// We've found a reference to a system partition DIRID. Store a copy
// of the system partition path we're using into the INF, and abort the
// search.
//
if(!ParseContext.OsLoaderPath) {
//
// We haven't yet retrieved the OsLoaderPath--do so now.
// (Re-use the parse context's TemporaryString buffer to get this.)
//
pSetupGetOsLoaderDriveAndPath(FALSE,
ParseContext.TemporaryString,
SIZECHARS(ParseContext.TemporaryString),
&OsLoaderPathLength
);
OsLoaderPathLength *= sizeof(TCHAR); // want # bytes--not chars
if(!(ParseContext.OsLoaderPath = MyMalloc(OsLoaderPathLength))) {
FreeLoadedInfDescriptor(ParseContext.Inf);
return ERROR_NOT_ENOUGH_MEMORY;
}
CopyMemory((PVOID)ParseContext.OsLoaderPath,
ParseContext.TemporaryString,
OsLoaderPathLength
);
}
ParseContext.Inf->OsLoaderPath = ParseContext.OsLoaderPath;
break;
}
}
}
}
//
// If there is no OsLoaderPath stored in the INF, then that means that it contains no
// references to system partition DIRIDs. We can free the OsLoaderPath character buffer
// contained in the parse context structure.
//
if(!ParseContext.Inf->OsLoaderPath && ParseContext.OsLoaderPath) {
MyFree(ParseContext.OsLoaderPath);
}
*Inf = ParseContext.Inf;
return(NO_ERROR);
}
return(ERROR_NOT_ENOUGH_MEMORY);
}
DWORD
PreprocessInf(
IN PCTSTR FileImage,
IN OUT PDWORD FileImageSize,
IN BOOL MatchClassGuid,
IN PCTSTR ClassGuidString, OPTIONAL
OUT PBOOL Win95Inf,
OUT PSTRINGSEC_PARAMS StringsSectionParams OPTIONAL
)
{
PCTSTR FileImageEnd;
PCTSTR VerAndStringsCheckUB, SigAndClassGuidCheckUB;
PCTSTR p;
PCTSTR StrSecStart, StrSecEnd;
UINT CurLineNumber, StrSecStartLine, StrSecEndLine;
BOOL InVersionSection, InStringsSection;
BOOL IsWin95Inf;
DWORD rc = NO_ERROR;
//
// We make some assumptions about the relative lengths of certain
// strings during the preprocessing phase for optimization reasons.
// The following asserts verify that our assumptions remain correct.
//
MYASSERT(CSTRLEN(pszVersion) == CSTRLEN(pszStrings));
MYASSERT(CSTRLEN(pszClassGuid) == CSTRLEN(pszSignature));
MYASSERT(CSTRLEN(pszChicagoSig) <= CSTRLEN(pszWindowsNTSig));
MYASSERT(CSTRLEN(pszWindowsNTSig) == CSTRLEN(pszWindows95Sig));
FileImageEnd = FileImage + *FileImageSize;
StrSecStart = StrSecEnd = NULL;
InVersionSection = InStringsSection = IsWin95Inf = FALSE;
CurLineNumber = 1;
//
// Pre-compute upper-bound for section name string comparison that we
// make multiple times, so that we don't have to compute it each
// time.
//
VerAndStringsCheckUB = FileImageEnd - CSTRLEN(pszVersion);
//
// Define a macro that lets us know we're at the end of the file
// if either:
// (a) we reach the end of the image, or
// (b) we hit a CTL-Z
//
#define AT_EOF ((p >= FileImageEnd) || (*p == (TCHAR)26))
//
// Guard the pre-processing pass through the file with a try/except, in
// case we get an inpage error.
//
try {
for(p=FileImage; !AT_EOF; ) {
//
// Skip whitespace and newlines.
//
while(TRUE) {
if(*p == TEXT('\n')) {
CurLineNumber++;
} else if(!IsWhitespace(p)) {
break;
}
p++;
if(AT_EOF) {
break;
}
}
if(AT_EOF) {
//
// We're through processing the buffer.
//
break;
}
//
// See if it's a section title.
//
if(*p == TEXT('[')) {
//
// If the section we were just in was a [Strings] section, then
// remember where the strings section ended.
//
if(InStringsSection) {
StrSecEnd = p;
StrSecEndLine = CurLineNumber;
InStringsSection = FALSE;
}
p++;
InVersionSection = FALSE;
//
// See if it's one of the ones we care about.
//
// (Be careful here--we check the closing bracket position
// _before_ the string compare as an optimization. It just
// so happens that both strings are the same length, so this
// acts as a quick filter to eliminate string compares.)
//
if((p < VerAndStringsCheckUB) &&
(*(p + CSTRLEN(pszVersion)) == TEXT(']'))) {
//
// Then we may have either a [Version] or a [Strings] section.
// Check for these in turn.
//
if(!_tcsnicmp(p, pszVersion, CSTRLEN(pszVersion))) {
InVersionSection = TRUE;
p += (CSTRLEN(pszVersion) + 1);
//
// Pre-compute an upper bound to speed up string comparisons
// when checking for signature and class GUID entries.
//
SigAndClassGuidCheckUB = FileImageEnd - CSTRLEN(pszSignature);
} else {
if(!StrSecStart && !_tcsnicmp(p, pszStrings, CSTRLEN(pszStrings))) {
InStringsSection = TRUE;
StrSecStart = p-1;
StrSecStartLine = CurLineNumber;
p += (CSTRLEN(pszStrings) + 1);
}
}
}
} else {
if(InVersionSection && (p < SigAndClassGuidCheckUB)) {
//
// See if this is the signature line indicating a Win95-style
// Device INF. (signature=$Chicago$ or "$Windows NT$")
//
if(!IsWin95Inf && !_tcsnicmp(p, pszSignature, CSTRLEN(pszSignature))) {
PCTSTR ChicagoCheckUB = FileImageEnd - CSTRLEN(pszChicagoSig);
//
// Skip over Signature, and look for "$Chicago$" or
// "$Windows NT$" anywhere on the rest of the line
//
p += CSTRLEN(pszSignature);
while((p <= ChicagoCheckUB) &&
(*p != (TCHAR)26) && (*p != TEXT('\n'))) {
if(*(p++) == TEXT('$')) {
//
// Check for signatures (check in order of
// increasing signature length, so that we can
// eliminate checks if we happen to be near the
// end of the file).
//
// Check for "$Chicago$"
//
if(!_tcsnicmp(p,
pszChicagoSig + 1,
CSTRLEN(pszChicagoSig) - 1)) {
IsWin95Inf = TRUE;
p += (CSTRLEN(pszChicagoSig) - 1);
} else if((p + (CSTRLEN(pszWindowsNTSig) - 1)) <= FileImageEnd) {
//
// Check for "Windows NT$" and "Windows 95$" (we already checked
// for the preceding '$').
//
if(!_tcsnicmp(p, pszWindowsNTSig + 1, CSTRLEN(pszWindowsNTSig) - 1) ||
!_tcsnicmp(p, pszWindows95Sig + 1, CSTRLEN(pszWindows95Sig) - 1)) {
IsWin95Inf = TRUE;
p += (CSTRLEN(pszWindowsNTSig) - 1);
}
}
break;
}
}
} else if(MatchClassGuid && !_tcsnicmp(p, pszClassGuid, CSTRLEN(pszClassGuid))) {
PCTSTR GuidStringCheckUB = FileImageEnd - (GUID_STRING_LEN - 1);
//
// We have found a ClassGUID line--see if it matches the
// class GUID specified by the caller.
//
p += CSTRLEN(pszClassGuid);
//
// If a class GUID string wasn't specified, then use GUID_NULL.
//
if(!ClassGuidString) {
ClassGuidString = pszGuidNull;
}
while((p <= GuidStringCheckUB) &&
(*p != (TCHAR)26) && (*p != TEXT('\n'))) {
if(*(p++) == TEXT('{')) {
if((*(p + (GUID_STRING_LEN - 3)) != TEXT('}')) ||
_tcsnicmp(p, ClassGuidString + 1, GUID_STRING_LEN - 3)) {
//
// The GUIDs don't match. If ClassGuid was NULL, then
// this means we should continue, because we were matching
// against GUID_NULL, which we want to disallow.
//
if(ClassGuidString == pszGuidNull) {
//
// We don't need to keep looking for ClassGUIDs.
//
MatchClassGuid = FALSE;
}
} else {
//
// The GUIDs match. If ClassGuid was not NULL, then this
// means that we should continue.
//
if(ClassGuidString != pszGuidNull) {
//
// We don't need to keep looking for ClassGUIDs.
//
MatchClassGuid = FALSE;
}
}
//
// Skip over the GUID string.
//
p += (GUID_STRING_LEN - 2);
break;
}
}
//
// If we get here, and MatchClassGuid hasn't been reset,
// then we know that this ClassGUID entry didn't match.
//
if(MatchClassGuid) {
rc = ERROR_CLASS_MISMATCH;
goto clean0;
}
}
}
}
//
// Skip to the newline or end of file.
//
while(!AT_EOF && (*p != TEXT('\n'))) {
p++;
}
}
clean0: ; // Nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_READ_FAULT;
}
if(rc == NO_ERROR) {
MYASSERT(p <= FileImageEnd);
if(p < FileImageEnd) {
//
// Then we hit a CTL-Z during processing, so update the
// FileImageSize output parameter with the new size.
//
*FileImageSize = p - FileImage;
}
if(StringsSectionParams) {
if(IsWin95Inf && StrSecStart) {
//
// If we found a [strings] section in a Win95-style INF,
// then store the beginning and ending positions, and the
// beginning line number, in the output parameter structure
//
StringsSectionParams->Start = StrSecStart;
StringsSectionParams->End = StrSecEnd ? StrSecEnd : p;
StringsSectionParams->StartLineNumber = StrSecStartLine;
StringsSectionParams->EndLineNumber = StrSecEnd ? StrSecEndLine
: CurLineNumber;
} else {
ZeroMemory(StringsSectionParams, sizeof(STRINGSEC_PARAMS));
}
}
*Win95Inf = IsWin95Inf;
}
return rc;
}
DWORD
DetermineInfStyle(
IN PCTSTR Filename,
IN LPWIN32_FIND_DATA FindData
)
/*++
Routine Description:
Open an inf file, determine its style, and close the file, without
keeping it around.
Arguments:
Filename - supplies the fully-qualified pathname of the inf file to be checked
Return Value:
INF_STYLE_NONE - style could not be determined
INF_STYLE_WIN4 - win95-style inf file
INF_STYLE_OLDNT - winnt3.5-style inf file
--*/
{
HANDLE TextFileHandle;
TEXTFILE_READ_BUFFER ReadBuffer;
DWORD Style;
BOOL Win95Inf;
PLOADED_INF Pnf;
//
// First, determine whether a precompiled form of this INF exists, and if so, then
// use it to determine the INF's style.
//
if(LoadPrecompiledInf(Filename,
&(FindData->ftLastWriteTime),
NULL,
LDINF_FLAG_IGNORE_SYSPART,
&Pnf)) {
//
// Now we can simply access the Style field of the INF.
//
try {
Style = (DWORD)Pnf->Style;
} except(EXCEPTION_EXECUTE_HANDLER) {
Style = INF_STYLE_NONE;
}
//
// Now close the PNF.
//
FreeInfFile(Pnf);
} else {
//
// No PNF--Open and preprocess the text version of the INF to find out its style.
//
if((TextFileHandle = CreateFile(Filename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL)) == INVALID_HANDLE_VALUE) {
return INF_STYLE_NONE;
} else {
//
// We're ready to make the determination--initially assume 'no-style'
//
Style = INF_STYLE_NONE;
}
if(ReadAsciiOrUnicodeTextFile(TextFileHandle, &ReadBuffer) == NO_ERROR) {
if(PreprocessInf(ReadBuffer.TextBuffer,
&(ReadBuffer.TextBufferSize),
FALSE,
NULL,
&Win95Inf,
NULL) == NO_ERROR) {
Style = Win95Inf ? INF_STYLE_WIN4 : INF_STYLE_OLDNT;
}
DestroyTextFileReadBuffer(&ReadBuffer);
}
//
// No need to close the textfile handle--it's taken care of in the above routines.
//
}
return Style;
}
DWORD
LoadInfFile(
IN PCTSTR Filename,
IN LPWIN32_FIND_DATA FileData,
IN DWORD Style,
IN DWORD Flags,
IN PCTSTR ClassGuidString, OPTIONAL
IN PCTSTR InfSourcePath, OPTIONAL
IN PLOADED_INF AppendInf, OPTIONAL
OUT PLOADED_INF *LoadedInf,
OUT UINT *ErrorLineNumber
)
/*++
Routine Description:
Top level routine to load an inf file. Both win95-style and winnt3.5-
style device infs are supported.
Arguments:
Filename - supplies the fully-qualified pathname of the inf file to be loaded
FileData - supplies data returned from FindFirstFile/FindNextFile for this INF.
Style - supplies a type of inf file to be loaded. May be a combination of
INF_STYLE_WIN4 - fail to load the given inf file if it is not a win95
inf file.
INF_STYLE_OLDNT - fail to load the given inf file if it is not an old
style inf file.
If a load fails because of the type, the return code is
ERROR_WRONG_INF_STYLE.
Flags - Specifies certain behaviors to use when loading the INF. May be a
combination of the following values:
LDINF_FLAG_MATCH_CLASS_GUID - Check the INF to make sure it matches the GUID
specified by the ClassGuid parameter (see discussion below).
LDINF_FLAG_ALWAYS_TRY_PNF - If specified, then a PNF file will be used, if
present, and will be generated, if not present.
LDINF_FLAG_IGNORE_SYSPART - If specified, then no validation will be done on
the stored OsLoaderPath present in the PNF. Since dynamically retrieving
the current path is time consuming, this flag should be specified as an
optimization if it is known that the relevant DIRIDs are not going to be
needed (e.g., driver searching).
ClassGuidString - Optionally, supplies the address of a class GUID string that
the INF should match in order to be opened. If the LDINF_FLAG_MATCH_CLASS_GUID
bit is set in the Flags parameter, this GUID is matched against the ClassGUID
entry in the [version] section of the INF. If the two GUIDs are different, the
load will fail with ERROR_CLASS_MISMATCH. If the INF has no ClassGUID entry,
then this check is not made, and the file is always opened. If ClassGUID matching
is requested, but ClassGuidString is NULL, then the INF load will succeed for all
INFs except those with a ClassGUID of GUID_NULL.
InfSourcePath - Optionally, supplies a directory path to be used as the INF's source
path, if a PNF is not used for this INF. If this parameter is NULL, and a PNF
is not used, then the default global SourcePath is used.
AppendInf - if supplied, specifies an already-loaded inf to which
the inf is to be load-appended. THIS INF MUST HAVE BEEN ALREADY LOCKED BY THE
CALLER!!!
LoadedInf - If AppendInf is not specified, receives a pointer to
the descriptor for the inf. If AppendInf is specified, receives AppendInf.
ErrorLineNumber - receives the line number of the error if there is
a syntax error in the file (see below)
Return Value:
Win32 error code (with inf extensions) for result.
If result is not NO_ERROR, ErrorLineNumber is filled in.
BUGBUG need to provide a function to retreive a descriptive string
associated with these errors!!
--*/
{
TEXTFILE_READ_BUFFER ReadBuffer;
DWORD rc;
PLOADED_INF Inf, InfListTail;
BOOL Win95Inf;
STRINGSEC_PARAMS StringsSectionParams;
HANDLE TextFileHandle;
PCTSTR OsLoaderPath = NULL;
*ErrorLineNumber = 0;
//
// If append-loading, then traverse the existing list of loaded INFs, to see if
// we've already loaded this one.
//
if(AppendInf) {
//
// Only allow appending with win95 infs
//
if(AppendInf->Style & INF_STYLE_OLDNT) {
return ERROR_WRONG_INF_STYLE;
}
for(Inf = AppendInf; Inf; Inf = Inf->Next) {
if(!lstrcmpi(Inf->VersionBlock.Filename, Filename)) {
//
// We've already loaded this INF--we can return success.
//
*LoadedInf = AppendInf;
return NO_ERROR;
}
//
// Check to see if the INF we're currently examining references the system
// partition/OsLoader path. If so, then remember this path so that we will
// use the same one later when append-loading our new INF.
//
if(Inf->OsLoaderPath) {
if(OsLoaderPath) {
//
// We'd better be using the same value for OsLoadPath for all our
// append-loaded INFs!
//
MYASSERT(!lstrcmpi(Inf->OsLoaderPath, OsLoaderPath));
} else {
OsLoaderPath = Inf->OsLoaderPath;
}
}
//
// Remember this node, in case it's the tail. We do this so we don't have
// to hunt for the tail again later.
//
InfListTail = Inf;
}
}
//
// Now determine whether a precompiled form of this INF exists, and if so, then
// use it instead.
//
if((Flags & LDINF_FLAG_ALWAYS_TRY_PNF) &&
LoadPrecompiledInf(Filename, &(FileData->ftLastWriteTime), OsLoaderPath, Flags, &Inf)) {
//
// Make sure that the PNF is of the specified style.
//
if(!(Style & (DWORD)Inf->Style)) {
FreeInfFile(Inf);
return ERROR_WRONG_INF_STYLE;
}
if(AppendInf) {
Inf->Prev = InfListTail;
InfListTail->Next = Inf;
}
rc = NO_ERROR;
goto clean0;
}
//
// We can't use a precompiled INF, so resort to reading in the textfile INF.
//
if((TextFileHandle = CreateFile(Filename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL)) == INVALID_HANDLE_VALUE) {
return GetLastError();
}
//
// Note: We don't have to worry about closing TextFileHandle from this point
// on, because the following routine will either close it for us, or copy it
// into the ReadBuffer structure, to be later closed via DestroyTextFileReadBuffer().
//
if((rc = ReadAsciiOrUnicodeTextFile(TextFileHandle, &ReadBuffer)) == NO_ERROR) {
//
// Make sure the style (and class) matched what the caller is asking
// for and go parse the inf file in a style-specific manner.
//
Inf = NULL;
if((rc = PreprocessInf(ReadBuffer.TextBuffer,
&(ReadBuffer.TextBufferSize),
(Flags & LDINF_FLAG_MATCH_CLASS_GUID),
ClassGuidString,
&Win95Inf,
&StringsSectionParams)) == NO_ERROR) {
rc = ERROR_WRONG_INF_STYLE;
if(Win95Inf) {
if(Style & INF_STYLE_WIN4) {
rc = ParseNewInf(ReadBuffer.TextBuffer,
ReadBuffer.TextBufferSize,
InfSourcePath,
OsLoaderPath,
&Inf,
ErrorLineNumber,
&StringsSectionParams
);
}
} else {
//
// Can't append old-style file.
//
if(!AppendInf && (Style & INF_STYLE_OLDNT)) {
rc = ParseOldInf(ReadBuffer.TextBuffer,
ReadBuffer.TextBufferSize,
&Inf,
ErrorLineNumber
);
}
}
}
//
// Free the in-memory image of the file.
//
DestroyTextFileReadBuffer(&ReadBuffer);
if(rc == NO_ERROR) {
//
// If we get here then we've parsed the file successfully.
// Set up version block for this file.
//
*ErrorLineNumber = 0;
rc = CreateInfVersionNode(Inf, Filename, &(FileData->ftLastWriteTime));
if((rc == NO_ERROR) && InfSourcePath) {
//
// If the caller specified a source path, then duplicate the string, and
// store a pointer to it in our INF structure, for later use by references
// to DIRID_SRCPATH.
//
if(!(Inf->InfSourcePath = DuplicateString(InfSourcePath))) {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
}
if(rc == NO_ERROR) {
//
// If we get here then we've parsed the file successfully and
// successfully created the version block. If we're allowed
// to write out a PNF file for this loaded INF, then do that
// now.
//
if(Flags & LDINF_FLAG_ALWAYS_TRY_PNF) {
SavePnf(Filename, Inf);
}
if(AppendInf) {
Inf->Prev = InfListTail;
InfListTail->Next = Inf;
}
} else {
FreeInfFile(Inf);
}
}
}
clean0:
if(AppendInf) {
//
// If the INF we're appending to has any user-defined DIRIDs, then apply those to the
// newly-appended INF now.
//
if((rc == NO_ERROR) && AppendInf->UserDirIdList.UserDirIdCount) {
if((rc = ApplyNewUserDirIdsToInfs(AppendInf, Inf)) != NO_ERROR) {
//
// So near, and yet, so far! Yank the new INF out of the linked list, and free it.
//
MYASSERT(Inf->Prev);
Inf->Prev->Next = Inf->Next;
FreeInfFile(Inf);
}
}
if(rc == NO_ERROR) {
*LoadedInf = AppendInf;
}
} else if(rc == NO_ERROR) {
*LoadedInf = Inf;
}
return rc;
}
VOID
FreeInfFile(
IN PLOADED_INF LoadedInf
)
/*++
Routine Description:
Unload an inf file, freeing all resources used by its internal
representation.
Arguments:
Inf - supplies a pointer to the inf descriptor for the loaded inf file.
Return Value:
None.
--*/
{
if(LockInf(LoadedInf)) {
DestroySynchronizedAccess(&LoadedInf->Lock);
FreeLoadedInfDescriptor(LoadedInf);
}
}
BOOL
AddDatumToVersionBlock(
IN OUT PINF_VERSION_NODE VersionNode,
IN PCTSTR DatumName,
IN PCTSTR DatumValue
)
/*++
Routine Description:
Append an inf version datum to the version node.
Arguments:
VersionNode - supplies pointer to the version node.
DatumName - supplies name of the datum.
DatumValue - supplies datum's value.
Return Value:
FALSE if OOM.
TRUE if datum added successfully. Various fields in the VersionNode
will have been updated.
--*/
{
UINT RequiredSpace;
UINT NameLength, ValueLength;
PTSTR NewDataBlock;
NameLength = lstrlen(DatumName) + 1;
ValueLength = lstrlen(DatumValue) + 1;
//
// The space needed to store the datum is the existing space plus
// the length of the 2 strings and their nul bytes.
//
RequiredSpace = VersionNode->DataSize + ((NameLength + ValueLength) * sizeof(TCHAR));
if(VersionNode->DataBlock) {
NewDataBlock = MyRealloc((PVOID)(VersionNode->DataBlock), RequiredSpace);
} else {
NewDataBlock = MyMalloc(RequiredSpace);
}
if(!NewDataBlock) {
return FALSE;
}
//
// Place the datum name in the version block.
//
lstrcpy((PTSTR)((PUCHAR)NewDataBlock + VersionNode->DataSize), DatumName);
VersionNode->DataSize += NameLength * sizeof(TCHAR);
//
// Place the datum value in the version block.
//
lstrcpy((PTSTR)((PUCHAR)NewDataBlock + VersionNode->DataSize), DatumValue);
VersionNode->DataSize += ValueLength * sizeof(TCHAR);
VersionNode->DatumCount++;
VersionNode->DataBlock = NewDataBlock;
return TRUE;
}
DWORD
ProcessNewInfVersionBlock(
IN PLOADED_INF Inf
)
/*++
Routine Description:
Set up a version node for a new-style inf file. The version node is
simply a mirror of the [Version] section in the file.
Since this routine is only called at INF load time, no locking is done.
Also, since we are guaranteed that this will operate on a single INF
only, we don't have to worry about traversing a linked list of INFs.
Arguments:
Inf - supplies a pointer to the inf descriptor for the file.
Return Value:
Win32 error code (with inf extensions) indicating outcome.
--*/
{
PINF_SECTION Section;
PINF_LINE Line;
UINT u;
BOOL b;
//
// Locate the [Version] section.
//
if(Section = InfLocateSection(Inf, pszVersion, NULL)) {
//
// Iterate each line in the section. If the line has a key and at least one
// other value, then it counts as a version datum. Otherwise ignore it.
//
for(u = 0, Line = &Inf->LineBlock[Section->Lines];
u < Section->LineCount;
u++, Line++)
{
if(HASKEY(Line)) {
MYASSERT(Line->ValueCount > 2);
//
// Use the case-sensitive key name.
//
b = AddDatumToVersionBlock(
&(Inf->VersionBlock),
pStringTableStringFromId(Inf->StringTable, Inf->ValueBlock[Line->Values+1]),
pStringTableStringFromId(Inf->StringTable, Inf->ValueBlock[Line->Values+2])
);
if(!b) {
return ERROR_NOT_ENOUGH_MEMORY;
}
}
}
}
return NO_ERROR;
}
DWORD
CreateInfVersionNode(
IN PLOADED_INF Inf,
IN PCTSTR Filename,
IN PFILETIME LastWriteTime
)
/*++
Routine Description:
Set up a version node for an inf file, and link it into the list of INFs for
the specified LOADED_INF structure.
THIS ROUTINE ASSUMES THAT THE VERSION BLOCK STRUCTURE IN THE INF HAS BEEN
ZEROED OUT.
Arguments:
Inf - supplies pointer to descriptor for loaded inf file.
Filename - supplies (fully-qualified) filename used to load inf file.
LastWriteTime - supplies a pointer to a FILETIME structure specifying
the time that the INF was last written to.
Return Value:
Win32 error code (with inf extensions) indicating outcome.
--*/
{
MYASSERT(!(Inf->VersionBlock.DataBlock));
MYASSERT(!(Inf->VersionBlock.DataSize));
MYASSERT(!(Inf->VersionBlock.DatumCount));
//
// Fill in the filename and other fields in the version descriptor.
//
Inf->VersionBlock.LastWriteTime = *LastWriteTime;
Inf->VersionBlock.FilenameSize = (lstrlen(Filename) + 1) * sizeof(TCHAR);
CopyMemory(Inf->VersionBlock.Filename, Filename, Inf->VersionBlock.FilenameSize);
//
// Style-specific processing.
//
return((Inf->Style == INF_STYLE_WIN4) ? ProcessNewInfVersionBlock(Inf)
: ProcessOldInfVersionBlock(Inf));
}
/////////////////////////////////////////////
//
// Inf data access functions
//
/////////////////////////////////////////////
PINF_SECTION
InfLocateSection(
IN PLOADED_INF Inf,
IN PCTSTR SectionName,
OUT PUINT SectionNumber OPTIONAL
)
/*++
Routine Description:
Locate a section within an inf file. This routine DOES NOT traverse a
linked list of INFs, looking for the section in each.
THIS ROUTINE DOES NOT LOCK THE INF--THE CALLER MUST HANDLE IT!!!
Arguments:
Inf - supplies a pointer to the inf descriptor for the loaded inf file.
SectionName - Supplies the name of the section to be located.
SectionNumber - if specified, receives the ordinal number of
the section.
Return Value:
Pointer to the section descriptor, or NULL if the section
does not exist.
--*/
{
LONG StringId;
PINF_SECTION Section;
PINF_SECTION Start,End;
UINT u;
DWORD StringLength;
TCHAR TempString[MAX_SECT_NAME_LEN];
//
// Make a copy of the SectionName into a modifiable buffer to speed
// the lookup.
//
lstrcpyn(TempString, SectionName, SIZECHARS(TempString));
//
// Start from the beginning.
//
StringId = pStringTableLookUpString(Inf->StringTable,
TempString,
&StringLength,
NULL,
NULL,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(StringId == -1) {
return(NULL);
}
for(u=0,Section=Inf->SectionBlock; u<Inf->SectionCount; u++,Section++) {
if(Section->SectionName == StringId) {
if(SectionNumber) {
*SectionNumber = u;
}
return(Section);
}
}
return(NULL);
}
BOOL
InfLocateLine(
IN PLOADED_INF Inf,
IN PINF_SECTION Section,
IN PCTSTR Key, OPTIONAL
IN OUT PUINT LineNumber,
OUT PINF_LINE *Line
)
/*++
Routine Description:
Locate a line within a section. This routine DOES NOT traverse a
linked list of INFs, looking for the section in each.
THIS ROUTINE DOES NOT LOCK THE INF--THE CALLER MUST HANDLE IT!!!
Arguments:
Inf - supplies a pointer to the inf descriptor for the loaded inf file.
SectionName - Supplies a pointer to the section descriptor for the section
to be searched.
Key - if specified, supplies the key of the line to look for.
LineNumber - on input, supplies the line number of the line where the
search is to begin. On output, receives the line number of the
line where the match was found
Line - receives a pointer to the line descriptor for the line
where the match was found.
Return Value:
TRUE if line is found, FALSE otherwise.
--*/
{
PINF_LINE line;
UINT u;
LONG StringId;
DWORD StringLength;
TCHAR TempString[MAX_STRING_LENGTH];
if(Key) {
//
// Copy the key name into a modifiable buffer to speed up the string table API.
//
lstrcpyn(TempString, Key, SIZECHARS(TempString));
StringId = pStringTableLookUpString(Inf->StringTable,
TempString,
&StringLength,
NULL,
NULL,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(StringId == -1) {
return FALSE;
}
for(u = *LineNumber, line = &Inf->LineBlock[Section->Lines + (*LineNumber)];
u < Section->LineCount;
u++, line++)
{
if(ISSEARCHABLE(line) && (Inf->ValueBlock[line->Values] == StringId)) {
*Line = line;
*LineNumber = u;
return TRUE;
}
}
} else {
if(*LineNumber < Section->LineCount) {
*Line = &Inf->LineBlock[Section->Lines + (*LineNumber)];
return TRUE;
}
}
return FALSE;
}
PTSTR
InfGetField(
IN PLOADED_INF Inf,
IN PINF_LINE InfLine,
IN UINT ValueNumber,
OUT PLONG StringId OPTIONAL
)
/*++
Routine Description:
Retrieve the key or a value from a specified line in an inf file.
THIS ROUTINE DOES NOT DO LOCKING!!!
Arguments:
Inf - supplies a pointer to the inf descriptor for the loaded inf file.
InfLine - supplies a pointer to the line descriptor for the line
from which the value is to be fetched. THIS LINE MUST BE CONTAINED
WITHIN THE SPECIFIED INF!!
ValueNumber - supplies the index for the value to retreive. If a line has a key,
the key is value #0 and other values start at 1. If a line does not have a
key, values start at 1. For Win95 INF compatibility, if there's only a single
value on the line (i.e., no '=' to denote it as a key), we'll consider it to
be both a key and the first value (either 0 or 1 will work).
StringId - if specified, receives the string table id of the value.
Return Value:
Pointer to the value, or NULL if not found. The caller must not write into
or otherwise alter this string.
--*/
{
LONG stringId;
PTSTR ret = NULL;
//
// Adjust the value number.
//
if(HASKEY(InfLine)) {
//
// All field references are shifted up by one, to account for the two
// copies of the key.
//
ValueNumber++;
} else {
if(ISSEARCHABLE(InfLine)) {
//
// If the line is searchable, then it has two values that are the same.
// We want to return the second of the two, since it's the one that was
// stored case-sensitively.
//
if(ValueNumber > 1) {
return NULL;
} else {
ValueNumber = 1;
}
} else {
//
// This line is not searchable, so asking for value #0 is an error.
//
if(ValueNumber) {
ValueNumber--;
} else {
return NULL;
}
}
}
//
// Get the value.
//
if(ValueNumber < InfLine->ValueCount) {
stringId = Inf->ValueBlock[InfLine->Values+ValueNumber];
if(StringId) {
*StringId = stringId;
}
return pStringTableStringFromId(Inf->StringTable, stringId);
}
return NULL;
}
PTSTR
InfGetKeyOrValue(
IN PLOADED_INF Inf,
IN PCTSTR SectionName,
IN PCTSTR LineKey, OPTIONAL
IN UINT LineNumber, OPTIONAL
IN UINT ValueNumber,
OUT PLONG StringId OPTIONAL
)
/*++
Routine Description:
Retrieve the key or a value from a specified line in an inf file.
Arguments:
Inf - supplies a pointer to the inf descriptor for the loaded inf file.
SectionName - supplies the name of the section where the value is located.
LineKey - if specified, supplies the key name for the line where the
value is located. If not specified, LineNumber is used instead.
LineNumber - if LineKey is not specified, supplies the 0-based line number
within the section where the value is located.
ValueNumber - supplies the index for the value to retreive. If a line has a key,
the key is value #0 and other values start at 1. If a line does not have a
key, values start at 1.
StringId - if specified, receives the string table id of the value.
Return Value:
Pointer to the value, or NULL if not found. The caller must not write into
or otherwise alter this string.
--*/
{
INFCONTEXT InfContext;
PINF_LINE Line;
PTSTR String;
if(LineKey) {
if(!SetupFindFirstLine((HINF)Inf, SectionName, LineKey, &InfContext)) {
return NULL;
}
} else {
if(!SetupGetLineByIndex((HINF)Inf, SectionName, LineNumber, &InfContext)) {
return NULL;
}
}
Line = InfLineFromContext(&InfContext);
//
// The above routines do their own locking. The following routine, however, does
// not, so we must lock the INF before preceding.
//
if(LockInf(Inf)) {
String = InfGetField(Inf, Line, ValueNumber, StringId);
UnlockInf(Inf);
} else {
String = NULL;
}
return String;
}
BOOL
LoadPrecompiledInf(
IN PCTSTR Filename,
IN PFILETIME LastWriteTime,
IN PCTSTR OsLoaderPath, OPTIONAL
IN DWORD Flags,
OUT PLOADED_INF *Inf
)
/*++
Routine Description:
This routine attempts to find a .PNF (Precompiled iNF) file corresponding to
the specified .INF name. If located, the .PNF is mapped into memory as a
LOADED_INF. To ensure that the INF hasn't changed since being compiled, the
INF's LastWriteTime, as stored in the .PNF's version block, is checked against
the LastWriteTime passed into this routine. If the two are different, then the
.PNF is out-of-sync, and is discarded from memory and deleted from the disk.
Arguments:
Filename - supplies the name of the INF file whose precompiled form is to be loaded.
This should be a fully qualified path (i.e., as returned by GetFullPathName).
LastWriteTime - supplies the last-write time for the INF.
OsLoaderPath - optionally, supplies path of the current OsLoader directory (e.g.,
"C:\os\winnt40"). If the specified PNF contains references to the system
partition, then its stored OsLoaderPath must match this path in order for the
PNF to be valid. If this parameter is not specified, the OsLoader path is
dynamically retrieved for comparison (unless the LDINF_FLAG_IGNORE_SYSPART flag
is specified).
Flags - supplies flags that modify the behavior of this routine. The following
flags are currently recognized:
LDINF_FLAG_IGNORE_SYSPART - If specified, then no validation will be done on
the stored OsLoaderPath present in the PNF. Since dynamically retrieving
the current path is time consuming, this flag should be specified as an
optimization if it is known that the relevant DIRIDs are not going to be
needed.
Inf - supplies the address of the variable that receives the LOADED_INF pointer,
if a valid .PNF is located.
Return Value:
If the PNF was successfully loaded, the return value is TRUE, otherwise, it is FALSE.
--*/
{
TCHAR CharBuffer[MAX_PATH];
PTSTR PnfFileName, PnfFileExt;
DWORD FileSize;
HANDLE FileHandle, MappingHandle;
PVOID BaseAddress;
BOOL IsPnfFile = FALSE;
PPNF_HEADER PnfHeader;
PLOADED_INF NewInf;
BOOL NeedToDestroyLock, SourcePathFieldAvailable;
lstrcpy(CharBuffer, Filename);
//
// Find the start of the filename component of the path, and then find the last
// period (if one exists) in that filename.
//
PnfFileName = (PTSTR)MyGetFileTitle(CharBuffer);
if(!(PnfFileExt = _tcsrchr(PnfFileName, TEXT('.')))) {
PnfFileExt = CharBuffer + lstrlen(CharBuffer);
}
//
// Now create a corresponding filename with the extension '.PNF'
//
lstrcpyn(PnfFileExt, pszPnfSuffix, SIZECHARS(CharBuffer) - (PnfFileExt - CharBuffer));
//
// Attempt to open and map the file into memory.
//
if(OpenAndMapFileForRead(CharBuffer,
&FileSize,
&FileHandle,
&MappingHandle,
&BaseAddress) != NO_ERROR) {
//
// Couldn't open a .PNF file--bail now.
//
return FALSE;
}
NewInf = NULL;
NeedToDestroyLock = FALSE;
SourcePathFieldAvailable = TRUE;
try {
//
// Now verify that this really is a precompiled INF (and that it's one we can use).
// Then see if the LastWriteTime field in its version block agrees with the filetime
// we were passed in.
//
PnfHeader = (PPNF_HEADER)BaseAddress;
if(HIBYTE(PnfHeader->Version) != PNF_MAJOR_VERSION) {
//
// A major version mismatch means the PNF is unusable.
//
goto clean0;
}
if(LOBYTE(PnfHeader->Version) != PNF_MINOR_VERSION) {
if(LOBYTE(PnfHeader->Version) < PNF_MINOR_VERSION) {
//
// We're currently at minor version 1. The only difference between version 0
// and version 1 was that a new field was added for an optional source path
// offset, so that we can track where the INF originally came from. Since we
// just discovered that we aren't at version 1, then we can't take advantage of
// this field.
//
SourcePathFieldAvailable = FALSE;
} else {
//
// This is a newer version that we know nothing about.
//
goto clean0;
}
}
#ifdef UNICODE
if(!(PnfHeader->Flags & PNF_FLAG_IS_UNICODE))
#else
if(PnfHeader->Flags & PNF_FLAG_IS_UNICODE)
#endif
{
//
// The APIs are Unicode while the PNF is ANSI, or vice versa.
//
goto clean0;
}
//
// The version information checks out--now check the last-write times.
//
if(CompareFileTime(LastWriteTime, &(PnfHeader->InfVersionLastWriteTime))) {
goto clean0;
}
//
// Now verify that the Windows (and, optionally, OsLoader) directories for this PNF
// match the current state of the world.
//
if(lstrcmpi((PCTSTR)((PBYTE)BaseAddress + PnfHeader->WinDirPathOffset), WindowsDirectory)) {
//
// This PNF doesn't match the current WindowsDirectory path, so don't
// use it.
//
goto clean0;
}
if((PnfHeader->OsLoaderPathOffset) && !(Flags & LDINF_FLAG_IGNORE_SYSPART)) {
//
// This INF contains references to the system partition. Verify that the path
// used during precompilation is the one we're currently using.
//
if(!OsLoaderPath) {
//
// The caller didn't specify an OsLoaderPath, so we must dynamically retrieve this
// value from the registry.
//
pSetupGetOsLoaderDriveAndPath(FALSE, CharBuffer, SIZECHARS(CharBuffer), NULL);
OsLoaderPath = CharBuffer;
}
if(lstrcmpi((PCTSTR)((PBYTE)BaseAddress + PnfHeader->OsLoaderPathOffset), OsLoaderPath)) {
goto clean0;
}
}
//
// One final check--make sure that the number of hash buckets used when precompiling
// this INF matches what we expect. (This wasn't rolled into the version check, since
// this is something that is subject to lots of modification, and we didn't want to
// rev the major version number each time.)
//
if(PnfHeader->StringTableHashBucketCount != HASH_BUCKET_COUNT) {
goto clean0;
}
//
// We can use the file--now set up our top level structures.
//
if(NewInf = MyMalloc(sizeof(LOADED_INF))) {
ZeroMemory(NewInf, sizeof(LOADED_INF));
if(NewInf->StringTable = InitializeStringTableFromPNF(PnfHeader)) {
if(InitializeSynchronizedAccess(&(NewInf->Lock))) {
NeedToDestroyLock = TRUE;
//
// All necessary resources were successfully allocated--now
// fill in the LOADED_INF fields
//
NewInf->Signature = LOADED_INF_SIG;
NewInf->FileHandle = FileHandle;
NewInf->MappingHandle = MappingHandle;
NewInf->ViewAddress = BaseAddress;
NewInf->SectionCount = PnfHeader->InfSectionCount;
NewInf->SectionBlockSizeBytes = PnfHeader->InfSectionBlockSize;
NewInf->SectionBlock = (PINF_SECTION)((PBYTE)BaseAddress +
PnfHeader->InfSectionBlockOffset);
NewInf->LineBlockSizeBytes = PnfHeader->InfLineBlockSize;
NewInf->LineBlock = (PINF_LINE)((PBYTE)BaseAddress +
PnfHeader->InfLineBlockOffset);
NewInf->ValueBlockSizeBytes = PnfHeader->InfValueBlockSize;
NewInf->ValueBlock = (PLONG)((PBYTE)BaseAddress +
PnfHeader->InfValueBlockOffset);
NewInf->Style = PnfHeader->InfStyle;
NewInf->HasStrings = (PnfHeader->Flags & PNF_FLAG_HAS_STRINGS);
//
// Next, fill in the VersionBlock fields.
//
NewInf->VersionBlock.LastWriteTime = *LastWriteTime;
NewInf->VersionBlock.DatumCount = PnfHeader->InfVersionDatumCount;
NewInf->VersionBlock.DataSize = PnfHeader->InfVersionDataSize;
NewInf->VersionBlock.DataBlock = (PCTSTR)((PBYTE)BaseAddress +
PnfHeader->InfVersionDataOffset);
NewInf->VersionBlock.FilenameSize = (lstrlen(Filename) + 1) * sizeof(TCHAR);
CopyMemory(NewInf->VersionBlock.Filename,
Filename,
NewInf->VersionBlock.FilenameSize
);
//
// Fill in the OsLoaderPath field, if present in the PNF.
//
if(PnfHeader->OsLoaderPathOffset) {
NewInf->OsLoaderPath = (PCTSTR)((PBYTE)BaseAddress +
PnfHeader->OsLoaderPathOffset);
}
//
// If the INF's SourcePath is available, then use it.
//
if(SourcePathFieldAvailable && (PnfHeader->InfSourcePathOffset)) {
NewInf->InfSourcePath = (PCTSTR)((PBYTE)BaseAddress +
PnfHeader->InfSourcePathOffset);
}
//
// Finally, fill in the string substitution list (if there is one).
//
if(PnfHeader->InfSubstValueCount) {
NewInf->SubstValueCount = PnfHeader->InfSubstValueCount;
NewInf->SubstValueList = (PSTRINGSUBST_NODE)((PBYTE)BaseAddress +
PnfHeader->InfSubstValueListOffset);
}
//
// We have successfully loaded the PNF.
//
IsPnfFile = TRUE;
}
}
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Reference the NeedToDestroyLock flag here in the except clause, so that the
// compiler won't try to re-order the code in such a way that the flag is unreliable.
//
NeedToDestroyLock = NeedToDestroyLock;
}
if(IsPnfFile) {
*Inf = NewInf;
} else {
if(NewInf) {
if(NeedToDestroyLock && LockInf(NewInf)) {
DestroySynchronizedAccess(&(NewInf->Lock));
}
if(NewInf->StringTable) {
MyFree(NewInf->StringTable);
}
MyFree(NewInf);
}
UnmapAndCloseFile(FileHandle, MappingHandle, BaseAddress);
}
return IsPnfFile;
}
VOID
SavePnf(
IN PCTSTR Filename,
IN PLOADED_INF Inf
)
/*++
Routine Description:
This routine attempts to write to disk a precompiled form (.PNF file) of the specified
loaded INF descriptor (from a .INF file).
Arguments:
Filename - specifies the fully-qualified path to the .INF textfile from which this
INF descriptor was loaded. A corresponding file with a .PNF extension will be
created to store the precompiled INF into.
Inf - supplies the address of the loaded INF descriptor to be written to disk as a
precompiled INF file.
Return Value:
None.
--*/
{
TCHAR PnfFilePath[MAX_PATH];
PTSTR PnfFileName, PnfFileExt;
HANDLE hFile;
PNF_HEADER PnfHeader;
DWORD Offset, BytesWritten, WinDirPathLen, SourcePathLen, OsLoaderPathLen;
BOOL Success;
PVOID StringTableDataBlock;
lstrcpy(PnfFilePath, Filename);
//
// Find the start of the filename component of the path, and then find the last
// period (if one exists) in that filename.
//
PnfFileName = (PTSTR)MyGetFileTitle(PnfFilePath);
if(!(PnfFileExt = _tcsrchr(PnfFileName, TEXT('.')))) {
PnfFileExt = PnfFilePath + lstrlen(PnfFilePath);
}
//
// Now create a corresponding filename with the extension '.PNF'
//
lstrcpyn(PnfFileExt, pszPnfSuffix, SIZECHARS(PnfFilePath) - (PnfFileExt - PnfFilePath));
hFile = CreateFile(PnfFilePath,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if(hFile == INVALID_HANDLE_VALUE) {
return;
}
//
// Enclose the rest of the function in try/except, in case we hit an error while
// writing to the file.
//
Success = FALSE;
try {
//
// Initialize a PNF header structure to be written to the beginning of the file.
//
ZeroMemory(&PnfHeader, sizeof(PNF_HEADER));
PnfHeader.InfStyle = Inf->Style;
#ifdef UNICODE
PnfHeader.Flags = PNF_FLAG_IS_UNICODE;
#else
PnfHeader.Flags = 0;
#endif
PnfHeader.Flags |= (Inf->HasStrings) ? PNF_FLAG_HAS_STRINGS : 0;
PnfHeader.Version = MAKEWORD(PNF_MINOR_VERSION, PNF_MAJOR_VERSION);
PnfHeader.StringTableHashBucketCount = HASH_BUCKET_COUNT;
//
// The Windows directory path is the first data block after the header.
//
Offset = PNF_ALIGN_BLOCK(sizeof(PNF_HEADER));
PnfHeader.WinDirPathOffset = Offset;
WinDirPathLen = (lstrlen(WindowsDirectory) + 1) * sizeof(TCHAR);
//
// The (optional) OsLoader directory path is the second data block.
//
Offset += PNF_ALIGN_BLOCK(WinDirPathLen);
if(Inf->OsLoaderPath) {
PnfHeader.OsLoaderPathOffset = Offset;
OsLoaderPathLen = (lstrlen(Inf->OsLoaderPath) + 1) * sizeof(TCHAR);
} else {
OsLoaderPathLen = 0;
}
//
// The string table is the third data block...
//
Offset += PNF_ALIGN_BLOCK(OsLoaderPathLen);
PnfHeader.StringTableBlockOffset = Offset;
PnfHeader.StringTableBlockSize = pStringTableGetDataBlock(Inf->StringTable, &StringTableDataBlock);
//
// Next comes the version block...
//
Offset += PNF_ALIGN_BLOCK(PnfHeader.StringTableBlockSize);
PnfHeader.InfVersionDataOffset = Offset;
PnfHeader.InfVersionDatumCount = Inf->VersionBlock.DatumCount;
PnfHeader.InfVersionDataSize = Inf->VersionBlock.DataSize;
PnfHeader.InfVersionLastWriteTime = Inf->VersionBlock.LastWriteTime;
//
// then, the section block...
//
Offset += PNF_ALIGN_BLOCK(PnfHeader.InfVersionDataSize);
PnfHeader.InfSectionBlockOffset = Offset;
PnfHeader.InfSectionCount = Inf->SectionCount;
PnfHeader.InfSectionBlockSize = Inf->SectionBlockSizeBytes;
//
// followed by the line block...
//
Offset += PNF_ALIGN_BLOCK(PnfHeader.InfSectionBlockSize);
PnfHeader.InfLineBlockOffset = Offset;
PnfHeader.InfLineBlockSize = Inf->LineBlockSizeBytes;
//
// and the value block...
//
Offset += PNF_ALIGN_BLOCK(PnfHeader.InfLineBlockSize);
PnfHeader.InfValueBlockOffset = Offset;
PnfHeader.InfValueBlockSize = Inf->ValueBlockSizeBytes;
//
// then the INF source path (if there is one)...
//
Offset += PNF_ALIGN_BLOCK(PnfHeader.InfValueBlockSize);
if(Inf->InfSourcePath) {
PnfHeader.InfSourcePathOffset = Offset;
SourcePathLen = (lstrlen(Inf->InfSourcePath) + 1) * sizeof(TCHAR);
Offset += PNF_ALIGN_BLOCK(SourcePathLen);
} else {
PnfHeader.InfSourcePathOffset = 0;
}
//
// and finally, the string substitution block (if there is one).
//
if(PnfHeader.InfSubstValueCount = Inf->SubstValueCount) {
PnfHeader.InfSubstValueListOffset = Offset;
} else {
PnfHeader.InfSubstValueListOffset = 0;
}
//
// Now write out all the blocks.
//
Offset = 0;
if(!WriteFile(hFile, &PnfHeader, sizeof(PnfHeader), &BytesWritten, NULL) ||
(BytesWritten != sizeof(PnfHeader))) {
goto clean0;
}
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.WinDirPathOffset - Offset)) {
Offset = PnfHeader.WinDirPathOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, WindowsDirectory, WinDirPathLen, &BytesWritten, NULL) ||
(BytesWritten != WinDirPathLen)) {
goto clean0;
}
Offset += BytesWritten;
if(Inf->OsLoaderPath) {
if(AlignForNextBlock(hFile, PnfHeader.OsLoaderPathOffset - Offset)) {
Offset = PnfHeader.OsLoaderPathOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, Inf->OsLoaderPath, OsLoaderPathLen, &BytesWritten, NULL) ||
(BytesWritten != OsLoaderPathLen)) {
goto clean0;
}
Offset += BytesWritten;
}
if(AlignForNextBlock(hFile, PnfHeader.StringTableBlockOffset - Offset)) {
Offset = PnfHeader.StringTableBlockOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, StringTableDataBlock, PnfHeader.StringTableBlockSize, &BytesWritten, NULL) ||
(BytesWritten != PnfHeader.StringTableBlockSize)) {
goto clean0;
}
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfVersionDataOffset - Offset)) {
Offset = PnfHeader.InfVersionDataOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, Inf->VersionBlock.DataBlock, PnfHeader.InfVersionDataSize, &BytesWritten, NULL) ||
(BytesWritten != PnfHeader.InfVersionDataSize)) {
goto clean0;
}
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfSectionBlockOffset - Offset)) {
Offset = PnfHeader.InfSectionBlockOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, Inf->SectionBlock, PnfHeader.InfSectionBlockSize, &BytesWritten, NULL) ||
(BytesWritten != PnfHeader.InfSectionBlockSize)) {
goto clean0;
}
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfLineBlockOffset - Offset)) {
Offset = PnfHeader.InfLineBlockOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, Inf->LineBlock, PnfHeader.InfLineBlockSize, &BytesWritten, NULL) ||
(BytesWritten != PnfHeader.InfLineBlockSize)) {
goto clean0;
}
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfValueBlockOffset - Offset)) {
Offset = PnfHeader.InfValueBlockOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, Inf->ValueBlock, PnfHeader.InfValueBlockSize, &BytesWritten, NULL) ||
(BytesWritten != PnfHeader.InfValueBlockSize)) {
goto clean0;
}
Offset += BytesWritten;
if(Inf->InfSourcePath) {
if(AlignForNextBlock(hFile, PnfHeader.InfSourcePathOffset - Offset)) {
Offset = PnfHeader.InfSourcePathOffset;
} else {
goto clean0;
}
if(!WriteFile(hFile, Inf->InfSourcePath, SourcePathLen, &BytesWritten, NULL) ||
(BytesWritten != SourcePathLen)) {
goto clean0;
}
Offset += BytesWritten;
}
if(PnfHeader.InfSubstValueCount) {
if(!AlignForNextBlock(hFile, PnfHeader.InfSubstValueListOffset - Offset)) {
goto clean0;
}
if(!WriteFile(hFile,
Inf->SubstValueList,
PnfHeader.InfSubstValueCount * sizeof(STRINGSUBST_NODE),
&BytesWritten,
NULL) ||
(BytesWritten != PnfHeader.InfSubstValueCount * sizeof(STRINGSUBST_NODE))) {
goto clean0;
}
}
//
// We're done.
//
Success = TRUE;
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
Success = FALSE;
}
CloseHandle(hFile);
if(!Success) {
//
// Something went wrong--get rid of the file.
//
DeleteFile(PnfFilePath);
}
}
BOOL
AddUnresolvedSubstToList(
IN PLOADED_INF Inf,
IN UINT ValueOffset,
IN BOOL CaseSensitive
)
/*++
Routine Description:
This routine adds a new STRINGSUBST_NODE to the array stored in the specified INF.
The entries in this array are used later to quickly locate all values that have
unresolved string substitutions in them (i.e., for subsequent user-defined DIRID
replacement).
Arguments:
Inf - Specifies the INF containing the string value to be added to the unresolved
substitutions list.
ValueOffset - Specifies the offset within the INF's value block of the unresolved
string value.
Return Value:
If the new element was successfully added to the array, the return value is TRUE.
If the routine failed (due to an out-of-memory error), the return value is FALSE.
--*/
{
PSTRINGSUBST_NODE p;
//
// Grow the array to accommodate the new element.
//
if(Inf->SubstValueList) {
p = MyRealloc(Inf->SubstValueList, (Inf->SubstValueCount + 1) * sizeof(STRINGSUBST_NODE));
} else {
MYASSERT(!(Inf->SubstValueCount));
p = MyMalloc(sizeof(STRINGSUBST_NODE));
}
if(!p) {
return FALSE;
}
//
// Now, we must check to see if the ValueOffset currently being inserted is the same
// as the entry on the end of the list. This will be the case if we're dealing with
// a line key, or a single-value line, since we first add the value case-sensitively,
// then add the value again case-insensitively for look-up, and insert it in front
// of the case-sensitive form.
//
if(Inf->SubstValueCount &&
(ValueOffset == p[Inf->SubstValueCount - 1].ValueOffset)) {
//
// The value offsets are the same. Increment the value offset for the value
// currently at the end of the list, before adding the new value.
//
p[Inf->SubstValueCount - 1].ValueOffset++;
}
p[Inf->SubstValueCount].ValueOffset = ValueOffset;
p[Inf->SubstValueCount].TemplateStringId = Inf->ValueBlock[ValueOffset];
p[Inf->SubstValueCount].CaseSensitive = CaseSensitive;
//
// Store the new array size and pointer back in the INF, and return success.
//
Inf->SubstValueList = p;
Inf->SubstValueCount++;
return TRUE;
}
DWORD
ApplyNewUserDirIdsToInfs(
IN PLOADED_INF MasterInf,
IN PLOADED_INF Inf OPTIONAL
)
/*++
Routine Description:
This routine processes either a single INF, or each loaded INF in the
linked list, applying user-defined DIRID mappings to each value
containing unresolved string substitutions.
THIS ROUTINE DOES NOT DO INF LOCKING--CALLER MUST DO IT!
Arguments:
MasterInf - Supplies a pointer to the head of a linked list of loaded inf
structures. This 'master' node contains the user-defined DIRID
mappings for this set of INFs. If the 'Inf' parameter is not specified,
then each INF in this linked list is processed.
Inf - Optionally, supplies a pointer to a single INF within the MasterInf list
to be processed. If this parameter is not specified, then all INFs in
the list are processed.
Return Value:
If success, the return value is NO_ERROR.
If failure, the return value is a Win32 error code.
--*/
{
PLOADED_INF CurInf, WriteableInf;
UINT UserDirIdCount;
PUSERDIRID UserDirIds;
DWORD i;
PCTSTR TemplateString;
PARSE_CONTEXT ParseContext;
BOOL UnresolvedSubst;
LONG NewStringId;
UserDirIdCount = MasterInf->UserDirIdList.UserDirIdCount;
UserDirIds = MasterInf->UserDirIdList.UserDirIds;
for(CurInf = Inf ? Inf : MasterInf;
CurInf;
CurInf = Inf ? NULL : CurInf->Next) {
//
// Nothing to do if there are no unresolved string substitutions.
//
if(!(CurInf->SubstValueCount)) {
continue;
}
//
// If this is a PNF, then we must move it into writeable memory before
// we do the string substitutions.
//
if(CurInf->FileHandle != INVALID_HANDLE_VALUE) {
if(!(WriteableInf = DuplicateLoadedInfDescriptor(CurInf))) {
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Replace the contents of the PNF in the linked list with that of our
// new writeable INF.
//
ReplaceLoadedInfDescriptor(CurInf, WriteableInf);
}
//
// There are one or more unresolved string substitutions in this INF.
// Process each one.
//
for(i = 0; i < CurInf->SubstValueCount; i++) {
//
// Retrieve the original (template) string for this value.
//
TemplateString = pStringTableStringFromId(CurInf->StringTable,
CurInf->SubstValueList[i].TemplateStringId
);
MYASSERT(TemplateString);
//
// Build a partial parse context structure to pass into ProcessForSubstitutions().
//
ParseContext.DoUserDirIds = TRUE;
ParseContext.Inf = MasterInf;
//
// None of the other fields are used in this case--don't bother initializing them.
//
ProcessForSubstitutions(&ParseContext, TemplateString, &UnresolvedSubst);
NewStringId = pStringTableAddString(CurInf->StringTable,
ParseContext.TemporaryString,
STRTAB_BUFFER_WRITEABLE | (CurInf->SubstValueList[i].CaseSensitive
? STRTAB_CASE_SENSITIVE
: STRTAB_CASE_INSENSITIVE)
);
if(NewStringId == -1) {
//
// We failed because of an out-of-memory condition. Aborting now means that the
// INF may have some of its unresolved strings fixed up, while others haven't yet
// been processed. Oh well...
//
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Replace the string ID at the value offset with the new one we just computed.
//
CurInf->ValueBlock[CurInf->SubstValueList[i].ValueOffset] = NewStringId;
}
}
return NO_ERROR;
}
BOOL
AlignForNextBlock(
IN HANDLE hFile,
IN DWORD ByteCount
)
{
DWORD i, BytesWritten;
BYTE byte = 0;
MYASSERT(ByteCount < PNF_ALIGNMENT);
for(i = 0; i < ByteCount; i++) {
if(!WriteFile(hFile, &byte, sizeof(byte), &BytesWritten, NULL) ||
(BytesWritten != sizeof(byte))) {
return FALSE;
}
}
return TRUE;
}
DWORD
pSetupGetOsLoaderDriveAndPath(
IN BOOL RootOnly,
OUT PTSTR CallerBuffer,
IN DWORD CallerBufferSize,
OUT PDWORD RequiredSize OPTIONAL
)
/*++
Routine Description:
This routine retrieves the current path for the system partition root/OsLoader directory
(from the registry).
Arguments:
RootOnly - if TRUE, then only the system partition root is returned (e.g., "C:\")
CallerBuffer - supplies a character buffer that receives the requested path
CallerBufferSize - supplies the size, in characters of the CallerBuffer
RequiredSize - optionally, supplies the address of a variable that receives the
number of characters required to store the requested path string (including
terminating NULL).
Return Value:
If success, the return value is NO_ERROR.
If failure, the return value is ERROR_INSUFFICIENT_BUFFER.
--*/
{
HKEY hKey;
TCHAR CharBuffer[MAX_PATH];
DWORD DataLen;
LONG Err;
PTSTR ReturnBuffer;
CopyMemory(CharBuffer,
pszPathSetup,
sizeof(pszPathSetup) - sizeof(TCHAR)
);
CopyMemory((PBYTE)CharBuffer + (sizeof(pszPathSetup) - sizeof(TCHAR)),
pszKeySetup,
sizeof(pszKeySetup)
);
if((Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
CharBuffer,
0,
KEY_READ,
&hKey)) == ERROR_SUCCESS) {
DataLen = sizeof(CharBuffer);
Err = RegQueryValueEx(hKey,
pszBootDir,
NULL,
NULL,
(PBYTE)CharBuffer,
&DataLen
);
RegCloseKey(hKey);
DataLen /= sizeof(TCHAR); // need # characters--not bytes.
}
if(Err != ERROR_SUCCESS) {
//
// If we couldn't retrieve the 'BootDir' value, drop back to default of "C:\".
//
CopyMemory(CharBuffer, pszDefaultSystemPartition, sizeof(pszDefaultSystemPartition));
DataLen = SIZECHARS(pszDefaultSystemPartition);
}
//
// If there is an OsLoader relative path, then concatenate it to our root path.
//
if(!RootOnly && OsLoaderRelativePath) {
ConcatenatePaths(CharBuffer, OsLoaderRelativePath, SIZECHARS(CharBuffer), &DataLen);
}
if(RequiredSize) {
*RequiredSize = DataLen;
}
if(CallerBufferSize < DataLen) {
return ERROR_INSUFFICIENT_BUFFER;
}
CopyMemory(CallerBuffer, CharBuffer, DataLen * sizeof(TCHAR));
return NO_ERROR;
}