/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: ds.c Abstract: WMI data provider registration code Author: AlanWar Environment: Kernel Mode Revision History: --*/ #include "wmikmp.h" #include void WmipEnableCollectionForNewGuid( LPGUID Guid, PBINSTANCESET InstanceSet ); void WmipDisableCollectionForRemovedGuid( LPGUID Guid, PBINSTANCESET InstanceSet ); ULONG WmipDetermineInstanceBaseIndex( LPGUID Guid, PWCHAR BaseName, ULONG InstanceCount ); ULONG WmipMangleInstanceName( LPGUID Guid, PWCHAR Name, ULONG MaxMangledNameLen, PWCHAR MangledName ); NTSTATUS WmipBuildInstanceSet( PWMIREGGUID RegGuid, PWMIREGINFOW WmiRegInfo, ULONG BufferSize, PBINSTANCESET InstanceSet, ULONG ProviderId, LPCTSTR MofImagePath ); NTSTATUS WmipLinkDataSourceToList( PBDATASOURCE DataSource, BOOLEAN AddDSToList ); void WmipSendGuidUpdateNotifications( NOTIFICATIONTYPES NotificationType, ULONG GuidCount, PTRCACHE *GuidList ); void WmipGenerateBinaryMofNotification( PBINSTANCESET BinaryMofInstanceSet, LPCGUID Guid ); void WmipGenerateRegistrationNotification( PBDATASOURCE DataSource, ULONG NotificationCode ); NTSTATUS WmipAddMofResource( PBDATASOURCE DataSource, LPWSTR ImagePath, BOOLEAN IsImagePath, LPWSTR MofResourceName, PBOOLEAN NewMofResource ); PBINSTANCESET WmipFindISInDSByGuid( PBDATASOURCE DataSource, LPGUID Guid ); ULONG WmipUpdateAddGuid( PBDATASOURCE DataSource, PWMIREGGUID RegGuid, PWMIREGINFO WmiRegInfo, ULONG BufferSize, PBINSTANCESET *AddModInstanceSet ); PWCHAR GuidToString( PWCHAR s, ULONG SizeInBytes, LPGUID piid ); BOOLEAN WmipUpdateRemoveGuid( PBDATASOURCE DataSource, PWMIREGGUID RegGuid, PBINSTANCESET *AddModInstanceSet ); BOOLEAN WmipIsEqualInstanceSets( PBINSTANCESET InstanceSet1, PBINSTANCESET InstanceSet2 ); ULONG WmipUpdateModifyGuid( PBDATASOURCE DataSource, PWMIREGGUID RegGuid, PWMIREGINFO WmiRegInfo, ULONG BufferSize, PBINSTANCESET *AddModInstanceSet ); void WmipCachePtrs( LPGUID Ptr1, PBINSTANCESET Ptr2, ULONG *PtrCount, ULONG *PtrMax, PTRCACHE **PtrArray ); NTSTATUS WmipUpdateDataSource( PREGENTRY RegEntry, PWMIREGINFOW WmiRegInfo, ULONG RetSize ); void WmipRemoveDataSourceByDS( PBDATASOURCE DataSource ); NTSTATUS WmipRemoveDataSource( PREGENTRY RegEntry ); NTSTATUS WmipInitializeDataStructs( void ); NTSTATUS WmipEnumerateMofResources( PWMIMOFLIST MofList, ULONG BufferSize, ULONG *RetSize ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,WmipInitializeDataStructs) #pragma alloc_text(PAGE,WmipEnableCollectionForNewGuid) #pragma alloc_text(PAGE,WmipDisableCollectionForRemovedGuid) #pragma alloc_text(PAGE,WmipDetermineInstanceBaseIndex) #pragma alloc_text(PAGE,WmipMangleInstanceName) #pragma alloc_text(PAGE,WmipBuildInstanceSet) #pragma alloc_text(PAGE,WmipLinkDataSourceToList) #pragma alloc_text(PAGE,WmipSendGuidUpdateNotifications) #pragma alloc_text(PAGE,WmipGenerateBinaryMofNotification) #pragma alloc_text(PAGE,WmipGenerateMofResourceNotification) #pragma alloc_text(PAGE,WmipGenerateRegistrationNotification) #pragma alloc_text(PAGE,WmipAddMofResource) #pragma alloc_text(PAGE,WmipAddDataSource) #pragma alloc_text(PAGE,WmipFindISInDSByGuid) #pragma alloc_text(PAGE,WmipUpdateAddGuid) #pragma alloc_text(PAGE,WmipUpdateRemoveGuid) #pragma alloc_text(PAGE,WmipIsEqualInstanceSets) #pragma alloc_text(PAGE,WmipUpdateModifyGuid) #pragma alloc_text(PAGE,WmipCachePtrs) #pragma alloc_text(PAGE,WmipUpdateDataSource) #pragma alloc_text(PAGE,WmipRemoveDataSourceByDS) #pragma alloc_text(PAGE,WmipRemoveDataSource) #pragma alloc_text(PAGE,WmipEnumerateMofResources) #if DBG #pragma alloc_text(PAGE,GuidToString) #endif #endif #ifdef ALLOC_DATA_PRAGMA #pragma const_seg("PAGECONST") #endif const GUID WmipBinaryMofGuid = BINARY_MOF_GUID; // {4EE0B301-94BC-11d0-A4EC-00A0C9062910} const GUID RegChangeNotificationGuid = { 0x4ee0b301, 0x94bc, 0x11d0, { 0xa4, 0xec, 0x0, 0xa0, 0xc9, 0x6, 0x29, 0x10 } }; void WmipEnableCollectionForNewGuid( LPGUID Guid, PBINSTANCESET InstanceSet ) { WNODE_HEADER Wnode; PBGUIDENTRY GuidEntry; ULONG Status; BOOLEAN IsTraceLog; PAGED_CODE(); GuidEntry = WmipFindGEByGuid(Guid, FALSE); if (GuidEntry != NULL) { memset(&Wnode, 0, sizeof(WNODE_HEADER)); memcpy(&Wnode.Guid, Guid, sizeof(GUID)); Wnode.BufferSize = sizeof(WNODE_HEADER); WmipEnterSMCritSection(); if ((GuidEntry->EventRefCount > 0) && ((InstanceSet->Flags & IS_ENABLE_EVENT) == 0)) { // // Events were previously enabled for this guid, but not for this // instance set so call data source for instance set to enable // the events. First set the in progress flag and InstanceSet // set flag to denote that events have been enabled for the // instance set. InstanceSet->Flags |= IS_ENABLE_EVENT; // // If it is Tracelog, NewGuid notifications are piggybacked with // Registration call return. // IsTraceLog = ((InstanceSet->Flags & IS_TRACED) == IS_TRACED) ? TRUE : FALSE; if (IsTraceLog) { if (!(InstanceSet->DataSource->Flags & DS_KERNEL_MODE) ) { if (GuidEntry != NULL) { WmipUnreferenceGE(GuidEntry); } WmipLeaveSMCritSection(); return; } // // For the Kernel Mode Trace Providers pass on the context // Wnode.HistoricalContext = GuidEntry->LoggerContext; } GuidEntry->Flags |= GE_FLAG_NOTIFICATION_IN_PROGRESS; WmipLeaveSMCritSection(); WmipDeliverWnodeToDS(IRP_MN_ENABLE_EVENTS, InstanceSet->DataSource, &Wnode, Wnode.BufferSize); WmipEnterSMCritSection(); // // Now we need to check if events were disabled while the enable // request was in progress. If so go do the work to actually // disable them. if (GuidEntry->EventRefCount == 0) { Status = WmipDoDisableRequest(GuidEntry, TRUE, IsTraceLog, GuidEntry->LoggerContext, GE_FLAG_NOTIFICATION_IN_PROGRESS); } else { GuidEntry->Flags &= ~GE_FLAG_NOTIFICATION_IN_PROGRESS; } } // // Now check to see if collection needs to be enabled for this guid // if ((GuidEntry->CollectRefCount > 0) && ((InstanceSet->Flags & IS_ENABLE_COLLECTION) == 0) && (InstanceSet->Flags & IS_EXPENSIVE) ) { // // Collection was previously enabled for this guid, but not // for this instance set so call data source for instance set // to enable collection. First set the in progress flag and // InstanceSet set flag to denote that collection has been enabled // for the instance set. // GuidEntry->Flags |= GE_FLAG_COLLECTION_IN_PROGRESS; InstanceSet->Flags |= IS_ENABLE_COLLECTION; WmipLeaveSMCritSection(); WmipDeliverWnodeToDS(IRP_MN_ENABLE_COLLECTION, InstanceSet->DataSource, &Wnode, Wnode.BufferSize); WmipEnterSMCritSection(); // // Now we need to check if events were disabled while the enable // request was in progress. If so go do the work to actually // disable them. // if (GuidEntry->CollectRefCount == 0) { Status = WmipDoDisableRequest(GuidEntry, FALSE, FALSE, 0, GE_FLAG_COLLECTION_IN_PROGRESS); } else { GuidEntry->Flags &= ~GE_FLAG_COLLECTION_IN_PROGRESS; // // If there are any other threads that were waiting // until all of the enable/disable work completed, we // close the event handle to release them from their wait. // WmipReleaseCollectionEnabled(GuidEntry); } } WmipUnreferenceGE(GuidEntry); WmipLeaveSMCritSection(); } else { WmipAssert(FALSE); } } void WmipDisableCollectionForRemovedGuid( LPGUID Guid, PBINSTANCESET InstanceSet ) { WNODE_HEADER Wnode; PBGUIDENTRY GuidEntry; ULONG Status; BOOLEAN IsTraceLog; PAGED_CODE(); GuidEntry = WmipFindGEByGuid(Guid, FALSE); if (GuidEntry != NULL) { memset(&Wnode, 0, sizeof(WNODE_HEADER)); memcpy(&Wnode.Guid, Guid, sizeof(GUID)); Wnode.BufferSize = sizeof(WNODE_HEADER); WmipEnterSMCritSection(); if ((GuidEntry->EventRefCount > 0) && ((InstanceSet->Flags & IS_ENABLE_EVENT) != 0)) { // Events were previously enabled for this guid, but not for this // instance set so call data source for instance set to enable // the events. First set the in progress flag and InstanceSet // set flag to denote that events have been enabled for the // instance set. InstanceSet->Flags &= ~IS_ENABLE_EVENT; // // If it is Tracelog, RemoveGuid notifications are handled // through UnregisterGuids call. // IsTraceLog = ((InstanceSet->Flags & IS_TRACED) == IS_TRACED) ? TRUE : FALSE; if (IsTraceLog) { if ( !(InstanceSet->DataSource->Flags & DS_KERNEL_MODE)) { WmipUnreferenceGE(GuidEntry); WmipLeaveSMCritSection(); return; } Wnode.HistoricalContext = GuidEntry->LoggerContext; } GuidEntry->Flags |= GE_FLAG_NOTIFICATION_IN_PROGRESS; WmipLeaveSMCritSection(); WmipDeliverWnodeToDS(IRP_MN_DISABLE_EVENTS, InstanceSet->DataSource, &Wnode, Wnode.BufferSize); WmipEnterSMCritSection(); // // Now we need to check if events were disabled while the enable // request was in progress. If so go do the work to actually // disable them. if (GuidEntry->EventRefCount == 0) { Status = WmipDoDisableRequest(GuidEntry, TRUE, IsTraceLog, GuidEntry->LoggerContext, GE_FLAG_NOTIFICATION_IN_PROGRESS); } else { GuidEntry->Flags &= ~GE_FLAG_NOTIFICATION_IN_PROGRESS; } } // // Now check to see if collection needs to be enabled for this guid if ((GuidEntry->CollectRefCount > 0) && ((InstanceSet->Flags & IS_ENABLE_COLLECTION) != 0)) { // Collection was previously enabled for this guid, but not // for this instance set so call data source for instance set // to enable collection. First set the in progress flag and // InstanceSet set flag to denote that collection has been enabled // for the instance set. GuidEntry->Flags |= GE_FLAG_COLLECTION_IN_PROGRESS; InstanceSet->Flags &= ~IS_ENABLE_COLLECTION; WmipLeaveSMCritSection(); WmipDeliverWnodeToDS(IRP_MN_DISABLE_COLLECTION, InstanceSet->DataSource, &Wnode, Wnode.BufferSize); WmipEnterSMCritSection(); // // Now we need to check if events were disabled while the enable // request was in progress. If so go do the work to actually // disable them. if (GuidEntry->CollectRefCount == 0) { Status = WmipDoDisableRequest(GuidEntry, FALSE, FALSE, 0, GE_FLAG_COLLECTION_IN_PROGRESS); } else { GuidEntry->Flags &= ~GE_FLAG_COLLECTION_IN_PROGRESS; // // If there are any other threads that were waiting // until all of the enable/disable work completed, we // close the event handle to release them from their wait. // WmipReleaseCollectionEnabled(GuidEntry); } } WmipUnreferenceGE(GuidEntry); WmipLeaveSMCritSection(); } else { WmipAssert(FALSE); } } ULONG WmipDetermineInstanceBaseIndex( LPGUID Guid, PWCHAR BaseName, ULONG InstanceCount ) /*++ Routine Description: Figure out the base index for the instance names specified by a base instance name. We walk the list of instances sets for the guid and if there is a match in the base instance name we set the base instance index above that used by the previously registered instance set. Arguments: Guid points at guid for the instance names BaseName points at the base name for the instances InstanceCount is the count of instance names Return Value: Base index for instance name --*/ { PBGUIDENTRY GuidEntry; ULONG BaseIndex = 0; PLIST_ENTRY InstanceSetList; PBINSTANCESET InstanceSet; ULONG LastBaseIndex; PAGED_CODE(); UNREFERENCED_PARAMETER (InstanceCount); WmipEnterSMCritSection(); GuidEntry = WmipFindGEByGuid(Guid, FALSE); if (GuidEntry != NULL) { InstanceSetList = GuidEntry->ISHead.Flink; while (InstanceSetList != &GuidEntry->ISHead) { InstanceSet = CONTAINING_RECORD(InstanceSetList, INSTANCESET, GuidISList); if (InstanceSet->Flags & IS_INSTANCE_BASENAME) { if (wcscmp(BaseName, InstanceSet->IsBaseName->BaseName) == 0) { LastBaseIndex = InstanceSet->IsBaseName->BaseIndex + InstanceSet->Count; if (BaseIndex <= LastBaseIndex) { BaseIndex = LastBaseIndex; } } } InstanceSetList = InstanceSetList->Flink; } WmipUnreferenceGE(GuidEntry); } WmipLeaveSMCritSection(); WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Static instance name %ws has base index %x\n", BaseName, BaseIndex)); return(BaseIndex); } ULONG WmipMangleInstanceName( LPGUID Guid, PWCHAR Name, ULONG MaxMangledNameLen, PWCHAR MangledName ) /*++ Routine Description: Copies a static instance name from the input buffer to the output buffer, mangling it if the name collides with another name for the same guid. Arguments: Guid points at guid for the instance name Name points at the proposed instance name MaxMangledNameLen has the maximum number of chars in mangled name buffer MangledName points at buffer to return mangled name Return Value: Actual length of mangled name --*/ { PBGUIDENTRY GuidEntry; WCHAR ManglingChar; ULONG ManglePos; ULONG InstanceIndex; PBINSTANCESET InstanceSet; PAGED_CODE(); WmipAssert(MaxMangledNameLen >= wcslen(Name)); wcsncpy(MangledName, Name, MaxMangledNameLen); GuidEntry = WmipFindGEByGuid(Guid, FALSE); if (GuidEntry != NULL) { ManglePos = (ULONG)wcslen(MangledName)-1; ManglingChar = L'Z'; // // Loop until we get a unique name InstanceSet = WmipFindISinGEbyName(GuidEntry, MangledName, &InstanceIndex); while (InstanceSet != NULL) { WmipUnreferenceIS(InstanceSet); WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Need to mangle name %ws\n", MangledName)); if (ManglingChar == L'Z') { ManglingChar = L'A'; if (++ManglePos == MaxMangledNameLen) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Instance Name could not be mangled\n")); break; } MangledName[ManglePos+1] = UNICODE_NULL; } else { ManglingChar++; } MangledName[ManglePos] = ManglingChar; InstanceSet = WmipFindISinGEbyName(GuidEntry, MangledName, &InstanceIndex) ; } WmipUnreferenceGE(GuidEntry); } return(ULONG)(wcslen(MangledName)+1); } NTSTATUS WmipBuildInstanceSet( PWMIREGGUID RegGuid, PWMIREGINFOW WmiRegInfo, ULONG BufferSize, PBINSTANCESET InstanceSet, ULONG ProviderId, LPCTSTR MofImagePath ) { PWCHAR InstanceName, InstanceNamePtr; PBISBASENAME IsBaseName; PBISSTATICNAMES IsStaticName; ULONG SizeNeeded; ULONG SuffixSize; PWCHAR StaticNames; ULONG Len; ULONG InstanceCount; ULONG j; ULONG MaxStaticInstanceNameSize; PWCHAR StaticInstanceNameBuffer; ULONG InstanceNameOffset; NTSTATUS Status; PAGED_CODE(); UNREFERENCED_PARAMETER (MofImagePath); // // Remember the count of instances for the guid in the DS // InstanceCount = RegGuid->InstanceCount; InstanceSet->Count = InstanceCount; InstanceSet->ProviderId = ProviderId; // // Reset any flags that might be changed by a new REGGUID // InstanceSet->Flags &= ~(IS_EXPENSIVE | IS_EVENT_ONLY | IS_PDO_INSTANCENAME | IS_INSTANCE_STATICNAMES | IS_INSTANCE_BASENAME); // // Finish initializing the Instance Set flags // if (RegGuid->Flags & WMIREG_FLAG_EXPENSIVE) { InstanceSet->Flags |= IS_EXPENSIVE; } if (RegGuid->Flags & WMIREG_FLAG_TRACED_GUID) { // // This guid is not queryable, but is used for sending trace // events. We mark the InstanceSet as special InstanceSet->Flags |= IS_TRACED; if (RegGuid->Flags & WMIREG_FLAG_TRACE_CONTROL_GUID) { InstanceSet->Flags |= IS_CONTROL_GUID; } } if (RegGuid->Flags & WMIREG_FLAG_EVENT_ONLY_GUID) { // // This guid is not queryable, but is only used for sending // events. We mark the InstanceSet as special InstanceSet->Flags |= IS_EVENT_ONLY; } InstanceName = (LPWSTR)OffsetToPtr(WmiRegInfo, RegGuid->BaseNameOffset); InstanceNameOffset = RegGuid->BaseNameOffset; if (RegGuid->Flags & WMIREG_FLAG_INSTANCE_LIST) { // // We have static list of instance names that might need mangling // We assume that any name mangling that must occur can be // done with a suffix of 5 or fewer characters. This allows // up to 100,000 identical static instance names within the // same guid. First lets get the amount of memory we'll need // SizeNeeded = FIELD_OFFSET(ISSTATICENAMES, StaticNamePtr) + 1; SuffixSize = MAXBASENAMESUFFIXSIZE; MaxStaticInstanceNameSize = 0; for (j = 0; j < InstanceCount; j++) { Status = WmipValidateWmiRegInfoString(WmiRegInfo, BufferSize, InstanceNameOffset, &InstanceNamePtr); if ((! NT_SUCCESS(Status)) || (InstanceNamePtr == NULL)) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipAddDataSource: bad static instance name %x\n", InstanceNamePtr)); WmipReportEventLog(EVENT_WMI_INVALID_REGINFO, EVENTLOG_WARNING_TYPE, 0, WmiRegInfo->BufferSize, WmiRegInfo, 1, MofImagePath ? MofImagePath : TEXT("Unknown")); return(STATUS_INVALID_PARAMETER); } if (*InstanceNamePtr > MaxStaticInstanceNameSize) { MaxStaticInstanceNameSize = *InstanceNamePtr; } SizeNeeded += *InstanceNamePtr + 1 + SuffixSize + (sizeof(PWCHAR) / sizeof(WCHAR)); InstanceNameOffset += *InstanceNamePtr + 2; } IsStaticName = (PBISSTATICNAMES)WmipAllocString(SizeNeeded); if (IsStaticName == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipAddDataSource: alloc static instance names\n")); return(STATUS_INSUFFICIENT_RESOURCES); } InstanceSet->Flags |= IS_INSTANCE_STATICNAMES; InstanceSet->IsStaticNames = IsStaticName; StaticNames = (PWCHAR) ((PUCHAR)IsStaticName + (InstanceCount * sizeof(PWCHAR))); InstanceNamePtr = InstanceName; StaticInstanceNameBuffer = WmipAlloc(MaxStaticInstanceNameSize + sizeof(WCHAR)); if (StaticInstanceNameBuffer == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipAddDataSource: couldn't alloc StaticInstanceNameBuffer\n")); return(STATUS_INSUFFICIENT_RESOURCES); } for (j = 0; j < InstanceCount; j++) { IsStaticName->StaticNamePtr[j] = StaticNames; memcpy(StaticInstanceNameBuffer, InstanceNamePtr+1, *InstanceNamePtr); StaticInstanceNameBuffer[*InstanceNamePtr/sizeof(WCHAR)] = UNICODE_NULL; Len = WmipMangleInstanceName(&RegGuid->Guid, StaticInstanceNameBuffer, *InstanceNamePtr + SuffixSize + 1, StaticNames); StaticNames += Len; InstanceNamePtr += (*((USHORT *)InstanceNamePtr) + 2)/sizeof(WCHAR); } WmipFree(StaticInstanceNameBuffer); } else if (RegGuid->Flags & WMIREG_FLAG_INSTANCE_BASENAME) { // // We have static instance names built from a base name Status = WmipValidateWmiRegInfoString(WmiRegInfo, BufferSize, InstanceNameOffset, &InstanceNamePtr); if (! NT_SUCCESS(Status)) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipAddDataSource: Invalid instance base name %x\n", InstanceName)); WmipReportEventLog(EVENT_WMI_INVALID_REGINFO, EVENTLOG_WARNING_TYPE, 0, WmiRegInfo->BufferSize, WmiRegInfo, 1, MofImagePath ? MofImagePath : TEXT("Unknown")); return(STATUS_INVALID_PARAMETER); } InstanceSet->Flags |= IS_INSTANCE_BASENAME; if (RegGuid->Flags & WMIREG_FLAG_INSTANCE_PDO) { InstanceSet->Flags |= IS_PDO_INSTANCENAME; } IsBaseName = (PBISBASENAME)WmipAlloc(*InstanceName + sizeof(WCHAR) + FIELD_OFFSET(ISBASENAME, BaseName)); if (IsBaseName == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipAddDataSource: alloc ISBASENAME failed\n")); return(STATUS_INSUFFICIENT_RESOURCES); } InstanceSet->IsBaseName = IsBaseName; memcpy(IsBaseName->BaseName, InstanceName+1, *InstanceName); IsBaseName->BaseName[*InstanceName/sizeof(WCHAR)] = UNICODE_NULL; IsBaseName->BaseIndex = WmipDetermineInstanceBaseIndex( &RegGuid->Guid, IsBaseName->BaseName, RegGuid->InstanceCount); } return(STATUS_SUCCESS); } NTSTATUS WmipLinkDataSourceToList( PBDATASOURCE DataSource, BOOLEAN AddDSToList ) /*++ Routine Description: This routine will take a DataSource that was just registered or updated and link any new InstanceSets to an appropriate GuidEntry. Then if the AddDSToList is TRUE the DataSource itself will be added to the main data source list. This routine will do all of the linkages within a critical section so the data source and its new instances are added atomically. The routine will also determine if the guid entry associated with a InstanceSet is a duplicate of another that is already on the main guid entry list and if so will use the preexisting guid entry. This routine assumes that the SM critical section has been taken Arguments: DataSource is a based pointer to a DataSource structure AddDSToList is TRUE then data source will be added to the main list of data sources Return Value: ERROR_SUCCESS or an error code --*/ { PBINSTANCESET InstanceSet; PLIST_ENTRY InstanceSetList; PBGUIDENTRY GuidEntry; PAGED_CODE(); InstanceSetList = DataSource->ISHead.Flink; while (InstanceSetList != &DataSource->ISHead) { InstanceSet = CONTAINING_RECORD(InstanceSetList, INSTANCESET, DSISList); // // If this instance set has just been registered then we need to // get it on a GuidEntry list. if (InstanceSet->Flags & IS_NEWLY_REGISTERED) { // // See if there is already a GUID entry for the instance set. // If not go allocate a new guid entry and place it on the // main guid list. If there already is a GuidEntry for the // InstanceSet we will assign the ref count that was given by // the WmipFindGEByGuid to the DataSource which will unreference // the GuidEntry when the DataSource is unregistered. GuidEntry = WmipFindGEByGuid((LPGUID)InstanceSet->GuidEntry, FALSE); if (GuidEntry == NULL) { GuidEntry = WmipAllocGuidEntry(); if (GuidEntry == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipLinkDataSourceToList: WmipAllocGuidEntry failed\n")); return(STATUS_INSUFFICIENT_RESOURCES); } // // Initialize the new GuidEntry and place it on the master // GuidEntry list. memcpy(&GuidEntry->Guid, (LPGUID)InstanceSet->GuidEntry, sizeof(GUID)); InsertHeadList(WmipGEHeadPtr, &GuidEntry->MainGEList); } InstanceSet->GuidEntry = GuidEntry; InstanceSet->Flags &= ~IS_NEWLY_REGISTERED; InsertTailList(&GuidEntry->ISHead, &InstanceSet->GuidISList); GuidEntry->ISCount++; } InstanceSetList = InstanceSetList->Flink; } if (AddDSToList) { WmipAssert(! (DataSource->Flags & FLAG_ENTRY_ON_INUSE_LIST)); DataSource->Flags |= FLAG_ENTRY_ON_INUSE_LIST; InsertTailList(WmipDSHeadPtr, &DataSource->MainDSList); } return(STATUS_SUCCESS); } void WmipSendGuidUpdateNotifications( NOTIFICATIONTYPES NotificationType, ULONG GuidCount, PTRCACHE *GuidList ) { PUCHAR WnodeBuffer; PWNODE_SINGLE_INSTANCE Wnode; ULONG WnodeSize; LPGUID GuidPtr; ULONG i; PWCHAR InstanceName; PMSWmi_GuidRegistrationInfo RegInfo; ULONG DataBlockSize; GUID RegChangeGuid = MSWmi_GuidRegistrationInfoGuid; #define REGUPDATENAME L"REGUPDATEINFO" PAGED_CODE(); DataBlockSize = sizeof(MSWmi_GuidRegistrationInfo) + GuidCount*sizeof(GUID) - sizeof(GUID); WnodeSize = sizeof(WNODE_SINGLE_INSTANCE) + sizeof(USHORT) + sizeof(REGUPDATENAME) + 8 + DataBlockSize; WnodeBuffer = WmipAlloc(WnodeSize); if (WnodeBuffer != NULL) { Wnode = (PWNODE_SINGLE_INSTANCE)WnodeBuffer; // // Setup a WNODE_SINGLE_INSTANCE event with the updated guid // registration information // memset(Wnode, 0, sizeof(WNODE_HEADER)); Wnode->WnodeHeader.Guid = RegChangeGuid; Wnode->WnodeHeader.BufferSize = WnodeSize; Wnode->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE | WNODE_FLAG_EVENT_ITEM; Wnode->OffsetInstanceName = sizeof(WNODE_SINGLE_INSTANCE); Wnode->DataBlockOffset = ((Wnode->OffsetInstanceName + sizeof(USHORT) + sizeof(REGUPDATENAME) + 7) & ~7); Wnode->SizeDataBlock = DataBlockSize; InstanceName = (PWCHAR)OffsetToPtr(Wnode, Wnode->OffsetInstanceName); *InstanceName++ = sizeof(REGUPDATENAME); StringCbCopy(InstanceName, sizeof(REGUPDATENAME), REGUPDATENAME); RegInfo = (PMSWmi_GuidRegistrationInfo)OffsetToPtr(Wnode, Wnode->DataBlockOffset); RegInfo->Operation = NotificationType; RegInfo->GuidCount = GuidCount; GuidPtr = (LPGUID)RegInfo->GuidList; for (i = 0; i < GuidCount; i++) { *GuidPtr++ = *GuidList[i].Guid; } WmipProcessEvent((PWNODE_HEADER)Wnode, TRUE, FALSE); WmipFree(WnodeBuffer); } } void WmipGenerateBinaryMofNotification( PBINSTANCESET BinaryMofInstanceSet, LPCGUID Guid ) { PWNODE_SINGLE_INSTANCE Wnode; SIZE_T ImagePathLen, ResourceNameLen, InstanceNameLen, BufferSize; PWCHAR Ptr; ULONG i; HRESULT hr; PAGED_CODE(); if (BinaryMofInstanceSet->Count == 0) { return; } for (i = 0; i < BinaryMofInstanceSet->Count; i++) { ImagePathLen = sizeof(USHORT); InstanceNameLen = (sizeof(USHORT) + 7) & ~7; if (BinaryMofInstanceSet->Flags & IS_INSTANCE_STATICNAMES) { ResourceNameLen = ((wcslen(BinaryMofInstanceSet->IsStaticNames->StaticNamePtr[i])+1) * sizeof(WCHAR)) + sizeof(USHORT); } else if (BinaryMofInstanceSet->Flags & IS_INSTANCE_BASENAME) { ResourceNameLen = (((wcslen(BinaryMofInstanceSet->IsBaseName->BaseName) + MAXBASENAMESUFFIXSIZE) * sizeof(WCHAR)) + sizeof(USHORT)); } else { return; } BufferSize = FIELD_OFFSET(WNODE_SINGLE_INSTANCE, VariableData) + InstanceNameLen + ImagePathLen + ResourceNameLen; Wnode = (PWNODE_SINGLE_INSTANCE)WmipAlloc(BufferSize); if (Wnode != NULL) { Wnode->WnodeHeader.BufferSize = (ULONG) BufferSize; Wnode->WnodeHeader.ProviderId = MOFEVENT_ACTION_BINARY_MOF; Wnode->WnodeHeader.Version = 1; Wnode->WnodeHeader.Linkage = 0; Wnode->WnodeHeader.Flags = (WNODE_FLAG_EVENT_ITEM | WNODE_FLAG_SINGLE_INSTANCE); memcpy(&Wnode->WnodeHeader.Guid, Guid, sizeof(GUID)); WmiInsertTimestamp(&Wnode->WnodeHeader); Wnode->OffsetInstanceName = FIELD_OFFSET(WNODE_SINGLE_INSTANCE, VariableData); Wnode->DataBlockOffset = (ULONG)(Wnode->OffsetInstanceName + InstanceNameLen); Wnode->SizeDataBlock = (ULONG)(ImagePathLen + ResourceNameLen); Ptr = (PWCHAR)&Wnode->VariableData; *Ptr++ = 0; // Empty instance name Ptr = (PWCHAR)OffsetToPtr(Wnode, Wnode->DataBlockOffset); *Ptr++ = 0; // Empty image path // Instance name for binary mof resource ResourceNameLen -= sizeof(USHORT); if (BinaryMofInstanceSet->Flags & IS_INSTANCE_STATICNAMES) { *Ptr++ = (USHORT)ResourceNameLen; hr = StringCbCopy(Ptr, ResourceNameLen, BinaryMofInstanceSet->IsStaticNames->StaticNamePtr[i]); WmipAssert(hr == S_OK); } else if (BinaryMofInstanceSet->Flags & IS_INSTANCE_BASENAME) { hr = (USHORT)StringCbPrintfEx(Ptr+1, ResourceNameLen, NULL, NULL, STRSAFE_FILL_BEHIND_NULL, L"%ws%d", BinaryMofInstanceSet->IsBaseName->BaseName, BinaryMofInstanceSet->IsBaseName->BaseIndex+i) * sizeof(WCHAR); WmipAssert(hr == S_OK); *Ptr = (USHORT)ResourceNameLen; } WmipProcessEvent((PWNODE_HEADER)Wnode, TRUE, FALSE); WmipFree(Wnode); } } } void WmipGenerateMofResourceNotification( LPWSTR ImagePath, LPWSTR ResourceName, LPCGUID Guid, ULONG ActionCode ) { PWNODE_SINGLE_INSTANCE Wnode; SIZE_T ImagePathLen, ResourceNameLen, InstanceNameLen, BufferSize; PWCHAR Ptr; PAGED_CODE(); ImagePathLen = (wcslen(ImagePath) + 2) * sizeof(WCHAR); ResourceNameLen = (wcslen(ResourceName) + 2) * sizeof(WCHAR); InstanceNameLen = ( sizeof(USHORT)+7 ) & ~7; BufferSize = FIELD_OFFSET(WNODE_SINGLE_INSTANCE, VariableData) + InstanceNameLen + ImagePathLen + ResourceNameLen; Wnode = (PWNODE_SINGLE_INSTANCE)WmipAlloc(BufferSize); if (Wnode != NULL) { Wnode->WnodeHeader.BufferSize = (ULONG) BufferSize; Wnode->WnodeHeader.ProviderId = ActionCode; Wnode->WnodeHeader.Version = 1; Wnode->WnodeHeader.Linkage = 0; Wnode->WnodeHeader.Flags = (WNODE_FLAG_EVENT_ITEM | WNODE_FLAG_SINGLE_INSTANCE | WNODE_FLAG_INTERNAL); memcpy(&Wnode->WnodeHeader.Guid, Guid, sizeof(GUID)); WmiInsertTimestamp(&Wnode->WnodeHeader); Wnode->OffsetInstanceName = FIELD_OFFSET(WNODE_SINGLE_INSTANCE, VariableData); Wnode->DataBlockOffset = (ULONG)(Wnode->OffsetInstanceName + InstanceNameLen); Wnode->SizeDataBlock = (ULONG)(ImagePathLen + ResourceNameLen); Ptr = (PWCHAR)&Wnode->VariableData; *Ptr = 0; // Empty instance name // ImagePath name Ptr = (PWCHAR)OffsetToPtr(Wnode, Wnode->DataBlockOffset); ImagePathLen -= sizeof(USHORT); *Ptr++ = (USHORT)ImagePathLen; memcpy(Ptr, ImagePath, ImagePathLen); Ptr += (ImagePathLen / sizeof(WCHAR)); // MofResource Name ResourceNameLen -= sizeof(USHORT); *Ptr++ = (USHORT)ResourceNameLen; memcpy(Ptr, ResourceName, ResourceNameLen); WmipProcessEvent((PWNODE_HEADER)Wnode, TRUE, FALSE); WmipFree(Wnode); } } void WmipGenerateRegistrationNotification( PBDATASOURCE DataSource, NOTIFICATIONTYPES NotificationType ) { PTRCACHE *Guids; ULONG GuidCount, GuidMax; PLIST_ENTRY InstanceSetList; PBINSTANCESET InstanceSet; LPGUID Guid; PAGED_CODE(); WmipReferenceDS(DataSource); // // Loop over all instance sets for this data source // GuidCount = 0; GuidMax = 0; Guids = NULL; InstanceSetList = DataSource->ISHead.Flink; while (InstanceSetList != &DataSource->ISHead) { InstanceSet = CONTAINING_RECORD(InstanceSetList, INSTANCESET, DSISList); // // Cache the guid and instance set so we can send registration // change notifications // Guid = &InstanceSet->GuidEntry->Guid; WmipCachePtrs(Guid, InstanceSet, &GuidCount, &GuidMax, &Guids); // // If we are adding a guid and it is already enabled then we // need to send an enable irp. Likewise if the guid is being // removed and is enabled then we need to send a disable // if (NotificationType == RegistrationAdd) { WmipEnableCollectionForNewGuid(Guid, InstanceSet); } else if (NotificationType == RegistrationDelete) { WmipDisableCollectionForRemovedGuid(Guid, InstanceSet); } InstanceSetList = InstanceSetList->Flink; } // // Send out event that informs about guid registration changes // WmipSendGuidUpdateNotifications(NotificationType, GuidCount, Guids); if (Guids != NULL) { WmipFree(Guids); } WmipUnreferenceDS(DataSource); } NTSTATUS WmipAddMofResource( PBDATASOURCE DataSource, LPWSTR ImagePath, BOOLEAN IsImagePath, LPWSTR MofResourceName, PBOOLEAN NewMofResource ) /*++ Routine Description: This routine will build MOFCLASSINFO structures for each guid that is described in the MOF for the data source. If there are any errors in the mof resource then no mof information from the resource is retained and the resource data is unloaded. Arguments: DataSource is the data source structure of the data provider ImagePath points at a string that has the full path to the image file that contains the MOF resource MofResourceName points at a string that has the name of the MOF resource Return Value: --*/ { PMOFRESOURCE MofResource; ULONG NewMofResourceCount; ULONG i; BOOLEAN FreeBuffer; size_t RegPathLen, ResNameLen; HRESULT hr; PAGED_CODE(); MofResource = WmipFindMRByNames(ImagePath, MofResourceName); if (MofResource == NULL) { // // Mof Resource not previously specified, so allocate a new one MofResource = WmipAllocMofResource(); if (MofResource == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } if (IsImagePath) { MofResource->Flags |= MR_FLAG_USER_MODE; } RegPathLen = (wcslen(ImagePath)+1) * sizeof(WCHAR); MofResource->RegistryPath = WmipAlloc(RegPathLen); ResNameLen = (wcslen(MofResourceName) + 1) * sizeof(WCHAR); MofResource->MofResourceName = WmipAlloc(ResNameLen); if ((MofResource->RegistryPath == NULL) || (MofResource->MofResourceName == NULL)) { // // Allocation cleanup routine will free any memory alloced for MR WmipUnreferenceMR(MofResource); return(STATUS_INSUFFICIENT_RESOURCES); } hr = StringCbCopy(MofResource->RegistryPath, RegPathLen, ImagePath); WmipAssert(hr == S_OK); hr = StringCbCopy(MofResource->MofResourceName, ResNameLen, MofResourceName); WmipAssert(hr == S_OK); WmipEnterSMCritSection(); InsertTailList(WmipMRHeadPtr, &MofResource->MainMRList); WmipLeaveSMCritSection(); *NewMofResource = TRUE; } else { *NewMofResource = FALSE; } if (DataSource != NULL) { WmipEnterSMCritSection(); for (i = 0; i < DataSource->MofResourceCount; i++) { if (DataSource->MofResources[i] == MofResource) { // // If this mof resource is already been registered for // this data source then we do not need to worry about // it anymore. // WmipUnreferenceMR(MofResource); break; } if (DataSource->MofResources[i] == NULL) { DataSource->MofResources[i] = MofResource; break; } } if (i == DataSource->MofResourceCount) { NewMofResourceCount = DataSource->MofResourceCount + AVGMOFRESOURCECOUNT; if (DataSource->MofResources != DataSource->StaticMofResources) { FreeBuffer = TRUE; } else { FreeBuffer = FALSE; } if (WmipRealloc((PVOID *)&DataSource->MofResources, DataSource->MofResourceCount * sizeof(PMOFRESOURCE), NewMofResourceCount * sizeof(PMOFRESOURCE), FreeBuffer ) ) { DataSource->MofResourceCount = NewMofResourceCount; DataSource->MofResources[i] = MofResource; } } WmipLeaveSMCritSection(); } return(STATUS_SUCCESS); } NTSTATUS WmipAddDataSource( IN PREGENTRY RegEntry, IN PWMIREGINFOW WmiRegInfo, IN ULONG BufferSize, IN PWCHAR RegPath, IN PWCHAR ResourceName, IN PWMIGUIDOBJECT RequestObject, IN BOOLEAN IsUserMode ) /*+++ Routine Description: This routine will register a information in the WMI database for a new DataSource or add additional guids to an existing data source. Arguments: RegEntry is the regentry for the data provider WmiRegInfo is the registration information to register BufferSize is the size of WmiRegInfo in bytes RegPath is a pointer into WmiRegInfo to a counted string that is the registry path (or image path for UM providers). ResourceName is a pointer into WmiRegInfo to a counted string that is the resource name RequestObject is the request object associated with the UM provider. If this is NULL then the registration is for a driver Return Value: STATUS_SUCCESS or an error code ---*/ { PBDATASOURCE DataSource; PWMIREGGUID RegGuid; ULONG i; NTSTATUS Status, Status2; PBINSTANCESET InstanceSet; PBINSTANCESET BinaryMofInstanceSet = NULL; PWCHAR MofRegistryPath; PWCHAR MofResourceName; BOOLEAN AppendToDS; BOOLEAN NewMofResource; PAGED_CODE(); if (RegEntry->DataSource != NULL) { DataSource = RegEntry->DataSource; WmipAssert(DataSource != NULL); AppendToDS = TRUE; } else { DataSource = WmipAllocDataSource(); AppendToDS = FALSE; } if (DataSource != NULL) { // // Loop over each guid being registered and build instance sets and // guid entries. // if (! AppendToDS) { DataSource->ProviderId = RegEntry->ProviderId; if (RequestObject != NULL) { DataSource->Flags |= DS_USER_MODE; DataSource->RequestObject = RequestObject; } else { DataSource->Flags |= DS_KERNEL_MODE; } } RegGuid = WmiRegInfo->WmiRegGuid; for (i = 0; i < WmiRegInfo->GuidCount; i++, RegGuid++) { if (! (RegGuid->Flags & WMIREG_FLAG_REMOVE_GUID)) { // // Only trace control guids are registered. Trace transaction // guids will not be registered since they can not be enabled or // disabled individually. They will be kept on the ControlGuids' // instance set structure. // if ( ( (RegGuid->Flags & WMIREG_FLAG_TRACED_GUID) != WMIREG_FLAG_TRACED_GUID ) || (RegGuid->Flags & WMIREG_FLAG_TRACE_CONTROL_GUID) ) { // // Allocate an instance set for this new set of instances // InstanceSet = WmipAllocInstanceSet(); if (InstanceSet == NULL) { WmipUnreferenceDS(DataSource); WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipAddDataSource: WmipAllocInstanceSet failed\n")); return(STATUS_INSUFFICIENT_RESOURCES); } // // We will allocate a proper guid entry for the instance // set when the data source gets linked into the main data // source list so we stash a pointer to the guid away now. // InstanceSet->GuidEntry = (PBGUIDENTRY)&RegGuid->Guid; // // Minimally initialize the InstanceSet and add it to // the DataSource's list of InstanceSets. We do this // first so that if there is any failure below and // the DataSource can'e be fully registered the instance // set and guid entry will be free when the DataSource is // freed. // InstanceSet->DataSource = DataSource; InstanceSet->Flags |= IS_NEWLY_REGISTERED; Status = WmipBuildInstanceSet(RegGuid, WmiRegInfo, BufferSize, InstanceSet, RegEntry->ProviderId, RegPath); // // If this is the guid that represents the binary mof data // then remember the InstanceSet for later // if (IsEqualGUID(&RegGuid->Guid, &WmipBinaryMofGuid)) { BinaryMofInstanceSet = InstanceSet; } InsertHeadList(&DataSource->ISHead, &InstanceSet->DSISList); if (! NT_SUCCESS(Status)) { WmipUnreferenceDS(DataSource); return(Status); } } } } // // Now that the instance sets have been built successfully we // can link them into the master list. // WmipEnterSMCritSection(); Status = WmipLinkDataSourceToList(DataSource, (BOOLEAN)(! AppendToDS)); WmipLeaveSMCritSection(); if (! NT_SUCCESS(Status)) { WmipUnreferenceDS(DataSource); return(Status); } RegEntry->DataSource = DataSource; // // We need to send out notification of new guids and mofs. // if (BinaryMofInstanceSet != NULL) { // // Send binary mof guid arrival notification // WmipGenerateBinaryMofNotification(BinaryMofInstanceSet, &GUID_MOF_RESOURCE_ADDED_NOTIFICATION); } // // Convert Registry path to a sz string so we can assign it to // the DS if the DS is a new one // if (RegPath != NULL) { MofRegistryPath = WmipCountedToSz(RegPath); } else { MofRegistryPath = NULL; } if ((AppendToDS == FALSE) && (MofRegistryPath != NULL)) { DataSource->RegistryPath = MofRegistryPath; } if (ResourceName != NULL) { MofResourceName = WmipCountedToSz(ResourceName); } else { MofResourceName = NULL; } // // Finally if we created a new data source we need to register // the mof for it. Only register those that have a RegistryPath // and a ResourceName // if ((MofRegistryPath != NULL) && (*MofRegistryPath != 0) && (MofResourceName != NULL) && (*MofResourceName != 0)) { // // If a mof is specified then add it to the list // Status2 = WmipAddMofResource(DataSource, MofRegistryPath, IsUserMode, MofResourceName, &NewMofResource); if (NT_SUCCESS(Status2) && NewMofResource) { // // We successfully added a brand new MOF resource so // we need to fire an event for wbem. // WmipGenerateMofResourceNotification(MofRegistryPath, MofResourceName, &GUID_MOF_RESOURCE_ADDED_NOTIFICATION, IsUserMode ? MOFEVENT_ACTION_IMAGE_PATH : MOFEVENT_ACTION_REGISTRY_PATH); } } // // Clean up registry path and mof resource name strings // if ((MofRegistryPath != NULL) && AppendToDS) { // // Only free if registry path not saved in DataSource // WmipAssert(MofRegistryPath != DataSource->RegistryPath); WmipFree(MofRegistryPath); } if (MofResourceName != NULL) { WmipFree(MofResourceName); } // // Send a notification about new/changed guids // WmipGenerateRegistrationNotification(DataSource, RegistrationAdd); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } return(Status); } PBINSTANCESET WmipFindISInDSByGuid( PBDATASOURCE DataSource, LPGUID Guid ) /*++ Routine Description: This routine will find the InstanceSet in the passed DataSource for the guid passed. This routine assumes that the SM critical section is held before it is called. Arguments: DataSource is the data source from which the guid is to be removed Guid has the Guid for the InstanceSet to find Return Value: --*/ { PLIST_ENTRY InstanceSetList; PBINSTANCESET InstanceSet; PAGED_CODE(); InstanceSetList = DataSource->ISHead.Flink; while (InstanceSetList != &DataSource->ISHead) { InstanceSet = CONTAINING_RECORD(InstanceSetList, INSTANCESET, DSISList); if ((InstanceSet->GuidEntry != NULL) && (IsEqualGUID(Guid, &InstanceSet->GuidEntry->Guid))) { WmipReferenceIS(InstanceSet); return(InstanceSet); } InstanceSetList = InstanceSetList->Flink; } return(NULL); } ULONG WmipUpdateAddGuid( PBDATASOURCE DataSource, PWMIREGGUID RegGuid, PWMIREGINFO WmiRegInfo, ULONG BufferSize, PBINSTANCESET *AddModInstanceSet ) /*++ Routine Description: This routine will add a new guid for the data source and send notification This routine assumes that the SM critical section is held before it is called. Arguments: DataSource is the data source from which the guid is to be removed RegGuid has the Guid update data structure WmiRegInfo points at the beginning of the registration update info Return Value: 1 if guid was added or 0 --*/ { PBINSTANCESET InstanceSet; LPGUID Guid = &RegGuid->Guid; NTSTATUS Status; PAGED_CODE(); // // Allocate an instance set for this new set of instances InstanceSet = WmipAllocInstanceSet(); if (InstanceSet == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: WmipUpdateAddGuid: WmipAllocInstanceSet failed\n")); return(0); } // // We will allocate a proper guid entry for the instance set when // the data source gets linked into the main data source list so // we stash a pointer to the guid away now. InstanceSet->GuidEntry = (PBGUIDENTRY)Guid; // // Minimally initialize the InstanceSet and add it to the DataSource's // list of InstanceSets. We do this first so that if there is any // failure below and the DataSource can'e be fully registered the // instance set and guid entry will be free when the DataSource is // freed. InstanceSet->DataSource = DataSource; InstanceSet->Flags |= IS_NEWLY_REGISTERED; InsertHeadList(&DataSource->ISHead, &InstanceSet->DSISList); Status = WmipBuildInstanceSet(RegGuid, WmiRegInfo, BufferSize, InstanceSet, DataSource->ProviderId, DataSource->RegistryPath); if (! NT_SUCCESS(Status)) { WmipUnreferenceIS(InstanceSet); return(0); } Status = WmipLinkDataSourceToList(DataSource, FALSE); *AddModInstanceSet = InstanceSet; return( NT_SUCCESS(Status) ? 1 : 0); } #if DBG PWCHAR GuidToString( PWCHAR s, ULONG SizeInBytes, LPGUID piid ) { HRESULT hr; PAGED_CODE(); hr = StringCbPrintf(s, SizeInBytes, L"%x-%x-%x-%x%x%x%x%x%x%x%x", piid->Data1, piid->Data2, piid->Data3, piid->Data4[0], piid->Data4[1], piid->Data4[2], piid->Data4[3], piid->Data4[4], piid->Data4[5], piid->Data4[6], piid->Data4[7]); WmipAssert(hr == S_OK); return(s); } #endif BOOLEAN WmipUpdateRemoveGuid( PBDATASOURCE DataSource, PWMIREGGUID RegGuid, PBINSTANCESET *AddModInstanceSet ) /*++ Routine Description: This routine will remove the guid for the data source and send notification This routine assumes that the SM critical section is held before it is called. Arguments: DataSource is the data source from which the guid is to be removed RegGuid has the Guid update data structure Return Value: TRUE if guid was removed else FALSE --*/ { PBINSTANCESET InstanceSet; LPGUID Guid = &RegGuid->Guid; BOOLEAN SendNotification; PAGED_CODE(); InstanceSet = WmipFindISInDSByGuid(DataSource, Guid); if (InstanceSet != NULL) { WmipUnreferenceIS(InstanceSet); *AddModInstanceSet = InstanceSet; SendNotification = TRUE; } else { #if DBG WCHAR s[256]; WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: UpdateRemoveGuid %ws not registered by %ws\n", GuidToString(s, sizeof(s), Guid), DataSource->RegistryPath)); #endif SendNotification = FALSE; } return(SendNotification); } BOOLEAN WmipIsEqualInstanceSets( PBINSTANCESET InstanceSet1, PBINSTANCESET InstanceSet2 ) { ULONG i; ULONG Flags1, Flags2; PAGED_CODE(); Flags1 = InstanceSet1->Flags & ~(IS_ENABLE_EVENT | IS_ENABLE_COLLECTION); Flags2 = InstanceSet2->Flags & ~(IS_ENABLE_EVENT | IS_ENABLE_COLLECTION); if (Flags1 == Flags2) { if (InstanceSet1->Flags & IS_INSTANCE_BASENAME) { if ((InstanceSet1->Count == InstanceSet2->Count) && (wcscmp(InstanceSet1->IsBaseName->BaseName, InstanceSet1->IsBaseName->BaseName) == 0)) { return(TRUE); } } else if (InstanceSet1->Flags & IS_INSTANCE_BASENAME) { if (InstanceSet1->Count == InstanceSet2->Count) { for (i = 0; i < InstanceSet1->Count; i++) { if (wcscmp(InstanceSet1->IsStaticNames->StaticNamePtr[i], InstanceSet2->IsStaticNames->StaticNamePtr[i]) != 0) { return(FALSE); } } return(TRUE); } } else { return(TRUE); } } return(FALSE); } ULONG WmipUpdateModifyGuid( PBDATASOURCE DataSource, PWMIREGGUID RegGuid, PWMIREGINFO WmiRegInfo, ULONG BufferSize, PBINSTANCESET *AddModInstanceSet ) /*++ Routine Description: This routine will modify an existing guid for the data source and send notification This routine assumes that the SM critical section is held before it is called. HEHEY: If a guid was opened when it was registered as cheap, but closed when the guid was registered expensive a disable collection will NOT be sent. Conversely if a guid was opened when it was registered as expensive and is closed when registed as cheap a disable collection may be sent. Arguments: DataSource is the data source from which the guid is to be removed RegGuid has the Guid update data structure WmiRegInfo points at the beginning of the registration update info Return Value: 1 if guid was added or 2 if guid was modified else 0 --*/ { PBINSTANCESET InstanceSet; LPGUID Guid = &RegGuid->Guid; ULONG SendNotification; PBINSTANCESET InstanceSetNew; PVOID ToFree; NTSTATUS Status; PAGED_CODE(); InstanceSet = WmipFindISInDSByGuid(DataSource, Guid); if (InstanceSet != NULL) { // // See if anything has changed with the instance names and if not // then don't bother to recreate the instance set InstanceSetNew = WmipAllocInstanceSet(); if (InstanceSetNew == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: UpdateModifyGuid Not enough memory to alloc InstanceSet\n")); WmipUnreferenceIS(InstanceSet); return(0); } Status = WmipBuildInstanceSet(RegGuid, WmiRegInfo, BufferSize, InstanceSetNew, DataSource->ProviderId, DataSource->RegistryPath); if (NT_SUCCESS(Status)) { if (! WmipIsEqualInstanceSets(InstanceSet, InstanceSetNew)) { ToFree = NULL; if (InstanceSet->IsBaseName != NULL) { ToFree = (PVOID)InstanceSet->IsBaseName; } RemoveEntryList(&InstanceSet->GuidISList); Status = WmipBuildInstanceSet(RegGuid, WmiRegInfo, BufferSize, InstanceSet, DataSource->ProviderId, DataSource->RegistryPath); if (NT_SUCCESS(Status)) { InsertHeadList(&InstanceSet->GuidEntry->ISHead, &InstanceSet->GuidISList); } else { // // It is sad, but we weren't able to rebuild the instance // set so the old one is gone. This is an unlikely // situation that can really only occur when the machine // is out of memory. // } if (ToFree != NULL) { WmipFree(ToFree); } *AddModInstanceSet = InstanceSet; SendNotification = 2; } else { // // The InstanceSets are identical so just delete the new one SendNotification = 0; } WmipUnreferenceIS(InstanceSetNew); WmipUnreferenceIS(InstanceSet); } else { // // We could not parse the new instance set so leave the old // one alone // WmipUnreferenceIS(InstanceSet); WmipUnreferenceIS(InstanceSetNew); SendNotification = FALSE; } } else { // // Guid not already registered so try to add it SendNotification = WmipUpdateAddGuid(DataSource, RegGuid, WmiRegInfo, BufferSize, AddModInstanceSet); } return(SendNotification); } void WmipCachePtrs( LPGUID Ptr1, PBINSTANCESET Ptr2, ULONG *PtrCount, ULONG *PtrMax, PTRCACHE **PtrArray ) { PTRCACHE *NewPtrArray; PTRCACHE *OldPtrArray; PTRCACHE *ActualPtrArray; PAGED_CODE(); if (*PtrCount == *PtrMax) { NewPtrArray = WmipAlloc((*PtrMax + PTRCACHEGROWSIZE) * sizeof(PTRCACHE)); if (NewPtrArray != NULL) { OldPtrArray = *PtrArray; memcpy(NewPtrArray, OldPtrArray, *PtrMax * sizeof(PTRCACHE)); *PtrMax += PTRCACHEGROWSIZE; if (*PtrArray != NULL) { WmipFree(*PtrArray); } *PtrArray = NewPtrArray; } else { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Couldn't alloc memory for pointer cache\n")); return; } } ActualPtrArray = *PtrArray; ActualPtrArray[*PtrCount].Guid = Ptr1; ActualPtrArray[*PtrCount].InstanceSet = Ptr2; (*PtrCount)++; } NTSTATUS WmipUpdateDataSource( PREGENTRY RegEntry, PWMIREGINFOW WmiRegInfo, ULONG RetSize ) /*++ Routine Description: This routine will update a data source with changes to already registered guids. Arguments: ProviderId is the provider id of the DataSource whose guids are being updated. WmiRegInfo has the registration update information RetSize has the size of the registration information returned from kernel mode. Return Value: --*/ { PBDATASOURCE DataSource; PUCHAR RegInfo; ULONG RetSizeLeft; ULONG i; PWMIREGGUID RegGuid; ULONG NextWmiRegInfo; PTRCACHE *RemovedGuids; PTRCACHE *AddedGuids; PTRCACHE *ModifiedGuids; ULONG RemovedGuidCount; ULONG AddedGuidCount; ULONG ModifiedGuidCount; ULONG RemovedGuidMax; ULONG AddedGuidMax; ULONG ModifiedGuidMax; PBINSTANCESET InstanceSet; ULONG Action; PAGED_CODE(); DataSource = RegEntry->DataSource; if (DataSource == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: RegEntry %p requested update but is not registered\n", RegEntry)); return(STATUS_OBJECT_NAME_NOT_FOUND); } WmipReferenceDS(DataSource); AddedGuidCount = 0; ModifiedGuidCount = 0; RemovedGuidCount = 0; AddedGuidMax = 0; ModifiedGuidMax = 0; RemovedGuidMax = 0; ModifiedGuids = NULL; AddedGuids = NULL; RemovedGuids = NULL; NextWmiRegInfo = 0; RetSizeLeft = RetSize; WmipEnterSMCritSection(); RegInfo = (PUCHAR)WmiRegInfo; for (i = 0; i < WmiRegInfo->GuidCount; i++) { RegGuid = &WmiRegInfo->WmiRegGuid[i]; if (RegGuid->Flags & WMIREG_FLAG_REMOVE_GUID) { if (WmipUpdateRemoveGuid(DataSource, RegGuid, &InstanceSet)) { WmipCachePtrs(&RegGuid->Guid, InstanceSet, &RemovedGuidCount, &RemovedGuidMax, &RemovedGuids); } } else { Action = WmipUpdateModifyGuid(DataSource, RegGuid, WmiRegInfo, RetSize, &InstanceSet); if (Action == 1) { WmipCachePtrs(&RegGuid->Guid, InstanceSet, &AddedGuidCount, &AddedGuidMax, &AddedGuids); } else if (Action == 2) { WmipCachePtrs(&RegGuid->Guid, InstanceSet, &ModifiedGuidCount, &ModifiedGuidMax, &ModifiedGuids); } } } WmipLeaveSMCritSection(); WmipUnreferenceDS(DataSource); if (RemovedGuidCount > 0) { for (i = 0; i < RemovedGuidCount; i++) { if (IsEqualGUID(RemovedGuids[i].Guid, &WmipBinaryMofGuid)) { WmipGenerateBinaryMofNotification(RemovedGuids[i].InstanceSet, &GUID_MOF_RESOURCE_REMOVED_NOTIFICATION); } InstanceSet = RemovedGuids[i].InstanceSet; WmipDisableCollectionForRemovedGuid(RemovedGuids[i].Guid, InstanceSet); WmipEnterSMCritSection(); // // If IS is on the GE list then remove it if (InstanceSet->GuidISList.Flink != NULL) { RemoveEntryList(&InstanceSet->GuidISList); InstanceSet->GuidEntry->ISCount--; } if (! (InstanceSet->Flags & IS_NEWLY_REGISTERED)) { WmipUnreferenceGE(InstanceSet->GuidEntry); } InstanceSet->GuidEntry = NULL; // // Remove IS from the DS List RemoveEntryList(&InstanceSet->DSISList); WmipUnreferenceIS(InstanceSet); WmipLeaveSMCritSection(); } WmipSendGuidUpdateNotifications(RegistrationDelete, RemovedGuidCount, RemovedGuids); WmipFree(RemovedGuids); } if (ModifiedGuidCount > 0) { for (i = 0; i < ModifiedGuidCount; i++) { if (IsEqualGUID(ModifiedGuids[i].Guid, &WmipBinaryMofGuid)) { WmipGenerateBinaryMofNotification(ModifiedGuids[i].InstanceSet, &GUID_MOF_RESOURCE_ADDED_NOTIFICATION); } } WmipSendGuidUpdateNotifications(RegistrationUpdate, ModifiedGuidCount, ModifiedGuids); WmipFree(ModifiedGuids); } if (AddedGuidCount > 0) { for (i = 0; i < AddedGuidCount; i++) { if (IsEqualGUID(AddedGuids[i].Guid, &WmipBinaryMofGuid)) { WmipGenerateBinaryMofNotification(AddedGuids[i].InstanceSet, &GUID_MOF_RESOURCE_ADDED_NOTIFICATION); } WmipEnableCollectionForNewGuid(AddedGuids[i].Guid, AddedGuids[i].InstanceSet); } WmipSendGuidUpdateNotifications(RegistrationAdd, AddedGuidCount, AddedGuids); WmipFree(AddedGuids); } return(STATUS_SUCCESS); } void WmipRemoveDataSourceByDS( PBDATASOURCE DataSource ) { PAGED_CODE(); WmipGenerateRegistrationNotification(DataSource, RegistrationDelete); WmipUnreferenceDS(DataSource); } NTSTATUS WmipRemoveDataSource( PREGENTRY RegEntry ) { PBDATASOURCE DataSource; NTSTATUS Status; PAGED_CODE(); DataSource = RegEntry->DataSource; if (DataSource != NULL) { WmipReferenceDS(DataSource); WmipRemoveDataSourceByDS(DataSource); WmipUnreferenceDS(DataSource); Status = STATUS_SUCCESS; } else { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Attempt to remove non existant data source %p\n", RegEntry)); Status = STATUS_OBJECT_NAME_NOT_FOUND; } return(Status); } NTSTATUS WmipEnumerateMofResources( PWMIMOFLIST MofList, ULONG BufferSize, ULONG *RetSize ) { PLIST_ENTRY MofResourceList; PMOFRESOURCE MofResource; ULONG MRCount; SIZE_T SizeNeeded, MRSize; PWMIMOFENTRY MofEntry; PWCHAR MRBuffer; ULONG i; HRESULT hr; PAGED_CODE(); WmipEnterSMCritSection(); MRCount = 0; SizeNeeded = 0; MofResourceList = WmipMRHeadPtr->Flink; while (MofResourceList != WmipMRHeadPtr) { MofResource = CONTAINING_RECORD(MofResourceList, MOFRESOURCE, MainMRList); MRCount++; SizeNeeded += (wcslen(MofResource->RegistryPath) + wcslen(MofResource->MofResourceName) + 2) * sizeof(WCHAR); MofResourceList = MofResourceList->Flink; } if (MRCount != 0) { MRSize = sizeof(WMIMOFLIST) + ((MRCount-1) * sizeof(WMIMOFENTRY)); SizeNeeded += MRSize; if (BufferSize >= SizeNeeded) { MofList->MofListCount = MRCount; MofResourceList = WmipMRHeadPtr->Flink; i = 0; while (MofResourceList != WmipMRHeadPtr) { MofResource = CONTAINING_RECORD(MofResourceList, MOFRESOURCE, MainMRList); MofEntry = &MofList->MofEntry[i++]; MofEntry->Flags = MofResource->Flags & MR_FLAG_USER_MODE ? WMIMOFENTRY_FLAG_USERMODE : 0; MofEntry->RegPathOffset = (ULONG) MRSize; MRBuffer = (PWCHAR)OffsetToPtr(MofList, MRSize); hr = StringCbCopy(MRBuffer, (BufferSize - MRSize), MofResource->RegistryPath); WmipAssert(hr == S_OK); MRSize += (wcslen(MofResource->RegistryPath) + 1) * sizeof(WCHAR); MofEntry->ResourceOffset = (ULONG) MRSize; MRBuffer = (PWCHAR)OffsetToPtr(MofList, MRSize); hr = StringCbCopy(MRBuffer, (BufferSize - MRSize), MofResource->MofResourceName); WmipAssert(hr == S_OK); MRSize += (wcslen(MofResource->MofResourceName) + 1) * sizeof(WCHAR); MofResourceList = MofResourceList->Flink; } } else { // // Buffer not large enough, return size needed // MofList->MofListCount = (ULONG) SizeNeeded; *RetSize = sizeof(ULONG); } } else { // // No mof resources // MofList->MofListCount = 0; *RetSize = sizeof(WMIMOFLIST); } WmipLeaveSMCritSection(); return(STATUS_SUCCESS); } NTSTATUS WmipInitializeDataStructs( void ) /*++ Routine Description: This routine will do the work of initializing the WMI service Arguments: Return Value: Error status value --*/ { ULONG Status; UCHAR RegInfoBuffer[sizeof(WMIREGINFOW) + 2 * sizeof(WMIREGGUIDW)]; PWMIREGINFOW RegInfo = (PWMIREGINFOW)RegInfoBuffer; GUID InstanceInfoGuid = INSTANCE_INFO_GUID; GUID EnumerateGuidsGuid = ENUMERATE_GUIDS_GUID; PREGENTRY RegEntry; PDEVICE_OBJECT Callback; PLIST_ENTRY GuidEntryList; PBGUIDENTRY GuidEntry; BOOLEAN NewResource; PAGED_CODE(); // // Initialize the various data structure lists that we maintain // WmipDSHeadPtr = &WmipDSHead; InitializeListHead(WmipDSHeadPtr); InitializeListHead(&WmipDSChunkInfo.ChunkHead); WmipGEHeadPtr = &WmipGEHead; InitializeListHead(WmipGEHeadPtr); InitializeListHead(&WmipGEChunkInfo.ChunkHead); WmipMRHeadPtr = &WmipMRHead; InitializeListHead(WmipMRHeadPtr); InitializeListHead(&WmipMRChunkInfo.ChunkHead); InitializeListHead(&WmipISChunkInfo.ChunkHead); // // Register any internal data provider guids and mark them as such // Callback = (PDEVICE_OBJECT)(ULONG_PTR) WmipUMProviderCallback; // // Establish a regentry for the data provider // RegEntry = WmipAllocRegEntry(Callback, WMIREG_FLAG_CALLBACK | REGENTRY_FLAG_TRACED | REGENTRY_FLAG_NEWREGINFO | REGENTRY_FLAG_INUSE | REGENTRY_FLAG_REG_IN_PROGRESS); if (RegEntry != NULL) { // // This code assumes that no other data providers have // yet registered. // WmipAssert(WmipGEHeadPtr->Flink == WmipGEHeadPtr); RtlZeroMemory(RegInfo, sizeof(RegInfoBuffer)); RegInfo->BufferSize = sizeof(RegInfoBuffer); RegInfo->GuidCount = 2; RegInfo->WmiRegGuid[0].Guid = InstanceInfoGuid; RegInfo->WmiRegGuid[1].Guid = EnumerateGuidsGuid; Status = WmipAddDataSource(RegEntry, RegInfo, RegInfo->BufferSize, NULL, NULL, NULL, FALSE); if (NT_SUCCESS(Status)) { GuidEntryList = WmipGEHeadPtr->Flink; while (GuidEntryList != WmipGEHeadPtr) { GuidEntry = CONTAINING_RECORD(GuidEntryList, GUIDENTRY, MainGEList); GuidEntry->Flags |= GE_FLAG_INTERNAL; GuidEntryList = GuidEntryList->Flink; } } else { RegEntry->Flags |= (REGENTRY_FLAG_RUNDOWN | REGENTRY_FLAG_NOT_ACCEPTING_IRPS); WmipUnreferenceRegEntry(RegEntry); } Status = WmipAddMofResource(RegEntry->DataSource, WMICOREIMAGEPATH, TRUE, WMICORERESOURCENAME, &NewResource); WmipAssert(NewResource); } return(STATUS_SUCCESS); } #ifdef ALLOC_DATA_PRAGMA #pragma const_seg() #endif