windows-nt/Source/XPSP1/NT/base/ntsetup/opktools/lib/msdinst/setupcdd.c
2020-09-26 16:20:57 +08:00

1447 lines
47 KiB
C

/****************************************************************************\
SYSPREP.C / Mass Storage Device Installer (MSDINST.LIB)
Microsoft Confidential
Copyright (c) Microsoft Corporation 2001
All rights reserved
Source file the MSD Installation library which contains the sysprep
releated code taken from the published sysprep code.
07/2001 - Jason Cohen (JCOHEN)
Added this new source file for the new MSD Installation project.
\****************************************************************************/
//
// Include File(s):
//
#include "pch.h"
#include <winbom.h>
#include "main.h"
//
// Local Define(s):
//
#define SYSPREP_DEVNODE _T("SYSPREP_TEMPORARY")
#define STR_SERVICE_MAIN _T("Service")
#define STR_SERVICE_UPPER _T("UpperFilter")
#define STR_SERVICE_LOWER _T("LowerFilter")
#define STR_SERVICES_SECTION _T(".Services")
#define NUM_SERVICE_MAIN 0
#define NUM_SERVICE_UPPER 1
#define NUM_SERVICE_LOWER 2
#define DIR_I386 _T("i386")
#define DIR_IA64 _T("ia64")
#define REG_KEY_HIVE_CDD _T("ControlSet001\\Control\\CriticalDeviceDatabase")
#define REG_KEY_HIVE_SETUP_SETUP _T("Microsoft\\Windows\\CurrentVersion\\Setup")
#define REG_KEY_SETUP_SETUP REGSTR_PATH_SETUP REGSTR_KEY_SETUP
#define STR_SYSPREP_INF _T("sysprep\\sysprep.inf")
//
// Local Type Define(s):
//
typedef struct _CLEANUP_NODE
{
LPTSTR lpszService;
DWORD dwType;
struct _CLEANUP_NODE * lpNext;
}
CLEANUP_NODE, *PCLEANUP_NODE, *LPCLEANUP_NODE;
//
// Local Global(s):
//
static LPTSTR s_lpszServiceType[] =
{
STR_SERVICE_MAIN,
STR_SERVICE_UPPER,
STR_SERVICE_LOWER,
};
//
// Local Prototype(s):
//
static BOOL SysprepDevnode(HDEVINFO * phDevInfo, SP_DEVINFO_DATA * pDeviceInfoData, BOOL bCreate);
static BOOL
GetDeviceInstallSection(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN PSP_DRVINFO_DATA pDriverInfoData,
OUT LPTSTR lpszSection,
IN DWORD cbSection
);
static BOOL
GetDeviceServicesSection(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN PSP_DRVINFO_DATA pDriverInfoData,
OUT LPTSTR lpszSection,
IN DWORD cbSection
);
static BOOL
ProcessDeviceProperty(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN LPCLEANUP_NODE * lplpcnList,
IN HKEY hkeyDevice,
IN HKEY hkeySystem,
IN LPTSTR lpszInfPath,
IN LPTSTR lpszServiceSection,
IN DWORD dwProperty
);
static BOOL AddCleanup(LPCLEANUP_NODE * lplpcnHead, LPTSTR lpszService, DWORD dwType);
static LPCLEANUP_NODE OpenCleanup(LPTSTR lpszInfFile);
static BOOL SaveCleanup(LPTSTR lpszInfFile, LPCLEANUP_NODE lpcnHead);
static void CloseCleanup(LPCLEANUP_NODE lpcnHead);
static BOOL AddStrToSect(LPTSTR * lplpmszSect, DWORD * lpcbSect, LPTSTR * lplpmszEnd, DWORD * lpdwSize, LPTSTR lpszStr);
static BOOL
OfflineSourcePath(
HKEY hkeySoftware,
LPTSTR lpszWindows,
LPTSTR lpszSourcePath,
DWORD cbSourcePath
);
//
// Exported Funtion(s):
//
BOOL
SetupCriticalDevices(
LPTSTR lpszInfFile,
HKEY hkeySoftware,
HKEY hkeySystem,
LPTSTR lpszWindows
)
/*++
===============================================================================
Routine Description:
Parse the [SysprepMassStorage] section in the sysprep.inf file and
populate the critical device database with the specified devices to ensure
that we can boot into the miniwizard when moving the image to a target
system with different boot storage devices.
The installed services/upperfilters/lowerfilters will be recorded, so
that on the next boot into the mini-wizard those without an associated
device will be disabled (the cleanup stage) in order not to unnecessarily
degrade Windows start time.
Arguments:
None.
Return Value:
TRUE if everything is OK, FALSE otherwise.
Assumptions:
1. No HardwareID exceeds MAX_PATH characters.
2. No field on a line in the [SysprepMassStorage] section exceeds MAX_PATH
characters.
3. No service's/upperfilter's/lowerfilter's name exceeds MAX_PATH characters.
4. DirectoryOnSourceDevice, source DiskDescription, or source DiskTag
(applying to vendor-supplied drivers) cannot exceed MAX_PATH characters.
===============================================================================
--*/
{
TCHAR szSysprepInfFile[MAX_PATH] = NULLSTR,
szDevice[MAX_PATH],
szSection[MAX_PATH],
szPath[MAX_PATH],
szSourcePath[MAX_PATH];
DWORD dwSize,
dwDis;
LPTSTR lpszCleanupInfFile,
lpszReplace,
lpszSourcePath = NULL;
BOOL bDevnode = TRUE,
bAllOK = TRUE,
bLineExists,
b2ndTry;
HKEY hkeyCDD = NULL,
hkeyDevice = NULL;
LPCLEANUP_NODE lpcnCleanupList;
HINF hInf;
INFCONTEXT InfContext;
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA DeviceInfoData;
SP_DEVINSTALL_PARAMS DevInstallParams;
SP_DRVINFO_DATA DriverInfoData;
HSPFILEQ QueueHandle = INVALID_HANDLE_VALUE;
// Do a little parameter validation.
//
if ( ( NULL == hkeySoftware ) ||
( NULL == hkeySystem ) ||
( NULL == lpszWindows ) )
{
// If any of these parameters are NULL, then they
// all must be.
//
hkeySoftware = NULL;
hkeySystem = NULL;
lpszWindows = NULL;
}
// Open the inf file with the mass storage list.
//
hInf = SetupOpenInfFile(lpszInfFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
if ( INVALID_HANDLE_VALUE == hInf )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_OPEN_INF, lpszInfFile);
return FALSE;
}
// If this is offline, we write the cleanup section to the sysprep inf
// that is in the image.
//
if ( lpszWindows )
{
LPTSTR lpFound;
// Strip the windows directory off the offline path so we can build a path to the sysprep.inf.
//
lstrcpy(szSysprepInfFile, lpszWindows);
lpFound = _tcsrchr(szSysprepInfFile, _T('\\'));
// Just in case the lpszWindows folder has a trailing backslash handle that here.
// If this is the case, the character after the backslash is a NULL. Remove the trailing backslash,
// and do the search for the last backslash again. This will be the one we actually want to
// get rid of.
//
if ( !(*(lpFound + 1)) )
{
*lpFound = NULLCHR;
lpFound = _tcsrchr(szSysprepInfFile, _T('\\'));
}
// Cut off the path in front of the windows directory name.
// Add the sysprep.inf path part.
// Set our cleanup file to point to the path we just built.
//
*lpFound = NULLCHR;
AddPathN(szSysprepInfFile, STR_SYSPREP_INF, AS(szSysprepInfFile));
lpszCleanupInfFile = szSysprepInfFile;
}
else
{
lpszCleanupInfFile = lpszInfFile;
}
// If this is an offline install, then we need to get the source path
// to our image.
//
if ( hkeySoftware && lpszWindows &&
OfflineSourcePath(hkeySoftware, lpszWindows, szSourcePath, AS(szSourcePath)) )
{
lpszSourcePath = szSourcePath;
}
// We need a handle to the critical device database registry key if we
// are doing an offline install.
//
if ( ( hkeySystem ) &&
( ERROR_SUCCESS != RegCreateKeyEx(hkeySystem,
REG_KEY_HIVE_CDD,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hkeyCDD,
&dwDis) ) )
{
SetupCloseInfFile(hInf);
OpkLogFile(0 | LOG_ERR, IDS_ERR_OPEN_OFFLINECDD);
return FALSE;
}
// Create a dummy devnode.
//
if ( !SysprepDevnode(&hDevInfo, &DeviceInfoData, TRUE) )
{
if ( hkeyCDD )
{
RegCloseKey(hkeyCDD);
}
SetupCloseInfFile(hInf);
OpkLogFile(0 | LOG_ERR, IDS_ERR_CREATE_DEVNODE);
return FALSE;
}
// Init the driver info data structure.
//
ZeroMemory(&DriverInfoData, sizeof(SP_DRVINFO_DATA));
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
// Read the current cleanup section in the inf file.
//
lpcnCleanupList = OpenCleanup(lpszCleanupInfFile);
// Process each line in our section. Each line should look like:
// <hardware-id>=<inf pathname>
//
// Or in the case of drivers that aren't on the product CD:
// <hardware-id>=<inf pathname>,<directory on recovery floppy>,<description of recovery floppy>,<disk tag of recovery floppy>
//
// If we see an entry like this, we'll know that in the case of system recovery, the
// file should be retrived from a floppy, and not the Windows CD.
//
for ( bLineExists = SetupFindFirstLine(hInf, INI_SEC_WBOM_SYSPREP_MSD, NULL, &InfContext);
bLineExists;
bLineExists = SetupFindNextLine(&InfContext, &InfContext) )
{
// Retrieve the hardwareID from the line.
//
dwSize = AS(szDevice);
if ( !SetupGetStringField(&InfContext, 0, szDevice, dwSize, &dwSize) )
{
bAllOK = FALSE;
continue;
}
// We do this in a loop because we might try twice.
//
b2ndTry = FALSE;
do
{
// And then set it to the devnode.
//
if ( !SetupDiSetDeviceRegistryProperty(hDevInfo,
&DeviceInfoData,
SPDRP_HARDWAREID,
(LPBYTE) szDevice,
(lstrlen(szDevice)+1) * sizeof(TCHAR)) )
{
// If someone removed the devnode, we need to re-create it and repeat this set.
//
if ( ( !b2ndTry ) &&
( ERROR_NO_SUCH_DEVINST == GetLastError() ) )
{
// Sometimes devices remove the devnode after we install, so we should
// try once to recreate.
//
if ( SysprepDevnode(&hDevInfo, &DeviceInfoData, TRUE) )
{
// If we were able to recreate it, then we should try this again.
//
b2ndTry = TRUE;
}
else
{
// We are really screwed if we have no devnode.
//
bDevnode = FALSE;
}
}
else
{
// Either we tried again already, or there is another error.
//
bAllOK = b2ndTry = FALSE;
}
}
else
{
// It worked, so make sure we don't loop again in case this is the second time
// through.
//
b2ndTry = FALSE;
}
}
while ( b2ndTry );
// If we the devnode was lost and unable to be recreated, then we just have
// to bail out.
//
if ( !bDevnode )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_CREATE_DEVNODE);
break;
}
// Build the SP_DEVINSTALL_PARAMS for this node.
//
DevInstallParams.cbSize = sizeof(DevInstallParams);
if ( !SetupDiGetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams) )
{
bAllOK = FALSE;
continue;
}
// Set the Flags field: only search the INF file specified in DriverPath field;
// don't create a copy queue, use the provided one in FileQueue; don't call the
// Configuration Manager while populating the CriticalDeviceDatabase.
//
DevInstallParams.Flags |= ( DI_ENUMSINGLEINF |
DI_NOVCP |
DI_DONOTCALLCONFIGMG );
// Set the device's inf pathname
//
dwSize = AS(szPath);
if ( !SetupGetStringField(&InfContext, 1, szPath, dwSize, &dwSize) )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_GET_INF_NAME);
bAllOK = FALSE;
continue;
}
ExpandEnvironmentStrings(szPath, DevInstallParams.DriverPath, AS(DevInstallParams.DriverPath));
lstrcpyn(szPath, DevInstallParams.DriverPath, AS(szPath));
// Replace the backslashes with pounds in the pnp id so we can use it for the registry key.
//
for ( lpszReplace = szDevice; *lpszReplace; lpszReplace = CharNext(lpszReplace) )
{
if ( _T('\\') == *lpszReplace )
{
*lpszReplace = _T('#');
}
}
// Set the file queue field
//
QueueHandle = SetupOpenFileQueue();
if ( INVALID_HANDLE_VALUE == QueueHandle )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_OPEN_FILE_QUEUE);
bAllOK = FALSE;
continue;
}
DevInstallParams.FileQueue = QueueHandle;
// 1. Save the parameters we have set.
// 2. Register the newly created device instance with the PnP Manager.
// 3. Perform a compatible driver search.
//
if ( ( SetupDiSetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams) ) &&
( SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDevInfo, &DeviceInfoData) ) &&
( SetupDiBuildDriverInfoList(hDevInfo, &DeviceInfoData, SPDIT_COMPATDRIVER) ) )
{
// Make sure there is at least 1 compat driver for this device.
// If there is not, and then we just process the next one in the list.
//
if ( SetupDiEnumDriverInfo(hDevInfo,
&DeviceInfoData,
SPDIT_COMPATDRIVER,
0,
&DriverInfoData) )
{
// 1. Select the best compatible driver.
// 2. Install the driver files.
// 3. Make sure we are able to create the drivers key in the CDD if
// we are doing an offline install (otherwise the CDD key will be
// NULL and we don't have to worry about creating the key).
//
if ( ( SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV, hDevInfo, &DeviceInfoData) ) &&
( SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, hDevInfo, &DeviceInfoData) ) &&
( ( NULL == hkeyCDD ) ||
( ERROR_SUCCESS == RegCreateKeyEx(hkeyCDD, szDevice, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyDevice, &dwDis) ) ) )
{
// Need to commit the file queue here, so the later steps can properly
// be executed in case the device doesn't use the already existing
// coinstaller(s).
//
// ACOSMA code here...
//
if ( !OfflineCommitFileQueue(QueueHandle, szPath, lpszSourcePath, lpszWindows) )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_COMMIT_OFFLINE_QUEUE);
bAllOK = FALSE;
}
// Install the device (do we really need to do this for the offline case).
//
#if 0
if ( SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
hDevInfo,
&DeviceInfoData) )
#else
SetupDiInstallDevice(hDevInfo,
&DeviceInfoData);
#endif
{
//
// Retrieve class guid (if offline) upper filters, lower filters,
// and controlling service, save them back to the inf file and put
// them in the hive (if offline).
//
// Retrieve the device class GUID (only needed for offline install).
//
if ( ( hkeyDevice ) &&
( !ProcessDeviceProperty(hDevInfo,
&DeviceInfoData,
&lpcnCleanupList,
hkeyDevice,
hkeySystem,
szPath,
NULL,
SPDRP_CLASSGUID) ) )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_CLASS_GUID);
bAllOK = FALSE;
}
if ( !GetDeviceServicesSection(hDevInfo, &DeviceInfoData, &DriverInfoData, szSection, AS(szSection)) )
{
szSection[0] = NULLCHR;
bAllOK = FALSE;
}
// Retrieve device upper filters (REG_MULTI_SZ).
//
if ( !ProcessDeviceProperty(hDevInfo,
&DeviceInfoData,
&lpcnCleanupList,
hkeyDevice,
hkeySystem,
szPath,
szSection[0] ? szSection : NULL,
SPDRP_UPPERFILTERS) )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_UPPER_FILTERS);
bAllOK = FALSE;
}
// Retrieve device lower filters (REG_MULTI_SZ).
//
if ( !ProcessDeviceProperty(hDevInfo,
&DeviceInfoData,
&lpcnCleanupList,
hkeyDevice,
hkeySystem,
szPath,
szSection[0] ? szSection : NULL,
SPDRP_LOWERFILTERS) )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_LOWER_FILTERS);
bAllOK = FALSE;
}
// Retrieve device its controlling service (REG_SZ).
//
if ( !ProcessDeviceProperty(hDevInfo,
&DeviceInfoData,
&lpcnCleanupList,
hkeyDevice,
hkeySystem,
szPath,
szSection[0] ? szSection : NULL,
SPDRP_SERVICE) )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_DEVICE_SERVICE);
bAllOK = FALSE;
}
}
// Close the device registry key in the CDD.
//
if ( hkeyDevice )
{
RegCloseKey(hkeyDevice);
hkeyDevice = NULL;
}
}
else
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_SELECT_COMPAT);
bAllOK = FALSE;
}
}
else
{
// Check to see what the error was. Any error other than ERROR_NO_MORE_ITEMS
// will be flaged, by setting the bAllOK return value to FALSE.
//
if ( ERROR_NO_MORE_ITEMS != GetLastError() )
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_ENUM_COMPAT_DRIVER);
bAllOK = FALSE;
}
}
// Make sure that there's no existing compatible list, since we're reusing
// the dummy devnode.
//
if ( !SetupDiDestroyDriverInfoList(hDevInfo, &DeviceInfoData, SPDIT_COMPATDRIVER) )
{
bAllOK = FALSE;
}
}
else
{
OpkLogFile(0 | LOG_ERR, IDS_ERR_BUILD_COMPAT_DRIVER);
bAllOK = FALSE;
}
// Dis-associate file copy queue before we close the queue.
//
DevInstallParams.cbSize = sizeof(DevInstallParams);
if ( SetupDiGetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams) )
{
// Remove the DI_NOVCP flag and NULL out the FileQueue.
//
DevInstallParams.Flags &= ~DI_NOVCP;
DevInstallParams.FileQueue = NULL;
if ( !SetupDiSetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams) )
{
bAllOK = FALSE;
}
}
else
{
bAllOK = FALSE;
}
// Close the file queue.
//
SetupCloseFileQueue(QueueHandle);
}
// See if we still have the devnode.
//
if ( bDevnode )
{
// Remove the SYSPREP_TEMPORARY node under Root.
//
SysprepDevnode(&hDevInfo, &DeviceInfoData, FALSE);
}
else
{
// If the devnode is lost, we need to make sure we return an error.
//
bAllOK = FALSE;
}
// If an offline install, we need to close this key.
//
if ( hkeyCDD )
{
RegCloseKey(hkeyCDD);
}
// Close the handles to our inf files.
//
SetupCloseInfFile(hInf);
//
// Check if the caller wants us to update the offline device path...
//
UpdateOfflineDevicePath( lpszInfFile, hkeySoftware );
// We need to save our cleanup list back to the inf file.
//
SaveCleanup(lpszCleanupInfFile, lpcnCleanupList);
CloseCleanup(lpcnCleanupList);
return bAllOK;
}
//
// Local Function(s):
//
static BOOL SysprepDevnode(HDEVINFO * phDevInfo, SP_DEVINFO_DATA * pDeviceInfoData, BOOL bCreate)
{
BOOL bRet = TRUE;
if ( ( NULL == phDevInfo ) ||
( NULL == pDeviceInfoData ) )
{
return FALSE;
}
if ( bCreate )
{
// Create a dummy devnode.
//
*phDevInfo = SetupDiCreateDeviceInfoList(NULL, NULL);
if ( INVALID_HANDLE_VALUE == *phDevInfo )
{
return FALSE;
}
// Initialize the DriverInfoData struct.
//
ZeroMemory(pDeviceInfoData, sizeof(SP_DEVINFO_DATA));
pDeviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
// Create the devnode.
//
if ( !SetupDiCreateDeviceInfo(*phDevInfo,
SYSPREP_DEVNODE,
(LPGUID) &GUID_NULL,
NULL,
NULL,
DICD_GENERATE_ID,
pDeviceInfoData) )
{
bRet = FALSE;
}
}
else
{
// Remove the dummy devnode.
//
SetupDiCallClassInstaller(DIF_REMOVE, *phDevInfo, pDeviceInfoData);
}
if ( ( !bCreate || !bRet ) &&
( INVALID_HANDLE_VALUE != *phDevInfo ) )
{
// Free up the dev info list (if we are removing the node or there
// was an error).
//
SetupDiDestroyDeviceInfoList(*phDevInfo);
*phDevInfo = INVALID_HANDLE_VALUE;
}
return bRet;
}
static BOOL
GetDeviceInstallSection(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN PSP_DRVINFO_DATA pDriverInfoData,
OUT LPTSTR lpszSection,
IN DWORD cbSection
)
{
BOOL bRet = FALSE;
PSP_DRVINFO_DETAIL_DATA pDriverInfoDetailData;
DWORD cbBytesNeeded;
// Must have a buffer to return the data or else
// there is no point.
//
if ( ( NULL == lpszSection ) ||
( 0 == cbSection ) )
{
return FALSE;
}
// Call the api once to get the size. We expect this
// to return a failure.
//
SetLastError(ERROR_SUCCESS);
SetupDiGetDriverInfoDetail(hDevInfo,
pDeviceInfoData,
pDriverInfoData,
NULL,
0,
&cbBytesNeeded);
// Check for the error, it should be insufficient buffer. Then
// try and allocate the memory needed.
//
if ( ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) &&
( cbBytesNeeded ) &&
( pDriverInfoDetailData = (PSP_DRVINFO_DETAIL_DATA) MALLOC(cbBytesNeeded) ) )
{
// Zero out the memory (although the MALLOC guy should be doing that) and
// set the size of the structure.
//
ZeroMemory(pDriverInfoDetailData, cbBytesNeeded);
pDriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
// Now call the function again to get data now that we have our buffer.
//
if ( SetupDiGetDriverInfoDetail(hDevInfo,
pDeviceInfoData,
pDriverInfoData,
pDriverInfoDetailData,
cbBytesNeeded,
NULL) )
{
HINF hDeviceInf;
hDeviceInf = SetupOpenInfFile( pDriverInfoDetailData->InfFileName, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
if ( INVALID_HANDLE_VALUE != hDeviceInf )
{
DWORD dwInfSectionWithExtLength = 0;
//
// Use SetupDiGetActualSectionToInstall to figure out the decorated driver section...
//
bRet = SetupDiGetActualSectionToInstall( hDeviceInf,
pDriverInfoDetailData->SectionName,
lpszSection,
cbSection,
&dwInfSectionWithExtLength,
NULL );
SetupCloseInfFile( hDeviceInf );
}
}
// Always free the memory now that we have the data we need.
//
FREE(pDriverInfoDetailData);
}
// Only return TRUE if we returned something in their buffer.
//
return bRet;
}
static BOOL
GetDeviceServicesSection(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN PSP_DRVINFO_DATA pDriverInfoData,
OUT LPTSTR lpszSection,
IN DWORD cbSection
)
{
BOOL bRet;
// Call our other function to get the device's install section.
//
bRet = GetDeviceInstallSection(hDevInfo,
pDeviceInfoData,
pDriverInfoData,
lpszSection,
cbSection);
// If it worked, add on the part that makes in the service.
// section.
//
if ( bRet )
{
// Make sure there is enough room to add on our string.
//
if ( AS(STR_SERVICES_SECTION) + lstrlen(lpszSection) <= cbSection )
{
// Woo hoo, add it.
//
lstrcat(lpszSection, STR_SERVICES_SECTION);
}
else
{
// Not enough room, so return an error and null out
// the caller's buffer.
//
*lpszSection = NULLCHR;
bRet = FALSE;
}
}
// Return TRUE only if something valid in the buffer.
//
return bRet;
}
static BOOL
ProcessDeviceProperty(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN LPCLEANUP_NODE * lplpcnList,
IN HKEY hkeyDevice,
IN HKEY hkeySystem,
IN LPTSTR lpszInfPath,
IN LPTSTR lpszServiceSection,
IN DWORD dwProperty
)
{
BOOL bRet = TRUE;
DWORD dwServiceType,
dwRegType,
dwRegSize;
LPTSTR lpszRegKey,
lpszBuffer,
lpszService;
// Figure out the other data we need based on
// the property.
//
switch ( dwProperty )
{
case SPDRP_CLASSGUID:
lpszRegKey = REGSTR_VAL_CLASSGUID;
dwServiceType = 0xFFFFFFFF;
break;
case SPDRP_UPPERFILTERS:
lpszRegKey = REGSTR_VAL_UPPERFILTERS;
dwServiceType = NUM_SERVICE_UPPER;
break;
case SPDRP_LOWERFILTERS:
lpszRegKey = REGSTR_VAL_LOWERFILTERS;
dwServiceType = NUM_SERVICE_LOWER;
break;
case SPDRP_SERVICE:
lpszRegKey = REGSTR_VAL_SERVICE;
dwServiceType = NUM_SERVICE_MAIN;
break;
default:
return FALSE;
}
// Call the registry property api to figure out the size of buffer
// we need.
//
SetLastError(ERROR_SUCCESS);
SetupDiGetDeviceRegistryProperty(hDevInfo,
pDeviceInfoData,
dwProperty,
&dwRegType,
NULL,
0,
&dwRegSize);
// If we get any other error then the one we are expecting, then just
// return TRUE.
//
if ( ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
return TRUE;
}
// Make sure reg type is a string.
//
switch ( dwRegType )
{
// We support both REG_SZ and REG_MULTI_SZ.
//
case REG_SZ:
case REG_MULTI_SZ:
// Don't really support this, but if the key happens to be
// this type it should still work fine.
//
case REG_EXPAND_SZ:
break;
// Any other type and there must be some kind of
// error.
//
default:
return FALSE;
}
// Now allocate the buffer we need. This must succeed.
//
lpszBuffer = (LPTSTR) MALLOC(dwRegSize);
if ( NULL == lpszBuffer )
{
return FALSE;
}
// Retrieve device information.
//
if ( SetupDiGetDeviceRegistryProperty(hDevInfo,
pDeviceInfoData,
dwProperty,
&dwRegType,
(LPBYTE) lpszBuffer,
dwRegSize,
&dwRegSize) )
{
// If this is a service, save it to our cleanup list.
//
if ( 0xFFFFFFFF != dwServiceType )
{
// Go through all the services (or just one if not multi sz).
//
for ( lpszService = lpszBuffer; *lpszService; lpszService += (lstrlen(lpszService) + 1) )
{
// Need to make sure this service is installed (only needed for offline install).
//
if ( hkeySystem )
{
// BRIANK code here...
//
AddService(lpszService, lpszServiceSection, lpszInfPath, hkeySystem);
}
// Add to our cleanup list.
//
AddCleanup(lplpcnList, lpszService, dwServiceType);
// If this isn't a multi sz string, break out so we don't
// try to do anymore.
//
if ( REG_MULTI_SZ != dwRegType )
{
break;
}
}
}
// Write it to the CDD (only needed for offline install).
//
if ( ( hkeyDevice ) &&
( ERROR_SUCCESS != RegSetValueEx(hkeyDevice,
lpszRegKey,
0,
dwRegType,
(CONST LPBYTE) lpszBuffer,
dwRegSize) ) )
{
// If a set value fails, we need to return an error.
//
bRet = FALSE;
}
}
// Make sure we free the buffer we allocated.
//
FREE(lpszBuffer);
return bRet;
}
static BOOL AddCleanup(LPCLEANUP_NODE * lplpcnHead, LPTSTR lpszService, DWORD dwType)
{
LPCLEANUP_NODE lpcnAdd = *lplpcnHead;
// Loop through our list looking for the a duplicate node.
//
while ( lpcnAdd )
{
// See if the node we want to add is the same as this one.
//
if ( 0 == lstrcmpi(lpcnAdd->lpszService, lpszService) )
{
// Already in the list, just return TRUE.
//
return TRUE;
}
// Advance to the next item in the list.
//
lplpcnHead = &(lpcnAdd->lpNext);
lpcnAdd = lpcnAdd->lpNext;
}
// If we didn't find a duplicate node then we need to add ours.
//
if ( lpcnAdd = (LPCLEANUP_NODE) MALLOC(sizeof(CLEANUP_NODE)) )
{
// Okay, now if all that worked, we just need to alloc the memory for the string
// that contains the name of the service.
//
if ( lpcnAdd->lpszService = (LPTSTR) MALLOC((lstrlen(lpszService) + 1) * sizeof(TCHAR)) )
{
// Already, copy the service string into the buffer we just allocated.
//
lstrcpy(lpcnAdd->lpszService, lpszService);
// Save the type in our node.
//
lpcnAdd->dwType = dwType;
// NULL out the next pointer since this is always the last item in
// the list (shouldn't have to do this because my malloc macro is
// supposed to zero memory, but for some reason it isn't working right.
//
lpcnAdd->lpNext = NULL;
// We should now have a pointer to the address of the next pointer
// in the last node (or the head pointer). Just add our node there.
//
*lplpcnHead = lpcnAdd;
// At this point we are all done.
//
return TRUE;
}
else
{
// Failed, so free our node that we were going to add.
//
FREE(lpcnAdd);
}
}
// Now if we ended up here, some memory allocation must have failed.
//
return FALSE;
}
static LPCLEANUP_NODE OpenCleanup(LPTSTR lpszInfFile)
{
LPCLEANUP_NODE lpcnHead = NULL,
lpcnNew,
*lplpcnAdd = &lpcnHead;
HINF hInf;
BOOL bLoop;
INFCONTEXT InfContext;
TCHAR szService[MAX_PATH];
DWORD dwType;
// First open up the inf. If it failes, there is no need to do anything
// because there is nothing to read. Just return NULL.
//
hInf = SetupOpenInfFile(lpszInfFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
if ( INVALID_HANDLE_VALUE == hInf )
{
return NULL;
}
// Loop through all the lines in the sysprep cleanup section.
//
for ( bLoop = SetupFindFirstLine(hInf, INI_SEC_WBOM_SYSPREP_CLEAN, NULL, &InfContext);
bLoop;
bLoop = SetupFindNextLine(&InfContext, &InfContext) )
{
// First get the service type (which is before the =).
//
if ( SetupGetStringField(&InfContext, 0, szService, AS(szService), NULL) )
{
// Now make sure it is a reconized type (either service, upperfilter,
// or lowerfilter).
//
for ( dwType = 0; ( dwType <= AS(s_lpszServiceType) ); dwType++ )
{
if ( 0 == lstrcmpi(s_lpszServiceType[dwType], szService) )
{
// If they match, break out and the dwType will be the index
// to the string.
//
break;
}
}
// Make sure we found the string in our array. If we did (dwType isn't too
// big) then get what is in the first field (after the =). This will be the
// name of the service. If we get that, then alloc the memory for the struture
// we are going to add to our cleanup list.
//
if ( ( dwType < AS(s_lpszServiceType) ) &&
( SetupGetStringField(&InfContext, 1, szService, AS(szService), NULL) ) &&
( AddCleanup(lplpcnAdd, szService, dwType) ) &&
( lpcnNew = *lplpcnAdd ) )
{
// Set the end pointer to the address of the next pointer in the node we
// just added. This is done so we don't rewalk the list every time we
// add another node.
//
lplpcnAdd = &(lpcnNew->lpNext);
}
}
}
// Close our inf file now that we are done.
//
SetupCloseInfFile(hInf);
// Return a pointer to the head of the list we just allocated.
//
return lpcnHead;
}
static BOOL SaveCleanup(LPTSTR lpszInfFile, LPCLEANUP_NODE lpcnHead)
{
DWORD cbSection = 8192,
dwSize = 0;
LPTSTR lpmszSection,
lpmszEnd;
// Need a buffer for the section we are creating.
//
lpmszSection = lpmszEnd = (LPTSTR) MALLOC(cbSection * sizeof(TCHAR));
if ( NULL == lpmszSection )
{
return FALSE;
}
// Loop through our whole list.
//
while ( lpcnHead )
{
// Add this line to our section in the form of: ServiceType=ServiceName\0
//
if ( !( AddStrToSect(&lpmszSection, &cbSection, &lpmszEnd, &dwSize, s_lpszServiceType[lpcnHead->dwType]) &&
AddStrToSect(&lpmszSection, &cbSection, &lpmszEnd, &dwSize, _T("=")) &&
AddStrToSect(&lpmszSection, &cbSection, &lpmszEnd, &dwSize, lpcnHead->lpszService) ) )
{
// Memory allocation error, must return.
//
return FALSE;
}
// Finished with this line, advance the pointer past the NULL.
//
lpmszEnd++;
dwSize++;
// Go to the next item in the list.
//
lpcnHead = lpcnHead->lpNext;
}
// Add another NULL after the last item because the section has to be
// double NULL terminated.
//
*lpmszEnd = NULLCHR;
// If we are going to write anything...
//
if ( *lpmszSection )
{
// Clear out the section that might already exist. We shouldn't have
// to do this because when we write out the new section it should replace
// the old one, but I don't trust these private profile APIs.
//
WritePrivateProfileSection(INI_SEC_WBOM_SYSPREP_CLEAN, NULLSTR, lpszInfFile);
}
// Now write out our new data.
//
WritePrivateProfileSection(INI_SEC_WBOM_SYSPREP_CLEAN, lpmszSection, lpszInfFile);
// Now we are done with our buffer and we can free it (macro checks for NULL).
//
FREE(lpmszSection);
// If we havent' returned yet, then everything must have worked.
//
return TRUE;
}
static void CloseCleanup(LPCLEANUP_NODE lpcnHead)
{
LPCLEANUP_NODE lpcnFree;
// Loop through the list until they are all gone.
//
while ( lpcnHead )
{
// Save a pointer to the node we are going to free
// (which is the first node in the list).
//
lpcnFree = lpcnHead;
// Now advance the head pointer past the node we are
// about to free.
//
lpcnHead = lpcnHead->lpNext;
// Now we can free the data in the node.
//
FREE(lpcnFree->lpszService);
// Now we can free the node itself.
//
FREE(lpcnFree);
}
}
static BOOL AddStrToSect(LPTSTR * lplpmszSect, DWORD * lpcbSect, LPTSTR * lplpmszEnd, DWORD * lpdwSize, LPTSTR lpszStr)
{
DWORD dwStrLen = lstrlen(lpszStr),
dwSizeNeeded;
// Make sure our string will fit in the buffer that is
// currently allocated. We leave room for at least two
// NULL terminators because we allways double terminate
// in case this is the last string in the section.
//
dwSizeNeeded = *lpdwSize + dwStrLen + 2;
if ( dwSizeNeeded >= *lpcbSect )
{
DWORD cbNewSect = *lpcbSect;
LPTSTR lpmszNewSect;
// Double the buffer size until we have enough room.
//
do
{
cbNewSect *= 2;
}
while ( ( cbNewSect <= dwSizeNeeded ) &&
( cbNewSect > *lpcbSect ) );
// Make sure we didn't wrap around with our size
// buffer (not likely, but doesn't hurt to check) and
// that our realloc works.
//
if ( !( ( cbNewSect > *lpcbSect ) &&
( lpmszNewSect = (LPTSTR) REALLOC(*lplpmszSect, cbNewSect * sizeof(TCHAR)) ) ) )
{
// This is bad. Free the buffer (the macro will NULL it out
// so the caller can't use it).
//
FREE(*lplpmszSect);
// Zero and NULL out all these other things so the caller
// can't rely on them.
//
*lpcbSect = 0;
*lplpmszEnd = NULL;
*lpdwSize = 0;
// Return now so we don't try to do anything else.
//
return FALSE;
}
// Woo hoo, we should be all good now.
//
*lplpmszEnd = lpmszNewSect + (*lplpmszEnd - *lplpmszSect);
*lplpmszSect = lpmszNewSect;
*lpcbSect = cbNewSect;
}
// At this point we must have room for our string, so copy it already.
//
lstrcpy(*lplpmszEnd, lpszStr);
*lpdwSize += dwStrLen;
*lplpmszEnd += dwStrLen;
return TRUE;
}
static BOOL
OfflineSourcePath(
HKEY hkeySoftware,
LPTSTR lpszWindows,
LPTSTR lpszSourcePath,
DWORD cbSourcePath
)
{
BOOL bRet = FALSE;
LPTSTR lpszOfflineSrc,
lpszName = NULL;
TCHAR szWinPEDir[MAX_PATH] = NULLSTR,
szNewOfflineSrc[MAX_PATH] = NULLSTR;
UINT uLen;
// Get the offline source path from the offline hive.
//
if ( lpszOfflineSrc = RegGetExpand(hkeySoftware, REG_KEY_HIVE_SETUP_SETUP, REGSTR_VAL_SRCPATH) )
{
// In case the offline source path had the %systemroot% or %windir% environment variable in it,
// we have to make sure the source path we got doesn't point to the WinPE system root.
//
// First get the current windows directory.
//
if ( ( uLen = GetSystemWindowsDirectory(szWinPEDir, AS(szWinPEDir)) ) &&
( szWinPEDir[0] ) )
{
// Now check to see if the source path we got starts with the WinPE directory.
//
if ( ( uLen <= (UINT) lstrlen(lpszOfflineSrc) ) &&
( CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, szWinPEDir, uLen, lpszOfflineSrc, uLen) == CSTR_EQUAL ) )
{
// Okay, it does. So we want to construct a new buffer with the offline windows
// directory passed in, and then with whatever was after the system root (if
// anything).
//
lstrcpyn(szNewOfflineSrc, lpszWindows, AS(szNewOfflineSrc));
if ( *(lpszOfflineSrc + uLen) )
{
AddPathN(szNewOfflineSrc, lpszOfflineSrc + uLen, AS(szNewOfflineSrc));
}
}
}
// If we didn't make a new path, we should at least make sure
// the drive leter is correct.
//
if ( NULLCHR == szNewOfflineSrc[0] )
{
// We need to make the offline source path based on the windows directory passed in and the
// on in the offline registry.
//
if ( GetFullPathName(lpszWindows, AS(szNewOfflineSrc), szNewOfflineSrc, &lpszName) && szNewOfflineSrc[0] && lpszName )
{
// This should chop off the windows folder from the offline windows directory.
//
*lpszName = NULLCHR;
// Now we should have the root of the system drive of the image, now add on what
// was in the registry (passed the drive letter).
//
if ( lstrlen(lpszOfflineSrc) > 3 )
{
AddPathN(szNewOfflineSrc, lpszOfflineSrc + 3, AS(szNewOfflineSrc));
}
}
else
{
// That failed (shouldn't though) so just use the one from the offline registry, but change
// the drive letter in case the image is on a different drive then it normally would be on.
//
lstrcpyn(szNewOfflineSrc, lpszOfflineSrc, AS(szNewOfflineSrc));
szNewOfflineSrc[0] = *lpszWindows;
}
}
// Now add on the arch folder.
//
if ( IsIA64() )
{
AddPathN(szNewOfflineSrc, DIR_IA64, AS(szNewOfflineSrc));
}
else
{
AddPathN(szNewOfflineSrc, DIR_I386, AS(szNewOfflineSrc));
}
// Make sure the folder exists.
//
if ( DirectoryExists(szNewOfflineSrc) )
{
bRet = TRUE;
lstrcpyn(lpszSourcePath, szNewOfflineSrc, cbSourcePath);
}
// Free the buffer allocated.
//
FREE(lpszOfflineSrc);
}
// Return TRUE only if we reset the buffer.
//
return bRet;
}