BOOL CNdasService::Impl::ServiceStart(DWORD dwArgc, LPTSTR* lpArgs) { XTLVERIFY(ReportServiceStartPending(1000)); m_hStopServiceEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); if (m_hStopServiceEvent.IsInvalid()) { return FALSE; } XTLVERIFY(ReportServiceStartPending(1000)); m_hWorkItemSemaphore = ::CreateSemaphore(NULL, 0, MAX_WORK_ITEMS, NULL); if (m_hWorkItemSemaphore.IsInvalid()) { return FALSE; } XTLVERIFY(ReportServiceStartPending(1000)); m_hStartServiceEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); if (m_hStartServiceEvent.IsInvalid()) { return FALSE; } XTLVERIFY(ReportServiceStartPending(1000)); // Create an initialization thread if (!CreateThread()) { return FALSE; } XTLVERIFY(ReportServiceRunning()); return TRUE; }
HRESULT CNdasLogicalUnitManager::Unregister(INdasUnit* pNdasUnit) { XTLTRACE2(NDASSVC_NDASLOGDEVMANAGER, TRACE_LEVEL_INFORMATION, "Unregistering NdasUnit=%p from LDM\n", pNdasUnit); if (NULL == pNdasUnit) { return E_POINTER; } CComPtr<INdasLogicalUnit> pNdasLogicalUnit; COMVERIFY(pNdasUnit->get_NdasLogicalUnit(&pNdasLogicalUnit)); DWORD luseq; COMVERIFY(pNdasUnit->get_LogicalUnitSequence(&luseq)); COMVERIFY(pNdasLogicalUnit->RemoveNdasUnitInstance(pNdasUnit)); DWORD instanceCount; COMVERIFY(pNdasLogicalUnit->get_NdasUnitInstanceCount(&instanceCount)); if (0 == instanceCount) { NDAS_LOGICALDEVICE_ID logicalUnitId; COMVERIFY(pNdasLogicalUnit->get_Id(&logicalUnitId)); NDAS_LOGICALDEVICE_GROUP logicalUnitDefinition; COMVERIFY(pNdasLogicalUnit->get_LogicalUnitDefinition(&logicalUnitDefinition)); LockInstance(); XTLVERIFY(1 == m_LogicalUnitDefinitionMap.erase(logicalUnitDefinition)); XTLVERIFY(1 == m_LogicalUnitIdMap.erase(logicalUnitId)); size_t count = m_NdasLogicalUnits.GetCount(); for (size_t i = 0; i < count; ++i) { INdasLogicalUnit* p = m_NdasLogicalUnits.GetAt(i); if (p == pNdasLogicalUnit) { m_NdasLogicalUnits.RemoveAt(i); break; } } XTLVERIFY(pDeallocateID(logicalUnitId)); UnlockInstance(); } return S_OK; }
HANDLE __stdcall pOpenStorageDeviceByNumber( DWORD StorageDeviceNumber, ACCESS_MASK DeviceFileAccessMask ){ // Device Name Format: \\.\PhysicalDriveXX TCHAR deviceName[24]; XTLVERIFY(SUCCEEDED( ::StringCchPrintf( deviceName, RTL_NUMBER_OF(deviceName), _T("\\\\.\\PhysicalDrive%d"), StorageDeviceNumber))); XTL::AutoFileHandle hDisk = ::CreateFile( deviceName, DeviceFileAccessMask, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDisk.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "CreateFile(%ls) failed.\n", deviceName); return INVALID_HANDLE_VALUE; } return hDisk.Detach(); }
void CNdasEventMonitor::Detach(INdasLogicalUnit* pNdasLogicalUnit) { XTLTRACE2(NDASSVC_EVENTMON, TRACE_LEVEL_INFORMATION, "Detaching logical unit %p from the monitor\n", pNdasLogicalUnit); // LOGICAL DEVICE WRITE LOCK REGION { XTL::CWriterLockHolder holder(m_NdasLogicalUnitDataLock); // CInterfaceArray size_t count = m_NdasLogicalUnits.GetCount(); for (size_t index = 0; index < count; ++index) { INdasLogicalUnit* p = m_NdasLogicalUnits.GetAt(index); if (pNdasLogicalUnit == p) { m_NdasLogicalUnits.RemoveAt(index); break; } } } // LOGICAL DEVICE WRITE LOCK REGION XTLVERIFY( ::SetEvent(m_NdasLogicalUnitSetChanged) ); }
CNdasDeviceComm::~CNdasDeviceComm() { if (m_hNdas) { XTLVERIFY(SUCCEEDED(Disconnect())); } }
DWORD CNdasCommandServer::CommandProcessStart(HANDLE hStopEvent) { // 'Param' should be a copy of the 'Param' supplied by the caller // If the initiating thread has been abandoned already, Param will // be invalidated if it is an automatic object. // increment the work item count or // check if stop event is signaled already HANDLE waitHandles[] = {hStopEvent, m_hProcSemaphore}; const DWORD nWaitHandles = RTL_NUMBER_OF(waitHandles); DWORD waitResult = ::WaitForMultipleObjects(nWaitHandles, waitHandles, FALSE, INFINITE); if (WAIT_OBJECT_0 == waitResult) { return 0xFFFFFFFF; // stopped without actually starting a work } else if (WAIT_OBJECT_0 + 1 == waitResult) { // incremented work item count // execute the actual work item DWORD ret = ServiceCommand(hStopEvent); // decrement the work item count XTLVERIFY( ::ReleaseSemaphore(m_hProcSemaphore, 1, NULL) ); return ret; } else { XTLASSERT(FALSE); return 0x0000FFFF; } }
BOOL GetLocalLpxAddressList( IN DWORD cbBuffer, OUT LPSOCKET_ADDRESS_LIST lpBuffer, OUT LPDWORD pcbBytesReturned) { SOCKET sock = ::WSASocket( AF_LPX, SOCK_STREAM, LPXPROTO_TCP, NULL, 0, 0); if (INVALID_SOCKET == sock) { XTLTRACE2(NDASSVC_LPXCOMM, TRACE_LEVEL_ERROR, "WSASocket() failed, error=0x%X\n", GetLastError()); return FALSE; } BOOL fSuccess = GetLocalLpxAddressList( sock, cbBuffer, lpBuffer, pcbBytesReturned); // // Close socket may shadow last error // XTL_SAVE_LAST_ERROR(); XTLVERIFY(SOCKET_ERROR != closesocket(sock)); return fSuccess; }
void CNdasService::DestroyInstance(CNdasService* pService) { delete pService; CNdasService::instance = NULL; XTLVERIFY(SOCKET_ERROR != WSACleanup()); }
CNdasService* CNdasService::CreateInstance() { WSADATA wsaData; XTLVERIFY(SOCKET_ERROR != WSAStartup(MAKEWORD(2,2),&wsaData) ); XTLASSERT(NULL == CNdasService::instance); CNdasService::instance = new CNdasService(); return CNdasService::instance; }
DWORD CNdasService::Impl::OnServiceShutdown() { XTLTRACE("System is shutting down...\n"); m_cLogDeviceManager.OnShutdown(); m_cDeviceEventHandler.OnShutdown(); XTLVERIFY( ::LfsFiltCtlShutdown() ); return NO_ERROR; }
CNdasUnitDiskDevice::~CNdasUnitDiskDevice() { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_VERBOSE, "%s\n", ToStringA()); if (NULL != m_pAddTargetInfo) { XTLVERIFY( HeapFree(::GetProcessHeap(), 0, m_pAddTargetInfo) ); m_pAddTargetInfo = NULL; } if (NULL != m_pDIBv2) { XTLVERIFY( HeapFree(::GetProcessHeap(), 0, m_pDIBv2) ); m_pDIBv2 = NULL; } if (NULL != m_pBACL) { XTLVERIFY( HeapFree(::GetProcessHeap(), 0, m_pBACL) ); m_pBACL = NULL; } }
bool CNdasEventPreSubscriber::EndWaitForConnection() { DWORD cbTransferred; BOOL fSuccess = ::GetOverlappedResult(m_hPipe, &m_overlapped,&cbTransferred,FALSE); if (!fSuccess) { XTLASSERT(ERROR_IO_INCOMPLETE == ::GetLastError()); XTLVERIFY( ::CancelIo(m_hPipe) ); } return fSuccess ? true : false; }
DWORD CNdasService::Impl::OnServiceShutdown() { XTLTRACE2(NDASSVC_INIT, TRACE_LEVEL_INFORMATION, "System is shutting down...\n"); m_cLogDeviceManager.OnShutdown(); m_cDeviceEventHandler.OnShutdown(); XTLVERIFY( ::LfsFiltCtlShutdown() ); return NO_ERROR; }
HRESULT pGetNdasSlotNumberFromDiskNumber( __in DWORD DiskNumber, __out LPDWORD NdasSlotNumber) { WCHAR diskName[MAX_PATH]; XTLVERIFY(SUCCEEDED( StringCchPrintfW( diskName, MAX_PATH, L"\\\\.\\PhysicalDrive%d", DiskNumber))); XTLTRACE2(NdasVolTrace, 2, "Disk Name: %ls\n", diskName); return pGetNdasSlotNumberFromDeviceNameW(diskName, NdasSlotNumber); }
void CNdasEventMonitor::Attach(INdasLogicalUnit* pNdasLogicalUnit) { XTLTRACE2(NDASSVC_EVENTMON, TRACE_LEVEL_INFORMATION, "Attaching logical unit %p to the monitor\n", pNdasLogicalUnit); // LOGICAL DEVICE WRITE LOCK REGION { XTL::CWriterLockHolder holder(m_NdasLogicalUnitDataLock); m_NdasLogicalUnits.Add(pNdasLogicalUnit); } // LOGICAL DEVICE WRITE LOCK REGION XTLVERIFY( ::SetEvent(m_NdasLogicalUnitSetChanged) ); }
HRESULT pGetNdasSlotNumberForScsiPortNumber( __in DWORD ScsiPortNumber, __out LPDWORD NdasSlotNumber) { // // Make up SCSI Port Name // WCHAR scsiPortName[MAX_PATH]; XTLVERIFY(SUCCEEDED( StringCchPrintfW( scsiPortName, MAX_PATH, L"\\\\.\\Scsi%d:", ScsiPortNumber))); XTLTRACE2(NdasVolTrace, 2, "SCSI Port Name: %ls\n", scsiPortName); return pGetNdasSlotNumberFromDeviceNameW(scsiPortName, NdasSlotNumber); }
HRESULT CNdasServiceDeviceEventHandler::RegisterDeviceInterfaceNotification( __in LPCGUID InterfaceGuid, __in LPCSTR TypeName) { HDEVNOTIFY devNotifyHandle; HRESULT hr = pRegisterDeviceInterfaceNotification( m_hRecipient, m_dwReceptionFlags, InterfaceGuid, &devNotifyHandle); if (FAILED(hr)) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "RegisterDeviceInterfaceNotification failed, type=%hs, hr=0x%X\n", TypeName, hr); return hr; } try { m_DeviceInterfaceNotifyHandles.push_back(devNotifyHandle); } catch (...) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "RegisterDeviceInterfaceNotification failed, " "C++ exception, type=%hs\n", TypeName); XTLVERIFY(UnregisterDeviceNotification(devNotifyHandle)); return E_FAIL; } return S_OK; }
HRESULT CNdasServiceDeviceEventHandler::UnregisterDeviceHandleNotification( __in HDEVNOTIFY DevNotifyHandle) { EnterCriticalSection(&m_DevNotifyMapSection); DevNotifyMap::iterator itr = m_DevNotifyMap.find(DevNotifyHandle); if (m_DevNotifyMap.end() == itr) { LeaveCriticalSection(&m_DevNotifyMapSection); return E_FAIL; } DEVICE_HANDLE_NOTIFY_DATA& ndata = itr->second; m_DevNotifyMap.erase(itr); LeaveCriticalSection(&m_DevNotifyMapSection); XTLVERIFY( UnregisterDeviceNotification(DevNotifyHandle) ); return S_OK; }
HRESULT CNdasServiceDeviceEventHandler::pEnumerateNdasStoragePorts() { HRESULT hr = S_OK; HDEVINFO hDevInfoSet = SetupDiGetClassDevs( &GUID_DEVINTERFACE_STORAGEPORT, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); if (static_cast<HDEVINFO>(INVALID_HANDLE_VALUE) == hDevInfoSet) { hr = HRESULT_FROM_WIN32(GetLastError()); XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "SetupDiCreateDeviceInfoList failed, hr=%x\n", hr); return hr; } for (DWORD index = 0; ; ++index) { SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { sizeof(SP_DEVICE_INTERFACE_DATA) }; BOOL success = SetupDiEnumDeviceInterfaces( hDevInfoSet, NULL, &GUID_DEVINTERFACE_STORAGEPORT, index, &deviceInterfaceData); if (!success) { if (ERROR_NO_MORE_ITEMS != GetLastError()) { hr = HRESULT_FROM_WIN32(GetLastError()); XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "SetupDiEnumDeviceInterfaces failed, hr=%X\n", hr); } break; } DWORD requiredSize = 0; success = SetupDiGetDeviceInterfaceDetail( hDevInfoSet, &deviceInterfaceData, NULL, 0, &requiredSize, NULL); if (success || ERROR_INSUFFICIENT_BUFFER != GetLastError()) { if (success) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "SetupDiGetDeviceInterfaceDetail failed, no interface details\n"); } else { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "SetupDiGetDeviceInterfaceDetail failed, error=0x%X\n", GetLastError()); } continue; } PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = static_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(malloc(requiredSize)); if (NULL == deviceInterfaceDetailData) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "malloc failed, bytes=%d\n", requiredSize); continue; } ZeroMemory(deviceInterfaceDetailData, requiredSize); deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); success = SetupDiGetDeviceInterfaceDetail( hDevInfoSet, &deviceInterfaceData, deviceInterfaceDetailData, requiredSize, NULL, NULL); if (!success) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "SetupDiGetDeviceInterfaceDetail failed, error=0x%X\n", GetLastError()); free(deviceInterfaceDetailData); continue; } XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_INFORMATION, "StoragePort found, device=%ls\n", deviceInterfaceDetailData->DevicePath); HANDLE hDevice = CreateFile( deviceInterfaceDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_DEVICE, NULL); if (INVALID_HANDLE_VALUE == hDevice) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_ERROR, "CreateFile failed, device=%ls, error=0x%X\n", deviceInterfaceDetailData->DevicePath, GetLastError()); free(deviceInterfaceDetailData); continue; } DWORD slotNo = 0; HRESULT hr2 = pGetNdasSlotNumberFromDeviceHandle(hDevice, &slotNo); if (S_OK != hr2) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_WARNING, "StoragePort is not an NDAS Storage Port, hr=%x\n", hr2); XTLVERIFY( CloseHandle(hDevice) ); free(deviceInterfaceDetailData); continue; } XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_INFORMATION, "NdasSlotNumber=%d\n", slotNo); // // As the ownership of the handler goes to the vector, // do not close the device handle here // AddDeviceNotificationHandle( hDevice, CDeviceHandleNotifyData( DNT_STORAGE_PORT, slotNo, deviceInterfaceDetailData->DevicePath)); XTLVERIFY( CloseHandle(hDevice) ); free(deviceInterfaceDetailData); } XTLVERIFY( SetupDiDestroyDeviceInfoList(hDevInfoSet) ); return hr; }
LRESULT CNdasServiceDeviceEventHandler:: OnDeviceHandleRemoveComplete( PDEV_BROADCAST_HANDLE pdbch) { // // Device Handle Remove Complete is called on Surprise Removal // XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_INFORMATION, "OnDeviceHandleRemoveComplete: Device Handle %p, Notify Handle %p\n", pdbch->dbch_handle, pdbch->dbch_hdevnotify); DEVICE_HANDLE_NOTIFY_DATA notifyData; if (!FindDeviceHandle(pdbch->dbch_hdevnotify, ¬ifyData)) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_INFORMATION, "Notify Handle is not registered.\n"); return TRUE; } // Erase from the map XTLVERIFY(1 == m_DevNotifyMap.erase(pdbch->dbch_hdevnotify)); BOOL fSuccess = ::UnregisterDeviceNotification(pdbch->dbch_hdevnotify); if (!fSuccess) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_WARNING, "Unregistering a device notification to %p failed.\n", pdbch->dbch_hdevnotify); } switch (notifyData.Type) { case DNT_STORAGE_PORT: { // // Remove removal is completed for NDAS SCSI Controller // if(m_service.NdasPortExists()) { break; } CNdasLogicalDevicePtr pLogDevice = pGetNdasLogicalDeviceByNdasLocation(notifyData.NdasLocation); if (CNdasLogicalDeviceNullPtr == pLogDevice) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_WARNING, "Logical Device not found, ndasLocation=%d.\n", notifyData.NdasLocation); } else { pLogDevice->OnUnmounted(); } } break; case DNT_DISK: case DNT_CDROM: { // // Remove removal is completed for NDAS port // if(m_service.NdasPortExists() == FALSE) { break; } CNdasLogicalDevicePtr pLogDevice = pGetNdasLogicalDeviceByNdasLocation(notifyData.NdasLocation); if (CNdasLogicalDeviceNullPtr == pLogDevice) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_WARNING, "Logical Device not found, ndasLocation=%d.\n", notifyData.NdasLocation); } else { pLogDevice->OnUnmounted(); } if(notifyData.Type == DNT_DISK) { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_INFORMATION, "Disk Remove Complete, ndasLocation=%d\n", notifyData.NdasLocation); } else { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_INFORMATION, "CDROM Remove Complete, ndasLocation=%d\n", notifyData.NdasLocation); } } break; case DNT_VOLUME: { XTLTRACE2(NDASSVC_PNP, TRACE_LEVEL_INFORMATION, "Volume Remove Complete, ndasLocation=%d\n", notifyData.NdasLocation); } break; default: // We do not care about other types at this time. break; } return TRUE; }
static void Release(HDEVINFO h) { XTL_SAVE_LAST_ERROR(); XTLVERIFY(::SetupDiDestroyDeviceInfoList(h)); }
STDMETHODIMP CNdasDeviceRegistrar::Register( __in_opt DWORD SlotNo, __in const NDAS_DEVICE_ID& DeviceId, __in DWORD RegFlags, __in_opt const NDASID_EXT_DATA* NdasIdExtension, __in BSTR Name, __in ACCESS_MASK GrantedAccess, __in_opt const NDAS_OEM_CODE* NdasOemCode, __deref_out INdasDevice** ppNdasDevice) { HRESULT hr; *ppNdasDevice = 0; NDAS_DEVICE_ID ndasDeviceId = DeviceId; // // this will lock this class from here // and releases the lock when the function returns; // CAutoLock<CLock> autolock(&m_DataLock); XTLTRACE2(NDASSVC_NDASDEVICEREGISTRAR, TRACE_LEVEL_INFORMATION, "Registering device %s at slot %d\n", CNdasDeviceId(DeviceId).ToStringA(), SlotNo); if (NULL == NdasIdExtension) { NdasIdExtension = &NDAS_ID_EXTENSION_DEFAULT; } // // Only DEFAULT and SEAGATE are currently implemented // if (NDAS_VID_SEAGATE != NdasIdExtension->VID && NDAS_VID_WINDWOS_RO != NdasIdExtension->VID && NDAS_VID_DEFAULT != NdasIdExtension->VID) { XTLTRACE2(NDASSVC_NDASDEVICEREGISTRAR, TRACE_LEVEL_ERROR, "Unknown Vendor ID=0x%02X\n", NdasIdExtension->VID); hr = NDASSVC_ERROR_UNKNOWN_VENDOR_ID; return hr; } ndasDeviceId.VID = NdasIdExtension->VID; // If SlotNo is zero, automatically assign it. // check slot number if (0 == SlotNo) { SlotNo = pLookupEmptySlot(); if (0 == SlotNo) { return NDASSVC_ERROR_DEVICE_ENTRY_SLOT_FULL; } } else if (SlotNo > MAX_SLOT_NUMBER) { return NDASSVC_ERROR_INVALID_SLOT_NUMBER; } // check and see if the slot is occupied if (m_slotbit[SlotNo]) { return NDASSVC_ERROR_SLOT_ALREADY_OCCUPIED; } // find an duplicate address { CComPtr<INdasDevice> pExistingDevice; if (SUCCEEDED(get_NdasDevice(&ndasDeviceId, &pExistingDevice))) { return NDASSVC_ERROR_DUPLICATE_DEVICE_ENTRY; } } // register CComObject<CNdasDevice>* pNdasDeviceInstance; hr = CComObject<CNdasDevice>::CreateInstance(&pNdasDeviceInstance); if (FAILED(hr)) { return hr; } hr = pNdasDeviceInstance->Initialize( SlotNo, ndasDeviceId, RegFlags, NdasIdExtension); if (FAILED(hr)) { XTLTRACE2(NDASSVC_NDASDEVICEREGISTRAR, TRACE_LEVEL_ERROR, "Device initialization failed, error=0x%X\n", GetLastError()); return hr; } CComPtr<INdasDevice> pNdasDevice(pNdasDeviceInstance); COMVERIFY(pNdasDevice->put_Name(Name)); COMVERIFY(pNdasDevice->put_GrantedAccess(GrantedAccess)); if (NdasOemCode) { COMVERIFY(pNdasDevice->put_OemCode(NdasOemCode)); } m_slotbit[SlotNo] = true; bool insertResult; m_NdasDevices.Add(pNdasDevice); XTLVERIFY( m_deviceSlotMap.insert(std::make_pair(SlotNo, pNdasDevice)).second ); //DeviceSlotMap::value_type(SlotNo, pNdasDevice)).second; XTLVERIFY( m_deviceIdMap.insert(std::make_pair(ndasDeviceId, pNdasDevice)).second ); //DeviceIdMap::value_type(ndasDeviceId, pNdasDevice)).second; XTLASSERT(m_deviceSlotMap.size() == m_deviceIdMap.size()); // // When NdasIdExtension is NULL, NDAS_ID_EXTENSION_DEFAULT is assigned already // if (RegFlags & NDAS_DEVICE_REG_FLAG_VOLATILE) { } else { BOOL success; XTL::CStaticStringBuffer<30> containerName(_T("Devices\\%04d"), SlotNo); if (0 != memcmp(&NDAS_ID_EXTENSION_DEFAULT, NdasIdExtension, sizeof(NDASID_EXT_DATA))) { NDAS_DEVICE_ID_REG_DATA regData = {0}; regData.DeviceId = ndasDeviceId; regData.NdasIdExtension = *NdasIdExtension; success = _NdasSystemCfg.SetSecureValueEx( containerName, _T("DeviceID2"), ®Data, sizeof(regData)); } else { success = _NdasSystemCfg.SetSecureValueEx( containerName, _T("DeviceID"), &ndasDeviceId, sizeof(ndasDeviceId)); } if (!success) { XTLTRACE2(NDASSVC_NDASDEVICEREGISTRAR, TRACE_LEVEL_WARNING, "Writing registration entry to the registry failed at %ls, error=0x%X\n", containerName.ToString(), GetLastError()); } success = _NdasSystemCfg.SetSecureValueEx( containerName, _T("RegFlags"), &RegFlags, sizeof(RegFlags)); if (!success) { XTLTRACE2(NDASSVC_NDASDEVICEREGISTRAR, TRACE_LEVEL_WARNING, "Writing registration entry to the registry failed at %ls, error=0x%X\n", containerName.ToString(), GetLastError()); } } BOOL publishEvent = !m_fBootstrapping; autolock.Release(); // // During bootstrapping, we do not publish this event // Bootstrap process will publish an event later // if (publishEvent) { (void) pGetNdasEventPublisher().DeviceEntryChanged(); } *ppNdasDevice = pNdasDevice.Detach(); return S_OK; }
BOOL CreateNdasCommandServerDefaultDACL( __inout LPSECURITY_ATTRIBUTES SecurityAttributes) { #define NDASSVC_ALLOW_INTERACTIVE_USERS // Anonymous in SDDL in Windows 2000 does not work // #define NDASSVC_DENY_ANONYMOUS // Define the SDDL for the DACL. This example sets // the following access: // Built-in guests are denied all access. // Anonymous Logon is denied all access. // Authenticated Users are allowed read/write/execute access. // Administrators are allowed full control. // Modify these values as needed to generate the proper // DACL for your application. #if 0 LPCWSTR securityDescriptor = L"D:" // Discretionary ACL L"(D;OICI;GA;;;BG)" // Deny access to Built-in Guests L"(D;OICI;GA;;;AN)" // Deny access to Anonymous Logon L"(A;OICI;GRGWGX;;;AU)" // Allow read/write/execute to Authenticated Users L"(A;OICI;GA;;;BA)"; // Allow full control to Administrators #else LPCTSTR securityDescriptor = SDDL_DACL SDDL_DELIMINATOR // Deny access to Built-in Guests SDDL_ACE_BEGIN SDDL_ACCESS_DENIED SDDL_SEPERATOR SDDL_OBJECT_INHERIT SDDL_CONTAINER_INHERIT SDDL_SEPERATOR SDDL_GENERIC_ALL SDDL_SEPERATOR SDDL_SEPERATOR SDDL_SEPERATOR SDDL_BUILTIN_GUESTS SDDL_ACE_END // // Anonymous DOES NOT work in Windows 2000 // #ifdef NDASSVC_DENY_ANONYMOUS // Deny access to Anonymous SDDL_ACE_BEGIN SDDL_ACCESS_DENIED SDDL_SEPERATOR SDDL_OBJECT_INHERIT SDDL_CONTAINER_INHERIT SDDL_SEPERATOR SDDL_GENERIC_ALL SDDL_SEPERATOR SDDL_SEPERATOR SDDL_SEPERATOR SDDL_ANONYMOUS SDDL_ACE_END #endif #ifdef NDASSVC_ALLOW_INTERACTIVE_USERS // Allow RWX to Interactive Users SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED SDDL_SEPERATOR SDDL_OBJECT_INHERIT SDDL_CONTAINER_INHERIT SDDL_SEPERATOR SDDL_GENERIC_READ SDDL_GENERIC_WRITE SDDL_GENERIC_EXECUTE SDDL_SEPERATOR SDDL_SEPERATOR SDDL_SEPERATOR SDDL_INTERACTIVE SDDL_ACE_END #endif // Allow Full Control to Administrators SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED SDDL_SEPERATOR SDDL_OBJECT_INHERIT SDDL_CONTAINER_INHERIT SDDL_SEPERATOR SDDL_GENERIC_ALL SDDL_SEPERATOR SDDL_SEPERATOR SDDL_SEPERATOR SDDL_BUILTIN_ADMINISTRATORS SDDL_ACE_END // Allow Full Control to Local System SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED SDDL_SEPERATOR SDDL_OBJECT_INHERIT SDDL_CONTAINER_INHERIT SDDL_SEPERATOR SDDL_GENERIC_ALL SDDL_SEPERATOR SDDL_SEPERATOR SDDL_SEPERATOR SDDL_LOCAL_SYSTEM SDDL_ACE_END; #endif if (NULL == SecurityAttributes) { XTLASSERT(FALSE); return FALSE; } BOOL success; XTLVERIFY( success = ConvertStringSecurityDescriptorToSecurityDescriptor( securityDescriptor, SDDL_REVISION_1, &(SecurityAttributes->lpSecurityDescriptor), NULL) ); return success; }
DWORD CNdasCommandServer::ServiceCommand(HANDLE hStopEvent) { // Named Pipe Connection Event XTL::AutoObjectHandle hConnectEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); if (hConnectEvent.IsInvalid()) { // Log Error Here XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_ERROR, "Creating ndascmd connection event failed, error=0x%X\n", GetLastError()); return 1; } // Default Security Settings SECURITY_ATTRIBUTES securityAttributes; BOOL success = CreateNdasCommandServerDefaultDACL(&securityAttributes); if (!success) { XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_ERROR, "CreateNdasCommandServerDefaultDACL failed, error=0x%X\n", GetLastError()); return 2; } // Create a named pipe instance XTL::AutoFileHandle hPipe = ::CreateNamedPipe( PipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, MaxPipeInstances, 0, 0, PipeTimeout, &securityAttributes); if (hPipe.IsInvalid()) { // Log Error Here XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_ERROR, "Creating pipe server failed, error=0x%X\n", GetLastError()); return 2; } HANDLE waitHandles[] = { hStopEvent, hConnectEvent }; const DWORD nWaitHandles = RTL_NUMBER_OF(waitHandles); while (TRUE) { // Initialize overlapped structure OVERLAPPED overlapped = {0}; overlapped.hEvent = hConnectEvent; XTLVERIFY( ::ResetEvent(hConnectEvent) ); CNamedPipeTransport namePipeTransport(hPipe); CNamedPipeTransport* pTransport = &namePipeTransport; BOOL fSuccess = pTransport->Accept(&overlapped); if (!fSuccess) { XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_ERROR, "Transport Accept (Named Pipe) failed, error=0x%X\n", GetLastError()); break; } DWORD waitResult = ::WaitForMultipleObjects( nWaitHandles, waitHandles, FALSE, INFINITE); switch (waitResult) { case WAIT_OBJECT_0: // Terminate Thread Event return 0; case WAIT_OBJECT_0 + 1: // Connect Event // // Process the request // (Safely ignore error) // { CNdasCommandProcessor processor(m_service, pTransport); if (!processor.Process()) { XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_ERROR, "CommandProcess failed, error=0x%X\n", GetLastError()); } // After processing the request, reset the pipe instance XTLVERIFY( ::FlushFileBuffers(hPipe) ); XTLVERIFY( ::DisconnectNamedPipe(hPipe) ); } break; default: XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_ERROR, "WaitForMultipleObjects failed, waitResult=0x%X error=0x%X\n", waitResult, GetLastError()); XTLASSERT(FALSE); } } return 0; }
DWORD CNdasEventMonitor::ThreadStart(HANDLE hStopEvent) { CCoInitialize coinit(COINIT_MULTITHREADED); // 15 sec = 10,000,000 nanosec // negative value means relative time LARGE_INTEGER liDueTime; liDueTime.QuadPart = - 10 * 1000 * 1000; BOOL success = ::SetWaitableTimer( m_HeartbeatMonitorTimer, &liDueTime, HEARTBEAT_MONITOR_INTERVAL, NULL, NULL, FALSE); if (!success) { XTLTRACE2(NDASSVC_EVENTMON, TRACE_LEVEL_ERROR, "Setting waitable timer failed, error=0x%X\n", GetLastError()); } XTLVERIFY( ::ResetEvent(m_NdasLogicalUnitSetChanged) ); std::vector<HANDLE> waitHandles; waitHandles.reserve(20); // Lock-free copy of the devices and logDevices CInterfaceArray<INdasDevice> ndasDevices; CInterfaceArray<INdasLogicalUnit> ndasLogicalUnits; while (true) { // // Copy m_NdasDevices and m_NdasLogicalUnits to devices and logDevices // for lock free accesses // // DEVICE READER LOCK REGION { XTL::CReaderLockHolder holder(m_NdasDeviceDataLock); ndasDevices.Copy(m_NdasDevices); } { XTL::CReaderLockHolder holder(m_NdasLogicalUnitDataLock); ndasLogicalUnits.Copy(m_NdasLogicalUnits); } // DEVICE READER LOCK REGION // // Recreate wait handles // size_t ndasLogicalUnitCount = ndasLogicalUnits.GetCount(); waitHandles.resize(3 + ndasLogicalUnitCount * 2); waitHandles[0] = hStopEvent; waitHandles[1] = m_NdasLogicalUnitSetChanged; waitHandles[2] = m_HeartbeatMonitorTimer; // Disconnect events i=[3 ...3+nLogDevices) // Alarm Events events i=[3+nLogDevices ... 3+2*nLogDevices) for (size_t index = 0; index < ndasLogicalUnitCount; ++index) { INdasLogicalUnit* pNdasLogicalUnit = ndasLogicalUnits.GetAt(index); waitHandles[3 + index] = NdasLogicalUnitDisconnectEvent()(pNdasLogicalUnit); waitHandles[3 + index + ndasLogicalUnitCount] = NdasLogicalUnitAlarmEvent()(pNdasLogicalUnit); } DWORD nWaitHandles = waitHandles.size(); DWORD waitResult = ::WaitForMultipleObjects( nWaitHandles, &waitHandles[0], FALSE, INFINITE); if (WAIT_OBJECT_0 == waitResult) { // Terminate Thread Event XTLVERIFY( ::CancelWaitableTimer(m_HeartbeatMonitorTimer) ); return 0; } else if (WAIT_OBJECT_0 + 1 == waitResult) { // Logical device set change event XTLVERIFY( ::ResetEvent(m_NdasLogicalUnitSetChanged) ); continue; } else if (WAIT_OBJECT_0 + 2 == waitResult) { // Heartbeat Monitor Timer Event AtlForEach(ndasDevices, InvokeTimerEventSink<INdasDevice>()); AtlForEach(ndasLogicalUnits, InvokeTimerEventSink<INdasLogicalUnit>()); } else if ( waitResult >= WAIT_OBJECT_0 + 3 && waitResult < WAIT_OBJECT_0 + 3 + ndasLogicalUnitCount) { XTLVERIFY( ::ResetEvent(waitHandles[waitResult - WAIT_OBJECT_0]) ); // Disconnect Event DWORD n = waitResult - (WAIT_OBJECT_0 + 3); OnLogicalDeviceDisconnected(ndasLogicalUnits[n]); } else if ( waitResult >= WAIT_OBJECT_0 + 3 + ndasLogicalUnitCount && waitResult < WAIT_OBJECT_0 + 3 + 2 * ndasLogicalUnitCount) { XTLVERIFY( ::ResetEvent(waitHandles[waitResult - WAIT_OBJECT_0]) ); // Alarm Event DWORD n = waitResult - (WAIT_OBJECT_0 + 3 + ndasLogicalUnitCount); OnLogicalDeviceAlarmed(ndasLogicalUnits[n]); } else { XTLASSERT(FALSE); } } }
DWORD CNdasCommandServer:: ThreadStart(LPVOID lpParam) { HANDLE hStopEvent = static_cast<HANDLE>(lpParam); CmdWorkItemVector workItems; workItems.reserve(MaxPipeInstances); size_t size = workItems.size(); XTLASSERT(0 == size); std::generate_n( std::back_inserter(workItems), MaxPipeInstances, pWorkItemPtrGenerator); size = workItems.size(); XTLASSERT(MaxPipeInstances == size); DWORD nWorkItems = 0; for (DWORD i = 0; i < MaxPipeInstances; ++i) { CmdWorkItemPtr p = workItems[i]; BOOL fSuccess = p->QueueUserWorkItemEx( this, &CNdasCommandServer::CommandProcessStart, hStopEvent, WT_EXECUTELONGFUNCTION); if (fSuccess) { ++nWorkItems; } else { XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_ERROR, "Starting work item (%d/%d) failed, error=0x%X\n", i + 1, MaxPipeInstances, GetLastError()); } } // Release semaphore to start workers (semaphore increment) LONG prev; XTLVERIFY( ::ReleaseSemaphore(m_hProcSemaphore, nWorkItems, &prev) ); XTLASSERT( 0 == prev ); // Wait for stop event XTLVERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(hStopEvent, INFINITE)); // Stopped and waits for user work items DWORD finished = 0; while (finished < nWorkItems) { ::Sleep(0); DWORD waitResult = ::WaitForSingleObject(m_hProcSemaphore, 0); if (waitResult == WAIT_OBJECT_0) { XTLTRACE2(NDASSVC_CMDSERVER, TRACE_LEVEL_INFORMATION, "Command Process work item finished (%d/%d).\n", finished + 1, nWorkItems); ++finished; } XTLVERIFY(WAIT_OBJECT_0 == waitResult || WAIT_TIMEOUT == waitResult); } // Now Finally this thread can stop return 0; }
DWORD CNdasService::Impl::ThreadStart(LPVOID) { // // Stop NdasBus auto-plugin feature to take over plugin facility. // BOOL fSuccess = LsBusCtlStartStopRegistrarEnum(FALSE, NULL); XTLASSERT(fSuccess); ////////////////////////////////////////////////////////////////////////// // Get the initialized instance ////////////////////////////////////////////////////////////////////////// if (!InitializeInstances()) { OnServiceStop(); return 1; } ////////////////////////////////////////////////////////////////////////// // Bootstrap registrar from the registry ////////////////////////////////////////////////////////////////////////// XTLVERIFY( m_cDeviceRegistrar.Bootstrap() ); ////////////////////////////////////////////////////////////////////////// // Start Queuing Work Items ////////////////////////////////////////////////////////////////////////// m_nWorkItems = 0; if (!StartWorkItems(m_nWorkItems)) { OnServiceStop(); return 1; } // Release semaphore to start workers (semaphore increment) LONG prev; XTLVERIFY( ::ReleaseSemaphore(m_hWorkItemSemaphore, m_nWorkItems, &prev) ); XTLASSERT( 0 == prev ); ////////////////////////////////////////////////////////////////////////// // Start Command Processor Thread ////////////////////////////////////////////////////////////////////////// if (!m_wiCmdServer.CreateThreadEx( &m_cCmdServer, &CNdasCommandServer::ThreadStart, LPVOID(m_hStopServiceEvent))) { OnServiceStop(); return 1; } ////////////////////////////////////////////////////////////////////////// // Initialization thread is done ////////////////////////////////////////////////////////////////////////// return 0; }
DWORD CNdasService::Impl::OnServiceStop() { ////////////////////////////////////////////////////////////////////////// // We should report the SCM that the service is stopping // Otherwise, the service will terminate the thread. // And we'll get ACCESS VIOLATION ERROR! ////////////////////////////////////////////////////////////////////////// XTLTRACE("Service is stopping...\n"); ReportServiceStopPending(1000); XTLVERIFY( ::SetEvent(m_hStopServiceEvent) ); // Yield to other threads to finish themselves. ::Sleep(0); ////////////////////////////////////////////////////////////////////////// // Wait for the command processor thread to stop ////////////////////////////////////////////////////////////////////////// XTLTRACE("Waiting for worker threads to stop....\n"); DWORD waitResult = WAIT_TIMEOUT; while (WAIT_OBJECT_0 != waitResult) { ReportServiceStopPending(3000); // yield to work threads for them to handle their terminations ::Sleep(0); waitResult = ::WaitForSingleObject(m_wiCmdServer.GetThreadHandle(), 3000); XTLVERIFY(WAIT_OBJECT_0 == waitResult || WAIT_TIMEOUT == waitResult); XTLTRACE("Waiting for command processors to stop in 3 seconds....\n"); } XTLTRACE("Command processors stopped....\n"); ////////////////////////////////////////////////////////////////////////// // Wait for the work items to stop ////////////////////////////////////////////////////////////////////////// DWORD finished = 0; while (finished < m_nWorkItems) { ReportServiceStopPending(1500); // yield to work threads for them to handle their terminations ::Sleep(0); DWORD waitResult = ::WaitForSingleObject(m_hWorkItemSemaphore, 1000); if (waitResult == WAIT_OBJECT_0) { ++finished; XTLTRACE("(%d/%d) WorkItems stopped...\n", finished, m_nWorkItems); } XTLVERIFY(WAIT_OBJECT_0 == waitResult || WAIT_TIMEOUT == waitResult); } ////////////////////////////////////////////////////////////////////////// // All threads and work items are done ////////////////////////////////////////////////////////////////////////// XTLTRACE("All work items stopped....\n"); XTLTRACE("Reporting to the SCM that the service is stopped....\n"); m_cDeviceEventHandler.Uninitialize(); m_cDeviceRegistrar.Cleanup(); m_cLogDeviceManager.Cleanup(); ReportServiceStopped(); return NO_ERROR; }
BOOL CNdasUnitDeviceCreator::ReadBACL(BLOCK_ACCESS_CONTROL_LIST** ppBACL, UINT32 BACLSize) { // // ppBACL will be set only if this function succeed. // UINT32 ElementCount = (BACLSize - (sizeof(BLOCK_ACCESS_CONTROL_LIST) - sizeof(BLOCK_ACCESS_CONTROL_LIST_ELEMENT))) / sizeof(BLOCK_ACCESS_CONTROL_LIST_ELEMENT); // allocate pBACL to fit sector align PBLOCK_ACCESS_CONTROL_LIST pBACL = reinterpret_cast<PBLOCK_ACCESS_CONTROL_LIST>( HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 512 * BACL_SECTOR_SIZE(ElementCount))); if (NULL == pBACL) { // Out of memory! return FALSE; } BOOL fSuccess = m_devComm.ReadDiskBlock( reinterpret_cast<PBYTE>(pBACL), NDAS_BLOCK_LOCATION_BACL, BACL_SECTOR_SIZE(ElementCount)); // // Regardless of the existence, // Disk Block should be read. // Failure means communication error or disk error // if (!fSuccess) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "ReadDiskBlock failed, block=%I64d, error=0x%X\n", NDAS_BLOCK_LOCATION_BACL, GetLastError()); XTLVERIFY( HeapFree(GetProcessHeap(), 0, pBACL) ); return FALSE; } // // check structure // if (BACL_SIGNATURE != pBACL->Signature || BACL_VERSION < pBACL->Version || crc32_calc((unsigned char *)&pBACL->Elements[0], sizeof(BLOCK_ACCESS_CONTROL_LIST_ELEMENT) * (pBACL->ElementCount)) != pBACL->crc) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "On-disk BACL information is invalid!\n"); XTLVERIFY( HeapFree(GetProcessHeap(), 0, pBACL) ); return FALSE; } *ppBACL = pBACL; return TRUE; }
BOOL CNdasUnitDeviceCreator::ReadDIB(NDAS_DIB_V2** ppDIBv2) { // // ppDIBv2 will be set only if this function succeed. // PNDAS_DIB_V2 pDIBv2 = reinterpret_cast<PNDAS_DIB_V2>( HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 512)); if (NULL == pDIBv2) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "ReadDiskBlock failed, error=0x%X\n", GetLastError()); return FALSE; } BOOL fSuccess = m_devComm.ReadDiskBlock( reinterpret_cast<PBYTE>(pDIBv2), NDAS_BLOCK_LOCATION_DIB_V2); // // Regardless of the existence, // Disk Block should be read. // Failure means communication error or disk error // if (!fSuccess) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "ReadDiskBlock failed, error=0x%X\n", GetLastError()); XTLVERIFY( HeapFree(GetProcessHeap(), 0, pDIBv2) ); return FALSE; } // // check signature // if(NDAS_DIB_V2_SIGNATURE != pDIBv2->Signature || pDIBv2->crc32 != crc32_calc((unsigned char *)pDIBv2, sizeof(pDIBv2->bytes_248)) || pDIBv2->crc32_unitdisks != crc32_calc((unsigned char *)pDIBv2->UnitDisks, sizeof(pDIBv2->UnitDisks))) { // // Read DIBv1 // fSuccess = ReadDIBv1AndConvert(pDIBv2); if (!fSuccess) { XTLVERIFY( HeapFree(GetProcessHeap(), 0, pDIBv2) ); return FALSE; } if ( ! IsConsistentDIB(pDIBv2) ) { // Inconsistent DIB will be reported as single InitializeDIBv2AsSingle(pDIBv2); } *ppDIBv2 = pDIBv2; return TRUE; } // // check version // if(IS_HIGHER_VERSION_V2(*pDIBv2)) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "Unsupported version V2.\n"); XTLVERIFY( HeapFree(GetProcessHeap(), 0, pDIBv2) ); return FALSE; } // // TODO: Lower version process (future code) ??? // if(0) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "lower version V2 detected\n"); } // // read additional locations if needed // if (pDIBv2->nDiskCount + pDIBv2->nSpareCount > NDAS_MAX_UNITS_IN_V2) { UINT32 nTrailSectorCount = GET_TRAIL_SECTOR_COUNT_V2(pDIBv2->nDiskCount + pDIBv2->nSpareCount); SIZE_T dwBytes = sizeof(NDAS_DIB_V2) + 512 * nTrailSectorCount; LPVOID ptr = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, pDIBv2, dwBytes); if (NULL == ptr) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "HeapReAlloc failed, bytes=%d\n", dwBytes); // When HeapReAlloc fails, pDIBv2 should be freed XTLVERIFY( HeapFree(GetProcessHeap(), 0, pDIBv2) ); return FALSE; } pDIBv2 = reinterpret_cast<PNDAS_DIB_V2>(ptr); for(DWORD i = 0; i < nTrailSectorCount; i++) { fSuccess = m_devComm.ReadDiskBlock( reinterpret_cast<PBYTE>(pDIBv2) + sizeof(NDAS_DIB_V2) + 512 * i, NDAS_BLOCK_LOCATION_ADD_BIND + i); if(!fSuccess) { XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "Reading additional block failed, block=%d, error=0x%X\n", NDAS_BLOCK_LOCATION_ADD_BIND + i, GetLastError()); XTLVERIFY( HeapFree(GetProcessHeap(), 0, pDIBv2) ); return FALSE; } } } // Virtual DVD check. Not supported ATM. // // DIB Consistency Check // if ( ! IsConsistentDIB(pDIBv2) ) { // Inconsistent DIB will be reported as single InitializeDIBv2AsSingle(pDIBv2); } *ppDIBv2 = pDIBv2; return TRUE; }