////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2003 Microsoft Corporation // // Module Name: VdsNasClasses.cpp // // Description: // Implementation of VDS NAS WMI Provider classes // // ////////////////////////////////////////////////////////////////////////////// #include "Pch.h" #include "VdsNasClasses.h" #include "vds_i.c" #include "vdsholder_i.c" const GUID CLSID_VdsLoader = {0x9C38ED61,0xD565,0x4728,{0xAE,0xEE,0xC8,0x09,0x52,0xF0,0xEC,0xDE}}; const GUID CLSID_VdsHolder = {0xe905d96c,0x6c6c,0x446b,{0x98,0xbe,0x13,0x59,0x29,0x69,0xdd,0x3a}}; #ifndef ARRAY_LEN #define ARRAY_LEN(A) (sizeof(A)/sizeof((A)[0])) #endif HRESULT g_LoadService( IN OUT IVdsService **ppService ) /*++ Description: Loads the VDS service Arguments: ppService - Pointer to loaded service Return values: HRESULT's --*/ { HRESULT hr = S_OK; // Start the COM interface. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hr)) return hr; // Hold VDS //IVdsHolder *pHolder = NULL; IVdsHolder *pHolder; hr = CoCreateInstance( CLSID_VdsHolder, NULL, CLSCTX_LOCAL_SERVER, IID_IVdsHolder, (void **)&pHolder ); SAFE_RELEASE(pHolder); // DEBUG... if this fails we should probably just keep on going if (FAILED(hr)) return hr; // Create a loader instance IVdsServiceLoader *pLoader; hr = CoCreateInstance( CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER, IID_IVdsServiceLoader, (void **)&pLoader ); if (FAILED(hr)) return hr; // Load Vds Service hr = pLoader->LoadService( NULL, ppService ); pLoader->Release(); pLoader = NULL; if (FAILED(hr)) return hr; return hr; } HRESULT g_GetDiskSize( IN IVdsDisk *pDisk, IN BOOL bFree, OUT ULONGLONG &ullSize ) /*++ Description: Gets the amount of free space remaining on a disk. Arguments: pDisk - the disk to calculate size on bFree - If TRUE, get only the free space ullFree - the total size of disk (or amount of free space) Return values: HRESULT's --*/ { HRESULT hr = S_OK; VDS_DISK_EXTENT *pDiskExtent = NULL; LONG lNumExtent; LONG i; ullSize = 0; hr = pDisk->QueryExtents(&pDiskExtent, &lNumExtent); if (hr == VDS_E_PACK_OFFLINE) return S_OK; // just return a size of 0... no error if (FAILED(hr)) return hr; for (i = 0; i < lNumExtent; i++) { // Add up the size.. if bFree is set, only add free space if (!bFree || (pDiskExtent+i)->type == VDS_DET_FREE) ullSize += (pDiskExtent+i)->ullSize; } SAFE_FREE(pDiskExtent); return hr; } BOOL g_IsDynamicDisk( IN IVdsDisk *pDisk ) /*++ Description: Determines whether a disk is dynamic or not. Arguments: pDisk - the disk being checked Return values: TRUE - if disk is dynamic FALSE - if disk is NOT dynamic --*/ { HRESULT hr = S_OK; CComPtr spPack; CComPtr spProv; VDS_PROVIDER_PROP ProvProp; hr = pDisk->GetPack(&spPack); if (hr != S_OK) // Should catch the case when disk has no pack... return FALSE; hr = spPack->GetProvider(&spProv); if (hr != S_OK) return FALSE; hr = spProv->GetProperties(&ProvProp); SAFE_FREE(ProvProp.pwszName); if (hr != S_OK) return FALSE; return (ProvProp.ulFlags & VDS_PF_DYNAMIC); } BOOL g_IsDynamicVolume( IN IVdsVolume *pVolume ) /*++ Description: Determines whether a volume is dynamic or not. Arguments: pVolume - the volume being checked Return values: TRUE - if volume is dynamic FALSE - if volume is NOT dynamic --*/ { HRESULT hr = S_OK; CComPtr spPack; CComPtr spProv; VDS_PROVIDER_PROP ProvProp; hr = pVolume->GetPack(&spPack); if (hr != S_OK) // Should catch the case when disk has no pack... return FALSE; hr = spPack->GetProvider(&spProv); if (hr != S_OK) return FALSE; hr = spProv->GetProperties(&ProvProp); SAFE_FREE(ProvProp.pwszName); if (hr != S_OK) return FALSE; return (ProvProp.ulFlags & VDS_PF_DYNAMIC); } //**************************************************************************** // // CNasDisk // //**************************************************************************** CNasDisk::CNasDisk( IN LPCWSTR pwszName, IN CWbemServices* pNamespace ) : CProvBase( pwszName, pNamespace ) { } HRESULT CNasDisk::Initialize() { HRESULT hr = S_OK; hr = g_LoadService(&m_spService); return hr; } CProvBase * CNasDisk::S_CreateThis( IN LPCWSTR pwszName, IN CWbemServices* pNamespace ) { HRESULT hr = WBEM_E_FAILED; CNasDisk * pNasDisk = NULL; pNasDisk = new CNasDisk(pwszName, pNamespace); if (pNasDisk) { hr = pNasDisk->Initialize(); if (FAILED(hr)) { if (pNasDisk) { delete pNasDisk; pNasDisk = NULL; } } } return pNasDisk; } HRESULT CNasDisk::EnumInstance( IN long lFlags, IN IWbemContext* pCtx, IN IWbemObjectSink* pHandler) { HRESULT hr = S_OK; VDS_DISK_PROP DiskProp; CComPtr spEnumUnallocatedDisk; // // Get all allocated disks // CComPtr spEnumProv; hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProv); if (FAILED(hr)) return hr; // For each provider, do the following do { CComPtr spUnk; CComPtr spSwProvider; CComPtr spEnumPack; ULONG ulFetched = 0; hr = spEnumProv->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) goto _cleanup; // Get IVdsSwProvider hr = spUnk->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) continue; // Get Packs hr = spSwProvider->QueryPacks(&spEnumPack); if (FAILED(hr)) continue; // For each pack, do the following do { CComPtr spPackUnk; CComPtr spPack; CComPtr spEnumDisk; ULONG ulFetchedPack = 0; hr = spEnumPack->Next(1, &spPackUnk, &ulFetchedPack); if (hr == S_FALSE) break; else if (FAILED(hr)) goto _cleanup; // Get IVdsPack hr = spPackUnk->QueryInterface(IID_IVdsPack, (void **)&spPack); if (FAILED(hr)) continue; // Get IVdsDisk hr = spPack->QueryDisks(&spEnumDisk); if (FAILED(hr)) continue; // For each disk, do the following do { CComPtr spDiskUnk; CComPtr spDisk; ULONG ulFetchedDisk = 0; hr = spEnumDisk->Next(1, &spDiskUnk, &ulFetchedDisk); if (hr == S_FALSE) // end of enumeration break; else if (FAILED(hr)) goto _cleanup; // Get IVdsDisk hr = spDiskUnk->QueryInterface(IID_IVdsDisk, (void **)&spDisk); if (FAILED(hr)) continue; hr = spDisk->GetProperties(&DiskProp); if (FAILED(hr)) continue; // Return only fixed disks (no removeable, CDROM etc...) if (DiskProp.dwMediaType == FixedMedia) { // Spawn an instance of the class CComPtr spInstance; hr = m_pClass->SpawnInstance( 0, &spInstance ); if (FAILED(hr)) goto _cleanup; hr = m_LoadInstance(spDisk, spInstance.p); if (FAILED(hr)) goto _cleanup; hr = pHandler->Indicate(1, &spInstance.p); if (FAILED(hr)) goto _cleanup; } SAFE_FREE_DISK_PROP(&DiskProp); }while(TRUE); }while(TRUE); }while(TRUE); // // Get all unallocated disks // hr = m_spService->QueryUnallocatedDisks(&spEnumUnallocatedDisk); if (FAILED(hr)) goto _cleanup; do { CComPtr spUnk; CComPtr spDisk; ULONG ulFetched = 0; hr = spEnumUnallocatedDisk->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) goto _cleanup; // Get IVdsDisk hr = spUnk->QueryInterface(IID_IVdsDisk, (void **)&spDisk); if (FAILED(hr)) continue; // Spawn an instance of the class CComPtr spInstance; hr = m_pClass->SpawnInstance( 0, &spInstance ); if (FAILED(hr)) goto _cleanup; hr = m_LoadInstance(spDisk, spInstance.p); if (FAILED(hr)) goto _cleanup; hr = pHandler->Indicate(1, &spInstance.p); if (FAILED(hr)) goto _cleanup; }while(TRUE); _cleanup: SAFE_FREE_DISK_PROP(&DiskProp); return hr; } HRESULT CNasDisk::GetObject( IN CObjPath& rObjPath, IN long lFlags, IN IWbemContext* pCtx, IN IWbemObjectSink* pHandler ) { HRESULT hr = S_OK; CComPtr spDisk; CComPtr spInstance; // // Get the Disk // hr = m_GetObject(rObjPath, &spDisk); if (FAILED(hr)) return hr; // Spawn an instance of the class hr = m_pClass->SpawnInstance( 0, &spInstance ); if (FAILED(hr)) return hr; hr = m_LoadInstance(spDisk, spInstance.p); if (FAILED(hr)) return hr; hr = pHandler->Indicate(1, &spInstance.p); if (FAILED(hr)) return hr; return hr; } HRESULT CNasDisk::ExecuteMethod( IN BSTR bstrObjPath, IN WCHAR* pwszMethodName, IN long lFlag, IN IWbemClassObject* pParams, IN IWbemObjectSink* pHandler ) { HRESULT hr = S_OK; CComPtr spOutParamClass; CComPtr spOutParams; hr = m_pClass->GetMethod( _bstr_t(pwszMethodName), 0, NULL, &spOutParamClass ); if (FAILED(hr)) return hr; hr = spOutParamClass->SpawnInstance(0, &spOutParams); if (FAILED(hr)) return hr; if (!_wcsicmp(pwszMethodName, PVDR_MTHD_RESCAN)) { hr = m_ExecRescan( bstrObjPath, pParams, spOutParams); } else if (!_wcsicmp(pwszMethodName, PVDR_MTHD_CLEAN)) { hr = m_ExecClean( bstrObjPath, pParams, spOutParams); } else if (!_wcsicmp(pwszMethodName, PVDR_MTHD_CONVERT)) { hr = m_ExecConvert( bstrObjPath, pParams, spOutParams); } else if (!_wcsicmp(pwszMethodName, PVDR_MTHD_REPLACE)) { hr = m_ExecReplace( bstrObjPath, pParams, spOutParams); } else { hr = WBEM_E_INVALID_METHOD; } // If successfully executed the method, return output parameters to WMI if (SUCCEEDED(hr)) { pHandler->Indicate(1, &spOutParams.p); } return hr; } HRESULT CNasDisk::m_GetObject( IN CObjPath& rObjPath, OUT IVdsDisk **ppDisk) { HRESULT hr = S_OK; // // Try to get object based on VDS_OBJECT_ID first // CComPtr spDiskUnk; VDS_OBJECT_ID diskId = GUID_NULL; _bstr_t bstrID; bstrID = rObjPath.GetStringValueForProperty(PVDR_PROP_ID); if (bstrID.length() != 0) { // Convert string GUID if (FAILED(CLSIDFromString(bstrID, &diskId))) return hr; hr = m_spService->GetObject(diskId, VDS_OT_DISK, &spDiskUnk); if (SUCCEEDED(hr)) { hr = spDiskUnk->QueryInterface(IID_IVdsDisk, (void**)ppDisk); return hr; } } // // Couldn't find disk using VDS_OBJECT_ID search by handle name // CComPtr spEnumProv; VDS_DISK_PROP DiskProp; // // Get the specified disk's handle name // _bstr_t bstrDiskName; bstrDiskName = rObjPath.GetStringValueForProperty(PVDR_PROP_NAME); if (bstrDiskName.length() == 0) return WBEM_E_NOT_FOUND; // // Query for all disks and look for disk matching the specified // disk name. // hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProv); if (FAILED(hr)) return hr; // For each provider, get packs do { CComPtr spUnk; CComPtr spSwProvider; CComPtr spEnumPack; ULONG ulFetched = 0; hr = spEnumProv->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsSwProvider hr = spUnk->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) continue; // Get Packs hr = spSwProvider->QueryPacks(&spEnumPack); if (FAILED(hr)) continue; // For each pack, get disks do { CComPtr spPackUnk; CComPtr spPack; CComPtr spEnumDisk; ULONG ulFetchedPack = 0; hr = spEnumPack->Next(1, &spPackUnk, &ulFetchedPack); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsPack hr = spPackUnk->QueryInterface(IID_IVdsPack, (void **)&spPack); if (FAILED(hr)) continue; // Get IVdsDisk hr = spPack->QueryDisks(&spEnumDisk); if (FAILED(hr)) continue; // For each disk, check to see if its the one we want do { CComPtr spDiskUnk; CComPtr spDisk; ULONG ulFetchedDisk = 0; hr = spEnumDisk->Next(1, &spDiskUnk, &ulFetchedDisk); if (hr == S_FALSE) // end of enumeration break; else if (FAILED(hr)) return hr; // Get IVdsDisk hr = spDiskUnk->QueryInterface(IID_IVdsDisk, (void **)&spDisk); if (FAILED(hr)) continue; hr = spDisk->GetProperties(&DiskProp); if (FAILED(hr)) continue; // Compare disk's name to the one we want if (DiskProp.pwszName != NULL && _wcsicmp(DiskProp.pwszName,bstrDiskName) == 0) { SAFE_FREE_DISK_PROP(&DiskProp); // Return the disk pointer *ppDisk = spDisk.Detach(); return hr; } SAFE_FREE_DISK_PROP(&DiskProp); }while(TRUE); }while(TRUE); }while(TRUE); // // Get all unallocated disks // CComPtr spEnumUnallocatedDisk; hr = m_spService->QueryUnallocatedDisks(&spEnumUnallocatedDisk); if (FAILED(hr)) return hr; do { CComPtr spUnk; CComPtr spDisk; ULONG ulFetched = 0; hr = spEnumUnallocatedDisk->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsDisk hr = spUnk->QueryInterface(IID_IVdsDisk, (void **)&spDisk); if (FAILED(hr)) continue; hr = spDisk->GetProperties(&DiskProp); if (FAILED(hr)) continue; // Compare disk's name to the one we want if (DiskProp.pwszName != NULL && _wcsicmp(DiskProp.pwszName,bstrDiskName) == 0) { SAFE_FREE_DISK_PROP(&DiskProp); // Return the disk pointer *ppDisk = spDisk.Detach(); return hr; } SAFE_FREE_DISK_PROP(&DiskProp); }while(TRUE); return WBEM_E_NOT_FOUND; } HRESULT CNasDisk::m_ExecClean( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CObjPath objPath; CComPtr spDisk; CWbemClassObject wcoOutParam(pOutParams); // // Get the disk // if (!objPath.Init(bstrObjPath)) return E_FAIL; hr = m_GetObject(objPath, &spDisk); if (FAILED(hr)) return hr; // // Call method // rcStatus = m_Clean(spDisk); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } HRESULT CNasDisk::m_ExecConvert( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CObjPath objPath; CComPtr spDisk; DWORD convertType; CWbemClassObject wcoOutParam(pOutParams); CWbemClassObject wcoInParam(pParams); // // Prepare to call method // // Get input params if (pParams == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; if (wcoInParam.data() == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; wcoInParam.GetProperty(&convertType, PVDR_PROP_TYPE); // Only support dynamic (0) and basic (1) if (convertType > 1) return WBEM_E_INVALID_METHOD_PARAMETERS; // // Get the disk // if (!objPath.Init(bstrObjPath)) return E_FAIL; hr = m_GetObject(objPath, &spDisk); if (FAILED(hr)) return hr; // // Call method // rcStatus = m_Convert(spDisk, convertType); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } HRESULT CNasDisk::m_ExecReplace( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CObjPath objPath; CComPtr spDisk; CWbemClassObject wcoOutParam(pOutParams); // // Get the disk // if (!objPath.Init(bstrObjPath)) return E_FAIL; hr = m_GetObject(objPath, &spDisk); if (FAILED(hr)) return hr; // // Call method // rcStatus = m_Replace(spDisk); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } HRESULT CNasDisk::m_ExecRescan( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CWbemClassObject wcoOutParam(pOutParams); CObjPath objPath; if (!objPath.Init(bstrObjPath)) return E_FAIL; // // Call method // rcStatus = m_Rescan(); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } DWORD CNasDisk::m_Clean( IN IVdsDisk *pDisk) /*++ Description: Calls into VDS to clean the specified disk. Uses IVdsAdvancedDisk::Clean() method. Arguments: pDisk - pointer to disk to be cleaned Return values: METHOD_RC_NO_ERROR METHOD_RC_FAIL --*/ { HRESULT hr = S_OK; DWORD rcStatus = METHOD_RC_NO_ERROR; CComPtr spAdvDisk; CComPtr spAsync; VDS_ASYNC_OUTPUT AsyncOut; HRESULT AsyncHr; hr = pDisk->QueryInterface(IID_IVdsAdvancedDisk, (void**)&spAdvDisk); if (FAILED(hr)) return METHOD_RC_FAIL; // // Clean it... params: (FORCE, OEM FORCE, NO ALL, async) // hr = spAdvDisk->Clean(TRUE, TRUE, FALSE, &spAsync); if (FAILED(hr)) return METHOD_RC_FAIL; hr = spAsync->Wait(&AsyncHr, &AsyncOut); if (FAILED(hr) || FAILED(AsyncHr)) return METHOD_RC_FAIL; return METHOD_RC_NO_ERROR; } DWORD CNasDisk::m_Convert( IN IVdsDisk *pDisk, IN DWORD convertType ) /*++ Description: Converts the specified disk to either dynamic or basic. Arguments: pDisk - pointer to disk to be converted. convertType - specifies whether to convert to basic or dynamic. Return values: CONVERT_RC_NO_ERROR CONVERT_RC_DISK_NOT_EMPTY CONVERT_RC_UNEXPECTED --*/ { HRESULT hr = S_OK; DWORD rcStatus = METHOD_RC_NO_ERROR; // // Get the disk to convert // CComPtr spSourcePack; BOOL bInitialized = TRUE; // Convert to basic if (convertType == CONVERT_PARAM_BASIC) { hr = m_ConvertBasic(pDisk); if (FAILED(hr)) { if (hr == VDS_E_DISK_NOT_EMPTY) rcStatus = CONVERT_RC_DISK_NOT_EMPTY; else rcStatus = CONVERT_RC_UNEXPECTED; } } else if (convertType == CONVERT_PARAM_DYNAMIC) { hr = m_ConvertDynamic(pDisk); if (FAILED(hr)) { rcStatus = CONVERT_RC_UNEXPECTED; } } else { // User entered in wrong conversion type rcStatus = CONVERT_RC_INVALID_ARG; } return rcStatus; } DWORD CNasDisk::m_Rescan() /*++ Description: Calls into VDS to initiate a system rescan. Arguments: None Return values: METHOD_RC_NO_ERROR METHOD_RC_FAIL --*/ { HRESULT hr = S_OK; DWORD rcStatus = METHOD_RC_NO_ERROR; hr = m_spService->Reenumerate(); if (FAILED(hr)) return METHOD_RC_FAIL; hr = m_spService->Refresh(); if (FAILED(hr)) return METHOD_RC_FAIL; return rcStatus; } DWORD CNasDisk::m_Replace( IN IVdsDisk *pDisk ) /*++ Description: Replaces a missing disk on the system with the specified disk. Repairs any failed redundant volumes on the missing disk onto the replacing disk. Arguments: pDisk - pointer to the replacing disk Return values: REPLACE_RC_NO_ERROR REPLACE_RC_DISK_NOT_DYNAMIC REPLACE_RC_DISK_TOO_SMALL REPLACE_RC_NO_MISSING_DISK REPLACE_RC_UNEXPECTED --*/ { HRESULT hr = S_OK; DWORD rcStatus = REPLACE_RC_NO_ERROR; CComPtr spMissingDisk; CComPtr spPack; VDS_DISK_PROP DiskProp; VDS_DISK_PROP MissingDiskProp; // Initialize memory ZeroMemory(&DiskProp, sizeof(VDS_DISK_PROP)); ZeroMemory(&MissingDiskProp, sizeof(VDS_DISK_PROP)); // // Check if disk is dynamic // if (!g_IsDynamicDisk(pDisk)) { rcStatus = REPLACE_RC_DISK_NOT_DYNAMIC; goto _cleanup; } hr = pDisk->GetProperties(&DiskProp); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } // // Get first missing disk // hr = m_GetMissingDisk(&spMissingDisk); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } else if (hr == S_FALSE) { rcStatus = REPLACE_RC_NO_MISSING_DISK; goto _cleanup; } hr = spMissingDisk->GetProperties(&MissingDiskProp); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } // // Get volumes on missing disk // - Query extents on missing disk // - For each extent get assoicated volume // - If volume is either RAID5 or Mirror sum up size, store volume // - Else delete the volume // VDS_DISK_EXTENT *pExtents; LONG lNumExtents; VolumeExtent *pFtVolumes = NULL; // Array of fault tolerant volumes LONG lNumFtVolumes = 0; // Number of ft voumes in array ULONGLONG ullMinDiskSize = 0; // Sum of the all ft volume extents on missing disk VDS_VOLUME_PROP VolumeProp; ZeroMemory(&VolumeProp, sizeof(VDS_VOLUME_PROP)); hr = spMissingDisk->QueryExtents(&pExtents, &lNumExtents); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } pFtVolumes = new VolumeExtent[lNumExtents]; if (!pFtVolumes) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } LONG index; for (index = 0; index < lNumExtents; index++) { if (pExtents[index].volumeId != GUID_NULL) { CComPtr spUnk; CComPtr spVolume; hr = m_spService->GetObject(pExtents[index].volumeId, VDS_OT_VOLUME, &spUnk); if (FAILED(hr)) continue; hr = spUnk->QueryInterface(IID_IVdsVolume, (void**)&spVolume); if (FAILED(hr)) continue; hr = spVolume->GetProperties(&VolumeProp); if (FAILED(hr)) continue; // Store the failed redundancy fault tolerant volume (FAILED FT Volumes cannot be fixed) if (VolumeProp.health == VDS_H_FAILED_REDUNDANCY) { pFtVolumes[lNumFtVolumes].spVolume = spVolume.Detach(); pFtVolumes[lNumFtVolumes].plexId = pExtents[index].plexId; pFtVolumes[lNumFtVolumes].type = VolumeProp.type; pFtVolumes[lNumFtVolumes].ullSize = pExtents[index].ullSize; ullMinDiskSize += pExtents[index].ullSize; lNumFtVolumes++; } // Delete non ft volumes else { hr = spVolume->Delete(TRUE); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } } } } // // See if we have enough space on the disk to repair all // the fault tolerant volumes. // if (DiskProp.ullSize < ullMinDiskSize) { rcStatus = REPLACE_RC_DISK_TOO_SMALL; goto _cleanup; } // // Go through all the failed redundant fault tolerant volumes and repair them. // for (index = 0; index < lNumFtVolumes; index++) { if(pFtVolumes[index].type == VDS_VT_MIRROR) { hr = m_ReplaceMirror(&pFtVolumes[index], DiskProp.id); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } } else if (pFtVolumes[index].type == VDS_VT_PARITY) { hr = m_ReplaceRaid5(&pFtVolumes[index], DiskProp.id); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } } else { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } } // // Delete missing disk // hr = spMissingDisk->GetPack(&spPack); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } hr = spPack->RemoveMissingDisk(MissingDiskProp.id); if (FAILED(hr)) { rcStatus = REPLACE_RC_UNEXPECTED; goto _cleanup; } _cleanup: delete [] pFtVolumes; SAFE_FREE_DISK_PROP(&DiskProp); SAFE_FREE_DISK_PROP(&MissingDiskProp); SAFE_FREE(pExtents); return rcStatus; } HRESULT CNasDisk::m_LoadInstance( IN IVdsDisk* pDisk, IN OUT IWbemClassObject* pObject ) /*++ Description: Initializes the disk properties. Arguments: pDisk - pointer to the VDS disk object we're creating an instance for pObject - wbem object for the disk Return values: HRESULT's --*/ { HRESULT hr = S_OK; CComPtr spPack; CComPtr spProv; VDS_DISK_PROP DiskProp; VDS_PACK_PROP PackProp; BOOL bInitialized = TRUE; CWbemClassObject wcoInstance(pObject); // // Get VDS disk properties // hr = pDisk->GetProperties(&DiskProp); if (FAILED(hr)) goto _cleanup; // // Get the disk's associated pack and it's properties // hr = pDisk->GetPack(&spPack); if (SUCCEEDED(hr)) { hr = spPack->GetProperties(&PackProp); } if (FAILED(hr)) { if (hr == VDS_E_DISK_NOT_INITIALIZED) bInitialized = FALSE; else goto _cleanup; } // // Set the ID property // LPWSTR lpwszGuid = NULL; lpwszGuid = GuidToString(DiskProp.id); wcoInstance.SetProperty(lpwszGuid, PVDR_PROP_ID); SAFE_FREE(lpwszGuid); // // Set the Status property // DISK_STATUS diskStatus; if (bInitialized && (PackProp.ulFlags & VDS_PKF_FOREIGN)) diskStatus = DISK_STATUS_FOREIGN; else if (DiskProp.health == VDS_H_FAILING) diskStatus = DISK_STATUS_ERRORS; else if (bInitialized && PackProp.status == VDS_PS_OFFLINE) diskStatus = DISK_STATUS_OFFLINE; else if (DiskProp.status == VDS_DS_ONLINE) diskStatus = DISK_STATUS_ONLINE; else if (DiskProp.status == VDS_DS_MISSING) diskStatus = DISK_STATUS_MISSING; else if (DiskProp.status == VDS_DS_UNKNOWN || DiskProp.status == VDS_DS_NOT_READY || DiskProp.status == VDS_DS_FAILED) diskStatus = DISK_STATUS_UNREADABLE; else diskStatus = DISK_STATUS_UNKNOWN; wcoInstance.SetProperty(diskStatus, PVDR_PROP_DISKSTATUS); // // Set the Size property // ULONGLONG ullSize = 0; hr = g_GetDiskSize(pDisk, FALSE, ullSize); // Get total size of disk wcoInstance.SetPropertyI64(ullSize, PVDR_PROP_SIZE); // // Set the FreeSpace property // ULONGLONG ullFreeSpace = 0; if (bInitialized) hr = g_GetDiskSize(pDisk, TRUE, ullFreeSpace); // Get free space on disk else ullFreeSpace = ullSize; wcoInstance.SetPropertyI64(ullFreeSpace, PVDR_PROP_FREESPACE); // // Set the Dynamic property // BOOL isDynamic = g_IsDynamicDisk(pDisk); wcoInstance.SetProperty(isDynamic, PVDR_PROP_DYNAMIC); // // Set the Bus Type property // wcoInstance.SetProperty(DiskProp.BusType, PVDR_PROP_BUSTYPE); // // Set the Name property // // Since the Name property is a key, it can never be NULL if (DiskProp.pwszName != NULL) wcoInstance.SetProperty(DiskProp.pwszName, PVDR_PROP_NAME); else wcoInstance.SetProperty(L"", PVDR_PROP_NAME); // // Set the Friendly Name property // wcoInstance.SetProperty(DiskProp.pwszFriendlyName, PVDR_PROP_FRIENDLYNAME); // // Set the Adaptor Name property // wcoInstance.SetProperty(DiskProp.pwszAdaptorName, PVDR_PROP_ADAPTORNAME); // // Set the Device Path property // wcoInstance.SetProperty(DiskProp.pwszDevicePath, PVDR_PROP_DEVICEPATH); // // Set the disk address properties // int nPort = 0; int nBus = 0; int nTarget = 0; int nLun = 0; if ( DiskProp.pwszDiskAddress!=NULL && wcslen(DiskProp.pwszDiskAddress)>0 ) { int nCount = 0; nCount = swscanf( DiskProp.pwszDiskAddress, L"Port%dPath%dTarget%dLun%d", &nPort, &nBus, &nTarget, &nLun ); //ASSERT( nCount==4 ); } wcoInstance.SetProperty(nPort, PVDR_PROP_PORTNUMBER); wcoInstance.SetProperty(nBus, PVDR_PROP_BUSNUMBER); wcoInstance.SetProperty(nTarget, PVDR_PROP_TARGETNUMBER); wcoInstance.SetProperty(nLun, PVDR_PROP_LUNNUMBER); _cleanup: SAFE_FREE_DISK_PROP(&DiskProp); SAFE_FREE_PACK_PROP(&PackProp); return hr; } HRESULT CNasDisk::m_GetMissingDisk( OUT IVdsDisk **ppMissingDisk ) /*++ Description: Returns the first missing disk found. Arguments: pDisk - the missing disk Return values: HRESULTS: S_OK - Found missing disk S_FALSE - Succesfully searched all disks, but could not find a missing disk. --*/ { HRESULT hr = S_OK; VDS_DISK_PROP DiskProp; CComPtr spEnumProv; CComPtr spMissingDisk; ULONGLONG ullMissingDiskSize; hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProv); if (FAILED(hr)) return hr; // For each provider, get packs do { CComPtr spUnk; CComPtr spProvider; CComPtr spSwProvider; CComPtr spEnumPack; VDS_PROVIDER_PROP ProvProp; ULONG ulFetched = 0; hr = spEnumProv->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsProvider hr = spUnk->QueryInterface(IID_IVdsProvider, (void **)&spProvider); if (FAILED(hr)) continue; hr = spProvider->GetProperties(&ProvProp); SAFE_FREE(ProvProp.pwszName); if (FAILED(hr)) continue; // Missing disks can only be dynamic if ((ProvProp.ulFlags & VDS_PF_DYNAMIC) == 0) continue; // Get IVdsSwProvider hr = spProvider->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) continue; // Get Packs hr = spSwProvider->QueryPacks(&spEnumPack); if (FAILED(hr)) continue; // For each pack, get disks do { CComPtr spPackUnk; CComPtr spPack; CComPtr spEnumDisk; ULONG ulFetchedPack = 0; hr = spEnumPack->Next(1, &spPackUnk, &ulFetchedPack); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsPack hr = spPackUnk->QueryInterface(IID_IVdsPack, (void **)&spPack); if (FAILED(hr)) continue; // Get IVdsDisk hr = spPack->QueryDisks(&spEnumDisk); if (FAILED(hr)) continue; // For each disk, check to see if it's missing do { CComPtr spDiskUnk; CComPtr spDisk; ULONG ulFetchedDisk = 0; hr = spEnumDisk->Next(1, &spDiskUnk, &ulFetchedDisk); if (hr == S_FALSE) // end of enumeration break; else if (FAILED(hr)) return hr; // Get IVdsDisk hr = spDiskUnk->QueryInterface(IID_IVdsDisk, (void **)&spDisk); if (FAILED(hr)) continue; hr = spDisk->GetProperties(&DiskProp); SAFE_FREE_DISK_PROP(&DiskProp); if (FAILED(hr)) continue; // Is the disk a missing disk? if (DiskProp.status == VDS_DS_MISSING) { *ppMissingDisk = spDisk.Detach(); return hr; } }while(TRUE); }while(TRUE); }while(TRUE); // If we get here, there are no missing disks return S_FALSE; } HRESULT CNasDisk::m_ReplaceMirror( IN VolumeExtent *pFtVolume, IN VDS_OBJECT_ID &diskId ) /*++ Description: Replaces the missing plex of a mirrored volume. Arguments: pFtVolume - The volume extent for the failed plex of volume pDisk - The replacement disk Return values: HRESULTS --*/ { HRESULT hr = S_OK; CComPtr spAsync; CComPtr spPack; CComPtr spNewVolume; VDS_ASYNC_OUTPUT AsyncOut; HRESULT AsyncHr; VDS_INPUT_DISK InputDisk; VDS_VOLUME_PROP NewVolumeProp; ZeroMemory(&NewVolumeProp, sizeof(VDS_VOLUME_PROP)); // Remove the missing plex hr = pFtVolume->spVolume->RemovePlex(pFtVolume->plexId, &spAsync); if (FAILED(hr)) goto _cleanup; hr = spAsync->Wait(&AsyncHr, &AsyncOut); spAsync.Release(); if (FAILED(hr) || FAILED(AsyncHr)) { if (SUCCEEDED(hr) && FAILED(AsyncHr)) hr = AsyncHr; goto _cleanup; } // Create simple volume on disk // Set up VDS_INPUT_DISK to create simple vol InputDisk.diskId = diskId; InputDisk.ullSize = pFtVolume->ullSize; InputDisk.plexId = GUID_NULL; InputDisk.memberIdx = 0; // Get our pack hr = pFtVolume->spVolume->GetPack(&spPack); if (FAILED(hr)) goto _cleanup; // Create a new volume (new plex for failed mirror) hr = spPack->CreateVolume(VDS_VT_SIMPLE, &InputDisk, 1, NULL, &spAsync); if (FAILED(hr)) goto _cleanup; hr = spAsync->Wait(&AsyncHr, &AsyncOut); spAsync.Release(); if (FAILED(hr) || FAILED(AsyncHr)) { if (SUCCEEDED(hr) && FAILED(AsyncHr)) hr = AsyncHr; goto _cleanup; } // Get the volume id of the newly created volume hr = AsyncOut.cv.pVolumeUnk->QueryInterface(IID_IVdsVolume, (void **)&spNewVolume); if (FAILED(hr)) goto _cleanup; hr = spNewVolume->GetProperties(&NewVolumeProp); if (FAILED(hr)) goto _cleanup; // Add new plex to volume hr = pFtVolume->spVolume->AddPlex(NewVolumeProp.id, &spAsync); if (FAILED(hr)) goto _cleanup; hr = spAsync->Wait(&AsyncHr, &AsyncOut); spAsync.Release(); if (FAILED(hr) || FAILED(AsyncHr)) { if (SUCCEEDED(hr) && FAILED(AsyncHr)) hr = AsyncHr; goto _cleanup; } _cleanup: SAFE_FREE_VOLUME_PROP(&NewVolumeProp); return hr; } HRESULT CNasDisk::m_ReplaceRaid5( IN VolumeExtent *pFtVolume, IN VDS_OBJECT_ID &diskId ) /*++ Description: Replaces the missing member of a failed RAID-5 plex. Arguments: pFtVolume - The volume extent for the failed plex of volume pDisk - The replacement disk Return values: HRESULTS --*/ { HRESULT hr = S_OK; CComPtr spVolumePlex; CComPtr spUnk; VDS_INPUT_DISK InputDisk; CComPtr spAsync; VDS_ASYNC_OUTPUT AsyncOut; HRESULT AsyncHr; hr = m_spService->GetObject(pFtVolume->plexId, VDS_OT_VOLUME_PLEX, &spUnk); if (FAILED(hr)) goto _cleanup; hr = spUnk->QueryInterface(IID_IVdsVolumePlex, (void**)&spVolumePlex); if (FAILED(hr)) goto _cleanup; InputDisk.diskId = diskId; InputDisk.ullSize = pFtVolume->ullSize; InputDisk.plexId = GUID_NULL; InputDisk.memberIdx = 0; hr = spVolumePlex->Repair(&InputDisk, 1, &spAsync); if (FAILED(hr)) goto _cleanup; hr = spAsync->Wait(&AsyncHr, &AsyncOut); if (FAILED(hr) || FAILED(AsyncHr)) { if (SUCCEEDED(hr) && FAILED(AsyncHr)) hr = AsyncHr; goto _cleanup; } _cleanup: return hr; } HRESULT CNasDisk::m_CreatePack( IN VDS_PROVIDER_FLAG providerFlag, OUT IVdsPack **ppPack ) /*++ Description: Creates a new pack. Arguments: providerFlag - Specifies which provider should create the pack ppPack - The newly created pack Return values: HRESULTS E_FAIL - if specified provider not found --*/ { HRESULT hr = S_OK; CComPtr spEnumProviders; hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProviders); if (FAILED(hr)) return hr; // Find the right provider do { CComPtr spUnk; CComPtr spProvider; ULONG ulFetched = 0; hr = spEnumProviders->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsProvider hr = spUnk->QueryInterface(IID_IVdsProvider, (void **)&spProvider); if (FAILED(hr)) return hr; VDS_PROVIDER_PROP ProvProp; hr = spProvider->GetProperties(&ProvProp); SAFE_FREE(ProvProp.pwszName); if (FAILED(hr)) return hr; // Check to see if we have the provider we want if (ProvProp.ulFlags & providerFlag) { // Found the right provider. Now create the pack. CComPtr spSwProvider; hr = spProvider->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) return hr; hr = spSwProvider->CreatePack(ppPack); return hr; } } while (TRUE); // Couldn't find the right provider... return error return E_FAIL; } HRESULT CNasDisk::m_GetOnlinePack( OUT IVdsPack **ppPack ) /*++ Description: Looks for and returns the first online dynamic pack encountered. Arguments: ppPack - The online dynamic pack found. Return values: HRESULTS S_FALSE - no online dynamic packs found --*/ { HRESULT hr = S_OK; CComPtr spEnumProvider; VDS_PROVIDER_PROP ProvProp; VDS_PACK_PROP PackProp; // get providers hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProvider); if (SUCCEEDED(hr)) { ULONG ulFetched = 0; while(1) { CComPtr spUnkProv; CComPtr spProvider; hr = spEnumProvider->Next(1, &spUnkProv, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsProvider hr = spUnkProv->QueryInterface(IID_IVdsProvider, (void **)&spProvider); if (FAILED(hr)) return hr; hr = spProvider->GetProperties(&ProvProp); SAFE_FREE(ProvProp.pwszName); SAFE_FREE(ProvProp.pwszVersion); if (FAILED(hr)) return hr; if (ProvProp.ulFlags & VDS_PF_DYNAMIC) { CComPtr spSwProvider; CComPtr spEnumPack; hr = spProvider->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) return hr; hr = spSwProvider->QueryPacks(&spEnumPack); if (FAILED(hr)) return hr; while(1) { CComPtr spUnkPack; CComPtr spPack; hr = spEnumPack->Next(1, &spUnkPack, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) { return hr; } hr = spUnkPack->QueryInterface(IID_IVdsPack, (void **)&spPack); if (FAILED(hr)) return hr; hr = spPack->GetProperties(&PackProp); SAFE_FREE(PackProp.pwszName); if (PackProp.status == VDS_PS_ONLINE) { *ppPack = spPack.Detach(); return S_OK; } } } } } // There are no online packs return S_FALSE; } HRESULT CNasDisk::m_ConvertDynamic( IN IVdsDisk *pDisk ) /*++ Description: Converts a basic disk to dynamic. Arguments: pDisk - The disk to convert to dynamic. Return values: HRESULTS --*/ { HRESULT hr = S_OK; // // Get the disk's properties // VDS_DISK_PROP DiskProp; hr = pDisk->GetProperties(&DiskProp); SAFE_FREE_DISK_PROP(&DiskProp); if (FAILED(hr)) return hr; // // Get the disk's pack (SourcePack) // CComPtr spSourcePack; BOOL bInitialized = TRUE; hr = pDisk->GetPack(&spSourcePack); if (hr == VDS_E_DISK_NOT_INITIALIZED) bInitialized = FALSE; else if (FAILED(hr)) return hr; // // Look for an online dynamic pack (TargetPack) // CComPtr spTargetPack; hr = m_GetOnlinePack(&spTargetPack); if (FAILED(hr)) return hr; else if (hr == S_FALSE) { // Couldn't find an online dynamic pack, let's make one! hr = m_CreatePack(VDS_PF_DYNAMIC, &spTargetPack); if (FAILED(hr)) return hr; } // // If disk is uninitialized, intialize disk to dynamic // if (!bInitialized) { // Get partition style VDS_PARTITION_STYLE partStyle; partStyle = (DiskProp.PartitionStyle == VDS_PST_UNKNOWN) ? VDS_PST_MBR : DiskProp.PartitionStyle; hr = spTargetPack->AddDisk(DiskProp.id, partStyle, FALSE); if (FAILED(hr)) return hr; } // // Else if disk is basic, convert it to dynamic // else if (!g_IsDynamicDisk(pDisk)) { // Get pack properties VDS_PACK_PROP TargetPackProp; hr = spTargetPack->GetProperties(&TargetPackProp); if (FAILED(hr)) return hr; // Migrate disk to dynamic pack HRESULT result; BOOL reboot; hr = spSourcePack->MigrateDisks(&(DiskProp.id), 1, TargetPackProp.id, FALSE, FALSE, &result, &reboot); if (FAILED(hr)) return hr; } return hr; } HRESULT CNasDisk::m_ConvertBasic( IN IVdsDisk *pDisk ) /*++ Description: Converts a dynamic disk to basic Arguments: pDisk - The disk to convert to basic. Return values: HRESULTS --*/ { HRESULT hr = S_OK; // // Get the disk's properties // VDS_DISK_PROP DiskProp; hr = pDisk->GetProperties(&DiskProp); SAFE_FREE_DISK_PROP(&DiskProp); if (FAILED(hr)) return hr; // // Get the disk's pack (SourcePack) // CComPtr spSourcePack; BOOL bInitialized = TRUE; hr = pDisk->GetPack(&spSourcePack); if (hr == VDS_E_DISK_NOT_INITIALIZED) bInitialized = FALSE; else if (FAILED(hr)) return hr; // // If disk is dynamic convert to basic. // if (g_IsDynamicDisk(pDisk)) { // Create the target basic pack CComPtr spTargetPack; VDS_PACK_PROP TargetPackProp; hr = m_CreatePack(VDS_PF_ONE_DISK_ONLY_PER_PACK, &spTargetPack); if (FAILED(hr)) return hr; hr = spTargetPack->GetProperties(&TargetPackProp); if (FAILED(hr)) return hr; // Migrate the disk over to the new basic pack HRESULT result; BOOL reboot; hr = spSourcePack->MigrateDisks(&(DiskProp.id), 1, TargetPackProp.id, FALSE, FALSE, &result, &reboot); if (FAILED(hr)) { if (result == VDS_E_DISK_NOT_EMPTY) hr = result; return hr; } } // // Else if disk is uninitialized, intialize it to basic // else if (!bInitialized) { // Create the target basic pack CComPtr spTargetPack; VDS_PACK_PROP TargetPackProp; hr = m_CreatePack(VDS_PF_ONE_DISK_ONLY_PER_PACK, &spTargetPack); if (FAILED(hr)) return hr; // Get the disks partition style VDS_PARTITION_STYLE partStyle; partStyle = (DiskProp.PartitionStyle == VDS_PST_UNKNOWN) ? VDS_PST_MBR : DiskProp.PartitionStyle; hr = spTargetPack->AddDisk(DiskProp.id, partStyle, FALSE); if (FAILED(hr)) return hr; } return hr; } //**************************************************************************** // // CNasVolume // //**************************************************************************** CNasVolume::CNasVolume( IN LPCWSTR pwszName, IN CWbemServices* pNamespace ) : CProvBase( pwszName, pNamespace ) { } HRESULT CNasVolume::Initialize() { HRESULT hr = S_OK; hr = g_LoadService(&m_spService); return hr; } CProvBase * CNasVolume::S_CreateThis( IN LPCWSTR pwszName, IN CWbemServices* pNamespace ) { HRESULT hr = WBEM_E_FAILED; CNasVolume * pNasVolume = NULL; pNasVolume = new CNasVolume(pwszName, pNamespace); if (pNasVolume) { hr = pNasVolume->Initialize(); if (FAILED(hr)) { if (pNasVolume) { delete pNasVolume; pNasVolume = NULL; } } } return pNasVolume; } HRESULT CNasVolume::EnumInstance( IN long lFlags, IN IWbemContext* pCtx, IN IWbemObjectSink* pHandler) { HRESULT hr = S_OK; // // Get all the volumes // CComPtr spEnumProv; hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProv); if (FAILED(hr)) return hr; // For each provider get the associated packs do { CComPtr spUnk; CComPtr spSwProvider; CComPtr spEnumPack; VDS_PACK_PROP PackProp; ULONG ulFetched = 0; hr = spEnumProv->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsSwProvider hr = spUnk->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) continue; // Get Packs hr = spSwProvider->QueryPacks(&spEnumPack); if (FAILED(hr)) continue; // For each pack get the associated volumes do { CComPtr spPackUnk; CComPtr spPack; CComPtr spEnumVolume; ULONG ulFetchedPack = 0; hr = spEnumPack->Next(1, &spPackUnk, &ulFetchedPack); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; hr = spPackUnk->QueryInterface(IID_IVdsPack, (void **)&spPack); if (FAILED(hr)) continue; hr = spPack->GetProperties(&PackProp); SAFE_FREE(PackProp.pwszName); if (FAILED(hr)) continue; // Only report volumes on online packs. if (PackProp.status != VDS_PS_ONLINE) continue; hr = spPack->QueryVolumes(&spEnumVolume); if (FAILED(hr)) continue; // For each volume create a WMI object for it do { CComPtr spVolumeUnk; CComPtr spVolume; ULONG ulFetchedDisk = 0; hr = spEnumVolume->Next(1, &spVolumeUnk, &ulFetchedDisk); if (hr == S_FALSE) // end of enumeration break; else if (FAILED(hr)) return hr; // Get IVdsVolume hr = spVolumeUnk->QueryInterface(IID_IVdsVolume, (void **)&spVolume); if (FAILED(hr)) continue; // Spawn an instance of the class CComPtr spInstance; hr = m_pClass->SpawnInstance( 0, &spInstance ); if (FAILED(hr)) return hr; hr = m_LoadInstance(spVolume, spInstance.p); if (FAILED(hr)) return hr; hr = pHandler->Indicate(1, &spInstance.p); if (FAILED(hr)) return hr; }while(TRUE); }while(TRUE); }while(TRUE); return hr; } HRESULT CNasVolume::GetObject( IN CObjPath& rObjPath, IN long lFlags, IN IWbemContext* pCtx, IN IWbemObjectSink* pHandler ) { HRESULT hr = S_OK; CComPtr spVolume; CComPtr spInstance; // // Get the volume // hr = m_GetObject(rObjPath, &spVolume); if (FAILED(hr)) return hr; // Spawn an instance of the class hr = m_pClass->SpawnInstance( 0, &spInstance ); if (FAILED(hr)) return hr; hr = m_LoadInstance(spVolume, spInstance.p); if (FAILED(hr)) return hr; hr = pHandler->Indicate(1, &spInstance.p); if (FAILED(hr)) return hr; return hr; } HRESULT CNasVolume::ExecuteMethod( IN BSTR bstrObjPath, IN WCHAR* pwszMethodName, IN long lFlag, IN IWbemClassObject* pParams, IN IWbemObjectSink* pHandler ) { HRESULT hr = S_OK; CComPtr spOutParamClass; CComPtr spOutParams; hr = m_pClass->GetMethod( _bstr_t(pwszMethodName), 0, NULL, &spOutParamClass ); if (FAILED(hr)) return hr; hr = spOutParamClass->SpawnInstance(0, &spOutParams); if (FAILED(hr)) return hr; if (!_wcsicmp(pwszMethodName, PVDR_MTHD_REACTIVATE)) { hr = m_ExecReactivate( bstrObjPath, pParams, spOutParams); } else if (!_wcsicmp(pwszMethodName, PVDR_MTHD_EXTEND)) { hr = m_ExecExtend( bstrObjPath, pParams, spOutParams); } else if (!_wcsicmp(pwszMethodName, PVDR_MTHD_DELETE)) { hr = m_ExecDelete( bstrObjPath, pParams, spOutParams); } else if (!_wcsicmp(pwszMethodName, PVDR_MTHD_CREATE)) { hr = m_ExecCreate( bstrObjPath, pParams, spOutParams); } else { hr = WBEM_E_INVALID_METHOD; } // If successfully executed the method, return output parameters to WMI if (SUCCEEDED(hr)) { pHandler->Indicate(1, &spOutParams.p); } return hr; } HRESULT CNasVolume::m_GetObject( IN CObjPath& rObjPath, OUT IVdsVolume **ppVolume) { HRESULT hr = S_OK; // // Try to get object based on VDS_OBJECT_ID first // CComPtr spVolUnk; VDS_OBJECT_ID volumeId = GUID_NULL; _bstr_t bstrID; bstrID = rObjPath.GetStringValueForProperty(PVDR_PROP_ID); if (bstrID.length() != 0) { // Convert string GUID if (FAILED(CLSIDFromString(bstrID, &volumeId))) return hr; hr = m_spService->GetObject(volumeId, VDS_OT_VOLUME, &spVolUnk); if (SUCCEEDED(hr)) { hr = spVolUnk->QueryInterface(IID_IVdsVolume, (void**)ppVolume); if (SUCCEEDED(hr)) return hr; } } // // Could not find object based on ID, need to get it based on name // // Get the specified volumes's handle name _bstr_t bstrVolumeName; bstrVolumeName = rObjPath.GetStringValueForProperty(PVDR_PROP_NAME); if (bstrVolumeName.length() == 0) return WBEM_E_NOT_FOUND; CComPtr spEnumProv; hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProv); if (FAILED(hr)) return hr; // For each provider get the associated packs do { CComPtr spUnk; CComPtr spSwProvider; CComPtr spEnumPack; ULONG ulFetched = 0; hr = spEnumProv->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsSwProvider hr = spUnk->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) continue; // Get Packs hr = spSwProvider->QueryPacks(&spEnumPack); if (FAILED(hr)) continue; // For each pack get the associated volumes do { CComPtr spPackUnk; CComPtr spPack; CComPtr spEnumVolume; ULONG ulFetchedPack = 0; hr = spEnumPack->Next(1, &spPackUnk, &ulFetchedPack); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; hr = spPackUnk->QueryInterface(IID_IVdsPack, (void **)&spPack); if (FAILED(hr)) continue; hr = spPack->QueryVolumes(&spEnumVolume); if (FAILED(hr)) continue; // For each volume create, check to see if it's the one we want do { CComPtr spVolumeUnk; CComPtr spVolume; VDS_VOLUME_PROP VolumeProp; ZeroMemory(&VolumeProp, sizeof(VDS_VOLUME_PROP)); ULONG ulFetchedDisk = 0; hr = spEnumVolume->Next(1, &spVolumeUnk, &ulFetchedDisk); if (hr == S_FALSE) // end of enumeration break; else if (FAILED(hr)) return hr; // Get IVdsVolume hr = spVolumeUnk->QueryInterface(IID_IVdsVolume, (void **)&spVolume); if (FAILED(hr)) continue; hr = spVolume->GetProperties(&VolumeProp); if (FAILED(hr)) continue; // Compare volumes's name to the one we want if (VolumeProp.pwszName != NULL && _wcsicmp(VolumeProp.pwszName,bstrVolumeName) == 0) { SAFE_FREE_VOLUME_PROP(&VolumeProp); // Return the volume pointer *ppVolume = spVolume.Detach(); return hr; } SAFE_FREE_VOLUME_PROP(&VolumeProp); }while(TRUE); }while(TRUE); }while(TRUE); return WBEM_E_NOT_FOUND; } HRESULT CNasVolume::m_ExecReactivate( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CComPtr spVolume; CObjPath objPath; CWbemClassObject wcoInParam(pParams); CWbemClassObject wcoOutParam(pOutParams); // // Get the volume // if (!objPath.Init(bstrObjPath)) return E_FAIL; hr = m_GetObject(objPath, &spVolume); if (FAILED(hr)) return hr; // // Call method // rcStatus = m_Reactivate(spVolume); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } HRESULT CNasVolume::m_ExecExtend( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CObjPath objPath; CComPtr spVolume; _bstr_t bstrDiskId; VDS_OBJECT_ID diskId; LONGLONG llSize = 0; CWbemClassObject wcoInParam(pParams); CWbemClassObject wcoOutParam(pOutParams); // // Prepare to call method // // Get input params if (pParams == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; if (wcoInParam.data() == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; // Get DiskID wcoInParam.GetProperty(bstrDiskId, PVDR_PROP_DISKID); if (FAILED(CLSIDFromString(bstrDiskId, &diskId))) return hr; // Get the extend size wcoInParam.GetPropertyI64(&llSize, PVDR_PROP_SIZE); // Get the volume if (!objPath.Init(bstrObjPath)) return E_FAIL; hr = m_GetObject(objPath, &spVolume); if (FAILED(hr)) return hr; // // Call method // rcStatus = m_Extend(spVolume, diskId, llSize); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } HRESULT CNasVolume::m_ExecCreate( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CObjPath objPath; DWORD dwVolumeType; // Volume type to create _bstr_t *pBstrDiskIDs; // Array of disk ID's DWORD dwNumDisks; // Number of disks inputted LONGLONG llSize = 0; _bstr_t bstrVolumeLabel; CWbemClassObject wcoInParam(pParams); CWbemClassObject wcoOutParam(pOutParams); if (!objPath.Init(bstrObjPath)) return E_FAIL; // // Prepare to call method // // Get input params if (pParams == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; if (wcoInParam.data() == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; // Get the volume type wcoInParam.GetProperty(&dwVolumeType, PVDR_PROP_TYPE); // Get the disks wcoInParam.GetProperty(&dwNumDisks, &pBstrDiskIDs, PVDR_PROP_DISKS); // Get the volume size wcoInParam.GetPropertyI64(&llSize, PVDR_PROP_SIZE); // Get the volume label wcoInParam.GetProperty(bstrVolumeLabel, PVDR_PROP_LABEL); // // Call method // rcStatus = m_Create(dwVolumeType, bstrVolumeLabel, pBstrDiskIDs, dwNumDisks, llSize); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } HRESULT CNasVolume::m_ExecDelete( IN BSTR bstrObjPath, IN IWbemClassObject* pParams, IN OUT IWbemClassObject* pOutParams) { HRESULT hr = S_OK; DWORD rcStatus = ERROR_SUCCESS; CObjPath objPath; CComPtr spVolume; BOOL bForce; CWbemClassObject wcoInParam(pParams); CWbemClassObject wcoOutParam(pOutParams); // // Prepare to call method // // Get input params if (pParams == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; if (wcoInParam.data() == NULL) return WBEM_E_INVALID_METHOD_PARAMETERS; // Get the volume type wcoInParam.GetProperty(&bForce, PVDR_PROP_FORCE); // Get the volume if (!objPath.Init(bstrObjPath)) return E_FAIL; hr = m_GetObject(objPath, &spVolume); if (FAILED(hr)) return hr; // // Call method // rcStatus = m_Delete(spVolume, bForce); // // Output the return code // hr = wcoOutParam.SetProperty(rcStatus, PVD_WBEM_PROP_RETURNVALUE); return hr; } DWORD CNasVolume::m_Reactivate( IN IVdsVolume *pVolume ) /*++ Description: Calls into VDS to reactivate a volume. Does this by calling IVdsPack::Recover() on the volume's pack. Arguments: rObjPath - Object path for the volume to be reactivated Return values: METHOD_RC_NO_ERROR METHOD_RC_FAIL --*/ { HRESULT hr = S_OK; DWORD rcStatus = METHOD_RC_NO_ERROR; CComPtr spPack; CComPtr spAsync; // // Get the volume's pack and call Recover on it // hr = pVolume->GetPack(&spPack); if (FAILED(hr)) return METHOD_RC_FAIL; hr = spPack->Recover(&spAsync); if (FAILED(hr)) return METHOD_RC_FAIL; // // Wait for completion // VDS_ASYNC_OUTPUT AsyncOut; HRESULT AsyncHr; hr = spAsync->Wait(&AsyncHr, &AsyncOut); if (FAILED(AsyncHr) || FAILED(hr)) return METHOD_RC_FAIL; return rcStatus; } DWORD CNasVolume::m_Extend( IN IVdsVolume *pVolume, IN VDS_OBJECT_ID diskId, IN ULONGLONG ullSize ) /*++ Description: Calls into VDS to extend a volume. Arguments: rObjPath - Object path for the volume to be extended diskId - ID of disk to be extended onto. ullSize - Amount to extend volume. (If 0, extend to max size) Return values: EXTEND_RC_NO_ERROR - Success EXTEND_RC_INVALID_ARG - Disk inputted is invalid EXTEND_RC_NOT_ENOUGH_SPACE - Not enough space on specified disk to extend volume EXTEND_RC_VOLUME_NO_SUPPORT - The specified volume type cannot be extended EXTEND_RC_UNEXPECTED --*/ { HRESULT hr = S_OK; DWORD rcStatus = METHOD_RC_NO_ERROR; CComPtr spEnumPlex; CComPtr spPlexUnk; CComPtr spVolumePlex; VDS_VOLUME_PROP VolumeProp; VDS_VOLUME_PLEX_PROP VolPlexProp; ZeroMemory(&VolumeProp, sizeof(VDS_VOLUME_PROP)); ZeroMemory(&VolPlexProp, sizeof(VDS_VOLUME_PLEX_PROP)); // // Get volume properties // hr = pVolume->GetProperties(&VolumeProp); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; // If the volume is neither simple or spanned, cannot extend. if (VolumeProp.type != VDS_VT_SIMPLE && VolumeProp.type != VDS_VT_SPAN) return EXTEND_RC_VOLUME_NO_SUPPORT; // Cannot extend failed volumes. if (VolumeProp.health == VDS_H_FAILED) return EXTEND_RC_VOLUME_NO_SUPPORT; // // Get the volume's first plex // hr = pVolume->QueryPlexes(&spEnumPlex); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; ULONG ulFetchedPlex; hr = spEnumPlex->Next(1, &spPlexUnk, &ulFetchedPlex); if (FAILED(hr) || hr == S_FALSE) return EXTEND_RC_UNEXPECTED; hr = spPlexUnk->QueryInterface(IID_IVdsVolumePlex, (void**)&spVolumePlex); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; hr = spVolumePlex->GetProperties(&VolPlexProp); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; // // Get the maximum possible extend size if no size specified. // // In the case of dynamic disks, we need to check if inputted size // is too large. This is due to a bug in the dynamic provider not // returning an error when inputted size to extend is too large // (bug#693222) // //if (ullSize == 0) if (ullSize == 0 || g_IsDynamicVolume(pVolume)) { CComPtr spUnk; CComPtr spDisk; hr = m_spService->GetObject(diskId, VDS_OT_DISK, &spUnk); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; hr = spUnk->QueryInterface(IID_IVdsDisk, (void**)&spDisk); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; // Get disk extents VDS_DISK_EXTENT *pExtents = NULL; LONG lNumExtents = 0; hr = spDisk->QueryExtents(&pExtents, &lNumExtents); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; // // Basic // - Find the extent that belongs to this volume // - See if we can extend into the next extent // if (!g_IsDynamicVolume(pVolume)) { // // Get the extended partition // ULONGLONG ullExtendedOffset; ULONGLONG ullExtendedSize; hr = m_GetExtendedPart(spDisk, &ullExtendedOffset, &ullExtendedSize); if (FAILED(hr)) return EXTEND_RC_UNEXPECTED; // // Look for the next contiguous free extent that's not in an extended // partition. Not handling extending into extended free areas. NAS // boxes don't usually have logical drives. // LONG i; for (i=0; i ullExtendedOffset + ullExtendedSize)) { ullSize = pExtents[i].ullSize; } else { return EXTEND_RC_NOT_ENOUGH_SPACE; } } // // Dynamic // - Sum up all free extents on disk else { ULONGLONG ullFreeSpace = 0; for (LONG i = 0; i < lNumExtents; i++) { // Add up free space if (pExtents[i].type == VDS_DET_FREE) ullFreeSpace += pExtents[i].ullSize; } // Check to see if there is enough space on the disk if (ullSize == 0) ullSize = ullFreeSpace; else if (ullFreeSpace == 0 || ullSize > ullFreeSpace) return EXTEND_RC_NOT_ENOUGH_SPACE; } } // // Set up VDS_INPUT_DISK // VDS_INPUT_DISK diskInput; diskInput.diskId = diskId; diskInput.ullSize = ullSize; diskInput.memberIdx = 0; diskInput.plexId = VolPlexProp.id; // // Call Extend // CComPtr spAsync; hr = pVolume->Extend(&diskInput, 1, &spAsync); if (FAILED(hr)) { switch(hr) { case VDS_E_NOT_ENOUGH_SPACE: rcStatus = EXTEND_RC_NOT_ENOUGH_SPACE; break; case E_INVALIDARG: rcStatus = EXTEND_RC_INVALID_ARG; break; default: rcStatus = EXTEND_RC_UNEXPECTED; } return rcStatus; } // wait for completion VDS_ASYNC_OUTPUT AsyncOut; HRESULT AsyncHr; hr = spAsync->Wait(&AsyncHr, &AsyncOut); if (FAILED(hr)) rcStatus = EXTEND_RC_UNEXPECTED; return rcStatus; } DWORD CNasVolume::m_Create( IN DWORD dwVolumeType, IN _bstr_t bstrVolumeLabel, IN _bstr_t *pDisks, IN DWORD dwNumDisks, IN ULONGLONG ullSize ) /*++ Description: Calls into VDS to create a volume Arguments: dwVolumeType - Specifies the type of volume to create bstrVolumeLabel - The volume label pDisks - Array of disk ID's of the disks to create volume on dwNumDisks - Number of disks in array ullSize - Amount of space to use on each disk (if 0, uses max) Return values: CREATE_RC_NO_ERROR CREATE_RC_NOT_ENOUGH_DISKS CREATE_RC_INVALID_DISK CREATE_RC_NOT_ENOUGH_SPACE CREATE_RC_SIZE_LESS_THAN_MIN CREATE_RC_ASSIGN_DRIVE_LETTER_FAILED CREATE_RC_FORMAT_FAILED CREATE_RC_UNEXPECTED --*/ { HRESULT hr = S_OK; DWORD rcStatus = METHOD_RC_NO_ERROR; ULONGLONG ullMaxSize = 0xFFFFFFFFFFFFFFFF; ULONG ulInterleave = 0; CComPtr spPack; // pack to create volume in CComPtr spVolume; // the newly created volume CComPtr spAsync; // // Check to see if we have enough disks // if (dwVolumeType == VDS_VT_PARITY) { if (dwNumDisks < 3) return CREATE_RC_NOT_ENOUGH_DISKS; } else if (dwVolumeType == VDS_VT_MIRROR || dwVolumeType == VDS_VT_STRIPE) { if (dwNumDisks < 2) return CREATE_RC_NOT_ENOUGH_DISKS; } // // Set interleave value // if (dwVolumeType == VDS_VT_PARITY || dwVolumeType == VDS_VT_STRIPE) ulInterleave = 64 * 1024; // // Create the VDS_INPUT_DISK array // VDS_INPUT_DISK *pInputDisks; pInputDisks = new VDS_INPUT_DISK[dwNumDisks]; // If size is unspecified, need to figure out max value for size // - Sum up the free space on each disk // - Max size for volume determined by smallest disk VDS_OBJECT_ID diskId; for (int i = 0; i < dwNumDisks; i++) { // Get the disk object CComPtr spDiskUnk; CComPtr spDisk; if (FAILED(CLSIDFromString(pDisks[i], &diskId))) { rcStatus = CREATE_RC_UNEXPECTED; goto _cleanup; } hr = m_spService->GetObject(diskId, VDS_OT_DISK, &spDiskUnk); if (SUCCEEDED(hr)) hr = spDiskUnk->QueryInterface(IID_IVdsDisk, (void**)&spDisk); if (FAILED(hr)) { rcStatus = CREATE_RC_UNEXPECTED; goto _cleanup; } // Make sure disk is dynamic if (!g_IsDynamicDisk(spDisk)) { rcStatus = CREATE_RC_INVALID_DISK; goto _cleanup; } // If we don't have a pack yet, get it. // Assuming that all the inputted disks belong to the same // pack. if (spPack.p == NULL) { hr = spDisk->GetPack(&spPack); if (FAILED(hr)) { rcStatus = CREATE_RC_UNEXPECTED; goto _cleanup; } } // Get the disk free space ULONGLONG ullFreeSpace = 0; hr = g_GetDiskSize(spDisk, TRUE, ullFreeSpace); if (FAILED(hr)) { rcStatus = CREATE_RC_UNEXPECTED; goto _cleanup; } // Compare the free space size to the current max size. if (ullFreeSpace < ullMaxSize) ullMaxSize = ullFreeSpace; pInputDisks[i].diskId = diskId; pInputDisks[i].plexId = GUID_NULL; } if (ullSize > ullMaxSize || ullMaxSize < (1024*1024)) { rcStatus = CREATE_RC_NOT_ENOUGH_SPACE; goto _cleanup; } // Set the size attribute for disk input if (ullSize == 0) ullSize = ullMaxSize; for (int i = 0; i < dwNumDisks; i++) pInputDisks[i].ullSize = ullSize; // // Create the volume // hr = spPack->CreateVolume( (VDS_VOLUME_TYPE)dwVolumeType, pInputDisks, dwNumDisks, ulInterleave, &spAsync); if (FAILED(hr)) { switch (hr) { case VDS_E_EXTENT_SIZE_LESS_THAN_MIN: rcStatus = CREATE_RC_SIZE_LESS_THAN_MIN; break; default: rcStatus = CREATE_RC_UNEXPECTED; } goto _cleanup; } // wait VDS_ASYNC_OUTPUT AsyncOut; HRESULT AsyncHr; hr = spAsync->Wait(&AsyncHr, &AsyncOut); spAsync.Release(); if (FAILED(hr) || FAILED(AsyncHr)) { rcStatus = CREATE_RC_UNEXPECTED; goto _cleanup; } // Get the newly created volume hr = AsyncOut.cv.pVolumeUnk->QueryInterface(IID_IVdsVolume, (void **)&spVolume); SAFE_RELEASE(AsyncOut.cv.pVolumeUnk); if (FAILED(hr)) { rcStatus = CREATE_RC_UNEXPECTED; goto _cleanup; } // // Format // hr = m_Format( spVolume, VDS_FST_NTFS, bstrVolumeLabel, 512, TRUE, // Force the format TRUE, // Quick format TRUE // Enable compression ); if (FAILED(hr)) rcStatus = CREATE_RC_FORMAT_FAILED; // // Auto-assign drive letter // WCHAR DriveLetter; hr = m_GetNextAvailableDriveLetter(DriveLetter); if (FAILED(hr)) { rcStatus = CREATE_RC_UNEXPECTED; goto _cleanup; } if (hr == S_FALSE) { // No drive letter to assign rcStatus = CREATE_RC_ASSIGN_DRIVE_LETTER_FAILED; goto _cleanup; } // If drive lettter available, assign it. hr = m_AssignDriveLetter(spVolume, DriveLetter); if (FAILED(hr)) { rcStatus = CREATE_RC_ASSIGN_DRIVE_LETTER_FAILED; } _cleanup: delete [] pInputDisks; return rcStatus; } DWORD CNasVolume::m_Delete( IN IVdsVolume *pVolume, IN BOOL bForce ) /*++ Description: Calls into VDS to delete a volume Arguments: pVolume - Volume to be deleted bForce - Force flag Return values: DELETE_RC_NO_ERROR DELETE_RC_VOLUME_IN_USE DELETE_RC_UNEXPECTED --*/ { HRESULT hr = S_OK; DWORD rcStatus = DELETE_RC_NO_ERROR; hr = pVolume->Delete(bForce); if (FAILED(hr)) { switch(hr) { case VDS_E_DEVICE_IN_USE: rcStatus = DELETE_RC_VOLUME_IN_USE; break; case VDS_E_OPERATION_DENIED: rcStatus = DELETE_RC_OPERATION_DENIED; break; default: rcStatus = DELETE_RC_UNEXPECTED; } } return rcStatus; } HRESULT CNasVolume::m_LoadInstance( IN IVdsVolume* pVolume, IN OUT IWbemClassObject* pObject ) /*++ Description: Initializes the volume properties. Arguments: pVolume - pObject - Return values: HRESULT's --*/ { HRESULT hr = S_OK; VDS_VOLUME_PROP VolumeProp; CComPtr spVolumeMF; VDS_FILE_SYSTEM_PROP FileSystemProp; BOOL bHasFileSystem = TRUE; LPWSTR lpwszGuid; VOLUME_STATUS volumeStatus; ULONGLONG ullFreeSpace = 0; LPWSTR* pwszPath = NULL; LONG lNumPath; BSTR* pBstrPath = NULL; LONG index = 0; CWbemClassObject wcoInstance(pObject); ZeroMemory(&VolumeProp, sizeof(VDS_VOLUME_PROP)); // // Get VDS volume properties // hr = pVolume->GetProperties(&VolumeProp); if (FAILED(hr)) { goto _cleanup; } // // Get the volumes's filesystem // hr = pVolume->QueryInterface(IID_IVdsVolumeMF, (void **)(&spVolumeMF)); if (FAILED(hr)) { goto _cleanup; } hr = spVolumeMF->GetFileSystemProperties(&FileSystemProp); if (FAILED(hr)) { bHasFileSystem = FALSE; } // // Set the ID property // lpwszGuid = GuidToString(VolumeProp.id); wcoInstance.SetProperty(lpwszGuid, PVDR_PROP_ID); SAFE_FREE(lpwszGuid); // // Set the Label property // wcoInstance.SetProperty(FileSystemProp.pwszLabel, PVDR_PROP_LABEL); // // Set the volume type // if (g_IsDynamicVolume(pVolume)) { wcoInstance.SetProperty(VolumeProp.type, PVDR_PROP_TYPE); } else { // Set the volume type to PARTITION if volume is basic // NOTE: we don't care about CDROM and removeable disks since NAS appliances // shoudln't have any. wcoInstance.SetProperty(1, PVDR_PROP_TYPE); } // // Set the volume status // // Get the volume status (Its really volume health, staying consistent with old diskpart) switch (VolumeProp.health) { case VDS_H_HEALTHY: case VDS_H_FAILING: case VDS_H_FAILING_REDUNDANCY: volumeStatus = VOLUME_STATUS_HEALTHY; break; case VDS_H_REBUILDING: volumeStatus = VOLUME_STATUS_REBUILDING; break; case VDS_H_FAILED: volumeStatus = VOLUME_STATUS_FAILED; break; case VDS_H_FAILED_REDUNDANCY: case VDS_H_FAILED_REDUNDANCY_FAILING: volumeStatus = VOLUME_STATUS_FAILED_RD; break; default: volumeStatus = VOLUME_STATUS_UNKNOWN; break; } wcoInstance.SetProperty(volumeStatus, PVDR_PROP_VOLUMESTATUS); // // Set the Size property // wcoInstance.SetPropertyI64(VolumeProp.ullSize, PVDR_PROP_SIZE); // // Set the free space property // if (bHasFileSystem) { ullFreeSpace = FileSystemProp.ullAvailableAllocationUnits * FileSystemProp.ulAllocationUnitSize; } wcoInstance.SetPropertyI64(ullFreeSpace, PVDR_PROP_FREESPACE); // // Set the Name property // // since Name property is a key, it can never be NULL if (VolumeProp.pwszName != NULL) wcoInstance.SetProperty(VolumeProp.pwszName, PVDR_PROP_NAME); else wcoInstance.SetProperty(L"", PVDR_PROP_NAME); // // Get the mount points for the volume // hr = spVolumeMF->QueryAccessPaths(&pwszPath, &lNumPath); if (FAILED(hr)) { // If failed, make sure we don't put junk into MountPath property lNumPath = 0; goto _cleanup; } // Convert WSTR to BSTR pBstrPath = (BSTR*)malloc(sizeof(BSTR)*lNumPath); if (!pBstrPath) { hr = E_OUTOFMEMORY; goto _cleanup; } while (index < lNumPath) { int pathLength = wcslen(pwszPath[index]); pBstrPath[index] = SysAllocStringLen(pwszPath[index], pathLength-1); index++; } wcoInstance.SetProperty(lNumPath, pBstrPath, PVDR_PROP_MOUNT); for (int i=0; i < lNumPath; i++) free(pBstrPath[i]); _cleanup: SAFE_FREE_VOLUME_PROP(&VolumeProp); SAFE_FREE(pwszPath); return hr; } HRESULT CNasVolume::m_GetExtendedPart( IN IVdsDisk *pDisk, OUT ULONGLONG *ullExtendedOffset, OUT ULONGLONG *ullExtendedSize ) /*++ Description: Get's the extened partition on a basic disk Arguments: pDisk - The disk to get the extended partition from ullExtendedOffset - offset of the extended partition ullExtendedSize - size of the extended partition Return values: HRESULT's --*/ { HRESULT hr = S_OK; CComPtr spAdvDisk; VDS_PARTITION_PROP *pPartProp = NULL; *ullExtendedOffset = 0; *ullExtendedSize = 0; // Get IVdsAdvancedDisk hr = pDisk->QueryInterface(IID_IVdsAdvancedDisk, (void **)(&spAdvDisk)); if (FAILED(hr)) return hr; // Get partitions LONG lNumPart = 0; hr = spAdvDisk->QueryPartitions(&pPartProp, &lNumPart); if (FAILED(hr)) return hr; // Get ullExtendedOffset and ullExtendedSize for (LONG i = 0; i < lNumPart; i++) { if (IsContainerPartition((pPartProp+i)->Mbr.partitionType)) { *ullExtendedOffset = (pPartProp+i)->ullOffset; *ullExtendedSize = (pPartProp+i)->ullSize; break; } } SAFE_FREE(pPartProp); return hr; } HRESULT CNasVolume::m_GetNextAvailableDriveLetter( OUT WCHAR &DriveLetter ) /*++ Description: Gets the next available drive letter. Arguments: DriveLetter - The next available drive letter Return values: S_FALSE - No drive letters available S_OK --*/ { HRESULT hr = S_OK; VDS_DRIVE_LETTER_PROP *pDLProp = NULL; pDLProp = new VDS_DRIVE_LETTER_PROP[26]; hr = m_spService->QueryDriveLetters(L'A', 26, pDLProp); if (FAILED(hr)) goto _cleanup; // Start searching for free DL from C int i = 2; while (i < 26 && (pDLProp+i)->bUsed == TRUE) i++; if (i == 26) { // See if either A or B are free if (pDLProp[1].bUsed == FALSE) DriveLetter = pDLProp[1].wcLetter; else if (pDLProp[0].bUsed == FALSE) DriveLetter = pDLProp[0].wcLetter; else { hr = S_FALSE; goto _cleanup; } } else DriveLetter = (pDLProp+i)->wcLetter; _cleanup: delete [] pDLProp; return hr; } HRESULT CNasVolume::m_AssignDriveLetter( IN IVdsVolume *pVolume, IN WCHAR DriveLetter ) /*++ Description: Assigns the specified drive letter to a volume. Arguments: pVolume - Pointer to the volume to assign drive letter to DriveLetter - The drive letter to assign Return values: HRESULTS --*/ { HRESULT hr = S_OK; CComPtr spVolumeMF; hr = pVolume->QueryInterface(IID_IVdsVolumeMF, (void**)&spVolumeMF); if (FAILED(hr)) return hr; WCHAR input[4] = L"X:\\"; input[0] = DriveLetter; hr = spVolumeMF->AddAccessPath(input); return hr; } HRESULT CNasVolume::m_Format( IN IVdsVolume *pVolume, IN VDS_FILE_SYSTEM_TYPE type, IN LPWSTR pwszLabel, IN DWORD dwUnitAllocationSize, IN BOOL bForce, IN BOOL bQuickFormat, IN BOOL bEnableCompression ) /*++ Description: Formats the volume with the specified parameters. Arguments: pVolume - Pointer to the volume you want to format. type - File system type pwszLabel - Volume label dwUnitAllocationSize- Allocation size bForce - Force the format bQuickFormat - Quick format bEnableCompression - Enable compression Return values: HRESULTS --*/ { HRESULT hr = S_OK; CComPtr spVolumeMF; CComPtr spAsync; hr = pVolume->QueryInterface(IID_IVdsVolumeMF, (void**)&spVolumeMF); if (FAILED(hr)) return hr; hr = spVolumeMF->Format( type, pwszLabel, dwUnitAllocationSize, bForce, bQuickFormat, bEnableCompression, &spAsync); return hr; } //**************************************************************************** // // CVolumeOn // //**************************************************************************** CVolumeOn::CVolumeOn( IN LPCWSTR pwszName, IN CWbemServices* pNamespace ) : CProvBase(pwszName, pNamespace) { } HRESULT CVolumeOn::Initialize() { HRESULT hr = S_OK; hr = g_LoadService(&m_spService); return hr; } CProvBase * CVolumeOn::S_CreateThis( IN LPCWSTR pwszName, IN CWbemServices* pNamespace ) { HRESULT hr = WBEM_E_FAILED; CVolumeOn *pVolumeOn = NULL; pVolumeOn = new CVolumeOn(pwszName, pNamespace); if (pVolumeOn) { hr = pVolumeOn->Initialize(); if (FAILED(hr)) { delete pVolumeOn; pVolumeOn = NULL; } } return pVolumeOn; } HRESULT CVolumeOn::EnumInstance( IN long lFlagsIn, IN IWbemContext* pCtx, IN IWbemObjectSink* pHandler ) { // Get all disks // Get extents on each disk // pass extent to LoadInstance HRESULT hr = S_OK; // // Get all allocated disks // CComPtr spEnumProv; hr = m_spService->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &spEnumProv); if (FAILED(hr)) return hr; // For each provider, do the following do { CComPtr spUnk; CComPtr spSwProvider; CComPtr spEnumPack; ULONG ulFetched = 0; hr = spEnumProv->Next(1, &spUnk, &ulFetched); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsSwProvider hr = spUnk->QueryInterface(IID_IVdsSwProvider, (void **)&spSwProvider); if (FAILED(hr)) continue; // Get Packs hr = spSwProvider->QueryPacks(&spEnumPack); if (FAILED(hr)) continue; // For each pack, do the following do { CComPtr spPackUnk; CComPtr spPack; CComPtr spEnumDisk; ULONG ulFetchedPack = 0; hr = spEnumPack->Next(1, &spPackUnk, &ulFetchedPack); if (hr == S_FALSE) break; else if (FAILED(hr)) return hr; // Get IVdsPack hr = spPackUnk->QueryInterface(IID_IVdsPack, (void **)&spPack); if (FAILED(hr)) continue; // Get IVdsDisk hr = spPack->QueryDisks(&spEnumDisk); if (FAILED(hr)) continue; // For each disk, do the following do { CComPtr spDiskUnk; CComPtr spDisk; VDS_DISK_PROP DiskProp; ULONG ulFetchedDisk = 0; hr = spEnumDisk->Next(1, &spDiskUnk, &ulFetchedDisk); if (hr == S_FALSE) // end of enumeration break; else if (FAILED(hr)) return hr; // Get IVdsDisk hr = spDiskUnk->QueryInterface(IID_IVdsDisk, (void **)&spDisk); if (FAILED(hr)) continue; hr = spDisk->GetProperties(&DiskProp); SAFE_FREE_DISK_PROP(&DiskProp); // Don't need the disk property strings if (FAILED(hr)) continue; // Return only fixed disks (no removeable, CDROM etc...) if (DiskProp.dwMediaType == FixedMedia) { VDS_DISK_EXTENT *pExtents = NULL; LONG lNumExtents = 0; LONG index = 0; hr = spDisk->QueryExtents(&pExtents, &lNumExtents); if (FAILED(hr)) continue; while (index < lNumExtents) { if (pExtents[index].type == VDS_DET_DATA) { // Spawn an instance of the class CComPtr spInstance; hr = m_pClass->SpawnInstance( 0, &spInstance ); if (SUCCEEDED(hr)) { hr = m_LoadInstance(&pExtents[index], spInstance.p); } if (SUCCEEDED(hr)) { hr = pHandler->Indicate(1, &spInstance.p); } if (FAILED(hr)) { SAFE_FREE(pExtents); return hr; } } index++; } SAFE_FREE(pExtents); } }while(TRUE); }while(TRUE); }while(TRUE); return hr; } HRESULT CVolumeOn::GetObject( IN CObjPath& rObjPath, IN long lFlags, IN IWbemContext* pCtx, IN IWbemObjectSink* pHandler ) { return WBEM_E_NOT_SUPPORTED; } HRESULT CVolumeOn::m_LoadInstance( IN VDS_DISK_EXTENT* pExtent, IN OUT IWbemClassObject* pObject ) { HRESULT hr = S_OK; CWbemClassObject wcoInstance(pObject); CObjPath pathVolume; CObjPath pathDisk; // // Set the Volume Ref property // pathVolume.Init(PVDR_CLASS_VOLUME); // Get the volume ID LPWSTR lpwszGuid; lpwszGuid = GuidToString(pExtent->volumeId); pathVolume.AddProperty(PVDR_PROP_ID, lpwszGuid); SAFE_FREE(lpwszGuid); // Get the volume name CComPtr spVolUnk; CComPtr spVolume; VDS_VOLUME_PROP VolumeProp; ZeroMemory(&VolumeProp, sizeof(VDS_VOLUME_PROP)); hr = m_spService->GetObject(pExtent->volumeId, VDS_OT_VOLUME, &spVolUnk); if (FAILED(hr)) return hr; hr = spVolUnk->QueryInterface(IID_IVdsVolume, (void**)&spVolume); if (FAILED(hr)) return hr; hr = spVolume->GetProperties(&VolumeProp); if (FAILED(hr)) return hr; if (VolumeProp.pwszName != NULL) pathVolume.AddProperty(PVDR_PROP_NAME, VolumeProp.pwszName); else pathVolume.AddProperty(PVDR_PROP_NAME, L""); // Set the VolumeOn volume ref wcoInstance.SetProperty((wchar_t*)pathVolume.GetObjectPathString(), PVDR_PROP_VOLUME); // // Set the Disk Ref property // pathDisk.Init(PVDR_CLASS_DISK); // Get the disk ID lpwszGuid = GuidToString(pExtent->diskId); pathDisk.AddProperty(PVDR_PROP_ID, lpwszGuid); SAFE_FREE(lpwszGuid); // Get the disk name CComPtr spDiskUnk; CComPtr spDisk; VDS_DISK_PROP DiskProp; hr = m_spService->GetObject(pExtent->diskId, VDS_OT_DISK, &spDiskUnk); if (FAILED(hr)) return hr; hr = spDiskUnk->QueryInterface(IID_IVdsDisk, (void**)&spDisk); if (FAILED(hr)) return hr; hr = spDisk->GetProperties(&DiskProp); if (FAILED(hr)) return hr; if (DiskProp.pwszName != NULL) pathDisk.AddProperty(PVDR_PROP_NAME, DiskProp.pwszName); else pathDisk.AddProperty(PVDR_PROP_NAME, L""); // Set the VolumeOn disk ref wcoInstance.SetProperty((wchar_t*)pathDisk.GetObjectPathString(), PVDR_PROP_DISK); return hr; }