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

6203 lines
208 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
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 "precomp.h"
#pragma hdrstop
#include <ntverp.h>
//
// 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
//
// Define unresolved substitution values for return by ParseValueString and
// ProcessForSubstitutions
//
#define UNRESOLVED_SUBST_NONE (0)
#define UNRESOLVED_SUBST_USER_DIRID (1)
#define UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID (2)
//
// 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 or volatile system DIRIDs to
// unresolved string substitutions. If this flag is TRUE, then the
// aforementioned routine will call pSetupVolatileDirIdToPath for %<x>%
// substrings, instead of its normal (i.e., load-time) processing.
//
BOOL DoVolatileDirIds;
//
// 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,
pszAddInterface[] = SZ_KEY_ADDINTERFACE,
pszInterfaceInstall32[] = INFSTR_SECT_INTERFACE_INSTALL_32,
pszAddService[] = SZ_KEY_ADDSERVICE,
pszDelService[] = SZ_KEY_DELSERVICE,
pszCatalogFile[] = INFSTR_KEY_CATALOGFILE;
//
// 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,
pszNtX86Suffix[] = DISTR_INF_NTX86_SUFFIX,
pszNtIA64Suffix[] = DISTR_INF_NTIA64_SUFFIX,
pszNtAMD64Suffix[] = DISTR_INF_NTAMD64_SUFFIX,
pszPnfSuffix[] = DISTR_INF_PNF_SUFFIX,
pszInfSuffix[] = DISTR_INF_INF_SUFFIX,
pszCatSuffix[] = DISTR_INF_CAT_SUFFIX,
pszServicesSectionSuffix[] = DISTR_INF_SERVICES_SUFFIX,
pszWmiSectionSuffix[] = DISTR_INF_WMI_SUFFIX,
pszInterfacesSectionSuffix[] = DISTR_INF_INTERFACES_SUFFIX,
pszCoInstallersSectionSuffix[] = DISTR_INF_COINSTALLERS_SUFFIX,
pszLogConfigOverrideSectionSuffix[] = DISTR_INF_LOGCONFIGOVERRIDE_SUFFIX,
pszX86SrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_X86,
pszIa64SrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_IA64,
pszAmd64SrcDiskSuffix[] = DISTR_INF_SRCDISK_SUFFIX_AMD64;
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 LanguageId,
IN DWORD Flags,
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
OUT PLOADED_INF *Inf,
OUT PTSTR *InfSourcePathToMigrate, OPTIONAL
OUT PDWORD InfSourcePathToMigrateMediaType, OPTIONAL
OUT PTSTR *InfOriginalNameToMigrate OPTIONAL
);
DWORD
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;
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 PDWORD UnresolvedSubst
)
{
PCTSTR In, q;
PTCHAR Out, p;
TCHAR Str[MAX_STRING_LENGTH];
ULONG Len, i;
PTCHAR End;
TCHAR DirId[MAX_PATH];
BOOL HasStrings = Context->Inf->HasStrings;
UINT DirIdUsed;
BOOL HasVolatileSysDirId;
In = String;
Out = Context->TemporaryString;
End = Out + SIZECHARS(Context->TemporaryString);
*UnresolvedSubst = UNRESOLVED_SUBST_NONE;
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('%'))) {
HasVolatileSysDirId = FALSE;
//
// Get value to substitute. If we can't find the value,
// put the whole string like %abc% in there.
//
Len = (ULONG)(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->DoVolatileDirIds) {
if(q = pSetupVolatileDirIdToPath(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('\\')) {
i = lstrlen(DirId);
if(i > 0 && (*CharPrev(DirId,DirId+i) == TEXT('\\'))) {
DirId[i-1] = TEXT('\0');
}
}
}
} else {
if(HasStrings) {
q = LocateStringSubstitute(Context, Str);
} else {
q = NULL;
}
if(!q) {
//
// Maybe we have a standard DIRID here...
//
if(q = pSetupDirectoryIdToPathEx(Str,
&DirIdUsed,
NULL,
Context->InfSourcePath,
&(Context->OsLoaderPath),
&HasVolatileSysDirId)) {
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('\\')) {
i = lstrlen(DirId);
if(i > 0 && (*CharPrev(DirId,DirId+i) == TEXT('\\'))) {
DirId[i-1] = 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 volatile
// and user-defined DIRIDs).
//
// (NOTE: Don't set this if we bailed because the token
// was too long!)
//
if(Len <= CSTRLEN(Str)) {
*UnresolvedSubst = HasVolatileSysDirId
? UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID
: UNRESOLVED_SUBST_USER_DIRID;
}
}
} 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 PDWORD 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 value indicating whether or not this value
contained any unresolved string substitutions (and as such, should be
tracked for user-defined DIRID replacement, etc.). May be one of the
following 3 values:
UNRESOLVED_SUBST_NONE (0)
UNRESOLVED_SUBST_USER_DIRID (1)
UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID (2)
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 = UNRESOLVED_SUBST_NONE;
}
}
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;
PVOID p;
LONG StringId;
PCTSTR BufferEnd = Context->BufferEnd;
PWORD pValueCount;
DWORD 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),
NULL,0
);
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) {
if(!AddUnresolvedSubstToList(Context->Inf,
Context->ValueBlockUseCount - 1,
CaseSensitive)) {
return ERROR_NOT_ENOUGH_MEMORY;
}
if(UnresolvedSubst == UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID) {
Context->Inf->Flags |= LIF_HAS_VOLATILE_DIRIDS;
}
}
//
// Reset the 'RepeatSingleVal' flag, in case we were faking the key behavior.
//
RepeatSingleVal = FALSE;
} else {
Context->Inf->ValueBlock[Context->ValueBlockUseCount] = StringId;
if(UnresolvedSubst) {
if(!AddUnresolvedSubstToList(Context->Inf,
Context->ValueBlockUseCount,
CaseSensitive)) {
return ERROR_NOT_ENOUGH_MEMORY;
}
if(UnresolvedSubst == UNRESOLVED_SUBST_SYSTEM_VOLATILE_DIRID) {
Context->Inf->Flags |= LIF_HAS_VOLATILE_DIRIDS;
}
}
}
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,
NULL,0
);
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,
IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
)
{
PLOADED_INF p;
if(p = MyTaggedMalloc(sizeof(LOADED_INF),MEMTAG_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(0)) {
p->LogContext = NULL;
if(InheritLogContext(LogContext, &p->LogContext) == NO_ERROR) {
//
// success
//
if(InitializeSynchronizedAccess(&p->Lock)) {
p->Signature = LOADED_INF_SIG;
p->FileHandle = p->MappingHandle = INVALID_HANDLE_VALUE;
return(p);
}
DeleteLogContext(p->LogContext);
}
pStringTableDestroy(p->StringTable);
}
MyFree(p->ValueBlock);
}
MyFree(p->LineBlock);
}
MyFree(p->SectionBlock);
}
MyTaggedFree(p,MEMTAG_INF);
}
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 = MyTaggedMalloc(sizeof(LOADED_INF),MEMTAG_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;
NewInf->OsLoaderPath = NULL;
NewInf->InfSourcePath = NULL;
NewInf->OriginalInfName = 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 = MyTaggedMalloc(Inf->VersionBlock.DataSize,MEMTAG_VBDATA);
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)
);
}
if(Inf->OsLoaderPath) {
NewInf->OsLoaderPath = DuplicateString(Inf->OsLoaderPath);
if(!NewInf->OsLoaderPath) {
goto clean0;
}
}
if(Inf->InfSourcePath) {
NewInf->InfSourcePath = DuplicateString(Inf->InfSourcePath);
if(!NewInf->InfSourcePath) {
goto clean0;
}
}
if(Inf->OriginalInfName) {
NewInf->OriginalInfName = DuplicateString(Inf->OriginalInfName);
if(!NewInf->OriginalInfName) {
goto clean0;
}
}
//
// 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->OriginalInfName = NewInf->OriginalInfName;
NewInf->InfSourcePath = NewInf->InfSourcePath;
NewInf->OsLoaderPath = NewInf->OsLoaderPath;
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->OriginalInfName) {
MyFree(NewInf->OriginalInfName);
}
if(NewInf->InfSourcePath) {
MyFree(NewInf->InfSourcePath);
}
if(NewInf->OsLoaderPath) {
MyFree(NewInf->OsLoaderPath);
}
if(NewInf->SubstValueList) {
MyFree(NewInf->SubstValueList);
}
if(NewInf->UserDirIdList.UserDirIds) {
MyFree(NewInf->UserDirIdList.UserDirIds);
}
if(NewInf->VersionBlock.DataBlock) {
MyTaggedFree(NewInf->VersionBlock.DataBlock,MEMTAG_VBDATA);
}
if(NewInf->StringTable) {
pStringTableDestroy(NewInf->StringTable);
}
if(NewInf->ValueBlock) {
MyFree(NewInf->ValueBlock);
}
if(NewInf->LineBlock) {
MyFree(NewInf->LineBlock);
}
if(NewInf->SectionBlock) {
MyFree(NewInf->SectionBlock);
}
MyTaggedFree(NewInf,MEMTAG_INF);
NewInf = NULL;
} else {
//
// The copy was successful, but it made a copy of the pointer to the
// log context, so we must addref if
//
RefLogContext(NewInf->LogContext);
}
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.
//
MyTaggedFree(NewInf,MEMTAG_INF);
}
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.
--*/
{
//
// 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) {
pSetupUnmapAndCloseFile(Inf->FileHandle, Inf->MappingHandle, Inf->ViewAddress);
pStringTableDestroy(Inf->StringTable);
} else {
MyFree(Inf->ValueBlock);
MyFree(Inf->LineBlock);
MyFree(Inf->SectionBlock);
pStringTableDestroy(Inf->StringTable);
if(Inf->VersionBlock.DataBlock) {
MyTaggedFree(Inf->VersionBlock.DataBlock,MEMTAG_VBDATA);
}
if(Inf->SubstValueList) {
MyFree(Inf->SubstValueList);
Inf->SubstValueList = NULL;
}
if(Inf->OsLoaderPath) {
MyFree(Inf->OsLoaderPath);
}
if(Inf->InfSourcePath) {
MyFree(Inf->InfSourcePath);
}
if(Inf->OriginalInfName) {
MyFree(Inf->OriginalInfName);
}
}
//
// 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);
}
//
// Delete the log context if there is one
//
DeleteLogContext(Inf->LogContext);
Inf->LogContext = NULL;
//
// 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
IN PSETUP_LOG_CONTEXT LogContext, 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.
LogContext - optionally supplies the log context we should inherit from
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.
--*/
{
PPARSE_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.
*ErrorLineNumber = 0;
ParseContext = MyMalloc(sizeof(PARSE_CONTEXT));
if(!ParseContext) {
return ERROR_NOT_ENOUGH_MEMORY;
}
ZeroMemory(ParseContext,sizeof(PARSE_CONTEXT));
ParseContext->Inf = AllocateLoadedInfDescriptor(
INITIAL_SECTION_BLOCK_SIZE,
INITIAL_LINE_BLOCK_SIZE,
INITIAL_VALUE_BLOCK_SIZE,
LogContext
);
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);
MyFree(ParseContext);
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.
// this will allow us to have these values ready for if/when they are referenced
//
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.)
//
Result = pSetupGetOsLoaderDriveAndPath(FALSE,
ParseContext->TemporaryString,
SIZECHARS(ParseContext->TemporaryString),
&OsLoaderPathLength
);
if(Result) {
FreeLoadedInfDescriptor(ParseContext->Inf);
MyFree(ParseContext);
return Result;
}
OsLoaderPathLength *= sizeof(TCHAR); // want # bytes--not chars
if(!(ParseContext->OsLoaderPath = MyMalloc(OsLoaderPathLength))) {
FreeLoadedInfDescriptor(ParseContext->Inf);
MyFree(ParseContext);
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;
MyFree(ParseContext);
return(NO_ERROR);
}
MyFree(ParseContext);
return(ERROR_NOT_ENOUGH_MEMORY);
}
DWORD
PreprocessInf(
IN PCTSTR FileImage,
IN OUT PDWORD FileImageSize,
IN BOOL MatchClassGuid,
IN PCTSTR ClassGuidString, OPTIONAL
IN DWORD LanguageId, OPTIONAL
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN PCTSTR FileName, OPTIONAL
OUT PBOOL Win95Inf,
OUT PSTRINGSEC_PARAMS StringsSectionParams OPTIONAL
)
{
PCTSTR FileImageEnd;
PCTSTR VerAndStringsCheckUB, DecoratedStringsCheckUB, SigAndClassGuidCheckUB;
PCTSTR p;
PTSTR endp;
UINT CurLineNumber, InStringsSection;
PCTSTR StrSecStart[5], StrSecEnd[5]; // 1-based, 0th entry unused.
UINT StrSecStartLine[5], StrSecEndLine[5]; // ""
BOOL InVersionSection;
BOOL IsWin95Inf;
DWORD rc = NO_ERROR;
DWORD StrSecLangId, PrimaryLanguageId, NearLanguageId;
BOOL LocalizedInf = FALSE;
//
// 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;
SigAndClassGuidCheckUB = FileImageEnd;
//
// I have to cast these two arrays to silence a bogus compiler warning about
// different 'const' qualifiers.
//
ZeroMemory((PVOID)StrSecStart, sizeof(StrSecStart));
ZeroMemory((PVOID)StrSecEnd, sizeof(StrSecEnd));
InStringsSection = 0;
PrimaryLanguageId = (DWORD)PRIMARYLANGID(LanguageId);
NearLanguageId = 0;
InVersionSection = 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);
DecoratedStringsCheckUB = VerAndStringsCheckUB - 5; // "strings" + ".xxxx"
//
// 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[InStringsSection] = p;
StrSecEndLine[InStringsSection] = CurLineNumber;
InStringsSection = 0;
}
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[4] && !_tcsnicmp(p, pszStrings, CSTRLEN(pszStrings))) {
//
// We matched on the undecorated string section--this is the lowest
// priority match.
//
InStringsSection = 4;
StrSecStart[4] = p-1;
StrSecStartLine[4] = CurLineNumber;
p += (CSTRLEN(pszStrings) + 1);
}
}
} else if(LanguageId && !StrSecStart[1]) {
//
// We don't have a [strings] nor a [version] section. However, we need to
// check to see if we have a language-specific strings section, for example,
//
// [strings.0409]
//
if((p < DecoratedStringsCheckUB) &&
(*(p + CSTRLEN(pszVersion) + 5) == TEXT(']'))) {
//
// The section name is of the right length. Now verify that the name
// begins with "strings."
//
if((*(p + CSTRLEN(pszVersion)) == TEXT('.')) &&
!_tcsnicmp(p, pszStrings, CSTRLEN(pszStrings))) {
//
// OK, we've found a language-specific strings section--retrieve
// the 4-digit (hex) language ID.
//
StrSecLangId = _tcstoul((p + CSTRLEN(pszVersion) + 1), &endp, 16);
if(endp == (p + CSTRLEN(pszVersion) + 5)) {
//
// The language ID was of the proper form - this
// is a localized INF
//
LocalizedInf = TRUE;
//
// now see if it matches the language we're
// supposed to be using when loading this INF.
//
if(StrSecLangId == LanguageId) {
//
// we have an exact match
//
InStringsSection = 1;
NearLanguageId = LanguageId;
} else if(StrSecLangId == PrimaryLanguageId) {
//
// we have a match on primary language (sublanguage is not
// included in the strings section's name--thus permitting
// a 'wildcard' match).
//
if(!StrSecStart[2]) {
InStringsSection = 2;
}
if(!StrSecStart[1]) {
NearLanguageId = PrimaryLanguageId;
}
} else if((DWORD)PRIMARYLANGID(StrSecLangId) == PrimaryLanguageId) {
//
// we have a match on primary language (sublanguage is a
// mismatch, but it's better than falling back to the default).
//
if(!StrSecStart[3]) {
InStringsSection = 3;
if(!StrSecStart[1] && !StrSecStart[2]) {
NearLanguageId = StrSecLangId;
}
}
}
if(InStringsSection) {
StrSecStart[InStringsSection] = p-1;
StrSecStartLine[InStringsSection] = CurLineNumber;
}
p += (CSTRLEN(pszStrings) + 6);
}
}
}
}
} 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 = (DWORD)(p - FileImage);
}
if(StringsSectionParams) {
//
// If a strings section happens to be the last section in the INF,
// then we need to remember the end of the INF as being the end of
// the string section also.
//
if(InStringsSection) {
StrSecEnd[InStringsSection] = p;
StrSecEndLine[InStringsSection] = CurLineNumber;
}
//
// Now search through our array of strings sections (highest priority to lowest),
// looking for the best match.
//
for(InStringsSection = 1; InStringsSection < 5; InStringsSection++) {
if(StrSecStart[InStringsSection]) {
break;
}
}
//
// if the INF appears to be partially localized, and we didn't
// pick an apropriate localized string section
// log it
//
if(LogContext && IsWin95Inf) {
if(InStringsSection >= 5) {
//
// it's quite valid to have an INF with no strings section
// so log it verbose here, we'll catch it later
//
WriteLogEntry(LogContext,
SETUP_LOG_VERBOSE,
MSG_LOG_NO_STRINGS,
NULL,
LanguageId,
PrimaryLanguageId,
NearLanguageId,
FileName
);
} else if(LocalizedInf && InStringsSection > 2) {
//
// INF has localized string sections
// but none were reasonable match for locale
//
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
(InStringsSection> 3 ? MSG_LOG_DEF_STRINGS :
MSG_LOG_NEAR_STRINGS),
NULL,
LanguageId,
PrimaryLanguageId,
NearLanguageId,
FileName
);
}
}
if(IsWin95Inf && (InStringsSection < 5)) {
//
// If we found a [strings] section in a Win95-style INF,
// then store the beginning and ending positions, and the
// beginning and ending line numbers, in the output parameter
// structure
//
StringsSectionParams->Start = StrSecStart[InStringsSection];
StringsSectionParams->End = StrSecEnd[InStringsSection];
StringsSectionParams->StartLineNumber = StrSecStartLine[InStringsSection];
StringsSectionParams->EndLineNumber = StrSecEndLine[InStringsSection];
} 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,
0,
LDINF_FLAG_IGNORE_VOLATILE_DIRIDS | LDINF_FLAG_IGNORE_LANGUAGE,
NULL,
&Pnf,
NULL,
NULL,
NULL)) {
//
// 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,NULL) == NO_ERROR) {
if(PreprocessInf(ReadBuffer.TextBuffer,
&(ReadBuffer.TextBufferSize),
FALSE,
NULL,
0,
NULL,
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 PCTSTR OriginalInfName, OPTIONAL
IN PLOADED_INF AppendInf, OPTIONAL
IN PSETUP_LOG_CONTEXT pLogContext, OPTIONAL
OUT PLOADED_INF *LoadedInf,
OUT UINT *ErrorLineNumber,
OUT BOOL *PnfWasUsed OPTIONAL
)
/*++
Routine Description:
Top level routine to load an inf file. Both win95-style and winnt3.x-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 we will always attempt to
generate a PNF file, if a valid one does not exist.
LDINF_FLAG_ALWAYS_IGNORE_PNF - If specified, then we will not even look
at or attempt to generate PNF file.
LDINF_FLAG_IGNORE_VOLATILE_DIRIDS - 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).
This flag also suppresses substitution of volatile system DIRIDs.
(Note: this flag should not be specified when append-loading an INF)
LDINF_FLAG_REGENERATE_PNF - If specified, then the existing PNF (if
present) is considered invalid, and is not even checked for. This
flag causes us to always generate a new PNF, and if we're unable to
do so, the routine will fail. This flag must always be specified in
conjunction with LDINF_FLAG_ALWAYS_TRY_PNF.
LDINF_FLAG_SRCPATH_IS_URL - If specified, then the InfSourcePath passed in is
not a file path, but rather a URL. If this flag is specified, InfSourcePath
may still be NULL, which indicates that the origin of this INF was the default
Code Download Manager site.
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 path to be used as the INF's source path. If
LDINF_FLAG_SRCPATH_IS_URL is specified, this is a URL (see above), otherwise, this
is a directory path. This information is stored in the PNF file if this INF gets
precompiled.
If LDINF_FLAG_SRCPATH_IS_URL is specified, then "A:\" is used as the directory string
substitution for DIRID_SRCPATH.
OriginalInfName - Optionally, supplies the original name of the INF (no path)
to be stored in the PNF, if generated. If this parameter is not supplied,
then the INF's present name is assumed to be its original name.
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!!!
pLogContext - if supplied, specifies a LogContext that should be inherited
as opposed to creating one
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)
PnfWasUsed - optionally, receives a boolean value upon successful return
indicating whether or not a precompiled INF was used/generated in
loading this INF. NOTE, this flag should not be specified if an
append-load is requested.
Return Value:
Win32 error code (with inf extensions) for result.
If result is not NO_ERROR, ErrorLineNumber is filled in.
--*/
{
TEXTFILE_READ_BUFFER ReadBuffer;
DWORD rc;
PLOADED_INF Inf, InfListTail;
BOOL Win95Inf;
STRINGSEC_PARAMS StringsSectionParams;
HANDLE TextFileHandle;
PCTSTR OsLoaderPath = NULL;
DWORD LanguageId;
PTSTR InfSourcePathToMigrate, InfOriginalNameToMigrate;
DWORD InfSourcePathToMigrateMediaType = SPOST_NONE;
BOOL PnfUsed = FALSE; // this allows us to log the flag if PnfWasUsed=NULL
BOOL PnfSaved = FALSE; // allows us to log the fact that we saved PNF
PSETUP_LOG_CONTEXT LogContext = NULL;
MYASSERT(!(AppendInf && PnfWasUsed));
MYASSERT(!(AppendInf && (Flags & LDINF_FLAG_IGNORE_VOLATILE_DIRIDS)));
*ErrorLineNumber = 0;
if(PnfWasUsed) {
*PnfWasUsed = FALSE;
}
//
// Since we're now storing zero-length INF files in %windir%\Inf as
// placeholders for the corresponding catalog files, add a quick check to
// make sure we haven't been handed a zero-length INF. If so, we can return
// immediately and short ciruit some code. (While we're at it, also make
// sure that the high DWORD doesn't have any bits set, as we can't handle
// files greater than 2^32).
//
if(FileData->nFileSizeHigh || !FileData->nFileSizeLow) {
return ERROR_GENERAL_SYNTAX;
}
//
// 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;
}
//
// We want to append-load the INF based on the locale of the already-
// loaded INF(s)
//
LanguageId = AppendInf->LanguageId;
} else {
//
// We want to load the INF based on the current locale set for this thread.
//
LanguageId = (DWORD)LANGIDFROMLCID(GetThreadLocale());
}
InheritLogContext(pLogContext,&LogContext);
//
// Now determine whether a precompiled form of this INF exists, and if so,
// then use it instead.
//
if((Flags & (LDINF_FLAG_REGENERATE_PNF|LDINF_FLAG_ALWAYS_IGNORE_PNF))==0) {
if (!InfSourcePath && !(Flags & LDINF_FLAG_SRCPATH_IS_URL)) {
//
// if no source information provided, then always use that provided in the PNF
// even if it might be wrong
// typically when we're replacing such an INF, we'll explicitly say
// not to load existing PNF
//
Flags |= LDINF_FLAG_ALWAYS_GET_SRCPATH;
}
if (LoadPrecompiledInf(Filename,
&(FileData->ftLastWriteTime),
OsLoaderPath,
LanguageId,
Flags,
LogContext,
&Inf,
&InfSourcePathToMigrate,
&InfSourcePathToMigrateMediaType,
&InfOriginalNameToMigrate)) {
//
// Make sure that the PNF is of the specified style.
//
if(!(Style & (DWORD)Inf->Style)) {
FreeInfFile(Inf);
DeleteLogContext(LogContext);
return ERROR_WRONG_INF_STYLE;
}
if(AppendInf) {
Inf->Prev = InfListTail;
InfListTail->Next = Inf;
}
PnfUsed = TRUE;
if(PnfWasUsed) {
*PnfWasUsed = TRUE;
}
rc = NO_ERROR;
goto clean0;
}
}
//
// If we tried to load the PNF and it failed, then check to see if we were
// returned any INF source path information to migrate to the new PNF. If
// so, then this overrides the InfSourcePath information passed into this
// routine.
//
if(InfSourcePathToMigrateMediaType != SPOST_NONE) {
//
// Discard the arguments the caller passed in and use what we retrieved
// from the old PNF instead.
//
InfSourcePath = InfSourcePathToMigrate;
if(InfSourcePathToMigrateMediaType == SPOST_PATH) {
//
// Make sure the "sourcepath is URL" bit is not set.
//
Flags &= ~LDINF_FLAG_SRCPATH_IS_URL;
} else {
//
// This is a URL path--make sure the "sourcepath is URL" bit is set.
//
Flags |= LDINF_FLAG_SRCPATH_IS_URL;
}
//
// If we're migrating source path information from the PNF, then we need
// to use the PNF-specified original INF name, as well, instead of what
// the caller may have specified.
//
OriginalInfName = InfOriginalNameToMigrate;
}
//
// 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) {
if(InfSourcePathToMigrateMediaType != SPOST_NONE) {
if(InfSourcePathToMigrate) {
MyFree(InfSourcePathToMigrate);
}
if(InfOriginalNameToMigrate) {
MyFree(InfOriginalNameToMigrate);
}
}
DeleteLogContext(LogContext);
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,LogContext)) == 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,
LanguageId,
LogContext,
Filename,
&Win95Inf,
&StringsSectionParams)) == NO_ERROR) {
rc = ERROR_WRONG_INF_STYLE;
if(Win95Inf) {
if(Style & INF_STYLE_WIN4) {
//
// If we're dealing with a URL, then we don't have a real
// directory that we can do substitions on for DIRID_SRCPATH.
// "A:\" is about the best we can do.
//
rc = ParseNewInf(ReadBuffer.TextBuffer,
ReadBuffer.TextBufferSize,
(Flags & LDINF_FLAG_SRCPATH_IS_URL) ? pszOemInfDefaultPath
: InfSourcePath,
OsLoaderPath,
LogContext,
&Inf,
ErrorLineNumber,
&StringsSectionParams
);
}
} else {
//
// Can't append old-style file.
//
if(!AppendInf && (Style & INF_STYLE_OLDNT)) {
rc = ParseOldInf(ReadBuffer.TextBuffer,
ReadBuffer.TextBufferSize,
LogContext,
&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) {
Inf->InfSourceMediaType = (Flags & LDINF_FLAG_SRCPATH_IS_URL) ? SPOST_URL
: SPOST_PATH;
if(InfSourcePath) {
//
// If the caller specified a source path (or we're migrating
// one from a previously-existing PNF), then duplicate the
// string, and store a pointer to it in our INF structure.
//
if(!(Inf->InfSourcePath = DuplicateString(InfSourcePath))) {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
}
if((rc == NO_ERROR) && OriginalInfName) {
//
// If the caller specified the INF's original filename (or
// we're migrating one from a previously-existing PNF), then
// duplicate the string, and store a pointer to it in our
// INF structure.
//
// We shouldn't be storing this name if it's the same as the
// INF's present name.
//
MYASSERT(lstrcmpi(OriginalInfName, pSetupGetFileTitle(Filename)));
if(!(Inf->OriginalInfName = DuplicateString(OriginalInfName))) {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
if(rc == NO_ERROR) {
//
// Store the language ID used to load this INF into the LOADED_INF structure.
//
Inf->LanguageId = LanguageId;
if (Flags & LDINF_FLAG_OEM_F6_INF) {
Inf->Flags = LIF_OEM_F6_INF;
}
//
// 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) {
rc = SavePnf(Filename, Inf);
if(rc == NO_ERROR) {
PnfSaved = TRUE;
if(PnfWasUsed) {
*PnfWasUsed = TRUE;
}
} else if(((rc == ERROR_SHARING_VIOLATION)
|| ((rc == ERROR_LOCK_VIOLATION)))
&& (Flags & LDINF_FLAG_ALLOW_PNF_SHARING_LOCK)) {
//
// A sharing-type error occurred
// so a PNF exists and is in USE
// and we are flagged to indicate that it's non-fatal.
//
rc = NO_ERROR;
} else if(!(Flags & LDINF_FLAG_REGENERATE_PNF)) {
//
// We weren't explicitly asked to generate a PNF, thus
// our failure to do so shouldn't be considered fatal.
//
rc = NO_ERROR;
}
}
}
if(rc == NO_ERROR) {
if(AppendInf) {
Inf->Prev = InfListTail;
InfListTail->Next = Inf;
}
} else {
FreeInfFile(Inf);
}
}
}
clean0:
if(AppendInf) {
//
// If the newly-loaded INF has any volatile system DIRIDs, or 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 || Inf->Flags & LIF_HAS_VOLATILE_DIRIDS)) {
if((rc = ApplyNewVolatileDirIdsToInfs(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) {
//
// We're not append-loading the INF, thus there's no user-defined
// DIRID substitutions to worry about. However, if the INF has volatile
// system DIRIDs, then we still need to apply paths to those DIRIDs now
// (unless the caller said to skip it).
//
if((Inf->Flags & LIF_HAS_VOLATILE_DIRIDS) &&
!(Flags & LDINF_FLAG_IGNORE_VOLATILE_DIRIDS)) {
rc = ApplyNewVolatileDirIdsToInfs(Inf, NULL);
}
if(rc == NO_ERROR) {
*LoadedInf = Inf;
} else {
FreeInfFile(Inf);
}
}
if (rc == NO_ERROR) {
//
// log that the INF was loaded
//
WriteLogEntry(
LogContext,
SETUP_LOG_VVERBOSE,
(PnfUsed ? MSG_LOG_OPENED_PNF
: (PnfSaved ? MSG_LOG_SAVED_PNF : MSG_LOG_OPENED_INF)),
NULL,
Filename,
LanguageId);
}
if(InfSourcePathToMigrateMediaType != SPOST_NONE) {
if(InfSourcePathToMigrate) {
MyFree(InfSourcePathToMigrate);
}
if(InfOriginalNameToMigrate) {
MyFree(InfOriginalNameToMigrate);
}
}
DeleteLogContext(LogContext);
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 = MyTaggedRealloc((PVOID)(VersionNode->DataBlock), RequiredSpace, MEMTAG_VBDATA);
} else {
NewDataBlock = MyTaggedMalloc(RequiredSpace, MEMTAG_VBDATA);
}
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
//
/////////////////////////////////////////////
#ifdef UNICODE
BOOL
WINAPI
SetupEnumInfSectionsA (
IN HINF InfHandle,
IN UINT Index,
OUT PSTR Buffer, OPTIONAL
IN UINT Size, OPTIONAL
OUT UINT *SizeNeeded OPTIONAL
)
/*++
Routine Description:
See SetupEnumInfSections
Ansi Wrapper
--*/
{
UINT UniSize;
UINT AnsiSize;
BOOL f;
PWSTR UniBuffer;
PSTR AnsiBuffer;
DWORD rc;
f = SetupEnumInfSectionsW(InfHandle,Index,NULL,0,&UniSize);
if(!f) {
return FALSE;
}
UniBuffer = (PWSTR)MyMalloc(UniSize*sizeof(WCHAR));
if(!UniBuffer) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
f = SetupEnumInfSectionsW(InfHandle,Index,UniBuffer,UniSize,NULL);
if(!f) {
rc = GetLastError();
MYASSERT(f);
MyFree(UniBuffer);
SetLastError(rc);
return FALSE;
}
AnsiBuffer = pSetupUnicodeToAnsi(UniBuffer);
MyFree(UniBuffer);
if(!AnsiBuffer) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
AnsiSize = strlen(AnsiBuffer)+1;
try {
if(SizeNeeded) {
*SizeNeeded = AnsiSize;
}
if (Buffer) {
if(Size<AnsiSize) {
rc = ERROR_INSUFFICIENT_BUFFER;
} else {
strcpy(Buffer,AnsiBuffer);
rc = NO_ERROR;
}
} else if(Size) {
rc = ERROR_INVALID_USER_BUFFER;
} else {
rc = NO_ERROR;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Assume InfHandle was bad pointer
//
rc = ERROR_INVALID_DATA;
}
MyFree(AnsiBuffer);
SetLastError(rc);
return (rc == NO_ERROR);
}
#else
BOOL
WINAPI
SetupEnumInfSectionsW (
IN HINF InfHandle,
IN UINT Index,
OUT PWSTR Buffer, OPTIONAL
IN UINT Size, OPTIONAL
OUT UINT *SizeNeeded OPTIONAL
)
/*++
Routine Description:
See SetupEnumInfSections
Unicode Stub for ANSI SetupAPI
--*/
{
UNREFERENCED_PARAMETER(InfHandle);
UNREFERENCED_PARAMETER(Buffer);
UNREFERENCED_PARAMETER(Size);
UNREFERENCED_PARAMETER(SizeNeeded);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#endif
BOOL
WINAPI
SetupEnumInfSections (
IN HINF InfHandle,
IN UINT Index,
OUT PTSTR Buffer, OPTIONAL
IN UINT Size, OPTIONAL
OUT UINT *SizeNeeded OPTIONAL
)
/*++
Routine Description:
Enumerate Sections of a single INF (ignoring any attached INF's)
Start with Index==0 and keep incrementing Index until ERROR_NO_MORE_ITEMS
is returned.
section name is copied into Buffer.
Arguments:
InfHandle - Specifies the handle to an open INF file
Index - enumeration index, not related to order sections are in INF
Buffer - Receives a single section name
Size - Specifies the size of Buffer, in characters
SizeNeeded - Receives the size of Buffer needed, in characters
Return Value:
TRUE if the function succeeds, or FALSE if not.
--*/
{
DWORD rc = NO_ERROR;
LPTSTR section;
UINT actsz;
PLOADED_INF pInf = (PLOADED_INF)InfHandle;
try {
if(!LockInf(pInf)) {
rc = ERROR_INVALID_HANDLE;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Assume InfHandle was bad pointer
//
rc = ERROR_INVALID_HANDLE;
}
if (rc != NO_ERROR) {
SetLastError (rc);
return FALSE;
}
try {
if(Index >= pInf->SectionCount) {
rc = ERROR_NO_MORE_ITEMS;
leave;
}
section = pStringTableStringFromId(pInf->StringTable, pInf->SectionBlock[Index].SectionName);
if(section == NULL) {
MYASSERT(section);
rc = ERROR_INVALID_DATA;
leave;
}
actsz = lstrlen(section)+1;
if(SizeNeeded) {
*SizeNeeded = actsz;
}
if (Buffer) {
if(Size<actsz) {
rc = ERROR_INSUFFICIENT_BUFFER;
} else {
_tcscpy(Buffer,section);
rc = NO_ERROR;
}
} else if(Size) {
rc = ERROR_INVALID_USER_BUFFER;
} else {
rc = NO_ERROR;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Assume InfHandle was bad pointer
//
rc = ERROR_INVALID_DATA;
}
UnlockInf(pInf);
SetLastError(rc);
return (rc == NO_ERROR);
}
//
// NTRAID#207847-JamieHun-2000/10/19 Fix users of (p)SetupGetInfSections
//
// pSectionEnumWorker and pSetupGetInfSections are busted implementations
// of obtaining a list of INF sections
//
// we have to leave them in until all internal tools get updated
//
VOID
pSectionEnumWorker (
IN PCTSTR String,
IN OUT PSECTION_ENUM_PARAMS Params
)
/*++
Routine Description:
Callback that receives each section name. It copies the string
to a supplied buffer (if available), and also keeps track of the
total size regardless if a buffer was supplied.
Arguments:
String - Specifies the section name
Params - Specifies a pointer to a SECTION_ENUM_PARAMS structure.
Receives the section appended to the supplied buffer (if
necessary) and an updated total buffer size.
Return Value:
Always TRUE.
--*/
{
UINT Size;
if (!String) {
MYASSERT(FALSE);
return;
}
Size = (UINT)((PBYTE) _tcschr (String, 0) - (PBYTE) String) + sizeof(TCHAR);
Params->SizeNeeded += Size;
if (Params->Size > Params->SizeNeeded) {
if (Params->Buffer) {
_tcscpy (Params->End, String);
Params->End = _tcschr (Params->End, 0);
Params->End++;
}
}
}
BOOL
pSetupGetInfSections (
IN HINF InfHandle,
OUT PTSTR Buffer, OPTIONAL
IN UINT Size, OPTIONAL
OUT UINT *SizeNeeded OPTIONAL
)
/*++
Routine Description:
Make a multi-sz list of section names by enumerating the section
string table and copying them into a caller-supplied buffer.
Caller can also request the size needed without supplying a
buffer.
This function was implemented for the Win9x upgrade and is NOT
exposed as a public API nor an ANSI version.
Arguments:
Inf - Specifies the handle to an open INF file
Buffer - Receives a multi-sz list of section names
Size - Specifies the size of Buffer, in bytes
SizeNeeded - Receives the size of Buffer needed, in bytes
Return Value:
TRUE if the function succeeds, or FALSE if not.
--*/
{
PLOADED_INF Inf;
DWORD rc = NO_ERROR;
SECTION_ENUM_PARAMS Params;
PBYTE p;
PINF_SECTION Section;
UINT u;
//
// Init the enum worker params
//
Params.Buffer = Buffer;
Params.Size = Buffer ? Size : 0;
Params.SizeNeeded = 0;
Params.End = Buffer;
//
// Validate buffer arg
//
try {
if (Buffer) {
p = (PBYTE) Buffer;
p[0] = 0;
p[Size - 1] = 0;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
if (rc != NO_ERROR) {
SetLastError (rc);
return FALSE;
}
//
// Lock the INF
//
try {
if(!LockInf((PLOADED_INF)InfHandle)) {
rc = ERROR_INVALID_HANDLE;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Assume InfHandle was bad pointer
//
rc = ERROR_INVALID_HANDLE;
}
if (rc != NO_ERROR) {
SetLastError (rc);
return FALSE;
}
//
// Traverse the linked list of loaded INFs, enumerating each INF's
// sections.
//
try {
for(Inf = (PLOADED_INF)InfHandle; Inf; Inf = Inf->Next) {
//
// Enumerate the sections
//
for(u=0,Section=Inf->SectionBlock; u<Inf->SectionCount; u++,Section++) {
pSectionEnumWorker (
pStringTableStringFromId (Inf->StringTable, Section->SectionName),
&Params
);
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
//
// Update the structure and OUT params for the last time
//
try {
if (rc == NO_ERROR) {
Params.SizeNeeded += sizeof(TCHAR);
if (SizeNeeded) {
*SizeNeeded = Params.SizeNeeded;
}
if (Params.Buffer && Params.Size >= Params.SizeNeeded) {
*Params.End = 0;
} else if (Params.Buffer) {
rc = ERROR_INSUFFICIENT_BUFFER;
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
//
// Unlock the INF
//
UnlockInf((PLOADED_INF)InfHandle);
return rc == NO_ERROR;
}
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;
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,
NULL,0
);
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,
NULL,0
);
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 (first is case insensative)
//
ValueNumber++;
if(ValueNumber==0) {
//
// wrap
//
return NULL;
}
} else {
if(ISSEARCHABLE(InfLine)) {
//
// lines that consist of one value "VaLue" are treated like "value=VaLue"
// this is such a line, and is recognized because HASKEY is FALSE but
// ISSEARCHABLE is TRUE
//
// 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;
}
PVOID
InitializeStringTableFromPNF(
IN PPNF_HEADER PnfHeader,
IN LCID Locale
)
{
PVOID StringTable = NULL;
try {
StringTable = InitializeStringTableFromMemoryMappedFile(
(PUCHAR)PnfHeader + PnfHeader->StringTableBlockOffset,
PnfHeader->StringTableBlockSize,
Locale,
0
);
} except(EXCEPTION_EXECUTE_HANDLER) {
}
return StringTable;
}
BOOL
LoadPrecompiledInf(
IN PCTSTR Filename,
IN PFILETIME LastWriteTime,
IN PCTSTR OsLoaderPath, OPTIONAL
IN DWORD LanguageId,
IN DWORD Flags,
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
OUT PLOADED_INF *Inf,
OUT PTSTR *InfSourcePathToMigrate, OPTIONAL
OUT PDWORD InfSourcePathToMigrateMediaType, OPTIONAL
OUT PTSTR *InfOriginalNameToMigrate OPTIONAL
)
/*++
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_VOLATILE_DIRIDS flag is specified).
LanguageId - supplies the language ID that must match the language ID stored in the
PNF in order for the PNF to be used (ignored if LDINF_FLAG_IGNORE_LANGUAGE is
specified).
Flags - supplies flags that modify the behavior of this routine. The following
flags are currently recognized:
LDINF_FLAG_IGNORE_VOLATILE_DIRIDS - 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.
LDINF_FLAG_IGNORE_LANGUAGE - If specified, then no validation will be done on
the language ID stored in the PNF. This flag should only be used if no data
is to be retrieved from the INF (e.g., if we're just interested in finding
out if this is an old- or new-style INF).
LogContext - if supplied, is a log context to be inherited
Inf - supplies the address of the variable that receives the LOADED_INF pointer,
if a valid .PNF is located.
InfSourcePathToMigrate - Optionally, supplies the address of a string pointer
that receives the address of a newly-allocated string buffer containing
the source path associated with the INF's PNF that, while valid, was
discarded because of a change in one of the stored system parameters
(e.g., OS loader path, windir path, language ID). This parameter will
only be filled in upon unsuccessful return. The type of path returned
is dependent upon the value received by the InfSourcePathToMigrateMediaType
argument, described below. ** THE CALLER MUST FREE THIS STRING **
InfSourcePathToMigrateMediaType - Optionally, supplies the address of a
variable that will be set whenever InfSourcePathToMigrate is returned.
This value indicates the type of source path we're talking about. It
can be one of the following values:
SPOST_PATH - InfSourcePathToMigrate is a pointer to a standard file path
SPOST_URL - If InfSourcePathToMigrate is NULL, then this INF came from
the Windows Update (aka, Code Download Manager) website. Otherwise,
InfSourcePathToMigrate indicates the URL where the INF came from.
InfOriginalNameToMigrate - Optionally, supplies the address of a string pointer
that receives the address of a newly-allocated string buffer containing
the original name of the associated INF (sans path). Like the
InfSourcePathToMigrate and InfSourcePathToMigrateMediaType arguments
described above, this argument is only filled in upon unsuccessful return
for a PNF that, while structurally sound, was invalid because of a system
parameter mismatch. ** THE CALLER MUST FREE THIS STRING **
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;
BOOL TimeDateMatch = FALSE;
PPNF_HEADER PnfHeader;
PLOADED_INF NewInf;
BOOL NeedToDestroyLock, MinorVer1FieldsAvailable;
PBYTE PnfImageEnd;
DWORD TempStringLen;
DWORD err;
//
// Either InfSourcePathToMigrate, InfSourcePathToMigrateMediaType, and
// InfOriginalNameToMigrate must all be specified, or none of them may be
// specified.
//
MYASSERT((InfSourcePathToMigrate && InfSourcePathToMigrateMediaType && InfOriginalNameToMigrate) ||
!(InfSourcePathToMigrate || InfSourcePathToMigrateMediaType || InfOriginalNameToMigrate));
if(InfSourcePathToMigrate) {
*InfSourcePathToMigrate = NULL;
*InfSourcePathToMigrateMediaType = SPOST_NONE;
*InfOriginalNameToMigrate = NULL;
}
lstrcpyn(CharBuffer, Filename, SIZECHARS(CharBuffer));
//
// Find the start of the filename component of the path, and then find the last
// period (if one exists) in that filename.
//
PnfFileName = (PTSTR)pSetupGetFileTitle(CharBuffer);
if(!(PnfFileExt = _tcsrchr(PnfFileName, TEXT('.')))) {
PnfFileExt = CharBuffer + lstrlen(CharBuffer);
}
//
// Now create a corresponding filename with the extension '.PNF'
//
lstrcpyn(PnfFileExt, pszPnfSuffix, SIZECHARS(CharBuffer) - (int)(PnfFileExt - CharBuffer));
//
// Attempt to open and map the file into memory.
//
if(pSetupOpenAndMapFileForRead(CharBuffer,
&FileSize,
&FileHandle,
&MappingHandle,
&BaseAddress) != NO_ERROR) {
//
// Couldn't open a .PNF file--bail now.
//
return FALSE;
}
NewInf = NULL;
NeedToDestroyLock = FALSE;
MinorVer1FieldsAvailable = TRUE;
PnfImageEnd = (PBYTE)BaseAddress + FileSize;
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 we ever rev the major version, the logic below will need to change,
// as we'll need to migrate the INF source path information over, thus
// we can't bail so quickly.
//
MYASSERT(PNF_MAJOR_VERSION == 1);
if(HIBYTE(PnfHeader->Version) != PNF_MAJOR_VERSION) {
//
// A major version mismatch means the PNF is unusable (see note above
// about the need to migrate INF source path info in the future).
//
if(LogContext) {
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_VERSION_MAJOR_MISMATCH,
NULL,
PnfFileName,
PNF_MAJOR_VERSION,
HIBYTE(PnfHeader->Version)
);
}
goto clean0;
}
if(LOBYTE(PnfHeader->Version) != PNF_MINOR_VERSION) {
if(LogContext) {
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_VERSION_MINOR_MISMATCH,
NULL,
PnfFileName,
PNF_MINOR_VERSION,
LOBYTE(PnfHeader->Version)
);
}
if(LOBYTE(PnfHeader->Version) < PNF_MINOR_VERSION) {
//
// We're currently at minor version 1. PNFs having a minor
// version of 1 differ from those having a minor version of 0
// in the following ways:
//
// 1. Minor version 1 PNFs store the LanguageId in which the
// INF was precompiled. For Minor version 0 INFs, this field
// was initialized to zero. This will cause our check for
// LanguageId match to fail, thus we'll consider the PNF
// invalid.
//
// 2. Minor version 1 PNFs contain additional fields for
// InfSourcePathOffset and OriginalInfNameOffset. This means
// that the PNF_HEADER struct got longer, thus we can only
// use these fields for minor version 1 or greater PNFs.
//
MinorVer1FieldsAvailable = FALSE;
}
//
// (If the minor version of the PNF we're looking at is _greater_ than
// the version we currently support, then we should attempt to use
// this PNF, since all the fields that we care about should be right
// where we expect them to be.)
//
}
//
// The version information checks out--now check the last-write times.
// note that if we add any other consistancy checks to determine that this PNF
// is associated with the INF
// we must also modify simular tests for INF cache
//
TimeDateMatch = CompareFileTime(LastWriteTime, &(PnfHeader->InfVersionLastWriteTime))?FALSE:TRUE;
if (!TimeDateMatch && !(Flags&LDINF_FLAG_ALWAYS_GET_SRCPATH)) {
//
// Time&Date don't match, and we're not interested in always getting source path
//
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_TIMEDATE_MISMATCH,
NULL,
PnfFileName
);
goto clean0;
}
#ifdef UNICODE
if(!(PnfHeader->Flags & PNF_FLAG_IS_UNICODE))
#else
if(PnfHeader->Flags & PNF_FLAG_IS_UNICODE)
#endif
{
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_REBUILD_NATIVE,
NULL,
PnfFileName
);
//
// The APIs are Unicode while the PNF is ANSI, or vice versa. We
// still want to migrate the source path and original filename
// information, if present, so that we preserve this information
// across an upgrade from Win9x to NT, for example.
//
if(MinorVer1FieldsAvailable && InfSourcePathToMigrate) {
//
// First, retrieve the original INF name
//
if(PnfHeader->OriginalInfNameOffset) {
//
// Use strlen/wcslen so if an exception occurs it won't get
// swallowed...
//
#ifdef UNICODE
TempStringLen = strlen((PCSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset)) + 1;
TempStringLen *= sizeof(CHAR);
#else
TempStringLen = wcslen((PCWSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset)) + 1;
TempStringLen *= sizeof(WCHAR);
#endif
if(PnfImageEnd <
((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset + TempStringLen))
{
goto clean0;
}
//
// Looks like we have a good original INF name string. Now
// convert it to the native character width.
//
#ifdef UNICODE
*InfOriginalNameToMigrate =
pSetupMultiByteToUnicode((PCSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset),
CP_ACP
);
#else
*InfOriginalNameToMigrate =
pSetupUnicodeToMultiByte((PCWSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset),
CP_ACP
);
#endif
if(!*InfOriginalNameToMigrate) {
goto clean0;
}
}
//
// Next, retrieve the source path information
//
if(PnfHeader->InfSourcePathOffset) {
#ifdef UNICODE
TempStringLen = strlen((PCSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset)) + 1;
TempStringLen *= sizeof(CHAR);
#else
TempStringLen = wcslen((PCWSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset)) + 1;
TempStringLen *= sizeof(WCHAR);
#endif
if(PnfImageEnd <
((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset + TempStringLen))
{
goto clean0;
}
//
// Looks like we have a good source path string. Now convert
// it to the native character width.
//
#ifdef UNICODE
*InfSourcePathToMigrate =
pSetupMultiByteToUnicode((PCSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset),
CP_ACP
);
#else
*InfSourcePathToMigrate =
pSetupUnicodeToMultiByte((PCWSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset),
CP_ACP
);
#endif
if(!*InfSourcePathToMigrate) {
goto clean0;
}
if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
*InfSourcePathToMigrateMediaType = SPOST_URL;
} else {
*InfSourcePathToMigrateMediaType = SPOST_PATH;
}
} else if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
//
// No source path stored in the PNF, but the flag says it's
// a URL, thus it came from Windows Update.
//
*InfSourcePathToMigrateMediaType = SPOST_URL;
}
}
goto clean0;
}
//
// Make sure that the last data block is still within the file. This
// prevents us from opening up a corrupted (truncated) PNF, and thinking
// it's valid until later when we actually try to access data at an
// offset that's past the end of the file's mapped image.
//
if(PnfHeader->InfSubstValueCount) {
if(PnfImageEnd <
((PBYTE)BaseAddress + PnfHeader->InfSubstValueListOffset + (PnfHeader->InfSubstValueCount * sizeof(STRINGSUBST_NODE))))
{
WriteLogEntry(LogContext,
SETUP_LOG_ERROR,
MSG_LOG_PNF_CORRUPTED,
NULL,
PnfFileName
);
goto clean0;
}
} else if(MinorVer1FieldsAvailable && (PnfHeader->OriginalInfNameOffset)) {
//
// Use _tcslen so if an exception occurs it won't get swallowed...
//
TempStringLen = _tcslen((PCTSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset)) + 1;
if(PnfImageEnd <
((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset + (TempStringLen * sizeof(TCHAR))))
{
WriteLogEntry(LogContext,
SETUP_LOG_ERROR,
MSG_LOG_PNF_CORRUPTED,
NULL,
PnfFileName
);
goto clean0;
}
} else if(MinorVer1FieldsAvailable && (PnfHeader->InfSourcePathOffset)) {
//
// Use _tcslen so if an exception occurs it won't get swallowed...
//
TempStringLen = _tcslen((PCTSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset)) + 1;
if(PnfImageEnd <
((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset + (TempStringLen * sizeof(TCHAR))))
{
WriteLogEntry(LogContext,
SETUP_LOG_ERROR,
MSG_LOG_PNF_CORRUPTED,
NULL,
PnfFileName
);
goto clean0;
}
} else {
//
// Well, we didn't have a substitution block or a source path block,
// so the last block in the PNF is the value block.
//
if(PnfImageEnd <
((PBYTE)BaseAddress + PnfHeader->InfValueBlockOffset + PnfHeader->InfValueBlockSize))
{
WriteLogEntry(LogContext,
SETUP_LOG_ERROR,
MSG_LOG_PNF_CORRUPTED,
NULL,
PnfFileName
);
goto clean0;
}
}
//
// From this point forward, we appear to have a structurally sound PNF
// of the appropriate version and character width. Any failures
// encountered should cause us to return the INF source path information
// to the caller (if requested).
//
if (!TimeDateMatch) {
MYASSERT(Flags&LDINF_FLAG_ALWAYS_GET_SRCPATH);
//
// Time&Date don't match, but we've recovered old media
// we have to do this since on FAT/FAT32 the UT reported for a file
// will change every time system TZ is changed.
//
WriteLogEntry(LogContext,
SETUP_LOG_INFO,
MSG_LOG_PNF_REBUILD_TIMEDATE_MISMATCH,
NULL,
PnfFileName
);
goto clean1;
}
//
// Make sure that the language ID that this PNF was compiled for matches
// that of the current thread.
//
if(!(Flags & LDINF_FLAG_IGNORE_LANGUAGE) && ((DWORD)(PnfHeader->LanguageId) != LanguageId)) {
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_REBUILD_LANGUAGE_MISMATCH,
NULL,
PnfFileName,
LanguageId,
PnfHeader->LanguageId
);
goto clean1;
}
//
// 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.
//
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_REBUILD_WINDIR_MISMATCH,
NULL,
PnfFileName,
WindowsDirectory,
(PCTSTR)((PBYTE)BaseAddress + PnfHeader->WinDirPathOffset)
);
goto clean1;
}
if((PnfHeader->OsLoaderPathOffset) && !(Flags & LDINF_FLAG_IGNORE_VOLATILE_DIRIDS)) {
//
// 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.
//
err = pSetupGetOsLoaderDriveAndPath(FALSE, CharBuffer, SIZECHARS(CharBuffer), NULL);
if(err) {
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_REBUILD_OSLOADER_MISMATCH,
NULL,
PnfFileName,
TEXT("?"),
(PCTSTR)((PBYTE)BaseAddress + PnfHeader->OsLoaderPathOffset)
);
goto clean1;
}
OsLoaderPath = CharBuffer;
}
if(lstrcmpi((PCTSTR)((PBYTE)BaseAddress + PnfHeader->OsLoaderPathOffset), OsLoaderPath)) {
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_REBUILD_OSLOADER_MISMATCH,
NULL,
PnfFileName,
OsLoaderPath,
(PCTSTR)((PBYTE)BaseAddress + PnfHeader->OsLoaderPathOffset)
);
goto clean1;
}
}
//
// Make sure that we have verified whether this INF is digitally signed or not
//
if (!(PnfHeader->Flags & PNF_FLAG_INF_VERIFIED)) {
WriteLogEntry(LogContext,
SETUP_LOG_INFO,
MSG_LOG_PNF_REBUILD_UNVERIFIED,
NULL,
PnfFileName
);
goto clean1;
}
//
// Verify that the product suite flags match
// this causes us to refresh the PNF's if there's any change in product
// suite
// if on NT, PNF_FLAG_16BIT_SUITE must be set and upper 16 bits contains
// product suite
// if not on NT, PNF_FLAG_16BIT_SUITE must NOT be set.
//
if(((OSVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) &&
(PnfHeader->Flags & PNF_FLAG_16BIT_SUITE)) ||
((OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
(((PnfHeader->Flags & PNF_FLAG_16BIT_SUITE) == 0) ||
((((PnfHeader->Flags >> 16) & 0xffff)^OSVersionInfo.wSuiteMask) &
~(VER_SUITE_TERMINAL|VER_SUITE_SINGLEUSERTS))))) {
WriteLogEntry(LogContext,
SETUP_LOG_INFO,
MSG_LOG_PNF_REBUILD_SUITE,
NULL,
PnfFileName,
((PnfHeader->Flags >> 16) & 0xffff),
OSVersionInfo.wSuiteMask
);
goto clean1;
}
//
// 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) {
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_REBUILD_HASH_MISMATCH,
NULL,
PnfFileName,
HASH_BUCKET_COUNT,
PnfHeader->StringTableHashBucketCount
);
goto clean1;
}
//
// We can use the file--now set up our top level structures.
//
if(NewInf = MyTaggedMalloc(sizeof(LOADED_INF),MEMTAG_INF)) {
ZeroMemory(NewInf, sizeof(LOADED_INF));
if(NewInf->StringTable = InitializeStringTableFromPNF(PnfHeader, (LCID)LanguageId)) {
NewInf->LogContext = NULL;
if(InheritLogContext(LogContext, &(NewInf->LogContext)) == NO_ERROR) {
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);
if(PnfHeader->Flags & PNF_FLAG_HAS_VOLATILE_DIRIDS) {
NewInf->Flags |= LIF_HAS_VOLATILE_DIRIDS;
}
if (PnfHeader->Flags & PNF_FLAG_INF_DIGITALLY_SIGNED) {
NewInf->Flags |= LIF_INF_DIGITALLY_SIGNED;
}
if (PnfHeader->Flags & PNF_FLAG_OEM_F6_INF) {
NewInf->Flags |= LIF_OEM_F6_INF;
}
if (PnfHeader->Flags & PNF_FLAG_INF_AUTHENTICODE_SIGNED) {
NewInf->Flags |= LIF_INF_AUTHENTICODE_SIGNED;
}
NewInf->LanguageId = (DWORD)(PnfHeader->LanguageId);
//
// 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 (default
// to assuming local (i.e., non-internet) source location).
//
// At this point, we should only be dealing with minor version
// 1 or later PNFs.
//
MYASSERT(MinorVer1FieldsAvailable);
NewInf->InfSourceMediaType = SPOST_PATH;
if(PnfHeader->InfSourcePathOffset) {
NewInf->InfSourcePath = (PCTSTR)((PBYTE)BaseAddress +
PnfHeader->InfSourcePathOffset);
}
if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
NewInf->InfSourceMediaType = SPOST_URL;
}
//
// Now retrieve the INF's original filename, if present. If
// this field isn't present, then the INF's current filename
// is assumed to be the same as its original filename (e.g.,
// a system-supplied INF).
//
if(PnfHeader->OriginalInfNameOffset) {
NewInf->OriginalInfName = (PCTSTR)((PBYTE)BaseAddress +
PnfHeader->OriginalInfNameOffset);
}
//
// 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;
}
}
}
}
clean1:
if(!IsPnfFile && InfSourcePathToMigrate && MinorVer1FieldsAvailable) {
//
// Actually, this is a good PNF, just one that we can't use. The
// caller has requested that we return the original INF source path
// location and original INF filename, so that this information can
// be migrated to the new PNF that will be built to replace this
// one.
//
#ifndef ANSI_SETUPAPI
#ifdef _X86_
MYASSERT(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
if((Flags&LDINF_FLAG_ALWAYS_GET_SRCPATH) &&
((PnfHeader->Flags & PNF_FLAG_16BIT_SUITE) == 0) &&
(PnfHeader->OriginalInfNameOffset == 0) &&
(PnfHeader->InfSourcePathOffset == 0) &&
!pSetupInfIsFromOemLocation(Filename,TRUE)) {
PCTSTR title;
PCTSTR p;
PTSTR catname = NULL;
PSP_ALTPLATFORM_INFO_V2 pPlatform = NULL;
DWORD FixErr;
//
// if we're here
// we may need to work around a Win2k-Gold bug
//
// the bug is that if the timezone is changed
// Win2k looses OriginalInfNameOffset/InfSourcePathOffset
// which causes the INF to appear as unsigned
// when it's really signed
//
//
// get file title, of form:
// xxxx.INF
//
title = pSetupGetFileTitle(Filename);
//
// see if it's of form OEMxxxx.INF
//
p = title;
if(_wcsnicmp(p,TEXT("OEM"),3)!=0) {
goto clean0;
}
p+=3;
if(p[0] == TEXT('.')) {
//
// OEM.xxx (we're expecting a number before '.')
//
goto clean0;
}
while(p[0]>=TEXT('0')&&p[0]<=TEXT('9')) {
p++;
}
if((p-title) > 7) {
//
// we're expecting no more than 4 digits
//
goto clean0;
}
if(_wcsicmp(p,pszInfSuffix)!=0) {
//
// not OEMnnnn.INF
//
goto clean0;
}
//
// see if there's a catalog that shadows this INF
//
WriteLogEntry(LogContext,
SETUP_LOG_INFO,
MSG_LOG_PNF_WIN2KBUG,
NULL,
PnfFileName
);
//
// see if the INF has a catalog that validates it
//
if(!pSetupApplyExtension(title,pszCatSuffix,&catname)) {
//
// validate against any catalog
// this is safe since the INF will get checked
// again when saving as PNF
//
catname = NULL;
}
pPlatform = MyMalloc(sizeof(SP_ALTPLATFORM_INFO_V2));
//
// if pPlatform is NULL, we'll probably fail the other bits
// too so bail.
//
if(!pPlatform) {
goto clean0;
}
ZeroMemory(pPlatform, sizeof(SP_ALTPLATFORM_INFO_V2));
pPlatform->cbSize = sizeof(SP_ALTPLATFORM_INFO_V2);
pPlatform->Platform = VER_PLATFORM_WIN32_NT;
pPlatform->Flags = SP_ALTPLATFORM_FLAGS_VERSION_RANGE;
pPlatform->MajorVersion = VER_PRODUCTMAJORVERSION;
pPlatform->MinorVersion = VER_PRODUCTMINORVERSION;
pPlatform->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
pPlatform->FirstValidatedMajorVersion = 0;
pPlatform->FirstValidatedMinorVersion = 0;
FixErr = _VerifyFile(
LogContext,
NULL, // no VerifyContext to pass in
catname, // eg "OEMx.CAT"
NULL,0, // we're not verifying against another catalog image
title, // eg "mydisk.inf"
Filename, // eg "....\OEMx.INF"
NULL, // return: problem info
NULL, // return: problem file
FALSE, // has to be FALSE because we don't have full path
pPlatform, // alt platform info
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
NULL, // return: catalog file, full path
NULL, // return: number of catalogs considered
NULL, // return: digital signer
NULL, // return: signer version
NULL // return: WinVerifyTrust state data
);
if(catname) {
MyFree(catname);
}
if(pPlatform) {
MyFree(pPlatform);
}
if(FixErr != NO_ERROR) {
//
// failed, don't fake any information
//
goto clean0;
}
//
// at this point, pretend original name was "OEM.INF"
// and that files are located in A:\
// we'll see at the time the inf is parsed
// if it's signed or not
//
*InfSourcePathToMigrate = DuplicateString(TEXT("A:\\"));
if(!*InfSourcePathToMigrate) {
goto clean0;
}
*InfOriginalNameToMigrate = DuplicateString(TEXT("OEM.INF"));
if(!*InfOriginalNameToMigrate) {
MyFree(*InfSourcePathToMigrate);
*InfSourcePathToMigrate = NULL;
goto clean0;
}
*InfSourcePathToMigrateMediaType = SPOST_PATH;
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_PNF_WIN2KBUGFIX,
NULL,
PnfFileName
);
goto clean0;
}
#endif
#endif
if(PnfHeader->OriginalInfNameOffset) {
*InfOriginalNameToMigrate =
DuplicateString((PCTSTR)((PBYTE)BaseAddress + PnfHeader->OriginalInfNameOffset));
if(!*InfOriginalNameToMigrate) {
goto clean0;
}
}
if(PnfHeader->InfSourcePathOffset) {
*InfSourcePathToMigrate =
DuplicateString((PCTSTR)((PBYTE)BaseAddress + PnfHeader->InfSourcePathOffset));
if(!*InfSourcePathToMigrate) {
goto clean0;
}
if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
*InfSourcePathToMigrateMediaType = SPOST_URL;
} else {
*InfSourcePathToMigrateMediaType = SPOST_PATH;
}
} else if(PnfHeader->Flags & PNF_FLAG_SRCPATH_IS_URL) {
//
// No source path stored in the PNF, but the flag says it's
// a URL, thus it came from Windows Update.
//
*InfSourcePathToMigrateMediaType = SPOST_URL;
}
}
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) {
pStringTableDestroy(NewInf->StringTable);
}
if(NewInf->LogContext) {
DeleteLogContext(NewInf->LogContext);
}
MyTaggedFree(NewInf,MEMTAG_INF);
}
pSetupUnmapAndCloseFile(FileHandle, MappingHandle, BaseAddress);
}
return IsPnfFile;
}
DWORD
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:
If successful, the return value is NO_ERROR.
If failure, the return value is a Win32 error code indicating the reason for
failure.
--*/
{
TCHAR PnfFilePath[MAX_PATH];
PTSTR PnfFileName, PnfFileExt;
HANDLE hFile;
PNF_HEADER PnfHeader;
DWORD Offset, BytesWritten, WinDirPathLen, SourcePathLen, OsLoaderPathLen;
DWORD OriginalInfNameLen;
PVOID StringTableDataBlock;
DWORD Err;
PSP_ALTPLATFORM_INFO_V2 ValidationPlatform;
DWORD VerificationPolicyToUse;
DWORD InfSigErr;
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// To minimize our footprint in certain embedded scenarios, we refrain
// from generating PNFs. We also assume the INF is valid...
//
Inf->Flags |= LIF_INF_DIGITALLY_SIGNED;
return NO_ERROR;
}
lstrcpyn(PnfFilePath, Filename,SIZECHARS(PnfFilePath));
//
// Find the start of the filename component of the path, and then find the last
// period (if one exists) in that filename.
//
PnfFileName = (PTSTR)pSetupGetFileTitle(PnfFilePath);
if(!(PnfFileExt = _tcsrchr(PnfFileName, TEXT('.')))) {
PnfFileExt = PnfFilePath + lstrlen(PnfFilePath);
}
//
// Now create a corresponding filename with the extension '.PNF'
//
lstrcpyn(PnfFileExt, pszPnfSuffix, SIZECHARS(PnfFilePath) - (int)(PnfFileExt - PnfFilePath));
//
// NOTE: If there's already a PNF for this INF, we're going to blow it away.
// If we encounter a failure after successfully creating the file, we're going
// to delete the partial PNF, and there'll be no rollback to restore the old
// PNF. This is OK because if CreateFile succeeds, then we know we're going
// to be able to write out the PNF barring out-of-disk-space problems. For
// out-of-disk-space problems, there could be one of two causes:
//
// 1. The INF associated with the old PNF has gotten bigger, hence the PNF
// has gotten bigger. In this case, it's desirable that we blow away
// the old PNF because it's invalid for the INF anyway.
//
// 2. The INF is the same, but something else has changed that caused us to
// need to regenerate the PNF (e.g., code page changed). Given the
// present information stored in PNFs, such a change would not result in
// a significant size difference between the old and new PNFs. Thus, if
// the old PNF fit in the available disk space, then so would the new
// one. If this changes in the future (e.g., storing out a new PNF can
// result in substantially increasing its size), then we'll need to be
// careful about backing up the old PNF before attempting to write out
// the new one, in case we need to rollback.
//
hFile = CreateFile(PnfFilePath,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if(hFile == INVALID_HANDLE_VALUE) {
return GetLastError();
}
//
// Enclose the rest of the function in try/except, in case we hit an error while
// writing to the file.
//
Err = NO_ERROR;
ValidationPlatform = NULL;
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
if(Inf->HasStrings) {
PnfHeader.Flags |= PNF_FLAG_HAS_STRINGS;
}
if(Inf->InfSourceMediaType == SPOST_URL) {
PnfHeader.Flags |= PNF_FLAG_SRCPATH_IS_URL;
}
if(Inf->Flags & LIF_HAS_VOLATILE_DIRIDS) {
PnfHeader.Flags |= PNF_FLAG_HAS_VOLATILE_DIRIDS;
}
if (Inf->Flags & LIF_OEM_F6_INF) {
PnfHeader.Flags |= PNF_FLAG_OEM_F6_INF;
}
//
// if this is NT, save product suite
// this helps us, eg, catch migration from PER to PRO
// so that we can refresh PNF's
//
if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
PnfHeader.Flags |= (((DWORD)OSVersionInfo.wSuiteMask)<<16) | PNF_FLAG_16BIT_SUITE;
}
//
// We can only verify the digital signature of an INF file
// after the crypto DLLs have been registered.
//
if(!(GlobalSetupFlags & PSPGF_NO_VERIFY_INF)) {
TCHAR CatalogName[MAX_PATH];
TCHAR FullCatalogPath[MAX_PATH];
PTSTR p;
FullCatalogPath[0] = TEXT('\0');
//
// If this INF does not live in %windir%\inf, or specifies a
// CatalogFile= entry, then we don't want to do global validataion.
// In these cases, we want to validate against the CatalogFile=
// catalog.
//
// Note that if there is no CatalogFile= then FullCatalogPath[0]
// will still be set to TEXT('\0') which will cause us to do global
// validataion.
//
if(pSetupGetCatalogFileValue(&(Inf->VersionBlock),
CatalogName,
SIZECHARS(CatalogName),
NULL) &&
(CatalogName[0] != TEXT('\0'))) {
//
// The INF specified a CatalogFile= entry. If the INF is in
// a 3rd-party location (i.e., not in %windir%\Inf, then we'll
// use the full path to the catalog (it must be located in the
// same directory as the INF). If the INF is in %windir%\Inf,
// then we will look for an installed catalog having the same
// primary filename as the INF, with an extension of ".CAT".
//
if(pSetupInfIsFromOemLocation(Filename, TRUE)) {
//
// Construct full path to the catalog based on the location
// of the INF.
//
lstrcpyn(FullCatalogPath, Filename, SIZECHARS(FullCatalogPath));
p = (PTSTR)pSetupGetFileTitle(FullCatalogPath);
lstrcpyn(p,
CatalogName,
(int)(SIZECHARS(FullCatalogPath) - (p - FullCatalogPath))
);
} else {
//
// Construct simple filename of catalog based on INF's name
// (with .CAT extension)
//
lstrcpyn(FullCatalogPath,
pSetupGetFileTitle(Filename),
SIZECHARS(FullCatalogPath)
);
p = _tcsrchr(FullCatalogPath, TEXT('.'));
if(!p) {
//
// Should never happen, but if our INF file has no
// extension, simply append ".CAT".
//
p = FullCatalogPath + lstrlen(FullCatalogPath);
}
lstrcpyn(p,
pszCatSuffix,
(int)(SIZECHARS(FullCatalogPath) - (p - FullCatalogPath))
);
}
}
//
// Check if the INF digitally signed
//
IsInfForDeviceInstall(NULL,
NULL,
Inf,
NULL,
&ValidationPlatform,
&VerificationPolicyToUse,
NULL,
FALSE
);
InfSigErr = _VerifyFile(NULL,
NULL,
(*FullCatalogPath ? FullCatalogPath : NULL),
NULL,
0,
(Inf->OriginalInfName
? Inf->OriginalInfName
: pSetupGetFileTitle(Filename)),
Filename,
NULL,
NULL,
FALSE,
ValidationPlatform,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
NULL,
NULL,
NULL,
NULL,
NULL
);
if(InfSigErr == NO_ERROR) {
PnfHeader.Flags |= PNF_FLAG_INF_DIGITALLY_SIGNED;
Inf->Flags |= LIF_INF_DIGITALLY_SIGNED;
} else if((InfSigErr != ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH) &&
*FullCatalogPath &&
(VerificationPolicyToUse & DRIVERSIGN_ALLOW_AUTHENTICODE)) {
//
// We failed to verify using standard driver signing policy
// (and the failure wasn't due to an invalid osattribute). We
// can fallback to Authenticode signatures for this INF, so
// check for that now...
//
InfSigErr = _VerifyFile(NULL,
NULL,
FullCatalogPath,
NULL,
0,
(Inf->OriginalInfName
? Inf->OriginalInfName
: pSetupGetFileTitle(Filename)),
Filename,
NULL,
NULL,
FALSE,
ValidationPlatform,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK
| VERIFY_FILE_USE_AUTHENTICODE_CATALOG),
NULL,
NULL,
NULL,
NULL,
NULL
);
if((InfSigErr == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(InfSigErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// For the purposes of setting the "INF is signed" flag in
// the PNF, we don't care whether or not we've established
// that the user trusts the publisher. That will be taken
// care of later, if the user ever attempts to perform a
// device install using this INF.
//
PnfHeader.Flags |= (PNF_FLAG_INF_DIGITALLY_SIGNED |
PNF_FLAG_INF_AUTHENTICODE_SIGNED);
Inf->Flags |= (LIF_INF_DIGITALLY_SIGNED |
LIF_INF_AUTHENTICODE_SIGNED);
}
}
PnfHeader.Flags |= PNF_FLAG_INF_VERIFIED;
}
PnfHeader.Version = MAKEWORD(PNF_MINOR_VERSION, PNF_MAJOR_VERSION);
PnfHeader.StringTableHashBucketCount = HASH_BUCKET_COUNT;
PnfHeader.LanguageId = (WORD)(Inf->LanguageId);
//
// 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;
}
//
// followed by the original INF's filename (if supplied, this indicates
// the INF originally had a different name prior to being copied into
// the current location)...
//
if(Inf->OriginalInfName) {
PnfHeader.OriginalInfNameOffset = Offset;
OriginalInfNameLen = (lstrlen(Inf->OriginalInfName) + 1) * sizeof(TCHAR);
Offset += PNF_ALIGN_BLOCK(OriginalInfNameLen);
} else {
PnfHeader.OriginalInfNameOffset = 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)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == sizeof(PnfHeader));
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.WinDirPathOffset - Offset)) {
Offset = PnfHeader.WinDirPathOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, WindowsDirectory, WinDirPathLen, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == WinDirPathLen);
Offset += BytesWritten;
if(Inf->OsLoaderPath) {
if(AlignForNextBlock(hFile, PnfHeader.OsLoaderPathOffset - Offset)) {
Offset = PnfHeader.OsLoaderPathOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, Inf->OsLoaderPath, OsLoaderPathLen, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == OsLoaderPathLen);
Offset += BytesWritten;
}
if(AlignForNextBlock(hFile, PnfHeader.StringTableBlockOffset - Offset)) {
Offset = PnfHeader.StringTableBlockOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, StringTableDataBlock, PnfHeader.StringTableBlockSize, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == PnfHeader.StringTableBlockSize);
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfVersionDataOffset - Offset)) {
Offset = PnfHeader.InfVersionDataOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, Inf->VersionBlock.DataBlock, PnfHeader.InfVersionDataSize, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == PnfHeader.InfVersionDataSize);
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfSectionBlockOffset - Offset)) {
Offset = PnfHeader.InfSectionBlockOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, Inf->SectionBlock, PnfHeader.InfSectionBlockSize, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == PnfHeader.InfSectionBlockSize);
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfLineBlockOffset - Offset)) {
Offset = PnfHeader.InfLineBlockOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, Inf->LineBlock, PnfHeader.InfLineBlockSize, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == PnfHeader.InfLineBlockSize);
Offset += BytesWritten;
if(AlignForNextBlock(hFile, PnfHeader.InfValueBlockOffset - Offset)) {
Offset = PnfHeader.InfValueBlockOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, Inf->ValueBlock, PnfHeader.InfValueBlockSize, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == PnfHeader.InfValueBlockSize);
Offset += BytesWritten;
if(Inf->InfSourcePath) {
if(AlignForNextBlock(hFile, PnfHeader.InfSourcePathOffset - Offset)) {
Offset = PnfHeader.InfSourcePathOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, Inf->InfSourcePath, SourcePathLen, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == SourcePathLen);
Offset += BytesWritten;
}
if(Inf->OriginalInfName) {
if(AlignForNextBlock(hFile, PnfHeader.OriginalInfNameOffset - Offset)) {
Offset = PnfHeader.OriginalInfNameOffset;
} else {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile, Inf->OriginalInfName, OriginalInfNameLen, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == OriginalInfNameLen);
Offset += BytesWritten;
}
if(PnfHeader.InfSubstValueCount) {
if(!AlignForNextBlock(hFile, PnfHeader.InfSubstValueListOffset - Offset)) {
Err = GetLastError();
goto clean0;
}
if(!WriteFile(hFile,
Inf->SubstValueList,
PnfHeader.InfSubstValueCount * sizeof(STRINGSUBST_NODE),
&BytesWritten,
NULL)) {
Err = GetLastError();
goto clean0;
}
MYASSERT(BytesWritten == PnfHeader.InfSubstValueCount * sizeof(STRINGSUBST_NODE));
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_DATA;
}
CloseHandle(hFile);
if(ValidationPlatform) {
MyFree(ValidationPlatform);
}
if(Err != NO_ERROR) {
//
// Something went wrong--get rid of the file.
//
DeleteFile(PnfFilePath);
}
return Err;
}
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
ApplyNewVolatileDirIdsToInfs(
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 volatile system or 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;
PPARSE_CONTEXT ParseContext = NULL;
DWORD 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))) {
if(ParseContext) {
MyFree(ParseContext);
}
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().
//
if(!ParseContext) {
ParseContext = MyMalloc(sizeof(PARSE_CONTEXT));
if(!ParseContext) {
return ERROR_NOT_ENOUGH_MEMORY;
}
ZeroMemory(ParseContext,sizeof(PARSE_CONTEXT));
}
ParseContext->DoVolatileDirIds = 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),
NULL,0
);
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...
//
MyFree(ParseContext);
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;
}
}
if(ParseContext) {
MyFree(ParseContext);
}
return NO_ERROR;
}
BOOL
AlignForNextBlock(
IN HANDLE hFile,
IN DWORD ByteCount
)
/*++
Routine Description:
This routine writes out the requested number of zero bytes into the specified
file.
Arguments:
hFile - Supplies a handle to the file where the zero-valued bytes are to be
written.
ByteCount - Specifies the number of zero-valued bytes to write to the file.
Return Value:
If success, the return value is TRUE.
If failure, the return value is FALSE. Call GetLastError() to retrieve a
Win32 error code indicating the cause of the failure.
--*/
{
DWORD i, BytesWritten;
BYTE byte = 0;
MYASSERT(ByteCount < PNF_ALIGNMENT);
for(i = 0; i < ByteCount; i++) {
if(!WriteFile(hFile, &byte, sizeof(byte), &BytesWritten, NULL)) {
//
// LastError already set.
//
return FALSE;
}
MYASSERT(BytesWritten == sizeof(byte));
}
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];
PTSTR Buffer = NULL;
DWORD DataLen;
DWORD Type;
LONG Err;
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) {
Err = QueryRegistryValue(hKey,pszBootDir,&Buffer,&Type,&DataLen);
if(Err == NO_ERROR) {
lstrcpyn(CharBuffer,Buffer,SIZECHARS(CharBuffer));
MyFree(Buffer);
}
RegCloseKey(hKey);
}
if(Err != ERROR_SUCCESS) {
#ifdef UNICODE
//
// If we couldn't retrieve the 'BootDir' value, resort to using the
// OsSystemPartitionRoot
//
// root path is \\?\GLOBALROOT\<SystemPartition> not <BootDir>
// can't make assumption about BootDir
// so fail if we don't have that information
//
if(!OsSystemPartitionRoot) {
//
// if this is NULL at this point, we can't support this call
// most likely due to out of memory condition, so report as such
//
return ERROR_OUTOFMEMORY;
}
lstrcpyn(CharBuffer,OsSystemPartitionRoot,SIZECHARS(CharBuffer));
#else
//
// If we couldn't retrieve the 'BootDir' value, drop back to default of "C:\".
//
lstrcpyn(CharBuffer,pszDefaultSystemPartition,SIZECHARS(CharBuffer));
#endif
Err = NO_ERROR;
}
//
// If there is an OsLoader relative path, then concatenate it to our root path.
//
if(!RootOnly && OsLoaderRelativePath) {
pSetupConcatenatePaths(CharBuffer, OsLoaderRelativePath, SIZECHARS(CharBuffer), &DataLen);
} else {
DataLen = lstrlen(CharBuffer)+1;
}
if(RequiredSize) {
*RequiredSize = DataLen;
}
if(CallerBufferSize < DataLen) {
return ERROR_INSUFFICIENT_BUFFER;
}
CopyMemory(CallerBuffer, CharBuffer, DataLen * sizeof(TCHAR));
return NO_ERROR;
}