PSTORAGE_DESCRIPTOR_HEADER pStorageQueryProperty( HANDLE hDevice, STORAGE_PROPERTY_ID PropertyId, STORAGE_QUERY_TYPE QueryType) { STORAGE_PROPERTY_QUERY spquery; ::ZeroMemory(&spquery, sizeof(spquery)); spquery.PropertyId = PropertyId; spquery.QueryType = QueryType; DWORD bufferLength = sizeof(STORAGE_DESCRIPTOR_HEADER); XTL::AutoProcessHeapPtr<STORAGE_DESCRIPTOR_HEADER> buffer = static_cast<STORAGE_DESCRIPTOR_HEADER*>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength)); if (buffer.IsInvalid()) { return NULL; } DWORD returnedLength; BOOL fSuccess = ::DeviceIoControl( hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &spquery, sizeof(spquery), buffer, bufferLength, &returnedLength, NULL); if (!fSuccess) { XTLTRACE_ERR("IOCTL_STORAGE_QUERY_PROPERTY(HEADER) failed.\n"); return NULL; } // We only retrived the header, now we reallocate the buffer // required for the actual query bufferLength = buffer->Size; XTL::AutoProcessHeapPtr<STORAGE_DESCRIPTOR_HEADER> newBuffer = static_cast<STORAGE_DESCRIPTOR_HEADER*>( ::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, bufferLength)); if (newBuffer.IsInvalid()) { return NULL; } // set the buffer with the new buffer buffer.Detach(); buffer = newBuffer.Detach(); // now we can query the actual property with the proper size fSuccess = ::DeviceIoControl( hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &spquery, sizeof(spquery), buffer, bufferLength, &returnedLength, NULL); if (!fSuccess) { XTLTRACE_ERR("IOCTL_STORAGE_QUERY_PROPERTY(DATA) failed.\n"); return NULL; } return buffer.Detach(); }
NDASVOL_LINKAGE HRESULT NDASVOL_CALL NdasIsNdasPathA( IN LPCSTR FilePath) { if (IsBadStringPtrA(FilePath, UINT_PTR(-1))) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "Invalid path, path=%hs\n", FilePath); return E_INVALIDARG; } XTLTRACE2(NdasVolTrace, 4, "NdasIsNdasPathA(%hs)\n", FilePath); int nChars = MultiByteToWideChar(CP_ACP, 0, FilePath, -1, NULL, 0); ++nChars; // additional terminating NULL char XTL::AutoProcessHeapPtr<WCHAR> wszFilePath = reinterpret_cast<LPWSTR>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, nChars * sizeof(WCHAR))); if (wszFilePath.IsInvalid()) { return E_OUTOFMEMORY; } nChars = MultiByteToWideChar(CP_ACP, 0, FilePath, -1, wszFilePath, nChars); XTLASSERT(nChars > 0); return NdasIsNdasPathW(wszFilePath); }
NDASVOL_LINKAGE BOOL NDASVOL_CALL NdasIsNdasPathW( IN LPCWSTR FilePath) { CPARAM(IsValidStringPtrW(FilePath, UINT_PTR(-1))); XTLTRACE2(NdasVolTrace, 4, "NdasIsNdasPathW(%ls)\n", FilePath); XTL::AutoProcessHeapPtr<TCHAR> mountPoint = pGetVolumeMountPointForPath(FilePath); if (mountPoint.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, _T("pGetVolumeMountPointForPath(%s) failed, error=0x%X\n"), FilePath, GetLastError()); return FALSE; } XTL::AutoProcessHeapPtr<TCHAR> volumeName = pGetVolumeDeviceNameForMountPoint(mountPoint); if (volumeName.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, _T("pGetVolumeDeviceNameForMountPoint(%s) failed, error=0x%X\n"), mountPoint, GetLastError()); return FALSE; } // Volume is a \\.\C: XTL::AutoFileHandle hVolume = ::CreateFileW( volumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hVolume.IsInvalid()) { return FALSE; } return NdasIsNdasVolume(hVolume); }
PDRIVE_LAYOUT_INFORMATION_EX pDiskGetDriveLayoutEx(HANDLE hDevice) { DWORD returnedLength; // always returns 0 DWORD bufferLength = sizeof(DRIVE_LAYOUT_INFORMATION_EX); XTL::AutoProcessHeapPtr<DRIVE_LAYOUT_INFORMATION_EX> buffer = static_cast<DRIVE_LAYOUT_INFORMATION_EX*>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength)); BOOL fSuccess = ::DeviceIoControl( hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buffer, bufferLength, &returnedLength, NULL); while (!fSuccess && ERROR_INSUFFICIENT_BUFFER == ::GetLastError()) { // To determine the size of output buffer that is required, caller // should send this IOCTL request in a loop. Every time the // storage stack rejects the IOCTL with an error message // indicating that the buffer was too small, caller should double // the buffer size. bufferLength += bufferLength; XTL::AutoProcessHeapPtr<DRIVE_LAYOUT_INFORMATION_EX> newBuffer = static_cast<DRIVE_LAYOUT_INFORMATION_EX*>( ::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, bufferLength)); if (newBuffer.IsInvalid()) { // buffer is still valid in case of failure // buffer will be released on return. return NULL; } // buffer points to the non-valid memory, so invalidate it now buffer.Detach(); // transfer the data from newBuffer to buffer buffer = newBuffer.Detach(); // query again fSuccess = ::DeviceIoControl( hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buffer, bufferLength, &returnedLength, NULL); } // returns the buffer return buffer.Detach(); }
HRESULT pGetVolumeMountPointForPathW( __in LPCWSTR Path, __out LPWSTR* MountPoint) { if (NULL == MountPoint) { return E_POINTER; } *MountPoint = NULL; // TODO: It is possible to be the path is more than MAX_PATH // in case of supporting unicode file names up to 65534 DWORD mountPointLength = MAX_PATH; XTL::AutoProcessHeapPtr<TCHAR> mountPoint = static_cast<TCHAR*>( ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, mountPointLength * sizeof(WCHAR))); if (mountPoint.IsInvalid()) { return E_OUTOFMEMORY; } if (!GetVolumePathName(Path, mountPoint, mountPointLength)) { HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "GetVolumePathName(%ls) failed, hr=0x%X\n", Path, hr); return hr; } XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, "Path(%ls) is mounted from %s.\n", Path, mountPoint); *MountPoint = mountPoint.Detach(); return S_OK; }
NDASVOL_LINKAGE BOOL NDASVOL_CALL NdasIsNdasPathA( IN LPCSTR FilePath) { CPARAM(IsValidStringPtrA(FilePath, UINT_PTR(-1))); XTLTRACE2(NdasVolTrace, 4, "NdasIsNdasPathA(%hs)\n", FilePath); int nChars = ::MultiByteToWideChar(CP_ACP, 0, FilePath, -1, NULL, 0); ++nChars; // additional terminating NULL char XTL::AutoProcessHeapPtr<WCHAR> wszFilePath = reinterpret_cast<LPWSTR>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, nChars * sizeof(WCHAR))); if (wszFilePath.IsInvalid()) { ::SetLastError(ERROR_OUTOFMEMORY); return FALSE; } nChars = ::MultiByteToWideChar(CP_ACP, 0, FilePath, -1, wszFilePath, nChars); return NdasIsNdasPathW(wszFilePath); }
CONFIGRET pCMGetDeviceInterfaceList( LPTSTR* SymbolicLinkNameList, LPCGUID InterfaceClassGuid, DEVINSTID DevInstId, ULONG Flags /*= CM_GET_DEVICE_INTERFACE_LIST_PRESENT*/) { *SymbolicLinkNameList = NULL; ULONG bufferLength; CONFIGRET ret = ::CM_Get_Device_Interface_List_Size( &bufferLength, const_cast<LPGUID>(InterfaceClassGuid), DevInstId, Flags); if (CR_SUCCESS != ret) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "CM_Get_Device_Interface_List_Size failed, cret=0x%X.\n", ret); return ret; } XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE, "RequiredBufferSize=%d chars\n", bufferLength); if (0 == bufferLength) { TCHAR* empty = static_cast<TCHAR*>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * sizeof(TCHAR))); *SymbolicLinkNameList = empty; return CR_SUCCESS; } XTL::AutoProcessHeapPtr<TCHAR> buffer = static_cast<TCHAR*>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength * sizeof(TCHAR))); if (buffer.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "HeapAlloc failed, bytes=%d\n", bufferLength * sizeof(TCHAR)); return CR_OUT_OF_MEMORY; } ret = ::CM_Get_Device_Interface_List( const_cast<LPGUID>(InterfaceClassGuid), DevInstId, buffer, bufferLength, Flags); if (CR_SUCCESS != ret) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "CM_Get_Device_Interface_List failed, cret=%x.\n", ret); return ret; } *SymbolicLinkNameList = buffer.Detach(); XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, _T("SymbolicLinkNameList=%s\n"), *SymbolicLinkNameList); return CR_SUCCESS; }
// // Returns the symbolic link name list of the volume. // Each entry is null-terminated and the last entry is terminated // by an additional null character // // Caller should free the non-null returned pointer // with HeapFree(GetProcessHeap(),...) // LPTSTR pGetVolumesForNdasScsiLocation( const NDAS_SCSI_LOCATION* NdasScsiLocation) { // Get the disk instance id of the location XTL::AutoProcessHeapPtr<TCHAR> DiskInstId = pGetDiskForNdasScsiLocationEx(NdasScsiLocation, FALSE); if (DiskInstId.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "GetDiskDevInstId failed, error=0x%X\n", GetLastError()); return NULL; } // Get the volume instance id list of the disk XTL::AutoProcessHeapPtr<TCHAR> VolumeInstIdList = pGetVolumeInstIdListForDisk(DiskInstId); if (VolumeInstIdList.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "GetVolumeInstIdList failed, error=0x%X\n", GetLastError()); return NULL; } // Get the volume names of the disk (via interface query) // Preallocate the buffer up to MAX_PATH DWORD bufferLength = MAX_PATH * sizeof(TCHAR); DWORD bufferRemaining = bufferLength; XTL::AutoProcessHeapPtr<TCHAR> buffer = static_cast<LPTSTR>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength)); LPTSTR bufferNext = static_cast<LPTSTR>(buffer); // direct resource access (non-const) for (LPCTSTR VolumeInstId = VolumeInstIdList; VolumeInstId && *VolumeInstId; VolumeInstId += ::lstrlen(VolumeInstId) + 1) { // Actually returned buffer is a list of null-terminated strings // but we queried for a specific instance id and for the specific // class, where only one or zero volume name may return. // So the return type is just a single string. XTL::AutoProcessHeapPtr<TCHAR> VolumeName = pGetDeviceSymbolicLinkList( &GUID_DEVINTERFACE_VOLUME, const_cast<LPTSTR>(VolumeInstId), CM_GET_DEVICE_INTERFACE_LIST_PRESENT); if (VolumeName.IsInvalid()) { // may not be a volume XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, _T("%s is not a volume device\n"), VolumeInstId); continue; } for (LPCTSTR VN = VolumeName; VN && *VN; VN += ::lstrlen(VN) + 1) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, _T("VolumeName=%s\n"), VN); } XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, _T("VolumeName=%s\n"), VolumeName); DWORD volumeNameLength = ::lstrlen(VolumeName); DWORD bufferRequired = (volumeNameLength + 1) * sizeof(TCHAR); if (bufferRemaining < bufferRequired) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, "Reallocation\n"); XTL::AutoProcessHeapPtr<TCHAR> newBuffer = static_cast<LPTSTR>( ::HeapReAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, bufferLength + bufferRequired - bufferRemaining)); if (newBuffer.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "HeapReAlloc failed, bytes=%d\n", bufferLength + bufferLength - bufferRemaining); return NULL; } size_t bufferNextOffset = reinterpret_cast<BYTE*>(bufferNext) - reinterpret_cast<BYTE*>(*(&buffer)); // discard the old buffer and attach it to the new buffer buffer.Detach(); buffer = newBuffer.Detach(); bufferNext = reinterpret_cast<TCHAR*>( reinterpret_cast<BYTE*>(static_cast<LPTSTR>(buffer)) + bufferNextOffset); } ::CopyMemory(bufferNext, VolumeName, bufferRequired); bufferNext += volumeNameLength + 1; bufferRemaining -= bufferRequired; } return buffer.Detach(); }
LPTSTR pGetDiskForNdasScsiLocationEx( const NDAS_SCSI_LOCATION* NdasScsiLocation, BOOL SymbolicLinkOrDevInstId) { // Get the child device instances of the NDAS SCSI pdo XTL::AutoProcessHeapPtr<TCHAR> ChildDevInstIdList = pGetChildDevInstIdsForNdasScsiLocation(NdasScsiLocation); // Find the disk of TargetID and LUN for all child device instances for (LPCTSTR ChildDevInstId = ChildDevInstIdList; ChildDevInstId && *ChildDevInstId; ChildDevInstId += ::lstrlen(ChildDevInstId) + 1) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE, _T("ChildDevInstId:%s\n"), ChildDevInstId); // Ensure that the child really is present (not ghosted) DEVINST ChildDevInst; CONFIGRET ret = ::CM_Locate_DevNode( &ChildDevInst, const_cast<DEVINSTID>(ChildDevInstId), CM_LOCATE_DEVNODE_NORMAL); if (CR_SUCCESS != ret) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "CM_Locate_DevNode failed, cret=0x%X\n", ret); continue; } // Query the Disk Class Interface, if there are no disk class // interface, it is not of a disk class. XTL::AutoProcessHeapPtr<TCHAR> SymLinkList; ret = pCMGetDeviceInterfaceList( &SymLinkList, &DiskClassGuid, const_cast<DEVINSTID>(ChildDevInstId), CM_GET_DEVICE_INTERFACE_LIST_PRESENT); if (CR_SUCCESS != ret) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "pCMGetDeviceInterfaceList failed, cret=0x%X\n", ret); continue; } // Returned list contains the list of the device file names (or // symbolic links) which we can open with CreateFile for IO_CTLs for (LPCTSTR DeviceName = SymLinkList; DeviceName && *DeviceName; DeviceName += ::lstrlen(DeviceName) + 1) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE, _T("Device:%s\n"), DeviceName); XTL::AutoFileHandle hDevice = ::CreateFile( DeviceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, _T("CreateFile(%s) failed, error=0x%X\n"), DeviceName, GetLastError()); continue; } // Query the SCSI Address and compare TargetID and LUN to // compare them with those of NDAS SCSI location. SCSI_ADDRESS scsiAddress; if (!pScsiGetAddress(hDevice, &scsiAddress)) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, _T("GetScsiAddress(%s) failed, error=0x%X\n"), DeviceName, GetLastError()); continue; } if (NdasScsiLocation->TargetID == scsiAddress.TargetId && NdasScsiLocation->LUN == scsiAddress.Lun) { // We found the target disk, and we create a buffer to // return. If SymbolicLinkOrDevInstId is non-zero (TRUE), // we will returns SymbolicLink, otherwise, we will return the DevInstId if (SymbolicLinkOrDevInstId) { DWORD TargetLength = (::lstrlen(DeviceName) + 1) * sizeof(TCHAR); XTL::AutoProcessHeapPtr<TCHAR> TargetDeviceName = static_cast<LPTSTR>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, TargetLength)); if (TargetDeviceName.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "HeapAlloc failed, byets=%d\n", TargetLength); return NULL; } ::CopyMemory(TargetDeviceName, DeviceName, TargetLength); return TargetDeviceName.Detach(); } else { DWORD TargetLength = (::lstrlen(ChildDevInstId) + 1) * sizeof(TCHAR); XTL::AutoProcessHeapPtr<TCHAR> TargetDevInstId = static_cast<LPTSTR>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, TargetLength)); if (TargetDevInstId.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "HeapAlloc failed, byets=%d\n", TargetLength); return NULL; } ::CopyMemory(TargetDevInstId, ChildDevInstId, TargetLength); return TargetDevInstId.Detach(); } } } } return NULL; }
CONFIGRET pCMGetDeviceIDList( LPTSTR* RelDevInstIdList, LPCTSTR DevInstId, ULONG Flags) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, _T("DevInstId=%s,Flags=%08X\n"), DevInstId, Flags); *RelDevInstIdList = NULL; ULONG bufferLength; CONFIGRET ret = ::CM_Get_Device_ID_List_Size( &bufferLength, DevInstId, Flags); if (CR_SUCCESS != ret) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "CM_Get_Device_ID_List_Size failed with cret=0x%X.\n", ret); return ret; } XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE, "RequiredBufferSize=%d chars\n", bufferLength); if (0 == bufferLength) { TCHAR* empty = static_cast<TCHAR*>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * sizeof(TCHAR))); *RelDevInstIdList = empty; return CR_SUCCESS; } XTL::AutoProcessHeapPtr<TCHAR> buffer = static_cast<TCHAR*>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength * sizeof(TCHAR))); if (buffer.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "HeapAlloc failed, bytes=%d\n", bufferLength * sizeof(TCHAR)); return CR_OUT_OF_MEMORY; } ret = ::CM_Get_Device_ID_List( DevInstId, buffer, bufferLength, Flags); if (CR_SUCCESS != ret) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "CM_Get_Device_ID_List failed with cret=0x%X.\n", ret); return ret; } *RelDevInstIdList = buffer.Detach(); XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, _T("RelDevInstIdList=%s\n"), *RelDevInstIdList); return CR_SUCCESS; }
HRESULT pGetVolumeDeviceNameForMountPointW( __in LPCWSTR VolumeMountPoint, __out LPWSTR* VolumeDeviceName) { // The lpszVolumeMountPoint parameter may be a drive letter with // appended backslash (\), such as "D:\". Alternatively, it may be // a path to a volume mount point, again with appended backslash (\), // such as "c:\mnt\edrive\". // A reasonable size for the buffer to accommodate the largest possible // volume name is 50 characters --> wrong 100 HRESULT hr; XTLASSERT(NULL != VolumeDeviceName); if (NULL == VolumeDeviceName) { return E_POINTER; } *VolumeDeviceName = NULL; const DWORD MAX_VOLUMENAME_LEN = 50; DWORD volumeDeviceNameLength = MAX_VOLUMENAME_LEN; XTL::AutoProcessHeapPtr<TCHAR> volumeDeviceName = static_cast<TCHAR*>( HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, volumeDeviceNameLength * sizeof(TCHAR))); if (volumeDeviceName.IsInvalid()) { XTLTRACE2(NdasVolTrace, 0, "HeapAlloc for %d bytes failed.\n", volumeDeviceNameLength); return E_OUTOFMEMORY; } BOOL success = GetVolumeNameForVolumeMountPoint( VolumeMountPoint, volumeDeviceName, volumeDeviceNameLength); if (!success) { hr = HRESULT_FROM_WIN32(GetLastError()); XTLTRACE2(NdasVolTrace, 0, "GetVolumeNameForVolumeMountPoint(%ls) failed, hr=0x%X\n", VolumeMountPoint, hr); return hr; } // Volume Name is a format of \\?\Volume{XXXX}\ with trailing backslash // Volume device name is that of \\.\Volume{XXXX} without trailing backslash _ASSERTE(_T('\\') == volumeDeviceName[0]); _ASSERTE(_T('\\') == volumeDeviceName[1]); _ASSERTE(_T('?') == volumeDeviceName[2]); _ASSERTE(_T('\\') == volumeDeviceName[3]); if (_T('\\') == volumeDeviceName[0] && _T('\\') == volumeDeviceName[1] && _T('?') == volumeDeviceName[2] && _T('\\') == volumeDeviceName[3]) { // replace ? to . volumeDeviceName[2] = _T('.'); } // remove trailing backslash pRemoveTrailingBackslash(volumeDeviceName); XTLTRACE2(NdasVolTrace, 2, "VolumeMountPoint(%ls)=>Volume(%ls)\n", VolumeMountPoint, volumeDeviceName); *VolumeDeviceName = volumeDeviceName.Detach(); return S_OK; }
HRESULT pIsVolumeSpanningNdasDevice( __in HANDLE hVolume) { HRESULT hr; XTL::AutoProcessHeapPtr<VOLUME_DISK_EXTENTS> extents = pVolumeGetVolumeDiskExtents(hVolume); if (extents.IsInvalid()) { hr = HRESULT_FROM_WIN32(GetLastError()); XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed, hr=0x%X\n", hr); return hr; } for (DWORD i = 0; i < extents->NumberOfDiskExtents; ++i) { const DISK_EXTENT* diskExtent = &extents->Extents[i]; XTLTRACE2(NdasVolTrace, 2, "Disk Number=%d\n", diskExtent->DiskNumber); DWORD ndasSlotNumber; hr = pGetNdasSlotNumberFromDiskNumber( diskExtent->DiskNumber, &ndasSlotNumber); if (FAILED(hr)) { SCSI_ADDRESS scsiAddress = {0}; // // Since Windows Server 2003, SCSIPORT PDO does not send // IOCTL_SCSI_MINIPORT down to the stack. Hence we should send // the IOCTL directly to SCSI controller. // XTLTRACE2(NdasVolTrace, 2, "PhysicalDrive%d does not seem to an NDAS SCSI. Try with the adapter\n", diskExtent->DiskNumber); hr = pGetScsiAddressForDiskNumber( diskExtent->DiskNumber, &scsiAddress); if (FAILED(hr)) { XTLTRACE2(NdasVolTrace, 1, "Disk %d does not seem to be attached to the SCSI controller.\n", diskExtent->DiskNumber); continue; } XTLTRACE2(NdasVolTrace, 2, "SCSI Address (PortNumber=%d,PathId=%d,TargetId=%d,LUN=%d)\n", scsiAddress.PortNumber, scsiAddress.PathId, scsiAddress.TargetId, scsiAddress.Lun); DWORD ndasSlotNumber; hr = pGetNdasSlotNumberForScsiPortNumber( scsiAddress.PortNumber, &ndasSlotNumber); if (FAILED(hr)) { XTLTRACE2(NdasVolTrace, 2, "ScsiPort %d does not seem to an NDAS SCSI.\n", scsiAddress.PortNumber); continue; } } XTLTRACE2(NdasVolTrace, 2, "Detected Disk %d/%d has a NDAS Slot Number=%d (first found only).\n", i + 1, extents->NumberOfDiskExtents, ndasSlotNumber); return S_OK; } return E_FAIL; }
HRESULT pGetScsiAddressForDisk( __in HANDLE hDisk, __out PSCSI_ADDRESS ScsiAddress) { HRESULT hr; // // Query Storage Property // XTLTRACE2(NdasVolTrace, 3, "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)\n"); if (NULL == ScsiAddress) { return E_POINTER; } XTL::AutoProcessHeapPtr<STORAGE_ADAPTER_DESCRIPTOR> adapterDescriptor = pStorageQueryAdapterProperty(hDisk); if (adapterDescriptor.IsInvalid()) { hr = HRESULT_FROM_WIN32(GetLastError()); XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "pStorageQueryAdapterProperty failed, hr=0x%X\n", hr); return hr; } // // Ignore non-SCSI device // if (BusTypeScsi != adapterDescriptor->BusType) { hr = NDASVOL_ERROR_NON_NDAS_VOLUME; XTLTRACE2(NdasVolTrace, 2, "Ignoring non-scsi bus, hr=0x%X\n", hr); return hr; } // // Query SCSI Address, given that the physical drive is a SCSI device // XTLTRACE2(NdasVolTrace, 3, "DeviceIoControl(IOCTL_SCSI_GET_ADDRESS)\n"); SCSI_ADDRESS scsiAddress = {0}; hr = pScsiGetAddress(hDisk, &scsiAddress); if (FAILED(hr)) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "pScsiGetAddress failed, hr=0x%X\n", hr); hr = NDASVOL_ERROR_NON_NDAS_VOLUME; return hr; } XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION, "SCSIAddress: Len: %d, PortNumber: %d, " "PathId: %d, TargetId: %d, Lun: %d\n", (DWORD) scsiAddress.Length, (DWORD) scsiAddress.PortNumber, (DWORD) scsiAddress.PathId, (DWORD) scsiAddress.TargetId, (DWORD) scsiAddress.Lun); // // Return the result // *ScsiAddress = scsiAddress; return S_OK; }
NDASVOL_LINKAGE HRESULT NDASVOL_CALL NdasEnumNdasLocationsForVolume( IN HANDLE hVolume, NDASLOCATIONENUMPROC EnumProc, LPVOID Context) { HRESULT hr; if (IsBadCodePtr((FARPROC)EnumProc)) { return E_POINTER; } XTL::AutoProcessHeapPtr<VOLUME_DISK_EXTENTS> extents = pVolumeGetVolumeDiskExtents(hVolume); if (extents.IsInvalid()) { hr = HRESULT_FROM_WIN32(GetLastError()); XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed, hr=0x%x\n", hr); return hr; } for (DWORD i = 0; i < extents->NumberOfDiskExtents; ++i) { const DISK_EXTENT* diskExtent = &extents->Extents[i]; DWORD ndasSlotNumber; hr = pGetNdasSlotNumberFromDiskNumber( diskExtent->DiskNumber, &ndasSlotNumber); if (FAILED(hr)) { SCSI_ADDRESS scsiAddress = {0}; // // Since Windows Server 2003, SCSIPORT PDO does not send // IOCTL_SCSI_MINIPORT down to the stack. Hence we should send // the IOCTL directly to SCSI controller. // hr = pGetScsiAddressForDiskNumber( diskExtent->DiskNumber, &scsiAddress); if (FAILED(hr)) { XTLTRACE2(NdasVolTrace, 1, "Disk %d does not seem to be attached to the SCSI controller.\n", diskExtent->DiskNumber); continue; } XTLTRACE2(NdasVolTrace, 2, "SCSI Address (PortNumber=%d,PathId=%d,TargetId=%d,LUN=%d)\n", scsiAddress.PortNumber, scsiAddress.PathId, scsiAddress.TargetId, scsiAddress.Lun); hr = pGetNdasSlotNumberForScsiPortNumber( scsiAddress.PortNumber, &ndasSlotNumber); if (FAILED(hr)) { XTLTRACE2(NdasVolTrace, 2, "ScsiPort %d does not seem to an NDAS SCSI.\n", scsiAddress.PortNumber); continue; } } XTLTRACE2(NdasVolTrace, 2, "Detected Disk %d/%d has NDAS Slot Number=%d (first found only).\n", i, extents->NumberOfDiskExtents, ndasSlotNumber); NDAS_LOCATION ndasScsiLocation = ndasSlotNumber; // // EnumProc // S_OK: continue enumeration // S_FALSE: returns S_OK to the caller (stop enumeration) // otherwise E_XXX: returns E_XXX to the caller // hr = EnumProc(ndasScsiLocation, Context); if (FAILED(hr)) { return hr; } else if (S_FALSE == hr) { return S_OK; } } return S_OK; }
NDASVOL_LINKAGE BOOL NDASVOL_CALL NdasEnumNdasScsiLocationsForVolume( IN HANDLE hVolume, NDASSCSILOCATIONENUMPROC EnumProc, LPVOID Context) { CPARAM(!::IsBadCodePtr((FARPROC)EnumProc)); XTL::AutoProcessHeapPtr<VOLUME_DISK_EXTENTS> extents = pVolumeGetVolumeDiskExtents(hVolume); if (extents.IsInvalid()) { XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR, "IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed, error=0x%X\n", GetLastError()); return FALSE; } for (DWORD i = 0; i < extents->NumberOfDiskExtents; ++i) { const DISK_EXTENT* diskExtent = &extents->Extents[i]; SCSI_ADDRESS scsiAddress = {0}; if (!pGetScsiAddressForDiskNumber(diskExtent->DiskNumber, &scsiAddress)) { XTLTRACE2(NdasVolTrace, 1, "Disk %d does not seem to be attached to the SCSI controller.\n", diskExtent->DiskNumber); continue; } XTLTRACE2(NdasVolTrace, 2, "SCSI Address (PortNumber=%d,PathId=%d,TargetId=%d,LUN=%d)\n", scsiAddress.PortNumber, scsiAddress.PathId, scsiAddress.TargetId, scsiAddress.Lun); DWORD ndasSlotNumber; if (!pGetNdasSlotNumberForScsiPortNumber(scsiAddress.PortNumber, &ndasSlotNumber)) { XTLTRACE2(NdasVolTrace, 2, "ScsiPort %d does not seem to an NDAS SCSI.\n", scsiAddress.PortNumber); continue; } XTLTRACE2(NdasVolTrace, 2, "Detected Disk %d/%d has NDAS Slot Number=%d (first found only).\n", i, extents->NumberOfDiskExtents, ndasSlotNumber); NDAS_SCSI_LOCATION ndasScsiLocation = {0}; ndasScsiLocation.SlotNo = ndasSlotNumber; ndasScsiLocation.TargetID = scsiAddress.TargetId; ndasScsiLocation.LUN = scsiAddress.Lun; BOOL fContinue = EnumProc(&ndasScsiLocation, Context); if (!fContinue) { return TRUE; } } return TRUE; }
PVOLUME_DISK_EXTENTS pVolumeGetVolumeDiskExtents( HANDLE hVolume) { DWORD returnedLength; DWORD bufferLength = sizeof(VOLUME_DISK_EXTENTS); XTL::AutoProcessHeapPtr<VOLUME_DISK_EXTENTS> buffer = static_cast<PVOLUME_DISK_EXTENTS>( ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength)); if (buffer.IsInvalid()) { XTLTRACE2_ERR(NdasVolTrace, 0, "HeapAlloc (%d bytes) failed.\n", bufferLength); return NULL; } BOOL fSuccess = ::DeviceIoControl( hVolume, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, buffer, bufferLength, &returnedLength, NULL); if (!fSuccess && ERROR_MORE_DATA == ::GetLastError()) { // // An extent is a contiguous run of sectors on one disk. When the // number of extents returned is greater than one (1), the error // code ERROR_MORE_DATA is returned. You should call // DeviceIoControl again, allocating enough buffer space based on // // the value of NumberOfDiskExtents after the first DeviceIoControl call. bufferLength = sizeof(VOLUME_DISK_EXTENTS) - sizeof(DISK_EXTENT) + sizeof(DISK_EXTENT) * buffer->NumberOfDiskExtents; XTL::AutoProcessHeapPtr<VOLUME_DISK_EXTENTS> newBuffer = static_cast<PVOLUME_DISK_EXTENTS>( ::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, bufferLength)); if (newBuffer.IsInvalid()) { // buffer is still valid in case of failure // buffer will be released on return. XTLTRACE2_ERR(NdasVolTrace, 0, "HeapReAlloc (%d bytes) failed.\n", bufferLength); return NULL; } // buffer points to the non-valid memory, so invalidate it now buffer.Detach(); // transfer the data from newBuffer to buffer buffer = newBuffer.Detach(); // query again fSuccess = ::DeviceIoControl( hVolume, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, buffer, bufferLength, &returnedLength, NULL); } if (!fSuccess) { XTLTRACE2_ERR(NdasVolTrace, 0, "DeviceIoControl(IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS) failed.\n"); return NULL; } // returns the buffer return buffer.Detach(); }