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.
 
 
 
 
 
 

915 lines
24 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
For Internal use only!
Module Name:
INFSCAN
installscan.cpp
Abstract:
Individual install section scanner class
main entry point InfScan::ScanInstallSection
WARNING! WARNING!
All of this implementation relies on intimate knowledge of
SetupAPI's SETUPAPI!SetupInstallFromInfSection
It is re-implemented here for speed due to having to process
700+ INF's in a single go at the cost of having to maintain
this.
DO NOT (I repeat) DO NOT re-implement the code here without
consultation with SetupAPI owner.
History:
Created July 2001 - JamieHun
--*/
#include "precomp.h"
#pragma hdrstop
InstallScan::InstallScan()
/*++
Routine Description:
Initialization
--*/
{
pGlobalScan = NULL;
pInfScan = NULL;
PlatformMask = 0;
pTargetDirectory = NULL;
NotDeviceInstall = false;
HasDependentFileChanged = false;
}
InstallScan::~InstallScan()
/*++
Routine Description:
Cleanup allocated data/handles
--*/
{
}
void InstallScan::AddHWIDs(const StringSet & hwids)
/*++
Routine Description:
Add to list of HWIDs effected by this section
--*/
{
StringSet::iterator i;
for(i = hwids.begin(); i != hwids.end(); i++) {
HWIDs.insert(*i);
}
}
void InstallScan::GetHWIDs(StringSet & hwids)
/*++
Routine Description:
Retrieve list of HWIDs effected by this section
--*/
{
StringSet::iterator i;
for(i = HWIDs.begin(); i != HWIDs.end(); i++) {
hwids.insert(*i);
}
}
int
InstallScan::ScanInstallSection()
/*++
Routine Description:
Main entry point for processing an install section
WARNING! tightly coupled with SetupAPI implementation that could change
MAINTAINANCE: keep this in step with supported Needs/nesting syntax
MAINTAINANCE: keep in step with install processing
Arguments:
NONE
Return Value:
0 on success
--*/
{
int res;
//
// prime initial list of search INF's
//
Layouts();
int count = 1; // no more than 1
res = RecurseKeyword(Section,TEXT("Include"),IncludeCallback,count);
if(res != 0) {
return res;
}
if(count<0) {
Fail(MSG_MULTIPLE_INCLUDE,Section);
} else if(Pedantic() && (count<1)) {
Fail(MSG_INCLUDE,Section);
}
count = 1; // no more than 1
res = RecurseKeyword(Section,TEXT("Needs"),NeedsCallback,count);
if(res != 0) {
return res;
}
if(count<0) {
Fail(MSG_MULTIPLE_NEEDS,Section);
} else if(Pedantic() && (count<1)) {
Fail(MSG_NEEDS,Section);
}
return CheckInstallSubSection(Section);
}
int
InstallScan::RecurseKeyword(const SafeString & sect,const SafeString & keyword,RecurseKeywordCallback callback,int & count)
/*++
Routine Description:
Section parsing workhorse
WARNING! Uses knowledge about how SetupAPI searches INF lists, could change
MAINTAINANCE: Keep search algorithm in step with SetupAPI
see aslo QuerySourceFile, GetLineCount and DoesSectionExist
given keyword = val[,val...]
each value is converted to lower-case and callback is called with information
about what inf/section the value is in.
Arguments:
sect - name of section to search
keyword - name of keyword
callback - callback function for each hit
count - must be initialized to max # of callbacks to invoke
- returns -1 if there are more entries than wanted
Return Value:
0 on success
--*/
{
INFCONTEXT context;
int res;
int i;
SafeString value;
//
// we didn't call SetupOpenAppendInf for any INF's, so instead
// enumerate all "included inf's" using our view of the world
// this allows us to reset our view for each install section
// to validate include/needs processing
//
ParseInfContextList::iterator AnInf;
for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
ParseInfContextBlob & TheInf = *AnInf;
if(TheInf->InfHandle == INVALID_HANDLE_VALUE) {
//
// a fail-loaded inf
//
continue;
}
if(!SetupFindFirstLine(TheInf->InfHandle,sect.c_str(),keyword.c_str(),&context)) {
//
// can't find (keyword in) section in this inf
//
continue;
}
do {
if(count>=0) {
count--;
if(count<0) {
return 0;
}
}
for(i=1;MyGetStringField(&context,i,value);i++) {
res = (this->*callback)(TheInf,sect,keyword,value);
if(res != 0) {
return res;
}
}
} while (SetupFindNextMatchLine(&context,keyword.c_str(),&context));
}
return 0;
}
int
InstallScan::IncludeCallback(ParseInfContextBlob & TheInf,const SafeString & sect,const SafeString & keyword,const SafeString & val)
/*++
Routine Description:
Callback for "INCLUDE" processing
Add INF to the search-list
MAINTAINANCE: keep this in step with supported Include/nesting syntax
Arguments:
TheInf - INF that the include was found in
sect - section that the include was found in
keyword - "INCLUDE"
val - name of INF to include
Return Value:
0 on success
--*/
{
if(!val.length()) {
return 0;
}
//
// simply 'append' the INF
//
return Include(val);
}
int
InstallScan::NeedsCallback(ParseInfContextBlob & TheInf,const SafeString & sect,const SafeString & keyword,const SafeString & val)
/*++
Routine Description:
Callback for "NEEDS" processing
process the specified needed section
MAINTAINANCE: keep this in step with supported Needs/nesting syntax
Arguments:
TheInf - INF that the needs was found in
sect - section that the needs was found in
keyword - "NEEDS"
val - name of section to process
Return Value:
0 on success
--*/
{
if(!val.length()) {
return 0;
}
INFCONTEXT context;
//
// concerning if section doesn't exist
//
if(!DoesSectionExist(val)) {
Fail(MSG_NEEDS_NOSECT,sect,val);
return 0;
}
//
// catch recursive includes/needs
// set want-count to zero so we don't actually recurse
// (we need to use RecurseKeyword to ensure correct searching)
//
int res;
int count = 0; // no more than zero (query)
res = RecurseKeyword(val,TEXT("Include"),IncludeCallback,count);
if(res != 0) {
return res;
}
if(count<0) {
Fail(MSG_RECURSIVE_INCLUDE,sect,val);
}
count = 0; // no more than zero (query)
res = RecurseKeyword(val,TEXT("Needs"),NeedsCallback,count);
if(res != 0) {
return res;
}
if(count<0) {
Fail(MSG_RECURSIVE_NEEDS,sect,val);
}
return CheckInstallSubSection(val);
}
int InstallScan::CheckInstallSubSection(const SafeString & section)
/*++
Routine Description:
Effectively SetupInstallInfSection
Processed for primary and each needed section
WARNING! tightly coupled with SetupAPI implementation that could change
MAINTAINANCE: keep in step with install processing
Arguments:
NONE
Return Value:
0 on success
--*/
{
int res;
res = CheckCopyFiles(section);
if(res != 0) {
return res;
}
return 0;
}
int InstallScan::CheckCopyFiles(const SafeString & section)
/*++
Routine Description:
Process CopyFiles entries in install section
WARNING! tightly coupled with SetupAPI implementation that could change
MAINTAINANCE: keep in step with CopyFiles processing
Arguments:
section - section to check for CopyFiles= entries
Return Value:
0 on success
--*/
{
//
// enumerate all CopyFiles entries in this section
//
int count = -1; // there can be any # of CopyFiles keywords in a section
return RecurseKeyword(section,TEXT("CopyFiles"),CopyFilesCallback,count);
}
int
InstallScan::CopyFilesCallback(ParseInfContextBlob & TheInf,const SafeString & section,const SafeString & keyword,const SafeString & val)
/*++
Routine Description:
Process CopyFiles entries in install section (callback)
WARNING! tightly coupled with SetupAPI implementation that could change
MAINTAINANCE: keep in step with CopyFiles processing
MAINTAINANCE: keep in step with CopyFiles section syntax
MAINTAINANCE: keep in step with DestinationDirs section syntax
Arguments:
TheInf - INF that the needs was found in
sect - section that the needs was found in
keyword - "CopyFiles"
val - section or @file
Return Value:
0 on success
--*/
{
if(!val.length()) {
return 0;
}
//
// a single copy-files section entry
//
INFCONTEXT context;
int res;
bool FoundAny = false;
if(val[0] == TEXT('@')) {
//
// immediate copy, use default target as specified in context inf
//
SafeString source = val.substr(1);
return CheckSingleCopyFile(TheInf,TheInf->GetDefaultTargetDirectory(),source,source,section);
}
//
// interpret as a section, we need to enumerate through all the INF's
// that have this particular copy section
//
ParseInfContextList::iterator AnInf;
for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
ParseInfContextBlob & TheCopyInf = *AnInf;
if(TheCopyInf->InfHandle == INVALID_HANDLE_VALUE) {
//
// a fail-loaded inf
//
continue;
}
DWORD flgs = TheCopyInf->DoingCopySection(val,PlatformMask);
if(flgs!=0) {
//
// we have already done this copy section in this INF
//
if(flgs != (DWORD)(-1)) {
FoundAny = true;
if(flgs & PLATFORM_MASK_MODIFIEDFILES) {
//
// hint that one of the files in this copy section
// was determined to be modified
//
HasDependentFileChanged = true;
}
}
continue;
}
//
// section contains multiple copy-files
//
if(!SetupFindFirstLine(TheCopyInf->InfHandle,val.c_str(),NULL,&context)) {
if(SetupGetLineCount(TheCopyInf->InfHandle, val.c_str()) != 0) {
//
// make a note that this inf doesn't have this copy section
//
TheCopyInf->NoCopySection(val);
} else {
FoundAny = true;
}
continue;
}
//
// we've found copy section inside TheCopyInf that we haven't
// previously processed for this platform
//
FoundAny = true;
//
// determine target directory for this copy section
// this must be in same inf as copy section
//
TargetDirectoryEntry *pTargDir = TheCopyInf->GetTargetDirectory(val);
SafeString target;
SafeString source;
//
// enumerate through each <desc> = <install>
//
do {
//
// each line consists of <target>[,<src>]
//
if(!MyGetStringField(&context,1,target) || !target.length()) {
continue;
}
if(!MyGetStringField(&context,2,source) || !source.length()) {
source = target;
}
//
// source/target always in lower-case to aid comparisons
//
res = CheckSingleCopyFile(TheCopyInf,pTargDir,target,source,val);
if(res != 0) {
return res;
}
} while(SetupFindNextLine(&context,&context));
}
if(!FoundAny) {
Fail(MSG_MISSING_COPY_SECTION,val,section);
}
return 0;
}
int InstallScan::CheckSingleCopyFile(ParseInfContextBlob & TheInf,TargetDirectoryEntry *pTargDir,const SafeString & target,const SafeString & source,const SafeString & section)
/*++
Routine Description:
Process a single CopyFile entry (either immediate or not)
WARNING! tightly coupled with SetupAPI implementation that could change
MAINTAINANCE: keep in step with CopyFiles processing
MAINTAINANCE: keep in step with CopyFiles section syntax
Arguments:
TheInf - Inf that the copy section is in
pTargDir - relevent target directory entry for this copy
target - name of target file
source - name of source file
section - name of copyfiles section, or of install section
Return Value:
0 on success
--*/
{
if(!target.length()) {
return 0;
}
if(!source.length()) {
return 0;
}
//
// this is a single copy target/source entry
//
// need to lookup file in SourceDisksFiles
// for matching platform
//
// for each file, need to
// (1) queue source name
// (2) textmode & guimode validate target
//
SourceDisksFilesList sources;
int res = QuerySourceFile(TheInf,section,source,sources);
if(res != 0) {
return res;
}
if(!sources.size()) {
return 0;
}
SafeString destdir;
if(pGlobalScan->TargetsToo) {
int IsDriverFile = NotDeviceInstall ? 0 : 1;
if(IsDriverFile) {
//
// make a special note that we determined at least once that this is a driver file
// wrt this primary INF
// (this saves us reporting that file is and is not a driver file for a given inf)
//
pInfScan->DriverSourceCheck.insert(source);
} else if(pInfScan->DriverSourceCheck.find(source) != pInfScan->DriverSourceCheck.end()) {
IsDriverFile = 1; // we found this to be a driver fill during driver pass
}
//
// we want a more detailed output for further analysis
// or import into database
//
basic_ostringstream<TCHAR> line;
//
// source name
//
line << QuoteIt(source);
//
// target location
//
if(pTargDir) {
line << TEXT(",") << pTargDir->DirId
<< TEXT(",") << QuoteIt(pTargDir->SubDir);
} else {
line << TEXT(",,");
}
//
// final name and if this appears to be a driver file or not
//
line << TEXT(",") << QuoteIt(target)
<< TEXT(",") << (NotDeviceInstall ? TEXT("0") : TEXT("1"));
//
// report primary INF
//
line << TEXT(",") << QuoteIt(GetFileNamePart(pInfScan->PrimaryInf->InfName));
if(!IsDriverFile) {
//
// if this doesn't appear to be a driver file, report install section
//
line << TEXT(",") << QuoteIt(Section);
}
pInfScan->SourceFiles.push_back(line.str());
} else {
//
// just source will do
//
pInfScan->SourceFiles.push_back(source);
}
if((pGlobalScan->BuildChangedDevices & BUILD_CHANGED_DEVICES_DEPCHANGED)
&& !pInfScan->HasDependentFileChanged
&& pGlobalScan->IsFileChanged(source)) {
HasDependentFileChanged = true;
//
// since we only do a copy section once, we need to make the copy section
// 'dirty' so next time we reference section out of here
//
TheInf->DoingCopySection(section,PLATFORM_MASK_MODIFIEDFILES);
}
if(pGlobalScan->IgnoreErrors) {
//
// if not interested in checking errors
// we're done
//
return 0;
}
SourceDisksFilesList::iterator i;
for(i = sources.begin(); i != sources.end(); i++) {
i->Used = true;
if((i->UpgradeDisposition != 3) || (i->TextModeDisposition != 3)) {
//
// need to do a consistency check
//
if(!pTargDir) {
//
// error already reported (?)
//
continue;
}
if(pTargDir->DirId != 10) {
//
// DirId should be 10 or normalized off 10
//
Fail(MSG_TEXTMODE_TARGET_MISMATCH,section,source);
continue;
}
if(!i->TargetDirectory) {
//
// error already reported
//
continue;
}
IntToString::iterator gd = pGlobalScan->GlobalDirectories.find(i->TargetDirectory);
if(gd == pGlobalScan->GlobalDirectories.end()) {
//
// no textmode subdir for specified dir
//
Fail(MSG_TEXTMODE_TARGET_UNKNOWN,section,source);
continue;
}
SafeString gm_target = PathConcat(pTargDir->SubDir,target);
SafeString tm_target = PathConcat(gd->second,i->TargetName.length() ? i->TargetName : source);
if(gm_target.compare(tm_target)!=0) {
Fail(MSG_TEXTMODE_TARGET_MISMATCH,section,source);
continue;
}
//
// get here, user-mode and text-mode match
//
}
}
return 0;
}
int InstallScan::Include(const SafeString & name)
/*++
Routine Description:
Process a single Include entry
WARNING! tightly coupled with SetupAPI implementation that could change
MAINTAINANCE: keep in step with INCLUDE processing
Arguments:
name - argument passed into INCLUDE
Return Value:
0 on success
--*/
{
//
// nested include
//
if(Included.find(name) == Included.end()) {
//
// first time this install section has come across this file
//
Included.insert(name); // so we don't try to re-load
ParseInfContextBlob & ThisInf = pInfScan->Include(name,true);
InfSearchList.push_back(ThisInf);
}
return 0;
}
int InstallScan::Layouts()
/*++
Routine Description:
Prime search list with primary inf and list of layout files
WARNING! tightly coupled with SetupAPI implementation that could change
MAINTAINANCE: keep in step with LAYOUT processing
DISCREPENCY: layout files list currently limited to layout.inf
Arguments:
NONE
Return Value:
0 on success
--*/
{
//
// start the search list with the default inf
//
Included.insert(pInfScan->PrimaryInf->InfName);
InfSearchList.push_back(pInfScan->PrimaryInf);
//
// start the search list with layout inf's
//
// we can do this with pGlobalScan as the data is readable but not modified
// there is an interlocked dependency on multi-processors
//
ParseInfContextList::iterator i;
for(i = pGlobalScan->LayoutInfs.begin(); i != pGlobalScan->LayoutInfs.end(); i++) {
Included.insert((*i)->InfName); // so we don't try to re-load
InfSearchList.push_back(*i); // interlocked dependency
}
return 0;
}
int InstallScan::QuerySourceFile(ParseInfContextBlob & TheInf,const SafeString & section,const SafeString & source,SourceDisksFilesList & Target)
/*++
Routine Description:
Determine list of possible source entries for given source file
This can be zero (bad layout)
1 (typical)
>1 (multiple targets)
Arguments:
TheInf - INF of the copy section, or install section
section - name of the copy section or install section (for error reporting only)
source - name of source file
Target - returned list of source media information
Return Value:
0 on success
--*/
{
int res =0;
Target.clear();
//
// return list of entries matching source file
//
DWORD platforms = pGlobalScan->Version.PlatformMask & PlatformMask & (PLATFORM_MASK_NT|PLATFORM_MASK_WIN);
//
// attempt to search within context of specified inf
//
res = TheInf->QuerySourceFile(platforms,section,source,Target);
if(res != 0) {
if(res<0) {
//
// non-fatal
//
res = 0;
}
return res;
}
if(Target.empty()) {
//
// now attempt to search within context of any inf's
//
ParseInfContextList::iterator AnInf;
for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
ParseInfContextBlob & TheLayoutInf = *AnInf;
if(TheLayoutInf->InfHandle == INVALID_HANDLE_VALUE) {
//
// a fail-loaded inf
//
continue;
}
res = TheLayoutInf->QuerySourceFile(platforms,section,source,Target);
if(res != 0) {
if(res<0) {
//
// non-fatal
//
res = 0;
}
return res;
}
if(!Target.empty()) {
//
// found a hit
//
break;
}
}
}
if(Target.empty()) {
//
// not found
//
Fail(MSG_SOURCE_NOT_LISTED,section,source);
return 0;
}
return 0;
}
LONG InstallScan::GetLineCount(const SafeString & section)
/*++
Routine Description:
Simulates SetupGetLineCount
WARNING! Uses knowledge about how SetupAPI searches INF lists, could change
MAINTAINANCE: Keep search algorithm in step with SetupAPI
reference RecurseKeyword
Arguments:
section - name of section to count
Return Value:
# of lines or -1 if no sections found
--*/
{
INFCONTEXT context;
int res;
int i;
SafeString value;
LONG count = -1;
ParseInfContextList::iterator AnInf;
for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
ParseInfContextBlob & TheInf = *AnInf;
if(TheInf->InfHandle == INVALID_HANDLE_VALUE) {
//
// a fail-loaded inf
//
continue;
}
LONG actcount = SetupGetLineCount(TheInf->InfHandle,section.c_str());
if(actcount >= 0) {
if(count<=0) {
count = actcount;
} else {
count += actcount;
}
}
}
return 0;
}
bool InstallScan::DoesSectionExist(const SafeString & section)
/*++
Routine Description:
Optimally simulates "SetupGetLineCount()>=0"
WARNING! Uses knowledge about how SetupAPI searches INF lists, could change
MAINTAINANCE: Keep search algorithm in step with SetupAPI
reference SetupGetLineCount and RecurseKeyword
Arguments:
section - name of section to count
Return Value:
true if named section exists
--*/
{
//
// optimized version of (GetLineCount(section)>=0)
//
INFCONTEXT context;
int res;
int i;
SafeString value;
ParseInfContextList::iterator AnInf;
for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
ParseInfContextBlob & TheInf = *AnInf;
if(TheInf->InfHandle == INVALID_HANDLE_VALUE) {
//
// a fail-loaded inf
//
continue;
}
LONG actcount = SetupGetLineCount(TheInf->InfHandle,section.c_str());
if(actcount >= 0) {
return true;
}
}
return false;
}