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.
1826 lines
48 KiB
1826 lines
48 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dynupdt.c
|
|
|
|
Abstract:
|
|
|
|
Routines to handle dynamic update support during GUI setup phase
|
|
|
|
Author:
|
|
|
|
Ovidiu Temereanca (ovidiut) 15-Aug-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "setupp.h"
|
|
#include "hwdb.h"
|
|
#include "newdev.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#define STR_UPDATES_INF TEXT("updates.inf")
|
|
#define STR_DEFAULTINSTALL TEXT("DefaultInstall")
|
|
#define STR_DEFAULTINSTALLFINAL TEXT("DefaultInstallFinal")
|
|
#define STR_DRIVERCACHEINF TEXT("drvindex.inf")
|
|
#define STR_VERSION TEXT("Version")
|
|
#define STR_CABFILES TEXT("CabFiles")
|
|
#define STR_CABS TEXT("Cabs")
|
|
#define S_HWCOMP_DAT TEXT("hwcomp.dat")
|
|
|
|
|
|
static TCHAR g_DuShare[MAX_PATH];
|
|
|
|
|
|
BOOL
|
|
BuildPath (
|
|
OUT PTSTR PathBuffer,
|
|
IN DWORD PathBufferSize,
|
|
IN PCTSTR Path1,
|
|
IN PCTSTR Path2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function builds a path given the 2 components, assumed not to contain
|
|
trailing or heading wacks
|
|
|
|
Arguments:
|
|
|
|
PathBuffer - Receives the full path
|
|
|
|
PathBuferSize - The size in chars of PathBuffer
|
|
|
|
Path1 - Specifies the head path
|
|
|
|
Path2 - Specifies the tail path
|
|
|
|
Return Value:
|
|
|
|
TRUE to indicate success; FALSE in case of failure; it means the supplied buffer
|
|
was too small to fit the whole new path
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!PathBuffer || !PathBufferSize || !Path1 || !Path2) {
|
|
MYASSERT (FALSE);
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (_sntprintf (PathBuffer, PathBufferSize, TEXT("%s\\%s"), Path1, Path2) < 0) {
|
|
PathBuffer[0] = 0;
|
|
SetLastError (ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pDoesFileExist (
|
|
IN PCTSTR FilePath
|
|
)
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
|
|
return FileExists (FilePath, &fd) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
}
|
|
|
|
BOOL
|
|
pDoesDirExist (
|
|
IN PCTSTR FilePath
|
|
)
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
|
|
return FileExists (FilePath, &fd) && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
}
|
|
|
|
|
|
DWORD
|
|
CreateMultiLevelDirectory (
|
|
IN LPCTSTR Directory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine ensures that a multi-level path exists by creating individual
|
|
levels one at a time. It can handle either paths of form x:... or \\?\Volume{...
|
|
|
|
Arguments:
|
|
|
|
Directory - supplies fully-qualified Win32 pathspec of directory to create
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR Buffer[MAX_PATH];
|
|
PTSTR p,q;
|
|
TCHAR c;
|
|
BOOL Done;
|
|
DWORD d = ERROR_SUCCESS;
|
|
|
|
if (FAILED (StringCchCopy (Buffer, ARRAYSIZE(Buffer), Directory))) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// If it already exists do nothing. (We do this before syntax checking
|
|
// to allow for remote paths that already exist. This is needed for
|
|
// remote boot machines.)
|
|
//
|
|
d = GetFileAttributes(Buffer);
|
|
if(d != (DWORD)(-1)) {
|
|
return((d & FILE_ATTRIBUTE_DIRECTORY) ? NO_ERROR : ERROR_DIRECTORY);
|
|
}
|
|
|
|
//
|
|
// Check path format
|
|
//
|
|
c = (TCHAR)CharUpper((LPTSTR)Buffer[0]);
|
|
if (c < TEXT('A') || c > TEXT('Z') || Buffer[1] != TEXT(':')) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(Buffer[2] != TEXT('\\')) {
|
|
return(Buffer[2] ? ERROR_INVALID_PARAMETER : ERROR_SUCCESS);
|
|
}
|
|
q = Buffer + 3;
|
|
if(*q == 0) {
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
Done = FALSE;
|
|
do {
|
|
//
|
|
// Locate the next path sep char. If there is none then
|
|
// this is the deepest level of the path.
|
|
//
|
|
if(p = _tcschr(q,TEXT('\\'))) {
|
|
*p = 0;
|
|
} else {
|
|
Done = TRUE;
|
|
}
|
|
|
|
//
|
|
// Create this portion of the path.
|
|
//
|
|
if(CreateDirectory(Buffer,NULL)) {
|
|
d = ERROR_SUCCESS;
|
|
} else {
|
|
d = GetLastError();
|
|
if(d == ERROR_ALREADY_EXISTS) {
|
|
d = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if(d == ERROR_SUCCESS) {
|
|
//
|
|
// Put back the path sep and move to the next component.
|
|
//
|
|
if(!Done) {
|
|
*p = TEXT('\\');
|
|
q = p+1;
|
|
}
|
|
} else {
|
|
Done = TRUE;
|
|
}
|
|
|
|
} while(!Done);
|
|
|
|
return(d);
|
|
}
|
|
|
|
DWORD
|
|
GetDriverCacheSourcePath (
|
|
OUT PTSTR Buffer,
|
|
IN DWORD BufChars
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the source path to the local driver cache.
|
|
|
|
This value is retrieved from the following registry location:
|
|
|
|
\HKLM\Software\Microsoft\Windows\CurrentVersion\Setup
|
|
|
|
DriverCachePath : REG_EXPAND_SZ :
|
|
|
|
Arguments:
|
|
|
|
Buffer - Receives the path.
|
|
|
|
BufChars - Specifies the size of Buffer, in chars
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE
|
|
|
|
If the function fails, the return value is FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hKey;
|
|
DWORD rc, DataType, DataSize;
|
|
TCHAR Value[MAX_PATH];
|
|
|
|
rc = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_SETUP TEXT("\\Setup"),
|
|
0,
|
|
KEY_READ,
|
|
&hKey
|
|
);
|
|
if(rc == ERROR_SUCCESS) {
|
|
//
|
|
// Attempt to read the "DriverCachePath" value.
|
|
//
|
|
DataSize = sizeof (Value);
|
|
rc = RegQueryValueEx (hKey, REGSTR_VAL_DRIVERCACHEPATH, NULL, &DataType, (PBYTE)Value, &DataSize);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if(rc == ERROR_SUCCESS) {
|
|
|
|
ExpandEnvironmentStrings (Value, Buffer, BufChars - 6);
|
|
|
|
if (Buffer[0]) {
|
|
_tcscat (
|
|
Buffer,
|
|
#if defined(_AMD64_)
|
|
TEXT("\\amd64")
|
|
#elif defined(_X86_)
|
|
IsNEC_98 ? TEXT("\\nec98") : TEXT("\\i386")
|
|
#elif defined(_IA64_)
|
|
TEXT("\\ia64")
|
|
#else
|
|
#error "No Target Architecture"
|
|
#endif
|
|
);
|
|
return ERROR_SUCCESS;
|
|
} else {
|
|
rc = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
FindSubString (
|
|
IN PCTSTR String,
|
|
IN TCHAR Separator,
|
|
IN PCTSTR SubStr,
|
|
IN BOOL CaseSensitive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks for a substring of a given string, only if found
|
|
between the specified separator chars
|
|
|
|
Arguments:
|
|
|
|
String - Specifies the full string
|
|
|
|
Separator - Specifies the separator
|
|
|
|
SubStr - Specifies the sub string to look for
|
|
|
|
CaseSensitive - Specifies if the comparison should be case sensitive or not
|
|
|
|
Return Value:
|
|
|
|
NULL if the substring was not found; a pointer to the SubString inside String
|
|
if it was found
|
|
|
|
--*/
|
|
|
|
{
|
|
SIZE_T len1, len2;
|
|
PCTSTR end;
|
|
|
|
MYASSERT (Separator);
|
|
MYASSERT (SubStr);
|
|
MYASSERT (!_tcschr (SubStr, Separator));
|
|
|
|
len1 = lstrlen (SubStr);
|
|
MYASSERT (SubStr[len1] == 0);
|
|
|
|
while (String) {
|
|
end = _tcschr (String, Separator);
|
|
if (end) {
|
|
len2 = end - String;
|
|
} else {
|
|
len2 = lstrlen (String);
|
|
}
|
|
if ((len1 == len2) &&
|
|
(CaseSensitive ?
|
|
!_tcsncmp (String, SubStr, len1) :
|
|
!_tcsnicmp (String, SubStr, len1)
|
|
)) {
|
|
break;
|
|
}
|
|
if (end) {
|
|
String = end + 1;
|
|
} else {
|
|
String = NULL;
|
|
}
|
|
}
|
|
|
|
return String;
|
|
}
|
|
|
|
|
|
BOOL
|
|
UpdateDrvIndex (
|
|
IN PCTSTR InfPath,
|
|
IN PCTSTR CabFilename,
|
|
IN PCTSTR SourceSifPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function fixes drvindex.inf such that SetupApi
|
|
will pick up the file from the right cabinet
|
|
|
|
Arguments:
|
|
|
|
InfPath - Specifies the full path to drvindex.inf
|
|
|
|
CabFilename - Specifies the filename of the
|
|
updates cabinet (basically it's "updates.cab")
|
|
|
|
SourceSifPath - Specifies the full path to the associated
|
|
updates.sif containing the list of files in updates.cab
|
|
|
|
Return Value:
|
|
|
|
TRUE to indicate success; FALSE in case of failure; use GetLastError()
|
|
to find the reason of failure
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE sectFile = INVALID_HANDLE_VALUE;
|
|
HANDLE concatFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hMap = NULL;
|
|
PTSTR section = NULL;
|
|
PBYTE base = NULL;
|
|
TCHAR tempPath[MAX_PATH];
|
|
TCHAR tempFile[MAX_PATH];
|
|
TCHAR temp[MAX_PATH];
|
|
PTSTR p;
|
|
DWORD sectSize;
|
|
DWORD concatSize;
|
|
DWORD rc;
|
|
DWORD bytes;
|
|
BOOL b = FALSE;
|
|
|
|
//
|
|
// create a temp file to put the new section in it
|
|
//
|
|
if (!GetTempPath (ARRAYSIZE(tempPath), tempPath) ||
|
|
!GetTempFileName (tempPath, TEXT("STP"), 0, tempFile)
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
__try {
|
|
|
|
if (!CopyFile (InfPath, tempFile, FALSE)) {
|
|
__leave;
|
|
}
|
|
SetFileAttributes (tempFile, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
section = pSetupDuplicateString (CabFilename);
|
|
if (!section) {
|
|
__leave;
|
|
}
|
|
p = _tcsrchr (section, TEXT('.'));
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
|
|
if (GetPrivateProfileString (
|
|
STR_CABS,
|
|
section,
|
|
TEXT(""),
|
|
temp,
|
|
ARRAYSIZE(temp),
|
|
tempFile
|
|
)) {
|
|
if (lstrcmpi (temp, CabFilename) == 0) {
|
|
if (GetPrivateProfileString (
|
|
STR_VERSION,
|
|
STR_CABFILES,
|
|
TEXT(""),
|
|
tempPath,
|
|
ARRAYSIZE(tempPath),
|
|
tempFile
|
|
)) {
|
|
if (FindSubString (tempPath, TEXT(','), section, FALSE)) {
|
|
//
|
|
// setup restarted, but drvindex.inf is already patched; nothing to do
|
|
//
|
|
b = TRUE;
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!WritePrivateProfileString (
|
|
STR_CABS,
|
|
section,
|
|
CabFilename,
|
|
tempFile
|
|
)) {
|
|
__leave;
|
|
}
|
|
if (!GetPrivateProfileString (
|
|
STR_VERSION,
|
|
STR_CABFILES,
|
|
TEXT(""),
|
|
tempPath,
|
|
ARRAYSIZE(tempPath),
|
|
tempFile
|
|
)) {
|
|
__leave;
|
|
}
|
|
if (!FindSubString (tempPath, TEXT(','), section, FALSE)) {
|
|
wsprintf (temp, TEXT("%s,%s"), section, tempPath);
|
|
if (!WritePrivateProfileString (
|
|
STR_VERSION,
|
|
STR_CABFILES,
|
|
temp,
|
|
tempFile
|
|
)) {
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
sectFile = CreateFile (
|
|
SourceSifPath,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
if (sectFile == INVALID_HANDLE_VALUE) {
|
|
__leave;
|
|
}
|
|
|
|
sectSize = GetFileSize (sectFile, NULL);
|
|
if (sectSize == INVALID_FILE_SIZE) {
|
|
__leave;
|
|
}
|
|
|
|
concatFile = CreateFile (
|
|
tempFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
if (concatFile == INVALID_HANDLE_VALUE) {
|
|
__leave;
|
|
}
|
|
concatSize = GetFileSize (concatFile, NULL);
|
|
if (concatSize == INVALID_FILE_SIZE) {
|
|
__leave;
|
|
}
|
|
|
|
hMap = CreateFileMapping (concatFile, NULL, PAGE_READWRITE, 0, concatSize + sectSize, NULL);
|
|
if (!hMap) {
|
|
__leave;
|
|
}
|
|
|
|
base = MapViewOfFile (
|
|
hMap,
|
|
FILE_MAP_ALL_ACCESS,
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
if (!base) {
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// make sure concatFile file didn't end in end-of-file
|
|
//
|
|
if (base[concatSize - 1] == 0x1A) {
|
|
base[concatSize - 1] = ' ';
|
|
}
|
|
//
|
|
// now append the other file
|
|
//
|
|
if (!ReadFile (sectFile, (LPVOID)(base + concatSize), sectSize, &bytes, NULL) || bytes != sectSize) {
|
|
__leave;
|
|
}
|
|
//
|
|
// now try to commit changes
|
|
//
|
|
if (!UnmapViewOfFile (base)) {
|
|
__leave;
|
|
}
|
|
base = NULL;
|
|
if (!CloseHandle (hMap)) {
|
|
__leave;
|
|
}
|
|
hMap = NULL;
|
|
//
|
|
// close the handle to the temporary file and overwrite the real one
|
|
//
|
|
if (!CloseHandle (concatFile)) {
|
|
__leave;
|
|
}
|
|
concatFile = INVALID_HANDLE_VALUE;
|
|
SetFileAttributes (InfPath, FILE_ATTRIBUTE_NORMAL);
|
|
b = MoveFileEx (tempFile, InfPath, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
|
|
}
|
|
__finally {
|
|
rc = b ? ERROR_SUCCESS : GetLastError ();
|
|
DeleteFile (tempFile);
|
|
if (base) {
|
|
UnmapViewOfFile (base);
|
|
}
|
|
if (hMap) {
|
|
CloseHandle (hMap);
|
|
}
|
|
if (concatFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle (concatFile);
|
|
}
|
|
if (sectFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle (sectFile);
|
|
}
|
|
if (section) {
|
|
MyFree (section);
|
|
}
|
|
SetLastError (rc);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
UINT
|
|
pExpandUpdatesCab (
|
|
IN PVOID Context,
|
|
IN UINT Code,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
)
|
|
{
|
|
switch (Code) {
|
|
case SPFILENOTIFY_FILEINCABINET:
|
|
{
|
|
PFILE_IN_CABINET_INFO FileInCabInfo = (PFILE_IN_CABINET_INFO)Param1;
|
|
//
|
|
// extract the file name
|
|
//
|
|
PCTSTR p = _tcsrchr (FileInCabInfo->NameInCabinet, TEXT('\\'));
|
|
if (p) {
|
|
p++;
|
|
} else {
|
|
p = FileInCabInfo->NameInCabinet;
|
|
}
|
|
|
|
lstrcpy (FileInCabInfo->FullTargetName, (PCTSTR)Context);
|
|
pSetupConcatenatePaths (
|
|
FileInCabInfo->FullTargetName,
|
|
p,
|
|
SIZECHARS (FileInCabInfo->FullTargetName),
|
|
NULL
|
|
);
|
|
return FILEOP_DOIT;
|
|
}
|
|
|
|
case SPFILENOTIFY_NEEDNEWCABINET:
|
|
{
|
|
PCABINET_INFO CabInfo = (PCABINET_INFO)Param1;
|
|
SetuplogError(
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
MSG_LOG_SYSSETUP_CAB_MISSING,
|
|
CabInfo->CabinetPath,
|
|
CabInfo->CabinetFile,
|
|
CabInfo->DiskName,
|
|
CabInfo->SetId,
|
|
CabInfo->CabinetNumber,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
pInstallUpdatesInf (
|
|
IN PCTSTR SectionToInstall
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function installs the specified section of STR_UPDATES_INF
|
|
if this file is found inside updates.cab
|
|
|
|
Arguments:
|
|
|
|
SectionToInstall - Specifies what section to install
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR infPath[MAX_PATH];
|
|
TCHAR commandLine[MAX_PATH + 30];
|
|
/*
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
*/
|
|
|
|
MYASSERT (!MiniSetup && !OobeSetup);
|
|
|
|
MYASSERT (g_DuShare[0]);
|
|
|
|
if (BuildPath (infPath, ARRAYSIZE(infPath), g_DuShare, STR_UPDATES_INF) &&
|
|
pDoesFileExist (infPath)) {
|
|
//
|
|
// install this INF as if the user chose "Install" on the right-click popup menu
|
|
//
|
|
if (SUCCEEDED (StringCchPrintf (
|
|
commandLine,
|
|
ARRAYSIZE(commandLine),
|
|
TEXT("RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection %s %u %s"),
|
|
SectionToInstall,
|
|
128, // don't reboot
|
|
infPath
|
|
))) {
|
|
InvokeExternalApplicationEx (NULL, commandLine, NULL, INFINITE, FALSE);
|
|
} else {
|
|
MYASSERT (FALSE);
|
|
}
|
|
/*
|
|
ZeroMemory (&si, sizeof (si));
|
|
si.cb = sizeof (si);
|
|
if (CreateProcess (
|
|
NULL,
|
|
commandLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NO_WINDOW | ABOVE_NORMAL_PRIORITY_CLASS,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi
|
|
)) {
|
|
CloseHandle (pi.hProcess);
|
|
CloseHandle (pi.hThread);
|
|
*/
|
|
} else {
|
|
SetuplogError (
|
|
LogSevInformation,
|
|
TEXT("DUInfo: No %1 to install"),
|
|
0,
|
|
STR_UPDATES_INF,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DuInitialize (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes DU in GUI setup
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE to indicate success; FALSE in case of failure; use GetLastError()
|
|
to find the reason of failure
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR cabFilename;
|
|
TCHAR sourceCabPath[MAX_PATH];
|
|
TCHAR workingDir[MAX_PATH];
|
|
DWORD rc;
|
|
|
|
MYASSERT (!MiniSetup && !OobeSetup);
|
|
|
|
MYASSERT (AnswerFile[0]);
|
|
|
|
if (!GetPrivateProfileString (
|
|
WINNT_SETUPPARAMS,
|
|
WINNT_SP_UPDATEDSOURCES,
|
|
TEXT(""),
|
|
sourceCabPath,
|
|
ARRAYSIZE(sourceCabPath),
|
|
AnswerFile
|
|
)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!GetPrivateProfileString (
|
|
WINNT_SETUPPARAMS,
|
|
WINNT_SP_DYNUPDTWORKINGDIR,
|
|
TEXT(""),
|
|
workingDir,
|
|
ARRAYSIZE(workingDir),
|
|
AnswerFile
|
|
)) {
|
|
|
|
MYASSERT (FALSE);
|
|
|
|
if (!GetWindowsDirectory (workingDir, ARRAYSIZE(workingDir))) {
|
|
return FALSE;
|
|
}
|
|
if (!pSetupConcatenatePaths (workingDir, TEXT("setupupd"), ARRAYSIZE(workingDir), NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
WritePrivateProfileString (
|
|
WINNT_SETUPPARAMS,
|
|
WINNT_SP_DYNUPDTWORKINGDIR,
|
|
workingDir,
|
|
AnswerFile
|
|
);
|
|
}
|
|
|
|
MYASSERT (workingDir[0]);
|
|
if (!pSetupConcatenatePaths (workingDir, TEXT("updates"), ARRAYSIZE(workingDir), NULL) ||
|
|
!pSetupConcatenatePaths (
|
|
workingDir,
|
|
#if defined(_AMD64_)
|
|
TEXT("amd64"),
|
|
#elif defined(_X86_)
|
|
TEXT("i386"),
|
|
#elif defined(_IA64_)
|
|
TEXT("ia64"),
|
|
#else
|
|
#error "No Target Architecture"
|
|
#endif
|
|
ARRAYSIZE(workingDir),
|
|
NULL
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (CreateMultiLevelDirectory (workingDir) != ERROR_SUCCESS) {
|
|
rc = GetLastError ();
|
|
SetuplogError (
|
|
LogSevError,
|
|
TEXT("DUError: DuInitialize: failed to create %1 (%2!u!)\r\n"),
|
|
0,
|
|
workingDir,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// expand updates.cab in this folder
|
|
//
|
|
if (!SetupIterateCabinet (sourceCabPath, 0, pExpandUpdatesCab, (PVOID)workingDir)) {
|
|
rc = GetLastError ();
|
|
SetuplogError (
|
|
LogSevError,
|
|
TEXT("DUError: DuInitialize: failed to expand %1 to %2 (%3!u!)\r\n"),
|
|
0,
|
|
sourceCabPath,
|
|
workingDir,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// OK, everything is set up; go ahead and set the global variable
|
|
//
|
|
MYASSERT (ARRAYSIZE(g_DuShare) >= ARRAYSIZE(workingDir));
|
|
lstrcpy (g_DuShare, workingDir);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DuInstallCatalogs (
|
|
OUT SetupapiVerifyProblem* Problem,
|
|
OUT PTSTR ProblemFile,
|
|
IN PCTSTR DescriptionForError OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function installs any catalogs found inside updates.cab
|
|
|
|
Arguments:
|
|
|
|
same as InstallProductCatalogs
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is ERROR_SUCCESS, otherwise it is a Win32 error
|
|
code indicating the cause of the failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR catPath[MAX_PATH];
|
|
WIN32_FIND_DATA fd;
|
|
HANDLE h;
|
|
UINT ErrorMessageId;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
MYASSERT (!MiniSetup && !OobeSetup);
|
|
|
|
if (!g_DuShare[0]) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!BuildPath (catPath, ARRAYSIZE(catPath), g_DuShare, TEXT("*.cat"))) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
h = FindFirstFile (catPath, &fd);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
|
|
do {
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
if (!BuildPath (catPath, ARRAYSIZE(catPath), g_DuShare, fd.cFileName)) {
|
|
SetuplogError (
|
|
LogSevWarning,
|
|
TEXT("DUWarning: ignoring catalog [%1\\%2] - path too long\r\n"),
|
|
0,
|
|
g_DuShare,
|
|
fd.cFileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
continue;
|
|
}
|
|
|
|
rc = pSetupVerifyCatalogFile (catPath);
|
|
if (rc == NO_ERROR) {
|
|
rc = pSetupInstallCatalog(catPath, fd.cFileName, NULL);
|
|
if(rc != NO_ERROR) {
|
|
ErrorMessageId = MSG_LOG_SYSSETUP_CATINSTALL_FAILED;
|
|
}
|
|
} else {
|
|
ErrorMessageId = MSG_LOG_SYSSETUP_VERIFY_FAILED;
|
|
}
|
|
|
|
if(rc != NO_ERROR) {
|
|
|
|
SetuplogError (
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
ErrorMessageId,
|
|
catPath,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
//
|
|
// Also, add an entry about this failure to setupapi's PSS
|
|
// exception logfile.
|
|
//
|
|
pSetupHandleFailedVerification (
|
|
MainWindowHandle,
|
|
SetupapiVerifyCatalogProblem,
|
|
catPath,
|
|
DescriptionForError,
|
|
pSetupGetCurrentDriverSigningPolicy(FALSE),
|
|
TRUE, // no UI!
|
|
rc,
|
|
NULL, // log context
|
|
NULL, // optional flags
|
|
NULL
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
} while (FindNextFile (h, &fd));
|
|
|
|
FindClose (h);
|
|
} else {
|
|
SetuplogError (
|
|
LogSevWarning,
|
|
TEXT("DUWarning: no catalogs found in %1\r\n"),
|
|
0,
|
|
g_DuShare,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
DWORD
|
|
DuInstallUpdates (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates drvindex.inf to point setupapi to the new binaries.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is ERROR_SUCCESS, otherwise it is a Win32 error
|
|
code indicating the cause of the failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR cabFilename;
|
|
TCHAR sourceCabPath[MAX_PATH];
|
|
TCHAR sourceSifPath[MAX_PATH];
|
|
TCHAR cabPath[MAX_PATH];
|
|
TCHAR infPath[MAX_PATH];
|
|
TCHAR tmpPath[MAX_PATH];
|
|
DWORD rc;
|
|
|
|
MYASSERT (!MiniSetup && !OobeSetup);
|
|
|
|
if (!g_DuShare[0]) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// make sure updates.sif is available
|
|
//
|
|
if (!GetPrivateProfileString (
|
|
WINNT_SETUPPARAMS,
|
|
WINNT_SP_UPDATEDSOURCES,
|
|
TEXT(""),
|
|
sourceCabPath,
|
|
ARRAYSIZE(sourceCabPath),
|
|
AnswerFile
|
|
)) {
|
|
return GetLastError ();
|
|
}
|
|
|
|
MYASSERT (ARRAYSIZE(sourceSifPath) >= ARRAYSIZE(sourceCabPath));
|
|
lstrcpy (sourceSifPath, sourceCabPath);
|
|
cabFilename = _tcsrchr (sourceSifPath, TEXT('.'));
|
|
if (!cabFilename || _tcschr (cabFilename, TEXT('\\'))) {
|
|
SetuplogError (
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
MSG_LOG_INVALID_UPDATESCAB_NAME,
|
|
sourceCabPath,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
lstrcpyn (cabFilename + 1, TEXT("sif"), (INT)(sourceSifPath + ARRAYSIZE(sourceSifPath) - (cabFilename + 1)));
|
|
if (!pDoesFileExist (sourceSifPath)) {
|
|
rc = GetLastError ();
|
|
SetuplogError (
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
MSG_LOG_UPDATESSIF_NOT_FOUND,
|
|
sourceSifPath,
|
|
sourceCabPath,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return rc;
|
|
}
|
|
//
|
|
// copy this where source cabs reside
|
|
//
|
|
rc = GetDriverCacheSourcePath (cabPath, ARRAYSIZE(cabPath));
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetuplogError (
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
MSG_LOG_DRIVER_CACHE_NOT_FOUND,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return rc;
|
|
}
|
|
cabFilename = _tcsrchr (sourceCabPath, TEXT('\\'));
|
|
if (cabFilename) {
|
|
cabFilename++;
|
|
} else {
|
|
cabFilename = cabPath;
|
|
}
|
|
if (!pSetupConcatenatePaths (cabPath, cabFilename, ARRAYSIZE(cabPath), NULL)) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
//
|
|
// GUI setup should be restartable; copy file, don't move it
|
|
//
|
|
SetFileAttributes (cabPath, FILE_ATTRIBUTE_NORMAL);
|
|
if (!CopyFile (sourceCabPath, cabPath, FALSE)) {
|
|
rc = GetLastError ();
|
|
SetuplogError (
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
MSG_LOG_FAILED_TO_COPY_UPDATES,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return rc;
|
|
}
|
|
//
|
|
// now make sure the file attributes are set to RHS to protect it
|
|
//
|
|
SetFileAttributes (cabPath, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
|
|
|
|
//
|
|
// temp folder for expanded files
|
|
//
|
|
if (!GetTempPath (ARRAYSIZE(tmpPath), tmpPath)) {
|
|
rc = GetLastError ();
|
|
return rc;
|
|
}
|
|
//
|
|
// full path to drvindex.inf, assuming the file is in %windir%\inf
|
|
//
|
|
if (!GetWindowsDirectory (infPath, ARRAYSIZE(infPath))) {
|
|
rc = GetLastError ();
|
|
return rc;
|
|
}
|
|
if (FAILED (StringCchCat (infPath, ARRAYSIZE(infPath), TEXT("\\inf\\"))) ||
|
|
FAILED (StringCchCat (infPath, ARRAYSIZE(infPath), STR_DRIVERCACHEINF)) ||
|
|
GetFileAttributes (infPath) == (DWORD)-1) {
|
|
|
|
rc = GetLastError ();
|
|
SetuplogError (
|
|
LogSevError,
|
|
TEXT("DUError: %1 not found (rc=%2!u!)\r\n"),
|
|
0,
|
|
infPath,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// now patch drvindex.inf
|
|
//
|
|
if (!UpdateDrvIndex (infPath, cabFilename, sourceSifPath)) {
|
|
rc = GetLastError ();
|
|
SetuplogError (
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
MSG_LOG_FAILED_TO_UPDATE_DRVINDEX,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
SetFileAttributes (cabPath, FILE_ATTRIBUTE_NORMAL);
|
|
DeleteFile (cabPath);
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// finally run updates.inf (first part)
|
|
//
|
|
pInstallUpdatesInf (STR_DEFAULTINSTALL);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DuInstallEndGuiSetupDrivers (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine installs any WU drivers not approved for the beginning of GUI setup.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is TRUE
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD chars;
|
|
TCHAR datPath[MAX_PATH];
|
|
TCHAR infPath[MAX_PATH];
|
|
TCHAR buf[MAX_PATH * 16];
|
|
PTSTR source, p, next;
|
|
BOOL bRebootRequired;
|
|
HWDBINF_ENUM e;
|
|
PCTSTR pnpId;
|
|
HMODULE hNewDev;
|
|
HINF hGuiDrvsInf;
|
|
INFCONTEXT ic;
|
|
UINT line;
|
|
BOOL (WINAPI* pfnUpdateDriver) (
|
|
HWND hwndParent,
|
|
LPCWSTR HardwareId,
|
|
LPCWSTR FullInfPath,
|
|
DWORD InstallFlags,
|
|
PBOOL bRebootRequired OPTIONAL
|
|
);
|
|
|
|
//
|
|
// try to append any additional POST GUI setup drivers
|
|
// in the DevicePath
|
|
//
|
|
if (!SpSetupLoadParameter (
|
|
WINNT_SP_DYNUPDTADDITIONALPOSTGUIDRIVERS,
|
|
buf,
|
|
ARRAYSIZE(buf)
|
|
)) {
|
|
return TRUE;
|
|
}
|
|
|
|
chars = lstrlen (buf);
|
|
if (!chars) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// load the support library newdev.dll
|
|
//
|
|
hNewDev = LoadLibrary (TEXT("newdev.dll"));
|
|
if (!hNewDev) {
|
|
return FALSE;
|
|
}
|
|
(FARPROC)pfnUpdateDriver = GetProcAddress (hNewDev, "UpdateDriverForPlugAndPlayDevicesW");
|
|
if (!pfnUpdateDriver) {
|
|
FreeLibrary (hNewDev);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!HwdbInitializeW (NULL)) {
|
|
SetuplogError (
|
|
LogSevWarning,
|
|
TEXT("DUWarning: HwdbInitialize failed (rc=%1!u!); no DU drivers will be installed\r\n"),
|
|
0,
|
|
GetLastError (),
|
|
NULL,
|
|
NULL
|
|
);
|
|
FreeLibrary (hNewDev);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// look for driver-controlling inf
|
|
//
|
|
hGuiDrvsInf = INVALID_HANDLE_VALUE;
|
|
if (SpSetupLoadParameter (
|
|
WINNT_SP_DYNUPDTDRIVERINFOFILE,
|
|
infPath,
|
|
ARRAYSIZE(infPath)
|
|
)) {
|
|
if (pDoesFileExist (infPath)) {
|
|
hGuiDrvsInf = SetupOpenInfFile (infPath, NULL, INF_STYLE_WIN4, &line);
|
|
if (hGuiDrvsInf == INVALID_HANDLE_VALUE) {
|
|
SetuplogError (
|
|
LogSevWarning,
|
|
TEXT("DUWarning: SetupOpenInfFile(%1) failed (rc=%2!u!); all DU drivers will be installed\r\n"),
|
|
0,
|
|
infPath,
|
|
GetLastError (),
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
} else {
|
|
SetuplogError (
|
|
LogSevWarning,
|
|
TEXT("DUWarning: File %1 missing; all DU drivers will be installed\r\n"),
|
|
0,
|
|
infPath,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
} else {
|
|
SetuplogError (
|
|
LogSevInformation,
|
|
TEXT("DUInfo: File %1 missing; all DU drivers will be installed\r\n"),
|
|
0,
|
|
infPath,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
source = buf;
|
|
while (source) {
|
|
next = _tcschr (source, TEXT(','));
|
|
if (next) {
|
|
*next = 0;
|
|
}
|
|
p = source;
|
|
if (*p == TEXT('\"')) {
|
|
p = ++source;
|
|
}
|
|
while (*p && *p != TEXT('\"')) {
|
|
p++;
|
|
}
|
|
*p = 0;
|
|
if (pDoesDirExist (source)) {
|
|
if (BuildPath (datPath, ARRAYSIZE(datPath), source, S_HWCOMP_DAT) &&
|
|
pDoesFileExist (datPath)) {
|
|
|
|
//
|
|
// OK, we have the file with hardware info
|
|
//
|
|
if (HwdbEnumFirstInf (&e, datPath)) {
|
|
do {
|
|
if (!BuildPath (infPath, ARRAYSIZE(infPath), source, e.InfFile) ||
|
|
!pDoesFileExist (infPath)) {
|
|
continue;
|
|
}
|
|
//
|
|
// iterate through all PNPIDs in this INF
|
|
//
|
|
for (pnpId = e.PnpIds; *pnpId; pnpId = _tcschr (pnpId, 0) + 1) {
|
|
//
|
|
// excluded PNPID are NOT in hwcomp.dat
|
|
// guidrvs.inf was already processed during winnt32
|
|
//
|
|
if (!pfnUpdateDriver (
|
|
NULL,
|
|
pnpId,
|
|
infPath,
|
|
0, // BUGBUG - if we specify INSTALLFLAG_NONINTERACTIVE and there is a driver signing problem, the API will fail!
|
|
&bRebootRequired
|
|
)) {
|
|
if (GetLastError() != ERROR_SUCCESS) {
|
|
//
|
|
// well, if the device we wanted to update the driver for
|
|
// doesn't actually exist on this machine, don't log anything
|
|
//
|
|
if (GetLastError() != ERROR_NO_SUCH_DEVINST) {
|
|
SetuplogError (
|
|
LogSevWarning,
|
|
TEXT("DUWarning: UpdateDriverForPlugAndPlayDevices failed (rc=%3!u!) for PNPID=%1 (INF=%2)\r\n"),
|
|
0,
|
|
pnpId,
|
|
infPath,
|
|
GetLastError (),
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
} else {
|
|
SetuplogError (
|
|
LogSevInformation,
|
|
TEXT("DUInfo: UpdateDriverForPlugAndPlayDevices did not update the driver for PNPID=%1\r\n"),
|
|
0,
|
|
pnpId,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
continue;
|
|
}
|
|
//
|
|
// SUCCESS! - log this information
|
|
//
|
|
SetuplogError (
|
|
LogSevInformation,
|
|
TEXT("DUInfo: UpdateDriverForPlugAndPlayDevices succeeded for PNPID=%1\r\n"),
|
|
0,
|
|
pnpId,
|
|
NULL,
|
|
NULL
|
|
);
|
|
//
|
|
// also update the PNF with the info that this is an INTERNET driver
|
|
//
|
|
if (!SetupCopyOEMInf (
|
|
infPath,
|
|
NULL,
|
|
SPOST_URL,
|
|
SP_COPY_REPLACEONLY,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
SetuplogError (
|
|
LogSevInformation,
|
|
TEXT("DUInfo: SetupCopyOEMInf failed to update OEMSourceMediaType for INF=%1\r\n"),
|
|
0,
|
|
infPath,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
} while (HwdbEnumNextInf (&e));
|
|
}
|
|
}
|
|
}
|
|
if (next) {
|
|
source = next + 1;
|
|
} else {
|
|
source = NULL;
|
|
}
|
|
}
|
|
|
|
HwdbTerminate ();
|
|
|
|
FreeLibrary (hNewDev);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DuCleanup (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs DU cleanup
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR buf[MAX_PATH * 16];
|
|
DWORD chars;
|
|
HKEY key;
|
|
PTSTR devicePath;
|
|
PTSTR p;
|
|
DWORD rc;
|
|
DWORD size;
|
|
DWORD type;
|
|
|
|
//
|
|
// cleanup the file system
|
|
//
|
|
MYASSERT (AnswerFile[0]);
|
|
if (GetPrivateProfileString (
|
|
WINNT_SETUPPARAMS,
|
|
WINNT_SP_DYNUPDTWORKINGDIR,
|
|
TEXT(""),
|
|
buf,
|
|
ARRAYSIZE(buf),
|
|
AnswerFile
|
|
)) {
|
|
Delnode (buf);
|
|
}
|
|
//
|
|
// cleanup the registry
|
|
//
|
|
chars = GetPrivateProfileString (
|
|
WINNT_SETUPPARAMS,
|
|
WINNT_SP_DYNUPDTADDITIONALGUIDRIVERS,
|
|
TEXT(""),
|
|
buf,
|
|
ARRAYSIZE(buf),
|
|
AnswerFile
|
|
);
|
|
if (chars > 0) {
|
|
//
|
|
// got it; now remove it from DevicePath
|
|
//
|
|
rc = RegOpenKey (HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP, &key);
|
|
if (rc == ERROR_SUCCESS) {
|
|
rc = RegQueryValueEx (key, REGSTR_VAL_DEVICEPATH, NULL, NULL, NULL, &size);
|
|
if (rc == ERROR_SUCCESS) {
|
|
devicePath = (PTSTR) MyMalloc (size);
|
|
if (devicePath) {
|
|
rc = RegQueryValueEx (key, REGSTR_VAL_DEVICEPATH, NULL, &type, (LPBYTE)devicePath, &size);
|
|
if (rc == ERROR_SUCCESS && size / sizeof (TCHAR) >= chars + 1) {
|
|
p = _tcsstr (devicePath, buf);
|
|
if (p &&
|
|
(p == devicePath || *(p - 1) == TEXT(';')) &&
|
|
(!p[chars] || p[chars] == TEXT(';'))
|
|
) {
|
|
if (p == devicePath) {
|
|
_tcscpy (p, p[chars] == TEXT(';') ? p + chars + 1 : p + chars);
|
|
} else {
|
|
_tcscpy (p - 1, p + chars);
|
|
}
|
|
size = (_tcslen (devicePath) + 1) * sizeof (TCHAR);
|
|
rc = RegSetValueEx (key, REGSTR_VAL_DEVICEPATH, 0, type, (PBYTE)devicePath, size);
|
|
}
|
|
}
|
|
MyFree (devicePath);
|
|
}
|
|
}
|
|
RegCloseKey (key);
|
|
}
|
|
}
|
|
|
|
g_DuShare[0] = 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DuInstallDuAsms (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine installs additional DU assemblies
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR duasmsRoot[MAX_PATH];
|
|
TCHAR duasmsSource[MAX_PATH];
|
|
DWORD chars;
|
|
SIDE_BY_SIDE SideBySide = {0};
|
|
BOOL b1;
|
|
BOOL b2;
|
|
PTSTR p;
|
|
PCTSTR source;
|
|
BOOL fSuccess = TRUE;
|
|
|
|
//
|
|
// look for any assemblies root specified in the answer file
|
|
//
|
|
|
|
MYASSERT (AnswerFile[0]);
|
|
if (GetPrivateProfileString (
|
|
WINNT_SETUPPARAMS,
|
|
WINNT_SP_UPDATEDDUASMS,
|
|
TEXT(""),
|
|
duasmsRoot,
|
|
ARRAYSIZE(duasmsRoot),
|
|
AnswerFile
|
|
)) {
|
|
|
|
//
|
|
// make sure this directory exists; remove any existing quotes first
|
|
//
|
|
p = duasmsRoot;
|
|
if (*p == TEXT('\"')) {
|
|
p++;
|
|
}
|
|
source = p;
|
|
while (*p && *p != TEXT('\"')) {
|
|
p++;
|
|
}
|
|
*p = 0;
|
|
if (pDoesDirExist (source)) {
|
|
//
|
|
// root directory found
|
|
// first copy them in a protected location, then install assemblies from there
|
|
//
|
|
DWORD rc;
|
|
|
|
rc = GetDriverCacheSourcePath (duasmsSource, ARRAYSIZE(duasmsSource));
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetuplogError (
|
|
LogSevError,
|
|
SETUPLOG_USE_MESSAGEID,
|
|
MSG_LOG_DRIVER_CACHE_NOT_FOUND,
|
|
rc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return FALSE;
|
|
}
|
|
if (!pSetupConcatenatePaths (duasmsSource, TEXT("duasms"), ARRAYSIZE(duasmsSource), NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// first remove any already existing duasms "backup source" previously downloaded
|
|
//
|
|
Delnode (duasmsSource);
|
|
//
|
|
// now tree copy from the temp location to this "backup source"
|
|
//
|
|
rc = TreeCopy (source, duasmsSource);
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetuplogError(
|
|
LogSevError,
|
|
TEXT("Setup failed to TreeCopy %2 to %3 (TreeCopy failed %1!u!)\r\n"),
|
|
0,
|
|
rc,
|
|
source,
|
|
duasmsSource,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// install duasms from there
|
|
//
|
|
b1 = SideBySidePopulateCopyQueue (&SideBySide, NULL, duasmsSource);
|
|
b2 = SideBySideFinish (&SideBySide, b1);
|
|
|
|
if (!b1 || !b2) {
|
|
fSuccess = FALSE;
|
|
SetuplogError (
|
|
LogSevError,
|
|
TEXT("DUError: DuInstallDuAsms failed (rc=%1!u!)\r\n"),
|
|
0,
|
|
GetLastError (),
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
} else {
|
|
fSuccess = FALSE;
|
|
SetuplogError (
|
|
LogSevError,
|
|
TEXT("DUError: Invalid directory %1; DuInstallDuAsms failed\r\n"),
|
|
0,
|
|
source,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BuildPathToInstallationFileEx (
|
|
IN PCTSTR Filename,
|
|
OUT PTSTR PathBuffer,
|
|
IN DWORD PathBufferSize,
|
|
IN BOOL UseDuShare
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the path to the updated DU file, if one exists
|
|
and the caller wanted this. Otherwise it will simply return the path
|
|
to the CD file.
|
|
|
|
Arguments:
|
|
|
|
Filename - Specifies the filename to look for
|
|
|
|
PathBuffer - Receives the full path to the file
|
|
|
|
PathBufferSize - Specifies the size in chars of the above buffer
|
|
|
|
UseDuShare - Specifies TRUE if the function should check the DU
|
|
location first
|
|
|
|
Return Value:
|
|
|
|
TRUE if building the path was successful. This does not guarantee the file exist.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (g_DuShare[0] && UseDuShare) {
|
|
if (BuildPath (PathBuffer, PathBufferSize, g_DuShare, Filename) &&
|
|
pDoesFileExist (PathBuffer)
|
|
) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return BuildPath (PathBuffer, PathBufferSize, LegacySourcePath, Filename);
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
DuGetUpdatesPath (
|
|
VOID
|
|
)
|
|
{
|
|
return g_DuShare[0] ? g_DuShare : NULL;
|
|
}
|
|
|
|
BOOL
|
|
DuDoesUpdatedFileExistEx (
|
|
IN PCTSTR Filename,
|
|
OUT PTSTR PathBuffer, OPTIONAL
|
|
IN DWORD PathBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if there exists an updated file with the given name.
|
|
|
|
Arguments:
|
|
|
|
Filename - Specifies the filename to look for
|
|
|
|
PathBuffer - Receives the full path to the file; optional
|
|
|
|
PathBufferSize - Specifies the size in chars of the above buffer
|
|
|
|
Return Value:
|
|
|
|
TRUE if a DU file with this name exists, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR path[MAX_PATH];
|
|
|
|
if (g_DuShare[0] &&
|
|
BuildPath (path, ARRAYSIZE(path), g_DuShare, Filename) &&
|
|
pDoesFileExist (path)
|
|
) {
|
|
if (PathBuffer) {
|
|
return SUCCEEDED (StringCchCopy (PathBuffer, PathBufferSize, path));
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
UINT
|
|
DuSetupPromptForDisk (
|
|
HWND hwndParent, // parent window of the dialog box
|
|
PCTSTR DialogTitle, // optional, title of the dialog box
|
|
PCTSTR DiskName, // optional, name of disk to insert
|
|
PCTSTR PathToSource, // optional, expected source path
|
|
PCTSTR FileSought, // name of file needed
|
|
PCTSTR TagFile, // optional, source media tag file
|
|
DWORD DiskPromptStyle, // specifies dialog box behavior
|
|
PTSTR PathBuffer, // receives the source location
|
|
DWORD PathBufferSize, // size of the supplied buffer
|
|
PDWORD PathRequiredSize // optional, buffer size needed
|
|
)
|
|
{
|
|
TCHAR buffer[MAX_PATH];
|
|
DWORD size;
|
|
|
|
if ((DiskPromptStyle & IDF_CHECKFIRST) &&
|
|
PathBuffer &&
|
|
PathBufferSize &&
|
|
FileSought &&
|
|
g_DuShare[0]
|
|
) {
|
|
|
|
if (BuildPath (buffer, ARRAYSIZE(buffer), g_DuShare, FileSought) &&
|
|
pDoesFileExist (buffer)
|
|
) {
|
|
|
|
size = lstrlen (buffer) + 1;
|
|
if (size > PathBufferSize) {
|
|
if (PathRequiredSize) {
|
|
*PathRequiredSize = size;
|
|
}
|
|
return DPROMPT_BUFFERTOOSMALL;
|
|
}
|
|
CopyMemory (PathBuffer, buffer, size * sizeof (buffer[0]));
|
|
return DPROMPT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return SetupPromptForDisk (
|
|
hwndParent,
|
|
DialogTitle,
|
|
DiskName,
|
|
PathToSource,
|
|
FileSought,
|
|
TagFile,
|
|
DiskPromptStyle,
|
|
PathBuffer,
|
|
PathBufferSize,
|
|
PathRequiredSize
|
|
);
|
|
}
|
|
|
|
VOID
|
|
DuInstallUpdatesInfFinal (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function installs the final installation section of STR_UPDATES_INF
|
|
if this file is found inside updates.cab
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!g_DuShare[0]) {
|
|
SetuplogError (
|
|
LogSevInformation,
|
|
TEXT("DUInfo: %1 is disabled"),
|
|
0,
|
|
TEXT(__FUNCTION__),
|
|
NULL,
|
|
NULL
|
|
);
|
|
return;
|
|
}
|
|
pInstallUpdatesInf (STR_DEFAULTINSTALLFINAL);
|
|
}
|