/*++ 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); }