|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1999.
//
// File: M O D I F Y . C P P
//
// Contents: Routines used to setup modifications to the network
// configuration.
//
// Notes:
//
// Author: shaunco 15 Jan 1999
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include "classinst.h"
#include "filtdevs.h"
#include "guisetup.h"
#include "inetcfg.h"
#include "lockdown.h"
#include "ncmsz.h"
#include "ncreg.h"
#include "ncsvc.h"
#include "ndispnp.h"
#include "netcfg.h"
#include "persist.h"
#include "pnpbind.h"
#include "pszarray.h"
#include "util.h"
#include "wscfg.h"
#include "ncwins.h"
#include "ncperms.h"
CNetConfig* CModifyContext::PNetConfig () { TraceFileFunc(ttidNetcfgBase);
CNetConfig* pNetConfig;
Assert ((LONG_PTR)this > FIELD_OFFSET(CNetConfig, ModifyCtx));
// Get our containing CNetConfig pointer.
//
pNetConfig = CONTAINING_RECORD(this, CNetConfig, ModifyCtx); pNetConfig->Core.DbgVerifyData ();
return pNetConfig; }
HRESULT CModifyContext::HrDirtyComponent ( IN const CComponent* pComponent) { TraceFileFunc(ttidNetcfgBase);
Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent);
m_hr = m_DirtyComponents.HrInsertComponent (pComponent, INS_IGNORE_IF_DUP | INS_SORTED);
#if DBG
m_fComponentExplicitlyDirtied = TRUE; #endif
TraceHr (ttidError, FAL, m_hr, FALSE, "CModifyContext::HrDirtyComponentAndComponentsAbove"); return m_hr; }
HRESULT CModifyContext::HrDirtyComponentAndComponentsAbove ( IN const CComponent* pComponent) { TraceFileFunc(ttidNetcfgBase);
GCCONTEXT Ctx;
Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent);
// And insert the component itself.
//
m_hr = HrDirtyComponent (pComponent);
// Only dirty the ones above if this component doesn't have the
// NCF_DONTEXPOSELOWER characteristic.
//
if ((S_OK == m_hr) && !(pComponent->m_dwCharacter & NCF_DONTEXPOSELOWER)) { // Initialize the members of our context structure for recursion.
//
ZeroMemory (&Ctx, sizeof(Ctx)); Ctx.pStackTable = &(PNetConfig()->Core.StackTable); Ctx.pComponents = &m_DirtyComponents;
// Insert all of the component above.
//
GetComponentsAboveComponent (pComponent, &Ctx); m_hr = Ctx.hr; }
TraceHr (ttidError, FAL, m_hr, FALSE, "CModifyContext::HrDirtyComponentAndComponentsAbove"); return m_hr; }
HRESULT CModifyContext::HrApplyIfOkOrCancel ( IN BOOL fApply) { TraceFileFunc(ttidNetcfgBase);
HRESULT hr; CNetConfig* pNetConfig;
Assert (m_fPrepared); pNetConfig = PNetConfig();
// Only apply if the context result is S_OK.
//
if (fApply && (S_OK == m_hr)) { // Setupapi calls that we make during ApplyChanges have the
// potential to return control to our clients windows message loop.
// When this happens, and our clients are poorly written, they
// may try to re-enter us on the same thread. This is disaster
// waiting to happen, so we need to prevent it by raising our
// reentrancy protection level before we start apply changes.
//
pNetConfig->Notify.PINetCfg()->RaiseRpl (RPL_DISALLOW);
ApplyChanges ();
pNetConfig->Notify.PINetCfg()->LowerRpl (RPL_DISALLOW);
// Delete those components from m_CoreStartedWith that are not
// in the current core and reset the modify context.
//
m_CoreStartedWith.Components.FreeComponentsNotInOtherComponentList ( &pNetConfig->Core.Components); m_CoreStartedWith.Clear();
hr = S_OK;
// Return the correct HRESULT to the caller. If we've successfully
// applied, but need a reboot, return so.
//
if (m_fRebootRecommended || m_fRebootRequired) { hr = NETCFG_S_REBOOT; } } else { // Cancel and release all notify objects. Do this for what is
// in the core as well as what we started with. (There will
// be some overlap, but they will only be released once.)
// We need to do both sets so we don't miss releasing components
// that have been removed. (Removed components won't be in
// current core, but they will be in the core that we started
// with.) Likewise, if we just released the core we started with,
// we'd miss releasing those components that were added.)
//
pNetConfig->Notify.ReleaseAllNotifyObjects (pNetConfig->Core.Components, TRUE); pNetConfig->Notify.ReleaseAllNotifyObjects (m_CoreStartedWith.Components, TRUE);
// Delete those components from m_CoreStartedWith that are not
// in the current core. Then delete all the components in the
// current core and reload from our persistent storage.
// (This has the nice effect of invalidating all outstanding
// INetCfgComponent interfaces.)
//
m_CoreStartedWith.Components.FreeComponentsNotInOtherComponentList ( &pNetConfig->Core.Components); pNetConfig->Core.Free ();
// Eject both cores (didn't you just know this metaphor was coming ;-)
// and reload the core from our persisted binary. This magically,
// and completely rolls everything back.
//
m_CoreStartedWith.Clear(); pNetConfig->Core.Clear();
// Return reason for failure through hr.
//
hr = m_hr;
// Reload the configuration and, if successful, it means m_hr
// will be S_OK. If unsuccessful, m_hr will be the error and will
// prevent subsequent operations.
//
m_hr = HrLoadNetworkConfigurationFromRegistry (KEY_READ, pNetConfig); }
// Very important to set m_fPrepared back to FALSE so that HrPrepare gets
// called for the next modifcation and correctly copy the core etc.
//
m_fPrepared = FALSE; m_AddedBindPaths.Clear(); m_DeletedBindPaths.Clear(); m_DirtyComponents.Clear(); #if DBG
m_fComponentExplicitlyDirtied = FALSE; #endif
Assert (!m_fPrepared); Assert (m_CoreStartedWith.FIsEmpty()); Assert (m_AddedBindPaths.FIsEmpty()); Assert (m_DeletedBindPaths.FIsEmpty()); Assert (m_DirtyComponents.FIsEmpty()); Assert (0 == m_ulRecursionDepth); Assert (!m_fComponentExplicitlyDirtied);
Assert ((S_OK == hr) || (NETCFG_S_REBOOT == hr) || FAILED(hr)); TraceHr (ttidError, FAL, hr, NETCFG_S_REBOOT == hr, "CModifyContext::HrApplyIfOkOrCancel"); return hr; }
HRESULT CModifyContext::HrPrepare () { TraceFileFunc(ttidNetcfgBase);
Assert (S_OK == m_hr); Assert (!m_fPrepared); Assert (m_CoreStartedWith.FIsEmpty()); Assert (m_AddedBindPaths.FIsEmpty()); Assert (m_DeletedBindPaths.FIsEmpty()); Assert (m_DirtyComponents.FIsEmpty()); Assert (0 == m_ulRecursionDepth); Assert (!m_fComponentExplicitlyDirtied);
CNetConfig* pThis;
pThis = PNetConfig();
// Prepare the bind context. This will ensure all of the external
// data for all components is loaded as well as all ensuring that
// all notify objects have been initialized.
//
m_hr = m_RegBindCtx.HrPrepare (pThis); if (S_OK != m_hr) { goto finished; }
// Snapshot the current core so that we know what we started with.
// We will use the differences when we apply (if we get that far).
//
m_hr = m_CoreStartedWith.HrCopyCore (&pThis->Core); if (S_OK != m_hr) { goto finished; }
// Reserve room for 64 components in the core.
// (64 * 4 = 256 bytes on 32-bit platforms)
//
m_hr = pThis->Core.Components.HrReserveRoomForComponents (64); if (S_OK != m_hr) { goto finished; }
// Reserve room for 64 stack entries in the core.
// (64 * (4 + 4) = 512 bytes on 32-bit platforms)
//
m_hr = pThis->Core.StackTable.HrReserveRoomForEntries (64); if (S_OK != m_hr) { goto finished; }
// Reserve room in our added list for 64 bindpaths of 8 components.
// (64 * 16 = 1K bytes on 32-bit platforms)
//
m_hr = m_AddedBindPaths.HrReserveRoomForBindPaths (64); if (S_OK != m_hr) { goto finished; }
// Reserve room in our deleted list for 64 bindpaths of 8 components.
// (64 * 16 = 1K bytes on 32-bit platforms)
//
m_hr = m_DeletedBindPaths.HrReserveRoomForBindPaths (64); if (S_OK != m_hr) { goto finished; }
// Reserve room for 64 components in the dirty component list.
// (64 * 4) = 256 bytes on 32-bit platforms)
//
m_hr = m_DirtyComponents.HrReserveRoomForComponents (64); if (S_OK != m_hr) { goto finished; }
m_fPrepared = TRUE;
finished: TraceHr (ttidError, FAL, m_hr, FALSE, "CModifyContext::HrPrepare"); return m_hr; }
HRESULT CModifyContext::HrBeginBatchOperation () { TraceFileFunc(ttidNetcfgBase);
Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (0 == m_ulRecursionDepth);
TraceTag (ttidBeDiag, "Begin batch operation...");
PushRecursionDepth(); return m_hr; }
HRESULT CModifyContext::HrEndBatchOperation ( IN EBO_FLAG Flag) { TraceFileFunc(ttidNetcfgBase);
HRESULT hr;
Assert (m_fPrepared); Assert (1 == m_ulRecursionDepth);
if (EBO_COMMIT_NOW == Flag) { TraceTag (ttidBeDiag, "End batch (commiting changes)...");
hr = HrPopRecursionDepth (); } else { Assert (EBO_DEFER_COMMIT_UNTIL_APPLY == Flag);
TraceTag (ttidBeDiag, "End batch (deferring commit until Apply)...");
m_ulRecursionDepth = 0; hr = S_OK; }
Assert (0 == m_ulRecursionDepth);
TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrEndBatchOperation"); return hr; }
VOID CModifyContext::PushRecursionDepth () { TraceFileFunc(ttidNetcfgBase); Assert (S_OK == m_hr); Assert (m_fPrepared);
m_ulRecursionDepth++; }
HRESULT CModifyContext::HrPopRecursionDepth () { TraceFileFunc(ttidNetcfgBase); Assert (m_fPrepared); Assert (m_ulRecursionDepth > 0);
m_ulRecursionDepth--;
if (0 != m_ulRecursionDepth) { return m_hr; }
// We're at the top-level of the install or remove modifcation so
// apply or cancel the changes depending on the state of the context
// result.
//
HRESULT hr;
hr = HrApplyIfOkOrCancel (S_OK == m_hr);
TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrPopRecursionDepth"); return hr; }
//----------------------------------------------------------------------------
// This is a convenience method to find and process Winsock Remove
// section for a component which is about to be removed.
HRESULT CModifyContext::HrProcessWinsockRemove(IN const CComponent *pComponent) { TraceFileFunc(ttidNetcfgBase);
HINF hinf = NULL; HKEY hkeyInstance = NULL; HRESULT hr;
Assert(pComponent);
hr = pComponent->HrOpenInfFile(&hinf); if (S_OK == hr) { static const WCHAR c_szRemoveSectionSuffix[] = L".Remove";
// We get the remove section name and process all relevant sections
WCHAR szRemoveSection[_MAX_PATH]; DWORD cbBuffer = sizeof (szRemoveSection);
hr = pComponent->HrOpenInstanceKey (KEY_READ, &hkeyInstance, NULL, NULL);
if(S_OK == hr) { hr = HrRegQuerySzBuffer (hkeyInstance, REGSTR_VAL_INFSECTION, szRemoveSection, &cbBuffer);
if (S_OK == hr) { //HrAddOrRemoveWinsockDependancy processes the winsock
//remove section in the given inf file and then calls
//MigrateWinsockConfiguration which will cause the
//necessary PnP notifications to be issued to the
//interested application.
wcscat (szRemoveSection, c_szRemoveSectionSuffix);
hr = HrAddOrRemoveWinsockDependancy (hinf, szRemoveSection); } RegSafeCloseKey (hkeyInstance); } }
TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrProcessWinsockRemove (%S)", pComponent->PszGetPnpIdOrInfId());
return hr; }
VOID CModifyContext::ApplyChanges () { TraceFileFunc(ttidNetcfgBase);
HRESULT hr; CNetConfig* pNetConfig; CComponentList::const_iterator iter; CComponent* pComponent; CFilterDevices FilterDevices (&PNetConfig()->Core); CPszArray ServiceNames; CServiceManager ServiceManager; PCWSTR pszService; BOOL fRebootNeeded; BOOL fMigrateWinsock; BOOL fModifyFilterDevices; BOOL fSignalNetworkProviderLoaded; BOOL fUserIsNetConfigOps; BOOL fCallCoFreeUnusedLibraries;
Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (0 == m_ulRecursionDepth);
pNetConfig = PNetConfig();
fMigrateWinsock = FALSE; fModifyFilterDevices = FALSE; fSignalNetworkProviderLoaded = FALSE; fUserIsNetConfigOps = FIsUserNetworkConfigOps(); fCallCoFreeUnusedLibraries = FALSE;
//+-----------------------------------------------------------------------
// Step 0: Prepare m_AddedBindPaths, m_DeletedBindPaths, and
// m_DirtyComponents.
//
// Add the bindpaths that were once disabled, but are now enabled, to
// m_AddedBindPaths. We do this so that PnP notifications are sent for
// them.
//
m_hr = m_AddedBindPaths.HrAddBindPathsInSet1ButNotInSet2 ( &m_CoreStartedWith.DisabledBindings, &pNetConfig->Core.DisabledBindings); if (S_OK != m_hr) { return; }
// Add the bindpaths that were once enabled, but are now disabled, to
// m_DeletedBindPaths. We do this so that PnP notifications are sent for
// them.
//
m_hr = m_DeletedBindPaths.HrAddBindPathsInSet1ButNotInSet2 ( &pNetConfig->Core.DisabledBindings, &m_CoreStartedWith.DisabledBindings); if (S_OK != m_hr) { return; }
// m_fDirtyComponents should be empty unless we've explicitly dirtied
// one or more. If m_fDirtyComponents were not empty, it would probably
// mean we forgot to clear it after the last Apply or Cancel.
// Conversely, m_DirtyComponents better not be empty if we've explicitly
// dirtied one or more.
//
Assert (FIff(!m_fComponentExplicitlyDirtied, m_DirtyComponents.FIsEmpty()));
// Dirty the affected components (owners and adapters in bindpaths of
// length 2) from the added and deleted bindpaths. We need to write
// bindings for these components.
//
m_hr = m_AddedBindPaths.HrGetAffectedComponentsInBindingSet ( &m_DirtyComponents); if (S_OK != m_hr) { return; }
m_hr = m_DeletedBindPaths.HrGetAffectedComponentsInBindingSet ( &m_DirtyComponents); if (S_OK != m_hr) { return; }
// Dirty components that exist in the current core, but not in the core
// we started with. (These are added components).
//
m_hr = m_DirtyComponents.HrAddComponentsInList1ButNotInList2 ( &pNetConfig->Core.Components, &m_CoreStartedWith.Components); if (S_OK != m_hr) { return; }
// Dirty components that exist in the core we started with, but not in
// the current core. (These are removed components).
//
m_hr = m_DirtyComponents.HrAddComponentsInList1ButNotInList2 ( &m_CoreStartedWith.Components, &pNetConfig->Core.Components); if (S_OK != m_hr) { return; }
g_pDiagCtx->Printf (ttidBeDiag, "Step 0: The following components are dirty:\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
if (!pNetConfig->Core.Components.FComponentInList (pComponent)) { g_pDiagCtx->Printf (ttidBeDiag, " %-12S (removed)\n", pComponent->PszGetPnpIdOrInfId()); } else if (!m_CoreStartedWith.Components.FComponentInList (pComponent)) { g_pDiagCtx->Printf (ttidBeDiag, " %-12S (installed)\n", pComponent->PszGetPnpIdOrInfId()); } else { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pComponent->PszGetPnpIdOrInfId()); } }
// Reserve room for 32 pointers to service names. We use this buffer
// to start and stop services.
//
m_hr = ServiceNames.HrReserveRoomForPointers (32); if (S_OK != m_hr) { return; }
// See if we are going to modify any filter devices. If we are,
// we'll go through all of the steps of loading filter devices, removing
// any we don't need, installing any new ones, and binding them all up.
// We only modify filter devices if the user is a normal admin and not
// a netcfgop.
//
// This test could be further refined to see if we had any filters which
// were dirty or if we had any dirty adapters which are filtered.
//
if (!fUserIsNetConfigOps) { fModifyFilterDevices = pNetConfig->Core.FContainsFilterComponent() || m_CoreStartedWith.FContainsFilterComponent(); } else { Assert(!fModifyFilterDevices); }
if (fModifyFilterDevices) { // Allow the filter devices structure to reserve whatever memory it
// may need.
//
m_hr = FilterDevices.HrPrepare (); if (S_OK != m_hr) { return; } }
pNetConfig->Core.DisabledBindings.Printf (ttidBeDiag, " The following bindings are currently disabled:\n");
//+-----------------------------------------------------------------------
// Step 1: Save the network configuration binary.
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 1: Save the network configuration binary.\n");
HrSaveNetworkConfigurationToRegistry (pNetConfig);
//+-----------------------------------------------------------------------
// Step 2: Write the static bindings for all changed components.
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 2: Write the following static bindings.\n");
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// If any protocols are dirty, we'll want to migrate winsock
// configuration later.
//
if (NC_NETTRANS == pComponent->Class()) { fMigrateWinsock = TRUE; }
// If the component is in the core, write its bindings.
// If it is not in the core, it means it has been removed and
// we should therefore remove its bindings.
//
if (pNetConfig->Core.Components.FComponentInList (pComponent)) { hr = m_RegBindCtx.HrWriteBindingsForComponent (pComponent);
// Remember any errors, but continue.
//
if (S_OK != hr) { Assert (FAILED(hr)); m_hr = hr; } } else { // Only delete if we're not installing another version of this
// component that has a duplicate PnpId. If we had already
// written the bindings for the newly installed one, and then
// deleted the ones for the removed (yet duplicate PnpId), we'd
// effectivly delete the bindings for the new one too. See the
// comments at step 6 for how we can get into this case.
//
if (!FIsEnumerated (pComponent->Class()) || !pNetConfig->Core.Components.PFindComponentByPnpId ( pComponent->m_pszPnpId)) { // There's no reason to fail if we can't delete the bindings.
// The entire component is about to be tossed anyway.
//
(VOID) m_RegBindCtx.HrDeleteBindingsForComponent (pComponent); } } }
//+-----------------------------------------------------------------------
// Step 3: Notify ApplyRegistryChanges
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 3: Notify: apply registry changes\n");
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
pComponent->Notify.ApplyRegistryChanges ( pNetConfig->Notify.PINetCfg(), &fRebootNeeded);
if (fRebootNeeded) { m_fRebootRecommended = TRUE;
g_pDiagCtx->Printf (ttidBeDiag, " %S notify object wants a reboot\n", pComponent->m_pszInfId); } }
// Migrate Winsock configuration if needed.
// Important to do this after the LANA map is written, afer Notify Applys
// are called, but before any services are started.
//
if (fMigrateWinsock) { g_pDiagCtx->Printf (ttidBeDiag, "Migrating winsock configuration.\n"); (VOID) HrMigrateWinsockConfiguration (); }
//+-----------------------------------------------------------------------
// Step 4: Unbind deleted bindpaths.
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 4: Unbind the following deleted bindings:\n");
if (!m_DeletedBindPaths.FIsEmpty()) { // We don't need to send UNBIND notifications for bindpaths that
// involve adapters that have been removed. They will be unbound
// automatically when the adapter is uninstalled. (For the case
// when the class installer is notifying us of a removed adapter,
// its important NOT to try to send an UNBIND notification because
// the adapter has already been uninstalled (hence unbound) and
// our notification might come back in error causing us to need
// a reboot uneccessary.
//
// So, remove the bindpaths in m_DeletedBindPaths that involve
// adapters that have been removed.
//
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// If its enumerated, and not in the current core, its a
// removed adapter.
//
if (FIsEnumerated (pComponent->Class()) && !pNetConfig->Core.Components.FComponentInList (pComponent)) { m_DeletedBindPaths.RemoveBindPathsWithComponent (pComponent); } }
m_DeletedBindPaths.SortForPnpUnbind ();
m_RegBindCtx.PnpBindOrUnbindBindPaths (UNBIND, &m_DeletedBindPaths, &fRebootNeeded);
if (fRebootNeeded) { m_fRebootRecommended = TRUE; } }
//+-----------------------------------------------------------------------
// Step 5: Stop services for removed components.
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 5: Stop the following services:\n");
Assert (0 == ServiceNames.Count()); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// Ignore enumerated components because they will have their drivers
// stopped automatically (if appropriate) when they are removed.
// Ignore components that are in the current core (not being removed).
//
if (FIsEnumerated (pComponent->Class()) || pNetConfig->Core.Components.FComponentInList (pComponent)) { continue; }
// Winsock remove section needs to be processed for every
// component that is being removed in order to update
// Transport key for the winsock registry settings
HrProcessWinsockRemove (pComponent);
// If its a protcol, send an UNLOAD before trying to stop the service.
//
if ((NC_NETTRANS == pComponent->Class()) || (NCF_NDIS_PROTOCOL & pComponent->m_dwCharacter)) { // Unload can fail as a lot of drivers do not support it.
// Treat it as an 'FYI' indication and don't set the reboot
// flag if it fails.
//
(VOID) HrPnpUnloadDriver (NDIS, pComponent->Ext.PszBindName()); }
// Ignore components that don't have any services.
if (!pComponent->Ext.PszCoServices()) { continue; }
for (pszService = pComponent->Ext.PszCoServices(); *pszService; pszService += wcslen(pszService) + 1) { (VOID)ServiceNames.HrAddPointer (pszService);
g_pDiagCtx->Printf (ttidBeDiag, " %S", pszService); } g_pDiagCtx->Printf (ttidBeDiag, "\n"); }
if (ServiceNames.Count() > 0) { static const CSFLAGS CsStopFlags = { FALSE, // FALSE means don't start
SERVICE_CONTROL_STOP, // use this control instead
15000, // wait up to 15 seconds...
SERVICE_STOPPED, // ... for service to reach this state
FALSE, //
};
hr = ServiceManager.HrControlServicesAndWait ( ServiceNames.Count(), ServiceNames.begin(), &CsStopFlags);
if (S_OK != hr) { m_fRebootRequired = TRUE;
g_pDiagCtx->Printf (ttidBeDiag, " some service failed to stop (hr = 0x%08X)\n", hr);
// Unfortunately, there is no easy way to get back which service
// did not stop and then to figure out which component contains
// that service. Sooo, we'll just put every component that is
// being removed in lockdown. This isn't a big deal when the UI
// is doing the removal because it only removes things one at a
// time.
//
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
if (FIsEnumerated (pComponent->Class()) || !pComponent->Ext.PszCoServices() || pNetConfig->Core.Components.FComponentInList (pComponent)) { continue; }
LockdownComponentUntilNextReboot (pComponent->m_pszInfId); } }
ServiceNames.Clear(); }
//+-----------------------------------------------------------------------
// Step 5a: Uninstall filters first.
//
if (fModifyFilterDevices) { g_pDiagCtx->Printf(ttidBeDiag, "Step 5a: Remove filter devices:\n");
// Order is of utmost importance here. Remove must be called first
// because it initializes some state internal to FilterDevices. Start
// must come after Write and Write obviously has to come after all
// new filter devices are installed.
//
FilterDevices.LoadAndRemoveFilterDevicesIfNeeded (); }
//+-----------------------------------------------------------------------
// Step 6: Uninstall removed components.
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 6: Uninstall the following components:\n");
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// If the component is in the core, ignore it.
// If it is not in the core, it means it has been removed and
// we should therefore remove its bindings.
//
if (pNetConfig->Core.Components.FComponentInList (pComponent)) { continue; }
// If this is an enumerated component whose PnpId matches that of
// a component in the current core, we've run into a special case.
// This can happen when the external data (like the NetCfgInstanceId)
// is corrupted and the class installer was told to update the
// component. The class installer is really told to "install" the
// component, but if it already exists as determined by the presence
// of NetCfgInstanceId, the class installer translates it to "update".
// Without the key, the class installer thinks its installing
// a new one. We detect the duplicate PnpId and remove the "prior"
// so we can install the "new". This "prior" instance is what we
// are finalizing the remove of, but if we call HrCiRemoveComponent,
// it just removes the same PnpId that the class installer told us
// to install. By not calling HrCiRemoveComponent for this case,
// the "prior" instance key gets reused implicitly by the "new"
// instance.
//
if (FIsEnumerated (pComponent->Class()) && pNetConfig->Core.Components.PFindComponentByPnpId ( pComponent->m_pszPnpId)) { g_pDiagCtx->Printf (ttidBeDiag, " Skip removal of %S because a duplicate was installed\n", pComponent->m_pszPnpId);
continue; }
g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pComponent->PszGetPnpIdOrInfId());
hr = HrCiRemoveComponent (pComponent, &pComponent->m_strRemoveSection);
// We can ignore SPAPI_E_NO_SUCH_DEVINST because the class installer
// may have already removed it and is just notifying us.
//
if ((S_OK != hr) && (SPAPI_E_NO_SUCH_DEVINST != hr)) { m_fRebootRequired = TRUE;
g_pDiagCtx->Printf (ttidBeDiag, " ^^^ needs a reboot (hr = 0x%08X)\n", hr); } }
//+-----------------------------------------------------------------------
// Step 6a: Modify filter devices.
//
if (fModifyFilterDevices) { g_pDiagCtx->Printf (ttidBeDiag, "Step 6a: Modify filter devices:\n");
FilterDevices.InstallFilterDevicesIfNeeded ();
(VOID)m_RegBindCtx.HrWriteBindingsForFilterDevices (&FilterDevices);
PruneNdisWanBindPathsIfActiveRasConnections ( &FilterDevices.m_BindPathsToRebind, &fRebootNeeded);
if (fRebootNeeded) { m_fRebootRecommended = TRUE; }
m_RegBindCtx.PnpBindOrUnbindBindPaths (UNBIND, &FilterDevices.m_BindPathsToRebind, &fRebootNeeded);
if (fRebootNeeded) { m_fRebootRecommended = TRUE; }
g_pDiagCtx->Printf (ttidBeDiag, "Step 6b: Starting filter devices:\n");
FilterDevices.StartFilterDevices (); FilterDevices.Free (); }
//+-----------------------------------------------------------------------
// Step 7: Start services for added components.
//
Assert (0 == ServiceNames.Count());
g_pDiagCtx->Printf (ttidBeDiag, "Step 7: Start the following drivers/services:\n");
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// If the component is in the core we started with, ignore it.
// If it is not in the core we started with, it means it is newly
// installed we should therefore start its services.
//
if (m_CoreStartedWith.Components.FComponentInList (pComponent)) { continue; }
// If we've added a network client, we'll need to signal the
// network provider loaded event after we've started its service.
//
if (NC_NETCLIENT == pComponent->Class()) { fSignalNetworkProviderLoaded = TRUE; }
if (FIsEnumerated (pComponent->Class())) { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pComponent->m_pszPnpId);
hr = pComponent->HrStartOrStopEnumeratedComponent (DICS_START);
if (S_OK != hr) { m_fRebootRecommended = TRUE;
g_pDiagCtx->Printf (ttidBeDiag, " ^^^ needs a reboot (hr = 0x%08X)\n", hr); }
if (FIsPhysicalNetAdapter (pComponent->Class(), pComponent->m_dwCharacter) && FInSystemSetup()) { ProcessAdapterAnswerFileIfExists (pComponent); } } else if (pComponent->Ext.PszCoServices()) { for (pszService = pComponent->Ext.PszCoServices(); *pszService; pszService += wcslen(pszService) + 1) { (VOID)ServiceNames.HrAddPointer (pszService);
g_pDiagCtx->Printf (ttidBeDiag, " %S", pszService); } g_pDiagCtx->Printf (ttidBeDiag, "\n");
// If we're in system setup, then exclude whatever services
// the component has marked as such.
//
if (FInSystemSetup()) { ExcludeMarkedServicesForSetup (pComponent, &ServiceNames); } } }
if ((ServiceNames.Count() > 0) && !(g_pDiagCtx->Flags() & DF_DONT_START_SERVICES)) { static const CSFLAGS CsStartFlags = { TRUE, // TRUE means start
0, 20000, // wait up to 20 seconds...
SERVICE_RUNNING, // ... for service to reach this state
TRUE, // ignore demand-start and disabled
};
hr = ServiceManager.HrControlServicesAndWait ( ServiceNames.Count(), ServiceNames.begin(), &CsStartFlags);
if (S_OK != hr) { m_fRebootRecommended = TRUE;
g_pDiagCtx->Printf (ttidBeDiag, " some service failed to start (hr = 0x%08X)\n", hr); } }
//+-----------------------------------------------------------------------
// Step 8: Bind added bindpaths.
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 8: Bind the following added bindings:\n");
if (fModifyFilterDevices) { hr = m_AddedBindPaths.HrAppendBindingSet ( &FilterDevices.m_BindPathsToRebind); if (S_OK != hr) { // Well, that's not good, but there is nothing we can do about
// it now. (Most likely we ran out of memory.)
} }
if (!m_AddedBindPaths.FIsEmpty()) { CBindPath* pBindPath;
// We don't need to send BIND notifications for bindpaths that
// involve adapters that have been newly installed. They will be
// bound automatically when the adapter is started.
//
// Update to the above comment: We THOUGHT that was correct, but it
// turns out it isn't. TDI isn't PNP (guess they must have missed
// THAT memo) and they are not re-reading the new bind strings
// from the registry when lower notifications bubble up. So, we
// have to send these BINDS for added adapters too.
//
// We should remove bindpaths that involve components that have
// been removed. These can end up in added bindpaths because way
// up in step 0, we added bindpaths that were disabled in the
// core we started with and that are no longer disabled in the
// current core. Well, when the component is removed, its disabled
// bindings are removed, so this case would have caused us to add
// the bindpath to this binding set.
//
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// If its been removed, remove any bindpaths that reference it.
//
if (!pNetConfig->Core.Components.FComponentInList (pComponent)) { m_AddedBindPaths.RemoveBindPathsWithComponent (pComponent); } }
// To prevent TDI from sending duplicate BINDs to its clients, we
// have to do a little more work. We need to not send the TDI
// BINDs to components that are newly installed. This is because
// TDI sent them the BINDs when we started the driver above.
// So, for each added bindpath, if it's going to the TDI layer, and
// the owner (topmost) component of the bindpath is newly installed,
// remove it from the added binding so we won't send a notification
// for it below.
//
pBindPath = m_AddedBindPaths.begin(); while (pBindPath != m_AddedBindPaths.end()) { UINT unLayer = GetPnpLayerForBindPath (pBindPath);
if ((TDI == unLayer) && !m_CoreStartedWith.Components.FComponentInList ( pBindPath->POwner())) { m_AddedBindPaths.erase (pBindPath); } else { pBindPath++; } }
m_AddedBindPaths.SortForPnpBind ();
m_RegBindCtx.PnpBindOrUnbindBindPaths (BIND, &m_AddedBindPaths, &fRebootNeeded);
if (fRebootNeeded) { // If BINDs fail, we should recommend a reboot, but one is
// not required for subsequent installs or removes.
//
m_fRebootRecommended = TRUE; } }
// Signal the network provider loaded event if needed.
// Probably best to do this after we've indiciated the PnP bindings
// (above) to the new clients.
//
if (fSignalNetworkProviderLoaded) { SignalNetworkProviderLoaded (); }
//+-----------------------------------------------------------------------
// Step 9: Allow notify objects to apply PnP changes
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 9: Notify: apply PnP changes\n");
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
pComponent->Notify.ApplyPnpChanges ( pNetConfig->Notify.PINetCfg(), &fRebootNeeded);
if (fRebootNeeded) { g_pDiagCtx->Printf (ttidBeDiag, " %S notify object wants a reboot\n", pComponent->m_pszInfId);
// If the component has been removed, treat the reboot
// as mandatory. (The reason being that we cannot risk a
// failed re-install.) We put the component into lockdown
// in this situation.
//
if (!pNetConfig->Core.Components.FComponentInList (pComponent)) { m_fRebootRequired = TRUE;
LockdownComponentUntilNextReboot (pComponent->m_pszInfId); } else { m_fRebootRecommended = TRUE; } } }
//+-----------------------------------------------------------------------
// Step 10: Release notify objects for removed components and
// process any DelFiles in the remove section of their INF.
//
g_pDiagCtx->Printf (ttidBeDiag, "Step 10: Release notify objects for removed components:\n");
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// Skip enumerated components (they don't have notify objects), and
// Skip components that were not removed.
// Skip components that don't have their INF open (like unsupported
// components that get removed during GUI setup.)
//
if (FIsEnumerated (pComponent->Class()) || pNetConfig->Core.Components.FComponentInList (pComponent) || !pComponent->GetCachedInfFile()) { continue; }
pComponent->Notify.ReleaseNotifyObject(NULL, FALSE);
fCallCoFreeUnusedLibraries = TRUE; }
if (fCallCoFreeUnusedLibraries) { g_pDiagCtx->Printf (ttidBeDiag, " calling CoFreeUnusedLibraries before running remove sections\n");
// Now ask COM to unload any DLLs hosting COM objects that are no longer
// in use. (This is a bit heavy-handed as it affects the entire process,
// but there is currently no other way to safely unload the DLLs hosting
// the notify objects of the removed components.)
//
CoFreeUnusedLibrariesEx(0, 0);
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// Skip enumerated components (they don't have notify objects), and
// Skip components that were not removed.
// Skip components that don't have their INF open (like unsupported
// components that get removed during GUI setup.)
//
if (FIsEnumerated (pComponent->Class()) || pNetConfig->Core.Components.FComponentInList (pComponent) || !pComponent->GetCachedInfFile()) { continue; }
g_pDiagCtx->Printf (ttidBeDiag, " %S [%S]\n", pComponent->PszGetPnpIdOrInfId(), pComponent->m_strRemoveSection.c_str());
(VOID) HrCiInstallFromInfSection( pComponent->GetCachedInfFile(), pComponent->m_strRemoveSection.c_str(), NULL, NULL, SPINST_FILES); } }
/*
//+-----------------------------------------------------------------------
// Step 11: Reconfigure moved bindings
//
// If we changed binding order, Send PnP RECONFIGURE for all dirty
// components that are neither installed nor removed so we
// pickup these order changes.
//
for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent);
// Skip components that have been newly installed or removed.
//
if (!pNetConfig->Core.Components.FComponentInList (pComponent) || !m_CoreStartedWith.Components.FComponentInList (pComponent)) { continue; }
// Note: send RECONFIGURE
} */ }
HRESULT CModifyContext::HrEnableOrDisableBindPath ( IN DWORD dwChangeFlag, IN CBindPath* pBindPath, IN INetCfgBindingPath* pIPath OPTIONAL) { TraceFileFunc(ttidNetcfgBase);
HRESULT hr; CNetConfig* pNetConfig; UINT CountBefore;
Assert (this); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert ((dwChangeFlag == NCN_ENABLE) || (dwChangeFlag == NCN_DISABLE)); Assert (pBindPath);
hr = S_OK; pNetConfig = PNetConfig();
// Get the count of bindpaths currently disabled. If it changes
// after we enable/disable this one, we'll inform notify objects
// about it. If the count does not change, it means the state
// of the bindpath has not changed.
//
CountBefore = pNetConfig->Core.DisabledBindings.CountBindPaths();
if (NCN_ENABLE == dwChangeFlag) { pNetConfig->Core.EnableBindPath (pBindPath); Assert (S_OK == hr); } else { hr = pNetConfig->Core.HrDisableBindPath (pBindPath); }
if ((S_OK == hr) && (pNetConfig->Core.DisabledBindings.CountBindPaths() != CountBefore)) { // Note: Need to protect against bad notify objects that
// switch the state of a bindpath we are notifying for.
// This could cause an infinite loop. Solve by adding a
// recursion count and bindset to the modify context dedicated
// to bindpath enabling/disabling. When the count is zero,
// we clear the bindingset, add the bindpath we are about to
// notify for, increment the recursion count and call
// NotifyBindPath. When the call returns, we decrement the
// recursion count, remove the bindpath from the binding set,
// and return. Before we call NotifyBindPath when the recursion
// count is non-zero, if the bindpath is already in the
// bindingset, we don't call.
//
pNetConfig->Notify.NotifyBindPath (dwChangeFlag, pBindPath, pIPath); }
TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrEnableOrDisableBindPath"); return hr; }
VOID CModifyContext::InstallOrRemoveRequiredComponents ( IN CComponent* pComponent, IN IOR_ACTION Action) { TraceFileFunc(ttidNetcfgBase);
HRESULT hr; HKEY hkeyInstance; PWSTR pszRequiredList; const WCHAR szDelims[] = L", ";
Assert (this); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent); Assert ((IOR_INSTALL == Action) || (IOR_REMOVE == Action));
pszRequiredList = NULL;
// Open the instance key of the component and read the RequireAll value.
// This value may not exist, which is okay, it means we have nothing
// to do.
//
hr = pComponent->HrOpenInstanceKey (KEY_READ, &hkeyInstance, NULL, NULL);
if (S_OK == hr) { HKEY hkeyNdi;
hr = HrRegOpenKeyEx (hkeyInstance, L"Ndi", KEY_READ, &hkeyNdi);
if (S_OK == hr) { hr = HrRegQuerySzWithAlloc (hkeyNdi, L"RequiredAll", &pszRequiredList);
RegCloseKey (hkeyNdi); }
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { hr = S_OK; Assert (!pszRequiredList); }
RegCloseKey (hkeyInstance); }
// If we have a list of required components, install or remove them.
//
if ((S_OK == hr) && pszRequiredList) { CNetConfig* pNetConfig; INetCfgComponent* pIComp;
pNetConfig = PNetConfig();
hr = pComponent->HrGetINetCfgComponentInterface ( pNetConfig->Notify.PINetCfg(), &pIComp);
if (S_OK == hr) { PCWSTR pszInfId; PWSTR pszNextInfId; OBO_TOKEN OboToken; CComponent* pComponentToRemove; WCHAR szInfFile [_MAX_PATH];
ZeroMemory (&OboToken, sizeof(OboToken)); OboToken.Type = OBO_COMPONENT; OboToken.pncc = pIComp;
// For each INF ID in the comma separate list of required
// components...
//
for (pszInfId = GetNextStringToken (pszRequiredList, szDelims, &pszNextInfId); pszInfId && *pszInfId; pszInfId = GetNextStringToken (NULL, szDelims, &pszNextInfId)) { if (IOR_INSTALL == Action) { NETWORK_INSTALL_PARAMS nip; COMPONENT_INSTALL_PARAMS Params;
ZeroMemory (&Params, sizeof(Params));
// Get the Class corresponding to the INF ID.
//
m_hr = HrCiGetClassAndInfFileOfInfId ( pszInfId, &Params.Class, szInfFile); if (S_OK != m_hr) { break; }
//$REVIEW:Should we stick the filename in the
// COMPONENT_INSTALL_PARAMS so that we don't grovel
// the INF directory to find it again?
// If so, store the filename buffer in the modify
// context so we don't take up stack space or heap space.
// Just need to be sure that we use it to install the
// component before we recurse and overwrite it.
// Pack the network install parameters and call the common
// function.
//
//$REVIEW: we probably need dwSetupFlags and dwUpgradeFromBuildNo
// in the modify context saved when it was called at
// recursion depth 0. Otherwise, things installed here
// during GUI setup will have wrong parameters.
//
nip.dwSetupFlags = 0; nip.dwUpgradeFromBuildNo = 0; nip.pszAnswerFile = NULL; nip.pszAnswerSection = NULL;
// Setup the component install parameters.
//
Params.pszInfId = pszInfId; Params.pszInfFile = szInfFile; Params.pOboToken = FIsEnumerated (Params.Class) ? NULL : &OboToken; Params.pnip = &nip;
//
// THIS MAY CAUSE RECURSION
//
// (just using pComponentToRemove as a placeholder
// for a required parameter.)
//
HrInstallNewOrReferenceExistingComponent ( Params, &pComponentToRemove); if (S_OK != m_hr) { break; } } else { Assert (IOR_REMOVE == Action);
// Search for the component to remove using its INF ID.
//
pComponentToRemove = pNetConfig->Core.Components. PFindComponentByInfId (pszInfId, NULL);
if (pComponentToRemove) { //
// THIS MAY CAUSE RECURSION
//
HrRemoveComponentIfNotReferenced ( pComponentToRemove, FIsEnumerated (pComponentToRemove->Class()) ? NULL : &OboToken, NULL); if (S_OK != m_hr) { break; } } } }
ReleaseObj (pIComp); }
MemFree (pszRequiredList); } }
//----------------------------------------------------------------------------
// Update a component. Do this by generating the bindings which involve
// the component and noting them as 'OldBindPaths'. The stack entries which
// involve the component are removed and re-generated and the bindings which
// involve the component are again noted as 'NewBindPaths'. The old bindings
// are compared to the new bindings and the differences are notified to
// notify objects as either being removed or added. For any removed bindings,
// we also remove them from the core's disabled set if they happen to exist
// there too.
//
// Assumptions:
// The INF for pComponent has already been re-run so that the potentially
// new values for UpperRange, LowerRange, etc. are present in the registry.
//
// pComponent has had its external data loaded already.
//
HRESULT CModifyContext::HrUpdateComponent ( IN CComponent* pComponent, IN DWORD dwSetupFlags, IN DWORD dwUpgradeFromBuildNo) { TraceFileFunc(ttidNetcfgBase);
HRESULT hr; CNetConfig* pNetConfig; CBindingSet OldBindPaths; CBindingSet NewBindPaths; CBindPath* pScan;
Assert (this); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent);
pNetConfig = PNetConfig();
// Now that we actually are going to modify something, push a new
// recursion depth.
//
PushRecursionDepth(); Assert (S_OK == m_hr);
// Generate the "old" bindings by noting those which involve the
// component.
//
hr = pNetConfig->Core.HrGetBindingsInvolvingComponent ( pComponent, GBF_ONLY_WHICH_CONTAIN_COMPONENT, &OldBindPaths); if (S_OK != hr) { goto finished; }
// Reload the external data so we pickup what the possibly updated
// INF has changed.
//
hr = pComponent->Ext.HrReloadExternalData ();
if (S_OK != hr) { goto finished; }
// Update the stack table entries for the component.
//
hr = pNetConfig->Core.StackTable.HrUpdateEntriesForComponent ( pComponent, &pNetConfig->Core.Components, INS_SORTED); if (S_OK != hr) { // This is not good. We ripped out the stack entries and failed
// putting them back in. The stack table now has no entries for
// this component. Prevent this from being applied by setting the
// modify context's HRESULT to the error.
//
m_hr = hr; goto finished; }
// Generate the "new" bindings by noting those which involve the
// component.
//
hr = pNetConfig->Core.HrGetBindingsInvolvingComponent ( pComponent, GBF_ONLY_WHICH_CONTAIN_COMPONENT, &NewBindPaths); if (S_OK != hr) { // Probably out of memory. Prevent apply by setting the modify
// context's HRESULT to the error.
//
m_hr = hr; goto finished; }
// Notify any bindpaths which have been removed.
//
for (pScan = OldBindPaths.begin(); pScan != OldBindPaths.end(); pScan++) { if (NewBindPaths.FContainsBindPath (pScan)) { continue; }
m_DeletedBindPaths.HrAddBindPath (pScan, INS_IGNORE_IF_DUP | INS_APPEND); pNetConfig->Core.DisabledBindings.RemoveBindPath (pScan); pNetConfig->Notify.NotifyBindPath (NCN_REMOVE, pScan, NULL); }
// Notify any bindpaths which have been added.
//
for (pScan = NewBindPaths.begin(); pScan != NewBindPaths.end(); pScan++) { if (OldBindPaths.FContainsBindPath (pScan)) { continue; }
m_AddedBindPaths.HrAddBindPath (pScan, INS_IGNORE_IF_DUP | INS_APPEND); pNetConfig->Notify.NotifyBindPath (NCN_ADD | NCN_ENABLE, pScan, NULL); }
// Notify that the component has been updated.
//
pNetConfig->Notify.ComponentUpdated (pComponent, dwSetupFlags, dwUpgradeFromBuildNo);
finished:
hr = HrPopRecursionDepth();
TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrUpdateComponent (%S)", pComponent->PszGetPnpIdOrInfId()); return hr; }
|