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.
 
 
 
 
 
 

14713 lines
592 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
devinstd.c
Abstract:
Default install handlers for SetupDiCallClassInstaller DIF_* functions.
Author:
Lonny McMichael (lonnym) 1-Aug-1995
Revision History:
Jamie Hunter (jamiehun) 20-January-1998 Added backup functionality in
_SetupDiInstallDevice - DI_FLAGSEX_BACKUPOLDFILES
--*/
#include "precomp.h"
#pragma hdrstop
//
// Global strings for use inside this file only.
//
CONST TCHAR pszDisplayName[] = INFSTR_KEY_DISPLAYNAME,
pszServiceType[] = INFSTR_KEY_SERVICETYPE,
pszStartType[] = INFSTR_KEY_STARTTYPE,
pszErrorControl[] = INFSTR_KEY_ERRORCONTROL,
pszServiceBinary[] = INFSTR_KEY_SERVICEBINARY,
pszLoadOrderGroup[] = INFSTR_KEY_LOADORDERGROUP,
pszDependencies[] = INFSTR_KEY_DEPENDENCIES,
pszStartName[] = INFSTR_KEY_STARTNAME,
pszSystemRoot[] = TEXT("%SystemRoot%\\"),
pszSecurity[] = INFSTR_KEY_SECURITY,
pszDescription[] = INFSTR_KEY_DESCRIPTION;
//
// Define the various copy scenarios that can be returned from GetNewInfName().
//
typedef enum _NEWINF_COPYTYPE {
NewInfCopyYes, // new INF placeholder created--need to copy real INF
NewInfCopyNo, // no need to copy--INF already present in destination
NewInfCopyZeroLength // previously-existing zero-length INF match found
} NEWINF_COPYTYPE, *PNEWINF_COPYTYPE;
//
// Define a list node to hold an interface class to be installed.
//
typedef struct _INTERFACE_CLASS_TO_INSTALL {
struct _INTERFACE_CLASS_TO_INSTALL *Next;
GUID InterfaceGuid;
DWORD Flags;
TCHAR InstallSection[MAX_SECT_NAME_LEN];
} INTERFACE_CLASS_TO_INSTALL, *PINTERFACE_CLASS_TO_INSTALL;
//
// Define a list node to hold an INF that has been newly-installed during
// SetupDiInstallDevice, hence must be cleaned up if SetupDiInstallDevice
// subsequently encounters a failure.
//
typedef struct _INSTALLED_INF_CLEANUP {
struct _INSTALLED_INF_CLEANUP *Next;
TCHAR InfName[MAX_PATH];
} INSTALLED_INF_CLEANUP, *PINSTALLED_INF_CLEANUP;
//
// Private function prototypes
//
BOOL
_SetupDiInstallDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall
);
DWORD
InstallHW(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HINF hDeviceInf,
IN PCTSTR szSectionName,
OUT PBOOL DeleteDevKey
);
BOOL
AssociateDevInstWithDefaultService(
IN PDEVINFO_ELEM DevInfoElem,
OUT PTSTR ServiceName,
IN OUT PDWORD ServiceNameSize
);
BOOL
CheckIfDevStarted(
IN PDEVINFO_ELEM DevInfoElem,
IN PDEVICE_INFO_SET pDeviceInfoSet
);
DWORD
pSetupAddService(
IN PINFCONTEXT LineContext,
OUT PSVCNAME_NODE * SvcListHead,
IN DWORD Flags,
IN DEVINST DevInst, OPTIONAL
OUT PBOOL NullDriverInstalled,
IN PSETUP_LOG_CONTEXT LogContext
);
DWORD
pSetupDeleteService(
IN PINFCONTEXT LineContext,
IN DWORD Flags,
IN PSETUP_LOG_CONTEXT LogContext
);
DWORD
DeleteServicesInList(
IN PSVCNAME_NODE ServicesToDelete,
IN PSETUP_LOG_CONTEXT LogContext
);
BOOL
IsDevRemovedFromAllHwProfiles(
IN PCTSTR DeviceInstanceId,
IN HMACHINE hMachine
);
DWORD
GetDevInstConfigFlags(
IN DEVINST DevInst,
IN DWORD Default,
IN HMACHINE hMachine
);
PTSTR
pSetupCmdLineAppendString(
IN PTSTR CmdLine,
IN PCTSTR Key,
IN PCTSTR Value, OPTIONAL
IN OUT PUINT StrLen,
IN OUT PUINT BufSize
);
BOOL
_SetupDiInstallDeviceInterfaces(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall,
IN HINF hDeviceInf, OPTIONAL
IN HSPFILEQ UserFileQ OPTIONAL
);
BOOL
_SetupDiRegisterCoDeviceInstallers(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall,
IN HINF hDeviceInf, OPTIONAL
IN HSPFILEQ UserFileQ OPTIONAL
);
BOOL
RetrieveAllDevNodesSharingDriversWithDevice(
IN PDEVINFO_ELEM DevInfoElem,
OUT PTSTR *Drivers,
OUT PDEVNODE *DevNodes,
OUT ULONG *NumberOfDevNodes,
IN HMACHINE hMachine
);
VOID
RestartSingleDevice(
IN PDEVINFO_ELEM DevInfoElem,
IN PDEVICE_INFO_SET pDeviceInfoSet,
IN BOOL NullDriverInstall,
IN PSETUP_LOG_CONTEXT LogContext
);
VOID
RestartAllDevicesUsingDrivers(
IN PDEVINFO_ELEM DevInfoElem,
IN PDEVICE_INFO_SET pDeviceInfoSet,
IN BOOL NullDriverInstall,
IN PSETUP_LOG_CONTEXT LogContext
);
DWORD
GetNewInfName(
IN HWND Owner, OPTIONAL
IN PCTSTR OemInfName,
IN PCTSTR OemInfOriginalName,
IN PCTSTR OemInfCatName, OPTIONAL
OUT PTSTR NewInfName,
IN DWORD NewInfNameSize,
OUT PDWORD RequiredSize,
OUT PNEWINF_COPYTYPE CopyNeeded,
IN BOOL ReplaceOnly,
IN PCTSTR DeviceDesc, OPTIONAL
IN DWORD DriverSigningPolicy,
IN DWORD Flags,
IN PCTSTR AltCatalogFile, OPTIONAL
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
OUT PDWORD DriverSigningError, OPTIONAL
OUT PTSTR CatalogFilenameOnSystem,
IN PSETUP_LOG_CONTEXT LogContext,
IN OUT PVERIFY_CONTEXT VerifyContext, OPTIONAL
OUT HANDLE *hWVTStateData OPTIONAL
);
VOID
_WriteVetoLogEntry(
IN PSETUP_LOG_CONTEXT LogContext,
IN DWORD Level,
IN DWORD MessageId,
IN PCTSTR szDevID,
IN PCTSTR szVetoName,
IN PNP_VETO_TYPE dwVetoType
)
{
static LPCTSTR VetoMap[] = {
TEXT("PNP_VetoTypeUnknown"),
TEXT("PNP_VetoLegacyDevice"),
TEXT("PNP_VetoPendingClose"),
TEXT("PNP_VetoWindowsApp"),
TEXT("PNP_VetoWindowsService"),
TEXT("PNP_VetoOutstandingOpen"),
TEXT("PNP_VetoDevice"),
TEXT("PNP_VetoDriver"),
TEXT("PNP_VetoIllegalDeviceRequest"),
TEXT("PNP_VetoInsufficientPower"),
TEXT("PNP_VetoNonDisableable"),
TEXT("PNP_VetoLegacyDriver"),
TEXT("PNP_VetoInsufficientRights")
};
MYASSERT(dwVetoType < ARRAYSIZE(VetoMap));
WriteLogEntry(
LogContext,
Level,
MessageId,
NULL,
szDevID,
szVetoName,
dwVetoType,
dwVetoType < ARRAYSIZE(VetoMap) ? VetoMap[dwVetoType] : TEXT("????")
);
}
LPCTSTR
_MapCmProbToString(
IN DWORD prob
)
{
#define PROBLEM_MAP_SIZE 0x33
#if PROBLEM_MAP_SIZE != NUM_CM_PROB
#error Add new problem code to ProbMap and update PROBLEM_MAP_SIZE.
#endif
static LPCTSTR ProbMap [PROBLEM_MAP_SIZE] = {
NULL,
TEXT("CM_PROB_NOT_CONFIGURED"),
TEXT("CM_PROB_DEVLOADER_FAILED"),
TEXT("CM_PROB_OUT_OF_MEMORY"),
TEXT("CM_PROB_ENTRY_IS_WRONG_TYPE"),
TEXT("CM_PROB_LACKED_ARBITRATOR"),
TEXT("CM_PROB_BOOT_CONFIG_CONFLICT"),
TEXT("CM_PROB_FAILED_FILTER"),
TEXT("CM_PROB_DEVLOADER_NOT_FOUND"),
TEXT("CM_PROB_INVALID_DATA"),
TEXT("CM_PROB_FAILED_START"),
TEXT("CM_PROB_LIAR"),
TEXT("CM_PROB_NORMAL_CONFLICT"),
TEXT("CM_PROB_NOT_VERIFIED"),
TEXT("CM_PROB_NEED_RESTART"),
TEXT("CM_PROB_REENUMERATION"),
TEXT("CM_PROB_PARTIAL_LOG_CONF"),
TEXT("CM_PROB_UNKNOWN_RESOURCE"),
TEXT("CM_PROB_REINSTALL"),
TEXT("CM_PROB_REGISTRY"),
NULL,
TEXT("CM_PROB_WILL_BE_REMOVED"),
TEXT("CM_PROB_DISABLED"),
TEXT("CM_PROB_DEVLOADER_NOT_READY"),
TEXT("CM_PROB_DEVICE_NOT_THERE"),
TEXT("CM_PROB_MOVED"),
TEXT("CM_PROB_TOO_EARLY"),
TEXT("CM_PROB_NO_VALID_LOG_CONF"),
TEXT("CM_PROB_FAILED_INSTALL"),
TEXT("CM_PROB_HARDWARE_DISABLED"),
TEXT("CM_PROB_CANT_SHARE_IRQ"),
TEXT("CM_PROB_FAILED_ADD"),
TEXT("CM_PROB_DISABLED_SERVICE"),
TEXT("CM_PROB_TRANSLATION_FAILED"),
TEXT("CM_PROB_NO_SOFTCONFIG"),
TEXT("CM_PROB_BIOS_TABLE"),
TEXT("CM_PROB_IRQ_TRANSLATION_FAILED"),
TEXT("CM_PROB_FAILED_DRIVER_ENTRY"),
TEXT("CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD"),
TEXT("CM_PROB_DRIVER_FAILED_LOAD"),
TEXT("CM_PROB_DRIVER_SERVICE_KEY_INVALID"),
TEXT("CM_PROB_LEGACY_SERVICE_NO_DEVICES"),
TEXT("CM_PROB_DUPLICATE_DEVICE"),
TEXT("CM_PROB_FAILED_POST_START"),
TEXT("CM_PROB_HALTED"),
TEXT("CM_PROB_PHANTOM"),
TEXT("CM_PROB_SYSTEM_SHUTDOWN"),
TEXT("CM_PROB_HELD_FOR_EJECT"),
TEXT("CM_PROB_DRIVER_BLOCKED"),
TEXT("CM_PROB_REGISTRY_TOO_LARGE"),
TEXT("CM_PROB_SETPROPERTIES_FAILED")
};
LPCTSTR ProbText = NULL;
MYASSERT(ARRAYSIZE(ProbMap)==NUM_CM_PROB);
MYASSERT(prob<NUM_CM_PROB);
if(prob < ARRAYSIZE(ProbMap)) {
ProbText = ProbMap[prob];
}
if(!ProbText) {
ProbText = TEXT("????");
}
return ProbText;
}
LPCTSTR
_MapCmRetToString(
IN DWORD cr
)
{
LPCTSTR RetText = NULL;
static LPCTSTR RetMap [] = {
TEXT("CR_SUCCESS"),
TEXT("CR_DEFAULT"),
TEXT("CR_OUT_OF_MEMORY"),
TEXT("CR_INVALID_POINTER"),
TEXT("CR_INVALID_FLAG"),
TEXT("CR_INVALID_DEVNODE"),
TEXT("CR_INVALID_RES_DES"),
TEXT("CR_INVALID_LOG_CONF"),
TEXT("CR_INVALID_ARBITRATOR"),
TEXT("CR_INVALID_NODELIST"),
TEXT("CR_DEVNODE_HAS_REQS"),
TEXT("CR_INVALID_RESOURCEID"),
TEXT("CR_DLVXD_NOT_FOUND"),
TEXT("CR_NO_SUCH_DEVNODE"),
TEXT("CR_NO_MORE_LOG_CONF"),
TEXT("CR_NO_MORE_RES_DES"),
TEXT("CR_ALREADY_SUCH_DEVNODE"),
TEXT("CR_INVALID_RANGE_LIST"),
TEXT("CR_INVALID_RANGE"),
TEXT("CR_FAILURE"),
TEXT("CR_NO_SUCH_LOGICAL_DEV"),
TEXT("CR_CREATE_BLOCKED"),
TEXT("CR_NOT_SYSTEM_VM"),
TEXT("CR_REMOVE_VETOED"),
TEXT("CR_APM_VETOED"),
TEXT("CR_INVALID_LOAD_TYPE"),
TEXT("CR_BUFFER_SMALL"),
TEXT("CR_NO_ARBITRATOR"),
TEXT("CR_NO_REGISTRY_HANDLE"),
TEXT("CR_REGISTRY_ERROR"),
TEXT("CR_INVALID_DEVICE_ID"),
TEXT("CR_INVALID_DATA"),
TEXT("CR_INVALID_API"),
TEXT("CR_DEVLOADER_NOT_READY"),
TEXT("CR_NEED_RESTART"),
TEXT("CR_NO_MORE_HW_PROFILES"),
TEXT("CR_DEVICE_NOT_THERE"),
TEXT("CR_NO_SUCH_VALUE"),
TEXT("CR_WRONG_TYPE"),
TEXT("CR_INVALID_PRIORITY"),
TEXT("CR_NOT_DISABLEABLE"),
TEXT("CR_FREE_RESOURCES"),
TEXT("CR_QUERY_VETOED"),
TEXT("CR_CANT_SHARE_IRQ"),
TEXT("CR_NO_DEPENDENT"),
TEXT("CR_SAME_RESOURCES"),
TEXT("CR_NO_SUCH_REGISTRY_KEY"),
TEXT("CR_INVALID_MACHINENAME"),
TEXT("CR_REMOTE_COMM_FAILURE"),
TEXT("CR_MACHINE_UNAVAILABLE"),
TEXT("CR_NO_CM_SERVICES"),
TEXT("CR_ACCESS_DENIED"),
TEXT("CR_CALL_NOT_IMPLEMENTED"),
TEXT("CR_INVALID_PROPERTY"),
TEXT("CR_DEVICE_INTERFACE_ACTIVE"),
TEXT("CR_NO_SUCH_DEVICE_INTERFACE"),
TEXT("CR_INVALID_REFERENCE_STRING"),
TEXT("CR_INVALID_CONFLICT_LIST"),
TEXT("CR_INVALID_INDEX"),
TEXT("CR_INVALID_STRUCTURE_SIZE")
};
MYASSERT(ARRAYSIZE(RetMap)==NUM_CR_RESULTS);
MYASSERT(cr<NUM_CR_RESULTS);
if(cr < ARRAYSIZE(RetMap)) {
RetText = RetMap[cr];
}
if(!RetText) {
RetText = TEXT("????");
}
return RetText;
}
BOOL
WINAPI
SetupDiInstallDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_INSTALLDEVICE
This routine will install a device by performing a SetupInstallFromInfSection
for the install section of the selected driver for the specified device
information element. The device will then be started (if possible).
NOTE: This API actually supports an OS/architecture-specific extension that
may be used to specify multiple installation behaviors for a single device,
based on the environment we're running under. The algorithm is as follows:
We take the install section name, as specified in the driver node (for this
example, it's "InstallSec"), and attempt to find one of the following INF
sections (searched for in the order specified):
If we're running under Windows 95:
1. InstallSec.Win
2. InstallSec
If we're running under Windows NT:
1. InstallSec.NT<platform> (platform is "x86", "AMD64", or "IA64")
2. InstallSec.NT
3. InstallSec
The first section that we find is the one we'll use to do the installation. This
section name is also what we'll base our ".Hw" and ".Services" installation against.
(E.g., if we match on "InstallSec.NTAmd64", then the service install section must be
named "InstallSec.NTAmd64.Services".)
The original install section name (i.e., the one specified in the driver node), will
be written as-is to the driver key's "InfSection" value entry, just as it was in the
past. The extension that we use (if any) will be stored in the device's driver key
as the REG_SZ value, "InfSectionExt". E.g.,
InfSection : REG_SZ : "InstallSec"
InfSectionExt : REG_SZ : ".NTMIPS"
If we successfully install the device, we'll kick off RunOnce. NOTE: We
must do this _regardless_ of whether or not the device was dynamically
brought on-line with its newly-installed driver/settings. This is because,
for server-side device installations, the RunOnce processing is done by the
user-mode PnP manager. We can't put this off until 'later'. Also, since
anyone can kick off a RunOnce at any time, you really would have no
assurance that the RunOnce would be postponed until after reboot anyway.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
driver is to be installed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for which
a driver is to be installed. This is an IN OUT parameter, since the
DevInst field of the structure may be updated with a new handle value upon
return.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
If no driver is selected for the specified device information element, then a
NULL driver will be installed.
Upon return, the install parameters Flags will indicate whether the system
needs to be rebooted or restarted in order for the device to be started.
During GUI-mode setup on Windows NT, quiet-install behavior is always
employed in the absence of a user-supplied file queue, regardless of
whether the device information element has the DI_QUIETINSTALL flag set.
--*/
{
return _SetupDiInstallDevice(DeviceInfoSet, DeviceInfoData, TRUE);
}
BOOL
_SetupDiInstallDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall
)
/*++
Routine Description:
Worker routine for both SetupDiInstallDevice and SetupDiInstallDriverFiles.
See the description of SetupDiInstallDevice for more information.
(jamiehun) If backups are enabled (DI_FLAGSEX_BACKUPOLDFILES or DI_FLAGSEX_BACKUPONREPLACE)
then old inf file is backed up.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
driver is to be installed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for which
a driver is to be installed. This is an IN OUT parameter, since the
DevInst field of the structure may be updated with a new handle value upon
return.
DoFullInstall - If TRUE, then an entire device installation is performed,
otherwise, only the driver files are copied.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err, ScanQueueResult;
PDEVINFO_ELEM DevInfoElem = NULL;
PTSTR szInfFileName, szInfSectionName;
PTSTR szInfSectionExt = NULL;
TCHAR InfSectionWithExt[MAX_PATH];
DWORD InfSectionWithExtLength;
HINF hDeviceInf = INVALID_HANDLE_VALUE;
HKEY hkDrv = INVALID_HANDLE_VALUE;
PSP_FILE_CALLBACK MsgHandler;
PVOID MsgHandlerContext;
BOOL MsgHandlerIsNativeCharWidth;
HSPFILEQ UserFileQ;
INFCONTEXT InfContext;
DWORD dwConfigFlags=0;
ULONG cbData;
PTSTR DevIdBuffer = NULL;
PCTSTR TempString;
ULONG ulStatus, ulProblem;
DEVINST dnReenum = 0;
TCHAR szNewName[MAX_PATH];
BOOL OemInfFileToCopy = FALSE;
BOOL InfFromOemPath = FALSE;
BOOL DeleteDevKey = FALSE;
PSVCNAME_NODE DeleteServiceList = NULL;
BOOL FreeMsgHandlerContext = FALSE;
BOOL CloseUserFileQ = FALSE;
HWND hwndParent;
BOOL DoFileCopying;
DWORD DevInstCapabilities;
TCHAR DeviceFullID[MAX_DEVICE_ID_LEN];
DWORD BackupFlags = 0;
BOOL NullDriverInstall = FALSE;
PINSTALLED_INF_CLEANUP InfsToCleanUp = NULL;
INT FileQueueNeedsReboot;
PSETUP_LOG_CONTEXT LogContext = NULL;
DWORD slot_deviceID = 0;
DWORD slot_section = 0;
DWORD FileQueueFlags;
BOOL NoProgressUI;
TCHAR className[MAX_CLASS_NAME_LEN];
PSP_ALTPLATFORM_INFO_V2 ValidationPlatform = NULL;
#if MAX_SECT_NAME_LEN > MAX_PATH
#error MAX_SECT_NAME_LEN is larger than MAX_PATH--fix InfSectionWithExt!
#endif
ASSERT_HEAP_IS_VALID();
//
// We can only install a device if a device was specified.
//
if(!DeviceInfoData) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
//
// Make sure we're local
//
if(NULL != pDeviceInfoSet->hMachine ) { //&& !g_ReadOnlyRemote ??
//
// hDevInfo may be valid, but it's not for this machine
//
UnlockDeviceInfoSet(pDeviceInfoSet);
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Err = NO_ERROR;
try {
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL))) {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// This routine can't install a non-native driver
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALTPLATFORM_DRVSEARCH) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
//
// Make sure we only use the devinfo element's window if it's valid.
//
if(hwndParent = DevInfoElem->InstallParamBlock.hwndParent) {
if(!IsWindow(hwndParent)) {
hwndParent = NULL;
}
}
//
// set the local log context before it gets used.
//
LogContext = DevInfoElem->InstallParamBlock.LogContext;
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_INSTALL_TIME,
NULL); // text message
//
// obtain the full id of the device we are (re)installing
//
if( CM_Get_Device_ID(DevInfoElem->DevInst,
DeviceFullID,
SIZECHARS(DeviceFullID),
0
) != CR_SUCCESS ) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
//
// If we are installing a driver, then the selected driver pointer will be
// non-NULL, otherwise we are actually removing the driver (i.e., installing the
// NULL driver)
//
if(DevInfoElem->SelectedDriver) {
if(DoFullInstall) {
if(slot_deviceID == 0) {
slot_deviceID = AllocLogInfoSlotOrLevel(LogContext,DRIVER_LOG_INFO,FALSE);
}
WriteLogEntry(
LogContext,
slot_deviceID,
MSG_LOG_DO_FULL_INSTALL,
NULL, // text message
DeviceFullID);
//
// Create the Driver Reg Key.
//
if((hkDrv = SetupDiCreateDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
NULL,
NULL)) == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
} else {
DWORD slot = slot_deviceID;
if(slot_deviceID == 0) {
if(DevInfoElem->InstallParamBlock.Flags & DI_NOVCP) {
slot = AllocLogInfoSlotOrLevel(LogContext,DRIVER_LOG_VERBOSE,TRUE); // may be being copied for someone else - keep this around
} else {
slot_deviceID = slot = AllocLogInfoSlotOrLevel(LogContext,DRIVER_LOG_INFO,FALSE); // we do copy ourselves
}
}
WriteLogEntry(
LogContext,
slot,
MSG_LOG_DO_COPY_INSTALL,
NULL, // text message
DeviceFullID);
}
szInfFileName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfFileName
);
szInfSectionName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfSectionName
);
if((hDeviceInf = SetupOpenInfFile(szInfFileName,
NULL,
INF_STYLE_WIN4,
NULL)) == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
//
// Give the INF whatever the local log context is.
//
InheritLogContext(LogContext, &((PLOADED_INF) hDeviceInf)->LogContext);
SetLogSectionName(LogContext, TEXT("Driver Install"));
//
// Figure out whether we need to copy files. (Ignore the DI_NOFILECOPY flag if we're
// doing a copy-only installation--that's what setupx does.)
//
DoFileCopying = (!(DevInfoElem->InstallParamBlock.Flags & DI_NOFILECOPY) || !DoFullInstall);
//
// Find out the 'real' install section we should be using (i.e.,
// the potentially OS/architecture-specific one.
//
if(!SetupDiGetActualSectionToInstall(hDeviceInf,
szInfSectionName,
InfSectionWithExt,
SIZECHARS(InfSectionWithExt),
&InfSectionWithExtLength,
&szInfSectionExt
)) {
Err = GetLastError();
goto clean0;
}
//
// Append the layout INF, if necessary.
//
if(DoFileCopying) {
SetupOpenAppendInfFile(NULL, hDeviceInf, NULL);
}
//
// Append-load any included INFs specified in an "include=" line in our
// install section.
//
AppendLoadIncludedInfs(hDeviceInf, szInfFileName, InfSectionWithExt, DoFileCopying);
ASSERT_HEAP_IS_VALID();
//
// Now perform file installation activities...
//
if(!DoFileCopying) {
//
// We're not supposed to do any file copying, In this case, we
// still need to have the INF copied to the INF directory
// (along with its associated catalog, if any).
//
if(pSetupInfIsFromOemLocation(szInfFileName, TRUE)) {
TCHAR CatalogName[MAX_PATH];
TCHAR OriginalInfName[MAX_PATH];
TCHAR CatalogFilenameOnSystem[MAX_PATH];
BOOL DifferentOriginalName, UseOriginalInfName;
DWORD PolicyToUse;
//
// Retrieve (potentially decorated) CatalogFile= entry, if
// present, from [version] section.
//
// Note: We're safe in casting our INF handle straight to a
// PLOADED_INF (without even locking it), since this INF
// handle will never be seen outside of this routine.
//
Err = pGetInfOriginalNameAndCatalogFile(
(PLOADED_INF)hDeviceInf,
NULL,
&DifferentOriginalName,
OriginalInfName,
SIZECHARS(OriginalInfName),
CatalogName,
SIZECHARS(CatalogName),
NULL // always native OS/arch (ver doesn't matter for CatalogFile=)
);
if(Err == NO_ERROR) {
if(*CatalogName) {
TempString = CatalogName;
} else {
TempString = NULL;
}
} else {
goto clean0;
}
PolicyToUse = GetCodeSigningPolicyForInf(LogContext,
hDeviceInf,
&ValidationPlatform,
&UseOriginalInfName
);
//
// An exception INF can't be used in a device installation!
//
if(UseOriginalInfName) {
Err = ERROR_INVALID_CLASS;
goto clean0;
}
//
// We aren't supposed to be copying files here, hence we
// want to fail if the INF doesn't already exist in the
// Inf directory.
//
// NOTE: We should never see an issue where we need to
// prompt user about their trust of an Authenticode
// publisher, since the presence of the INF and CAT are
// sufficient here (trust was confirmed when queue
// committal occurred).
//
Err = GLE_FN_CALL(FALSE,
_SetupCopyOEMInf(
szInfFileName,
NULL, // default source location to where INF presently is
(DevInfoElem->SelectedDriver->Flags & DNF_INET_DRIVER)
? SPOST_URL : SPOST_PATH,
SP_COPY_REPLACEONLY,
szNewName,
SIZECHARS(szNewName),
NULL,
NULL,
(DifferentOriginalName
? OriginalInfName
: pSetupGetFileTitle(szInfFileName)),
TempString,
hwndParent,
pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DrvDescription),
PolicyToUse,
SCOI_TRY_UPDATE_PNF // not fatal if existing PNF locked
| SCOI_NO_UI_ON_SIGFAIL,
NULL,
ValidationPlatform,
NULL,
CatalogFilenameOnSystem,
LogContext,
NULL,
NULL)
);
if(Err != NO_ERROR) {
if(Err == ERROR_FILE_EXISTS) {
//
// We couldn't copy the legacy INF because it
// already exists in the %windir%\Inf directory.
// Since it probably has better source path
// information than we do, it's best to leave the
// PNF alone. We also need this information about
// the INF's existence to that we know whether or
// not to blow away the INF later in case we hit a
// failure.
//
Err = NO_ERROR;
} else {
//
// SetupCopyOEMInf failed for some reason other than
// file-already-exists (most likely, because we were
// dealing with a new-style device INF that didn't
// already exist in the Inf directory). Bail now.
//
goto clean0;
}
}
}
} else {
//
// If the DI_NOVCP flag is set, then just queue up the file
// copy/rename/delete operations. Otherwise, perform the
// actions.
//
if(DevInfoElem->InstallParamBlock.Flags & DI_NOVCP) {
//
// We must have a user-supplied file queue.
//
MYASSERT(DevInfoElem->InstallParamBlock.UserFileQ);
UserFileQ = DevInfoElem->InstallParamBlock.UserFileQ;
} else {
//
// Since we may need to check the queued files to determine
// whether file copy is necessary, we have to open our own
// queue, and commit it ourselves.
//
if((UserFileQ = SetupOpenFileQueue()) != INVALID_HANDLE_VALUE) {
CloseUserFileQ = TRUE;
} else {
//
// SetupOpenFileQueue sets actual error
//
Err = GetLastError();
goto clean0;
}
}
//
// Maybe replace the file queue's log context with the Inf's
//
if (LogContext) {
InheritLogContext(LogContext,
&((PSP_FILE_QUEUE) UserFileQ)->LogContext);
}
if(slot_section == 0) {
//
// we haven't done anything about logging section yet...
//
slot_section = AllocLogInfoSlotOrLevel(LogContext,DRIVER_LOG_VERBOSE,FALSE);
//
// Say what section is about to be installed.
//
WriteLogEntry(LogContext,
slot_section,
MSG_LOG_INSTALLING_SECTION_FROM,
NULL,
InfSectionWithExt,
szInfFileName);
}
//
// DI_FLAGSEX_PREINSTALLBACKUP has precedence over DI_FLAGSEX_BACKUPONREPLACE
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_PREINSTALLBACKUP) {
BackupFlags |= SP_BKFLG_PREBACKUP | SP_BKFLG_CALLBACK;
} else if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_BACKUPONREPLACE) {
BackupFlags |= SP_BKFLG_LATEBACKUP | SP_BKFLG_CALLBACK;
}
if(BackupFlags != 0) {
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_PREP_BACKUP_TIME,
NULL); // text message
pSetupGetBackupQueue(DeviceFullID, UserFileQ, BackupFlags);
//
// We don't care about errors here
//
}
ASSERT_HEAP_IS_VALID();
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_INSTALL_FROM_INF_TIME,
NULL); // text message
Err = InstallFromInfSectionAndNeededSections(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_FILES,
NULL,
NULL,
SP_COPY_NEWER_OR_SAME | SP_COPY_LANGUAGEAWARE |
((DevInfoElem->InstallParamBlock.Flags & DI_NOBROWSE) ? SP_COPY_NOBROWSE : 0),
NULL,
NULL,
INVALID_HANDLE_VALUE,
NULL,
UserFileQ
);
//
// If we're not doing a full install (i.e., file copy-only), then we also want to
// queue up any file operations for co-installer registration and device interface
// installation.
//
if(!DoFullInstall && (Err == NO_ERROR)) {
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_CO_INSTALLER_COPY_TIME,
NULL); // text message
if(!_SetupDiRegisterCoDeviceInstallers(DeviceInfoSet, DeviceInfoData, FALSE, hDeviceInf, UserFileQ) ||
!_SetupDiInstallDeviceInterfaces(DeviceInfoSet, DeviceInfoData, FALSE, hDeviceInf, UserFileQ)) {
Err = GetLastError();
}
}
//
// Mark the queue as a device install queue (and make sure
// there's a catalog node representing our device INF in the
// queue).
//
Err = MarkQueueForDeviceInstall(UserFileQ,
hDeviceInf,
pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DrvDescription)
);
if(CloseUserFileQ) {
if(Err == NO_ERROR) {
//
// If the parameter block contains an install message handler, then use it,
// otherwise, initialize our default one.
//
if(DevInfoElem->InstallParamBlock.InstallMsgHandler) {
MsgHandler = DevInfoElem->InstallParamBlock.InstallMsgHandler;
MsgHandlerContext = DevInfoElem->InstallParamBlock.InstallMsgHandlerContext;
MsgHandlerIsNativeCharWidth = DevInfoElem->InstallParamBlock.InstallMsgHandlerIsNativeCharWidth;
} else {
NoProgressUI = (GuiSetupInProgress || (DevInfoElem->InstallParamBlock.Flags & DI_QUIETINSTALL));
MsgHandlerContext = SetupInitDefaultQueueCallbackEx(
hwndParent,
(NoProgressUI ? INVALID_HANDLE_VALUE : NULL),
0,
0,
NULL
);
if(MsgHandlerContext) {
FreeMsgHandlerContext = TRUE;
MsgHandler = SetupDefaultQueueCallback;
MsgHandlerIsNativeCharWidth = TRUE;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Call _SetupVerifyQueuedCatalogs separately (i.e.,
// don't let it happen automatically as a result of
// scanning/committing the queue that happens below).
// We do this beforehand so that we know what unique
// name was generated when an OEM INF was installed into
// %windir%\Inf (in case we need to delete the
// INF/PNF/CAT files later if we encounter an error).
//
if(Err == NO_ERROR) {
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_VERIFY_CAT_TIME,
NULL); // text message
Err = _SetupVerifyQueuedCatalogs(
hwndParent,
UserFileQ,
(VERCAT_INSTALL_INF_AND_CAT |
((DevInfoElem->SelectedDriver->Flags & DNF_INET_DRIVER) ? VERCAT_PRIMARY_DEVICE_INF_FROM_INET : 0)),
szNewName,
&OemInfFileToCopy
);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_VERIFY_CAT_TIME,
NULL); // text message
}
ASSERT_HEAP_IS_VALID();
if (Err == NO_ERROR) {
//
// We successfully queued up the file operations and
// we have a message handler to use--now we need to
// commit the queue. First off, though, we should
// check to see if the files are already there.
//
// ScanQueueResult can have 1 of 3 values:
//
// 0: Some files were missing or not valid--must
// commit queue.
//
// 1: All files to be copied are already present and
// valid, and the queue is empty--skip committing
// queue.
//
// 2: All files to be copied are present and valid,
// but del/ren/backup queues not empty--must
// commit queue. The copy queue will have been
// emptied, so only del/ren/backup functions will
// be performed.
//
// (jamiehun) If DI_FLAGSEX_PREINSTALLBACKUP is
// specified and there are items to be backed up, it
// is covered in (2) the inf file will have already
// been backed up
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_PRESCAN_TIME,
NULL); // text message
if(!SetupScanFileQueue(UserFileQ,
SPQ_SCAN_FILE_VALIDITY |
SPQ_SCAN_PRUNE_COPY_QUEUE,
hwndParent,
NULL,
NULL,
&ScanQueueResult)) {
//
// SetupScanFileQueue should really never
// fail when you don't ask it to call a
// callback routine, but if it does, just
// go ahead and commit the queue.
//
ScanQueueResult = 0;
}
if(ScanQueueResult != 1) {
//
// Copy enqueued files.
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_COMMIT_TIME,
NULL); // text message
if(_SetupCommitFileQueue(hwndParent,
UserFileQ,
MsgHandler,
MsgHandlerContext,
MsgHandlerIsNativeCharWidth
)) {
//
// Check to see whether a reboot is required
// as a result of committing the queue (i.e.,
// because files were in use, or the INF
// requested a reboot).
//
FileQueueNeedsReboot = SetupPromptReboot(UserFileQ, NULL, TRUE);
//
// This should never fail...
//
MYASSERT(FileQueueNeedsReboot != -1);
if(FileQueueNeedsReboot) {
SetDevnodeNeedsRebootProblem(DevInfoElem, pDeviceInfoSet, MSG_LOG_REBOOT_REASON_INUSE);
}
} else {
Err = GetLastError();
}
}
//
// If no files were modified as a result of commiting
// the file queue, then set the DI_FLAGSEX_RESTART_DEVICE_ONLY
// flag so that we only restart this one device, as opposed
// to restarting this device and all other device that
// are sharing a driver or filter with this device.
//
if (SetupGetFileQueueFlags(UserFileQ, &FileQueueFlags) &&
!(FileQueueFlags & SPQ_FLAG_FILES_MODIFIED)) {
DevInfoElem->InstallParamBlock.FlagsEx |= DI_FLAGSEX_RESTART_DEVICE_ONLY;
}
//
// Terminate the default queue callback, if it was created.
//
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
FreeMsgHandlerContext = FALSE;
}
}
}
//
// Close our file queue handle.
//
SetupCloseFileQueue(UserFileQ);
CloseUserFileQ = FALSE;
}
if(Err != NO_ERROR) {
goto clean0;
}
}
//
// If the copy succeeded (or in setup's case was queued), then
// it's time to update the registry and ini files.
//
if(Err == NO_ERROR) {
//
// We've got some registry modifications to do, but we don't want to
// do them if this is a copy-only installation.
//
if(DoFullInstall) {
//
// Skip installation of basic log configs if this is an
// enumerated device.
//
if((CM_Get_DevInst_Status_Ex(&ulStatus, &ulProblem, DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine) != CR_SUCCESS) ||
(ulStatus & DN_ROOT_ENUMERATED)) {
LOG_CONF LogConf;
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_WRITE_BASIC_CFGS_TIME,
NULL); // text message
//
// Clean out all existing BASIC_LOG_CONF LogConfigs before writing out new ones from
// the INF.
//
while(CM_Get_First_Log_Conf_Ex(&LogConf, DevInfoElem->DevInst, BASIC_LOG_CONF,pDeviceInfoSet->hMachine) == CR_SUCCESS) {
CM_Free_Log_Conf(LogConf, 0);
CM_Free_Log_Conf_Handle(LogConf);
}
//
// Now write out the new basic log configs.
//
Err = InstallFromInfSectionAndNeededSections(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_LOGCONFIG,
NULL,
NULL,
0,
NULL,
NULL,
DeviceInfoSet,
DeviceInfoData,
NULL
);
} else {
//
// For non-root-enumerated devices, check to see if there's an [<InstallSec>.LogConfigOverride]
// section, and if so, run it.
//
if(MYVERIFY(SUCCEEDED(StringCchCopy(&(InfSectionWithExt[InfSectionWithExtLength - 1]),
SIZECHARS(InfSectionWithExt)-(InfSectionWithExtLength - 1),
pszLogConfigOverrideSectionSuffix)))) {
if(SetupFindFirstLine(hDeviceInf, InfSectionWithExt, NULL, &InfContext)) {
LOG_CONF LogConf;
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_WRITE_OVERRIDE_CFGS_TIME,
NULL); // text message
//
// Clean out all existing OVERRIDE_LOG_CONF LogConfigs before writing out new ones from
// the INF.
//
while(CM_Get_First_Log_Conf_Ex(&LogConf, DevInfoElem->DevInst,
OVERRIDE_LOG_CONF,pDeviceInfoSet->hMachine) == CR_SUCCESS) {
CM_Free_Log_Conf(LogConf, 0);
CM_Free_Log_Conf_Handle(LogConf);
}
//
// Now write out the new override log configs.
//
Err = InstallFromInfSectionAndNeededSections(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_LOGCONFIG | SPINST_LOGCONFIGS_ARE_OVERRIDES,
NULL,
NULL,
0,
NULL,
NULL,
DeviceInfoSet,
DeviceInfoData,
NULL
);
}
}
//
// Make sure we strip off the ".LogConfigOverride" we added above.
//
InfSectionWithExt[InfSectionWithExtLength - 1] = TEXT('\0');
}
if((Err == NO_ERROR) && !(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NO_DRVREG_MODIFY)) {
//
// (Don't pass devinfo set and element here, because we're writing
// to the _driver_ key, not the _device_ key.)
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_INSTALL_REG_TIME,
NULL); // text message
Err = InstallFromInfSectionAndNeededSections(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_INIFILES
| SPINST_REGISTRY
| SPINST_INI2REG
| SPINST_BITREG
| SPINST_REGSVR
| SPINST_UNREGSVR
| SPINST_PROFILEITEMS,
hkDrv,
NULL,
0,
NULL,
NULL,
INVALID_HANDLE_VALUE,
NULL,
NULL
);
if(Err == NO_ERROR) {
//
// Install extra HardWare registry section (if any).
//
Err = InstallHW(DeviceInfoSet,
DeviceInfoData,
hDeviceInf,
InfSectionWithExt,
&DeleteDevKey
);
}
}
//
// Set appropriate flags if we need to reboot or restart after
// this installation.
//
if(SetupFindFirstLine(hDeviceInf, InfSectionWithExt, pszReboot, &InfContext)) {
SetDevnodeNeedsRebootProblemWithArg2(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_KEY,
(ULONG_PTR)pszReboot,
(ULONG_PTR)InfSectionWithExt);
} else if(SetupFindFirstLine(hDeviceInf, InfSectionWithExt, pszRestart, &InfContext)) {
//
// NOTE: This behavior is taken from setupx. In both "Reboot"
// and "Restart" cases, it sets the DI_NEEDREBOOT flag.
//
SetDevnodeNeedsRebootProblemWithArg2(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_KEY,
(ULONG_PTR)pszRestart,
(ULONG_PTR)InfSectionWithExt);
}
//
// Set the value to write for the config flags, only if there
// are no config flags yet. If they exist, i.e., we are updating
// an existing device, just clear the re-install flag.
//
dwConfigFlags = GetDevInstConfigFlags(
DevInfoElem->DevInst,
(DevInfoElem->InstallParamBlock.Flags & DI_INSTALLDISABLED)
? CONFIGFLAG_DISABLED
: 0,
pDeviceInfoSet->hMachine
);
//
// Always clear the REINSTALL bit and the FAILEDINSTALL bit
// when installing a device.
// (Also, clear the CONFIGFLAG_FINISH_INSTALL bit, which is used for
// Raw and driver-detected devnodes.)
//
dwConfigFlags &= ~(CONFIGFLAG_REINSTALL | CONFIGFLAG_FAILEDINSTALL | CONFIGFLAG_FINISH_INSTALL);
}
//
// If we're doing a copy-only installation, then we're done.
//
if(!DoFullInstall) {
goto clean0;
}
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_WRITE_REG_TIME,
NULL); // text message
//
// Insert Driver Specific strings into the registry.
//
if(InfFromOemPath = pSetupInfIsFromOemLocation(szInfFileName, TRUE)) {
TempString = pSetupGetFileTitle(szNewName);
} else {
TempString = pSetupGetFileTitle(szInfFileName);
}
RegSetValueEx(hkDrv,
pszInfPath,
0,
REG_SZ,
(PBYTE)TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR)
);
RegSetValueEx(hkDrv,
pszInfSection,
0,
REG_SZ,
(PBYTE)szInfSectionName,
(lstrlen(szInfSectionName) + 1) * sizeof(TCHAR)
);
if(szInfSectionExt) {
RegSetValueEx(hkDrv,
pszInfSectionExt,
0,
REG_SZ,
(PBYTE)szInfSectionExt,
(lstrlen(szInfSectionExt) + 1) * sizeof(TCHAR)
);
} else {
//
// This wasn't an OS/architecture-specific install section, _or_ we are
// installing from a legacy INF. Make sure there's no value hanging
// around from a previous installation.
//
RegDeleteValue(hkDrv, pszInfSectionExt);
}
if(DevInfoElem->SelectedDriver->ProviderDisplayName == -1) {
//
// No provider specified--delete any previously existing value entry.
//
RegDeleteValue(hkDrv, pszProviderName);
} else {
//
// Retrieve the Provider name, and store it in the driver key.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->ProviderDisplayName
);
RegSetValueEx(hkDrv,
pszProviderName,
0,
REG_SZ,
(PBYTE)TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR)
);
}
if (DevInfoElem->SelectedDriver->DriverDate.dwLowDateTime != 0 ||
DevInfoElem->SelectedDriver->DriverDate.dwHighDateTime != 0) {
SYSTEMTIME SystemTime;
TCHAR Date[20];
//
// Save the driver date in binary (FILETIME) format in the registry
// so it can be localized by other components.
//
RegSetValueEx(hkDrv,
pszDriverDateData,
0,
REG_BINARY,
(LPBYTE)&DevInfoElem->SelectedDriver->DriverDate,
sizeof(DevInfoElem->SelectedDriver->DriverDate)
);
//
// Save the driver date in string format for compatibility.
//
if (FileTimeToSystemTime(&(DevInfoElem->SelectedDriver->DriverDate), &SystemTime)) {
MYVERIFY(SUCCEEDED(StringCchPrintf(Date,
SIZECHARS(Date),
TEXT("%d-%d-%d"),
SystemTime.wMonth,
SystemTime.wDay,
SystemTime.wYear)));
RegSetValueEx(hkDrv,
pszDriverDate,
0,
REG_SZ,
(PBYTE)Date,
(lstrlen(Date) + 1) * sizeof(TCHAR)
);
}
} else {
//
//No driver date for this driver--delete any previously existing value entry.
//
RegDeleteValue(hkDrv, pszDriverDateData);
RegDeleteValue(hkDrv, pszDriverDate);
}
if (DevInfoElem->SelectedDriver->DriverVersion != 0) {
ULARGE_INTEGER Version;
TCHAR VersionString[LINE_LEN];
Version.QuadPart = DevInfoElem->SelectedDriver->DriverVersion;
MYVERIFY(SUCCEEDED(StringCchPrintf(VersionString,
SIZECHARS(VersionString),
TEXT("%0d.%0d.%0d.%0d"),
HIWORD(Version.HighPart),
LOWORD(Version.HighPart),
HIWORD(Version.LowPart),
LOWORD(Version.LowPart))));
RegSetValueEx(hkDrv,
pszDriverVersion,
0,
REG_SZ,
(PBYTE)VersionString,
(lstrlen(VersionString) + 1) * sizeof(TCHAR)
);
} else {
//
//No driver version for this driver--delete any previously existing value entry.
//
RegDeleteValue(hkDrv, pszDriverVersion);
}
//
// Set the Class property for this device.
//
SetupDiClassNameFromGuid(&DevInfoElem->ClassGuid, className, MAX_CLASS_NAME_LEN, NULL);
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CLASS,
className,
(lstrlen(className) + 1) * sizeof(TCHAR),
0,
pDeviceInfoSet->hMachine);
//
// Set the MFG device registry property.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->MfgDisplayName
);
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_MFG,
TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR),
0,
pDeviceInfoSet->hMachine);
//
// Add hardware and compatible IDs to the hardware key if they exist
// in the driver node and don't already exist in the registry. This
// sets up an ID for manually installed devices.
//
if(!(DevInfoElem->InstallParamBlock.Flags & DI_NOWRITE_IDS) && // Want IDs written?
(DevInfoElem->SelectedDriver->HardwareId != -1)) // ID in driver node?
{
//
// Don't write IDs if either Hardware or Compatible IDs already
// exist in the registry. Note that I use cbData as an IN/OUT parameter
// to both CM calls. This is OK, however, since cbSize will not be modified
// on a CR_NO_SUCH_VALUE return, and I won't try to re-use it otherwise.
//
cbData = 0;
if((DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALWAYSWRITEIDS) ||
((CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_HARDWAREID,
NULL,
NULL,
&cbData,
0,
pDeviceInfoSet->hMachine) == CR_NO_SUCH_VALUE) &&
(CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_COMPATIBLEIDS,
NULL,
NULL,
&cbData,
0,
pDeviceInfoSet->hMachine) == CR_NO_SUCH_VALUE)))
{
DWORD CurStringLen, TotalStringLen, DevIdBufferLen, i;
//
// Compute the maximum buffer size needed to hold our REG_MULTI_SZ
// ID lists.
//
DevIdBufferLen = (((DevInfoElem->SelectedDriver->NumCompatIds) ?
DevInfoElem->SelectedDriver->NumCompatIds : 1)
* MAX_DEVICE_ID_LEN) + 1;
if(!(DevIdBuffer = MyMalloc(DevIdBufferLen * sizeof(TCHAR)))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Build a multi-sz list of the (single) HardwareID, and set it.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->HardwareId
);
TotalStringLen = lstrlen(TempString) + 1;
MYVERIFY(SUCCEEDED(StringCchCopy(DevIdBuffer,
DevIdBufferLen,
TempString)));
DevIdBuffer[TotalStringLen++] = TEXT('\0'); // Add extra terminating NULL;
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_HARDWAREID,
DevIdBuffer,
TotalStringLen * sizeof(TCHAR),
0,
pDeviceInfoSet->hMachine );
//
// Build a multi-sz list of the zero or more CompatibleIDs, and set it
//
TotalStringLen = 0;
for(i = 0; i < DevInfoElem->SelectedDriver->NumCompatIds; i++) {
TempString = pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->CompatIdList[i]);
CurStringLen = lstrlen(TempString) + 1;
MYVERIFY(SUCCEEDED(StringCchCopy(DevIdBuffer+TotalStringLen,
DevIdBufferLen-TotalStringLen,
TempString)));
TotalStringLen += CurStringLen;
}
if(TotalStringLen) {
DevIdBuffer[TotalStringLen++] = TEXT('\0'); // Add extra terminating NULL;
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_COMPATIBLEIDS,
DevIdBuffer,
TotalStringLen * sizeof(TCHAR),
0,
pDeviceInfoSet->hMachine);
}
}
}
//
// Write out the 'MatchingDeviceId' value entry to the driver key that indicates which
// device ID (hardware or compatible) was used to pick this driver. For a compatible
// driver, this is the device ID that the compatible match was based on. For a class
// driver, this is the driver node's HardwareId (if present), or best CompatibleId. If
// the class driver node didn't specify a hardware or compatible IDs, then this value
// will not be written (we'll actually delete it to make sure it doesn't exist from a
// previous driver installation).
//
TempString = NULL;
if(DevInfoElem->SelectedDriverType == SPDIT_COMPATDRIVER) {
if(DevInfoElem->SelectedDriver->MatchingDeviceId == -1) {
//
// We have a HardwareID match.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->HardwareId
);
} else {
//
// We have a CompatibleID match.
//
MYASSERT((DevInfoElem->SelectedDriver->MatchingDeviceId >= 0) &&
((DWORD)(DevInfoElem->SelectedDriver->MatchingDeviceId) < DevInfoElem->SelectedDriver->NumCompatIds));
TempString = pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->CompatIdList[DevInfoElem->SelectedDriver->MatchingDeviceId]
);
}
} else if(DevInfoElem->SelectedDriver->HardwareId != -1) {
//
// There's no notion of compatibility for class drivers--pick the device ID with the
// highest order of preference.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->HardwareId
);
if(!(*TempString)) {
//
// The HardwareID was an empty string--use the first CompatibleID.
//
if(DevInfoElem->SelectedDriver->NumCompatIds) {
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->CompatIdList[0]
);
} else {
TempString = NULL;
}
}
}
if(TempString) {
RegSetValueEx(hkDrv,
pszMatchingDeviceId,
0,
REG_SZ,
(PBYTE)TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR)
);
} else {
//
// We have an override situation where the user picked a class driver that didn't
// specify any hardware or compatible IDs. Make sure there's no value hanging
// around from a previous installation.
//
RegDeleteValue(hkDrv, pszMatchingDeviceId);
}
//
// If we're running under Windows NT, and we've successfully installed the device instance,
// then we need to install any required services.
//
if((Err == NO_ERROR) && (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
PTSTR pServiceInstallSection;
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_SERVICE_TIME,
NULL); // text message
//
// The install section name is of the form:
//
// <InfSectionWithExt>.Services
//
MYVERIFY(SUCCEEDED(StringCchCopy(&(InfSectionWithExt[InfSectionWithExtLength - 1]),
SIZECHARS(InfSectionWithExt)-(InfSectionWithExtLength - 1),
pszServicesSectionSuffix)));
pServiceInstallSection = InfSectionWithExt;
Err = InstallNtService(DevInfoElem,
hDeviceInf,
szInfFileName,
pServiceInstallSection,
&DeleteServiceList,
0,
&NullDriverInstall
);
}
if((Err == NO_ERROR) && DoFullInstall) {
InfSectionWithExt[InfSectionWithExtLength - 1] = TEXT('\0');
pSetupCopyRelatedInfs(hDeviceInf,
szInfFileName,
InfSectionWithExt,
(DevInfoElem->SelectedDriver->Flags & DNF_INET_DRIVER)
? SPOST_URL : SPOST_PATH,
LogContext);
if(SUCCEEDED(StringCchCopy(&(InfSectionWithExt[InfSectionWithExtLength - 1]),
SIZECHARS(InfSectionWithExt)-(InfSectionWithExtLength-1),
pszWmiSectionSuffix))) {
//
// InfSectionWithExt refers to DDInstall.WMI
//
if(!SetupConfigureWmiFromInfSection(hDeviceInf,
InfSectionWithExt,
0)) {
Err = GetLastError();
}
}
}
}
} else {
//
// Installing the NULL driver.
// This means to set the Config flags, and nothing else.
// Config Flags get set to enabled in this case, so the device
// gets assigned the correct config. (Win95 bug 26320)
//
WriteLogEntry(
LogContext,
DRIVER_LOG_INFO,
MSG_LOG_INSTALL_NULL,
NULL, // text message
DeviceFullID);
NullDriverInstall = TRUE;
if(DoFullInstall) {
BOOL NullDrvInstallOK = FALSE;
//
// Check to see if the devnode is raw-capable, or if it already has a controlling
// service. If neither of those conditions are met, then we should fail this call
// unless the caller has explicitly passed us the DI_FLAGSEX_SETFAILEDINSTALL flag.
//
cbData = sizeof(DevInstCapabilities);
if(CR_SUCCESS == CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CAPABILITIES,
NULL,
&DevInstCapabilities,
&cbData,
0,
pDeviceInfoSet->hMachine))
{
NullDrvInstallOK = (DevInstCapabilities & CM_DEVCAP_RAWDEVICEOK);
}
if(!NullDrvInstallOK) {
//
// The devnode isn't raw-capable. Check to see if it already has a
// controlling service (e.g., because it was created as a result of
// a driver's call to IoReportDetectedDevice).
//
cbData = 0;
if(CR_BUFFER_SMALL == CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_SERVICE,
NULL,
NULL,
&cbData,
0,
pDeviceInfoSet->hMachine))
{
NullDrvInstallOK = TRUE;
}
}
if(!NullDrvInstallOK &&
!(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_SETFAILEDINSTALL)) {
Err = ERROR_NO_ASSOCIATED_SERVICE;
} else {
dwConfigFlags = GetDevInstConfigFlags(DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine) &
~(CONFIGFLAG_DISABLED | CONFIGFLAG_REINSTALL | CONFIGFLAG_FINISH_INSTALL);
//
// Delete all driver (software) keys associated with the
// device (this will automatically reset the "Driver"
// registry property.
//
SetupDiDeleteDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGGENERAL,
0,
DIREG_DRV
);
//
// Delete the controlling (FDO) Service property, as well as the UpperFilters and
// LowerFilters properties. Only do this if the devnode is not root-enumerated,
// however, since NT can have root-enumerated devnodes reported by drivers via
// IoReportDetectedDevice for which there's no corresponding INF (hence, the user
// must do a null-driver install in order to silence the "New Hardware Found" popups
// for these devices.
//
if((CM_Get_DevInst_Status_Ex(&ulStatus, &ulProblem, DevInfoElem->DevInst,
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
!(ulStatus & DN_ROOT_ENUMERATED)) {
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst, CM_DRP_SERVICE,
NULL, 0, 0,pDeviceInfoSet->hMachine);
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst, CM_DRP_UPPERFILTERS,
NULL, 0, 0,pDeviceInfoSet->hMachine);
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst, CM_DRP_LOWERFILTERS,
NULL, 0, 0,pDeviceInfoSet->hMachine);
}
}
} else {
//
// It is an error to not have a selected driver in the copy-only case.
//
Err = ERROR_NO_DRIVER_SELECTED;
}
}
//
// If all went well above, then write some configflags, and re-enumerate
// the parent device instance if necessary
//
if(Err == NO_ERROR) {
//
// Write the Driver Description to the Registry, if there
// is an lpSelectedDriver, and the Device Description also
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_WRITE_REG2_TIME,
NULL); // text message
if(DevInfoElem->SelectedDriver) {
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DrvDescription
);
RegSetValueEx(hkDrv,
pszDrvDesc,
0,
REG_SZ,
(PBYTE)TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR)
);
//
// (setupx BUG 12721) always update the DevDesc in the registry with the
// value from the INF (ie only do this if we have a SELECTED driver)
// The semantics are weird, but the SelectedDriver NODE contains the
// INF Device description, and DRV description
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DevDescriptionDisplayName
);
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_DEVICEDESC,
TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR),
0,
pDeviceInfoSet->hMachine);
} else {
//
// No driver is selected, so use the description stored with the device
// information element itself for the device description. However, only set this
// if it isn't already present.
//
cbData = 0;
if(CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_DEVICEDESC,
NULL,
NULL,
&cbData,
0,
pDeviceInfoSet->hMachine) == CR_NO_SUCH_VALUE) {
if(DevInfoElem->DeviceDescriptionDisplayName != -1) {
TempString = pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->DeviceDescriptionDisplayName
);
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_DEVICEDESC,
TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR),
0,
pDeviceInfoSet->hMachine);
}
}
}
//
// Unless the caller explicitly requested that this device be installed disabled, clear
// the CONFIGFLAG_DISABLED bit.
//
if(!(DevInfoElem->InstallParamBlock.Flags & DI_INSTALLDISABLED)) {
dwConfigFlags &= ~CONFIGFLAG_DISABLED;
}
//
// Write the config flags. If no selected driver means we should mark the install
// as a failure, then do so.
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_SETFAILEDINSTALL) {
dwConfigFlags |= CONFIGFLAG_FAILEDINSTALL;
}
//
// NTRAID#700056-2002/09/05-LonnyM check return codes from CM_Set_DevInst_Registry_Property.
//
// Only stop/start the device if we can set the ConfigFlags on the
// device. Otherwise we can get stuck in an infinite loop where
// the CONFIGFLAG_REINSTALL is not cleared and when the device is
// restarted we try to install it all over again.
// In the future we need to check all return codes from the
// CM_Set_DevInst_Registry_Property APIs and fail/exit appropriately.
//
if (CR_SUCCESS == CM_Set_DevInst_Registry_Property_Ex(
DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0,
pDeviceInfoSet->hMachine)) {
if(!(DevInfoElem->InstallParamBlock.Flags & (DI_DONOTCALLCONFIGMG | DI_NEEDREBOOT | DI_NEEDRESTART))) {
if (DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_RESTART_DEVICE_ONLY) {
//
// Restart this device only.
//
// This should only be done if no files were copied during
// DIF_INSTALLDEVICEFILES.
//
RestartSingleDevice(DevInfoElem,
pDeviceInfoSet,
NullDriverInstall,
LogContext);
} else {
//
// Restart this device as well as any other device that is
// using one of the same drivers as this device. This
// includes the function driver as well as the device and
// class upper and lower filter drivers.
//
// This only needs to be used if files were copied during
// DIF_INSTALLDEVICEFILES.
//
RestartAllDevicesUsingDrivers(DevInfoElem,
pDeviceInfoSet,
NullDriverInstall,
LogContext);
}
}
}
}
if((Err == NO_ERROR) && !GuiSetupInProgress) {
MYASSERT(DoFullInstall);
//
// Kick off RunOnce.
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_INSTALLSTOP_TIME,
NULL); // text message
Err = pSetupInstallStopEx(TRUE, INSTALLSTOP_NO_GRPCONV, LogContext);
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error,
// otherwise, assume it was an inpage error dealing with a mapped-in
// file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
}
if(CloseUserFileQ) {
SetupCloseFileQueue(UserFileQ);
}
//
// Access the following variables so that the compiler will respect our
// statement ordering w.r.t. these values. Otherwise, we may not be
// able to know with certainty whether or not we should release their
// corresponding resources.
//
DevInfoElem = DevInfoElem;
hDeviceInf = hDeviceInf;
hkDrv = hkDrv;
DevIdBuffer = DevIdBuffer;
DeleteServiceList = DeleteServiceList;
OemInfFileToCopy = OemInfFileToCopy;
}
//
// Clean up the registry if the install didn't go well. Along with other
// error paths, this handles the case where the user cancels the install
// while copying files
//
if(Err != NO_ERROR) {
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_CLEANUP_TIME,
NULL); // text message
if(DevInfoElem && DoFullInstall) {
//
// Disable the device if the error wasn't a user cancel.
//
if(Err != ERROR_CANCELLED) {
DWORD dwConfigFlagsSize;
//
// The device is in an unknown state. Disable it by setting the
// CONFIGFLAG_DISABLED config flag.
//
dwConfigFlagsSize = sizeof(DWORD);
if(CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
NULL,
&dwConfigFlags,
&dwConfigFlagsSize,
0,
pDeviceInfoSet->hMachine) == CR_SUCCESS) {
dwConfigFlags |= (CONFIGFLAG_DISABLED | CONFIGFLAG_REINSTALL);
//
// Also, make sure we clear the finish-install flag.
//
dwConfigFlags &= ~CONFIGFLAG_FINISH_INSTALL;
} else {
dwConfigFlags = CONFIGFLAG_DISABLED;
}
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0,
pDeviceInfoSet->hMachine);
//
// Delete the driver key (as well as the device key, if it
// didn't previously exist).
//
if(DevInfoElem->SelectedDriver) {
SetupDiDeleteDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGGENERAL,
0,
(DeleteDevKey ? DIREG_BOTH : DIREG_DRV)
);
}
//
// If necessary, delete any service entries created for this device instance.
//
if(DeleteServiceList) {
DeleteServicesInList(DeleteServiceList,LogContext);
}
}
}
//
// If we copied the OEM INF into the INF directory under a
// newly-generated name, delete it now.
//
if(OemInfFileToCopy) {
pSetupUninstallOEMInf(szNewName, LogContext, SUOI_FORCEDELETE, NULL);
}
}
UnlockDeviceInfoSet(pDeviceInfoSet);
if(hDeviceInf != INVALID_HANDLE_VALUE) {
SetupCloseInfFile(hDeviceInf);
}
if(hkDrv != INVALID_HANDLE_VALUE) {
RegCloseKey(hkDrv);
}
if(DevIdBuffer) {
MyFree(DevIdBuffer);
}
if(ValidationPlatform) {
MyFree(ValidationPlatform);
}
if(DeleteServiceList) {
PSVCNAME_NODE TmpSvcNode;
for(TmpSvcNode = DeleteServiceList; TmpSvcNode; TmpSvcNode = DeleteServiceList) {
DeleteServiceList = DeleteServiceList->Next;
MyFree(TmpSvcNode);
}
}
if (Err == NO_ERROR) {
//
// give a +ve affirmation of install
// if copy install, only do in Verbose-Logging, else do for standard Info-Logging
//
WriteLogEntry(
LogContext,
DoFullInstall?DRIVER_LOG_INFO:DRIVER_LOG_VERBOSE,
MSG_LOG_INSTALLED,
NULL,
DeviceFullID);
} else {
//
// indicate install failed, display error
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_INSTALL_ERROR_ENCOUNTERED,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
}
if (slot_deviceID) {
ReleaseLogInfoSlot(LogContext,slot_deviceID);
}
if (slot_section) {
ReleaseLogInfoSlot(LogContext,slot_section);
}
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_INSTALL_TIME,
NULL); // text message
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiInstallDriverFiles(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_INSTALLDEVICEFILES
This routine is similiar to a combination of SetupDiRegisterCoDeviceInstallers,
SetupDiInstallDeviceInterfaces, and SetupDiInstallDevice, but it only performs
the file copy commands in the install sections, and will not attempt to
configure the device in any way. This API is useful for pre-copying a device's
driver files.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set containing
a device information element to be installed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for which
a driver is to be installed.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
A driver must be selected for the specified device information element before
calling this API.
--*/
{
return _SetupDiInstallDevice(DeviceInfoSet, DeviceInfoData, FALSE);
}
BOOL
WINAPI
SetupDiRemoveDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_REMOVE
This routine removes a device from the system.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
device is to be removed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for
which a device is to be removed. This is an IN OUT parameter, since the
DevInst field of the structure may be updated with a new handle value upon
return. (If this is a global removal, or the last hardware profile-specific
removal, then all traces of the devinst are removed from the registry, and
the handle becomes NULL at that point.)
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
This routine will remove the device from the system, deleting both of its
registry keys, and dynamically stopping the device if its DevInst is 'live'.
If the device cannot be dynamically stopped, then flags will be set in the
install parameter block that will eventually cause the user to be prompted
to shut the system down. The removal is either global to all hardware
profiles, or specific to one hardware profile depending on the contents of
the ClassInstallParams field.
** This API cannot be called for a remote HDEVINFO set **
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err, ConfigFlags;
PDEVINFO_ELEM DevInfoElem;
PDEVINSTALL_PARAM_BLOCK dipb;
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
PSP_REMOVEDEVICE_PARAMS RemoveDevParams;
BOOL IsCurrentHwProfile = FALSE;
ULONG HwProfFlags;
DWORD HwProfileToRemove;
HWPROFILEINFO HwProfileInfo;
BOOL RemoveDevInst = FALSE, NukeDevInst = FALSE;
BOOL RemoveGlobally;
DEVINST DevNodeRemoved = 0;
DWORD i;
PDEVICE_INTERFACE_NODE DeviceInterfaceNode;
CONFIGRET cr;
ULONG ulStatus;
ULONG ulProblem;
PSETUP_LOG_CONTEXT LogContext = NULL;
DWORD slot_deviceID = 0;
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
Err = ERROR_INVALID_HANDLE;
goto clean1;
}
LogContext = pDeviceInfoSet->InstallParamBlock.LogContext;
Err = NO_ERROR;
try {
//
// This operation isn't supported for a remote HDEVINFO...
//
if(pDeviceInfoSet->hMachine) {
Err = ERROR_REMOTE_REQUEST_UNSUPPORTED;
goto clean0;
}
//
// Locate the devinfo element to be removed.
//
if(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL)) {
dipb = &(DevInfoElem->InstallParamBlock);
LogContext = dipb->LogContext;
} else {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// This routine can't be called if non-native drivers are involved.
// (Note: while presently this doesn't matter, it's possible that in
// the future uninstall will involve running one or more INF "uninstall"
// sections, and I don't want to limit that possibility by introducing
// the complication of non-native driver nodes.)
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALTPLATFORM_DRVSEARCH) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
if(CM_Get_DevInst_Status_Ex(&ulStatus, &ulProblem, DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine) == CR_SUCCESS &&
(ulStatus & DN_ROOT_ENUMERATED) &&
!(ulStatus & DN_DISABLEABLE)) {
//
// we cannot remove a root enumerated non-disableable device
//
Err = ERROR_NOT_DISABLEABLE;
goto clean0;
}
//
// Retrieve the name of the device instance. This is necessary, because
// we're about to remove the DEVINST, but we need to be able to locate it
// again, as a phantom. This should never fail.
//
if(CM_Get_Device_ID_Ex(DevInfoElem->DevInst,
DeviceInstanceId,
SIZECHARS(DeviceInstanceId),
0,
pDeviceInfoSet->hMachine) != CR_SUCCESS) {
MYASSERT(!CR_SUCCESS);
Err = ERROR_INVALID_DATA;
goto clean0;
}
if(slot_deviceID == 0) {
slot_deviceID = AllocLogInfoSlotOrLevel(LogContext,DRIVER_LOG_INFO,FALSE);
}
WriteLogEntry(
LogContext,
slot_deviceID,
MSG_LOG_DO_REMOVE,
NULL, // text message
DeviceInstanceId);
//
// See if there's a SP_REMOVEDEVICE_PARAMS structure we need to pay
// attention to.
//
if((dipb->Flags & DI_CLASSINSTALLPARAMS) &&
(dipb->ClassInstallHeader->InstallFunction == DIF_REMOVE)) {
RemoveDevParams = (PSP_REMOVEDEVICE_PARAMS)(dipb->ClassInstallHeader);
if(RemoveGlobally = (RemoveDevParams->Scope == DI_REMOVEDEVICE_GLOBAL)) {
//
// We are doing a global removal. We still want to set CSCONFIGFLAG_DO_NOT_CREATE
// for this device in the current hardware profile, so that someone else happening
// to do an enumeration won't turn this guy back on before we get a chance to
// remove it.
//
HwProfileToRemove = 0;
} else {
//
// Remove device from a particular hardware profile.
//
HwProfileToRemove = RemoveDevParams->HwProfile;
//
// Set the CSCONFIGFLAG_DO_NOT_CREATE flag for the specified hardware profile.
//
if(CM_Get_HW_Prof_Flags_Ex(DeviceInstanceId,
HwProfileToRemove,
&HwProfFlags,
0,
pDeviceInfoSet->hMachine) == CR_SUCCESS) {
HwProfFlags |= CSCONFIGFLAG_DO_NOT_CREATE;
} else {
HwProfFlags = CSCONFIGFLAG_DO_NOT_CREATE;
}
Err = MapCrToSpError(
CM_Set_HW_Prof_Flags_Ex(DeviceInstanceId, HwProfileToRemove,
HwProfFlags, 0,pDeviceInfoSet->hMachine),
ERROR_INVALID_DATA
);
if(Err != NO_ERROR) {
goto clean0;
}
//
// Determine if we are deleting the device from the current hw profile.
//
if((HwProfileToRemove == 0) ||
((CM_Get_Hardware_Profile_Info_Ex((ULONG)-1, &HwProfileInfo,
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
(HwProfileInfo.HWPI_ulHWProfile == HwProfileToRemove))) {
IsCurrentHwProfile = TRUE;
}
}
//
// Is this the current hardware profile or a global removal AND
// is there a present device?
//
if((IsCurrentHwProfile || RemoveGlobally) &&
!(DevInfoElem->DiElemFlags & DIE_IS_PHANTOM) &&
!(dipb->Flags & DI_DONOTCALLCONFIGMG)) {
RemoveDevInst = TRUE;
}
} else {
//
// No device removal params given, so do a global removal.
//
RemoveGlobally = TRUE;
HwProfileToRemove = 0;
if(!(dipb->Flags & DI_DONOTCALLCONFIGMG)) {
RemoveDevInst = TRUE;
}
}
//
// If this is a global removal, or the last hardware profile-specific one, then clean up
// the registry.
//
if(RemoveGlobally || IsDevRemovedFromAllHwProfiles(DeviceInstanceId,pDeviceInfoSet->hMachine)) {
NukeDevInst = TRUE;
}
if(RemoveDevInst) {
TCHAR VetoName[MAX_PATH];
PNP_VETO_TYPE VetoType;
cr = CM_Query_And_Remove_SubTree_Ex(DevInfoElem->DevInst,
&VetoType,
VetoName,
SIZECHARS(VetoName),
CM_REMOVE_NO_RESTART |
((DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NOUIONQUERYREMOVE)
? CM_REMOVE_UI_NOT_OK
: CM_REMOVE_UI_OK),
pDeviceInfoSet->hMachine
);
if(cr == CR_SUCCESS) {
//
// Device instance successfully removed--now locate it as a phantom.
//
CM_Locate_DevInst_Ex(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_PHANTOM,
pDeviceInfoSet->hMachine);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
//
// Set the DevNodeRemoved DevNode so that we know we need to
// call CM_Setup_DevInst with the CM_SETUP_DEVNODE_RESET flag
// to allow this devnode to come back online on the next
// enumeration.
//
DevNodeRemoved = DevInfoElem->DevInst;
} else {
//
// If the failure was due to a veto, then log information about
// who vetoed us.
//
// SPLOG--write out an entry with real logging.
//
if(cr == CR_REMOVE_VETOED) {
//
// get the LogContext from dipb which should be a pointer
// to the appropriate DevInstallParamBlock
//
_WriteVetoLogEntry(
dipb->LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REMOVE_VETOED_IN_UNINSTALL,
DeviceInstanceId,
VetoName,
VetoType);
}
if (cr != CR_INVALID_DEVNODE) {
SetDevnodeNeedsRebootProblemWithArg2(DevInfoElem,
pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_QR_VETOED_UNINSTALL,
cr,
(ULONG_PTR)_MapCmRetToString(cr)
);
}
}
}
if(NukeDevInst) {
//
// Remove all traces of this device from the registry.
//
pSetupDeleteDevRegKeys(DevInfoElem->DevInst,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGSPECIFIC,
(DWORD)-1,
DIREG_BOTH,
TRUE,
pDeviceInfoSet->hMachine // must be NULL
);
cr = CM_Uninstall_DevInst_Ex(DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine);
if (cr != CR_SUCCESS) {
//
// we try to catch this earlier
//
Err = MapCrToSpError(cr, ERROR_INVALID_DATA);
goto clean0;
}
//
// The above API will also remove all device interfaces associated with this
// device instance. Now we need to mark every device interface node that we
// are tracking for this devinfo element to indicate that they have been removed.
//
for(i = 0; i < DevInfoElem->InterfaceClassListSize; i++) {
for(DeviceInterfaceNode = DevInfoElem->InterfaceClassList[i].DeviceInterfaceNode;
DeviceInterfaceNode;
DeviceInterfaceNode = DeviceInterfaceNode->Next) {
DeviceInterfaceNode->Flags |= SPINT_REMOVED;
}
}
//
// Mark this device information element as unregistered, and set its
// devinst handle to NULL.
//
DevInfoElem->DiElemFlags &= ~DIE_IS_REGISTERED;
DevInfoElem->DevInst = (DEVINST)0;
}
//
// Now update the DevInst field of the DeviceInfoData structure with the new
// value of the devinst handle (possibly NULL).
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
clean0: ; // nothing to do.
//
// If the DevNode was really removed then we need to call CM_Setup_DevInst
// with the CM_SETUP_DEVNODE_RESET flag to allow the devnode to come back
// on the next enumeration.
//
if (DevNodeRemoved) {
CM_Setup_DevInst_Ex(DevNodeRemoved,
CM_SETUP_DEVNODE_RESET,
pDeviceInfoSet->hMachine
);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
UnlockDeviceInfoSet(pDeviceInfoSet);
clean1:
if (slot_deviceID) {
if (Err == NO_ERROR) {
//
// give a +ve affirmation of remove
//
WriteLogEntry(
LogContext,
DRIVER_LOG_INFO,
MSG_LOG_REMOVED,
NULL);
} else {
//
// indicate remove failed, display error
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_REMOVE_ERROR,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
}
ReleaseLogInfoSlot(LogContext,slot_deviceID);
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiUnremoveDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_UNREMOVE
This routine unremoves a device from the system.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
device is to be unremoved.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for
which a device is to be unremoved. This is an IN OUT parameter, since the
DevInst field of the structure may be updated with a new handle value upon
return.
This device must contain class install parameters for DIF_UNREMOVE
or the API will fail with ERROR_NO_CLASSINSTALL_PARAMS.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
This function will unremove the the device on the system, dynamically starting
the device if possible. If the device cannot be dynamically started, then flags
will be set in the device install parameters that will eventually cause the user
to be prompted to shut the system down.
The unremoval is specific to one configuration, specified in the HwProfile field
of the SP_UNREMOVEDEVICE_PARAMS class install parameters associated with this
device information element. (The Scope field of this structure must be set to
DI_UNREMOVEDEVICE_CONFIGSPECIFIC.)
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err;
PDEVINFO_ELEM DevInfoElem;
PDEVINSTALL_PARAM_BLOCK dipb;
PSP_UNREMOVEDEVICE_PARAMS UnremoveDevParams;
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
ULONG HwProfFlags;
HWPROFILEINFO HwProfileInfo;
DEVINST dnRoot;
PSETUP_LOG_CONTEXT LogContext = NULL;
CONFIGRET cr;
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
Err = ERROR_INVALID_HANDLE;
goto clean1;
}
LogContext = pDeviceInfoSet->InstallParamBlock.LogContext;
Err = NO_ERROR;
try {
//
// Locate the devinfo element to be unremoved.
//
if(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL)) {
dipb = &(DevInfoElem->InstallParamBlock);
LogContext = dipb->LogContext;
} else {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// This routine can't be called if non-native drivers are involved.
// (Note: while presently this doesn't matter, it's possible that in
// the future this operation could involve running one or more INF
// sections, and I don't want to limit that possibility by introducing
// the complication of non-native driver nodes.)
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALTPLATFORM_DRVSEARCH) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
//
// We'd better have DIF_UNREMOVE class install params
//
if(!(dipb->Flags & DI_CLASSINSTALLPARAMS) ||
(dipb->ClassInstallHeader->InstallFunction != DIF_UNREMOVE)) {
Err = ERROR_NO_CLASSINSTALL_PARAMS;
goto clean0;
}
UnremoveDevParams = (PSP_UNREMOVEDEVICE_PARAMS)(dipb->ClassInstallHeader);
//
// This only works in a hardware profile-specific manner.
//
MYASSERT(UnremoveDevParams->Scope == DI_UNREMOVEDEVICE_CONFIGSPECIFIC);
//
// Retrieve the name of the device instance. We need to do this because
// accessing hardware profile-specific config flags is done via name instead
// of devnode handle. (Also, we'll need this later after re-enumerating
// this device's parent.)
//
if((cr = CM_Get_Device_ID_Ex(DevInfoElem->DevInst,
DeviceInstanceId,
SIZECHARS(DeviceInstanceId),
0,
pDeviceInfoSet->hMachine)) != CR_SUCCESS) {
Err = MapCrToSpError(cr,ERROR_INVALID_DATA);
goto clean0;
}
if(CM_Get_HW_Prof_Flags_Ex(DeviceInstanceId,
UnremoveDevParams->HwProfile,
&HwProfFlags,
0,
pDeviceInfoSet->hMachine) == CR_SUCCESS) {
HwProfFlags &= ~CSCONFIGFLAG_DO_NOT_CREATE;
Err = MapCrToSpError(
CM_Set_HW_Prof_Flags_Ex(DeviceInstanceId, UnremoveDevParams->HwProfile,
HwProfFlags, 0,pDeviceInfoSet->hMachine),
ERROR_INVALID_DATA
);
if(Err != NO_ERROR) {
goto clean0;
}
}
//
// Determine if we are trying to un-remove the device in the current
// hardware profile.
//
if((UnremoveDevParams->HwProfile == 0) ||
((CM_Get_Hardware_Profile_Info_Ex((ULONG)-1, &HwProfileInfo,
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
(HwProfileInfo.HWPI_ulHWProfile == UnremoveDevParams->HwProfile))) {
//
// Make sure the device has started correctly.
//
if(CM_Locate_DevInst_Ex(&dnRoot, NULL, CM_LOCATE_DEVINST_NORMAL,pDeviceInfoSet->hMachine) == CR_SUCCESS) {
//
// Try to get this device enumerated
//
CM_Reenumerate_DevInst_Ex(dnRoot, CM_REENUMERATE_SYNCHRONOUS,pDeviceInfoSet->hMachine);
if(CM_Locate_DevInst_Ex(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_NORMAL,
pDeviceInfoSet->hMachine) == CR_SUCCESS) {
CheckIfDevStarted(DevInfoElem, pDeviceInfoSet);
} else {
//
// We couldn't locate the devnode. We don't need to
// request a reboot, because if the devnode ever shows up
// again, we should be able to bring it back on-line just
// fine.
//
// Retrieve the devnode as a phantom
//
CM_Locate_DevInst_Ex(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_PHANTOM,
pDeviceInfoSet->hMachine
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
}
//
// Update the caller's buffer to reflect the new device instance handle
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
} else {
//
// We couldn't locate the root of the hardware tree! This should never happen...
//
Err = ERROR_INVALID_DATA;
}
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
UnlockDeviceInfoSet(pDeviceInfoSet);
clean1:
if (Err == NO_ERROR) {
//
// give a +ve affirmation of unremove
//
WriteLogEntry(
LogContext,
DRIVER_LOG_INFO,
MSG_LOG_UNREMOVED,
NULL);
} else {
//
// indicate unremove failed, display error
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_UNREMOVE_ERROR,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiMoveDuplicateDevice(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DestinationDeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_MOVEDEVICE. This routine (and DIF code) has been
deprecated.
Arguments:
DeviceInfoSet - Supplied a handle to the device information set for which
a device was to be moved.
DestinationDeviceInfoData - Supplied the address of a SP_DEVINFO_DATA
structure for the device instance that was the destination of the move.
Return Value:
This function always returns FALSE, with GetLastError returning
ERROR_DI_FUNCTION_OBSOLETE.
--*/
{
UNREFERENCED_PARAMETER(DeviceInfoSet);
UNREFERENCED_PARAMETER(DestinationDeviceInfoData);
SetLastError(ERROR_DI_FUNCTION_OBSOLETE);
return FALSE;
}
BOOL
WINAPI
SetupDiChangeState(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_PROPERTYCHANGE
This routine is used to change the state of an installed device.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
device's state is to be changed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure identifying
the device whose state is to be changed. This is an IN OUT parameter, since
the DevInst field of the structure may be updated with a new handle value upon
return.
Return Value:
If the function succeeds, and there are files to be copied, the return value is TRUE.
If the function fails, the return value is FALSE, and GetLastError returns the cause
of failure.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err;
PDEVINFO_ELEM DevInfoElem;
PDEVINSTALL_PARAM_BLOCK dipb = NULL;
DWORD dwConfigFlags;
HKEY hk;
DEVINST dnToReenum;
DWORD dwStateChange = 0;
DWORD dwFlags = 0;
ULONG lParam = 0;
TCHAR szDevID[MAX_DEVICE_ID_LEN];
DWORD dwHWProfFlags;
HWPROFILEINFO HwProfileInfo;
CONFIGRET cr;
DWORD slot_deviceID = 0;
DWORD action = MSG_LOG_PROPERTYCHANGE_ERROR;
DWORD actionerr = MSG_LOG_PROPERTYCHANGE_ERROR;
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Err = NO_ERROR;
try {
//
// Locate the devinfo element whose state is to be changed.
//
if(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL)) {
dipb = &(DevInfoElem->InstallParamBlock);
} else {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
if((cr = CM_Get_Device_ID_Ex(DevInfoElem->DevInst,
szDevID,
SIZECHARS(szDevID),
0,
pDeviceInfoSet->hMachine)) != CR_SUCCESS) {
//
// this should never fail
//
MYASSERT(!CR_SUCCESS);
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
slot_deviceID = AllocLogInfoSlotOrLevel(dipb->LogContext,DRIVER_LOG_INFO,FALSE);
if(slot_deviceID) {
WriteLogEntry(
dipb->LogContext,
slot_deviceID,
MSG_LOG_DO_PROPERTYCHANGE,
NULL, // text message
szDevID);
}
//
// This routine can't be called if non-native drivers are involved.
// (Note: while presently this doesn't matter, it's possible that in
// the future this operation could involve running one or more INF
// sections, and I don't want to limit that possibility by introducing
// the complication of non-native driver nodes.)
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALTPLATFORM_DRVSEARCH) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
if(!(dipb->Flags & DI_CLASSINSTALLPARAMS) ||
(dipb->ClassInstallHeader->InstallFunction != DIF_PROPERTYCHANGE)) {
//
// Don't have any class install parameters to tell us what needs to be done!
//
Err = ERROR_NO_CLASSINSTALL_PARAMS;
goto clean0;
}
if(!DevInfoElem->DevInst) {
Err = ERROR_NO_SUCH_DEVINST;
goto clean0;
}
dwStateChange = ((PSP_PROPCHANGE_PARAMS)(dipb->ClassInstallHeader))->StateChange;
dwFlags = ((PSP_PROPCHANGE_PARAMS)(dipb->ClassInstallHeader))->Scope;
lParam = ((PSP_PROPCHANGE_PARAMS)(dipb->ClassInstallHeader))->HwProfile;
//
// DICS_FLAG_CONFIGGENERAL is allowed below and is ignored
// people are relying on this broken behaviour
//
switch(dwStateChange) {
case DICS_ENABLE:
if(dwFlags == DICS_FLAG_GLOBAL) {
action = MSG_LOG_PROPERTYCHANGE_ENABLE_GLOBAL;
actionerr = MSG_LOG_PROPERTYCHANGE_ENABLE_GLOBAL_ERR;
//
// Clear the Disabled config flag, and attempt to enumerate the
// device. Presumably it has a device node, it is just dormant (ie
// prob 80000001).
//
dwConfigFlags = GetDevInstConfigFlags(DevInfoElem->DevInst,
0,pDeviceInfoSet->hMachine) & ~CONFIGFLAG_DISABLED;
//
// Set the New config flags value
//
CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0,
pDeviceInfoSet->hMachine);
if(!(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
if(CM_Enable_DevNode_Ex(DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine) == CR_SUCCESS) {
//
// Find the parent of this devnode and reenumerate it to bring this devnode online.
//
if (CM_Get_Parent_Ex(&dnToReenum, DevInfoElem->DevInst, 0, pDeviceInfoSet->hMachine) == CR_SUCCESS) {
//
// Process this devnode now.
//
CM_Reenumerate_DevNode_Ex(dnToReenum, CM_REENUMERATE_SYNCHRONOUS,pDeviceInfoSet->hMachine);
}
//
// See if we sucessfully started dynamically.
//
CheckIfDevStarted(DevInfoElem, pDeviceInfoSet);
} else {
//
// We could not enable so we should restart
//
SetDevnodeNeedsRebootProblem(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_ENABLE_FAILED);
}
}
} else {
action = MSG_LOG_PROPERTYCHANGE_ENABLE_PROFILE;
actionerr = MSG_LOG_PROPERTYCHANGE_ENABLE_PROFILE_ERR;
//
// Get the hardware profile-specific flags
//
if(CM_Get_HW_Prof_Flags_Ex(szDevID, lParam, &dwHWProfFlags, 0,pDeviceInfoSet->hMachine) == CR_SUCCESS) {
//
// Clear the Disabled bit.
//
dwHWProfFlags &= ~CSCONFIGFLAG_DISABLED;
} else {
dwHWProfFlags = 0;
}
//
// Set the profile Flags for this device to Enabled. Setting the flags will
// also bring the devnode on-line, if we're modifying the current hardware
// profile.
//
cr = CM_Set_HW_Prof_Flags_Ex(szDevID, lParam, dwHWProfFlags, 0, pDeviceInfoSet->hMachine);
if(cr == CR_NEED_RESTART) {
SetDevnodeNeedsRebootProblem(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_HW_PROF_ENABLE_FAILED);
} else if(cr != CR_SUCCESS) {
Err = MapCrToSpError(cr, ERROR_INVALID_DATA);
goto clean0;
}
}
break;
case DICS_DISABLE:
if(dwFlags == DICS_FLAG_GLOBAL) {
BOOL disabled = FALSE;
action = MSG_LOG_PROPERTYCHANGE_DISABLE_GLOBAL;
actionerr = MSG_LOG_PROPERTYCHANGE_DISABLE_GLOBAL_ERR;
//
// we try to dynamically disable a device
// if it fails with anything but CR_NOT_DISABLEABLE
// then we set flag to try to disable it on reboot
//
if(!(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
cr = CM_Disable_DevNode_Ex(DevInfoElem->DevInst,
CM_DISABLE_POLITE | CM_DISABLE_UI_NOT_OK,
pDeviceInfoSet->hMachine
);
if (cr == CR_SUCCESS) {
//
// we managed to disable it immediately
//
disabled = TRUE;
} else if (cr == CR_NOT_DISABLEABLE) {
//
// we couldn't and shouldn't try to disable it
//
Err = MapCrToSpError(cr, ERROR_INVALID_DATA);
goto clean0;
} else {
//
// set flag to indicate a reboot is required to disable this
//
SetDevnodeNeedsRebootProblem(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_DISABLE_FAILED);
}
}
//
// Note: There is a case where reboot-disabled device becomes non-disableable
// nothing we can do about this!
//
// Try and set the Disabled config flag if not already set, even if we managed to disable it
//
dwConfigFlags = GetDevInstConfigFlags(DevInfoElem->DevInst,
0,pDeviceInfoSet->hMachine);
if ((dwConfigFlags & CONFIGFLAG_DISABLED) == 0) {
dwConfigFlags |= CONFIGFLAG_DISABLED;
//
// Set the New config flags value
//
cr = CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0,
pDeviceInfoSet->hMachine);
if (cr != CR_SUCCESS) {
Err = MapCrToSpError(cr, ERROR_INVALID_DATA);
goto clean0;
}
}
if (disabled) {
//
// We don't want DICS_DISABLE to increment the disable
// ref count, so if we disabled the device then we
// always want to enable it. Note, however, that if we
// were successful in setting the CONFIGFLAG_DISABLED
// then the device won't come back when we enable it.
//
CM_Enable_DevNode_Ex(DevInfoElem->DevInst,
0,
pDeviceInfoSet->hMachine);
}
} else {
action = MSG_LOG_PROPERTYCHANGE_DISABLE_PROFILE;
actionerr = MSG_LOG_PROPERTYCHANGE_DISABLE_PROFILE_ERR;
//
// Get the hardware profile-specific flags
//
if(CM_Get_HW_Prof_Flags_Ex(szDevID, lParam,
&dwHWProfFlags, 0,pDeviceInfoSet->hMachine) != CR_SUCCESS) {
dwHWProfFlags = 0;
}
//
// Set the Disabled bit.
//
dwHWProfFlags |= CSCONFIGFLAG_DISABLED;
//
// Set the profile Flags for this device to Disabled. Setting this
// flag will also take this device off-line, if we're modifying the
// current hardware profile.
//
cr = CM_Set_HW_Prof_Flags_Ex(szDevID,
lParam,
dwHWProfFlags,
CM_SET_HW_PROF_FLAGS_UI_NOT_OK,
pDeviceInfoSet->hMachine
);
if(cr == CR_NEED_RESTART) {
SetDevnodeNeedsRebootProblem(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_HW_PROF_DISABLE_FAILED);
} else if(cr != CR_SUCCESS) {
Err = MapCrToSpError(cr, ERROR_INVALID_DATA);
goto clean0;
}
}
break;
case DICS_PROPCHANGE:
//
// Properties have changed, so we need to remove the Devnode, and
// re-enumerate its parent.
//
// Don't remove/reenumerate if reboot/restart is required, or if DI_DONOTCALLCONFIGMG
// is set (the device may implement some form of 'non-stop' property change mechanism).
//
if(dipb->Flags & (DI_DONOTCALLCONFIGMG | DI_NEEDREBOOT | DI_NEEDRESTART)) {
action = MSG_LOG_PROPERTYCHANGE_NORESTART;
} else {
TCHAR VetoName[MAX_PATH];
PNP_VETO_TYPE VetoType;
action = MSG_LOG_PROPERTYCHANGE_RESTART;
actionerr = MSG_LOG_PROPERTYCHANGE_RESTART_ERR;
CM_Get_Parent_Ex(&dnToReenum, DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine);
cr = CM_Query_And_Remove_SubTree_Ex(DevInfoElem->DevInst,
&VetoType,
VetoName,
SIZECHARS(VetoName),
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NOUIONQUERYREMOVE)
? CM_REMOVE_UI_NOT_OK
: CM_REMOVE_UI_OK,
pDeviceInfoSet->hMachine
);
if(cr == CR_SUCCESS) {
CM_Reenumerate_DevInst_Ex(dnToReenum, CM_REENUMERATE_SYNCHRONOUS,pDeviceInfoSet->hMachine);
DevInfoElem->DevInst = 0;
if(CM_Locate_DevInst_Ex(&(DevInfoElem->DevInst),
(DEVINSTID)szDevID,
CM_LOCATE_DEVINST_NORMAL,
pDeviceInfoSet->hMachine) == CR_SUCCESS) {
//
// Make sure the device instance started OK
//
if(!CheckIfDevStarted(DevInfoElem,pDeviceInfoSet)) {
action = MSG_LOG_PROPERTYCHANGE_RESTART_FAILED;
}
} else {
//
// We couldn't locate the devnode. We don't need
// to request a reboot, because if the devnode ever
// shows up again, we should be able to bring it
// back on-line just fine.
//
// Retrieve the devnode as a phantom
//
CM_Locate_DevInst_Ex(&(DevInfoElem->DevInst),
(DEVINSTID)szDevID,
CM_LOCATE_DEVINST_PHANTOM,
pDeviceInfoSet->hMachine
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
}
//
// Update the caller's buffer to reflect the new device instance handle
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
} else {
//
// If the failure was due to a veto, then log
// information about who vetoed us.
//
// SPLOG--write a log entry
//
if(cr == CR_REMOVE_VETOED) {
//
// get the LogContext from dipb which should be a pointer
// to the appropriate DevInstallParamBlock
//
_WriteVetoLogEntry(
dipb->LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REMOVE_VETOED_IN_PROPCHANGE,
szDevID,
VetoName,
VetoType);
}
SetDevnodeNeedsRebootProblem(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_VETOED_IN_PROPCHANGE);
action = MSG_LOG_PROPERTYCHANGE_RESTART_FAILED;
}
}
break;
case DICS_START:
action = MSG_LOG_PROPERTYCHANGE_START;
actionerr = MSG_LOG_PROPERTYCHANGE_START_ERR;
//
// DICS_START is always config specific (we enforce this in SetupDiSetClassInstallParams).
//
MYASSERT(dwFlags == DICS_FLAG_CONFIGSPECIFIC);
//
// Get the Profile Flags.
//
if(CM_Get_HW_Prof_Flags_Ex(szDevID, lParam, &dwHWProfFlags, 0,pDeviceInfoSet->hMachine) == CR_SUCCESS) {
//
// Clear the "don't start" bit.
//
dwHWProfFlags &= ~CSCONFIGFLAG_DO_NOT_START;
} else {
dwHWProfFlags = 0;
}
cr = CM_Set_HW_Prof_Flags_Ex(szDevID, lParam, dwHWProfFlags, 0, pDeviceInfoSet->hMachine);
if(cr == CR_NEED_RESTART) {
//
// Since setting/clearing the CSCONFIGFLAG_DO_NOT_START doesn't
// automatically effect a change, we should never get here.
//
SetDevnodeNeedsRebootProblem(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_CLEAR_CSCONFIGFLAG_DO_NOT_START);
action = MSG_LOG_PROPERTYCHANGE_START_FAILED;
} else if(cr != CR_SUCCESS) {
Err = MapCrToSpError(cr, ERROR_INVALID_DATA);
goto clean0;
}
//
// Start the device instance if this is for the current config (ie dwConfigID/lparam == 0)
//
if((lParam == 0) ||
((CM_Get_Hardware_Profile_Info_Ex((ULONG)-1, &HwProfileInfo,
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
(HwProfileInfo.HWPI_ulHWProfile == lParam)))
{
//
// Try to start the devnode.
//
if(!(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
CM_Setup_DevNode_Ex(DevInfoElem->DevInst, CM_SETUP_DEVNODE_READY,pDeviceInfoSet->hMachine);
if(!CheckIfDevStarted(DevInfoElem, pDeviceInfoSet)) {
action = MSG_LOG_PROPERTYCHANGE_START_FAILED;
}
} else {
action = MSG_LOG_PROPERTYCHANGE_START_FAILED;
}
}
break;
case DICS_STOP:
action = MSG_LOG_PROPERTYCHANGE_STOP;
actionerr = MSG_LOG_PROPERTYCHANGE_STOP_ERR;
//
// DICS_STOP is always config specific (we enforce this in SetupDiSetClassInstallParams).
//
MYASSERT(dwFlags == DICS_FLAG_CONFIGSPECIFIC);
//
// Get the Profile Flags.
//
if(CM_Get_HW_Prof_Flags_Ex(szDevID, lParam, &dwHWProfFlags,
0,pDeviceInfoSet->hMachine) != CR_SUCCESS) {
dwHWProfFlags = 0;
}
//
// Set the "don't start" bit.
//
dwHWProfFlags |= CSCONFIGFLAG_DO_NOT_START;
cr = CM_Set_HW_Prof_Flags_Ex(szDevID,
lParam,
dwHWProfFlags,
CM_SET_HW_PROF_FLAGS_UI_NOT_OK,
pDeviceInfoSet->hMachine
);
if(cr == CR_NEED_RESTART) {
//
// Since setting/clearing the CSCONFIGFLAG_DO_NOT_START doesn't
// automatically effect a change, we should never get here.
//
SetDevnodeNeedsRebootProblem(DevInfoElem,pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_SET_CSCONFIGFLAG_DO_NOT_START);
action = MSG_LOG_PROPERTYCHANGE_STOP_FAILED;
} else if(cr != CR_SUCCESS) {
Err = MapCrToSpError(cr, ERROR_INVALID_DATA);
goto clean0;
}
//
// Stop the device instance if this is for the current config (ie dwConfigID/lparam == 0)
//
if((lParam == 0) ||
((CM_Get_Hardware_Profile_Info_Ex((ULONG)-1, &HwProfileInfo,
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
(HwProfileInfo.HWPI_ulHWProfile == lParam)))
{
//
// Try to stop the devnode.
//
if(!(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
TCHAR VetoName[MAX_PATH];
PNP_VETO_TYPE VetoType;
//
// Remove the device instance in order to stop the device.
//
cr = CM_Query_And_Remove_SubTree_Ex(DevInfoElem->DevInst,
&VetoType,
VetoName,
SIZECHARS(VetoName),
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NOUIONQUERYREMOVE)
? CM_REMOVE_UI_NOT_OK
: CM_REMOVE_UI_OK,
pDeviceInfoSet->hMachine
);
if(cr == CR_SUCCESS) {
//
// Device instance successfully removed--now locate it as a phantom.
//
CM_Locate_DevInst_Ex(&(DevInfoElem->DevInst),
(DEVINSTID)szDevID,
CM_LOCATE_DEVINST_PHANTOM,
pDeviceInfoSet->hMachine);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
//
// Update the caller's buffer to reflect the new device instance handle.
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
} else {
//
// If the failure was due to a veto, then log
// information about who vetoed us.
//
// SPLOG--write out a real log entry
//
if(cr == CR_REMOVE_VETOED) {
//
// get the LogContext from dipb which should be a pointer
// to the appropriate DevInstallParamBlock
//
_WriteVetoLogEntry(
dipb->LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REMOVE_VETOED_IN_STOP,
szDevID,
VetoName,
VetoType);
}
SetDevnodeNeedsRebootProblemWithArg2(DevInfoElem,
pDeviceInfoSet,
MSG_LOG_REBOOT_QR_VETOED_IN_STOP,
cr,
(ULONG_PTR)_MapCmRetToString(cr)
);
}
} else {
action = MSG_LOG_PROPERTYCHANGE_STOP_FAILED;
}
}
break;
default:
action = actionerr = MSG_LOG_PROPERTYCHANGE_UNKNOWN;
Err = ERROR_DI_DO_DEFAULT;
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
if(slot_deviceID) {
if ((Err == NO_ERROR) || (Err == ERROR_DI_DO_DEFAULT)) {
//
// give a +ve affirmation of property change
//
WriteLogEntry(
dipb->LogContext,
Err ? DRIVER_LOG_ERROR : DRIVER_LOG_INFO,
action,
NULL,
dwStateChange,
dwFlags,
lParam
);
} else {
//
// indicate property change failed, display error
//
WriteLogEntry(
dipb->LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
actionerr,
NULL,
dwStateChange,
dwFlags,
lParam
);
WriteLogError(
dipb->LogContext,
DRIVER_LOG_ERROR,
Err);
}
ReleaseLogInfoSlot(dipb->LogContext,slot_deviceID);
}
UnlockDeviceInfoSet(pDeviceInfoSet);
SetLastError(Err);
return(Err == NO_ERROR);
}
DWORD
GetNewInfName(
IN HWND Owner, OPTIONAL
IN PCTSTR OemInfName,
IN PCTSTR OemInfOriginalName,
IN PCTSTR OemInfCatName, OPTIONAL
OUT PTSTR NewInfName,
IN DWORD NewInfNameSize,
OUT PDWORD RequiredSize,
OUT PNEWINF_COPYTYPE CopyNeeded,
IN BOOL ReplaceOnly,
IN PCTSTR DeviceDesc, OPTIONAL
IN DWORD DriverSigningPolicy,
IN DWORD Flags,
IN PCTSTR AltCatalogFile, OPTIONAL
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
OUT PDWORD DriverSigningError, OPTIONAL
OUT PTSTR CatalogFilenameOnSystem,
IN PSETUP_LOG_CONTEXT LogContext,
IN OUT PVERIFY_CONTEXT VerifyContext, OPTIONAL
OUT HANDLE *hWVTStateData OPTIONAL
)
/*++
Routine Description:
This routine finds a unique INF name of the form "<systemroot>\Inf\OEM<n>.INF",
and returns it in the supplied buffer. It leaves an (empty) file of that
name in the INF directory, so that anyone else who attempts to generate a
unique filename won't pick the same name.
NOTE: We will search the INF directory to determine if the specified INF is
already present there based on the criteria outlined for SetupCopyOEMInf.
If so, then we will return the existing name. This name may be an
OEM<n>.INF form, or it may be the same name as the source INF.
Arguments:
Owner - supplies window to own any signature verification problem dialogs
that must be displayed.
OemInfName - Supplies the full pathname of the OEM INF that needs to be
copied into the Inf directory (under a unique name).
OemInfOriginalName - Supplies the original (simple) filename of the INF, to
be used for digital signature verification (i.e., the INF is only known
to the catalog under its original name).
OemInfCatName - Optionally, supplies the simple filename of the catalog file
specified by the OEM INF via a CatalogFile= entry in its [Version]
section.
NewInfName - supplies the address of a character buffer to store the unique
name in.
NewInfNameSize - Specifies the size, in characters, of the NewInfName buffer.
RequiredSize - supplies the address of a variable that receives the size, in
characters, required to store the full new filename.
CopyNeeded - Supplies the address of an enum variable that is set upon
successful return to indicate whether or not the OEM INF actually needs
to be copied (and whether or not the previously-existing INF, if found,
is zero-length. This variable will be set to one of the following
values:
NewInfCopyNo - no need to copy--INF already present in destination
NewInfCopyYes, - new INF placeholder created--need to copy real INF
NewInfCopyZeroLength - previously-existing zero-length INF match found
ReplaceOnly - If this flag is set, then this routine will fail if it doesn't
find that the INF/CAT is already installed.
DeviceDesc - Optionally, supplies the device description to be used in the
digital signature verification error dialogs that may be popped up.
DriverSigningPolicy - supplies the driver signing policy currently in
effect. Used when determining whether we can validate via an
Authenticode catalog, and to control behavior of
_HandleFailedVerification, if necessary.
Flags - supplies flags which alter the behavior of the routine. May be a
combination of the following values:
SCOI_NO_UI_ON_SIGFAIL - indicates whether user should be prompted (per
DriverSigningPolicy) if a digital signature
failure is encountered. Used when calling
_HandleFailedVerification, if necessary.
SCOI_NO_ERRLOG_ON_MISSING_CATALOG - if there's a signature verification
failure due to the INF lacking a
CatalogFile= entry, then that error
will be ignored if this flag is set
(no UI will be given, and no log
entry will be generated).
SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES - Install the INF and CAT under
their original (current) names
(i.e., don't generate a unique
oem<n>.inf/cat name). Used only
for exception INFs.
SCOI_ABORT_IF_UNSIGNED - If the INF is unsigned (and user wants to copy
it anyway, or policy is Ignore), then _don't_
copy the INF and instead return the error
ERROR_SET_SYSTEM_RESTORE_POINT. This gives
the caller a chance to set a system restore
point prior to installing the unsigned package.
SCOI_TRY_UPDATE_PNF - If an existing PNF cannot be updated, don't
consider it fatal.
AltCatalogFile - Optionally, supplies the full pathname of a catalog file to
be installed and used for verification of the INF in cases where the INF
doesn't specify a CatalogFile= entry (i.e., when the OemInfCatName
parameter is not specified.
If this parameter is specified (and OemInfCatName isn't specified), then
this catalog will be used to validate the INF, and if successful, the
catalog will be installed into the system using its current name (thus
overwriting any existing catalog having that name). Nothing more will
be done with the INF--we won't even create a zero-length placeholder for
it in this case.
AltPlatformInfo - Optionally, supplies alternate platform information to be
used in digital signature verification instead of the default (native)
platform info.
DriverSigningError - Optionally, supplies the address of a variable that
receives the error encountered when attempting to verify the digital
signature of either the INF or associated catalog. If no digital
signature problems were encountered, this is set to NO_ERROR. (Note
that this value can come back as NO_ERROR, yet GetNewInfName still
failed for some other reason).
CatalogFilenameOnSystem - receives the fully-qualified path of the catalog
file within the catalog store where this INF's catalog file was
installed to. This buffer should be at least MAX_PATH bytes (ANSI
version) or chars (Unicode version).
LogContext - supplies a LogContext to be used throughout the function.
VerifyContext - optionally, supplies the address of a structure that caches
various verification context handles. These handles may be NULL (if
not previously acquired, and they may be filled in upon return (in
either success or failure) if they were acquired during the processing
of this verification request. It is the caller's responsibility to
free these various context handles when they are no longer needed by
calling pSetupFreeVerifyContextMembers.
hWVTStateData - if supplied, this parameter points to a buffer that
receives a handle to WinVerifyTrust state data. This handle will be
returned only when validation was successfully performed using
Authenticode policy. This handle may be used, for example, to retrieve
signer info when prompting the user about whether they trust the
publisher. (The status code returned will indicate whether or not this
is necessary, see "Return Value" section below.)
This parameter *must* be supplied if the DRIVERSIGN_ALLOW_AUTHENTICODE
bit is set in the caller-specified DriverSigningPolicy. If the
routine fails, then this handle will be set to NULL.
It is the caller's responsibility to close this handle when they're
finished with it by calling pSetupCloseWVTStateData().
Return Value:
If the routine is successful, and the INF was successfully validated via
driver signing policy, then the return value is NO_ERROR.
If the routine is successful, the INF was successfully validated via
Authenticode policy, and the publisher was in the TrustedPublisher store,
then the return value is ERROR_AUTHENTICODE_TRUSTED_PUBLISHER.
If the routine was successful, the INF was successfully validated via
Authenticode policy, and the publisher was _not_ in the TrustedPublisher
store (hence we must prompt the user to establish their trust of the
publisher), then the return value is
ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED.
If a failure occurred, the return value is a Win32 error code indicating
the cause of the failure.
--*/
{
INT i;
HANDLE h;
DWORD Err, SavedErr;
DWORD AuthenticodeErr = NO_ERROR;
DWORD CpyErr;
TCHAR szNewName[MAX_PATH];
WIN32_FIND_DATA FindData;
HANDLE FindHandle;
PTSTR FilenamePart;
PTSTR FilenamePartCatFile;
DWORD OemInfFileSize, CatalogFileSize;
HANDLE OemInfFileHandle, OemInfMappingHandle;
HANDLE CatalogFileHandle, CatalogMappingHandle;
PVOID OemInfBaseAddress, CatalogBaseAddress;
DWORD CurInfFileSize;
HANDLE CurInfFileHandle, CurInfMappingHandle;
PVOID CurInfBaseAddress;
BOOL FileOfSameNameExists, MoreInfsToCheck;
WIN32_FILE_ATTRIBUTE_DATA FileAttribData;
BOOL FoundMatchingInf;
TCHAR PathBuffer[MAX_PATH];
TCHAR CatalogName[MAX_PATH];
SetupapiVerifyProblem Problem = SetupapiVerifyNoProblem;
PCTSTR ProblemFile;
DWORD NumCatalogsConsidered;
PTSTR LastResortInf;
BOOL FileNewlyCreated;
//
// Initially, assume that the specified INF isn't already present in the
// Inf directory.
//
*CopyNeeded = NewInfCopyYes;
//
// Initialize the driver signing error output parameter to success, and set
// the CatalogFilenameOnSystem character buffer to an empty string.
//
if(DriverSigningError) {
*DriverSigningError = NO_ERROR;
}
*CatalogFilenameOnSystem = TEXT('\0');
//
// This output parameter would get initialized if we ended up calling
// _VerifyFile, but we might not do that, so make sure it starts out as
// NULL (i.e., no user prompting for Authenticode publisher trust
// verification necessary)
//
if(hWVTStateData) {
*hWVTStateData = NULL;
}
//
// If the caller requested that we allow for verification via Authenticode
// policy, then they must also supply us with an output buffer in which to
// store the resultant handle for the WinVerifyTrust state data.
//
MYASSERT(!(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE) || hWVTStateData);
if(OemInfCatName || !AltCatalogFile) {
//
// The INF has a CatalogFile= entry, or we don't have an override. This
// means we want to do the 'normal' behavior of looking for an existing
// match in the INF directory, using default rules for which catalog
// files we'll consider during validation, etc.
//
// Examine all the existing OEM INFs in the Inf directory, to see if
// this INF already exists there. If so, we'll just return the name of
// the previously-existing file.
//
MYVERIFY(SUCCEEDED(StringCchCopy(szNewName,SIZECHARS(szNewName),InfDirectory)));
pSetupConcatenatePaths(szNewName, pszOemInfWildcard, SIZECHARS(szNewName), NULL);
//
// If we're supposed to install the INF and CAT under their original
// names, then we don't want to look at any of the oem<n>.inf files.
//
if(Flags & SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES) {
FindHandle = INVALID_HANDLE_VALUE;
//
// We'd better not have a policy that says it's acceptable to
// validate via Authenticode!
//
MYASSERT(!(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE));
} else {
FindHandle = FindFirstFile(szNewName, &FindData);
}
//
// Now reuse our buffer to look for an INF in the Inf directory having
// the same name as our OEM INF's original name.
//
FilenamePart = (PTSTR)pSetupGetFileTitle(szNewName);
MYVERIFY(SUCCEEDED(StringCchCopy(FilenamePart,
SIZECHARS(szNewName)-(FilenamePart-szNewName),
OemInfOriginalName)));
FileOfSameNameExists = GetFileAttributesEx(szNewName, GetFileExInfoStandard, &FileAttribData);
if(FileOfSameNameExists && !OemInfCatName) {
//
// We can try to match up an OEM INF with a system INF having the
// same name even if the INF doesn't specify a CatalogFile= entry,
// but _not_ if the system INF is zero-length!
//
if(!FileAttribData.nFileSizeLow) {
FileOfSameNameExists = FALSE;
}
}
if((FindHandle != INVALID_HANDLE_VALUE) || FileOfSameNameExists) {
//
// We have at least one INF to compare against, so open our source
// INF in preparation.
//
if(pSetupOpenAndMapFileForRead(OemInfName,
&OemInfFileSize,
&OemInfFileHandle,
&OemInfMappingHandle,
&OemInfBaseAddress) == NO_ERROR) {
if(OemInfCatName) {
//
// INF has an associated catalog--map it into memory for
// subsequent comparisons with existing installed catalogs.
//
MYVERIFY(SUCCEEDED(StringCchCopy(CatalogName,
SIZECHARS(CatalogName),
OemInfName)));
FilenamePartCatFile = (PTSTR)pSetupGetFileTitle(CatalogName);
MYVERIFY(SUCCEEDED(StringCchCopy(FilenamePartCatFile,
SIZECHARS(CatalogName)-(FilenamePartCatFile-CatalogName),
OemInfCatName)));
if(NO_ERROR != pSetupOpenAndMapFileForRead(
CatalogName,
&CatalogFileSize,
&CatalogFileHandle,
&CatalogMappingHandle,
&CatalogBaseAddress)) {
//
// Act as if the INF specified no CatalogFile. This
// will allow us to match up with any OEM<n>.INF that
// binary-compares with this one. We will consider
// this a digital signature verification failure,
// however. (Refer to code below that explicitly fails
// OEM<n>.INF files that don't have a CatalogFile
// entry.)
//
// Note, also, in this case we don't want to consider
// a previously-existing INF in the Inf directory that
// has this OEM INF's original name. That's because
// this would trip us up later because we'd believe we
// could do global validation on it (i.e., we'd think
// it was a system INF).
//
CatalogBaseAddress = NULL; // don't try to unmap and close later
if(FindHandle == INVALID_HANDLE_VALUE) {
//
// There are no OEM<n>.INF files to check--go ahead
// and bail.
//
goto FinishedCheckingOemInfs;
}
//
// There are some OEM<n>.INF files to check--make sure
// we _don't_ consider the originally-named file in
// %windir%\Inf, if it happens to exist.
//
FileOfSameNameExists = FALSE;
//
// Now make it look like the INF had no CatalogFile=
// entry in the first place.
//
OemInfCatName = NULL;
CatalogFileSize = 0;
}
} else {
//
// INF didn't have a CatalogFile= entry in its version
// section. This means we'll do global validation if we
// find an existing INF having this name that binary-compares
// identical. If the verification succeeds, then we'll consider
// this a match. Basically, this means you'll be able to
// re-install from system INFs, even if you point at them
// elsewhere (e.g., you point back at system.inf on the
// distribution media, but system.inf is already installed in
// your %windir%\Inf directory).
//
// If our INF doesn't specify a CatalogFile= entry, then
// we'll drop back to our pre-driver-signing behavior where
// we'll simply check to see if the INF's binary compare.
// If they do, then we know that there is no INF installed
// based on that matching INF's OEM name, thus we'll just
// drop out of the search loop and consider this a driver
// signing failure (which it is).
//
CatalogBaseAddress = NULL;
CatalogFileSize = 0;
}
LastResortInf = NULL;
do {
if(FileOfSameNameExists) {
if(FileAttribData.nFileSizeHigh) {
goto CheckNextOemInf;
}
if(FileAttribData.nFileSizeLow &&
(FileAttribData.nFileSizeLow != OemInfFileSize)) {
goto CheckNextOemInf;
}
//
// Note: We will consider a zero-length system INF
// that has the same name as our OEM INF, even if our
// OEM INF doesn't have a CatalogFile= entry in its
// version section. This allows us to re-use this name
// as long as we find our catalog is already installed.
//
CurInfFileSize = FileAttribData.nFileSizeLow;
} else {
if(FindData.nFileSizeHigh) {
goto CheckNextOemInf;
}
if(FindData.nFileSizeLow &&
(FindData.nFileSizeLow != OemInfFileSize)) {
goto CheckNextOemInf;
}
CurInfFileSize = FindData.nFileSizeLow;
//
// Build the fully-qualified path to the INF being
// compared.
//
MYVERIFY(SUCCEEDED(StringCchCopy(FilenamePart,
SIZECHARS(szNewName)-(FilenamePart-szNewName),
FindData.cFileName)));
}
//
// If the INF isn't zero-length, then map it into memory to
// see if it matches our OEM INF.
//
if(CurInfFileSize) {
if(NO_ERROR == pSetupOpenAndMapFileForRead(
szNewName,
&CurInfFileSize,
&CurInfFileHandle,
&CurInfMappingHandle,
&CurInfBaseAddress)) {
//
// Surround the following in try/except, in case we
// get an inpage error.
//
try {
//
// We've found a potential match.
//
FoundMatchingInf = !memcmp(OemInfBaseAddress,
CurInfBaseAddress,
OemInfFileSize
);
} except(EXCEPTION_EXECUTE_HANDLER) {
FoundMatchingInf = FALSE;
}
pSetupUnmapAndCloseFile(CurInfFileHandle,
CurInfMappingHandle,
CurInfBaseAddress
);
} else {
FoundMatchingInf = FALSE;
}
if(!FoundMatchingInf) {
goto CheckNextOemInf;
}
//
// If this is an OEM*.INF name and the INF has no
// CatalogFile= entry, then we've found our match. Set
// up our driver signing problem variables to indicate
// that there was an INF failure (since there was no
// catalog.
//
if(!OemInfCatName && !FileOfSameNameExists) {
*CopyNeeded = NewInfCopyNo;
Problem = SetupapiVerifyInfProblem;
ProblemFile = OemInfName;
Err = ERROR_NO_CATALOG_FOR_OEM_INF;
//
// go to end of loop (we'll drop out since we've
// found a match).
//
goto CheckNextOemInf;
}
} else {
//
// The current INF we're considering for a match is
// zero-length. This won't work for us if the INF
// we're searching for doesn't have a CatalogFile=
// entry, and this isn't an INF having the same name.
//
if(!OemInfCatName && !FileOfSameNameExists) {
goto CheckNextOemInf;
}
}
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// We aren't using crypto, so if the file we found
// isn't zero-length, we've found our match.
//
if(CurInfFileSize) {
Err = NO_ERROR;
} else {
//
// File is zero-length. Keep looking.
//
goto CheckNextOemInf;
}
} else {
//
// OK, the files binary compare OK (unless the one
// we're currently examining is zero length!), but
// we're not out of the woods yet! If the INF we're
// examining had a CatalogFile= entry, then generate
// the catalog name to be used for verification (based
// on the filename of the INF we're examining).
//
if(OemInfCatName) {
if(MYVERIFY(SUCCEEDED(StringCchCopy(
CatalogName,
SIZECHARS(CatalogName),
FilenamePart)))) {
PTSTR DotPart;
DotPart = _tcsrchr(CatalogName, TEXT('.'));
if(MYVERIFY(DotPart!=NULL)) {
MYVERIFY(SUCCEEDED(StringCchCopy(
DotPart,
SIZECHARS(CatalogName)-(DotPart-CatalogName),
pszCatSuffix)));
}
}
}
//
// Now verify the INF's signature against the specified
// catalog (or globally if the INF doesn't specify a
// catalog).
//
Err = _VerifyFile(LogContext,
VerifyContext,
(OemInfCatName ? CatalogName : NULL),
CatalogBaseAddress,
CatalogFileSize,
OemInfOriginalName,
OemInfName,
&Problem,
PathBuffer,
FALSE,
AltPlatformInfo,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
CatalogFilenameOnSystem,
&NumCatalogsConsidered,
NULL,
NULL,
NULL
);
if((Err != NO_ERROR) && (Err != ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH) &&
OemInfCatName &&
(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE)) {
//
// If we failed to validate via standard OS code-
// signing policy (for some reason other than an
// osattibute mismatch), and we're allowed to
// attempt validation via an Authenticode-signed
// catalog, then try again.
//
MYASSERT(!(*CatalogFilenameOnSystem));
AuthenticodeErr = _VerifyFile(
LogContext,
VerifyContext,
CatalogName,
CatalogBaseAddress,
CatalogFileSize,
OemInfOriginalName,
OemInfName,
&Problem,
PathBuffer,
FALSE,
AltPlatformInfo,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK
| VERIFY_FILE_USE_AUTHENTICODE_CATALOG),
CatalogFilenameOnSystem,
&NumCatalogsConsidered,
NULL,
NULL,
hWVTStateData
);
if((AuthenticodeErr == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// The catalog validated via Authenticode
// policy--reset the error (previously
// received during the failed attempt at
// validation via driversigning policy).
//
Err = NO_ERROR;
} else {
//
// Transfer Authenticode error over to be
// the "official" failure. We always want
// the Authenticode error to be NO_ERROR
// unless it's one of the two "special"
// values.
//
MYASSERT(AuthenticodeErr != NO_ERROR);
Err = AuthenticodeErr;
AuthenticodeErr = NO_ERROR;
}
}
}
if(Err == NO_ERROR) {
//
// We've found this INF/CAT combination already
// installed, and the signatures check out!
//
*CopyNeeded = CurInfFileSize ? NewInfCopyNo
: NewInfCopyZeroLength;
Problem = SetupapiVerifyNoProblem;
} else if(CurInfFileSize &&
(Err == ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH)) {
//
// We found this INF/CAT combination already installed,
// but it's no longer valid. This could happen, for
// example, if a driver WHQL-signed for a given version
// of the OS was installed on that version of the OS.
// Then, the OS was subsequently upgraded, and backward
// compatibility was not maintained for the device's
// class. Thus, while the INF and CAT were valid at
// the time of install, they no longer are valid (even
// though there's been no tampering with the INF/CAT).
//
// We still want to use this INF, but we also want to
// warn the user that it's not validly signed.
//
MYASSERT(Problem == SetupapiVerifyFileProblem);
MYASSERT(*CatalogFilenameOnSystem);
ProblemFile = PathBuffer;
*CopyNeeded = NewInfCopyNo;
} else {
//
// We should never see SetupapiVerifyCatalogProblem
// reported, since we're validating against a set of
// catalogs, and a match might be found in several
// (though, none turn out to successfully validate).
// It therefore makes no sense to "blame" any
// particular catalog.
//
MYASSERT(Problem != SetupapiVerifyCatalogProblem);
if((Problem == SetupapiVerifyFileProblem) &&
!(*CatalogFilenameOnSystem)) {
//
// We didn't find a catalog match--move on to the
// next INF (note: there still might be a match
// later on, because if the INF specified a
// CatalogFile= entry, we must match on _both_ the
// catalog filename _and_ the catalog's image.
//
// However, if the number of catalogs we considered
// when examining this INF/CAT combination was
// zero, that means that there is no installed
// catalog for this INF, thus we can use it if we
// don't find anything better. The reason why we'd
// rather not use this INF is that it obviously
// wasn't properly installed, hence the original
// source name and location info is almost
// certainly bogus. (One more thing--make sure the
// catalog isn't zero length. If it is, then that
// definitely implies that this was being used as a
// placeholder by setupapi, and we don't want to
// touch it!)
//
if(!NumCatalogsConsidered &&
!LastResortInf &&
CurInfFileSize)
{
LastResortInf = DuplicateString(szNewName);
SavedErr = Err;
}
goto CheckNextOemInf;
}
//
// We want to fall on through and proceed with
// examining other INFs for a potential match--this one
// ain't it!
//
}
CheckNextOemInf:
if(FileOfSameNameExists) {
FileOfSameNameExists = FALSE;
MoreInfsToCheck = (FindHandle != INVALID_HANDLE_VALUE);
} else {
MoreInfsToCheck = FindNextFile(FindHandle, &FindData);
}
} while((*CopyNeeded == NewInfCopyYes) && MoreInfsToCheck);
if(LastResortInf) {
if(*CopyNeeded == NewInfCopyYes) {
//
// We didn't find any better INF, so we'll try to use
// the incorrectly-installed INF.
//
// If this INF doesn't specify a CatalogFile= entry,
// then we'll just automatically set up the digital
// signature verification failure parameters, because
// that's the state we're in.
//
if(!OemInfCatName) {
Err = SavedErr;
Problem = SetupapiVerifyInfProblem;
MYVERIFY(SUCCEEDED(StringCchCopy(PathBuffer,
SIZECHARS(PathBuffer),
OemInfName)));
ProblemFile = PathBuffer;
} else {
//
// Attempt to verify our OEM INF's catalog, and if
// it verifies, then install it. We clear the
// problem set earlier, since we really don't have
// a problem (yet). If we do encounter a failure
// below, we'll set the failure as appropriate.
//
Problem = SetupapiVerifyNoProblem;
MYVERIFY(SUCCEEDED(StringCchCopy(CatalogName,
SIZECHARS(CatalogName),
OemInfName)));
*((PTSTR)pSetupGetFileTitle(CatalogName)) = TEXT('\0');
pSetupConcatenatePaths(CatalogName,
OemInfCatName,
SIZECHARS(CatalogName),
NULL
);
Err = _VerifyFile(LogContext,
VerifyContext,
CatalogName,
NULL,
0,
OemInfOriginalName,
OemInfName,
&Problem,
PathBuffer,
FALSE,
AltPlatformInfo,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
NULL,
NULL,
NULL,
NULL,
NULL
);
if((Err != NO_ERROR) &&
(Err != ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH) &&
(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE)) {
//
// If we failed to validate via standard OS
// code-signing policy (due to some problem
// other than osattribute mismatch), and we're
// allowed to attempt validation via an
// Authenticode signed catalog, then try again.
//
AuthenticodeErr = _VerifyFile(
LogContext,
VerifyContext,
CatalogName,
NULL,
0,
OemInfOriginalName,
OemInfName,
&Problem,
PathBuffer,
FALSE,
AltPlatformInfo,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK
| VERIFY_FILE_USE_AUTHENTICODE_CATALOG),
NULL,
NULL,
NULL,
NULL,
hWVTStateData
);
if((AuthenticodeErr == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// The catalog validated via
// Authenticode policy--reset the error
// (previously received during the
// failed attempt at validation via
// driversigning policy).
//
Err = NO_ERROR;
} else {
//
// Transfer Authenticode error over to
// be the "official" failure. We
// always want the Authenticode error
// to be NO_ERROR unless it's one of
// the two "special" values.
//
MYASSERT(AuthenticodeErr != NO_ERROR);
Err = AuthenticodeErr;
AuthenticodeErr = NO_ERROR;
}
}
if(Err != NO_ERROR) {
if(Problem != SetupapiVerifyCatalogProblem) {
MYASSERT(Problem != SetupapiVerifyNoProblem);
//
// If the problem was not a catalog problem,
// then it's an INF problem (the _VerifyFile
// routine doesn't know the file we passed
// it is an INF).
//
Problem = SetupapiVerifyInfProblem;
}
ProblemFile = PathBuffer;
} else {
//
// Make sure we aren't remembering any residual
// intermediate problems...
//
Problem = SetupapiVerifyNoProblem;
//
// Only attempt to install a catalog if we're
// not running in "minimal embedded" mode...
//
if(!(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED)) {
//
// Take the INF's new, unique name and
// generate a unique catalog filename under
// setupapi's namespace by simply replacing
// ".INF" with ".CAT".
//
if(MYVERIFY(SUCCEEDED(StringCchCopy(
PathBuffer,
SIZECHARS(PathBuffer),
pSetupGetFileTitle(LastResortInf))))) {
PTSTR DotPart = _tcsrchr(PathBuffer, TEXT('.'));
if(MYVERIFY(DotPart != NULL)) {
MYVERIFY(SUCCEEDED(StringCchCopy(
DotPart,
SIZECHARS(PathBuffer)-(DotPart-PathBuffer),
pszCatSuffix)));
}
}
//
// At this point, PathBuffer contains the
// basename to be used for the catalog on
// the system, and CatalogName is the
// fully-qualified path of the catalog file
// in the oem location.
//
Err = pSetupInstallCatalog(
CatalogName,
PathBuffer,
CatalogFilenameOnSystem
);
if(Err != NO_ERROR) {
Problem = SetupapiVerifyCatalogProblem;
ProblemFile = CatalogName;
}
}
}
}
MYVERIFY(SUCCEEDED(StringCchCopy(szNewName,
SIZECHARS(szNewName),
LastResortInf)));
//
// We will never consider a zero-length INF as a last-
// resort candidate, thus if we get here we know the
// previously-existing INF wasn't zero-length.
//
*CopyNeeded = NewInfCopyNo;
}
MyFree(LastResortInf);
}
FinishedCheckingOemInfs:
if(CatalogBaseAddress) {
pSetupUnmapAndCloseFile(CatalogFileHandle, CatalogMappingHandle, CatalogBaseAddress);
}
pSetupUnmapAndCloseFile(OemInfFileHandle, OemInfMappingHandle, OemInfBaseAddress);
}
if(FindHandle != INVALID_HANDLE_VALUE) {
FindClose(FindHandle);
}
}
} else {
//
// The INF has no CatalogFile= entry, and we have an alternate catalog
// to use. Validate our INF using the specified alternate catalog.
//
// We don't bother trying to "fall back" to an Authenticode signature,
// since it's invalid for an INF to validate via an Authenticode
// catalog unless that INF explicitly references the catalog.
//
Err = _VerifyFile(LogContext,
VerifyContext,
AltCatalogFile,
NULL,
0,
OemInfOriginalName,
OemInfName,
&Problem,
PathBuffer,
FALSE,
AltPlatformInfo,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
NULL,
NULL,
NULL,
NULL,
NULL
);
if(Err != NO_ERROR) {
if(Problem != SetupapiVerifyCatalogProblem) {
MYASSERT(Problem != SetupapiVerifyNoProblem);
//
// If the problem was not a catalog problem,
// then it's an INF problem (the _VerifyFile
// routine doesn't know the file we passed
// it is an INF).
//
Problem = SetupapiVerifyInfProblem;
}
ProblemFile = PathBuffer;
} else {
//
// Make sure we aren't remembering any residual intermediate
// problems...
//
Problem = SetupapiVerifyNoProblem;
//
// Only attempt to install a catalog if we're not running in
// "minimal embedded" mode...
//
if(!(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED)) {
Err = pSetupInstallCatalog(
AltCatalogFile,
pSetupGetFileTitle(AltCatalogFile),
CatalogFilenameOnSystem
);
if(Err != NO_ERROR) {
Problem = SetupapiVerifyCatalogProblem;
ProblemFile = AltCatalogFile;
}
}
}
//
// An INF copy is never needed when we're using an alternate catalog.
//
*CopyNeeded = NewInfCopyNo;
//
// Setup szNewName to be the same as the INF's present full pathname.
// Since we didn't copy this into the INF directory (or even create a
// zero-length placeholder), the only reasonable path to return is the
// INF's current full pathname.
//
MYVERIFY(SUCCEEDED(StringCchCopy(szNewName,
SIZECHARS(szNewName),
OemInfName)));
}
if(*CopyNeeded != NewInfCopyYes) {
MYASSERT(((Problem == SetupapiVerifyNoProblem) && (Err == NO_ERROR)) ||
((Problem != SetupapiVerifyNoProblem) && (Err != NO_ERROR)));
//
// Then this INF already exists in the Inf directory (or at least its
// zero-length placeholder does), and its associated catalog (if it has
// one) is already installed, too. If either of these files had a
// signature verification problem (or we need to establish the user's
// trust of an Authenticode signature publisher), then inform the user
// now (based on policy).
//
if((Problem != SetupapiVerifyNoProblem) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
BOOL result;
MYASSERT((Err != NO_ERROR) || (hWVTStateData && *hWVTStateData));
//
// If we're here to ask the user whether they trust an Authenticode
// publisher, then we'd better not have encountered a verification
// error...
//
MYASSERT((AuthenticodeErr != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) ||
(Err == NO_ERROR));
if(DriverSigningError) {
*DriverSigningError = Err;
}
if(((Err == ERROR_NO_CATALOG_FOR_OEM_INF) && (Flags & SCOI_NO_ERRLOG_ON_MISSING_CATALOG)) ||
(Flags & SCOI_NO_ERRLOG_IF_INF_ALREADY_PRESENT)) {
//
// We shouldn't do any UI/logging because of one of the two
// following reasons:
//
// 1. This may be a valid INF after all (i.e., if it uses
// layout.inf for source media info, or doesn't copy any
// files at all).
//
// 2. We were asked not to. The public API, SetupCopyOEMInf
// doesn't want/need anything to happen here, because the
// INF is already present, and we haven't really done
// anything (except potentially update the source path
// information contained in the PNF), thus there's no
// reason to make noise about this.
//
result = TRUE;
} else {
result = _HandleFailedVerification(
Owner,
((AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)
? SetupapiVerifyCatalogProblem
: Problem),
((AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)
? CatalogName
: ProblemFile),
DeviceDesc,
DriverSigningPolicy,
(Flags & SCOI_NO_UI_ON_SIGFAIL),
((Err == NO_ERROR) ? AuthenticodeErr : Err),
LogContext,
NULL,
NULL,
(hWVTStateData ? *hWVTStateData : NULL)
);
if(result) {
//
// The user wants to proceed with the unsigned/untrusted
// installation (or policy is Ignore, so they weren't even
// informed). If we were able to confirm with the user
// that they trust the Authenticode publisher, then set our
// error to indicate this.
//
if(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) {
MYASSERT(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE);
MYASSERT((DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_BLOCKING);
if((DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
AuthenticodeErr = ERROR_AUTHENTICODE_TRUSTED_PUBLISHER;
}
} else {
//
// If the caller wants a chance to set a system restore
// point prior to doing any unsigned installations, then we
// abort now with a "special" error code that tells them
// what to do...
//
if(Flags & SCOI_ABORT_IF_UNSIGNED) {
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
}
return ERROR_SET_SYSTEM_RESTORE_POINT;
}
}
}
}
if(!result) {
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
MYASSERT(Err == NO_ERROR);
//
// Need to report some error, since the user indicated they
// don't want to trust the publisher of this Authenticode-
// signed package (unless policy was block, in which case
// they didn't have a choice).
//
MYASSERT(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE);
Err = ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED;
}
return Err;
}
}
//
// There was no problem, or the user elected to install in spite of a
// problem, so return the INF name we found.
//
*RequiredSize = lstrlen(szNewName) + 1;
if(*RequiredSize < NewInfNameSize) {
MYVERIFY(SUCCEEDED(StringCchCopy(NewInfName, *RequiredSize, szNewName)));
//
// If Authenticode verification was done, then we'll return one of
// the two errors appropriate to that. Otherwise, we'll return
// NO_ERROR (that's what we initialized our AuthenticodeErr
// variable to at the beginning of this routine).
//
MYASSERT((AuthenticodeErr == NO_ERROR) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED));
return AuthenticodeErr;
} else {
//
// So close, yet so far. :-( Free the WinVerifyTrust state data,
// if any, since the caller won't be expecting it when a failure is
// encountered due to insufficient buffer.
//
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
}
return ERROR_INSUFFICIENT_BUFFER;
}
}
//
// If we're in 'replace only' mode, then the fact that we didn't find the
// INF/CAT already installed above means we should fail with
// ERROR_FILE_NOT_FOUND.
//
if(ReplaceOnly) {
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
}
return ERROR_FILE_NOT_FOUND;
}
//
// OK, so the INF isn't presently in the Inf directory--find a unique name
// for it. (Note: We'll go into the loop below even if we're meant to be
// installing the INF and CAT under their original names. We'll just skip
// the auto-generation part, and then we'll break out of the loop once
// we've made the attempt at INF/CAT installation.)
//
for(i = 0; i < 100000; i++) {
if(Flags & SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES) {
MYVERIFY(SUCCEEDED(StringCchCopy(szNewName,
SIZECHARS(szNewName),
InfDirectory)));
pSetupConcatenatePaths(szNewName, OemInfOriginalName, SIZECHARS(szNewName), NULL);
} else {
MYVERIFY(SUCCEEDED(StringCchPrintf(szNewName,
SIZECHARS(szNewName),
pszOemInfGenerate,
InfDirectory,
i)));
}
Err = GLE_FN_CALL(INVALID_HANDLE_VALUE,
h = CreateFile(szNewName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)
);
if(Err == NO_ERROR) {
//
// We either opened an existing file (that we need to leave alone,
// unless we're replacing it with a new exception INF), or we
// created a new file (in which case, we've found our unique name).
//
// CreateFile actually sets last error upon success to indicate if
// the file was already in existence...
//
Err = GetLastError();
//
// Before we decide what to do, close the file handle.
//
CloseHandle(h);
FileNewlyCreated = (Err != ERROR_ALREADY_EXISTS);
if(FileNewlyCreated || (Flags & SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES)) {
//
// We've either (a) created a new file, or (b) found that we
// can replace an existing exception INF. Determine whether
// the filename fits in the caller-supplied buffer.
//
*RequiredSize = lstrlen(szNewName) + 1;
if(*RequiredSize < NewInfNameSize) {
//
// OK, we have a unique filename, and the caller-
// supplied buffer is large enough to hold that name.
//
// get LastKnownGood in on the loop
// if this is a newly created file, it will get deleted on revert
//
pSetupDoLastKnownGoodBackup(NULL,
szNewName,
SP_LKG_FLAG_FORCECOPY|SP_LKG_FLAG_DELETEIFNEW|(FileNewlyCreated?SP_LKG_FLAG_DELETEEXISTING:0),
LogContext);
//
// Now we need to verify the INF and its associated CAT.
//
if(!OemInfCatName) {
//
// An OEM INF without a CatalogFile= entry is
// automatically a digital signature failure!
//
Err = ERROR_NO_CATALOG_FOR_OEM_INF;
Problem = SetupapiVerifyInfProblem;
ProblemFile = OemInfName;
} else {
//
// Now verify the catalog file and INF, which must
// both be in the same directory.
//
MYVERIFY(SUCCEEDED(StringCchCopy(CatalogName,
SIZECHARS(CatalogName),
OemInfName)));
*((PTSTR)pSetupGetFileTitle(CatalogName)) = TEXT('\0');
pSetupConcatenatePaths(CatalogName, OemInfCatName, SIZECHARS(CatalogName), NULL);
Err = _VerifyFile(LogContext,
VerifyContext,
CatalogName,
NULL,
0,
OemInfOriginalName,
OemInfName,
&Problem,
PathBuffer,
FALSE,
AltPlatformInfo,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
NULL,
NULL,
NULL,
NULL,
NULL
);
if(Err != NO_ERROR) {
if(Problem != SetupapiVerifyCatalogProblem) {
MYASSERT(Problem != SetupapiVerifyNoProblem);
//
// If the problem was not a catalog problem,
// then it's an INF problem (the _VerifyFile
// routine doesn't know the file we passed
// it is an INF).
//
Problem = SetupapiVerifyInfProblem;
}
ProblemFile = PathBuffer;
if((Err != ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH) &&
(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE)) {
//
// If we failed to validate via standard OS
// code-signing policy (due to some reason
// other than osattribute mismatch), and we're
// allowed to attempt validation via an
// Authenticode-signed catalog, then try again.
//
AuthenticodeErr = _VerifyFile(
LogContext,
VerifyContext,
CatalogName,
NULL,
0,
OemInfOriginalName,
OemInfName,
&Problem,
PathBuffer,
FALSE,
AltPlatformInfo,
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK
| VERIFY_FILE_USE_AUTHENTICODE_CATALOG),
NULL,
NULL,
NULL,
NULL,
hWVTStateData
);
if((AuthenticodeErr == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// The catalog validated via
// Authenticode policy--reset the error
// (previously received during the
// failed attempt at validation via
// driversigning policy).
//
Err = NO_ERROR;
} else {
//
// Transfer Authenticode error over to
// be the "official" failure. We
// always want the Authenticode error
// to be NO_ERROR unless it's one of
// the two "special" values.
//
MYASSERT(AuthenticodeErr != NO_ERROR);
Err = AuthenticodeErr;
AuthenticodeErr = NO_ERROR;
}
}
}
if(Err == NO_ERROR) {
//
// Make sure we aren't remembering any residual
// intermediate problems...
//
Problem = SetupapiVerifyNoProblem;
//
// Only attempt to install a catalog if we're not
// running in "minimal embedded" mode...
//
if(!(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED)) {
//
// Take the INF's new, unique name and generate
// a unique catalog filename under setupapi's
// namespace by simply replacing ".INF" with
// ".CAT".
//
if(MYVERIFY(SUCCEEDED(StringCchCopy(
PathBuffer,
SIZECHARS(PathBuffer),
pSetupGetFileTitle(szNewName))))) {
PTSTR DotPart = _tcsrchr(PathBuffer, TEXT('.'));
if(MYVERIFY(DotPart != NULL)) {
MYVERIFY(SUCCEEDED(StringCchCopy(
DotPart,
SIZECHARS(PathBuffer)-(DotPart-PathBuffer),
pszCatSuffix)));
}
}
//
// At this point, PathBuffer contains the
// basename to be used for the catalog on the
// system, and CatalogName is the fully-
// qualified path of the catalog file in the oem
// location.
//
Err = pSetupInstallCatalog(
CatalogName,
PathBuffer,
CatalogFilenameOnSystem
);
if(Err != NO_ERROR) {
Problem = SetupapiVerifyCatalogProblem;
ProblemFile = CatalogName;
//
// If this was au Authenticode-signed
// catalog that required user confirmation
// to establish publisher trust, then we
// need to free the WinVerifyTrust data
// now, since we've encountered an error.
//
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(hWVTStateData);
*hWVTStateData = NULL;
}
}
}
}
}
MYASSERT(((Problem == SetupapiVerifyNoProblem) && (Err == NO_ERROR)) ||
((Problem != SetupapiVerifyNoProblem) && (Err != NO_ERROR)));
//
// If we encountered a signature verification failure, were
// unable to install the catalog, or need to establish the
// user's trust for this Authenticode publisher, then
// prompt the user (based on policy) about what they want
// to do.
//
if((Problem != SetupapiVerifyNoProblem) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
MYASSERT((Err != NO_ERROR) || (hWVTStateData && *hWVTStateData));
//
// If we're here to ask the user whether they trust an
// Authenticode publisher, then we'd better not have
// encountered a verification error...
//
MYASSERT((AuthenticodeErr != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) ||
(Err == NO_ERROR));
if(DriverSigningError) {
*DriverSigningError = Err;
}
//
// Unless the error was due to missing CatalogFile=
// entry in the INF (and we were instructed to ignore
// such errors), then we need to handle the
// verification failure.
//
if((Err != ERROR_NO_CATALOG_FOR_OEM_INF) ||
!(Flags & SCOI_NO_ERRLOG_ON_MISSING_CATALOG)) {
if(_HandleFailedVerification(
Owner,
((AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)
? SetupapiVerifyCatalogProblem
: Problem),
((AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)
? CatalogName
: ProblemFile),
DeviceDesc,
DriverSigningPolicy,
(Flags & SCOI_NO_UI_ON_SIGFAIL),
((Err == NO_ERROR) ? AuthenticodeErr
: Err),
LogContext,
NULL,
NULL,
(hWVTStateData ? *hWVTStateData : NULL))) {
//
// The user wants to proceed with the unsigned/
// untrusted installation (or policy is Ignore,
// so they weren't even informed). If we were
// able to confirm with the user that they
// trust the Authenticode publisher, then set
// our error to indicate this.
//
if(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) {
MYASSERT(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE);
MYASSERT((DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_BLOCKING);
if((DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
AuthenticodeErr = ERROR_AUTHENTICODE_TRUSTED_PUBLISHER;
}
}
} else {
//
// Failed verification should be treated as
// fatal.
//
if(FileNewlyCreated) {
//
// Delete the INF (and PNF and CAT, if they
// exist).
//
pSetupUninstallOEMInf(szNewName,
LogContext,
SUOI_FORCEDELETE,
NULL
);
}
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
MYASSERT(Err == NO_ERROR);
//
// Need to report some error, since the
// user indicated they don't want to trust
// the publisher of this Authenticode-
// signed package (unless policy was block,
// in which case they didn't have a
// choice).
//
MYASSERT(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE);
Err = ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED;
}
return Err;
}
//
// The user wants to proceed with the installation
// (or policy is Ignore, so they weren't even
// informed). If the caller wants a chance to set
// a system restore point prior to doing any
// unsigned installations, then we abort now with a
// "special" error code that tells them what to
// do. (Note: if the user indicated that they
// trust the publisher of an Authenticode signed
// catalog, then we treat that as "signed", and
// thus don't bail out in this case.
//
if((Err != NO_ERROR) &&
(Flags & SCOI_ABORT_IF_UNSIGNED)) {
if(FileNewlyCreated) {
//
// Delete the INF (and PNF and CAT, if they
// exist)--we don't want these files to be
// present when the system restore point is
// created.
//
pSetupUninstallOEMInf(szNewName,
LogContext,
SUOI_FORCEDELETE,
NULL
);
}
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
}
return ERROR_SET_SYSTEM_RESTORE_POINT;
}
}
}
MYVERIFY(SUCCEEDED(StringCchCopy(NewInfName, *RequiredSize, szNewName)));
//
// If we're installing an unsigned INF, then clean out any
// existing CAT that might've previously been installed.
//
if(Err != NO_ERROR) {
PTSTR DotPart = _tcsrchr(szNewName, TEXT('.'));
if(MYVERIFY(DotPart != NULL)) {
MYVERIFY(SUCCEEDED(StringCchCopy(DotPart,
SIZECHARS(szNewName)-(DotPart-szNewName),
pszCatSuffix)));
}
pSetupUninstallCatalog(pSetupGetFileTitle(szNewName));
}
//
// If Authenticode verification was done, then we'll return
// one of the two errors appropriate to that. Otherwise,
// we'll return NO_ERROR (that's what we initialized our
// AuthenticodeErr variable to at the beginning of this
// routine).
//
MYASSERT((AuthenticodeErr == NO_ERROR) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED));
return AuthenticodeErr;
} else {
//
// The caller's buffer isn't large enough. We have to
// delete the file we created. We don't want to delete the
// file, however, if it already existed (i.e., for the
// exception INF case).
//
if(FileNewlyCreated) {
pSetupUninstallOEMInf(szNewName, LogContext, SUOI_FORCEDELETE, NULL);
}
//
// So close, yet so far. :-( Free the WinVerifyTrust state
// data, if any, since the caller won't be expecting it
// when a failure is encountered due to insufficient
// buffer.
//
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
}
return ERROR_INSUFFICIENT_BUFFER;
}
}
} else {
MYASSERT(!hWVTStateData || !*hWVTStateData);
//
// We failed to open/create this oem inf. Check to see if the
// failure was access-denied. If so, then it's possible that the
// INF directory is ACL'ed such that we can't write to it. We want
// to bail in this case, otherwise we're going to spend a bunch of
// time trying all 100,000 oem<n>.inf filenames (with each one
// failing) before we give up.
//
// We check for this case by seeing if the file we were trying to
// create/open already exists. If so, then we want to keep going
// (e.g., maybe the individual file was ACL'ed, etc.). If, however,
// the file doesn't exist, then this indicates that we can't create
// files in the directory, and we should bail now.
//
if(Flags & SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES) {
//
// If we're installing an exception INF, we don't care what the
// error is--we gotta bail.
//
return Err;
} else if((Err == ERROR_ACCESS_DENIED) && !FileExists(szNewName, NULL)) {
return ERROR_ACCESS_DENIED;
}
}
}
//
// We didn't find a unique OEM INF name to use!
//
MYASSERT(!hWVTStateData || !*hWVTStateData);
return ERROR_FILE_NOT_FOUND;
}
DWORD
InstallHW(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HINF hDeviceInf,
IN PCTSTR szSectionName,
OUT PBOOL DeleteDevKey
)
/*++
Routine Description:
This routine appends a ".Hw" to the end of the install section name for the
specified device, and attempts to find that section name in the specified INF.
If found, it does a performs a registry installation against it.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set to call
SetupInstallFromInfSection for.
DeviceInfoData - Supplies the address of a device information element structure
for which the installation action is to be performed.
hDeviceInf - Supplies a handle to the opened INF containing the device install
section.
szSectionName - Supplies the address of a string specifying the install section
name for this device. This string will be appended with ".Hw" to create
the corresponding hardware section name.
DeleteDevKey - Supplies the address of a variable that receives a boolean value
indicating whether or not a user-accessible device key was created as a
result of calling this routine. This output may be used to indicate whether
or not the key should be destroyed if the caller encounters some error later
on that requires clean-up.
Return Value:
If the function succeeds, the return value is NO_ERROR, otherwise it is an
ERROR_* code.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
PDEVINFO_ELEM DevInfoElem;
HKEY hKey;
DWORD Err;
TCHAR szHwSection[MAX_SECT_NAME_LEN];
INFCONTEXT InfContext;
PTSTR szInfFileName;
PTSTR NeedsSectionList, CurInstallSection;
BOOL b;
REGMOD_CONTEXT RegContext;
//
// Initially, assume the device key is already there, and therefore shouldn't be
// deleted during error clean-up.
//
*DeleteDevKey = FALSE;
//
// Form the hardware INF section name, and see if that section exists in the INF.
//
MYVERIFY(SUCCEEDED(StringCchPrintf(szHwSection,
SIZECHARS(szHwSection),
pszHwSectionFormat,
szSectionName)));
if(!SetupFindFirstLine(hDeviceInf, szHwSection, NULL, &InfContext)) {
return NO_ERROR;
}
if((hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
KEY_ALL_ACCESS)) == INVALID_HANDLE_VALUE) {
//
// Open failed--try create.
//
if((hKey = SetupDiCreateDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
NULL,
NULL)) == INVALID_HANDLE_VALUE) {
return GetLastError();
} else {
*DeleteDevKey = TRUE;
}
}
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
MYASSERT(pDeviceInfoSet);
RegCloseKey(hKey);
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Err = NO_ERROR;
NeedsSectionList = NULL;
try {
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL))) {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
szInfFileName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfFileName
);
//
// Append-load any included INFs specified in an "include=" line in our
// ".Hw" section.
//
AppendLoadIncludedInfs(hDeviceInf, szInfFileName, szHwSection, FALSE);
NeedsSectionList = GetMultiSzFromInf(hDeviceInf, szHwSection, TEXT("needs"), &b);
if(!NeedsSectionList && b) {
//
// Out of memory!
//
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
ZeroMemory(&RegContext, sizeof(RegContext));
RegContext.Flags = INF_PFLAG_DEVPROP;
RegContext.UserRootKey = hKey;
RegContext.DevInst = DeviceInfoData->DevInst;
//
// Process the registry lines ("AddReg" and "DelReg") in this section, as well as
// those contained with any sections referenced in the "needs=" entry in this section.
//
for(CurInstallSection = szHwSection;
(CurInstallSection && *CurInstallSection);
CurInstallSection = (CurInstallSection == szHwSection)
? NeedsSectionList
: (CurInstallSection + lstrlen(CurInstallSection) + 1))
{
if((Err = pSetupInstallRegistry(hDeviceInf, CurInstallSection, &RegContext)) != NO_ERROR) {
//
//Stop if we encounter an error while processing one of the section's registry entries
//
break;
}
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was
// an inpage error dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
}
UnlockDeviceInfoSet(pDeviceInfoSet);
RegCloseKey(hKey);
if(NeedsSectionList) {
MyFree(NeedsSectionList);
}
return Err;
}
BOOL
CheckIfDevStarted(
IN PDEVINFO_ELEM DevInfoElem,
IN PDEVICE_INFO_SET pDeviceInfoSet
)
/*++
Routine Description:
This routine calls CM_Get_DevInst_Status to see if the specified device
instance has been started. If the device hasn't been started, and it has
either the CM_PROB_NEED_RESTART or CM_PROB_NORMAL_CONFLICT problem codes,
the DI_NEEDREBOOT flag is set in the device information element. We also
set the CM_PROB_NEED_RESTART problem on the devnode.
Arguments:
DevInfoElem - Supplies the address of the device information element to
check.
pDeviceInfoSet - Supplies the address of the device info set
LogContext - Supplies a log context for logging the reason if a reboot is
needed.
Return Value:
None.
--*/
{
ULONG ulStatus, ulProblem;
BOOL restarted = FALSE;
if(CM_Get_DevInst_Status_Ex(&ulStatus, &ulProblem, DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine) == CR_SUCCESS) {
if(ulStatus & DN_STARTED) {
restarted = TRUE;
} else {
if (ulStatus & DN_HAS_PROBLEM) {
if((ulProblem == CM_PROB_NEED_RESTART) ||
(ulProblem == CM_PROB_NORMAL_CONFLICT) ||
(ulProblem == CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD) ||
(ulProblem == CM_PROB_HELD_FOR_EJECT)) {
//
// The device either has the problem CM_PROB_NEED_RESTART or
// CM_PROB_NORMAL_CONFLICT. - change into need reboot
// and log what problem code was
//
SetDevnodeNeedsRebootProblemWithArg2(DevInfoElem,
pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_DEVHASPROBLEM,
(DWORD)ulProblem,
(ULONG_PTR)_MapCmProbToString((DWORD)ulProblem)
);
} else {
//
// The device has some other problem so we won't prompt for a reboot
// however these are interesting things to log
//
WriteLogEntry(
DevInfoElem->InstallParamBlock.LogContext,
DRIVER_LOG_INFO, // not worth a warning
MSG_LOG_NOTSTARTED_REASON_DEVHASPROBLEM,
NULL,
(DWORD)ulProblem,
_MapCmProbToString((DWORD)ulProblem)
);
}
} else if (ulStatus & DN_PRIVATE_PROBLEM) {
//
// some private problem, change into need reboot
// and log private problem
//
SetDevnodeNeedsRebootProblem(DevInfoElem, pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_PRIVATEPROBLEM);
} else {
//
// not started for some other reason
// indicate reboot required and log this issue
//
SetDevnodeNeedsRebootProblem(DevInfoElem, pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_NOTSTARTED);
}
}
}
return restarted;
}
DWORD
InstallNtService(
IN PDEVINFO_ELEM DevInfoElem, OPTIONAL
IN HINF hDeviceInf,
IN PCTSTR InfFileName, OPTIONAL
IN PCTSTR szSectionName, OPTIONAL
OUT PSVCNAME_NODE *ServicesToDelete, OPTIONAL
IN DWORD Flags,
OUT PBOOL NullDriverInstalled
)
/*++
Routine Description:
This routine looks for the specified INF section, and if found, it deletes
any services specified in "DelService" entries, then installs any services
specified in "AddService" entries. These entries have the following form:
AddService = [<ServiceName>], [<Flags>], <ServiceInstallSection>[, <EventLogInstallSection>[, [<EventLogType>] [, <EventName>]]]
DelService = <ServiceName>[, [<flags>] [, [<EventLogType>] [, <EventName>]]]
(<ServiceName> is only optional for an AddService entry if the
SPSVCINST_ASSOCSERVICE flag is set. This indicates that we're explicitly
installing a NULL driver for this device, even though the underlying bus
didn't report the device as being raw-capable. This is used for device such
as the BIOS-reported PIC, DMA controller, etc. devnodes that don't need a
driver (since the HAL runs them), yet need to have a NULL driver installed
so that they don't show up as yellow-banged in Device Manager.)
A linked list is built of newly-created services, and optionally returned to the
caller (in case a subsequent installation failure requires all modifications to
be undone).
After all service modifications are complete, this routine checks to see if we're
in the context of a device installation. If so, then it checks to see if the device
instance specifies a valid controlling service, and that the service is not disabled
(disabled services are assumed to be uninstalled). If the device's 'RawDeviceOK'
capability bit is set, then a device with no controlling service will be allowed.
Arguments:
DevInfoElem - Optionally, supplies the device information element for whom the
service installation is being performed. If this parameter is not specified,
then the service is not being installed in relation to a device instance.
hDeviceInf - Supplies a handle to the opened INF containing the service install
section.
InfFileName - Optionally, supplies the full path of the INF file containing the
service install section. If this parameter is NULL, the no Include= or Needs=
values will be processed in this section.
szSectionName - Optionally, supplies the name of the service install section in a
Win95-style device INF. If this parameter is NULL, then no AddService or
DelService lines will be processed.
ServicesToDelete - Optionally, supplies the address of a linked list head pointer,
that receives a list of services that were newly-created by this routine, and
as such, should be deleted if the installation fails later on. The caller must
free the memory allocated for the nodes in this list by calling MyFree() on each
one.
Flags - Supplies flags controlling how the services are to be installed. May be a
combination of the following values:
SPSVCINST_TAGTOFRONT - For every kernel or filesystem driver installed (that
has an associated LoadOrderGroup), always move this service's tag to the
front of the ordering list.
SPSVCINST_ASSOCSERVICE - This flag may only be specified if a device information
element is specified. If set, this flag specifies that the service being
installed is the owning service (i.e., function driver) for this device instance.
SPSVCINST_DELETEEVENTLOGENTRY - For every service specified in a DelService entry,
delete the associated event log entry (if there is one).
SPSVCINST_NOCLOBBER_DISPLAYNAME - If this flag is specified, then we will
not overwrite the service's display name, if it already exists.
SPSVCINST_NOCLOBBER_STARTTYPE - If this flag is specified, then we will
not overwrite the service's start type if the service already exists.
SPSVCINST_NOCLOBBER_ERRORCONTROL - If this flag is specified, then we
will not overwrite the service's error control value if the service
already exists.
SPSVCINST_NOCLOBBER_LOADORDERGROUP - If this flag is specified, then we
will not overwrite the service's load order group if it already
exists.
SPSVCINST_NOCLOBBER_DEPENDENCIES - If this flag is specified, then we
will not overwrite the service's dependencies list if it already
exists.
SPSVCINST_NO_DEVINST_CHECK - If this flag is specified, then we will not check
to ensure that a function driver is installed for the specified devinfo
element after running the service install section. This is a private flag
used only by SetupInstallServicesFromInfSection(Ex) and InstallHinfSection.
SPSVCINST_STOPSERVICE - If this flag is specified, then we will stop the service
before removing the service.
SPSVCINST_CLOBBER_SECURITY - If this flag is specified, security may be
overridden.
NullDriverInstalled - Supplies the address of a boolean variable that
indicates whether or not an explicit null driver installation was done
for this device.
Return Value:
If the function succeeds, the return value is NO_ERROR, otherwise it is an
ERROR_* code.
If NO_ERROR is returned, GetLastError may return ERROR_SUCCESS_REBOOT_REQUIRED
--*/
{
CONFIGRET cr;
TCHAR ServiceName[MAX_SERVICE_NAME_LEN];
ULONG ServiceNameSize;
DWORD Err = NO_ERROR, i;
SC_HANDLE SCMHandle, ServiceHandle, FilterServiceHandle;
LPQUERY_SERVICE_CONFIG ServiceConfig, FilterServiceConfig;
DWORD ServiceConfigSize;
PCTSTR Key;
INFCONTEXT LineContext;
PSVCNAME_NODE SvcListHead = NULL;
PSVCNAME_NODE TmpSvcNode;
SC_LOCK SCLock;
DWORD NewTag;
BOOL AssociatedService;
DWORD DevInstCapabilities;
ULONG DevInstCapabilitiesSize;
PTSTR FilterDrivers, CurFilterDriver;
BOOL FilterNeedsTag;
BOOL NullFunctionDriverAdded;
PTSTR NeedsSectionList, CurInstallSection;
BOOL b;
BOOL NeedsReboot;
DWORD slot_section = 0;
PSETUP_LOG_CONTEXT LogContext;
try {
LogContext = ((PLOADED_INF) hDeviceInf)->LogContext;
} except(EXCEPTION_EXECUTE_HANDLER) {
LogContext = NULL;
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
goto FinalClean0;
}
//
// Initially, assume this is not a null driver install.
//
*NullDriverInstalled = FALSE;
NeedsReboot = FALSE;
if(szSectionName) {
//
// Surround the following in try/except, in case we get an inpage error.
//
try {
NeedsSectionList = NULL;
if (InfFileName) {
AppendLoadIncludedInfs(hDeviceInf, InfFileName, szSectionName, FALSE);
NeedsSectionList = GetMultiSzFromInf(hDeviceInf, szSectionName, TEXT("needs"), &b);
if(!NeedsSectionList && b) {
//
// Out of memory!
//
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
if (slot_section == 0) {
slot_section = AllocLogInfoSlot(LogContext,FALSE);
}
//
// Make two passes through the section--once for deletions, and a
// second time for additions.
//
for(i = 0; i < 2; i++) {
//
// Find the relevent line (if there is one) in the given install section.
//
Key = (i) ? pszAddService : pszDelService;
//
// Process the service lines in this section, as well as
// those contained with any sections referenced in the "needs=" entry in this section.
//
for(CurInstallSection = (PTSTR)szSectionName;
(CurInstallSection && *CurInstallSection);
CurInstallSection = (CurInstallSection == szSectionName)
? NeedsSectionList
: (CurInstallSection + lstrlen(CurInstallSection) + 1))
{
if(!SetupFindFirstLine(hDeviceInf, CurInstallSection, Key, &LineContext)) {
continue;
}
//
// Log which section we're installing if we log anything else
//
WriteLogEntry(
LogContext,
slot_section,
MSG_LOG_PROCESS_SERVICE_SECTION,
NULL,
CurInstallSection);
do {
//
// We have a line to act upon.
//
Err = (i) ? pSetupAddService(&LineContext,
&SvcListHead,
Flags,
(DevInfoElem ? DevInfoElem->DevInst : 0),
&NullFunctionDriverAdded,
LogContext)
: pSetupDeleteService(&LineContext,
Flags,
LogContext);
if(Err != NO_ERROR) {
//
// Log that an error occurred
//
WriteLogError(
LogContext,
// we don't know if it's a driver or not,
// so just allow both to work
SETUP_LOG_ERROR | DRIVER_LOG_ERROR,
Err);
goto clean0;
} else if(i) {
//
// We're processing AddService entries, so check to see
// if we just installed a null service (thus having no
// function driver on a non-raw-capable PDO should be
// allowed).
//
*NullDriverInstalled |= NullFunctionDriverAdded;
if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED) {
NeedsReboot |= TRUE;
}
}
} while(SetupFindNextMatchLine(&LineContext, Key, &LineContext));
}
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was
// an inpage error dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
}
if (NeedsSectionList) {
MyFree(NeedsSectionList);
}
if((Err != NO_ERROR) || (Flags & SPSVCINST_NO_DEVINST_CHECK)) {
goto FinalClean0;
}
}
MYASSERT(DevInfoElem);
//
// Find out if the device instance already has an associated service.
//
ServiceNameSize = sizeof(ServiceName);
if(CR_SUCCESS == (cr = CM_Get_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_SERVICE,
NULL,
ServiceName,
&ServiceNameSize,
0)))
{
AssociatedService = TRUE;
//
// Make sure that the NullDriverInstalled output parameter is still FALSE.
// It typically would be, but might not be in the case where there are
// multiple AddService entries that specify SPSVCINST_ASSOCSERVICE (e.g.,
// when additional service install sections are pulled in via include=/needs=.
//
*NullDriverInstalled = FALSE;
} else {
//
// For the moment, there is no associated service.
//
AssociatedService = FALSE;
//
// Either the device instance has gone sour (in which case we return an error),
// or we couldn't retrieve an associated service name. In the latter case, we
// will make the association based on the default service for the class.
//
if(cr == CR_INVALID_DEVINST) {
Err = ERROR_NO_SUCH_DEVINST;
} else if(!*NullDriverInstalled) {
ServiceNameSize = sizeof(ServiceName);
AssociatedService = AssociateDevInstWithDefaultService(DevInfoElem,
ServiceName,
&ServiceNameSize
);
if(!AssociatedService) {
//
// If the device's capabilities report that it can be driven 'raw', then
// not having a function driver is OK. Otherwise, we have an error.
//
DevInstCapabilitiesSize = sizeof(DevInstCapabilities);
if(CR_SUCCESS != CM_Get_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_CAPABILITIES,
NULL,
&DevInstCapabilities,
&DevInstCapabilitiesSize,
0))
{
DevInstCapabilities = 0;
}
if(!(DevInstCapabilities & CM_DEVCAP_RAWDEVICEOK)) {
Err = ERROR_NO_ASSOCIATED_SERVICE;
}
}
}
if(!AssociatedService) {
//
// Either we hit an error, or the device can be driven 'raw'. In either case, we
// can skip the service controller checks that lie ahead.
//
goto FinalClean0;
}
}
//
// At this point, we have the name of the service with which the device instance is
// associated. Attempt to locate this service in the SCM database.
//
if(!(SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
Err = GetLastError();
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_INSTSERVSCM_ERROR,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto FinalClean0;
}
if(!(ServiceHandle = OpenService(SCMHandle, ServiceName, SERVICE_ALL_ACCESS))) {
//
// We couldn't access the service--either because it doesn't exist, or because
// this is a detected device reported by a 'disembodied' driver object (e.g., the
// one the HAL creates for its driver object it got via IoCreateDriver).
//
// The former case is an error, the latter case is just fine.
//
Err = GetLastError();
if((lstrlen(ServiceName) > CSTRLEN(pszDriverObjectPathPrefix)) &&
CharUpper(ServiceName) &&
!memcmp(ServiceName, pszDriverObjectPathPrefix, CSTRLEN(pszDriverObjectPathPrefix)))
{
//
// The "service name" is actually a driver name (e.g., "\Driver\PCI_HAL"), so it's OK.
//
Err = NO_ERROR;
}
if(Err) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_INSTSERVOPEN_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
}
goto FinalClean1;
}
//
// The service exists. Make sure that it's not disabled.
//
if((Err = pSetupRetrieveServiceConfig(ServiceHandle, &ServiceConfig)) == NO_ERROR) {
if(ServiceConfig->dwStartType == SERVICE_DISABLED) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_INSTSERV_DISABLED,
NULL,
ServiceName
);
Err = ERROR_SERVICE_DISABLED;
} else {
//
// If this service has a load order group, and is a kernel or filesystem
// driver, then make sure that it has a tag.
//
// NOTE: We have to do this here, even though we ensure that all new services we install
// have their tags set up properly in pSetupAddService(). The reason is that the device may
// using an existing service that wasn't installed via a Win95-style INF.
//
if(ServiceConfig->lpLoadOrderGroup && *(ServiceConfig->lpLoadOrderGroup) &&
(ServiceConfig->dwServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER))) {
//
// This service needs a tag--does it have one???
//
if(!(NewTag = ServiceConfig->dwTagId)) {
//
// Attempt to lock the service database before generating a tag. We'll go ahead
// and make the change, even if this fails.
//
pAcquireSCMLock(SCMHandle, &SCLock, LogContext);
if(!ChangeServiceConfig(ServiceHandle,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
NULL,
ServiceConfig->lpLoadOrderGroup, // have to specify this to generate new tag.
&NewTag,
NULL,
NULL,
NULL,
NULL)) {
DWORD LastErr = GetLastError();
WriteLogEntry(
LogContext,
DRIVER_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_INSTSERVTAG_WARN,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_WARNING,
LastErr);
NewTag = 0;
}
if(SCLock) {
UnlockServiceDatabase(SCLock);
}
}
//
// Make sure that the tag exists in the service's corresponding GroupOrderList entry.
//
if(NewTag) {
pSetupAddTagToGroupOrderListEntry(ServiceConfig->lpLoadOrderGroup,
NewTag,
Flags & SPSVCINST_TAGTOFRONT
);
}
}
//
// If the function driver is marked as boot-start, then make sure that all
// associated upper- and lower-filters (both class- and device-specific) are
// also boot-start drivers.
//
if((ServiceConfig->dwStartType == SERVICE_BOOT_START) &&
RetrieveAllDriversForDevice(DevInfoElem, &FilterDrivers,RADFD_FLAG_ALL_FILTERS,NULL)) {
//
// If FilterDrivers is NULL, then we hit an out-of-memory error.
//
if(!FilterDrivers) {
Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_INSTSERV_BOOT,
NULL,
ServiceName
);
//
// Check each filter driver.
//
for(CurFilterDriver = FilterDrivers;
*CurFilterDriver;
CurFilterDriver += (lstrlen(CurFilterDriver) + 1)) {
if(!(FilterServiceHandle = OpenService(SCMHandle, CurFilterDriver, SERVICE_ALL_ACCESS))) {
//
// We couldn't access the service--probably because it doesn't exist.
// Bail now.
//
Err = GetLastError();
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_BOOTFILTSERVOPEN_ERROR,
NULL,
CurFilterDriver);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
break;
}
//
// The service exists. Make sure that it's not disabled.
//
Err = pSetupRetrieveServiceConfig(FilterServiceHandle, &FilterServiceConfig);
if(Err != NO_ERROR) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_BOOTFILTSERVCONFIG_ERROR,
NULL,
CurFilterDriver);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto CloseFilterSvcAndContinue;
}
if(FilterServiceConfig->dwStartType == SERVICE_DISABLED) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_BOOTFILTSERV_DISABLED,
NULL,
CurFilterDriver);
Err = ERROR_SERVICE_DISABLED;
} else {
//
// Ensure that this service is a boot-start kernel driver, and that it has
// a tag if necessary.
//
if(FilterServiceConfig->dwServiceType & SERVICE_KERNEL_DRIVER) {
if(FilterServiceConfig->lpLoadOrderGroup &&
*(FilterServiceConfig->lpLoadOrderGroup)) {
FilterNeedsTag = TRUE;
NewTag = FilterServiceConfig->dwTagId;
}
if((FilterNeedsTag && !NewTag) ||
(FilterServiceConfig->dwStartType != SERVICE_BOOT_START)) {
//
// Lock the service database before modifying this service.
//
Err = pAcquireSCMLock(SCMHandle, &SCLock, LogContext);
if(Err == NO_ERROR) {
//
// Make the modifications to the service (NOTE: Because the
// service controller is really bad when it comes to driver paths,
// we must explicitly pass the lpBinaryPathName in, even though we
// aren't changing it. Otherwise, the service controller will complain
// because it thinks all paths have to begin with \SystemRoot\.)
//
if(!ChangeServiceConfig(FilterServiceHandle,
SERVICE_NO_CHANGE,
(FilterServiceConfig->dwStartType != SERVICE_BOOT_START)
? SERVICE_BOOT_START
: SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
(FilterServiceConfig->dwStartType != SERVICE_BOOT_START)
? FilterServiceConfig->lpBinaryPathName
: NULL,
(FilterNeedsTag && !NewTag)
? FilterServiceConfig->lpLoadOrderGroup
: NULL,
(FilterNeedsTag && !NewTag)
? &NewTag
: NULL,
NULL,
NULL,
NULL,
NULL)) {
Err = GetLastError();
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_BOOTFILTSERVCHANGE_ERROR,
NULL,
CurFilterDriver);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
} else {
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_BOOTFILTSERVCHANGE_OK,
NULL,
CurFilterDriver);
}
UnlockServiceDatabase(SCLock);
} else {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_BOOTFILTSERVSCM_ERROR,
NULL,
CurFilterDriver);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
}
if((Err == NO_ERROR) && FilterNeedsTag) {
//
// Make sure that the tag exists in the service's corresponding GroupOrderList entry.
//
MYASSERT(NewTag);
pSetupAddTagToGroupOrderListEntry(FilterServiceConfig->lpLoadOrderGroup,
NewTag,
FALSE
);
}
}
} else {
//
// This is not a kernel driver. This is an error.
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_BOOTFILTSERV_KERN,
NULL,
CurFilterDriver);
Err = ERROR_INVALID_FILTER_DRIVER;
}
}
MyFree(FilterServiceConfig);
CloseFilterSvcAndContinue:
CloseServiceHandle(FilterServiceHandle);
if(Err) {
break;
}
}
MyFree(FilterDrivers);
}
}
}
MyFree(ServiceConfig);
} else {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_INSTSERVCONFIG_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
}
CloseServiceHandle(ServiceHandle);
FinalClean1:
CloseServiceHandle(SCMHandle);
FinalClean0:
if(Err == NO_ERROR) {
//
// If requested, store the linked-list of newly-created service nodes in the output
// parameter, otherwise, delete the list.
//
if(ServicesToDelete) {
*ServicesToDelete = SvcListHead;
} else {
for(TmpSvcNode = SvcListHead; TmpSvcNode; TmpSvcNode = SvcListHead) {
SvcListHead = SvcListHead->Next;
MyFree(TmpSvcNode);
}
}
if (NeedsReboot) {
//
// this is intentional - return NO_ERROR but GetLastError = ERROR_SUCCESS_REBOOT_REQUIRED
//
SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
}
} else {
//
// Something failed along the way, so we need to clean up any newly-created
// services.
//
if(Err && LogContext) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_INSTSERV_ERROR,
NULL
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
}
if(SvcListHead) {
DeleteServicesInList(SvcListHead,LogContext);
for(TmpSvcNode = SvcListHead; TmpSvcNode; TmpSvcNode = SvcListHead) {
SvcListHead = SvcListHead->Next;
MyFree(TmpSvcNode);
}
}
}
if (slot_section != 0) {
ReleaseLogInfoSlot(LogContext,slot_section);
}
return Err;
}
BOOL
AssociateDevInstWithDefaultService(
IN PDEVINFO_ELEM DevInfoElem,
OUT PTSTR ServiceName,
IN OUT PDWORD ServiceNameSize
)
/*++
Routine Description:
This routine attempts to find out the default service with which to associate
the specified device. The default service (if there is one) is associated with
the device's class. If a default is found, the device instance is associated
with that service.
Arguments:
DeviceInfoData - Specifies the device information element to create a default
service association for.
ServiceName - Supplies the address of a character buffer that receives the name
of the service with which the device instance was associated (if this routine
is successful).
ServiceNameSize - Supplies the address of a variable containing the size, in bytes,
of the ServiceName buffer. On output, this variable receives the number of
bytes actually stored in ServiceName.
Return Value:
If the function succeeds, the return value is TRUE, otherwise it is FALSE.
--*/
{
HKEY hClassKey;
DWORD RegDataType;
BOOL Success;
//
// Open up the class key for this device's class.
//
if((hClassKey = SetupDiOpenClassRegKey(&(DevInfoElem->ClassGuid),
KEY_READ)) == INVALID_HANDLE_VALUE) {
return FALSE;
}
Success = FALSE; // assume failure
try {
//
// Retrieve the "Default Service" value from the class key. If present, this value entry
// indicates what service to associate the device with, when one isn't specified during
// installation.
//
if (RegQueryValueEx(hClassKey,
pszDefaultService,
NULL,
&RegDataType,
(PBYTE)ServiceName,
ServiceNameSize) != ERROR_SUCCESS) {
goto clean0;
}
*ServiceNameSize = (lstrlen(ServiceName) + 1) * sizeof(TCHAR);
if((RegDataType != REG_SZ) || (*ServiceNameSize < sizeof(TCHAR)) || !(*ServiceName)) {
goto clean0;
}
//
// We have successfully retrieved the default service name to be associated with this
// device instance. Perform the association now by setting the Service device registry
// property.
//
if(CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_SERVICE,
ServiceName,
*ServiceNameSize,
0) == CR_SUCCESS) {
Success = TRUE;
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
Success = FALSE;
}
RegCloseKey(hClassKey);
return Success;
}
DWORD
DeleteServicesInList(
IN PSVCNAME_NODE ServicesToDelete,
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
This routine deletes each service entry in the supplied linked list. This is
typically called to clean up if something goes wrong during a device's installation.
If the 'DeleteEventLog' flag for a particular node is TRUE, then the corresponding
event log entry under HKLM\System\CurrentControlSet\Services\EventLog\<EventLogType> is
also deleted.
Arguments:
ServicesToDelete - supplies a pointer to the head of a linked list of service names
to be deleted.
LogContext - context for logging failures
Return Value:
Error if we could not open service manager or acquire lock.
Note that we do not return error for individual services.
--*/
{
SC_HANDLE SCMHandle, ServiceHandle;
SC_LOCK SCLock;
HKEY hKeyEventLog = NULL, hKeyEventLogType;
TCHAR RegistryPath[SIZECHARS(REGSTR_PATH_SERVICES) + SIZECHARS(DISTR_EVENTLOG) + (2 * 256)];
DWORD Result = NO_ERROR;
DWORD LastErr;
if(SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) {
Result = pAcquireSCMLock(SCMHandle, &SCLock, LogContext);
if(Result == NO_ERROR) {
for(; ServicesToDelete; ServicesToDelete = ServicesToDelete->Next) {
LastErr = NO_ERROR;
if(ServiceHandle = OpenService(SCMHandle,
ServicesToDelete->Name,
SERVICE_ALL_ACCESS)) {
//
// stop the service first if we're supposed to
// wait awhile for the service to stop before deleting the
// service, since we don't want the service to be in use when
// delete the service or the service binaries
if (ServicesToDelete->Flags & SPSVCINST_STOPSERVICE) {
SERVICE_STATUS ssStatus;
if (ControlService( ServiceHandle,
SERVICE_CONTROL_STOP ,
&ssStatus)
|| (LastErr = GetLastError()) == ERROR_SERVICE_NOT_ACTIVE) {
#define SLEEP_TIME 4000
#define LOOP_COUNT 30
DWORD loopCount = 0;
do {
BOOL b;
b = QueryServiceStatus( ServiceHandle, &ssStatus);
if ( !b ) {
LastErr = GetLastError();
//
// query failed for some reason, but let's
// just delete the service anyway
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELSERVSTAT_ERROR,
NULL,
ServicesToDelete->Name
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
LastErr);
break;
}
if (ssStatus.dwCurrentState == SERVICE_STOP_PENDING) {
if ( loopCount++ == LOOP_COUNT ) {
// still pending after LOOP_COUNT iterations...
// just delete the service anyway
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_DELSERVPEND_ERROR,
NULL,
ServicesToDelete->Name
);
break;
}
Sleep( SLEEP_TIME );
} else {
loopCount++;
}
} while ( ssStatus.dwCurrentState != SERVICE_STOPPED
&& loopCount < LOOP_COUNT );
} else {
// control service failed for some reason...
// let's just continue on and try to delete the
// service anyway
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELSERVCTRL_ERROR,
NULL,
ServicesToDelete->Name
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
LastErr);
}
}
if(DeleteService(ServiceHandle) ||
((LastErr = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE)) {
//
// Delete succeeded, or we don't care
//
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_DELSERV_OK,
NULL,
ServicesToDelete->Name
);
} else {
// delete service failed for some reason...
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELSERV_ERROR,
NULL,
ServicesToDelete->Name
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
LastErr);
}
CloseServiceHandle(ServiceHandle);
} else {
LastErr = GetLastError();
if(LastErr == ERROR_SERVICE_DOES_NOT_EXIST) {
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_DELSERVNOSERV,
NULL,
ServicesToDelete->Name
);
} else {
//
// open service failed for some reason
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELSERVOPEN_ERROR,
NULL,
ServicesToDelete->Name
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
LastErr);
}
}
//
// Delete the event log entry (if required) if either (a) we succeeded in deleting
// the service, or (b) the service didn't exist.
//
if(ServicesToDelete->DeleteEventLog) {
if(ServiceHandle || (LastErr == ERROR_SERVICE_DOES_NOT_EXIST)) {
//
// We need to delete the associated event log for this service.
//
if(!hKeyEventLog) {
//
// We haven't opened up the EventLog registry key yet, so do that now.
//
PTSTR EndPtr;
size_t EndCount;
if(MYVERIFY(SUCCEEDED(StringCchCopyEx(RegistryPath,
SIZECHARS(RegistryPath),
pszServicesRegPath,
&EndPtr,
&EndCount,
0)))) {
MYVERIFY(SUCCEEDED(StringCchCopy(EndPtr,
EndCount,
pszEventLog)));
}
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
RegistryPath,
0,
KEY_READ,
&hKeyEventLog) != ERROR_SUCCESS) {
hKeyEventLog = NULL; // make sure this value is still NULL!
continue;
}
}
//
// Now open up the event log type key.
//
if(RegOpenKeyEx(hKeyEventLog,
ServicesToDelete->EventLogType,
0,
KEY_READ | KEY_WRITE,
&hKeyEventLogType) == ERROR_SUCCESS) {
pSetupRegistryDelnode(hKeyEventLogType, ServicesToDelete->EventLogName);
RegCloseKey(hKeyEventLogType);
}
}
}
}
if(hKeyEventLog) {
RegCloseKey(hKeyEventLog);
}
UnlockServiceDatabase(SCLock);
}
CloseServiceHandle(SCMHandle);
} else {
Result = GetLastError();
}
if (Result != NO_ERROR) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELSERVSCM_ERROR,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Result);
}
return Result;
}
BOOL
IsDevRemovedFromAllHwProfiles(
IN PCTSTR DeviceInstanceId,
IN HMACHINE hMachine
)
/*++
Routine Description:
This routine determines whether the specified device instance has been removed from
every hardware profile. The device has been removed from a particular profile if
its corresponding CsConfigFlags has the CSCONFIGFLAG_DO_NOT_CREATE bit set.
Arguments:
DeviceInstanceId - Supplies the name of the device instance to check.
Return Value:
If the device exists in only the specified profile, the return value is TRUE,
otherwise, it is FALSE.
--*/
{
CONFIGRET cr;
ULONG i = 0;
HWPROFILEINFO HwProfileInfo;
ULONG HwProfFlags;
//
// Enumerate all the hardware profiles.
//
do {
if((cr = CM_Get_Hardware_Profile_Info_Ex(i, &HwProfileInfo, 0,hMachine)) == CR_SUCCESS) {
if((CM_Get_HW_Prof_Flags_Ex((DEVINSTID)DeviceInstanceId,
HwProfileInfo.HWPI_ulHWProfile,
&HwProfFlags,
0,
hMachine) != CR_SUCCESS) ||
!(HwProfFlags & CSCONFIGFLAG_DO_NOT_CREATE))
{
//
// If we couldn't retrieve the CSConfigFlags, or if the
// CSCONFIGFLAG_DO_NOT_CREATE bit was not set, then we've found
// a profile where the device still exists, so we can bail here.
//
return FALSE;
}
}
i++;
} while(cr != CR_NO_MORE_HW_PROFILES);
//
// We didn't find any hardware profile where the device wasn't removed.
//
return TRUE;
}
DWORD
GetDevInstConfigFlags(
IN DEVINST DevInst,
IN DWORD Default,
IN HMACHINE hMachine
)
/*++
Routine Description:
This routine retrieves the ConfigFlags for the specified device instance. If the
value can not be retrieved, the specified default is returned.
Arguments:
DevInst - Supplies the handle of the device instance for which the ConfigFlags value
is to be retrieved.
Default - Supplies the default value that should be returned if for some reason the
ConfigFlags cannot be retrieved.
Return Value:
The ConfigFlags value for the specified device instance.
Notes:
This is used for device install, and doesn't need to be remotable for 5.0
--*/
{
DWORD ConfigFlags;
ULONG ConfigFlagsSize = sizeof(ConfigFlags);
if(CM_Get_DevInst_Registry_Property_Ex(DevInst,
CM_DRP_CONFIGFLAGS,
NULL,
&ConfigFlags,
&ConfigFlagsSize,
0,
hMachine) != CR_SUCCESS) {
ConfigFlags = Default;
}
return ConfigFlags;
}
DWORD
pSetupDeleteService(
IN PINFCONTEXT LineContext,
IN DWORD Flags,
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
This routine processes the specified DelService line in an INF's Service
install section. The line has the form:
DelService = <ServiceName>[, [<flags>] [, [<EventLogType>] [, <EventName>]]]
Flags :
SPSVCINST_DELETEEVENTLOGENTRY - delete the associated event log entry
for this service (if there is one).
If the EventLogType field isn't specified,
then it is assumed to be "System". If
the EventName field isn't specified, then
it is assumed to be the same as the service
name.
SPSVCINST_DELETEEVENTLOGENTRY - stop the service before deleting it
Arguments:
LineContext - Supplies the context of the DelService line to be processed.
Flags - specifies one or more SPSVCINST_* flags
LogContext - Supplies a pointer to a log context to be used for logging.
Return Value:
If field 1 on the specified line could not be retrieved, then an error
is returned. Otherwise, the routine returns NO_ERROR (i.e., the routine
is considered successful regardless of whether the service to delete
actually existed).
--*/
{
SVCNAME_NODE TempSvcNode;
DWORD DelServiceFlags;
PCTSTR EventLogType, EventLogName;
BOOL DeleteEventLogEntry;
DWORD Result = NO_ERROR;
//
// Initialize a service name node for a call to DeleteServicesInList.
//
if(!SetupGetStringField(LineContext,
1,
TempSvcNode.Name,
SIZECHARS(TempSvcNode.Name),
NULL)) {
return GetLastError();
}
//
// Get the flags field.
//
if(!SetupGetIntField(LineContext, 2, (PINT)&DelServiceFlags)) {
DelServiceFlags = 0;
}
DeleteEventLogEntry = (Flags & SPSVCINST_DELETEEVENTLOGENTRY);
//
// If the caller specified that the associated event log entry should be
// deleted, then make sure that flag is set.
//
if(DeleteEventLogEntry) {
DelServiceFlags |= SPSVCINST_DELETEEVENTLOGENTRY;
}
if(TempSvcNode.DeleteEventLog = (DelServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY)) {
//
// Retrieve the event log type (default is "System") and the event log name
// (default is the service name).
//
if(!(EventLogType = pSetupGetField(LineContext, 3)) || !(*EventLogType)) {
EventLogType = pszSystem;
}
if(!(EventLogName = pSetupGetField(LineContext, 4)) || !(*EventLogName)) {
EventLogName = TempSvcNode.Name;
}
MYVERIFY(SUCCEEDED(StringCchCopy(TempSvcNode.EventLogType,
SIZECHARS(TempSvcNode.EventLogType),
EventLogType)));
MYVERIFY(SUCCEEDED(StringCchCopy(TempSvcNode.EventLogName,
SIZECHARS(TempSvcNode.EventLogName),
EventLogName)));
}
TempSvcNode.Next = NULL;
TempSvcNode.Flags = DelServiceFlags | Flags;
Result = DeleteServicesInList(&TempSvcNode,LogContext);
return Result;
}
BOOL
IsNativeDriver(
PCTSTR FullPath
)
/*++
Routine Description:
determines if a kernel-mode driver binary is valid
helps avoid bugchecks and catch problems earlier
Arguments:
FullPath - Fully qualified path to the binary to be processed
Return Value:
TRUE indicates that the file is a native driver
--*/
{
LOADED_IMAGE LoadedImage;
BOOL RetVal = FALSE;
PSTR FullPathCopy;
WORD WantedImage;
BOOL locked = FALSE;
#if defined(_IA64_)
WantedImage = IMAGE_FILE_MACHINE_IA64;
#elif defined(_AMD64_)
WantedImage = IMAGE_FILE_MACHINE_AMD64;
#elif defined(_X86_)
if(IsWow64) {
//
// we don't support installing drivers if in Wow64
//
return FALSE;
}
WantedImage = IMAGE_FILE_MACHINE_I386;
#else
#error Unknown platform
#endif
//
// imagehlp takes a non-const ANSI string, so convert it.
//
FullPathCopy = pSetupUnicodeToMultiByte(FullPath, CP_ACP);
if (!FullPathCopy) {
return(FALSE);
}
ZeroMemory(&LoadedImage,sizeof(LoadedImage));
//
// get the image headers
//
try {
EnterCriticalSection(&ImageHlpMutex);
locked = TRUE;
} except(EXCEPTION_EXECUTE_HANDLER) {
}
if(!locked) {
MyFree(FullPathCopy);
return FALSE;
}
if (MapAndLoad(
FullPathCopy,
NULL,
&LoadedImage,
FALSE, // assume it's an exe if there isn't any file extension
TRUE /* read only */ )) {
if ((LoadedImage.FileHeader->Signature == IMAGE_NT_SIGNATURE)
&& (LoadedImage.FileHeader->FileHeader.Machine == WantedImage)
&& (LoadedImage.FileHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) {
RetVal = TRUE;
}
UnMapAndLoad(&LoadedImage);
}
LeaveCriticalSection(&ImageHlpMutex);
MyFree(FullPathCopy);
return(RetVal);
}
DWORD
pSetupAddService(
IN PINFCONTEXT LineContext,
OUT PSVCNAME_NODE *SvcListHead,
IN DWORD Flags,
IN DEVINST DevInst, OPTIONAL
OUT PBOOL NullDriverInstalled,
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
This routine processes the specified AddService line in an INF's Service
install section. The line has the form:
AddService = <ServiceName>, [<Flags>], <ServiceInstallSection>[, <EventLogInstallSection>[, [<EventLogType>] [, <EventName>]]]
Currently, the following flags are defined:
SPSVCINST_TAGTOFRONT (0x1) - Move the tag for this service to the front of its
group order list
SPSVCINST_ASSOCSERVICE (0x2) - Associate this service with the device instance
being installed (only used if DevInst is non-zero)
SPSVCINST_NOCLOBBER_DISPLAYNAME (0x8) - If this flag is specified, then
we will not overwrite the service's
display name, if it already exists.
SPSVCINST_NOCLOBBER_STARTTYPE (0x10) - If this flag is specified, then
we will not overwrite the service's
start type if the service already exists.
SPSVCINST_NOCLOBBER_ERRORCONTROL (0x20) - If this flag is specified, then
we will not overwrite the service's
error control value if the service
already exists.
SPSVCINST_NOCLOBBER_LOADORDERGROUP (0x40) - If this flag is specified, then
we will not overwrite the service's
load order group if it already
exists.
SPSVCINST_NOCLOBBER_DEPENDENCIES (0x80) - If this flag is specified, then
we will not overwrite the service's
dependencies list if it already
exists.
SPSVCINST_CLOBBER_SECURITY (0x400) - If this flag is specified, then
security may be overridden.
A service with the name <ServiceName> is created. The parameters used in the
call to CreateService are retrieved from the <ServiceInstallSection>, and are
in the following format (lines not marked as optional must be present or the
routine will fail):
DisplayName = <string> ; (optional) 'Friendly name' for the service
ServiceType = <number> ; one of the SERVICE_* type codes
StartType = <number> ; one of the SERVICE_* start codes
ErrorControl = <number> ; one of the SERVICE_ERROR_* error control codes
ServiceBinary = <string> ; path to binary
LoadOrderGroup = <string> ; (optional) group to which this service belongs
Dependencies = <string>[[, <string>]...] ; (optional) list of groups (prefixed with '+')
; and services this service depends on
StartName = <string> ; (optional) driver object name used to load the
; driver--only used for drivers & filesystems
Security = <string> ; (optional) SDS specifying security
SetupInstallFromInfSection is then called for the <ServiceInstallSection>, which may
also contain registry modifications (SPINST_REGISTRY is the only flag used). HKR is
the service entry key.
Finally, if <EventLogInstallSection> is specified, then a key for this service is
created under HKLM\System\CurrentControlSet\Services\EventLog, and SetupInstallFromInfSection
is invoked to do registry modifications specified in that section, with HKR being the event log
entry (again, only SPINST_REGISTRY is used). By default, the event log type is "System" and the
event log name is the same as the service name.
Arguments:
LineContext - Supplies the context of the AddService line to be processed.
SvcListHead - Supplies the address of the linked-list head containing a list of
all services newly created as a result of the current installation. This
routine first checks for the presence of the service, and if it already exists,
then it simply modifies the existing one. If the service doesn't already exist,
then this routine creates a new SVCNAME_NODE, and fills it in with the name of
the newly-created service. Likewise, if an EventLog entry is given, then the
presence of an existing one is checked first, and the service node's
'DeleteEventLog' field is set to TRUE only if the event log entry didn't
previously exist. This list is kept to allow for proper clean-up in case
of a later failure.
Flags - Specifies how the service should be installed. These flags are basically
overrides of what the AddService flags field specifies, as described above.
DevInst - If specified (i.e., non-zero), and if the SPSVCINST_ASSOCSERVICE flag is
set in either the Flags parameter or the AddService flags INF field, then we will
store this service name in the device instance's 'Service' registry property.
NullDriverInstalled - Supplies a pointer to a boolean variable that is set
upon successful return to indicate whether or not the service install
specified a null service (i.e., the service name field in the INF AddService
entry was empty).
LogContext - Supplies a pointer to a log context so that info may be logged.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
Remarks:
Note that we don't do anything special for SERVICE_ADAPTER and SERVICE_RECOGNIZER_DRIVER
service types. These types are invalid as far as the service contoller is concerned, so
we just let the create/change service APIs do the validation on them.
--*/
{
PCTSTR ServiceName, InstallSection, EventLogType, EventLogName;
HINF hInf;
INFCONTEXT InstallSectionContext;
DWORD ServiceType, StartType, ErrorControl, ServiceInstallFlags;
PCTSTR ServiceBinary;
PCTSTR ActualBinary = NULL;
TCHAR ServiceBinaryBuffer[MAX_PATH];
PCTSTR DisplayName = NULL, LoadOrderGroup = NULL,
StartName = NULL, Security = NULL, Description = NULL;
PTSTR DependenciesBuffer;
DWORD TagId;
PDWORD NewTag;
DWORD Err;
SC_HANDLE SCMHandle, ServiceHandle;
SC_LOCK SCLock;
HKEY hKeyService, hKeyEventLog;
TCHAR RegistryPath[SIZECHARS(REGSTR_PATH_SERVICES) + SIZECHARS(DISTR_EVENTLOG) + (2 * 256)];
DWORD EventLogKeyDisposition;
SVCNAME_NODE NewSvcNameNode;
PSVCNAME_NODE TmpNode;
BOOL NewService;
INT PathLen;
BOOL b, BinaryInSysRoot, ServiceHasTag;
LPQUERY_SERVICE_CONFIG ServiceConfig;
REGMOD_CONTEXT RegContext;
BOOL NeedsReboot;
//
// Initially, assume we're not doing a null service install.
//
*NullDriverInstalled = FALSE;
NeedsReboot = FALSE;
//
// Get the AddService flags.
//
if(!SetupGetIntField(LineContext, 2, (PINT)&ServiceInstallFlags)) {
ServiceInstallFlags = 0;
}
//
// Allow the caller-supplied flags to override the INF.
//
ServiceInstallFlags |= Flags;
//
// Now get the service name.
//
if(!(ServiceName = pSetupGetField(LineContext, 1)) || !(*ServiceName)) {
//
// This is only allowed if the SPSVCINST_ASSOCSERVICE flag is set. That
// indicates to PnP that a null driver installation is allowed, even
// though the underlying bus didn't report the device as raw-capable.
//
if(ServiceInstallFlags & SPSVCINST_ASSOCSERVICE) {
if(DevInst) {
CM_Set_DevInst_Registry_Property(DevInst,
CM_DRP_SERVICE,
NULL,
0,
0
);
}
*NullDriverInstalled = TRUE;
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_ADDSERV_NULL,
NULL);
return NO_ERROR;
} else {
return GetLastError();
}
}
//
// Next, get the name of the install section.
//
if(!(InstallSection = pSetupGetField(LineContext, 3))) {
return GetLastError();
}
//
// Locate the service install section.
//
hInf = LineContext->Inf;
//
// Retrieve the required values from this section. Don't do validation on them--leave
// that up to the Service Control Manager.
//
if(!SetupFindFirstLine(hInf, InstallSection, pszServiceType, &InstallSectionContext) ||
!SetupGetIntField(&InstallSectionContext, 1, (PINT)&ServiceType)) {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
if(!SetupFindFirstLine(hInf, InstallSection, pszStartType, &InstallSectionContext) ||
!SetupGetIntField(&InstallSectionContext, 1, (PINT)&StartType)) {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
if(!SetupFindFirstLine(hInf, InstallSection, pszErrorControl, &InstallSectionContext) ||
!SetupGetIntField(&InstallSectionContext, 1, (PINT)&ErrorControl)) {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
BinaryInSysRoot = FALSE;
if(SetupFindFirstLine(hInf, InstallSection, pszServiceBinary, &InstallSectionContext) &&
(ServiceBinary = pSetupGetField(&InstallSectionContext, 1)) && *ServiceBinary) {
//
// Compare the initial part of this path with the WindowsDirectory path. If they're
// the same, then we strip off that part (including the dividing backslash), and use
// the rest of the path for the subsequent calls to SCM. This allows SCM to assign
// the special path to the binary, that is accessible, at any time (i.e, boot-loader on).
//
ActualBinary = ServiceBinary;
PathLen = lstrlen(WindowsDirectory);
MYASSERT(PathLen);
//
// Make sure that the it is possible for the WindowsDirectory to fit in the ServiceBinary
// path string.
//
if(PathLen < lstrlen(ServiceBinary)) {
//
// There will never be a trailing backslash in the WindowsDirectory path, unless the
// installation is at the root of a drive (e.g., C:\). Check this, just to be
// on the safe side.
//
// DBCS-unfriendly code ahead. This isn't a problem in the ANSI version of
// setupapi presently, because there's no such thing as service installation on Win9x.
//
b = (WindowsDirectory[PathLen - 1] == TEXT('\\'));
if(b || (ServiceBinary[PathLen] == TEXT('\\'))) {
//
// The path prefix is in the right format--now we need to see if the two
// paths actually match. Copy just the prefix part to another buffer, so
// that we can do the comparison.
//
MYVERIFY(SUCCEEDED(StringCchCopy(ServiceBinaryBuffer,
SIZECHARS(ServiceBinaryBuffer),
ServiceBinary)));
ServiceBinaryBuffer[PathLen] = TEXT('\0');
if(!lstrcmpi(WindowsDirectory, ServiceBinaryBuffer)) {
//
// We have a match--take the relative part of the path (relative to SystemRoot),
// and do one of the following:
//
// 1. If it's a driver, simply use the relative part (no preceding backslash).
// This tells the bootloader/NtLoadDriver that the path is relative to the
// SystemRoot, so the driver can be loaded no matter what phase it's loaded in.
//
// 2. If it's a Win32 service, prepend a %SystemRoot%, so that the service will
// still be able to start if the drive letter mappings change.
//
ServiceBinary += PathLen;
if(!b) {
ServiceBinary++;
}
if(ServiceType & SERVICE_WIN32) {
PTSTR EndPtr;
size_t EndCount;
if(MYVERIFY(SUCCEEDED(StringCchCopyEx(ServiceBinaryBuffer,
SIZECHARS(ServiceBinaryBuffer),
pszSystemRoot,
&EndPtr,
&EndCount,
0)))) {
MYVERIFY(SUCCEEDED(StringCchCopy(EndPtr,EndCount,ServiceBinary)));
}
ServiceBinary = ServiceBinaryBuffer;
}
BinaryInSysRoot = TRUE;
}
}
}
} else {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
//
// If this is a driver, then it has to be located under SystemRoot.
//
if(ServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER)) {
if(!BinaryInSysRoot) {
//
// service install section is wrong, driver path is bad
//
return ERROR_BAD_SERVICE_INSTALLSECT;
}
if(DevInst) {
if(!FileExists(ActualBinary,NULL)) {
//
// service install section is wrong, it doesn't point to existing
// binary
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_MISSING_DRIVER,
NULL,
ServiceName,
ActualBinary
);
return ERROR_BAD_SERVICE_INSTALLSECT;
}
if(!IsNativeDriver(ActualBinary)) {
//
// oh oh, we've come this far, only to find that we're going to try and use
// a non-native or bad driver
//
// we might be able to revert original binary if there was one
//
if(pSetupRestoreLastKnownGoodFile(ActualBinary,0,LogContext)
&& IsNativeDriver(ActualBinary)) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_REVERTED_BAD_DRIVER,
NULL,
ServiceName,
ActualBinary
);
} else {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_HAVE_BAD_DRIVER,
NULL,
ServiceName,
ActualBinary
);
}
return ERROR_DRIVER_NONNATIVE;
}
}
}
//
// if this is a boot start driver, we need a reboot for it to start running
//
if (StartType == SERVICE_BOOT_START) {
NeedsReboot = TRUE;
}
//
// Now check for the other, optional, parameters.
//
if(SetupFindFirstLine(hInf, InstallSection, pszDisplayName, &InstallSectionContext)) {
if((DisplayName = pSetupGetField(&InstallSectionContext, 1)) && !(*DisplayName)) {
DisplayName = NULL;
}
}
if(SetupFindFirstLine(hInf, InstallSection, pszLoadOrderGroup, &InstallSectionContext)) {
if((LoadOrderGroup = pSetupGetField(&InstallSectionContext, 1)) && !(*LoadOrderGroup)) {
LoadOrderGroup = NULL;
}
}
if(SetupFindFirstLine(hInf, InstallSection, pszSecurity, &InstallSectionContext)) {
if((Security = pSetupGetField(&InstallSectionContext, 1)) && !(*Security)) {
Security = NULL;
}
}
if(SetupFindFirstLine(hInf, InstallSection, pszDescription, &InstallSectionContext)) {
if((Description = pSetupGetField(&InstallSectionContext, 1)) && !(*Description)) {
Description = NULL;
}
}
//
// Only retrieve the StartName parameter for kernel-mode drivers and win32 services.
//
if(ServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_WIN32)) {
if(SetupFindFirstLine(hInf, InstallSection, pszStartName, &InstallSectionContext)) {
if((StartName = pSetupGetField(&InstallSectionContext, 1)) &&
!(*StartName)) {
StartName = NULL;
}
}
}
//
// We now need to retrieve the multi-sz list of dependencies. This requires memory allocation,
// so we include everything from here on out in try/except, so that we can do proper clean-up
// in case we encounter an inpage error.
//
DependenciesBuffer = NULL;
SCMHandle = ServiceHandle = NULL;
SCLock = NULL;
hKeyService = hKeyEventLog = NULL;
Err = NO_ERROR;
NewService = FALSE;
ServiceConfig = NULL;
try {
if(!(DependenciesBuffer = GetMultiSzFromInf(hInf, InstallSection, pszDependencies, &b)) && b) {
//
// Then we failed to retrieve a dependencies list because of an out-of-memory error.
//
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// We've now retrieved all parameters necessary to create a service.
//
if(!(SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
Err = GetLastError();
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVSCM_ERROR,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto clean0;
}
//
// Only generate a tag for this service if it has a load order group, and is a kernel or
// filesystem driver.
//
ServiceHasTag = (LoadOrderGroup &&
(ServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER)));
NewTag = ServiceHasTag ? &TagId : NULL;
ServiceHandle = CreateService(SCMHandle,
ServiceName,
DisplayName,
SERVICE_CHANGE_CONFIG,
ServiceType,
StartType,
ErrorControl,
ServiceBinary,
LoadOrderGroup,
NewTag,
DependenciesBuffer,
StartName,
NULL
);
if(ServiceHandle) {
NewService = TRUE;
NewSvcNameNode.Next = NULL;
NewSvcNameNode.DeleteEventLog = FALSE;
MYVERIFY(SUCCEEDED(StringCchCopy(NewSvcNameNode.Name,
SIZECHARS(NewSvcNameNode.Name),
ServiceName)));
if( Security ){
//
// Log security being set.
//
if( NO_ERROR != (Err = pSetupCallSCE( ST_SCE_SERVICES, ServiceName, NULL, Security, StartType, NULL )) ) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVSECURE_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto clean0;
}
}
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_ADDSERVCREATE_OK,
NULL,
ServiceName
);
} else {
//
// If we were unable to create the service, then check to see if the service already
// exists. If so, all we need to do is change the configuration parameters in the
// service.
//
if((Err = GetLastError()) != ERROR_SERVICE_EXISTS) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVCREATE_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto clean0;
}
//
// Lock the service database.
//
if(NO_ERROR != (Err = pAcquireSCMLock(SCMHandle, &SCLock, LogContext))) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVLOCK_ERROR,
NULL
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto clean0;
}
if(!(ServiceHandle = OpenService(SCMHandle, ServiceName, SERVICE_ALL_ACCESS))) {
Err = GetLastError();
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVOPEN_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto clean0;
}
if((Err = pSetupRetrieveServiceConfig(ServiceHandle, &ServiceConfig)) != NO_ERROR) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVCONFIG_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
//
// Make sure our ServiceConfig pointer is still NULL.
//
ServiceConfig = NULL;
goto clean0;
}
//
// Since this is an existing driver, then it may already have a perfectly good tag. If
// so, we don't want to disturb it.
//
if(ServiceHasTag) {
if(ServiceConfig->lpLoadOrderGroup && *(ServiceConfig->lpLoadOrderGroup)) {
//
// The service already has a load order group specified.
// Check to see whether the load order group 'noclobber'
// flag is set.
//
if(ServiceInstallFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) {
//
// We should leave the existing load order group as-is.
// We do this by replacing the INF-specified one with the
// previously-existing one. That way, our code below will
// still generate a tag if necessary.
//
LoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
}
if(!lstrcmpi(ServiceConfig->lpLoadOrderGroup, LoadOrderGroup) && ServiceConfig->dwTagId) {
//
// The load order group hasn't changed, and there's already a tag assigned, so
// leave it alone.
//
NewTag = NULL;
TagId = ServiceConfig->dwTagId;
}
}
}
if(ServiceInstallFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME) {
//
// If the service already has a display name, then we don't want
// to overwrite it.
//
if(ServiceConfig->lpDisplayName && *(ServiceConfig->lpDisplayName)) {
DisplayName = NULL;
}
}
if(ServiceInstallFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES) {
//
// If the service already has a dependencies list, then we don't
// want to overwrite it.
//
if(ServiceConfig->lpDependencies && *(ServiceConfig->lpDependencies)) {
MyFree(DependenciesBuffer);
DependenciesBuffer = NULL;
}
}
if(!ChangeServiceConfig(ServiceHandle,
ServiceType,
(ServiceInstallFlags & SPSVCINST_NOCLOBBER_STARTTYPE)
? SERVICE_NO_CHANGE : StartType,
(ServiceInstallFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL)
? SERVICE_NO_CHANGE : ErrorControl,
ServiceBinary,
LoadOrderGroup,
NewTag,
DependenciesBuffer,
StartName,
TEXT(""),
DisplayName)) {
Err = GetLastError();
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVCHANGE_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
goto clean0;
} else {
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_ADDSERVCHANGE_OK,
NULL,
ServiceName
);
}
if( Security && (ServiceInstallFlags & SPSVCINST_CLOBBER_SECURITY)){
//
// Set/log security
// in this scenario, service was already running
// so don't fail if we can't change security
//
if( NO_ERROR != (Err = pSetupCallSCE( ST_SCE_SERVICES,
ServiceName,
NULL,
Security,
(ServiceInstallFlags & SPSVCINST_NOCLOBBER_STARTTYPE)
? ServiceConfig->dwStartType
: StartType,
NULL )) ) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_ADDSERVSECURE_ERROR,
NULL,
ServiceName
);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
//goto clean0;
}
}
}
//
// we've added/updated the service, now handle the description, which is an oddball
// parameter since it's a new parameter and isn't present in the prior calls.
//
//
// we ignore failure at this point since this won't effect operation of the service.
//
if ((NewService && Description) || ((ServiceInstallFlags & SPSVCINST_NOCLOBBER_DESCRIPTION) == 0)) {
SERVICE_DESCRIPTION ServiceDescription;
ServiceDescription.lpDescription = (LPTSTR)Description;
ChangeServiceConfig2(ServiceHandle, SERVICE_CONFIG_DESCRIPTION,&ServiceDescription);
}
//
// We've successfully created/updated the service. If this service has a load order group
// tag, then make sure it's in the appropriate GroupOrderList entry.
//
// (We ignore failure here, since the service should still work just fine without this.)
//
if(ServiceHasTag) {
pSetupAddTagToGroupOrderListEntry(LoadOrderGroup,
TagId,
ServiceInstallFlags & SPSVCINST_TAGTOFRONT);
}
//
// Now process any AddReg and DelReg entries found in this service install section.
//
MYVERIFY(SUCCEEDED(StringCchCopy(RegistryPath,
SIZECHARS(RegistryPath),
pszServicesRegPath)));
MYVERIFY(pSetupConcatenatePaths(RegistryPath,
ServiceName,
SIZECHARS(RegistryPath),
NULL
));
if((Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
RegistryPath,
0,
KEY_READ | KEY_WRITE,
&hKeyService)) != ERROR_SUCCESS) {
goto clean0;
}
ZeroMemory(&RegContext, sizeof(RegContext));
RegContext.UserRootKey = hKeyService;
if((Err = pSetupInstallRegistry(hInf, InstallSection, &RegContext)) != NO_ERROR) {
goto clean0;
}
//
// Now, see if the INF also specifies an EventLog installation section. If so, create a
// key under HKLM\System\CurrentControlSet\Services\EventLog\System for that service, and
// run the registry modification lines in the specified install section.
//
if((InstallSection = pSetupGetField(LineContext, 4)) && *InstallSection) {
//
// Get the (optional) event log type and event log name strings.
//
if(!(EventLogType = pSetupGetField(LineContext, 5)) || !(*EventLogType)) {
EventLogType = pszSystem;
}
if(!(EventLogName = pSetupGetField(LineContext, 6)) || !(*EventLogName)) {
EventLogName = ServiceName;
}
//
// We already have the services database registry path in our registry path buffer. All
// we need to do is add the \EventLog\<EventLogType>\<EventLogName> part.
//
MYVERIFY(SUCCEEDED(StringCchCopy(RegistryPath + CSTRLEN(REGSTR_PATH_SERVICES),
SIZECHARS(RegistryPath)-CSTRLEN(REGSTR_PATH_SERVICES),
pszEventLog)));
MYVERIFY(pSetupConcatenatePaths(RegistryPath,
EventLogType,
SIZECHARS(RegistryPath),
NULL
));
MYVERIFY(pSetupConcatenatePaths(RegistryPath,
EventLogName,
SIZECHARS(RegistryPath),
NULL
));
if((Err = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
RegistryPath,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE,
NULL,
&hKeyEventLog,
&EventLogKeyDisposition)) != ERROR_SUCCESS) {
goto clean0;
}
if(EventLogKeyDisposition == REG_CREATED_NEW_KEY) {
NewSvcNameNode.DeleteEventLog = TRUE;
MYVERIFY(SUCCEEDED(StringCchCopy(NewSvcNameNode.EventLogType,
SIZECHARS(NewSvcNameNode.EventLogType),
EventLogType)));
MYVERIFY(SUCCEEDED(StringCchCopy(NewSvcNameNode.EventLogName,
SIZECHARS(NewSvcNameNode.EventLogName),
EventLogName)));
}
ZeroMemory(&RegContext, sizeof(RegContext));
RegContext.UserRootKey = hKeyEventLog;
if((Err = pSetupInstallRegistry(hInf, InstallSection, &RegContext)) != NO_ERROR) {
goto clean0;
}
}
//
// Service entry (and optional EventLog entry) were successfully installed. If the
// AddService flags field in the INF included the SPSVCINST_ASSOCSERVICE flag, _and_
// the caller supplied us with a non-zero DevInst handle, then we need to set the
// device instance's 'Service' property to indicate that it is associated with this
// service.
//
if(DevInst && (ServiceInstallFlags & SPSVCINST_ASSOCSERVICE)) {
CM_Set_DevInst_Registry_Property(DevInst,
CM_DRP_SERVICE,
ServiceName,
(lstrlen(ServiceName) + 1) * sizeof(TCHAR),
0
);
}
//
// If a new service was created, then link a new service name node into the list we
// were passed in. Don't fret about the case where we can't allocate a node--it just
// means we won't know about this new service in case clean-up is required later.
//
if(NewService) {
if(TmpNode = MyMalloc(sizeof(SVCNAME_NODE))) {
MYVERIFY(SUCCEEDED(StringCchCopy(TmpNode->Name,
SIZECHARS(TmpNode->Name),
NewSvcNameNode.Name)));
if(TmpNode->DeleteEventLog = NewSvcNameNode.DeleteEventLog) {
MYVERIFY(SUCCEEDED(StringCchCopy(TmpNode->EventLogType,
SIZECHARS(TmpNode->EventLogType),
NewSvcNameNode.EventLogType)));
MYVERIFY(SUCCEEDED(StringCchCopy(TmpNode->EventLogName,
SIZECHARS(TmpNode->EventLogName),
NewSvcNameNode.EventLogName)));
}
TmpNode->Next = *SvcListHead;
*SvcListHead = TmpNode;
}
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was
// an inpage error dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
//
// Access the following variables so that the compiler will respect our statement ordering
// w.r.t. these values. Otherwise, we can't be sure that we know whether or not their
// corresponding resources should be freed.
//
DependenciesBuffer = DependenciesBuffer;
hKeyService = hKeyService;
hKeyEventLog = hKeyEventLog;
ServiceHandle = ServiceHandle;
SCLock = SCLock;
SCMHandle = SCMHandle;
NewService = NewService;
ServiceConfig = ServiceConfig;
}
if(ServiceConfig) {
MyFree(ServiceConfig);
}
if(DependenciesBuffer) {
MyFree(DependenciesBuffer);
}
if(hKeyService) {
RegCloseKey(hKeyService);
}
if(hKeyEventLog) {
RegCloseKey(hKeyEventLog);
}
if(ServiceHandle) {
CloseServiceHandle(ServiceHandle);
}
if(SCLock) {
UnlockServiceDatabase(SCLock);
}
if(SCMHandle) {
CloseServiceHandle(SCMHandle);
}
if (Err != NO_ERROR) {
if (NewService) {
//
// Then we failed part-way through, and need to clean up the service (and
// possibly event log entry) we created.
//
DeleteServicesInList(&NewSvcNameNode,LogContext);
}
} else {
if (NeedsReboot) {
SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
}
}
return Err;
}
DWORD
pSetupRetrieveServiceConfig(
IN SC_HANDLE ServiceHandle,
OUT LPQUERY_SERVICE_CONFIG *ServiceConfig
)
/*++
Routine Description:
This routine allocates a buffer for the specified service's configuration parameters,
and retrieves those parameters into the buffer. The caller is responsible for freeing
the buffer.
Arguments:
ServiceHandle - supplies a handle to the service being queried
ServiceConfig - supplies the address of a QUERY_SERVICE_CONFIG pointer that receives
the address of the allocated buffer containing the requested information.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
Remarks:
The pointer whose address is contained in ServiceConfig is guaranteed to be NULL upon
return if any error occurred.
--*/
{
DWORD ServiceConfigSize = 0, Err;
MYASSERT(ServiceConfig);
*ServiceConfig = NULL;
while(TRUE) {
if(QueryServiceConfig(ServiceHandle, *ServiceConfig, ServiceConfigSize, &ServiceConfigSize)) {
MYASSERT(*ServiceConfig);
return NO_ERROR;
} else {
Err = GetLastError();
if(*ServiceConfig) {
MyFree(*ServiceConfig);
}
if(Err == ERROR_INSUFFICIENT_BUFFER) {
//
// Allocate a larger buffer, and try again.
//
if(!(*ServiceConfig = MyMalloc(ServiceConfigSize))) {
return ERROR_NOT_ENOUGH_MEMORY;
}
} else {
*ServiceConfig = NULL;
return Err;
}
}
}
}
DWORD
RetrieveServiceConfig2(
IN SC_HANDLE ServiceHandle,
IN DWORD Level,
OUT LPBYTE *Buffer
)
/*++
Routine Description:
This routine allocates a buffer for the specified service's configuration parameters,
and retrieves those parameters into the buffer. The caller is responsible for freeing
the buffer.
Arguments:
ServiceHandle - supplies a handle to the service being queried
Level - specifies the information to query
Buffer - supplies the address of an opaque address to the buffer containing the info.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
Remarks:
The pointer whose address is contained in Buffer is guaranteed to be NULL upon
return if any error occurred.
--*/
{
DWORD ServiceConfigSize = 0, Err;
*Buffer = NULL;
while(TRUE) {
if(QueryServiceConfig2(ServiceHandle, Level, *Buffer, ServiceConfigSize, &ServiceConfigSize)) {
MYASSERT(*Buffer);
return NO_ERROR;
} else {
Err = GetLastError();
if(*Buffer) {
MyFree(*Buffer);
}
if(Err == ERROR_INSUFFICIENT_BUFFER) {
//
// Allocate a larger buffer, and try again.
//
if(!(*Buffer = MyMalloc(ServiceConfigSize))) {
return ERROR_NOT_ENOUGH_MEMORY;
}
} else {
*Buffer = NULL;
return Err;
}
}
}
}
DWORD
pSetupAddTagToGroupOrderListEntry(
IN PCTSTR LoadOrderGroup,
IN DWORD TagId,
IN BOOL MoveToFront
)
/*++
Routine Description:
This routine first creates the specified LoadOrderGroup value entry under
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GroupOrderList
if the value doesn't already exist. The routine then inserts the specified
tag into the list. If MoveToFront is TRUE, the tag is inserted at the front
of the list (or moved to the front of the list if it was already present in
the list). If MoveToFront is FALSE, then the new tag is inserted at the end
of the list, or left where it is if it already exists in the list.
Arguments:
LoadOrderGroup - Specifies the name of the LoadOrderGroup to insert this new
tag into.
TagId - Specifies the tag ID to be inserted into the list.
MoveToFront - If TRUE, place the tag at the front of the list. If FALSE, then
append the tag to the end of the list, unless it was already there, in which
case it is left where it was.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
--*/
{
DWORD Err;
HKEY hKey;
PDWORD GroupOrderList, p;
DWORD GroupOrderListSize, DataType, ExtraBytes, i, NumElements;
if((Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
pszGroupOrderListPath,
0,
KEY_ALL_ACCESS,
&hKey)) != ERROR_SUCCESS) {
return Err;
}
Err = QueryRegistryValue(hKey,
LoadOrderGroup,
(PVOID)(&GroupOrderList),
&DataType,
&GroupOrderListSize
);
if(Err == NO_ERROR) {
//
// Validate the list, and fix it if it's broken.
//
if(GroupOrderListSize < sizeof(DWORD)) {
if(GroupOrderList) {
MyFree(GroupOrderList);
}
if(GroupOrderList = MyMalloc(sizeof(DWORD))) {
*GroupOrderList = 0;
GroupOrderListSize = sizeof(DWORD);
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
} else {
if(ExtraBytes = GroupOrderListSize % sizeof(DWORD)) {
if(p = MyRealloc(GroupOrderList, GroupOrderListSize + (sizeof(DWORD) - ExtraBytes))) {
GroupOrderList = p;
ZeroMemory((PBYTE)GroupOrderList + GroupOrderListSize, ExtraBytes);
GroupOrderListSize += ExtraBytes;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
}
}
MYASSERT(!(GroupOrderListSize % sizeof(DWORD)));
//
// We now have a list that's at least in the correct format. Now validate the list count,
// and adjust if necessary.
//
NumElements = (GroupOrderListSize / sizeof(DWORD)) - 1;
if(*GroupOrderList != NumElements) {
if(*GroupOrderList > NumElements) {
*GroupOrderList = NumElements;
} else {
NumElements = *GroupOrderList;
GroupOrderListSize = (NumElements + 1) * sizeof(DWORD);
}
}
} else {
//
// If we ran out of memory, then bail, otherwise, just assume
// there wasn't a list to retrieve.
//
if(Err == ERROR_NOT_ENOUGH_MEMORY) {
goto clean0;
} else {
//
// Allocate a list containing no tags.
//
if(GroupOrderList = MyMalloc(sizeof(DWORD))) {
*GroupOrderList = 0;
GroupOrderListSize = sizeof(DWORD);
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
}
//
// Now we have a valid group order list to manipulate.
//
for(i = 0; i < *GroupOrderList; i++) {
if(GroupOrderList[i + 1] == TagId) {
//
// Tag already exists in the list.
//
break;
}
}
if(i == *GroupOrderList) {
//
// Then we didn't find the tag in the list. Add it either to the front, or
// the end, depending on the 'MoveToFront' flag.
//
if(p = MyRealloc(GroupOrderList, GroupOrderListSize + sizeof(DWORD))) {
GroupOrderList = p;
GroupOrderListSize += sizeof(DWORD);
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(MoveToFront) {
MoveMemory(&(GroupOrderList[2]), &(GroupOrderList[1]), *GroupOrderList * sizeof(DWORD));
GroupOrderList[1] = TagId;
} else {
GroupOrderList[*GroupOrderList + 1] = TagId;
}
(*GroupOrderList)++;
} else if(MoveToFront && i) {
MoveMemory(&(GroupOrderList[2]), &(GroupOrderList[1]), i * sizeof(DWORD));
GroupOrderList[1] = TagId;
}
//
// Now write the value back to the registry.
//
Err = RegSetValueEx(hKey,
LoadOrderGroup,
0,
REG_BINARY,
(PBYTE)GroupOrderList,
GroupOrderListSize
);
clean1:
MyFree(GroupOrderList);
clean0:
RegCloseKey(hKey);
return Err;
}
PTSTR
pSetupCmdLineAppendString(
IN OUT PTSTR CmdLine,
IN PCTSTR Key,
IN PCTSTR Value, OPTIONAL
IN OUT PUINT StrLen,
IN OUT PUINT BufSize
)
/*++
Routine Description:
Forms a new (multi-sz) command line by appending a list of arguments to
the current command line. For example:
CmdLine = SpSetupCmdLineAppendString(
CmdLine,
"STF_PRODUCT",
"NTWKSTA"
);
would append "STF_PRODUCT\0NTWKSTA\0\0" to CmdLine.
Arguments:
CmdLine - Original CmdLine, to be appended to. THIS BUFFER MUST CONTAIN
AT LEAST A SINGLE NULL CHARACTER!
Key - Key identifier
Value - Value of Key
StrLen - How long the current string in -- save on strlens
BufSize - Size of Current Buffer
Returns:
Pointer to the new string, or NULL if out-of-memory (in that case, the
original CmdLine buffer is freed).
--*/
{
PTSTR Ptr;
UINT NewLen;
//
// Handle special cases so we don't end up with empty strings.
//
if(!Value || !(*Value)) {
Value = TEXT("\"\"");
}
//
// "\0" -> 1 chars
// "\0\0" -> 2 char
// but we have to back up 1 character...
//
NewLen = (*StrLen + 2 + lstrlen(Key) + lstrlen(Value));
//
// Allocate more space if necessary.
//
if(NewLen >= *BufSize) {
//
// Grow the current buffer
//
*BufSize += 1024;
if(Ptr = MyRealloc(CmdLine, (*BufSize) * sizeof(TCHAR))) {
CmdLine = Ptr;
} else {
//
// Free the memory here so the caller doesn't have to worry about it.
//
MyFree(CmdLine);
return NULL;
}
}
Ptr = &(CmdLine[*StrLen-1]);
MYVERIFY(SUCCEEDED(StringCchCopy(Ptr,
*BufSize-(Ptr-CmdLine),
Key)));
Ptr = &(CmdLine[*StrLen+lstrlen(Key)]);
MYVERIFY(SUCCEEDED(StringCchCopy(Ptr,
*BufSize-(Ptr-CmdLine),
Value)));
CmdLine[NewLen-1] = TEXT('\0');
//
// Update the length of the buffer that we are using
//
*StrLen = NewLen;
return CmdLine;
}
//
// ANSI version
//
BOOL
WINAPI
SetupDiGetActualSectionToInstallA(
IN HINF InfHandle,
IN PCSTR InfSectionName,
OUT PSTR InfSectionWithExt, OPTIONAL
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PSTR *Extension OPTIONAL
)
{
return SetupDiGetActualSectionToInstallExA(InfHandle,
InfSectionName,
NULL,
InfSectionWithExt,
InfSectionWithExtSize,
RequiredSize,
Extension,
NULL
);
}
BOOL
WINAPI
SetupDiGetActualSectionToInstall(
IN HINF InfHandle,
IN PCTSTR InfSectionName,
OUT PTSTR InfSectionWithExt, OPTIONAL
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PTSTR *Extension OPTIONAL
)
/*++
Routine Description:
(See description of SetupDiGetActualSectionToInstallEx)
--*/
{
return SetupDiGetActualSectionToInstallEx(InfHandle,
InfSectionName,
NULL,
InfSectionWithExt,
InfSectionWithExtSize,
RequiredSize,
Extension,
NULL
);
}
//
// ANSI version
//
BOOL
WINAPI
SetupDiGetActualSectionToInstallExA(
IN HINF InfHandle,
IN PCSTR InfSectionName,
IN PSP_ALTPLATFORM_INFO_V2 AlternatePlatformInfo, OPTIONAL
OUT PSTR InfSectionWithExt, OPTIONAL
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PSTR *Extension, OPTIONAL
IN PVOID Reserved
)
{
PWSTR infsectionname;
DWORD rc;
BOOL b;
PWSTR extension;
UINT CharOffset,i;
PSTR p;
DWORD requiredsize;
WCHAR newsection[MAX_SECT_NAME_LEN];
PSTR ansi;
rc = pSetupCaptureAndConvertAnsiArg(InfSectionName,&infsectionname);
if(rc != NO_ERROR) {
SetLastError(rc);
return(FALSE);
}
b = SetupDiGetActualSectionToInstallExW(
InfHandle,
infsectionname,
AlternatePlatformInfo,
newsection,
MAX_SECT_NAME_LEN,
&requiredsize,
&extension,
Reserved
);
rc = GetLastError();
if(b) {
if(ansi = pSetupUnicodeToAnsi(newsection)) {
requiredsize = lstrlenA(ansi)+1;
if(RequiredSize) {
try {
*RequiredSize = requiredsize;
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
b = FALSE;
}
}
if(b && InfSectionWithExt) {
if(requiredsize <= InfSectionWithExtSize) {
if(FAILED(StringCchCopyA(InfSectionWithExt,
InfSectionWithExtSize,
ansi))) {
//
// lstrcpy faulted, so InfSectionWithExt must be bad
//
rc = ERROR_INVALID_PARAMETER;
b = FALSE;
}
} else {
rc = ERROR_INSUFFICIENT_BUFFER;
b = FALSE;
}
}
if(b && Extension) {
if(extension && InfSectionWithExt) {
//
// We need to figure out where the extension is
// in the converted string. To be DBCS safe we will
// count characters forward to find it.
//
CharOffset = (UINT)(extension - newsection);
p = InfSectionWithExt;
for(i=0; i<CharOffset; i++) {
p = CharNextA(p);
}
} else {
p = NULL;
}
try {
*Extension = p;
} except(EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
rc = ERROR_INVALID_PARAMETER;
}
}
MyFree(ansi);
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
b = FALSE;
}
}
MyFree(infsectionname);
SetLastError(rc);
return(b);
}
BOOL
WINAPI
SetupDiGetActualSectionToInstallEx(
IN HINF InfHandle,
IN PCTSTR InfSectionName,
IN PSP_ALTPLATFORM_INFO_V2 AlternatePlatformInfo, OPTIONAL
OUT PTSTR InfSectionWithExt, OPTIONAL
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PTSTR *Extension, OPTIONAL
IN PVOID Reserved
)
/*++
Routine Description:
This API finds the appropriate install section to be used when installing
a device from a Win95-style device INF. Refer to the documentation for
SetupDiInstallDevice for details on how this determination is made.
Arguments:
InfHandle - Supplies the handle of the INF to be installed from.
InfSectionName - Supplies the name of the install section, as specified by
the driver node being installed.
AlternatePlatformInfo - Optionally, supplies alternate platform information
to be used in selecting the section. Presently, only the Platform and
ProcessorArchitecture fields are used in formulating the section
decoration. (NOTE: caller may actually pass in a V1 struct instead--
this is fine, as the fields we care about are shared in common between
the V1 and V2 structs.)
InfSectionWithExt - Optionally, supplies the address of a character buffer
that receives the actual install section name that should be used
during installation. If this parameter is NULL, then
InfSectionWithExtSize must be zero. In that case, the caller is only
interested in retrieving the required buffer size, so the API will
return TRUE, and RequiredSize (if supplied), will be set to the size,
in characters, necessary to store the actual install section name.
InfSectionWithExtSize - Supplies the size, in characters, of the
InfSectionWithExt buffer.
RequiredSize - Optionally, supplies the address of a variable that receives
the size, in characters, required to store the actual install section
name (including terminating NULL).
Extension - Optionally, supplies the address of a variable that receives a
pointer to the extension (including '.'), or NULL if no extension is to
be used. The pointer points to the extension within the caller-
supplied buffer. If the InfSectionWithExt buffer is not supplied, then
this variable will not be filled in.
Reserved - Must be NULL.
Returns:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
Presently, the only possible failures are ERROR_INVALID_PARAMETER
(bad caller-supplied pointers), and ERROR_INSUFFICIENT_BUFFER (if the
caller-supplied buffer isn't large enough). If we fall back to the
baseline (i.e., non-decorated) section name, then we simply return it,
without verifying that the section actually exists.
--*/
{
TCHAR TempInfSectionName[MAX_SECT_NAME_LEN];
DWORD SectionNameLen = (DWORD)lstrlen(InfSectionName);
DWORD ExtBufferLen;
BOOL ExtFound = TRUE;
DWORD Err = NO_ERROR;
DWORD Platform;
PCTSTR NtArchSuffix;
DWORD NtArchSuffixSize;
if(Reserved) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(SectionNameLen >= MAX_SECT_NAME_LEN) {
SetLastError(ERROR_SECTION_NAME_TOO_LONG);
return FALSE;
}
//
// Both V1 and V2 SP_ALTPLATFORM_INFO structures share a common layout for
// the first 6 fields (including Platform and ProcessorArchitecture, which
// are the ones we're interested in here). Thus, all we need to do is
// verify that the cbSize field is one of the two valid values.
//
if(AlternatePlatformInfo) {
if((AlternatePlatformInfo->cbSize != sizeof(SP_ALTPLATFORM_INFO_V2)) &&
(AlternatePlatformInfo->cbSize != sizeof(SP_ALTPLATFORM_INFO_V1))) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
MYVERIFY(SUCCEEDED(StringCchCopy(TempInfSectionName,
SIZECHARS(TempInfSectionName),
InfSectionName)));
Platform = AlternatePlatformInfo ? AlternatePlatformInfo->Platform
: OSVersionInfo.dwPlatformId;
if(Platform == VER_PLATFORM_WIN32_NT) {
//
// We're running on NT, so first try the NT architecture-specific
// extension, then the generic NT extension.
//
if(AlternatePlatformInfo) {
switch(AlternatePlatformInfo->ProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL :
NtArchSuffix = pszNtX86Suffix;
NtArchSuffixSize = sizeof(pszNtX86Suffix);
break;
case PROCESSOR_ARCHITECTURE_IA64 :
NtArchSuffix = pszNtIA64Suffix;
NtArchSuffixSize = sizeof(pszNtIA64Suffix);
break;
case PROCESSOR_ARCHITECTURE_AMD64 :
NtArchSuffix = pszNtAMD64Suffix;
NtArchSuffixSize = sizeof(pszNtAMD64Suffix);
break;
default:
//
// Unknown/invalid architecture
//
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
} else {
NtArchSuffix = pszNtPlatformSuffix;
NtArchSuffixSize = sizeof(pszNtPlatformSuffix);
}
if(NtArchSuffixSize <=
sizeof(TempInfSectionName) - (SectionNameLen * sizeof(TCHAR))) {
MYVERIFY(SUCCEEDED(StringCchCopy(&(TempInfSectionName[SectionNameLen]),
SIZECHARS(TempInfSectionName)-SectionNameLen,
NtArchSuffix)));
if(SetupGetLineCount(InfHandle, TempInfSectionName) != -1) {
goto clean0;
}
//
// We know the ".NT" suffix is always guaranteed to be shorter than
// any of the ".NT<architecture>" suffixes, thus we know this will
// fit in our MAX_SECT_NAME_LEN buffer. (Note: if the OS/arch.
// decoration is too big, then we won't even try the simple OS
// decoration. To do otherwise might be misleading, as we'd select
// a "<reallylongsectionname>.NT" section, even though the INF
// (erroneously) contains a "<reallylongsectionname>.NTx86" section
// that doesn't fit.)
//
MYVERIFY(SUCCEEDED(StringCchCopy(&(TempInfSectionName[SectionNameLen]),
SIZECHARS(TempInfSectionName)-SectionNameLen,
pszNtSuffix)));
if(SetupGetLineCount(InfHandle, TempInfSectionName) != -1) {
goto clean0;
}
}
} else {
//
// We're running on Windows 95, so try the Windows-specific extension
//
if(sizeof(pszWinSuffix) <=
sizeof(TempInfSectionName) - (SectionNameLen * sizeof(TCHAR))) {
MYVERIFY(SUCCEEDED(StringCchCopy(&(TempInfSectionName[SectionNameLen]),
SIZECHARS(TempInfSectionName)-SectionNameLen,
pszWinSuffix)));
if(SetupGetLineCount(InfHandle, TempInfSectionName) != -1) {
goto clean0;
}
}
}
//
// If we get to here, then we found no applicable extensions. We'll just use
// the install section specified.
//
TempInfSectionName[SectionNameLen] = TEXT('\0');
ExtFound = FALSE;
clean0:
//
// Now, determine whether the caller-supplied buffer is large enough to contain
// the section name.
//
ExtBufferLen = lstrlen(TempInfSectionName) + 1;
//
// Guard the rest of the routine in try/except, since we're dealing with caller-supplied
// memory.
//
try {
if(RequiredSize) {
*RequiredSize = ExtBufferLen;
}
if(InfSectionWithExt) {
if(ExtBufferLen > InfSectionWithExtSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
MYVERIFY(SUCCEEDED(StringCchCopy(InfSectionWithExt,
InfSectionWithExtSize,
TempInfSectionName)));
if(Extension) {
*Extension = ExtFound ? InfSectionWithExt + SectionNameLen : NULL;
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
SetLastError(Err);
return (Err == NO_ERROR);
}
BOOL
pSetupInfIsFromOemLocation(
IN PCTSTR InfFileName,
IN BOOL InfDirectoryOnly
)
/*++
Routine Description:
This routine determines whether the specified INF came from one of the directories
in our INF search path list. This list can also be limited to just the %windir%\Inf
directory.
Arguments:
InfFileName - Supplies the fully-qualified path of the INF file.
InfDirectoryOnly - If TRUE, then consider the INF to be from an OEM location if it is
not in the %windir%\Inf directory (i.e., ignore any other directories in the INF
search path).
Returns:
If the file is from an OEM location (i.e., _not_ in our INF search path list), then
the return value is TRUE. Otherwise, it is FALSE.
--*/
{
PCTSTR CharPos, DirTruncPos;
INT DirectoryPathLen, CurSearchPathLen;
//
// First, retrieve just the directory path part of the specified filename.
//
CharPos = pSetupGetFileTitle(InfFileName);
DirTruncPos = CharPrev(InfFileName, CharPos);
//
// (We know pSetupGetFileTitle will never return a pointer to a path separator character,
// so the following check is valid.)
//
if(*DirTruncPos == TEXT('\\')) {
//
// If this is in a root directory (e.g., "A:\"), then we don't want to strip off
// the trailing backslash.
//
if(((DirTruncPos - InfFileName) != 2) || (*CharNext(InfFileName) != TEXT(':'))) {
CharPos = DirTruncPos;
}
}
DirectoryPathLen = (int)(CharPos - InfFileName);
//
// Now, see if this directory matches any of the ones in our search path list.
//
if(InfDirectoryOnly) {
CharPos = InfDirectory;
} else {
CharPos = InfSearchPaths;
}
do {
//
// If the current search path ends in a backslash, we want to strip it off.
//
CurSearchPathLen = lstrlen(CharPos);
if((DirectoryPathLen == CurSearchPathLen) &&
!_tcsnicmp(CharPos, InfFileName, CurSearchPathLen)) {
//
// We've found this directory in our list--we can return.
//
return FALSE;
}
if(InfDirectoryOnly) {
//
// We're only supposed to consider the %windir%\Inf directory--that failed,
// so we're done.
//
break;
} else {
//
// Move on to next component of INF search path.
//
CharPos += (CurSearchPathLen + 1);
}
} while(*CharPos);
//
// If we get to here, then we didn't find the directory in our search path list.
// Therefore, it's from an OEM location.
//
return TRUE;
}
BOOL
WINAPI
SetupDiInstallDeviceInterfaces(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_INSTALLINTERFACES.
This routine will install any device interface specified in an
[<InstallSec>.Interfaces] section, where <InstallSec> is the install
section name for the selected driver node, potentially decorated with
an OS/architecture-specific extension (e.g., "InstallSec.NTAmd64.Interfaces").
Presently, only "AddInterface" lines in this section are processed. Their
format is as follows:
AddInterface = <InterfaceClassGuid> [, [<RefString>] [, [<InstallSection>] [, <Flags>]]]
(There are currently no flags defined for the <Flags> field--must be zero.)
If the interface class specified by <InterfaceClassGuid> is not already
installed in the system, we will install it. An INF can optionally specify
installation actions to be done when this interface class is installed (just
like it can do for class installers via a [ClassInstall32] section. It does
this by including an [InterfaceInstall32] section with an entry for the
interface class to be installed. This section name does not allow os/architecture-
specific decoration. Instead, the install sections referenced by lines within
this section are processed with decoration rules. Thus, it is possible to have
an install entry for interface class <x> that applies to both Win9x and NT, and
have another install entry for interface class <y> that behaves differently
depending on whether we're running on Win9x or NT.
The format of an entry in the [InterfaceInstall32] section is as follows:
<InterfaceClassGuid> = <InstallSection> [, <Flags>]
(There are currently no flags defined for the <Flags> field--must be zero.)
Arguments:
DeviceInfoSet - Supplies a handle to the device information set containing
a device information element whose device interfaces are to be installed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for which
device interfaces are to be installed.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
If no driver is selected (i.e., this is a null-driver installation), then
this routine does nothing.
--*/
{
return _SetupDiInstallDeviceInterfaces(DeviceInfoSet,
DeviceInfoData,
TRUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE
);
}
BOOL
_SetupDiInstallDeviceInterfaces(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall,
IN HINF hDeviceInf, OPTIONAL
IN HSPFILEQ UserFileQ OPTIONAL
)
/*++
Routine Description:
Worker routine for both SetupDiInstallDeviceInterfaces and SetupDiInstallDriverFiles.
See the description of SetupDiInstallDeviceInterfaces for more information.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set containing
a device information element whose device interfaces are to be installed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for which
device interfaces are to be installed.
DoFullInstall - If TRUE, then the device interfaces will be fully installed.
Otherwise, only the required files are copied.
hDeviceInf - Optionally, supplies a handle to the INF for which installation
is being performed. If this handle is not supplied, the INF specified in
the selected driver node will be opened. If this handle is not supplied,
this parameter must be set to INVALID_HANDLE_VALUE.
UserFileQ - Optionally, supplies a file queue where file operations should be added.
If this handle is not supplied, then the queue associated with this devinfo
element will be used (if the DI_NOVCP flag is set), or one will be automatically
generated and committed. If this handle is not supplied, this parameter must
be set to INVALID_HANDLE_VALUE.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
During GUI-mode setup on Windows NT, quiet-install behavior is always
employed in the absence of a user-supplied file queue, regardless of
whether the device information element has the DI_QUIETINSTALL flag set.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err, ScanQueueResult;
PDEVINFO_ELEM DevInfoElem;
HWND hwndParent;
PTSTR szInfFileName, szInfSectionName;
TCHAR InfSectionWithExt[MAX_SECT_NAME_LEN];
DWORD InfSectionWithExtLength;
INFCONTEXT DeviceInterfaceInstallLine, InterfaceClassInstallLine;
TCHAR InterfaceGuidString[GUID_STRING_LEN];
DWORD InstallFlags;
GUID InterfaceGuid;
HKEY hKeyInterfaceClass, hKeyDeviceClassesRoot, hKeyDeviceInterface;
PINTERFACE_CLASS_TO_INSTALL InterfacesToInstall, InterfaceInstallNode, CurInterfaceInstallNode;
DWORD RegDisposition;
BOOL NeedToInstallInterfaceClass;
TCHAR InterfaceInstallSection[MAX_SECT_NAME_LEN];
BOOL DoFileCopying;
BOOL CloseUserFileQ;
BOOL FreeMsgHandlerContext;
PSP_FILE_CALLBACK MsgHandler;
PVOID MsgHandlerContext;
BOOL MsgHandlerIsNativeCharWidth;
TCHAR RefString[MAX_PATH];
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
PCTSTR UndecoratedInstallSection;
BOOL CloseInfHandle;
PTSTR NeedsSectionList, CurInstallSection;
BOOL b;
INT FileQueueNeedsReboot;
PSETUP_LOG_CONTEXT LogContext = NULL;
BOOL NoProgressUI;
DWORD slot_section = 0;
TCHAR szNewName[MAX_PATH];
BOOL OemInfFileToCopy = FALSE;
//
// A device information element must be specified.
//
if(!DeviceInfoData) {
Err = ERROR_INVALID_PARAMETER;
goto clean1;
}
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
Err = ERROR_INVALID_HANDLE;
goto clean1;
}
LogContext = pDeviceInfoSet->InstallParamBlock.LogContext;
Err = NO_ERROR;
hKeyDeviceClassesRoot = hKeyInterfaceClass = hKeyDeviceInterface = INVALID_HANDLE_VALUE;
InterfacesToInstall = InterfaceInstallNode = NULL;
CloseUserFileQ = FALSE;
FreeMsgHandlerContext = FALSE;
CloseInfHandle = FALSE;
NeedsSectionList = NULL;
try {
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL))) {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// This routine can't install a non-native driver
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALTPLATFORM_DRVSEARCH) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
//
// set the LogContext for this function
//
LogContext = DevInfoElem->InstallParamBlock.LogContext;
//
// If there's no driver selected (i.e., this is a null driver install)
// then there's nothing for us to do.
//
if(!DevInfoElem->SelectedDriver) {
goto clean0;
}
//
// Make sure we only use the devinfo element's window if it's valid.
//
if(hwndParent = DevInfoElem->InstallParamBlock.hwndParent) {
if(!IsWindow(hwndParent)) {
hwndParent = NULL;
}
}
//
// Ignore the DI_NOFILECOPY flag if we're doing a copy-only installation--that's
// what setupx does.
//
if(DoFileCopying = (!(DevInfoElem->InstallParamBlock.Flags & DI_NOFILECOPY) || !DoFullInstall)) {
if(UserFileQ == INVALID_HANDLE_VALUE) {
if(DevInfoElem->InstallParamBlock.Flags & DI_NOVCP) {
//
// We must have a user-supplied file queue.
//
MYASSERT(DevInfoElem->InstallParamBlock.UserFileQ);
UserFileQ = DevInfoElem->InstallParamBlock.UserFileQ;
} else {
//
// Create our own queue.
//
if((UserFileQ = SetupOpenFileQueue()) != INVALID_HANDLE_VALUE) {
CloseUserFileQ = TRUE;
} else {
//
// SetupOpenFileQueue sets actual error
//
Err = GetLastError();
goto clean0;
}
}
//
// Maybe replace the file queue's log context with the DevInfoElem's
//
InheritLogContext(LogContext,
&((PSP_FILE_QUEUE) UserFileQ)->LogContext);
}
}
szInfFileName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfFileName
);
szInfSectionName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfSectionName
);
if(hDeviceInf == INVALID_HANDLE_VALUE) {
if((hDeviceInf = SetupOpenInfFile(szInfFileName,
NULL,
INF_STYLE_WIN4,
NULL)) == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
CloseInfHandle = TRUE;
}
//
// Find out the 'real' install section we should be using (i.e., the potentially
// OS/architecture-specific one.
//
if(!SetupDiGetActualSectionToInstall(hDeviceInf,
szInfSectionName,
InfSectionWithExt,
SIZECHARS(InfSectionWithExt),
&InfSectionWithExtLength,
NULL
)) {
Err = GetLastError();
goto clean0;
}
//
// Now append the ".Interfaces" extension to the install section name to find
// the interface install section to run.
//
MYVERIFY(SUCCEEDED(StringCchCopy(&(InfSectionWithExt[InfSectionWithExtLength - 1]),
SIZECHARS(InfSectionWithExt)-(InfSectionWithExtLength - 1),
pszInterfacesSectionSuffix)));
if(slot_section == 0) {
//
// we haven't done anything about logging section yet...
//
slot_section = AllocLogInfoSlotOrLevel(LogContext,DRIVER_LOG_VERBOSE,FALSE);
//
// Say what section is about to be installed.
//
WriteLogEntry(LogContext,
slot_section,
MSG_LOG_INSTALLING_SECTION_FROM,
NULL,
InfSectionWithExt,
szInfFileName);
}
//
// Append the layout INF, if necessary.
//
if(DoFileCopying) {
SetupOpenAppendInfFile(NULL, hDeviceInf, NULL);
}
//
// Append-load any included INFs specified in an "include=" line in our
// install section.
//
AppendLoadIncludedInfs(hDeviceInf, szInfFileName, InfSectionWithExt, DoFileCopying);
NeedsSectionList = GetMultiSzFromInf(hDeviceInf, InfSectionWithExt, TEXT("needs"), &b);
if(!NeedsSectionList && b) {
//
// Out of memory!
//
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Process the "AddInterface" lines in this section, as well as those contained with any
// sections referenced in the "needs=" entry in this section.
//
for(CurInstallSection = InfSectionWithExt;
(CurInstallSection && *CurInstallSection);
CurInstallSection = (CurInstallSection == InfSectionWithExt)
? NeedsSectionList
: (CurInstallSection + lstrlen(CurInstallSection) + 1))
{
if(SetupFindFirstLine(hDeviceInf, CurInstallSection, pszAddInterface, &DeviceInterfaceInstallLine)) {
do {
//
// Retrieve the interface class GUID for this device interface.
//
if(!SetupGetStringField(&DeviceInterfaceInstallLine,
1,
InterfaceGuidString,
SIZECHARS(InterfaceGuidString),
NULL))
{
Err = ERROR_BAD_INTERFACE_INSTALLSECT;
goto clean0;
}
if(NO_ERROR != pSetupGuidFromString(InterfaceGuidString, &InterfaceGuid)) {
Err = ERROR_BAD_INTERFACE_INSTALLSECT;
goto clean0;
}
//
// Next, we need to check and see if the specified interface class is already present.
// If not, we'll need to install it, too.
//
NeedToInstallInterfaceClass = FALSE;
hKeyInterfaceClass = SetupDiOpenClassRegKeyEx(&InterfaceGuid,
KEY_READ,
DIOCR_INTERFACE,
NULL,
NULL
);
if(hKeyInterfaceClass != INVALID_HANDLE_VALUE) {
//
// OK, this interface class already exists. We don't need to install it.
//
RegCloseKey(hKeyInterfaceClass);
hKeyInterfaceClass = INVALID_HANDLE_VALUE;
} else {
//
// This interface class isn't currently installed. Check to see if we've
// already encountered this class (and added it to our list of interface
// classes to install).
//
for(CurInterfaceInstallNode = InterfacesToInstall;
CurInterfaceInstallNode;
CurInterfaceInstallNode = CurInterfaceInstallNode->Next)
{
if(IsEqualGUID(&InterfaceGuid, &(CurInterfaceInstallNode->InterfaceGuid))) {
//
// The installation of this interface class is already in our
// 'to do' list.
//
break;
}
}
if(!CurInterfaceInstallNode) {
NeedToInstallInterfaceClass = TRUE;
}
}
if(NeedToInstallInterfaceClass) {
//
// Look for this interface class GUID in the INF's [InterfaceInstall32] section
// (if present).
//
if(SetupFindFirstLine(hDeviceInf,
pszInterfaceInstall32,
InterfaceGuidString,
&InterfaceClassInstallLine)) {
//
// Get the name of the install section for this interface class.
//
if((UndecoratedInstallSection = pSetupGetField(&InterfaceClassInstallLine, 1))
&& !(*UndecoratedInstallSection))
{
UndecoratedInstallSection = NULL;
}
if(!UndecoratedInstallSection) {
*InterfaceInstallSection = TEXT('\0');
} else {
//
// Get the (potentially) os/architecture-specific install section.
//
PTSTR SectionExtension;
if(!SetupDiGetActualSectionToInstall(hDeviceInf,
UndecoratedInstallSection,
InterfaceInstallSection,
SIZECHARS(InterfaceInstallSection),
NULL,
&SectionExtension
)) {
Err = GetLastError();
goto clean0;
}
//
// If this is the undecorated name, then make sure that the section actually exists.
//
if(!SectionExtension && (SetupGetLineCount(hDeviceInf, InterfaceInstallSection) == -1)) {
WriteLogEntry(LogContext,
DRIVER_LOG_ERROR,
MSG_LOG_NOSECTION,
NULL,
InterfaceInstallSection);
Err = ERROR_SECTION_NOT_FOUND;
goto clean0;
}
}
if(!SetupGetIntField(&InterfaceClassInstallLine, 2, (PINT)&InstallFlags)) {
InstallFlags = 0;
}
} else {
*InterfaceInstallSection = TEXT('\0');
InstallFlags = 0;
}
//
// At this point, we just want to queue up any file operations required.
//
if(DoFileCopying && *InterfaceInstallSection) {
Err = pSetupInstallFiles(hDeviceInf,
NULL,
InterfaceInstallSection,
NULL,
NULL,
NULL,
SP_COPY_NEWER_OR_SAME | SP_COPY_LANGUAGEAWARE |
((DevInfoElem->InstallParamBlock.Flags & DI_NOBROWSE) ? SP_COPY_NOBROWSE : 0),
NULL,
UserFileQ,
TRUE
);
if(Err != NO_ERROR) {
goto clean0;
}
}
//
// Now add a node onto our 'to do' list of interface classes to install.
//
if(InterfaceInstallNode = MyMalloc(sizeof(INTERFACE_CLASS_TO_INSTALL))) {
InterfaceInstallNode->InterfaceGuid = InterfaceGuid;
MYVERIFY(SUCCEEDED(StringCchCopy(InterfaceInstallNode->InstallSection,
SIZECHARS(InterfaceInstallNode->InstallSection),
InterfaceInstallSection)));
InterfaceInstallNode->Flags = InstallFlags;
InterfaceInstallNode->Next = InterfacesToInstall;
InterfacesToInstall = InterfaceInstallNode;
//
// Now set newly allocated node pointer to NULL, so we won't try
// to free it if we hit an exception.
//
InterfaceInstallNode = NULL;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
//
// Now queue up any file operations for this particular device interface.
//
if(DoFileCopying
&& SetupGetStringField(&DeviceInterfaceInstallLine,
3,
InterfaceInstallSection,
SIZECHARS(InterfaceInstallSection),
NULL)
&& *InterfaceInstallSection)
{
Err = pSetupInstallFiles(hDeviceInf,
NULL,
InterfaceInstallSection,
NULL,
NULL,
NULL,
SP_COPY_NEWER_OR_SAME | SP_COPY_LANGUAGEAWARE |
((DevInfoElem->InstallParamBlock.Flags & DI_NOBROWSE)? SP_COPY_NOBROWSE : 0),
NULL,
UserFileQ,
TRUE
);
if(Err != NO_ERROR) {
goto clean0;
}
}
} while(SetupFindNextMatchLine(&DeviceInterfaceInstallLine, pszAddInterface, &DeviceInterfaceInstallLine));
}
}
//
// Mark the queue as a device install queue (and make sure there's a
// catalog node representing our device INF in the queue).
//
if(DoFileCopying) {
MYASSERT(UserFileQ && (UserFileQ != INVALID_HANDLE_VALUE));
Err = MarkQueueForDeviceInstall(UserFileQ,
hDeviceInf,
pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DrvDescription)
);
}
//
// At this point, we have queued up all the files that need to be
// copied. If we weren't given a user-supplied queue, then commit our
// queue now.
//
if(CloseUserFileQ) {
if(Err == NO_ERROR) {
//
// Determine whether the queue actually needs to be committed.
//
// ScanQueueResult can have 1 of 3 values:
//
// 0: Some files were missing--must commit queue.
//
// 1: All files to be copied are already present and valid,
// and the queue is empty--skip committing queue.
//
// 2: All files to be copied are present/valid, but
// del/ren/backup queues not empty--must commit queue.
// The copy queue will have been emptied, so only
// del/ren/backup functions will be performed.
//
// (jamiehun) see previous case of SetupScanFileQueue for a
// discussion of DI_FLAGSEX_PREINSTALLBACKUP handling.
//
if(!SetupScanFileQueue(UserFileQ,
SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE,
hwndParent,
NULL,
NULL,
&ScanQueueResult)) {
//
// SetupScanFileQueue should really never fail when you
// don't ask it to call a callback routine, but if it does,
// just go ahead and commit the queue.
//
ScanQueueResult = 0;
}
}
if((Err == NO_ERROR) && (ScanQueueResult != 1)) {
//
// We need to commit this file queue. Figure out what message
// handler to use.
//
if(DevInfoElem->InstallParamBlock.InstallMsgHandler) {
MsgHandler = DevInfoElem->InstallParamBlock.InstallMsgHandler;
MsgHandlerContext = DevInfoElem->InstallParamBlock.InstallMsgHandlerContext;
MsgHandlerIsNativeCharWidth = DevInfoElem->InstallParamBlock.InstallMsgHandlerIsNativeCharWidth;
} else {
NoProgressUI = (GuiSetupInProgress || (DevInfoElem->InstallParamBlock.Flags & DI_QUIETINSTALL));
if(MsgHandlerContext = SetupInitDefaultQueueCallbackEx(
hwndParent,
(NoProgressUI ? INVALID_HANDLE_VALUE : NULL),
0,
0,
NULL))
{
FreeMsgHandlerContext = TRUE;
MsgHandler = SetupDefaultQueueCallback;
MsgHandlerIsNativeCharWidth = TRUE;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Copy enqueued files.
//
if(Err == NO_ERROR) {
//
// Call _SetupVerifyQueuedCatalogs separately (i.e., don't
// let it happen automatically as a result of committing
// the queue that happens below). We do this beforehand so
// that we know what unique name was generated when an OEM
// INF was installed into %windir%\Inf (in case we need to
// delete the INF/PNF/CAT files later if we encounter an
// error).
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_INTFC_VERIFY_CAT_TIME,
NULL); // text message
Err = _SetupVerifyQueuedCatalogs(
hwndParent,
UserFileQ,
(VERCAT_INSTALL_INF_AND_CAT |
((DevInfoElem->SelectedDriver->Flags & DNF_INET_DRIVER)
? VERCAT_PRIMARY_DEVICE_INF_FROM_INET : 0)),
szNewName,
&OemInfFileToCopy
);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_INTFC_VERIFY_CAT_TIME,
NULL); // text message
if(Err == NO_ERROR) {
if(_SetupCommitFileQueue(hwndParent,
UserFileQ,
MsgHandler,
MsgHandlerContext,
MsgHandlerIsNativeCharWidth
)) {
//
// Check to see whether a reboot is required as a
// result of committing the queue (i.e., because files
// were in use, or the INF requested a reboot).
//
FileQueueNeedsReboot = SetupPromptReboot(UserFileQ, NULL, TRUE);
//
// This should never fail...
//
MYASSERT(FileQueueNeedsReboot != -1);
if(FileQueueNeedsReboot) {
SetDevnodeNeedsRebootProblem(DevInfoElem, pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_INUSE);
}
} else {
Err = GetLastError();
}
}
}
}
//
// Close our file queue handle.
//
SetupCloseFileQueue(UserFileQ);
CloseUserFileQ = FALSE;
//
// Terminate the default queue callback, if it was created.
//
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
FreeMsgHandlerContext = FALSE;
}
if(Err != NO_ERROR) {
goto clean0;
}
}
//
// If all we were asked to do was copy files, then we're done.
//
if(!DoFullInstall) {
goto clean0;
}
//
// Now go through our list of interface classes to install, and install
// each one.
//
if(CurInterfaceInstallNode = InterfacesToInstall) {
//
// Open a handle to the root of the DeviceClasses registry branch.
//
hKeyDeviceClassesRoot = SetupDiOpenClassRegKeyEx(NULL,
KEY_ALL_ACCESS,
DIOCR_INTERFACE,
NULL,
NULL
);
if(hKeyDeviceClassesRoot == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
do {
//
// Presently, the flags field, if present, must be zero.
//
if(CurInterfaceInstallNode->Flags) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
pSetupStringFromGuid(&(CurInterfaceInstallNode->InterfaceGuid),
InterfaceGuidString,
SIZECHARS(InterfaceGuidString)
);
Err = RegCreateKeyEx(hKeyDeviceClassesRoot,
InterfaceGuidString,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKeyInterfaceClass,
&RegDisposition
);
if(Err != ERROR_SUCCESS) {
//
// Ensure that the registry handle is still invalid, so we won't try
// to close it.
//
hKeyInterfaceClass = INVALID_HANDLE_VALUE;
goto clean0;
}
//
// Now run the INF section for this newly-created key.
//
if(!SetupInstallFromInfSection(NULL,
hDeviceInf,
CurInterfaceInstallNode->InstallSection,
SPINST_INIFILES
| SPINST_REGISTRY
| SPINST_INI2REG
| SPINST_BITREG
| SPINST_REGSVR
| SPINST_UNREGSVR
| SPINST_PROFILEITEMS,
hKeyInterfaceClass,
NULL,
0,
NULL,
NULL,
INVALID_HANDLE_VALUE,
NULL))
{
Err = GetLastError();
//
// Normally, we would want to clean up this newly-created key.
// However, the kernel-mode IoRegisterDeviceClassAssociation API
// will quite happily create the class key if it doesn't exist.
// Thus, a driver might have registered a device while we were
// trying to run the INF, and if we deleted the key that might
// really mess things up. Thus, we'll leave this alone. Since
// we should never see a failure with this call, this isn't a big
// deal anyway.
//
}
RegCloseKey(hKeyInterfaceClass);
hKeyInterfaceClass = INVALID_HANDLE_VALUE;
if(Err != NO_ERROR) {
goto clean0;
}
CurInterfaceInstallNode = CurInterfaceInstallNode->Next;
} while(CurInterfaceInstallNode);
}
//
// At this point, we've done everything except for actually registering the
// device interfaces and (potentially) running INFs against their registry keys.
// Do that now...
//
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for(CurInstallSection = InfSectionWithExt;
(CurInstallSection && *CurInstallSection);
CurInstallSection = (CurInstallSection == InfSectionWithExt)
? NeedsSectionList
: (CurInstallSection + lstrlen(CurInstallSection) + 1))
{
if(SetupFindFirstLine(hDeviceInf, CurInstallSection, pszAddInterface, &DeviceInterfaceInstallLine)) {
do {
//
// Retrieve the interface class GUID for this device interface. (There's
// no need to check the return status of these two calls, since we've already
// done this once, and they were fine.
//
SetupGetStringField(&DeviceInterfaceInstallLine,
1,
InterfaceGuidString,
SIZECHARS(InterfaceGuidString),
NULL
);
pSetupGuidFromString(InterfaceGuidString, &InterfaceGuid);
if(!SetupGetStringField(&DeviceInterfaceInstallLine,
2,
RefString,
SIZECHARS(RefString),
NULL))
{
*RefString = TEXT('\0');
}
if(!SetupGetStringField(&DeviceInterfaceInstallLine,
3,
InterfaceInstallSection,
SIZECHARS(InterfaceInstallSection),
NULL))
{
*InterfaceInstallSection = TEXT('\0');
}
if(!SetupGetIntField(&DeviceInterfaceInstallLine, 4, (PINT)&InstallFlags)) {
InstallFlags = 0;
}
//
// (Presently, no flags are defined--field, if present, must be zero.
//
if(InstallFlags) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
//
// OK, we have all the information we need to create this device interface.
//
if(!SetupDiCreateDeviceInterface(DeviceInfoSet,
DeviceInfoData,
&InterfaceGuid,
(*RefString) ? RefString : NULL,
0,
&DeviceInterfaceData)) {
Err = GetLastError();
goto clean0;
}
if(*InterfaceInstallSection) {
//
// Run the INF against this device interface's registry key.
//
hKeyDeviceInterface = SetupDiCreateDeviceInterfaceRegKey(DeviceInfoSet,
&DeviceInterfaceData,
0,
KEY_ALL_ACCESS,
INVALID_HANDLE_VALUE,
NULL
);
if(hKeyDeviceInterface == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
//
// Now run the INF section for this newly-created key.
//
if(!SetupInstallFromInfSection(NULL,
hDeviceInf,
InterfaceInstallSection,
SPINST_INIFILES
| SPINST_REGISTRY
| SPINST_INI2REG
| SPINST_BITREG
| SPINST_REGSVR
| SPINST_UNREGSVR
| SPINST_PROFILEITEMS,
hKeyDeviceInterface,
NULL,
0,
NULL,
NULL,
INVALID_HANDLE_VALUE,
NULL))
{
Err = GetLastError();
}
RegCloseKey(hKeyDeviceInterface);
hKeyDeviceInterface = INVALID_HANDLE_VALUE;
if(Err != NO_ERROR) {
goto clean0;
}
}
} while(SetupFindNextMatchLine(&DeviceInterfaceInstallLine, pszAddInterface, &DeviceInterfaceInstallLine));
}
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
if(hKeyInterfaceClass != INVALID_HANDLE_VALUE) {
RegCloseKey(hKeyInterfaceClass);
}
if(hKeyDeviceInterface != INVALID_HANDLE_VALUE) {
RegCloseKey(hKeyDeviceInterface);
}
if(InterfaceInstallNode) {
MyFree(InterfaceInstallNode);
}
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
}
if(CloseUserFileQ) {
SetupCloseFileQueue(UserFileQ);
}
//
// Reference the following variables so the compiler will respect statement
// ordering w.r.t. assignment.
//
InterfacesToInstall = InterfacesToInstall;
CloseInfHandle = CloseInfHandle;
NeedsSectionList = NeedsSectionList;
OemInfFileToCopy = OemInfFileToCopy;
}
UnlockDeviceInfoSet(pDeviceInfoSet);
for(CurInterfaceInstallNode = InterfacesToInstall;
CurInterfaceInstallNode;
CurInterfaceInstallNode = InterfacesToInstall) {
InterfacesToInstall = CurInterfaceInstallNode->Next;
MyFree(CurInterfaceInstallNode);
}
if(hKeyDeviceClassesRoot != INVALID_HANDLE_VALUE) {
RegCloseKey(hKeyDeviceClassesRoot);
}
if(CloseInfHandle) {
MYASSERT(hDeviceInf != INVALID_HANDLE_VALUE);
SetupCloseInfFile(hDeviceInf);
}
if(NeedsSectionList) {
MyFree(NeedsSectionList);
}
clean1:
if (Err == NO_ERROR) {
//
// give a +ve affirmation of Success
//
WriteLogEntry(
LogContext,
DoFullInstall ? DRIVER_LOG_INFO : DRIVER_LOG_VERBOSE,
MSG_LOG_INSTALLEDINTERFACES,
NULL);
} else {
//
// indicate failed, display error
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_INSTALLINTERFACES_ERROR,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
//
// If we copied the OEM INF into the INF directory under a newly-
// generated name, delete it now.
//
if(OemInfFileToCopy) {
pSetupUninstallOEMInf(szNewName, LogContext, SUOI_FORCEDELETE, NULL);
}
}
if (slot_section) {
ReleaseLogInfoSlot(LogContext,slot_section);
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
pSetupGetSourceMediaTypeFromPnf(
PCTSTR InfName,
PDWORD SourceMediaType
)
/*++
Routine Description:
This function will get the Source Media Type from the PNF for a given INF
file. If the INF does not have a PNF file then the API will fail.
Arguments:
InfFileName - Supplies the full path of the INF that we will get the source
media type from it's PNF.
SourceMediaType - Supplies a pointer that will be filled in the the source
media type from the PNF.
Return Value:
TRUE if the INF has a PNF and we are able to get a valid source media type
FALSE if their is no PNF for this INF.
Remarks:
--*/
{
PLOADED_INF Inf;
BOOL PnfWasUsed;
WIN32_FIND_DATA InfFileData;
UINT ErrorLineNumber;
BOOL bReturn = FALSE;
if (FileExists(InfName, &InfFileData)) {
if(LoadInfFile(InfName,
&InfFileData,
INF_STYLE_WIN4,
LDINF_FLAG_IGNORE_VOLATILE_DIRIDS,
NULL,
NULL,
NULL,
NULL,
NULL,
&Inf,
&ErrorLineNumber,
&PnfWasUsed) == NO_ERROR) {
if (PnfWasUsed) {
//
// We were able to open the INF with a PNF. Now get the
// InfSourceMediaType.
//
LockInf(Inf);
*SourceMediaType = Inf->InfSourceMediaType;
UnlockInf(Inf);
bReturn = TRUE;
}
FreeInfFile(Inf);
}
}
return bReturn;
}
//
// ANSI version
//
BOOL
WINAPI
SetupCopyOEMInfA(
IN PCSTR SourceInfFileName,
IN PCSTR OEMSourceMediaLocation, OPTIONAL
IN DWORD OEMSourceMediaType,
IN DWORD CopyStyle,
OUT PSTR DestinationInfFileName, OPTIONAL
IN DWORD DestinationInfFileNameSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PSTR *DestinationInfFileNameComponent OPTIONAL
)
{
PWSTR UnicodeSourceInfFileName, UnicodeOEMSourceMediaLocation, UnicodeFileNameComponent;
WCHAR UnicodeDestinationInfFileName[MAX_PATH];
PSTR AnsiDestinationInfFileName;
DWORD AnsiRequiredSize;
DWORD rc;
DWORD CharOffset, i;
PSTR p;
WCHAR SourceInfCatalogName[MAX_PATH];
WCHAR CatalogFilenameOnSystem[MAX_PATH];
BOOL DifferentOriginalName;
TCHAR OriginalInfName[MAX_PATH];
HINF hInf;
DWORD CodeSigningPolicy;
BOOL UseOriginalInfName;
PSETUP_LOG_CONTEXT LogContext = NULL;
PSP_ALTPLATFORM_INFO_V2 ValidationPlatform;
rc = pSetupCaptureAndConvertAnsiArg(SourceInfFileName,
&UnicodeSourceInfFileName
);
if(rc != NO_ERROR) {
SetLastError(rc);
return FALSE;
}
//
// Open the specified INF.
//
hInf = SetupOpenInfFile(UnicodeSourceInfFileName,
NULL,
INF_STYLE_OLDNT | INF_STYLE_WIN4,
NULL
);
if(hInf == INVALID_HANDLE_VALUE) {
rc = GetLastError();
MyFree(UnicodeSourceInfFileName);
SetLastError(rc);
return FALSE;
}
ValidationPlatform = NULL;
//
// GetCodeSigningPolicyForInf() presently can generate an unhandled
// exception, so guard the code below in try/except to protect ourselves...
//
try {
if(NO_ERROR != InheritLogContext(((PLOADED_INF)hInf)->LogContext,
&LogContext)) {
LogContext = NULL;
}
//
// Retrieve the CatalogFile entry (if present) from this INF's version
// section.
//
rc = pGetInfOriginalNameAndCatalogFile(
(PLOADED_INF)hInf,
NULL,
&DifferentOriginalName,
OriginalInfName,
SIZECHARS(OriginalInfName),
SourceInfCatalogName,
SIZECHARS(SourceInfCatalogName),
NULL // always native OS/arch (ver doesn't matter for CatalogFile=)
);
if(rc == NO_ERROR) {
CodeSigningPolicy = GetCodeSigningPolicyForInf(LogContext,
hInf,
&ValidationPlatform,
&UseOriginalInfName
);
if(UseOriginalInfName) {
//
// If we're looking at an exception INF, make sure it's under its
// original name.
//
if(DifferentOriginalName) {
rc = ERROR_INVALID_CLASS;
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If we hit an AV, then use invalid parameter error, otherwise, assume
// an inpage error when dealing with a mapped-in file (e.g., in the
// call to GetCodeSigningPolicyForInf).
//
rc = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
}
//
// We're done with the INF handle
//
SetupCloseInfFile(hInf);
if(rc == NO_ERROR) {
if(OEMSourceMediaLocation) {
rc = pSetupCaptureAndConvertAnsiArg(OEMSourceMediaLocation,
&UnicodeOEMSourceMediaLocation
);
} else {
UnicodeOEMSourceMediaLocation = NULL;
}
}
if(rc != NO_ERROR) {
MyFree(UnicodeSourceInfFileName);
if(ValidationPlatform) {
MyFree(ValidationPlatform);
}
SetLastError(rc);
return FALSE;
}
rc = GLE_FN_CALL(FALSE,
_SetupCopyOEMInf(
UnicodeSourceInfFileName,
UnicodeOEMSourceMediaLocation,
OEMSourceMediaType,
CopyStyle,
UnicodeDestinationInfFileName,
SIZECHARS(UnicodeDestinationInfFileName),
NULL,
&UnicodeFileNameComponent,
(DifferentOriginalName ? OriginalInfName
: pSetupGetFileTitle(UnicodeSourceInfFileName)),
(*SourceInfCatalogName ? SourceInfCatalogName : NULL),
NULL, // no HWND for UI!
NULL,
CodeSigningPolicy,
SCOI_NO_ERRLOG_IF_INF_ALREADY_PRESENT |
(UseOriginalInfName ? SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES : 0),
NULL,
ValidationPlatform,
NULL,
CatalogFilenameOnSystem,
LogContext, // either made-up or thread log context
NULL,
NULL)
);
if((rc == NO_ERROR) || (rc == ERROR_FILE_EXISTS)) {
if(DestinationInfFileName || RequiredSize) {
if(AnsiDestinationInfFileName = pSetupUnicodeToAnsi(UnicodeDestinationInfFileName)) {
AnsiRequiredSize = lstrlenA(AnsiDestinationInfFileName) + 1;
if(RequiredSize) {
try {
*RequiredSize = AnsiRequiredSize;
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
}
if(((rc == NO_ERROR) || (rc == ERROR_FILE_EXISTS))
&& DestinationInfFileName) {
if(AnsiRequiredSize <= DestinationInfFileNameSize) {
if(FAILED(StringCchCopyA(DestinationInfFileName,
DestinationInfFileNameSize,
AnsiDestinationInfFileName))) {
rc = ERROR_INVALID_PARAMETER;
} else if(DestinationInfFileNameComponent) {
//
// We need to figure out where the extension is
// in the converted string. To be DBCS safe we will
// count characters forward to find it.
//
CharOffset = (DWORD)(UnicodeFileNameComponent - UnicodeDestinationInfFileName);
p = DestinationInfFileName;
for(i = 0; i < CharOffset; i++) {
p = CharNextA(p);
}
try {
*DestinationInfFileNameComponent = p;
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
}
} else {
rc = ERROR_INSUFFICIENT_BUFFER;
}
}
MyFree(AnsiDestinationInfFileName);
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
//
// It would be dumb to request the filename part of the destination
// INF path when the path itself wasn't requested, but let's make
// sure we set this to NULL if that happens.
//
if(DestinationInfFileNameComponent) {
try {
*DestinationInfFileNameComponent = NULL;
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
}
}
}
MyFree(UnicodeSourceInfFileName);
if(UnicodeOEMSourceMediaLocation) {
MyFree(UnicodeOEMSourceMediaLocation);
}
if(ValidationPlatform) {
MyFree(ValidationPlatform);
}
DeleteLogContext(LogContext);
SetLastError(rc);
return (rc == NO_ERROR);
}
BOOL
WINAPI
SetupCopyOEMInf(
IN PCTSTR SourceInfFileName,
IN PCTSTR OEMSourceMediaLocation, OPTIONAL
IN DWORD OEMSourceMediaType,
IN DWORD CopyStyle,
OUT PTSTR DestinationInfFileName, OPTIONAL
IN DWORD DestinationInfFileNameSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PTSTR *DestinationInfFileNameComponent OPTIONAL
)
/*++
Routine Description:
This API copies an INF into %windir%\Inf, giving it a unique name if it
doesn't already exist there. We determine whether the INF already exists
in the INF directory as follows:
1. All INFs of the form "OEM*.INF" are enumerated, and any that have the
same file size as that of our INF are binary-compared with it.
2. We also look for the INF using its source filename. If a file of the
same name exists, and is the same size as that of our INF, we
binary-compare the two to see if they are identical.
If the INF already exists (by either of the two criteria described above),
then we will further check to see if the INF specifies a CatalogFile= entry
in its version section. If so, we will see if that catalog is already
installed (using the INF's %windir%\Inf primary filename with a ".CAT"
extension). If there is a catalog installed, but it isn't the same as the
catalog associated with the source INF, then we won't consider that INF to
be a match, and we'll keep enumerating looking for a match. This means
it's possible to have multiple identical INFs contained in %windir%\Inf,
each having its own unique catalog. (In general, this shouldn't happen,
since driver package updates should also update the DriverVer information
in the INF, thus you wouldn't get identical INFs in the first place.) If
we don't find an existing match (i.e., both INF and CAT), then we'll
install the INF and CAT under a new, unique name.
OEM INFs that don't specify a CatalogFile= entry are considered invalid
w.r.t. digital signature verification.
In cases where the INF must be copied to %windir%\Inf (i.e., it wasn't
already there), we'll report any digital signature verification failures
(based on applicable policy).
If we decide that the INF/CAT files already exist, then we will use that
name, and our replacement behavior is based on the caller-specified
CopyStyle flags. (NOTE: Replacement behavior here refers solely to the
source media information stored in the PNF. We will not blow away existing
INFs/PNFs/CATs under any circumstances.)
Arguments:
SourceInfFileName - Specifies the full path to the INF to be copied.
OEMSourceMediaLocation - Specifies source location information to be stored
in the precompiled INF (.PNF), thus allowing the system to go to the
correct location for files when installing from this INF. This location
information is specific to the source media type specified.
OEMSourceMediaType - Specifies the type of source media that the location
information references. May be one of the following values:
SPOST_NONE - No source media information should be stored in the PNF
file. (OEMSourceMediaLocation is ignored in this case.)
SPOST_PATH - OEMSourceMediaLocation contains a path to the source media.
For example, if the media is on a floppy, this path might
be "A:\". If OEMSourceMediaLocation is NULL, then the path
is assumed to be the path where the INF is located (unless
the INF has a corresponding PNF in that location, in which
case that PNF's source media information will be
transferred to the destination PNF).
SPOST_URL - OEMSourceMediaLocation contains a URL indicating the
internet location where the INF/driver files were retrieved
from. If OEMSourceMediaLocation is NULL, then it is
assumed that the default Code Download Manager location was
used.
CopyStyle - Specifies flags that control how the INF is copied into the INF
directory. May be a combination of the following flags (all other
SP_COPY_* flags are ignored).
SP_COPY_DELETESOURCE - Delete source file on successful copy.
SP_COPY_REPLACEONLY - Copy only if this file already exists in the INF
directory. This could be used to update the
source location information for an existing INF.
SP_COPY_NOOVERWRITE - Copy only if this files doesn't already exist in
the INF directory. If the INF _does_ already
exist, this API will fail with GetLastError
returning ERROR_FILE_EXISTS. In this case, the
destination INF file information output buffers
will be filled in for the existing INF's filename.
SP_COPY_OEMINF_CATALOG_ONLY - Don't copy the INF into %windir%\Inf--just
install its corresponding catalog file.
If this flag is specified, then the
destination filename information will only
be filled out upon successful return if
the INF is already in the Inf directory.
SP_COPY_OEM_F6_INF - The Inf was installed by the user during textmode
setup using F6.
DestinationInfFileName - Optionally, supplies a character buffer that
receives the name that the INF was assigned when it was copied into the
INF directory. If the SP_COPY_NOOVERWRITE flag is specified, and the
API fails with ERROR_FILE_EXISTS, then this buffer will contain the name
of the existing INF.
If the SP_COPY_OEMINF_CATALOG_ONLY flag was specified, then this buffer
will only be filled in with a destination INF filename if the INF is
already in the INF directory. Otherwise, this buffer will be set to the
empty string.
DestinationInfFileNameSize - Specifies the size, in characters, of the
DestinationInfFileName buffer, or zero if the buffer is not specified.
If DestinationInfFileName is specified, and this buffer size is less
than the size required to return the destination INF name (including
full path), then this API will fail, and GetLastError will return
ERROR_INSUFFICIENT_BUFFER.
RequiredSize - Optionally, supplies the address of a variable that receives
the size (in characters) required to store the destination INF file name
(including terminating NULL.
If the SP_COPY_OEMINF_CATALOG_ONLY flag was specified, then this
variable will only receive a string length if the INF is already in the
INF directory. Otherwise, this variable will be set to zero.
DestinationInfFileNameComponent - Optionally, supplies the address of a
character pointer that is set, upon successful return (or
ERROR_FILE_EXISTS), to point to the beginning of the filename component
of the path stored in DestinationInfFileName.
If the SP_COPY_OEMINF_CATALOG_ONLY flag was specified, then the
DestinationInfFileName may be an empty string (see above). In that case
this character pointer will be set to NULL upon successful return.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
--*/
{
TCHAR SourceInfCatalogName[MAX_PATH];
TCHAR CatalogFilenameOnSystem[MAX_PATH];
DWORD rc;
BOOL DifferentOriginalName;
TCHAR OriginalInfName[MAX_PATH];
HINF hInf;
DWORD CodeSigningPolicy;
BOOL UseOriginalInfName;
PSETUP_LOG_CONTEXT LogContext = NULL;
PSP_ALTPLATFORM_INFO_V2 ValidationPlatform;
//
// Open the specified INF.
//
hInf = SetupOpenInfFile(SourceInfFileName,
NULL,
INF_STYLE_OLDNT | INF_STYLE_WIN4,
NULL
);
if(hInf == INVALID_HANDLE_VALUE) {
//
// last error already set.
//
return FALSE;
}
ValidationPlatform = NULL;
//
// GetCodeSigningPolicyForInf() presently can generate an unhandled
// exception, so guard the code below in try/except to protect ourselves...
//
try {
if(NO_ERROR != InheritLogContext(((PLOADED_INF)hInf)->LogContext,
&LogContext)) {
LogContext = NULL;
}
//
// Retrieve the CatalogFile entry (if present) from this INF's version
// section.
//
rc = pGetInfOriginalNameAndCatalogFile(
NULL,
SourceInfFileName,
&DifferentOriginalName,
OriginalInfName,
SIZECHARS(OriginalInfName),
SourceInfCatalogName,
SIZECHARS(SourceInfCatalogName),
NULL // always native OS/arch (ver doesn't matter for CatalogFile=)
);
if(rc == NO_ERROR) {
CodeSigningPolicy = GetCodeSigningPolicyForInf(LogContext,
hInf,
&ValidationPlatform,
&UseOriginalInfName
);
if(UseOriginalInfName) {
//
// If we're looking at an exception INF, make sure it's under its
// original name.
//
if(DifferentOriginalName) {
rc = ERROR_INVALID_CLASS;
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If we hit an AV, then use invalid parameter error, otherwise, assume
// an inpage error when dealing with a mapped-in file (e.g., in the
// call to GetCodeSigningPolicyForInf).
//
rc = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
}
//
// We're done with the INF handle
//
SetupCloseInfFile(hInf);
if(rc != NO_ERROR) {
goto final;
}
rc = GLE_FN_CALL(FALSE,
_SetupCopyOEMInf(
SourceInfFileName,
OEMSourceMediaLocation,
OEMSourceMediaType,
CopyStyle,
DestinationInfFileName,
DestinationInfFileNameSize,
RequiredSize,
DestinationInfFileNameComponent,
(DifferentOriginalName ? OriginalInfName
: pSetupGetFileTitle(SourceInfFileName)),
(*SourceInfCatalogName ? SourceInfCatalogName : NULL),
NULL, // no HWND for UI!
NULL,
CodeSigningPolicy,
SCOI_NO_ERRLOG_IF_INF_ALREADY_PRESENT |
(UseOriginalInfName ? SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES : 0),
NULL,
ValidationPlatform,
NULL,
CatalogFilenameOnSystem,
LogContext, // either made-up or thread log context
NULL,
NULL)
);
final:
if(ValidationPlatform) {
MyFree(ValidationPlatform);
}
DeleteLogContext(LogContext);
SetLastError(rc);
return (rc == NO_ERROR);
}
BOOL
_SetupCopyOEMInf(
IN PCTSTR SourceInfFileName,
IN PCTSTR OEMSourceMediaLocation, OPTIONAL
IN DWORD OEMSourceMediaType,
IN DWORD CopyStyle,
OUT PTSTR DestinationInfFileName, OPTIONAL
IN DWORD DestinationInfFileNameSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PTSTR *DestinationInfFileNameComponent, OPTIONAL
IN PCTSTR SourceInfOriginalName,
IN PCTSTR SourceInfCatalogName, OPTIONAL
IN HWND Owner,
IN PCTSTR DeviceDesc, OPTIONAL
IN DWORD DriverSigningPolicy,
IN DWORD Flags,
IN PCTSTR AltCatalogFile, OPTIONAL
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
OUT PDWORD DriverSigningError, OPTIONAL
OUT PTSTR CatalogFilenameOnSystem,
IN PSETUP_LOG_CONTEXT LogContext,
IN OUT PVERIFY_CONTEXT VerifyContext, OPTIONAL
OUT HANDLE *hWVTStateData OPTIONAL
)
/*++
Routine Description:
(See SetupCopyOEMInf)
Arguments:
(See SetupCopyOEMInf)
SourceInfOriginalName - supplies the simple filename (no path) that this INF
originally had. This will typically be the same as its current name,
except in cases where there's a valid PNF for that INF, and the PNF
specifies a different original name.
SourceInfCatalogName - Optionally, supplies the simple filename of the
catalog file specified by the OEM INF via a CatalogFile= entry in its
[Version] section. If this parameter is not specified, then the INF
doesn't specify an associated catalog. (NOTE: One may still be used if
the AltCatalogFile parameter is specified.
Owner - supplies window handle to be used for any UI related to digital
signature verification failures.
DeviceDesc - Optionally, supplies the device description to be used in the
digital signature verification error dialogs that may be popped up.
DriverSigningPolicy - supplies the driver signing policy currently in
effect. Used when determining whether we can validate via an
Authenticode catalog, and to control behavior of
_HandleFailedVerification, if necessary.
Flags - supplies flags which alter the behavior of the routine. May be a
combination of the following values:
SCOI_NO_UI_ON_SIGFAIL - indicates whether user should be prompted (per
DriverSigningPolicy) if a digital signature
failure is encountered. Used when calling
_HandleFailedVerification, if necessary.
SCOI_NO_ERRLOG_ON_MISSING_CATALOG - if there's a signature verification
failure due to the INF lacking a
CatalogFile= entry, then that error
will be ignored if this flag is set
(no UI will be given, and no log
entry will be generated).
SCOI_NO_ERRLOG_IF_INF_ALREADY_PRESENT - If we discover that the INF
already exists in %windir%\Inf,
then don't popup any UI and
don't even generate an error
log entry.
SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES - Install the INF and CAT under
their original (current) names
(i.e., don't generate a unique
oem<n>.inf/cat name). Used only
for exception INFs.
SCOI_ABORT_IF_UNSIGNED - If the INF is unsigned (and user wants to copy
it anyway, or policy is Ignore), then _don't_
copy the INF and instead return the error
ERROR_SET_SYSTEM_RESTORE_POINT. This gives
the caller a chance to set a system restore
point prior to installing the unsigned package.
SCOI_TRY_UPDATE_PNF - If an existing PNF cannot be updated, don't
consider it fatal.
AltCatalogFile - Optionally, supplies the name of a catalog file to be
installed and used for verification of the INF in cases where the INF
doesn't specify a CatalogFile= entry (i.e., when the
SourceInfCatalogName parameter is not specified.
AltPlatformInfo - Optionally, supplies alternate platform information to be
used in digital signature verification instead of the default (native)
platform info.
DriverSigningError - Optionally, supplies the address of a variable that
receives the error encountered when attempting to verify the digital
signature of either the INF or associated catalog. If no digital
signature problems were encountered, this is set to NO_ERROR. (Note
that this value can come back as NO_ERROR, yet _SetupCopyOEMInf still
failed for some other reason).
If the INF was successfully validated via Authenticode policy, and the
publisher was in the TrustedPublisher store, then this value will be
set to ERROR_AUTHENTICODE_TRUSTED_PUBLISHER upon return.
If the INF was successfully validated via Authenticode policy, and the
publisher was not in the TrustedPublisher store (hence we must prompt
the user to establish their trust of the publisher), then this value
will be set to ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED upon return.
CatalogFilenameOnSystem - Receives the fully-qualified path of the catalog
file within the catalog store where this INF's catalog file was
installed to. This buffer should be at least MAX_PATH bytes (ANSI
version) or chars (Unicode version).
LogContext - supplies a LogContext to be passed to GetNewInfName.
VerifyContext - optionally, supplies the address of a structure that caches
various verification context handles. These handles may be NULL (if
not previously acquired, and they may be filled in upon return (in
either success or failure) if they were acquired during the processing
of this verification request. It is the caller's responsibility to
free these various context handles when they are no longer needed by
calling pSetupFreeVerifyContextMembers.
hWVTStateData - if supplied, this parameter points to a buffer that
receives a handle to WinVerifyTrust state data. This handle will be
returned only when validation was successfully performed using
Authenticode policy. This handle may be used, for example, to retrieve
signer info when prompting the user about whether they trust the
publisher. (The status code returned in the DriverSigningError output
parameter will indicate whether or not this is necessary.)
This parameter should only be supplied if the
DRIVERSIGN_ALLOW_AUTHENTICODE bit is set in the caller-specified
DriverSigningPolicy. If the routine fails, then this handle will be
set to NULL.
It is the caller's responsibility to close this handle when they're
finished with it by calling pSetupCloseWVTStateData().
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
--*/
{
TCHAR NewName[MAX_PATH];
DWORD TempRequiredSize, Err;
NEWINF_COPYTYPE CopyNeeded;
WIN32_FIND_DATA FindData;
PLOADED_INF PrecompiledNewInf;
UINT ErrorLineNumber;
TCHAR TempOemSourceMediaLocation[MAX_PATH];
PTSTR TempCharPtr;
BOOL AlternateCatInstalled;
DWORD TempDriverSigningError;
DWORD DefaultSourceMediaType;
HINF hInf;
HANDLE LocalhWVTStateData = NULL;
if(DriverSigningError) {
*DriverSigningError = NO_ERROR;
}
//
// Initialize the output handle for WinVerifyTrust state data.
//
if(hWVTStateData) {
*hWVTStateData = NULL;
}
//
// If the DestinationInfFileName buffer is NULL, the size had better be
// zero. Also, make sure the caller passed us a valid OEMSourceMediaType.
//
if((!DestinationInfFileName && DestinationInfFileNameSize) ||
(OEMSourceMediaType >= SPOST_MAX))
{
MYASSERT(!DriverSigningError);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// If we're installing an exception INF, it's illegal to request that we
// not copy the INF.
//
if((Flags & SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES) &&
(CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
Err = NO_ERROR;
CopyNeeded = NewInfCopyNo;
AlternateCatInstalled = FALSE;
TempCharPtr = NULL;
hInf = INVALID_HANDLE_VALUE;
try {
//
// Check to see if the INF is already in the %windir%\Inf directory,
// and create a uniquely-named, zero-length placeholder file if it
// isn't.
//
Err = GetNewInfName(Owner,
SourceInfFileName,
SourceInfOriginalName,
SourceInfCatalogName,
NewName,
SIZECHARS(NewName),
&TempRequiredSize,
&CopyNeeded,
CopyStyle & SP_COPY_REPLACEONLY,
DeviceDesc,
DriverSigningPolicy,
Flags,
AltCatalogFile,
AltPlatformInfo,
&TempDriverSigningError,
CatalogFilenameOnSystem,
LogContext,
VerifyContext,
&LocalhWVTStateData
);
if((Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
MYASSERT(TempDriverSigningError == NO_ERROR);
MYASSERT(LocalhWVTStateData);
if(hWVTStateData) {
*hWVTStateData = LocalhWVTStateData;
} else {
pSetupCloseWVTStateData(LocalhWVTStateData);
}
//
// We want to return Authenticode signature status to the caller
// via the DriverSigningError output parameter. We can't use a
// return of FALSE (with GetLastError), since we also need to
// report ERROR_FILE_EXISTS (which isn't really an error, and needs
// to be communicated in tandem with the Authenticode status).
//
TempDriverSigningError = Err;
Err = NO_ERROR;
} else {
MYASSERT(!LocalhWVTStateData);
}
if(DriverSigningError) {
*DriverSigningError = TempDriverSigningError;
}
if(Err != NO_ERROR) {
//
// Value of CopyNeeded parameter is undefined upon error, but we do
// know that no clean-up will be necessary if GetNewInfName fails.
//
CopyNeeded = NewInfCopyNo;
//
// If we were only trying to get the catalog installed, and the
// failure was for some reason _other_ than a digital signature
// verification failure (e.g., we couldn't create the placeholder
// INF in %windir%\Inf), then we want to treat this as if it were
// a signature verification failure. Since we aren't trying to
// install the full INF, this implies that we aren't doing a device
// install, thus the policy is going to be non-driver-signing
// policy. This has the default of "Ignore", which is fortunate,
// since the dialog complains about an unsigned driver package when
// in fact we don't really know whether or not the package is
// unsigned--all we know is that we couldn't install its catalog,
// even if it had one.
//
// Note: if the user clicks the "More Info..." button, we will tell
// them that the catalog installation failed, and what a possible
// cause might be (e.g., no write access to Windows directory).
//
if((CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY) &&
(TempDriverSigningError == NO_ERROR)) {
if(SourceInfCatalogName) {
//
// Typical case (INF specified a catalog), give the user
// some indication (via codesigning popup based on policy)
// about what went wrong...
//
if(_HandleFailedVerification(
Owner,
SetupapiVerifyCatalogInstallProblem,
SourceInfCatalogName,
DeviceDesc,
DriverSigningPolicy,
(Flags & SCOI_NO_UI_ON_SIGFAIL),
Err,
LogContext,
NULL,
NULL,
NULL)) {
TempDriverSigningError = Err;
if(DriverSigningError) {
*DriverSigningError = TempDriverSigningError;
}
//
// The user wants to proceed with the unsigned
// installation (or policy is Ignore, so they weren't
// even informed). If the caller wants a chance to set
// a system restore point prior to doing any unsigned
// installations, then we abort now with a "special"
// error code that tells them what to do...
//
if(Flags & SCOI_ABORT_IF_UNSIGNED) {
Err = ERROR_SET_SYSTEM_RESTORE_POINT;
} else {
//
// We should consider the operation a success, even
// though we couldn't install the catalog.
//
Err = NO_ERROR;
}
}
} else {
//
// Unlikely scenario (INF didn't specify a catalog, yet we
// were called with SP_COPY_OEMINF_CATALOG_ONLY???), just
// ignore the issue, generate a setupapi.log entry, and
// proceed...
//
WriteLogEntry(LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_FAILED_INF_INSTALL,
NULL,
SourceInfFileName
);
WriteLogError(LogContext,
SETUP_LOG_ERROR,
Err
);
TempDriverSigningError = Err;
if(DriverSigningError) {
*DriverSigningError = TempDriverSigningError;
}
Err = NO_ERROR;
}
if(Err == NO_ERROR) {
//
// In this case, we don't have a filename to return to the
// caller.
//
if(RequiredSize) {
*RequiredSize = 0;
}
if(DestinationInfFileName && DestinationInfFileNameSize) {
*DestinationInfFileName = TEXT('\0');
}
if(DestinationInfFileNameComponent) {
*DestinationInfFileNameComponent = NULL;
}
}
}
leave;
}
//
// OK, we have a filename to copy to, now copy the file (if necessary),
// unless the caller doesn't want to overwrite the existing file.
//
if(CopyNeeded != NewInfCopyNo) {
//
// We either created a new zero-length placeholder file ourselves,
// or we found an existing placeholder that we could use.
//
// Copy our INF over the top of this placeholder file, unless the
// caller specified the SP_COPY_OEMINF_CATALOG_ONLY flag, in which
// case we want to leave the zero-length file as-is (and mark it
// hidden), so that no other OEM INFs can subsequently get this name
// (hence causing namespace collisions for the catalog files).
//
if(!(CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)) {
SetFileAttributes(NewName, FILE_ATTRIBUTE_NORMAL);
Err = GLE_FN_CALL(FALSE,
InfCacheAwareCopyFile(SourceInfFileName, NewName)
);
if(Err != NO_ERROR) {
//
// Hopefully, this didn't return ERROR_FILE_EXISTS, as that
// return code means something special for SetupCopyOEMInf.
//
MYASSERT(Err != ERROR_FILE_EXISTS);
if(Err == ERROR_FILE_EXISTS) {
Err = ERROR_INVALID_DATA; // just pick something else!
}
leave;
}
} else {
//
// If we didn't encounter a driver signing error, or if the
// zero-length file was already present, then just make sure
// it's marked as hidden.
//
if((TempDriverSigningError == NO_ERROR) ||
(TempDriverSigningError == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(TempDriverSigningError == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) ||
(CopyNeeded == NewInfCopyZeroLength)) {
//
// Mark the zero-length file as hidden.
//
SetFileAttributes(NewName, FILE_ATTRIBUTE_HIDDEN);
} else {
//
// Delete the newly-created zero-length placeholder INF,
// because it serves no purpose.
//
SetFileAttributes(NewName, FILE_ATTRIBUTE_NORMAL);
DeleteFile(NewName);
}
//
// Since we only have a zero-length placeholder (or possibly,
// no placeholder at all if catalog installation failed), we
// shouldn't return a filename to the caller.
//
if(RequiredSize) {
*RequiredSize = 0;
}
if(DestinationInfFileName && DestinationInfFileNameSize) {
*DestinationInfFileName = TEXT('\0');
}
if(DestinationInfFileNameComponent) {
*DestinationInfFileNameComponent = NULL;
}
//
// We're done!
//
leave;
}
} else {
//
// If the original INF name and the new INF name are different,
// then we know this wasn't an alternate catalog-only install.
//
AlternateCatInstalled = !lstrcmpi(SourceInfFileName, NewName);
if((CopyStyle & SP_COPY_NOOVERWRITE) && !AlternateCatInstalled) {
//
// OK, the INF already exists in the INF directory, and the
// caller has specified that they _do not_ want to wipe out the
// existing PNF. We may need to return back the name of the
// existing INF if they requested it, however.
//
// NOTE: It's OK to return the WinVerifyTrust state data handle
// in this case, since this isn't "really" an error (and both
// pieces of info need to be returned to caller).
//
Err = ERROR_FILE_EXISTS;
}
}
//
// If we get to here, then the INF exists in %windir%\Inf, and we
// either have no error, or ERROR_FILE_EXISTS. Next, we need to store
// the name of the INF in the caller's buffer (if supplied).
//
if(RequiredSize) {
*RequiredSize = TempRequiredSize;
}
if(DestinationInfFileName) {
if(DestinationInfFileNameSize >= TempRequiredSize) {
MYVERIFY(SUCCEEDED(StringCchCopy(DestinationInfFileName,
DestinationInfFileNameSize,
NewName)));
//
// If requested by the caller, return a pointer to the filename
// part of this path.
//
if(DestinationInfFileNameComponent) {
*DestinationInfFileNameComponent = (PTSTR)pSetupGetFileTitle(DestinationInfFileName);
}
} else {
Err = ERROR_INSUFFICIENT_BUFFER;
}
} else {
//
// Nobody should be requesting a pointer to the filename component
// of the path when they don't supply us a buffer to store the full
// path. If anyone does do this, make sure we set the pointer to
// NULL.
//
if(DestinationInfFileNameComponent) {
*DestinationInfFileNameComponent = NULL;
}
}
if((Err != NO_ERROR) || AlternateCatInstalled) {
leave;
}
//
// If the code calling this API claims that this INF is from the
// Internet we first want to check if there is a PNF for this INF and
// that the PNF agrees that this INF is from the Internet. If the PNF
// says the INF is from a location other than the Internet then we will
// change the OEMSourceMediaType to this other media type. We will
// also set the OEMSourceMediaLocation to NULL. This has the desired
// affect of us falling through the next if statement which will read
// the SourceMediaLocation from the PNF instead of the current INF
// location. If there is no PNF for the given INF then we just leave
// OEMSourceMediaType alone.
//
if(OEMSourceMediaType == SPOST_URL) {
if(pSetupGetSourceMediaTypeFromPnf(SourceInfFileName, &DefaultSourceMediaType) &&
(DefaultSourceMediaType != SPOST_URL)) {
OEMSourceMediaType = DefaultSourceMediaType;
OEMSourceMediaLocation = NULL;
}
}
if(!OEMSourceMediaLocation && (OEMSourceMediaType == SPOST_PATH)) {
//
// The caller wants to store the OEM source path, but they didn't
// provide us with one to use. Thus, we'll use the path where the
// INF existed (unless there's a valid PNF there, in which case
// we'll use its SPOST_PATH information, if it has any).
// pSetupGetDefaultSourcePath() does just what we want.
//
Err = GLE_FN_CALL(INVALID_HANDLE_VALUE,
hInf = SetupOpenInfFile(SourceInfFileName,
NULL,
INF_STYLE_WIN4 | INF_STYLE_OLDNT,
NULL)
);
if(Err != NO_ERROR) {
//
// Hopefully, this didn't return ERROR_FILE_EXISTS, as that
// return code means something special for SetupCopyOEMInf.
//
MYASSERT(Err != ERROR_FILE_EXISTS);
if(Err == ERROR_FILE_EXISTS) {
Err = ERROR_INVALID_DATA; // just pick something else!
}
leave;
}
Err = GLE_FN_CALL(NULL,
TempCharPtr = pSetupGetDefaultSourcePath(
hInf,
SRCPATH_USEINFLOCATION,
&DefaultSourceMediaType)
);
//
// We don't need the INF handle anymore.
//
SetupCloseInfFile(hInf);
hInf = INVALID_HANDLE_VALUE;
MYASSERT(DefaultSourceMediaType == SPOST_PATH);
if(Err != NO_ERROR) {
//
// Hopefully, this didn't return ERROR_FILE_EXISTS, as that
// return code means something special for SetupCopyOEMInf.
//
MYASSERT(Err != ERROR_FILE_EXISTS);
if(Err == ERROR_FILE_EXISTS) {
Err = ERROR_INVALID_DATA; // just pick something else!
}
leave;
}
MYVERIFY(SUCCEEDED(StringCchCopy(TempOemSourceMediaLocation,
SIZECHARS(TempOemSourceMediaLocation),
TempCharPtr)));
OEMSourceMediaLocation = TempOemSourceMediaLocation;
//
// We no longer need the buffer allocated for us by
// pSetupGetDefaultSourcePath.
//
MyFree(TempCharPtr);
//
// Reset this pointer so we won't try to free it should we
// subsequently encounter an exception.
//
TempCharPtr = NULL;
}
Err = GLE_FN_CALL(FALSE, FileExists(NewName, &FindData));
if(Err != NO_ERROR) {
//
// Hopefully, this didn't return ERROR_FILE_EXISTS, as that
// return code means something special for SetupCopyOEMInf.
//
MYASSERT(Err != ERROR_FILE_EXISTS);
if(Err == ERROR_FILE_EXISTS) {
Err = ERROR_INVALID_DATA; // just pick something else!
}
leave;
}
Err = LoadInfFile(NewName,
&FindData,
INF_STYLE_WIN4 | INF_STYLE_OLDNT,
LDINF_FLAG_ALWAYS_TRY_PNF
| LDINF_FLAG_REGENERATE_PNF
| ((Flags & SCOI_TRY_UPDATE_PNF) ? LDINF_FLAG_ALLOW_PNF_SHARING_LOCK : 0)
| ((OEMSourceMediaType == SPOST_URL) ? LDINF_FLAG_SRCPATH_IS_URL : 0)
| ((CopyStyle & SP_COPY_OEM_F6_INF) ? LDINF_FLAG_OEM_F6_INF : 0),
NULL,
(OEMSourceMediaType == SPOST_NONE) ? NULL : OEMSourceMediaLocation,
(lstrcmpi(SourceInfOriginalName, pSetupGetFileTitle(NewName))
? SourceInfOriginalName
: NULL),
NULL,
LogContext,
&PrecompiledNewInf,
&ErrorLineNumber,
NULL
);
if(Err != NO_ERROR) {
//
// Hopefully, this didn't return ERROR_FILE_EXISTS, as that
// return code means something special for SetupCopyOEMInf.
//
MYASSERT(Err != ERROR_FILE_EXISTS);
if(Err == ERROR_FILE_EXISTS) {
Err = ERROR_INVALID_DATA; // just pick something else!
}
leave;
}
//
// The INF was successfully precompiled.
//
FreeInfFile(PrecompiledNewInf);
//
// Finally, delete the source INF file if the caller requested it.
//
if(CopyStyle & SP_COPY_DELETESOURCE) {
DeleteFile(SourceInfFileName);
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
MYASSERT(Err != ERROR_FILE_EXISTS);
if(TempCharPtr) {
MyFree(TempCharPtr);
}
if(hInf != INVALID_HANDLE_VALUE) {
SetupCloseInfFile(hInf);
}
}
if(Err != NO_ERROR) {
//
// For any error other than ERROR_FILE_EXISTS (which doesn't really
// indicate a problem), we need to ensure that no WinVerifyTrust state
// data is being returned to the caller.
//
if((Err != ERROR_FILE_EXISTS) && hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
}
if(CopyNeeded == NewInfCopyYes) {
//
// We encountered an error, after GetNewInfName created a new,
// unique INF for us. Delete it now.
//
// (Note: we don't do clean-up when GetNewInfName found an
// existing, zero-length file for us to use, even if we may have
// subsequently copied the 'real' INF over the top of it. Since we
// believed the existing file to be a duplicate of our OEM INF,
// this should be OK--the worst that's going to happen is that an
// INF that was once zero-length is now a full-blown INF after
// SetupCopyOEMInf encountered an error.)
//
pSetupUninstallOEMInf(NewName, LogContext, SUOI_FORCEDELETE, NULL);
//
// NTRAID#256075-2000/08/18-JamieHun Handle Rollback of INF/PNF/CAT files
//
// If we were installing an exception INF and encountered an error,
// we may have already blown away a previous INF/CAT. In the
// future, we might want to look into doing a backup on the old
// INF/CAT before installing the new INF/CAT over the top of them.
//
}
}
SetLastError(Err);
return (Err == NO_ERROR);
}
//
// ANSI version
//
BOOL
WINAPI
SetupUninstallOEMInfA(
IN PCSTR InfFileName,
IN DWORD Flags,
IN PVOID Reserved
)
{
DWORD Err;
PCWSTR UnicodeInfFileName;
BOOL b;
Err = pSetupCaptureAndConvertAnsiArg(InfFileName, &UnicodeInfFileName);
if(Err != NO_ERROR) {
SetLastError(Err);
return FALSE;
}
b = SetupUninstallOEMInfW(UnicodeInfFileName, Flags, Reserved);
Err = GetLastError();
MyFree(UnicodeInfFileName);
SetLastError(Err);
return b;
}
BOOL
WINAPI
SetupUninstallOEMInf(
IN PCTSTR InfFileName,
IN DWORD Flags,
IN PVOID Reserved
)
/*++
Routine Description:
This API uninstalls the specified INF file, and deletes the associated
.PNF and .CAT files, if they exist.
The default behavior of this API is to first verify that there are no
other devices, live or phantom, that are using this INF. If another
device is using this INF then it will not be uninstalled. This behavior
can be overwritten by the SUOI_FORCEDELETE flag.
Arguments:
InfFileName - Supplies the simple filename (no path) of the INF to be
uninstalled.
Flags - Supplies flags that alter the behavior of this API.
SUOI_FORCEDELETE - forces the INF, PNF, and CAT to be uninstalled, even
if another device is using this INF.
Reserved - Reserved for future use. Must be NULL.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
--*/
{
DWORD Err;
TCHAR InfFullPath[MAX_PATH];
if(Flags & ~(SUOI_FORCEDELETE)) {
SetLastError(ERROR_INVALID_FLAGS);
return FALSE;
}
if(Reserved) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
try {
//
// INF filename must not contain path
//
if(InfFileName != pSetupGetFileTitle(InfFileName)) {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// Look for specified INF in %windir%\Inf...
//
if(MYVERIFY(SUCCEEDED(StringCchCopy(InfFullPath,
SIZECHARS(InfFullPath),
InfDirectory)))) {
MYVERIFY(pSetupConcatenatePaths(InfFullPath,
InfFileName,
SIZECHARS(InfFullPath),
NULL
));
}
if(!FileExists(InfFullPath, NULL)) {
Err = GetLastError();
goto clean0;
}
pSetupUninstallOEMInf(InfFullPath, NULL, Flags, &Err);
clean0:
; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiRegisterCoDeviceInstallers(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_REGISTER_COINSTALLERS
This routine will install any (device-specific) co-installers specified
in an [<InstallSec>.CoInstallers] section, where <InstallSec> is the install
section name for the selected driver node, potentially decorated with
an OS/architecture-specific extension (e.g., "InstallSec.NTAmd64.CoInstallers").
AddReg entries listed in a CoInstallers section use the device's driver key
for their HKR. To register a device-specific co-installer, a REG_MULTI_SZ entry
titled "CoInstallers32" must be written to the driver key. Each entry in this list
has the following format:
dll[,procname]
where dll is the name of the module to load and procname is an optional entry
point name. If procname is not specified, then the entry point name
"CoDeviceInstall" will be used.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set containing
a device information element for whom co-installers are to be registered.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for whom
co-installers are to be registered.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
If no driver is selected (i.e., this is a null-driver installation), then
this routine does nothing.
--*/
{
return _SetupDiRegisterCoDeviceInstallers(DeviceInfoSet,
DeviceInfoData,
TRUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE
);
}
BOOL
_SetupDiRegisterCoDeviceInstallers(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall,
IN HINF hDeviceInf, OPTIONAL
IN HSPFILEQ UserFileQ OPTIONAL
)
/*++
Routine Description:
Worker routine for both SetupDiRegisterCoDeviceInstallers and SetupDiInstallDriverFiles.
See the description of SetupDiRegisterCoDeviceInstallers for more information.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set containing
a device information element for whom co-installers are to be registered.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for whom
co-installers are to be registered.
DoFullInstall - If TRUE (non-zero), then the co-installers are completely registered,
otherwise, only files are copied.
hDeviceInf - Optionally, supplies a handle to the INF for which installation
is being performed. If this handle is not supplied, the INF specified in
the selected driver node will be opened. If this handle is not supplied,
this parameter must be set to INVALID_HANDLE_VALUE.
UserFileQ - Optionally, supplies a file queue where file operations should be added.
If this handle is not supplied, then the queue associated with this devinfo
element will be used (if the DI_NOVCP flag is set), or one will be automatically
generated and committed. If this handle is not supplied, this parameter must
be set to INVALID_HANDLE_VALUE.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
During GUI-mode setup on Windows NT, quiet-install behavior is always
employed in the absence of a user-supplied file queue, regardless of
whether the device information element has the DI_QUIETINSTALL flag set.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
DWORD Err, ScanQueueResult;
PDEVINFO_ELEM DevInfoElem;
HWND hwndParent;
BOOL CloseUserFileQ;
PTSTR szInfFileName, szInfSectionName;
TCHAR InfSectionWithExt[MAX_SECT_NAME_LEN];
DWORD InfSectionWithExtLength;
BOOL FreeMsgHandlerContext;
PSP_FILE_CALLBACK MsgHandler;
PVOID MsgHandlerContext;
BOOL MsgHandlerIsNativeCharWidth, DeleteDrvRegKey;
HKEY hkDrv;
BOOL CloseInfHandle;
BOOL DoFileCopying;
INT FileQueueNeedsReboot;
PSETUP_LOG_CONTEXT LogContext = NULL;
DWORD slot_section = 0;
BOOL NoProgressUI;
TCHAR szNewName[MAX_PATH];
BOOL OemInfFileToCopy = FALSE;
//
// A device information element must be specified.
//
if(!DeviceInfoData) {
Err = ERROR_INVALID_PARAMETER;
goto clean1;
}
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
Err = ERROR_INVALID_HANDLE;
goto clean1;
}
LogContext = pDeviceInfoSet->InstallParamBlock.LogContext;
Err = NO_ERROR;
hDeviceInf = INVALID_HANDLE_VALUE;
CloseUserFileQ = FALSE;
FreeMsgHandlerContext = FALSE;
hkDrv = INVALID_HANDLE_VALUE;
DeleteDrvRegKey = FALSE;
CloseInfHandle = FALSE;
try {
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL))) {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// This routine can't install a non-native driver
//
if(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALTPLATFORM_DRVSEARCH) {
Err = ERROR_INVALID_FLAGS;
goto clean0;
}
//
// set the LogContext for this function
//
LogContext = DevInfoElem->InstallParamBlock.LogContext;
//
// If there's no driver selected (i.e., this is a null driver install)
// then there's nothing for us to do.
//
if(!DevInfoElem->SelectedDriver) {
goto clean0;
}
//
// Make sure we only use the devinfo element's window if it's valid.
//
if(hwndParent = DevInfoElem->InstallParamBlock.hwndParent) {
if(!IsWindow(hwndParent)) {
hwndParent = NULL;
}
}
szInfFileName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfFileName
);
szInfSectionName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfSectionName
);
if(hDeviceInf == INVALID_HANDLE_VALUE) {
if((hDeviceInf = SetupOpenInfFile(szInfFileName,
NULL,
INF_STYLE_WIN4,
NULL)) == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
CloseInfHandle = TRUE;
}
//
// see if we should give the INF the same LogContext as the DevInfoElem
//
InheritLogContext(LogContext,
&((PLOADED_INF) hDeviceInf)->LogContext);
slot_section = AllocLogInfoSlot(LogContext,FALSE);
//
// Find out the 'real' install section we should be using (i.e., the potentially
// OS/architecture-specific one.
//
if(!SetupDiGetActualSectionToInstall(hDeviceInf,
szInfSectionName,
InfSectionWithExt,
SIZECHARS(InfSectionWithExt),
&InfSectionWithExtLength,
NULL
)) {
Err = GetLastError();
goto clean0;
}
//
// Now append the ".CoInstallers" extension to the install section name to find
// the co-installer INF section to run.
//
MYVERIFY(SUCCEEDED(StringCchCopy(&(InfSectionWithExt[InfSectionWithExtLength - 1]),
SIZECHARS(InfSectionWithExt)-(InfSectionWithExtLength - 1),
pszCoInstallersSectionSuffix)));
//
// Figure out whether we need to do file copying. (Ignore the DI_NOFILECOPY
// flag if we're doing a copy-only installation--that's what setupx does.)
//
DoFileCopying = (!(DevInfoElem->InstallParamBlock.Flags & DI_NOFILECOPY) || !DoFullInstall);
//
// Append the layout INF, if necessary.
//
if(DoFileCopying) {
SetupOpenAppendInfFile(NULL, hDeviceInf, NULL);
}
//
// Append-load any included INFs specified in an "include=" line in our
// install section.
//
AppendLoadIncludedInfs(hDeviceInf, szInfFileName, InfSectionWithExt, DoFileCopying);
//
// Now copy the files, if necessary.
//
if(DoFileCopying) {
if(UserFileQ == INVALID_HANDLE_VALUE) {
//
// If the DI_NOVCP flag is set, then just queue up the file
// copy/rename/delete operations. Otherwise, perform the
// actions.
//
if(DevInfoElem->InstallParamBlock.Flags & DI_NOVCP) {
//
// We must have a user-supplied file queue.
//
MYASSERT(DevInfoElem->InstallParamBlock.UserFileQ);
UserFileQ = DevInfoElem->InstallParamBlock.UserFileQ;
} else {
//
// Since we may need to check the queued files to determine whether file copy
// is necessary, we have to open our own queue, and commit it ourselves.
//
if((UserFileQ = SetupOpenFileQueue()) != INVALID_HANDLE_VALUE) {
CloseUserFileQ = TRUE;
} else {
//
// SetupOpenFileQueue sets actual error
//
Err = GetLastError();
goto clean0;
}
}
}
//
// See if we should replace the file queue's log context with the
// DevInfoElem's
//
InheritLogContext(LogContext,
&((PSP_FILE_QUEUE) UserFileQ)->LogContext);
WriteLogEntry(LogContext,
slot_section,
MSG_LOG_COINSTALLER_REGISTRATION,
NULL,
InfSectionWithExt);
Err = InstallFromInfSectionAndNeededSections(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_FILES,
NULL,
NULL,
SP_COPY_NEWER_OR_SAME | SP_COPY_LANGUAGEAWARE |
((DevInfoElem->InstallParamBlock.Flags & DI_NOBROWSE) ? SP_COPY_NOBROWSE : 0),
NULL,
NULL,
INVALID_HANDLE_VALUE,
NULL,
UserFileQ
);
//
// Mark the queue as a device install queue (and make sure there's a
// catalog node representing our device INF in the queue).
//
Err = MarkQueueForDeviceInstall(UserFileQ,
hDeviceInf,
pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DrvDescription)
);
//
// At this point, we have queued up all the files that need to be copied. If
// we weren't given a user-supplied queue, then commit our queue now.
//
if(CloseUserFileQ) {
if(Err == NO_ERROR) {
//
// Determine whether the queue actually needs to be
// committed.
//
// ScanQueueResult can have 1 of 3 values:
//
// 0: Some files were missing--must commit queue.
//
// 1: All files to be copied are already present and
// queue is empty--skip committing queue.
//
// 2: All files to be copied are present, but
// del/ren/backup queues not empty--must commit
// queue. The copy queue will have been emptied, so
// only del/ren/backup functions will be performed.
//
// (jamiehun) see previous case of SetupScanFileQueue
// for a discussion of DI_FLAGSEX_PREINSTALLBACKUP
// handling.
//
if(!SetupScanFileQueue(UserFileQ,
SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE,
hwndParent,
NULL,
NULL,
&ScanQueueResult)) {
//
// SetupScanFileQueue should really never fail when
// you don't ask it to call a callback routine, but
// if it does, just go ahead and commit the queue.
//
ScanQueueResult = 0;
}
}
if((Err == NO_ERROR) && (ScanQueueResult != 1)) {
//
// We need to commit this file queue. Figure out what
// message handler to use.
//
if(DevInfoElem->InstallParamBlock.InstallMsgHandler) {
MsgHandler = DevInfoElem->InstallParamBlock.InstallMsgHandler;
MsgHandlerContext = DevInfoElem->InstallParamBlock.InstallMsgHandlerContext;
MsgHandlerIsNativeCharWidth = DevInfoElem->InstallParamBlock.InstallMsgHandlerIsNativeCharWidth;
} else {
NoProgressUI = (GuiSetupInProgress || (DevInfoElem->InstallParamBlock.Flags & DI_QUIETINSTALL));
if(MsgHandlerContext = SetupInitDefaultQueueCallbackEx(
hwndParent,
(NoProgressUI ? INVALID_HANDLE_VALUE : NULL),
0,
0,
NULL))
{
FreeMsgHandlerContext = TRUE;
MsgHandler = SetupDefaultQueueCallback;
MsgHandlerIsNativeCharWidth = TRUE;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Copy enqueued files.
//
if(Err == NO_ERROR) {
//
// Call _SetupVerifyQueuedCatalogs separately (i.e.,
// don't let it happen automatically as a result of
// committing the queue that happens below). We do
// this beforehand so that we know what unique name was
// generated when an OEM INF was installed into
// %windir%\Inf (in case we need to delete the
// INF/PNF/CAT files later if we encounter an error).
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_COINST_VERIFY_CAT_TIME,
NULL); // text message
Err = _SetupVerifyQueuedCatalogs(
hwndParent,
UserFileQ,
(VERCAT_INSTALL_INF_AND_CAT |
((DevInfoElem->SelectedDriver->Flags & DNF_INET_DRIVER)
? VERCAT_PRIMARY_DEVICE_INF_FROM_INET : 0)),
szNewName,
&OemInfFileToCopy
);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_COINST_VERIFY_CAT_TIME,
NULL); // text message
if(Err == NO_ERROR) {
if(_SetupCommitFileQueue(hwndParent,
UserFileQ,
MsgHandler,
MsgHandlerContext,
MsgHandlerIsNativeCharWidth
)) {
//
// Check to see whether a reboot is required as a
// result of committing the queue (i.e., because
// files were in use, or the INF requested a reboot).
//
FileQueueNeedsReboot = SetupPromptReboot(UserFileQ, NULL, TRUE);
//
// This should never fail...
//
MYASSERT(FileQueueNeedsReboot != -1);
if(FileQueueNeedsReboot) {
SetDevnodeNeedsRebootProblem(DevInfoElem, pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_INUSE);
}
} else {
Err = GetLastError();
}
}
}
}
//
// Close our file queue handle.
//
SetupCloseFileQueue(UserFileQ);
CloseUserFileQ = FALSE;
//
// Terminate the default queue callback, if it was created.
//
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
FreeMsgHandlerContext = FALSE;
}
if(Err != NO_ERROR) {
goto clean0;
}
}
}
//
// If all we were asked to do was copy files, then we're done.
//
if(!DoFullInstall ||
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NO_DRVREG_MODIFY)) {
goto clean0;
}
//
// Open/create the Driver Reg Key.
//
if((hkDrv = SetupDiOpenDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_READ | KEY_WRITE)) == INVALID_HANDLE_VALUE) {
//
// Assume the driver key doesn't already exist--try to create it.
//
if((hkDrv = SetupDiCreateDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
NULL,
NULL)) != INVALID_HANDLE_VALUE) {
//
// We successfully created the driver key. Set a flag so we'll know
// to delete it in case we hit an error and need to clean up.
//
DeleteDrvRegKey = TRUE;
} else {
Err = GetLastError();
goto clean0;
}
}
//
// We don't pass a msg handler so no need to worry about ansi
// vs. unicode issues here.
//
Err = InstallFromInfSectionAndNeededSections(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_INIFILES
| SPINST_REGISTRY
| SPINST_INI2REG
| SPINST_BITREG
| SPINST_REGSVR
| SPINST_UNREGSVR
| SPINST_PROFILEITEMS,
hkDrv,
NULL,
0,
NULL,
NULL,
INVALID_HANDLE_VALUE,
NULL,
NULL
);
if(Err == NO_ERROR) {
//
// Registering co-installers invalidates our existing co-installer
// list. Reset this list (migrating the module handles to the devinfo
// set's list of "things to clean up later".
// (NOTE: SetupDiCallClassInstaller also does this when its handling
// DIF_REGISTER_COINSTALLERS, but since we can't be guaranteed we're
// being called in the context of SetupDiCallClassInstaller, we have
// to do this ourselves as well.)
//
Err = InvalidateHelperModules(DeviceInfoSet,
DeviceInfoData,
IHM_COINSTALLERS_ONLY
);
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
}
if(CloseUserFileQ) {
SetupCloseFileQueue(UserFileQ);
}
//
// Reference the following variables so the compiler will respect our statement
// ordering w.r.t. assignment.
//
DeleteDrvRegKey = DeleteDrvRegKey;
CloseInfHandle = CloseInfHandle;
OemInfFileToCopy = OemInfFileToCopy;
}
if(hkDrv != INVALID_HANDLE_VALUE) {
RegCloseKey(hkDrv);
if((Err != NO_ERROR) && DeleteDrvRegKey) {
SetupDiDeleteDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGGENERAL,
0,
DIREG_DRV
);
}
}
if(CloseInfHandle) {
MYASSERT(hDeviceInf != INVALID_HANDLE_VALUE);
SetupCloseInfFile(hDeviceInf);
}
clean1:
if (Err == NO_ERROR) {
//
// give a +ve affirmation of Success
//
WriteLogEntry(
LogContext,
DoFullInstall ? DRIVER_LOG_INFO : DRIVER_LOG_VERBOSE,
MSG_LOG_REGISTEREDCOINSTALLERS,
NULL);
} else {
//
// indicate failed, display error
//
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_REGISTERCOINSTALLERS_ERROR,
NULL);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Err);
//
// If we copied the OEM INF into the INF directory under a newly-
// generated name, delete it now.
//
if(OemInfFileToCopy) {
pSetupUninstallOEMInf(szNewName, LogContext, SUOI_FORCEDELETE, NULL);
}
}
if (slot_section) {
ReleaseLogInfoSlot(LogContext,slot_section);
}
if (pDeviceInfoSet) {
UnlockDeviceInfoSet(pDeviceInfoSet);
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiSelectBestCompatDrv(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default handler for DIF_SELECTBESTCOMPATDRV
This routine will select the best driver from the device information element's
compatible driver list.
This function will enumerate through all of the drivers and find the one with
the Best Rank (this is the lowest Rank number). If there are multiple drivers
with the same Best Rank then we will take the driver with the newest DriverDate.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set containing
the element for which the best compatible driver is to be selected.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for
which the best compatible driver is to be selected. This element must
already have a (non-empty) compatible driver list built for it, or this
API will fail and GetLastError will return ERROR_NO_COMPAT_DRIVERS.
This is an IN OUT parameter because the class GUID for the device will be
updated to reflect the class of the selected driver.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
--*/
{
SP_DRVINFO_DATA DriverInfoData;
SP_DRVINSTALL_PARAMS DriverInstallParams;
DWORD BestRank, BasicBestRank;
DWORDLONG BestDriverVersion, BasicBestDriverVersion;
DWORD MemberIndex;
INT BestMemberIndex, BasicBestMemberIndex;
PSETUP_LOG_CONTEXT LogContext = NULL;
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
DWORD Err = NO_ERROR;
PDEVINFO_ELEM DevInfoElem = NULL;
FILETIME BestDriverDate, BasicBestDriverDate;
BestRank = BasicBestRank = RANK_NO_MATCH;
BestMemberIndex = BasicBestMemberIndex = -1;
BestDriverVersion = BasicBestDriverVersion = 0;
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
Err = ERROR_INVALID_HANDLE;
goto clean1;
}
LogContext = pDeviceInfoSet->InstallParamBlock.LogContext;
if(DeviceInfoData
&& (DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL))!=NULL) {
LogContext = DevInfoElem->InstallParamBlock.LogContext;
}
//
//Enumerate through all of the drivers and find the Best Rank
//
MemberIndex = 0;
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
while (SetupDiEnumDriverInfo(DeviceInfoSet,
DeviceInfoData,
SPDIT_COMPATDRIVER,
MemberIndex,
&DriverInfoData)) {
DriverInstallParams.cbSize = sizeof(SP_DRVINSTALL_PARAMS);
if ((SetupDiGetDriverInstallParams(DeviceInfoSet,
DeviceInfoData,
&DriverInfoData,
&DriverInstallParams))) {
if(DriverInstallParams.Flags & DNF_BAD_DRIVER) {
//
// This driver was marked as bad--we don't want to use it, but
// we should log an informational message as to why it wasn't
// considered.
//
SP_DRVINFO_DETAIL_DATA Data;
Data.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if(SetupDiGetDriverInfoDetail(DeviceInfoSet,
DeviceInfoData,
&DriverInfoData,
&Data,
Data.cbSize,
NULL) ||
(GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
WriteLogEntry(
LogContext,
DRIVER_LOG_INFO,
MSG_LOG_SELECTBEST_BAD_DRIVER,
NULL,
Data.DrvDescription,
Data.InfFileName,
Data.SectionName
);
}
} else if(DriverInstallParams.Flags & DNF_BASIC_DRIVER) {
//
// This driver is flagged as a "basic functionality" driver.
// We'll keep track of the best such driver we encounter, but
// we'll only use a basic driver if there exists no non-basic
// signed drivers.
//
if(DriverInstallParams.Rank <= BasicBestRank) {
if(BasicBestMemberIndex == -1) {
//
// This is the first acceptable basic driver we've
// encountered...
//
BasicBestDriverDate = DriverInfoData.DriverDate;
BasicBestMemberIndex = (INT)MemberIndex;
BasicBestDriverVersion = DriverInfoData.DriverVersion;
} else {
if((DriverInstallParams.Rank < BasicBestRank) ||
(CompareFileTime(&BasicBestDriverDate, &(DriverInfoData.DriverDate)) == -1)) {
//
// If this new driver has a better (smaller) Rank than
// the current Best Driver or it has a newer date then
// make it the Best Driver.
//
BasicBestDriverDate = DriverInfoData.DriverDate;
BasicBestMemberIndex = (INT)MemberIndex;
BasicBestDriverVersion = DriverInfoData.DriverVersion;
} else if ((DriverInstallParams.Rank == BasicBestRank) &&
(CompareFileTime(&BasicBestDriverDate, &(DriverInfoData.DriverDate)) == 0) &&
(DriverInfoData.DriverVersion > BasicBestDriverVersion)) {
//
// If the Rank and DriverDate of the new driver
// are identical to the current Best driver,
// then check if the new driver has a newer
// DriverVersion and if so make it the Best Driver.
//
BasicBestDriverDate = DriverInfoData.DriverDate;
BasicBestMemberIndex = (INT)MemberIndex;
BasicBestDriverVersion = DriverInfoData.DriverVersion;
}
}
BasicBestRank = DriverInstallParams.Rank;
}
} else {
//
// This isn't a basic driver. If it's the best one we've seen,
// remember it.
//
if(DriverInstallParams.Rank <= BestRank) {
if(BestMemberIndex == -1) {
//
// This is the first acceptable non-basic driver we've
// encountered...
//
BestDriverDate = DriverInfoData.DriverDate;
BestMemberIndex = (INT)MemberIndex;
BestDriverVersion = DriverInfoData.DriverVersion;
} else {
//
// If this new driver has a better (smaller) Rank than
// the current Best Driver or it has a newer date then
// make it the Best Driver.
//
if((DriverInstallParams.Rank < BestRank) ||
(CompareFileTime(&BestDriverDate, &(DriverInfoData.DriverDate)) == -1)) {
BestDriverDate = DriverInfoData.DriverDate;
BestMemberIndex = (INT)MemberIndex;
BestDriverVersion = DriverInfoData.DriverVersion;
} else if ((DriverInstallParams.Rank == BestRank) &&
(CompareFileTime(&BestDriverDate, &(DriverInfoData.DriverDate)) == 0) &&
(DriverInfoData.DriverVersion > BestDriverVersion)) {
//
// If the Rank and DriverDate of the new driver
// are identical to the current Best driver,
// then check if the new driver has a newer
// DriverVersion and if so make it the Best Driver.
//
BestDriverDate = DriverInfoData.DriverDate;
BestMemberIndex = (INT)MemberIndex;
BestDriverVersion = DriverInfoData.DriverVersion;
}
}
BestRank = DriverInstallParams.Rank;
}
}
}
MemberIndex++;
}
//
// If BestRank and BasicBestRank still equal RANK_NO_MATCH then we don't
// have any compatible drivers
//
if((BestRank == RANK_NO_MATCH) && (BasicBestRank == RANK_NO_MATCH)) {
Err = ERROR_NO_COMPAT_DRIVERS;
goto clean1;
}
//
// Select best driver from among basic and full-featured offerings as
// follows:
//
// 1. Full-featured, signed driver
// 2. Basic signed driver
// 3. Best rank/newest date between full-featured and basic unsigned
// driver (note: it will be rare that we encounter a basic unsigned
// driver since this will typically be used only for our in-box
// offerings, which are always signed)
//
if(BestRank < DRIVER_UNTRUSTED_RANK) {
MemberIndex = (DWORD)BestMemberIndex;
} else if(BasicBestRank < DRIVER_UNTRUSTED_RANK) {
MemberIndex = (DWORD)BasicBestMemberIndex;
} else {
if(BestRank < BasicBestRank) {
MemberIndex = (DWORD)BestMemberIndex;
} else if(BasicBestRank < BestRank) {
MemberIndex = (DWORD)BasicBestMemberIndex;
} else {
//
// We have both a basic driver and full-featured one, both of which
// are unsigned, and both of which are equivalently ranked. Pick
// the newer one. In the unlikely event that they're the same
// date, pick the full-featured one.
//
// We have both a basic driver and full-featured one, both of which
// are unsigned, and both of which are equivalently ranked. If the
// driver dates are the same, then pick the one with the newer
// driver version, otherwise pick the one with the newest date.
// In the unlikely event that the DriverDate and DriverVersion are
// the same, pick the full-featured one.
//
if (CompareFileTime(&BestDriverDate, &BasicBestDriverDate) == 0) {
//
// Driver dates are the same, so take the one with the newest
// driver version.
//
if (BasicBestDriverVersion > BestDriverVersion) {
MemberIndex = (DWORD)BasicBestMemberIndex;
} else {
MemberIndex = (DWORD)BestMemberIndex;
}
} else if(CompareFileTime(&BestDriverDate, &BasicBestDriverDate) == -1) {
MemberIndex = (DWORD)BasicBestMemberIndex;
} else {
MemberIndex = (DWORD)BestMemberIndex;
}
}
}
//
// (Re-)retrieve the driver info data for the best driver we found.
//
if(!SetupDiEnumDriverInfo(DeviceInfoSet,
DeviceInfoData,
SPDIT_COMPATDRIVER,
MemberIndex,
&DriverInfoData)) {
//
// We should never see this error, since we retrieved this driver node
// just moments ago, and we have the HDEVINFO locked, so no one could
// have deleted it out from under us.
//
Err = GetLastError();
goto clean1;
}
if(!SetupDiSetSelectedDriver(DeviceInfoSet,
DeviceInfoData,
&DriverInfoData)) {
Err = GetLastError();
}
clean1:
if(Err == NO_ERROR) {
//
// give a +ve affirmation of Success
//
WriteLogEntry(
LogContext,
DRIVER_LOG_INFO,
MSG_LOG_SELECTEDBEST,
NULL);
//
// If we had a basic driver but didn't use it, log an information entry
//
if((BasicBestMemberIndex != -1) &&
(BasicBestMemberIndex != (INT)MemberIndex)) {
if(SetupDiEnumDriverInfo(DeviceInfoSet,
DeviceInfoData,
SPDIT_COMPATDRIVER,
(DWORD)BasicBestMemberIndex,
&DriverInfoData)) {
SP_DRVINFO_DETAIL_DATA Data;
Data.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if(SetupDiGetDriverInfoDetail(DeviceInfoSet,
DeviceInfoData,
&DriverInfoData,
&Data,
Data.cbSize,
NULL) ||
(GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
WriteLogEntry(
LogContext,
DRIVER_LOG_INFO,
MSG_LOG_SELECTBEST_BASIC_DRIVER_SKIPPED,
NULL,
Data.DrvDescription,
Data.InfFileName,
Data.SectionName
);
}
}
}
} else {
//
// indicate failed, display error
//
WriteLogEntry(
LogContext,
((Err == ERROR_NO_COMPAT_DRIVERS) ?
DRIVER_LOG_WARNING :
DRIVER_LOG_ERROR)
| SETUP_LOG_BUFFER,
MSG_LOG_SELECTBEST_ERROR,
NULL);
WriteLogError(
LogContext,
((Err == ERROR_NO_COMPAT_DRIVERS) ?
DRIVER_LOG_WARNING :
DRIVER_LOG_ERROR),
Err);
}
if (pDeviceInfoSet) {
UnlockDeviceInfoSet(pDeviceInfoSet);
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
RetrieveAllDriversForDevice(
IN PDEVINFO_ELEM DevInfoElem,
OUT PTSTR *DriverList,
IN DWORD Flags,
IN HMACHINE hMachine
)
/*++
Routine Description:
This routine returns a multi-sz list of all filter drivers (both upper and
lower, class-specific and device-specific) for the specified device information
element.
Arguments:
DevInfoElem - Specifies the device information element whose list of filter
drivers are to be retrieved.
DriverList - If this routine returns TRUE (i.e., there was at least one
filter driver for this device), then this pointer is filled in to point
to a newly-allocated buffer containing a multi-sz list of all filters
associated with this device. If we encountered an out-of-memory error,
this pointer will be set to NULL.
Flags - can be one of the following:
RADFD_FLAG_FUNCTION_DRIVER - include the function driver in the
DriverList return value.
RADFD_FLAG_DEVICE_UPPER_FILTERS - include the device upper filters in
the DriverList return value.
RADFD_FLAG_DEVICE_LOWER_FILTERS - include the device lower filters in
the DriverList return value.
RADFD_FLAG_CLASS_UPPER_FILTERS - include the class upper filters in the
DriverList return value.
RADFD_FLAG_CLASS_LOWER_FILTERS - include the class lower filters in the
DriverList return value.
RADFD_FLAG_DEVICE_FILTERS - include both the device upper and lower
filters in the DriverList return value.
RADFD_FLAG_CLASS_FILTERS include both the class upper and lower filters
in the DriverList return value.
Return Value:
If there is at least one filter driver for the specified device (or we couldn't
tell because we ran out of memory), the return value is TRUE.
Otherwise, it is FALSE.
--*/
{
PTSTR Buffer, NewBuffer, CurPos, EndPos, p;
DWORD BufferSize, UsedSize, RequiredSize;
DWORD i, NumLists, Err;
CONFIGRET cr;
DWORD RegDataType;
HKEY hk;
ULONG ulProperty;
DWORD Gap = sizeof(TCHAR)*2; // extra space at end of buffer for nulls
*DriverList = NULL;
BufferSize = 1024 * sizeof(TCHAR); // start out with a reasonably-sized buffer.
Buffer = MyMalloc(BufferSize+Gap);
if(Buffer) {
UsedSize = 0;
CurPos = Buffer;
} else {
//
// We really don't know whether there were any filters for this device, but
// if we return TRUE, the NULL DriverList OUT parameter will signal to the
// caller that we encountered an out-of-memory condition.
//
return TRUE;
}
//
// Attempt to open the class key so we can retrieve the class-specific filter lists.
//
hk = SetupDiOpenClassRegKey(&(DevInfoElem->ClassGuid), KEY_READ);
NumLists = (hk == INVALID_HANDLE_VALUE) ? 3 : 5;
//
// First, retrieve the UpperFilters and LowerFilters device properties (i.e.,
// the device-specific filters).
//
for(i = 0; i < NumLists; i++) {
RequiredSize = Gap;
while(TRUE) {
//
// Do we need a larger buffer?
//
if((BufferSize<UsedSize) || (RequiredSize > (BufferSize - UsedSize))) {
BufferSize = UsedSize + RequiredSize;
NewBuffer = MyRealloc(Buffer, BufferSize+Gap);
if(NewBuffer) {
//
// Adjust our current position pointer.
//
CurPos = NewBuffer + (CurPos - Buffer);
Buffer = NewBuffer;
} else {
MyFree(Buffer);
if(hk != INVALID_HANDLE_VALUE) {
RegCloseKey(hk);
}
return TRUE;
}
} else {
RequiredSize = BufferSize - UsedSize;
}
if(i < 3) {
//
// Then we're retrieving the device-specific lists.
//
if (i == 0) {
//
// Check to see if we need to include the function driver
//
if (!(Flags & RADFD_FLAG_FUNCTION_DRIVER)) {
//
// Skip the function driver.
//
break;
}
ulProperty = CM_DRP_SERVICE;
} else if (i == 1) {
//
// Check to see if we need to include the device lower filters.
//
if (!(Flags & RADFD_FLAG_DEVICE_LOWER_FILTERS)) {
//
// Skip the device lower filters.
//
break;
}
ulProperty = CM_DRP_LOWERFILTERS;
} else {
//
// Check to see if we need to include the device upper filters.
//
if (!(Flags & RADFD_FLAG_DEVICE_UPPER_FILTERS)) {
//
// Skip the device upper filters.
//
break;
}
ulProperty = CM_DRP_UPPERFILTERS;
}
cr = CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
ulProperty,
NULL,
CurPos,
&RequiredSize,
0,
hMachine
);
//
// Map the CONFIGRET code to Win32 error codes that mimic the
// behavior of the registry APIs. This way, we can have common
// handling of the error codes down below...
//
switch(cr) {
case CR_SUCCESS :
Err = ERROR_SUCCESS;
break;
case CR_BUFFER_SMALL :
Err = ERROR_MORE_DATA;
break;
default :
Err = ERROR_INVALID_DATA; // any old error will do.
break;
}
} else {
//
// We're retrieving the class-specific lists.
//
if (i == 3) {
//
// Check to see if we need to include the class lower filters
//
if (!(Flags & RADFD_FLAG_CLASS_LOWER_FILTERS)) {
//
// Skip the class lower filters.
//
break;
}
} else {
//
// Check to see if we need to include the class upper filters.
//
if (!(Flags & RADFD_FLAG_CLASS_UPPER_FILTERS)) {
//
// Skip the class upper filters.
//
break;
}
}
Err = RegQueryValueEx(hk,
((i == 3) ? pszLowerFilters : pszUpperFilters),
NULL,
&RegDataType,
(PBYTE)CurPos,
&RequiredSize
);
}
if(Err == ERROR_SUCCESS) {
//
// Walk through the service names in the multi-sz list we just retrieved to find
// the end (just in case someone screwed up when saving out this list and didn't
// size it properly, etc.)
//
// we made sure we have space for a couple of extra nulls (Gap)
// so add them here
// this covers all the bad string termination cases
// it also covers the case where we're given a REG_SZ instead
// of REG_MULTI_SZ
//
p = CurPos;
EndPos = CurPos+(RequiredSize/sizeof(TCHAR));
ZeroMemory(EndPos,Gap);
try {
while(*CurPos) {
CurPos += _tcslen(CurPos)+1;
p = CurPos;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
MYASSERT(CurPos != p);
CurPos = p;
*CurPos = TEXT('\0');
}
UsedSize = (DWORD)((PBYTE)CurPos - (PBYTE)Buffer) + sizeof(TCHAR);
MYASSERT(UsedSize <= (BufferSize+Gap));
break;
} else if(Err != ERROR_MORE_DATA) {
//
// We failed for some reason other than buffer-too-small. Move
// on to the next filter driver list.
//
break;
}
}
}
if(hk != INVALID_HANDLE_VALUE) {
RegCloseKey(hk);
}
if(UsedSize) {
//
// We retrieved a list of services to return to the caller.
//
*DriverList = Buffer;
return TRUE;
}
MYASSERT(Buffer);
MyFree(Buffer);
return FALSE;
}
BOOL
RetrieveAllDevNodesSharingDriversWithDevice(
IN PDEVINFO_ELEM DevInfoElem,
OUT PTSTR *Drivers,
OUT PDEVNODE *DevNodes,
OUT ULONG *NumberOfDevNodes,
IN HMACHINE hMachine
)
/*++
Routine Description:
This routine returns a multi-sz list of all the devnodes that share a
driver with the specified device. This includes the device's function driver
and all filter drivers (both upper and lower, class-specific and device-
specific).
This routine returns a multi-sz list of all filter drivers (both upper and
lower device-specific) for the specified device information
element.
Arguments:
DevInfoElem - Specifies the device information element.
Drivers -
DevNodes -
NumberOfDevNodes -
Return Value:
TRUE unless there is an unexpected error such as out of memory.
--*/
{
DWORD Err = ERROR_SUCCESS;
PTSTR p, Buffer = NULL;
ULONG BufferSize = 1024;
PTSTR DriversList = NULL, CurDriver;
DEVNODE DevNode;
PDEVNODE NewBuffer, ArrayOfDevNodes;
ULONG DevNodeArraySize = 10;
ULONG i;
CONFIGRET cr;
*NumberOfDevNodes = 0;
try {
//
// Allocate an array of devnodes. Start with 10 and we will grow it below
// if we have more.
//
if ((ArrayOfDevNodes = MyMalloc(DevNodeArraySize * sizeof(DEVNODE))) == NULL) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Always include the devnode that was passed in.
//
ArrayOfDevNodes[*NumberOfDevNodes] = DevInfoElem->DevInst;
(*NumberOfDevNodes)++;
//
// Now get a list of all the filter drivers (both upper and lower
// device-specific) that this device is using and
// then add any devnode using one of these services to our list.
//
if (RetrieveAllDriversForDevice(DevInfoElem,
&DriversList,
RADFD_FLAG_FUNCTION_DRIVER | RADFD_FLAG_DEVICE_FILTERS,
hMachine)) {
//
// We need a buffer when we call CM_Get_Device_ID_list_Ex, we will
// start with a 1024 buffer and grow it as needed.
//
Buffer = MyMalloc(BufferSize * sizeof(TCHAR));
if (!Buffer) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Enumerate through all the filters and build up a list of drivers
// they are using.
//
for (CurDriver=DriversList; *CurDriver; CurDriver+=(lstrlen(CurDriver)+1)) {
while ((cr = CM_Get_Device_ID_List_Ex(
CurDriver,
Buffer,
BufferSize,
CM_GETIDLIST_FILTER_SERVICE |
CM_GETIDLIST_DONOTGENERATE,
hMachine
)) == CR_BUFFER_SMALL) {
//
// If the buffer is too small then we need to reallocate a
// larger one.
//
PTSTR TempBuffer;
CM_Get_Device_ID_List_Size_Ex(&BufferSize,
CurDriver,
CM_GETIDLIST_FILTER_SERVICE,
hMachine);
TempBuffer = MyRealloc(Buffer, BufferSize * sizeof(TCHAR));
if (TempBuffer) {
Buffer = TempBuffer;
} else {
//
// We couldn't create a larger buffer!
//
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
if (cr == CR_SUCCESS) {
//
// Enumerate through all the device instance Ids and add them
// to the list of devnodes.
//
for (p=Buffer; *p; p+=(lstrlen(p)+1)) {
if (CM_Locate_DevInst_Ex(&DevNode,
p,
0,
hMachine
) == CR_SUCCESS) {
//
// Check to see if this devnode already exists in
// our list.
//
for (i=0; i<*NumberOfDevNodes; i++) {
if (ArrayOfDevNodes[i] == DevNode) {
break;
}
}
if (i == *NumberOfDevNodes) {
//
// Check to see if we need to increase our devnode array.
//
if (DevNodeArraySize <= *NumberOfDevNodes) {
DevNodeArraySize += 10;
NewBuffer = MyRealloc(ArrayOfDevNodes, DevNodeArraySize * sizeof(DEVNODE));
if (NewBuffer) {
ArrayOfDevNodes = NewBuffer;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
ArrayOfDevNodes[*NumberOfDevNodes] = DevNode;
(*NumberOfDevNodes)++;
}
}
}
} else {
//
// We got another error back so we will fail.
//
Err = ERROR_INVALID_DATA;
goto clean0;
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
//
// Access the following variables, so that the compiler will respect our
// statement ordering w.r.t. their assignment.
//
Buffer = Buffer;
ArrayOfDevNodes = ArrayOfDevNodes;
DriversList = DriversList;
}
clean0:
if (Buffer) {
MyFree(Buffer);
}
if (Err == ERROR_SUCCESS) {
*DevNodes = ArrayOfDevNodes;
*Drivers = DriversList;
} else {
//
// If we failed then free all the memory and set the pointers
// to NULL.
//
if (ArrayOfDevNodes) {
MyFree(ArrayOfDevNodes);
*DevNodes = NULL;
}
if (DriversList) {
MyFree(DriversList);
*Drivers = NULL;
}
}
return (Err == ERROR_SUCCESS);
}
BOOL
AnyServicesLoaded(
IN PCTSTR ServiceNameList,
OUT PTSTR LoadedService, OPTIONAL
IN ULONG LoadedServiceSize
)
/*++
Routine Description:
This routine will determine if any of the services passed in our currently
loaded.
Arguments:
ServiceNameList - multi-sz list of service names.
LoadedService - buffer that will receive the service the first service in
the list that is still loaded into memory. This value is only valid
if the API returns TRUE.
LoadedServiceSize - size of the LoadedService buffer.
Return Value:
TRUE if any of the services in the list are loaded, FALSE if none of them
are currently loaded.
--*/
{
NTSTATUS Status;
BOOL bObjectIsLoaded = FALSE;
DWORD BufSize = 1024;
PUCHAR Buffer = NULL;
UNICODE_STRING UnicodeStringDriver;
UNICODE_STRING UnicodeStringService;
OBJECT_ATTRIBUTES Attributes;
HANDLE DirectoryHandle = INVALID_HANDLE_VALUE;
POBJECT_DIRECTORY_INFORMATION DirInfo;
POBJECT_NAME_INFORMATION NameInfo;
ULONG Context = 0;
ULONG ReturnedLength;
PTSTR CurServiceName;
try {
if (LoadedService) {
LoadedService[0] = TEXT('\0');
}
//
// If no services were passed in then just leave.
//
if (!ServiceNameList) {
goto clean0;
}
Buffer = MyMalloc(BufSize);
if (!Buffer) {
goto clean0;
}
ZeroMemory(Buffer, BufSize);
RtlInitUnicodeString(&UnicodeStringDriver, TEXT("\\Driver"));
InitializeObjectAttributes(&Attributes,
&UnicodeStringDriver,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenDirectoryObject(&DirectoryHandle,
DIRECTORY_QUERY,
&Attributes
);
if (!NT_SUCCESS(Status)) {
goto clean0;
}
//
// Get the actual name of the object directory object.
//
NameInfo = (POBJECT_NAME_INFORMATION)Buffer;
if (!NT_SUCCESS(Status = NtQueryObject(DirectoryHandle,
ObjectNameInformation,
NameInfo,
BufSize,
(PULONG)NULL))) {
goto clean0;
}
//
// Grab the driver objects in chuncks instead of one at a time.
//
for (Status = NtQueryDirectoryObject(DirectoryHandle,
Buffer,
BufSize,
FALSE,
FALSE,
&Context,
&ReturnedLength
);
NT_SUCCESS(Status) && !bObjectIsLoaded;
Status = NtQueryDirectoryObject(DirectoryHandle,
Buffer,
BufSize,
FALSE,
FALSE,
&Context,
&ReturnedLength
)) {
if (!NT_SUCCESS(Status)) {
break;
}
DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer;
while (TRUE) {
//
// Check if there is another record. If there isn't, then get out
// of the loop now.
//
if (DirInfo->Name.Length == 0) {
break;
}
for (CurServiceName=(PTSTR)ServiceNameList;
*CurServiceName;
CurServiceName+=(lstrlen(CurServiceName)+1)) {
RtlInitUnicodeString(&UnicodeStringService, CurServiceName);
if (RtlCompareUnicodeString(&UnicodeStringService, &(DirInfo->Name), TRUE) == 0) {
bObjectIsLoaded = TRUE;
if (LoadedService) {
MYVERIFY(SUCCEEDED(StringCchCopy(LoadedService,
LoadedServiceSize,
CurServiceName)));
}
break;
}
}
//
// If one of the drivers is loaded then just stop now.
//
if (bObjectIsLoaded) {
break;
}
DirInfo = (POBJECT_DIRECTORY_INFORMATION)(((PUCHAR)DirInfo) +
sizeof(OBJECT_DIRECTORY_INFORMATION));
}
ZeroMemory(Buffer, BufSize);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
bObjectIsLoaded = TRUE;
//
// Access the following variables, so that the compiler will respect our
// statement ordering w.r.t. their assignment.
//
Buffer = Buffer;
}
clean0:
if (DirectoryHandle != INVALID_HANDLE_VALUE) {
NtClose(DirectoryHandle);
}
if (Buffer) {
MyFree(Buffer);
}
return bObjectIsLoaded;
}
VOID
RestartSingleDevice(
IN PDEVINFO_ELEM DevInfoElem,
IN PDEVICE_INFO_SET pDeviceInfoSet,
IN BOOL NullDriverInstall,
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
Arguments:
DevInfoElem -
pDeviceInfoSet -
NullDriverInstall -
LogContext -
Return Value:
--*/
{
DWORD DevInstCapabilities;
ULONG cbData;
ULONG ulStatus, ulProblem;
TCHAR DeviceFullID[MAX_DEVICE_ID_LEN];
//
// Retrieve the device's capabilities. We need to know whether the device is
// capable of being driven 'raw' (i.e., without a function driver).
//
cbData = sizeof(DevInstCapabilities);
if(CR_SUCCESS != CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CAPABILITIES,
NULL,
&DevInstCapabilities,
&cbData,
0,
pDeviceInfoSet->hMachine))
{
DevInstCapabilities = 0;
}
//
// If the device instance has a problem but is not disabled. Just
// restart it. This should be 90% of the cases.
//
if((CM_Get_DevInst_Status_Ex(&ulStatus, &ulProblem, DevInfoElem->DevInst,
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
((ulStatus & DN_HAS_PROBLEM) || !(ulStatus & DN_DRIVER_LOADED))) {
//
// Poke at Config Manager to make it load the driver.
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_RESTART_TIME,
NULL); // text message
CM_Setup_DevInst_Ex(DevInfoElem->DevInst, CM_SETUP_DEVINST_READY, pDeviceInfoSet->hMachine);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_RESTART_TIME,
NULL); // text message
//
// If we're installing a 'real' driver (i.e., not null), or if the device is
// raw-capable, then we want to check and see if the device actually started as
// a result of CM_Setup_DevInst. If not, a reboot is in order. (There's no need
// to check when we're doing a null driver install for a non-raw devnode. We know
// it can't start, and we definitely don't want to generate a 'need reboot' popup
// in this case!)
//
if(!NullDriverInstall || (DevInstCapabilities & CM_DEVCAP_RAWDEVICEOK)) {
CheckIfDevStarted(DevInfoElem, pDeviceInfoSet);
}
} else {
CONFIGRET cr;
TCHAR VetoName[MAX_PATH];
PNP_VETO_TYPE VetoType;
//
// If there is a device instance with no problem, then we
// should remove it (to unload the current drivers off of
// it), and then call CM_Setup_DevInst on it. If the
// device instance refuses to remove, then set the flags
// that say we need to reboot.
//
// NOTE: In Win9x, virtual removal of a devnode (e.g., to
// unload the driver(s) for it), resulted in the devnode's
// deletion. Thus, on Win9x, it was necessary to
// re-enumerate the devnode's parent to cause the devnode
// to be re-created (and subsequently started). This is
// inefficient, especially if we're dealing with a root-
// enumerated device (hence requiring a reenumeration of
// the entire tree). Since the device installer
// functionality of setupapi will apparently never be used
// on Win9x, we can optimize this such that we just call
// CM_Setup_DevInst on this devnode. We know it didn't
// actually go away, because devnodes only go away on NT
// when their underlying hardware is physically removed.
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_REMOVE_TIME,
NULL); // text message
cr = CM_Query_And_Remove_SubTree_Ex(DevInfoElem->DevInst,
&VetoType,
VetoName,
SIZECHARS(VetoName),
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NOUIONQUERYREMOVE)
? CM_REMOVE_UI_NOT_OK
: CM_REMOVE_UI_OK,
pDeviceInfoSet->hMachine
);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_REMOVE_TIME,
NULL); // text message
if(cr == CR_SUCCESS) {
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_RESTART_TIME,
NULL); // text message
CM_Setup_DevInst_Ex(DevInfoElem->DevInst,
CM_SETUP_DEVINST_READY,
pDeviceInfoSet->hMachine
);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_RESTART_TIME,
NULL); // text message
//
// If we're installing a 'real' driver (i.e., not null),
// or if the device is raw-capable, then we want to
// check and see if the device actually started as a
// result of CM_Setup_DevInst. If not, a reboot is in
// order. (There's no need to check when we're doing a
// null driver install for a non-raw devnode. We know
// it can't start, and we definitely don't want to
// generate a 'need reboot' popup in this case!)
//
if(!NullDriverInstall || (DevInstCapabilities & CM_DEVCAP_RAWDEVICEOK)) {
CheckIfDevStarted(DevInfoElem,pDeviceInfoSet);
}
} else {
//
// If the failure was due to a veto, then log
// information about who vetoed us.
//
// SPLOG-- write out a log entry
//
if(cr == CR_REMOVE_VETOED) {
if( CM_Get_Device_ID(DevInfoElem->DevInst,
DeviceFullID,
SIZECHARS(DeviceFullID),
0
) == CR_SUCCESS ) {
_WriteVetoLogEntry(
LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REMOVE_VETOED_IN_INSTALL,
DeviceFullID,
VetoName,
VetoType);
}
}
//
// If the failure was due to the device not being there,
// then prompting for reboot isn't going to help
// anything (plus, you can't set a problem on a non-
// existent devnode anyway). This could happen, for
// example, if the user plugged in a USB mouse, then
// unplugged it again before we got a chance to
// complete the device installation. In this case,
// we'll just ignore the failure and continue on.
//
if(cr != CR_INVALID_DEVNODE) {
SetDevnodeNeedsRebootProblemWithArg2(DevInfoElem,
pDeviceInfoSet,
MSG_LOG_REBOOT_REASON_QR_VETOED,
cr,
(ULONG_PTR)_MapCmRetToString(cr)
);
}
}
}
}
VOID
RestartAllDevicesUsingDrivers(
IN PDEVINFO_ELEM DevInfoElem,
IN PDEVICE_INFO_SET pDeviceInfoSet,
IN BOOL NullDriverInstall,
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
Arguments:
DevInfoElem -
pDeviceInfoSet -
NullDriverInstall -
LogContext -
Return Value:
--*/
{
BOOL bNeedReboot = FALSE;
PTSTR CurDriver, DriverList = NULL;
PDEVNODE DevNodeList = NULL;
ULONG NumberOfDevNodes, LastDevNodeStopped, i;
ULONG ulStatus, ulProblem;
PBOOL DevNodeStartedList = NULL;
TCHAR DeviceFullID[MAX_DEVICE_ID_LEN];
TCHAR LoadedService[MAX_SERVICE_NAME_LEN];
CONFIGRET cr;
try {
if (RetrieveAllDevNodesSharingDriversWithDevice(DevInfoElem,
&DriverList,
&DevNodeList,
&NumberOfDevNodes,
pDeviceInfoSet->hMachine)) {
//
// Allocate an array of BOOLs so we can remember if each devnode was
// started before we tried to unload all of the drivers.
//
if (NumberOfDevNodes) {
DevNodeStartedList = MyMalloc(NumberOfDevNodes * sizeof(BOOL));
if (!DevNodeStartedList) {
bNeedReboot = TRUE;
goto clean0;
}
}
//
// First we will enumerate through all of the devices and remember
// if they were started before we remove/restart them all. We need
// to do this step first since we don't have any devnode dependency
// information so stopping the first one could affect one of the
// others in the list.
//
for (i=0; i<NumberOfDevNodes; i++) {
if((CM_Get_DevInst_Status_Ex(&ulStatus, &ulProblem, DevNodeList[i],
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
(!(ulStatus & DN_HAS_PROBLEM) && (ulStatus & DN_DRIVER_LOADED))) {
DevNodeStartedList[i] = TRUE;
} else {
DevNodeStartedList[i] = FALSE;
}
}
//
// Next stop all of the devnodes that are using one of the drivers
// associated with our new device.
//
for (i=0; i<NumberOfDevNodes; i++) {
//
// Remember the last devnode that we tried to stop.
//
LastDevNodeStopped = i;
if(CM_Get_Device_ID(DevNodeList[i],
DeviceFullID,
SIZECHARS(DeviceFullID),
0
) != CR_SUCCESS ) {
DeviceFullID[0] = TEXT('\0');
}
//
// Only attempt to stop the device if it is currently started.
//
if((CM_Get_DevInst_Status_Ex(&ulStatus, &ulProblem, DevNodeList[i],
0,pDeviceInfoSet->hMachine) == CR_SUCCESS) &&
(!(ulStatus & DN_HAS_PROBLEM) && (ulStatus & DN_DRIVER_LOADED))) {
//
// Since the devnode is started we need to query remove it.
//
TCHAR VetoName[MAX_PATH];
PNP_VETO_TYPE VetoType;
//
// Remove the devnode (to unload the current drivers off of
// it). If the device instance refuses to remove, then set
// the flags that say we need to reboot.
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_REMOVE_TIME_DEVICE,
NULL,
DeviceFullID);
cr = CM_Query_And_Remove_SubTree_Ex(DevNodeList[i],
&VetoType,
VetoName,
SIZECHARS(VetoName),
CM_REMOVE_NO_RESTART |
((DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NOUIONQUERYREMOVE)
? CM_REMOVE_UI_NOT_OK
: CM_REMOVE_UI_OK),
pDeviceInfoSet->hMachine
);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_REMOVE_TIME_DEVICE,
NULL,
DeviceFullID);
if(cr != CR_SUCCESS) {
//
// If the failure was due to a veto, then log
// information about who vetoed us.
//
// SPLOG-- write out a log entry
//
if(cr == CR_REMOVE_VETOED) {
_WriteVetoLogEntry(
LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REMOVE_VETOED_IN_INSTALL,
DeviceFullID,
VetoName,
VetoType);
}
//
// If the failure was due to the device not being there,
// then prompting for reboot isn't going to help
// anything (plus, you can't set a problem on a non-
// existent devnode anyway). This could happen, for
// example, if the user plugged in a USB mouse, then
// unplugged it again before we got a chance to
// complete the device installation. In this case,
// we'll just ignore the failure and continue on.
//
if(cr != CR_INVALID_DEVNODE) {
bNeedReboot = TRUE;
WriteLogEntry(
LogContext,
DRIVER_LOG_WARNING, // should this be a warning?
MSG_LOG_REBOOT_REASON_QR_VETOED,
NULL,
cr,
(ULONG_PTR)_MapCmRetToString(cr),
DeviceFullID);
}
}
}
//
// Don't bother stopping the rest of the drivers if we need a reboot
//
if (bNeedReboot) {
break;
}
}
if (!bNeedReboot) {
//
// Verify that all of the drivers have unloaded. If just one did not
// unload then we need to prompt for a reboot.
//
if (AnyServicesLoaded(DriverList, LoadedService, SIZECHARS(LoadedService))) {
bNeedReboot = TRUE;
WriteLogEntry(
LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REBOOT_REASON_DRIVER_LOADED,
NULL,
LoadedService);
}
}
//
// Now start up all the devices that we stopped.
//
for (i=0; i<=LastDevNodeStopped; i++) {
if(CM_Get_Device_ID(DevNodeList[i],
DeviceFullID,
SIZECHARS(DeviceFullID),
0
) != CR_SUCCESS ) {
DeviceFullID[0] = TEXT('\0');
}
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_BEGIN_RESTART_TIME_DEVICE,
NULL,
DeviceFullID);
CM_Setup_DevInst_Ex(DevNodeList[i],
CM_SETUP_DEVNODE_READY,
pDeviceInfoSet->hMachine);
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_RESTART_TIME_DEVICE,
NULL,
DeviceFullID);
}
//
// Finally make one last pass through all the devnodes and verify
// that the ones that were started initially are started now.
// If the devnode wasn't started initially then it is OK that it
// isn't started now.
//
// This last pass can be skipped if we already need a reboot, since
// it is simply a verification pass.
//
if (!bNeedReboot) {
for (i=0; i<NumberOfDevNodes; i++) {
if (DevNodeStartedList[i]) {
if(CM_Get_Device_ID(DevNodeList[i],
DeviceFullID,
SIZECHARS(DeviceFullID),
0
) != CR_SUCCESS ) {
DeviceFullID[0] = TEXT('\0');
}
if ((cr = CM_Get_DevNode_Status_Ex(&ulStatus, &ulProblem, DevNodeList[i],
0, pDeviceInfoSet->hMachine)) == CR_SUCCESS) {
if (!(ulStatus & DN_STARTED)) {
//
// This device was started before and now it isn't started,
// so a reboot is needed.
//
bNeedReboot = TRUE;
if (ulStatus & DN_HAS_PROBLEM) {
//
// The device now has a problem when it was
// working fine before. Log the problem.
//
WriteLogEntry(
LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REBOOT_REASON_DEVHASPROBLEM,
NULL,
(DWORD)ulProblem,
_MapCmProbToString((DWORD)ulProblem),
DeviceFullID);
} else if (ulStatus & DN_PRIVATE_PROBLEM) {
//
// some private problem, change into need reboot
// and log private problem
//
WriteLogEntry(
LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REBOOT_REASON_PRIVATEPROBLEM,
NULL,
NULL,
NULL,
DeviceFullID);
} else {
//
// not started for some other reason
// indicate reboot required and log this issue
//
WriteLogEntry(
LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REBOOT_REASON_NOTSTARTED,
NULL,
NULL,
NULL,
DeviceFullID);
}
}
} else {
bNeedReboot = TRUE;
WriteLogEntry(
LogContext,
DRIVER_LOG_WARNING,
MSG_LOG_REBOOT_REASON_DEVHASPROBLEM,
NULL,
cr,
(ULONG_PTR)_MapCmRetToString(cr),
DeviceFullID);
}
}
}
}
} else {
//
// If we can't get the list of drivers for some reason then just
// set the needs reboot flag.
//
bNeedReboot = TRUE;
}
if (!bNeedReboot) {
//
// At this point we need to check that the device that we just installed
// is actually started, unless it is a NULL driver install.
//
DWORD DevInstCapabilities;
ULONG cbData;
cbData = sizeof(DevInstCapabilities);
if(CR_SUCCESS != CM_Get_DevInst_Registry_Property_Ex(DevInfoElem->DevInst,
CM_DRP_CAPABILITIES,
NULL,
&DevInstCapabilities,
&cbData,
0,
pDeviceInfoSet->hMachine))
{
DevInstCapabilities = 0;
}
if(!NullDriverInstall || (DevInstCapabilities & CM_DEVCAP_RAWDEVICEOK)) {
CheckIfDevStarted(DevInfoElem,pDeviceInfoSet);
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
bNeedReboot = TRUE;
//
// Access the following variables, so that the compiler will respect our
// statement ordering w.r.t. their assignment.
//
DriverList = DriverList;
DevNodeList = DevNodeList;
DevNodeStartedList = DevNodeStartedList;
}
clean0:
if (DriverList) {
MyFree(DriverList);
}
if (DevNodeList) {
MyFree(DevNodeList);
}
if (DevNodeStartedList) {
MyFree(DevNodeStartedList);
}
if (bNeedReboot) {
CM_Set_DevInst_Problem_Ex(DevInfoElem->DevInst,
CM_PROB_NEED_RESTART,
CM_SET_DEVINST_PROBLEM_OVERRIDE,
pDeviceInfoSet->hMachine);
}
}
VOID
AppendLoadIncludedInfs(
IN HINF hDeviceInf,
IN PCTSTR InfFileName,
IN PCTSTR InfSectionName,
IN BOOL AppendLayoutInfs
)
/*++
Routine Description:
This routine processes the "include=" line in the specified section of the
specified INF. For each filename entry on this line, it attempts to append-load
that file to the supplied INF (first, from the location where the original INF
was located, and if that fails, then using the default INF search path).
Arguments:
hDeviceInf - supplies a handle to the INF containing the section specified by
InfSectionName. Upon return, this INF handle will also contain any additional
INFs that were append-loaded based on INFs listed in the "include=" entry of
the InfSectionName section.
InfFileName - supplies the full path of the INF whose handle was supplied in
the hDeviceInf parameter. The path component is used in an attempt to first
locate the specified included INFs in the same location as that of the original
INF.
InfSectionName - supplies the name of the section containing an "include=" line
whose fields are simply filenames of INFs to be append loaded to the original
INF whose handle is supplied in the hDeviceInf parameter.
AppendLayoutInfs - if non-zero (TRUE), then we will attempt to append-load the
corresponding layout INF for each included INF.
Return Value:
none.
--*/
{
TCHAR DefaultInfPath[MAX_PATH];
PTSTR FileNamePos;
INFCONTEXT InfContext;
DWORD FieldIndex;
BOOL b;
//
// Store the full directory path to where the supplied INF is located, so we
// can first attempt to append-load the included INFs from that same directory.
//
MYVERIFY(SUCCEEDED(StringCchCopy(DefaultInfPath,
SIZECHARS(DefaultInfPath),
InfFileName)));
FileNamePos = (PTSTR)pSetupGetFileTitle(DefaultInfPath);
if(SetupFindFirstLine(hDeviceInf, InfSectionName, TEXT("include"), &InfContext)) {
for(FieldIndex = 1;
SetupGetStringField(&InfContext,
FieldIndex,
FileNamePos,
(DWORD)((DefaultInfPath + SIZECHARS(DefaultInfPath)) - FileNamePos),
NULL);
FieldIndex++)
{
//
// Try full path and if that fails just use the inf name
// and let the open routine try to locate the inf.
// Ignore errors. We'll catch them later, during the install phases.
//
b = SetupOpenAppendInfFile(DefaultInfPath, hDeviceInf, NULL);
if(!b) {
b = SetupOpenAppendInfFile(FileNamePos, hDeviceInf, NULL);
}
//
// If we successfully append-loaded the included INF, and if we're also
// supposed to be append-loading any associated layout INFs, then do that
// now.
//
if(b && AppendLayoutInfs) {
SetupOpenAppendInfFile(NULL, hDeviceInf, NULL);
}
}
}
}
DWORD
InstallFromInfSectionAndNeededSections(
IN HWND Owner, OPTIONAL
IN HINF InfHandle,
IN PCTSTR SectionName,
IN UINT Flags,
IN HKEY RelativeKeyRoot, OPTIONAL
IN PCTSTR SourceRootPath, OPTIONAL
IN UINT CopyFlags,
IN PSP_FILE_CALLBACK MsgHandler,
IN PVOID Context, OPTIONAL
IN HDEVINFO DeviceInfoSet, OPTIONAL
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
IN HSPFILEQ UserFileQ OPTIONAL
)
/*++
Routine Description:
This routine calls SetupInstallFromInfSection for the specified install section,
as well as for any additional sections specified in a "needs=" line contained in
that section.
Arguments:
Same as for SetupInstallFromInfSection, except for UserFileQ. If UserFileQ is
non-NULL, then we will only install files (via pSetupInstallFiles) using this
file queue.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code
indicating the cause of the failure.
--*/
{
DWORD FieldIndex, Err;
INFCONTEXT InfContext;
BOOL NeedsEntriesToProcess;
TCHAR SectionToInstall[MAX_SECT_NAME_LEN];
MYVERIFY(SUCCEEDED(StringCchCopy(SectionToInstall,
SIZECHARS(SectionToInstall),
SectionName)));
NeedsEntriesToProcess = SetupFindFirstLine(InfHandle,
SectionName,
TEXT("needs"),
&InfContext
);
Err = NO_ERROR;
for(FieldIndex = 0; (!FieldIndex || NeedsEntriesToProcess); FieldIndex++) {
if(FieldIndex) {
//
// Get next section name on "needs=" line to be processed.
//
if(!SetupGetStringField(&InfContext,
FieldIndex,
SectionToInstall,
SIZECHARS(SectionToInstall),
NULL)) {
//
// We've exhausted all the extra sections we needed to install.
//
break;
}
}
if(UserFileQ) {
//
// The caller supplied their own file queue, so all we can do is copy
// files. Make sure that file copying is all they want, and that they
// gave us a valid file queue handle.
//
MYASSERT(Flags == SPINST_FILES);
MYASSERT(UserFileQ != INVALID_HANDLE_VALUE);
Err = pSetupInstallFiles(InfHandle,
NULL,
SectionToInstall,
SourceRootPath,
MsgHandler,
Context,
CopyFlags,
Owner,
UserFileQ,
TRUE
);
} else {
//
// The caller didn't supply their own file queue, so we can just use
// SetupInstallFromInfSection.
//
if(!_SetupInstallFromInfSection(Owner,
InfHandle,
SectionToInstall,
Flags,
RelativeKeyRoot,
SourceRootPath,
CopyFlags,
MsgHandler,
Context,
DeviceInfoSet,
DeviceInfoData,
TRUE,
NULL)) {
Err = GetLastError();
break;
}
}
}
return Err;
}
DWORD
pSetupCopyRelatedInfs(
IN HINF hInf,
IN PCTSTR InfFileName, OPTIONAL
IN PCTSTR InfSectionName,
IN DWORD OEMSourceMediaType,
IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
)
/*++
Routine Description:
This routine processes "CopyINF" directives in the DDInstall section of the
primary INF.
CopyINF is not processed under following cases:
Primary INF is in driver search path
The CopyINF is specified in an included section.
Arguments:
hInf - supplies a handle to the INF containing the section specified by
InfSectionName.
InfFileName - supplies the full path of the INF whose handle was supplied in
the hDeviceInf parameter. The path component is used in an attempt to
locate the specified INFs. If not specified, we try to get this from hInf
InfSectionName - supplies the name of the section containing "CopyINF=" lines.
OEMSourceMediaType - Specifies the type of source media that the location
information references. May be one of the following values:
SPOST_NONE - No source media information should be stored in the PNF
file. (OEMSourceMediaLocation is ignored in this case.)
SPOST_PATH - OEMSourceMediaLocation contains a path to the source media.
For example, if the media is on a floppy, this path might
be "A:\". If OEMSourceMediaLocation is NULL, then the path
is assumed to be the path where the INF is located (unless
the INF has a corresponding PNF in that location, in which
case that PNF's source media information will be
transferred to the destination PNF).
SPOST_URL - OEMSourceMediaLocation contains a URL indicating the
internet location where the INF/driver files were retrieved
from. If OEMSourceMediaLocation is NULL, then it is
assumed that the default Code Download Manager location was
used.
LogContext - used for logging. If not specified, we get this from hInf
Return Value:
none.
--*/
{
INFCONTEXT CopyInfLineContext;
TCHAR CombinedPath[MAX_PATH*2];
TCHAR FullSourcePath[MAX_PATH];
PTSTR Title;
PCTSTR Field;
PCTSTR SourcePath;
DWORD Status;
DWORD FieldCount;
DWORD FieldIndex;
DWORD RetVal;
PLOADED_INF pInf = NULL;
if(hInf == NULL || InfSectionName == NULL) {
return ERROR_INVALID_PARAMETER;
}
pInf = (PLOADED_INF)hInf;
if(!LockInf(pInf)) {
return ERROR_INVALID_HANDLE;
}
if(!LogContext) {
LogContext = pInf->LogContext;
}
if(!InfFileName) {
InfFileName = pInf->VersionBlock.Filename;
}
//
// if primary INF is from OEM location, then process CopyINF directives
// note that if the INF is in search path, we ignore CopyINF
// we also ignore CopyINF's pulled in from other INF's (ie, don't process
// Include/Needs).
//
if(!pSetupInfIsFromOemLocation(InfFileName, FALSE)) {
//
// Primary INF is on search path, ignore CopyINF directives
//
Status = NO_ERROR;
goto final;
}
MYVERIFY(SUCCEEDED(StringCchCopy(CombinedPath,MAX_PATH,InfFileName)));
Title = (PTSTR)pSetupGetFileTitle(CombinedPath);
if (!Title) {
//
// shouldn't happen
//
MYASSERT(Title);
Status = ERROR_INVALID_PARAMETER;
goto final;
}
//
// we look for keyword "CopyInf" in the install section
//
if (SetupFindFirstLine(hInf,
InfSectionName,
SZ_KEY_COPYINF,
&CopyInfLineContext)) {
PSETUP_LOG_CONTEXT SavedLogContext = NULL;
BOOL ChangedThreadLogContext = SetThreadLogContext(LogContext,&SavedLogContext);
//
// we've pushed log context so SetupCopyOemInf will log correctly
//
do {
//
// CopyInf = a.inf,b.inf,c.inf...
// a.inf will be at index 1.
//
FieldCount = SetupGetFieldCount(&CopyInfLineContext);
for(FieldIndex = 1;FieldIndex<=FieldCount;FieldIndex++) {
Status = NO_ERROR;
Field = pSetupGetField(&CopyInfLineContext,FieldIndex);
MYVERIFY(SUCCEEDED(StringCchCopy(Title,
SIZECHARS(CombinedPath)-(Title-CombinedPath),
Field)));
//
// we have a listed INF, obtain canonical pathname
// for this INF
// (note that we assume the INF is relative
// to original INF and is typically just a filename)
//
RetVal = GetFullPathName(CombinedPath,
MAX_PATH,
FullSourcePath,
NULL);
if (RetVal == 0 || RetVal > MAX_PATH) {
SourcePath = CombinedPath;
} else {
SourcePath = FullSourcePath;
}
if(!SetupCopyOEMInf(FullSourcePath,
NULL,
OEMSourceMediaType,
0,
NULL,
0,
NULL,
NULL)) {
Status = GetLastError();
}
if (Status != NO_ERROR) {
WriteLogEntry(
LogContext,
DRIVER_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_COPYINF_ERROR,
NULL,
FullSourcePath);
WriteLogError(
LogContext,
DRIVER_LOG_ERROR,
Status);
} else {
WriteLogEntry(
LogContext,
DRIVER_LOG_INFO,
MSG_LOG_COPYINF_OK,
NULL,
FullSourcePath);
}
}
} while (SetupFindNextMatchLine(&CopyInfLineContext,
SZ_KEY_COPYINF,
&CopyInfLineContext));
if (ChangedThreadLogContext) {
//
// restore thread log context
//
SetThreadLogContext(SavedLogContext,NULL);
}
}
Status = NO_ERROR;
final:
if(pInf) {
UnlockInf(pInf);
}
return Status;
}
VOID
SetDevnodeNeedsRebootProblemWithArg2(
IN PDEVINFO_ELEM DevInfoElem,
IN PDEVICE_INFO_SET pDevInfoSet,
IN DWORD Reason, OPTIONAL
IN ULONG_PTR Arg1, OPTIONAL
IN ULONG_PTR Arg2 OPTIONAL
)
/*++
Routine Description:
This routine sets DI_NEEDREBOOT,
sets the problem of the specified devnode to be CM_PROB_NEED_RESTART
and logs information about why we did this
Arguments:
DevInfoElem )_ identify the devnode with the problem
DevInfoSet )
Reason - Supplies a string resource ID for logging the reason a reboot is
required. If this is 0, nothing will be logged.
Arg1/Arg2 - optional arguments %1 and %2 of the logged message
Return Value:
none.
--*/
{
TCHAR szDevID[MAX_DEVICE_ID_LEN];
CONFIGRET cr;
DevInfoElem->InstallParamBlock.Flags |= DI_NEEDREBOOT;
if((cr = CM_Get_Device_ID_Ex(DevInfoElem->DevInst,
szDevID,
SIZECHARS(szDevID),
0,
pDevInfoSet->hMachine)) != CR_SUCCESS) {
szDevID[0] = TEXT('\0');
}
CM_Set_DevInst_Problem_Ex(DevInfoElem->DevInst,
CM_PROB_NEED_RESTART,
CM_SET_DEVINST_PROBLEM_OVERRIDE,
pDevInfoSet->hMachine);
if (Reason) {
//
// if the caller gave a reason, log why a reboot is required
//
WriteLogEntry(
DevInfoElem->InstallParamBlock.LogContext,
DRIVER_LOG_WARNING, // should this be a warning?
Reason,
NULL,
Arg1,
Arg2,
szDevID);
}
}
DWORD
MarkQueueForDeviceInstall(
IN HSPFILEQ QueueHandle,
IN HINF DeviceInfHandle,
IN PCTSTR DeviceDesc OPTIONAL
)
/*++
Routine Description:
This routine adds catalog info entries (if not already present) into a file
queue for the INFs represented by the supplied INF handle. It also marks
the first INF's catalog entry (whether newly-created or not) with a flag
indicating that this is the 'primary' device INF for this installation. It
then sets a flag in the queue indicating that the behavior when subsequently
calling _SetupVerifyQueuedCatalogs should be to copy the INF into the
%windir%\Inf directory (as opposed to merely creating a zero-length file
there of the correct name so that the catfile's name is guaranteed unique).
Finally, it retrieves the driver signing policy and associates it with the
queue (up until this point, the queue has associated with it the non-driver
signing policy).
Arguments:
QueueHandle - supplies a handle to the file queue to be marked as a device
install file queue.
DeviceInfHandle - supplies a handle to the device INF upon which this
installation is based. In addition to the "primary" device INF, this
handle may also contain one or more append-loaded INFs.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error
code indicating the cause of the failure.
--*/
{
PSP_FILE_QUEUE Queue;
TCHAR TempBuffer[MAX_PATH];
LONG InfStringId, CatStringId, OriginalInfStringId;
PLOADED_INF pInf;
DWORD Err;
PTSTR InfDeviceDesc;
PSPQ_CATALOG_INFO CatalogNode, PrevCatalogNode, NewCatalogNode;
BOOL DifferentOriginalName;
TCHAR OriginalInfName[MAX_PATH];
BOOL UnlockInf;
BOOL UseOriginalInfName;
//
// Queue handle is actually a pointer to the queue structure.
//
Queue = (PSP_FILE_QUEUE)QueueHandle;
NewCatalogNode = NULL;
Err = NO_ERROR;
InfDeviceDesc = NULL;
UnlockInf = FALSE;
try {
if(LockInf((PLOADED_INF)DeviceInfHandle)) {
UnlockInf = TRUE;
} else {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
//
// We want to process each INF in the LOADED_INF list...
//
for(pInf = (PLOADED_INF)DeviceInfHandle; pInf; pInf = pInf->Next) {
//
// First, get the (potentially decorated) CatalogFile= entry from
// the version block of this INF member, as well as the INF's
// original name (if different from the INF's current name).
//
Err = pGetInfOriginalNameAndCatalogFile(
pInf,
NULL,
&DifferentOriginalName,
OriginalInfName,
SIZECHARS(OriginalInfName),
TempBuffer,
SIZECHARS(TempBuffer),
NULL // always native OS/arch (ver doesn't matter for CatalogFile=)
);
if(Err != NO_ERROR) {
goto clean0;
}
if(DifferentOriginalName) {
//
// Add the INF's original (simple) filename to our string table.
//
OriginalInfStringId = pSetupStringTableAddString(
Queue->StringTable,
OriginalInfName,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(OriginalInfStringId == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
} else {
//
// INF's original name is the same as its present name.
//
OriginalInfStringId = -1;
}
if(*TempBuffer) {
CatStringId = pSetupStringTableAddString(Queue->StringTable,
TempBuffer,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(CatStringId == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
} else {
//
// This INF doesn't have a CatalogFile= entry.
//
CatStringId = -1;
}
//
// Now, get the INF's full path.
//
MYVERIFY(SUCCEEDED(StringCchCopy(TempBuffer,
SIZECHARS(TempBuffer),
pInf->VersionBlock.Filename)));
InfStringId = pSetupStringTableAddString(Queue->StringTable,
TempBuffer,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(InfStringId == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Now search for an existing catalog node (there'll typically be
// one unless the device INF doesn't copy any files, such as a
// modem INF).
//
for(PrevCatalogNode=NULL, CatalogNode=Queue->CatalogList;
CatalogNode;
CatalogNode=CatalogNode->Next) {
if(CatalogNode->InfFullPath == InfStringId) {
//
// Already in there. No need to create a new node.
// Break out here, with CatalogNode pointing at the
// proper node for this catalog file.
//
// In this case, PrevCatalogNode should not be used later,
// but it shouldn't need to be used, since we won't be
// adding anything new onto the list of catalog nodes.
//
// NOTE: Our alternate catalog should be correct because
// either (a) the altplatform info was in effect when the
// INF was added, or (b) the altplatform info was applied
// after the INF's addition, and it was updated at that
// time.
//
MYASSERT(CatalogNode->CatalogFileFromInf == CatStringId);
MYASSERT(CatalogNode->InfOriginalName == OriginalInfStringId);
break;
}
//
// PrevCatalogNode will end up pointing to the final node
// currently in the linked list, in the case where we need
// to allocate a new node. This is useful so we don't have to
// traverse the list again later when we add the new catalog
// node to the list for this queue.
//
PrevCatalogNode = CatalogNode;
}
//
// If we didn't find an existing catalog node, then add one now.
//
if(!CatalogNode) {
//
// Need to create a new catalog node.
//
NewCatalogNode = MyMalloc(sizeof(SPQ_CATALOG_INFO));
if(!NewCatalogNode) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
ZeroMemory(NewCatalogNode, sizeof(SPQ_CATALOG_INFO));
NewCatalogNode->CatalogFileFromInf = CatStringId;
NewCatalogNode->InfOriginalName = OriginalInfStringId;
NewCatalogNode->InfFullPath = InfStringId;
NewCatalogNode->InfFinalPath = -1;
NewCatalogNode->AltCatalogFileFromInfPending = -1;
//
// If this queue has alternate platform info associated with
// it, then we need to retrieve the alternate catalog to be
// used for digital signature verification.
//
if(Queue->Flags & FQF_USE_ALT_PLATFORM) {
if(pSetupGetCatalogFileValue(&(pInf->VersionBlock),
TempBuffer,
SIZECHARS(TempBuffer),
&(Queue->AltPlatformInfo))) {
NewCatalogNode->AltCatalogFileFromInf =
pSetupStringTableAddString(Queue->StringTable,
TempBuffer,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(NewCatalogNode->AltCatalogFileFromInf == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
} else {
//
// This INF doesn't have a CatalogFile= entry.
//
NewCatalogNode->AltCatalogFileFromInf = -1;
}
} else {
//
// No alternate platform info associated with the queue at
// this time.
//
NewCatalogNode->AltCatalogFileFromInf = -1;
}
if(Queue->CatalogList) {
PrevCatalogNode->Next = NewCatalogNode;
} else {
Queue->CatalogList = NewCatalogNode;
}
//
// Reset NewCatalogNode so we won't try to free it in case we
// encounter a subsequent error.
//
CatalogNode = NewCatalogNode;
NewCatalogNode = NULL;
//
// We've successfully added a new, as yet unvalidated, catalog
// node. We must therefore reset the the "catalog
// verifications done" flags so that we'll redo them later.
//
Queue->Flags &= ~(FQF_DID_CATALOGS_OK | FQF_DID_CATALOGS_FAILED);
}
if(pInf == (PLOADED_INF)DeviceInfHandle) {
//
// At this point, CatalogNode points to the node representing
// the device INF upon which this device installation is based.
// Mark this node as such, so that _SetupVerifyQueuedCatalogs
// can return the INF's new name if it's an OEM INF.
//
CatalogNode->Flags |= CATINFO_FLAG_PRIMARY_DEVICE_INF;
}
}
if(!(Queue->Flags & FQF_DEVICE_INSTALL)) {
//
// This queue wasn't previously known to be a device install queue,
// so it's possible the catalog nodes have already been verified
// subject to non-driver signing policy. Further, it's possible
// (albeit unlikely) that a non-device INF was passed into this
// routine, and that a catalog node for that INF already existed in
// our catalog list. In that case, one of our "catalog
// verifications done" flags would be set, but would not have been
// cleared above since we didn't add any catalog nodes. All of
// this discussion simply to justify that we really do need to
// clear the bits here too... :-(
//
Queue->Flags &= ~(FQF_DID_CATALOGS_OK | FQF_DID_CATALOGS_FAILED);
//
// Note: We don't clear the CATINFO_FLAG_NEWLY_COPIED flags for any
// of the catalog nodes, because we want to make sure we re-use
// those same names when re-doing verification (e.g., maybe the
// first time around the INF was installed into %windir%\Inf as a
// zero-length file--we want to re-use the same filename to now hold
// our INF).
//
//
// Since we previously didn't know this was a device install queue,
// we shouldn't have a ValidationPlatform. Make sure that's the
// case, because the call below would blow away any we had (thus
// causing a memory leak).
//
MYASSERT(!(Queue->ValidationPlatform));
//
// Retrieve the codesigning policy in effect for this device (thus
// replacing the default non-driver signing policy that was
// associated with the queue when it was originally created).
//
IsInfForDeviceInstall(Queue->LogContext,
NULL,
(PLOADED_INF)DeviceInfHandle,
(DeviceDesc ? NULL : &InfDeviceDesc),
&(Queue->ValidationPlatform),
&(Queue->DriverSigningPolicy),
&UseOriginalInfName,
FALSE
);
if(UseOriginalInfName) {
Queue->Flags |= FQF_KEEP_INF_AND_CAT_ORIGINAL_NAMES;
}
//
// If the caller supplied us with a device description (or we got
// one from IsInfForDeviceInstall), attempt to add that string to
// the queue's string table in case we need to give a digital
// signature verification failure popup for this queue.
//
//
// NOTE: When adding the following string to the string table, we
// cast away its CONST-ness to avoid a compiler warning. Since we
// are adding it case-sensitively, we are guaranteed it will not be
// modified.
//
if(DeviceDesc) {
Queue->DeviceDescStringId = pSetupStringTableAddString(Queue->StringTable,
(PTSTR)DeviceDesc,
STRTAB_CASE_SENSITIVE
);
} else if(InfDeviceDesc) {
//
// Use the more generic description based on the device's class
//
Queue->DeviceDescStringId = pSetupStringTableAddString(Queue->StringTable,
InfDeviceDesc,
STRTAB_CASE_SENSITIVE
);
}
//
// Set a flag in the queue that indicates this is for a device
// installation. If we're doing a native platform installation
// (i.e., the FQF_USE_ALT_PLATFORM isn't set), then this also
// causes us to copy the INF into %windir%\Inf instead of merely
// creating a zero-length placeholder file there upon which the
// corresponding CAT file's installation is based.
//
Queue->Flags |= FQF_DEVICE_INSTALL;
} else {
//
// This queue has previously been marked as a device install queue.
// However, we still want to update the device description, if the
// caller supplied one.
//
if(DeviceDesc) {
Queue->DeviceDescStringId = pSetupStringTableAddString(Queue->StringTable,
(PTSTR)DeviceDesc,
STRTAB_CASE_SENSITIVE
);
}
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If we hit an AV, then use invalid parameter error, otherwise, assume
// an inpage error when dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
? ERROR_INVALID_PARAMETER
: ERROR_READ_FAULT;
//
// Access the following variables, so that the compiler will respect our
// statement ordering w.r.t. their assignment.
//
UnlockInf = UnlockInf;
NewCatalogNode = NewCatalogNode;
}
if(UnlockInf) {
UnlockInf((PLOADED_INF)DeviceInfHandle);
}
if(NewCatalogNode) {
MyFree(NewCatalogNode);
}
if(InfDeviceDesc) {
MyFree(InfDeviceDesc);
}
return Err;
}