[Lldb-commits] [lldb] [lldb][windows] enumerate volumes via FindFirstVolumeW (PR #200231)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri May 29 04:22:47 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/200231
>From 847318442b018042f8765a6a7e600a40907d1869 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Thu, 28 May 2026 18:21:03 +0100
Subject: [PATCH 1/2] [lldb][windows] enumerate volumes via FindFirstVolumeW
---
.../Process/Windows/Common/DebuggerThread.cpp | 82 ++++++++++++++-----
1 file changed, 61 insertions(+), 21 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index c4bfa11e9635d..0e8a716e552ea 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -485,33 +485,73 @@ DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info,
static std::optional<std::string>
ConvertNtDevicePathToDosPath(llvm::ArrayRef<wchar_t> nt_path) {
- std::array<wchar_t, 512> drive_strings;
- drive_strings[0] = L'\0';
- if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data()))
+ Log *log = GetLog(WindowsLog::Event);
+
+ std::array<wchar_t, MAX_PATH> volume_name;
+ HANDLE hVolFind = ::FindFirstVolumeW(volume_name.data(), volume_name.size());
+ if (hVolFind == INVALID_HANDLE_VALUE) {
+ LLDB_LOG(log,
+ "ConvertNtDevicePathToDosPath: FindFirstVolumeW failed, error={0}",
+ ::GetLastError());
return std::nullopt;
+ }
- std::array<wchar_t, 3> drive = {L"_:"};
- for (const wchar_t *it = drive_strings.data(); *it != L'\0';
- it += wcslen(it) + 1) {
- drive[0] = it[0];
+ std::optional<std::string> result;
+ do {
+ // FindFirstVolumeW yields names of the form "\\?\Volume{GUID}\".
+ // QueryDosDeviceW expects "Volume{GUID}".
+ size_t vlen = ::wcsnlen(volume_name.data(), volume_name.size());
+ if (vlen < 5 || volume_name[vlen - 1] != L'\\')
+ continue;
+
+ volume_name[vlen - 1] = L'\0';
std::array<wchar_t, MAX_PATH> device_name;
- if (!::QueryDosDeviceW(drive.data(), device_name.data(),
- device_name.size()))
+ BOOL ok = ::QueryDosDeviceW(volume_name.data() + 4, device_name.data(),
+ device_name.size());
+ volume_name[vlen - 1] = L'\\';
+ if (!ok)
+ continue;
+
+ size_t device_name_len = ::wcsnlen(device_name.data(), device_name.size());
+ if (device_name_len == 0 || device_name_len >= nt_path.size())
continue;
- size_t device_name_len = wcslen(device_name.data());
- if (device_name_len >= nt_path.size())
+ if (_wcsnicmp(nt_path.data(), device_name.data(), device_name_len) != 0)
continue;
- bool match =
- _wcsnicmp(nt_path.data(), device_name.data(), device_name_len) == 0;
- if (match && nt_path[device_name_len] == L'\\') {
- std::wstring rebuilt_path(drive.data());
- rebuilt_path.append(&nt_path[device_name_len]);
- std::string path_utf8;
- llvm::convertWideToUTF8(rebuilt_path, path_utf8);
- return path_utf8;
+ if (nt_path[device_name_len] != L'\\')
+ continue;
+
+ std::wstring rebuilt;
+ DWORD names_size = 0;
+ ::GetVolumePathNamesForVolumeNameW(volume_name.data(), nullptr, 0,
+ &names_size);
+ if (names_size > 1) {
+ std::vector<wchar_t> names(names_size);
+ DWORD got_size = 0;
+ if (::GetVolumePathNamesForVolumeNameW(volume_name.data(), names.data(),
+ names_size, &got_size) &&
+ names[0] != L'\0') {
+ rebuilt = std::wstring(names.data());
+ }
}
- }
- return std::nullopt;
+ if (rebuilt.empty())
+ rebuilt = std::wstring(volume_name.data(), vlen);
+
+ // The mount point / volume name ends with a backslash, and so does the
+ // separator at nt_path[device_name_len]. Drop one to avoid doubling.
+ if (!rebuilt.empty() && rebuilt.back() == L'\\')
+ rebuilt.pop_back();
+ rebuilt.append(&nt_path[device_name_len]);
+
+ std::string path_utf8;
+ llvm::convertWideToUTF8(rebuilt, path_utf8);
+ result = path_utf8;
+ break;
+ } while (::FindNextVolumeW(hVolFind, volume_name.data(), volume_name.size()));
+
+ ::FindVolumeClose(hVolFind);
+ if (!result)
+ LLDB_LOG(log, "ConvertNtDevicePathToDosPath: no matching volume found");
+ return result;
}
static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {
>From 010d88c995572e3e550309abe6145d08c6ad5ba5 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 29 May 2026 12:22:34 +0100
Subject: [PATCH 2/2] refactor code to use llvm apis
---
.../Process/Windows/Common/DebuggerThread.cpp | 86 ++++++++++---------
1 file changed, 44 insertions(+), 42 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index 0e8a716e552ea..cdd3bf89bb2e3 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -487,71 +487,73 @@ static std::optional<std::string>
ConvertNtDevicePathToDosPath(llvm::ArrayRef<wchar_t> nt_path) {
Log *log = GetLog(WindowsLog::Event);
- std::array<wchar_t, MAX_PATH> volume_name;
- HANDLE hVolFind = ::FindFirstVolumeW(volume_name.data(), volume_name.size());
- if (hVolFind == INVALID_HANDLE_VALUE) {
+ llvm::SmallVector<wchar_t, MAX_PATH> vol_name(MAX_PATH);
+ HANDLE vol_iter = ::FindFirstVolumeW(vol_name.data(), vol_name.size());
+ if (vol_iter == INVALID_HANDLE_VALUE) {
LLDB_LOG(log,
- "ConvertNtDevicePathToDosPath: FindFirstVolumeW failed, error={0}",
+ "ConvertNtDevicePathToDosPath: FindFirstVolumeW failed, "
+ "error={0}",
::GetLastError());
return std::nullopt;
}
+ auto close_iter = llvm::make_scope_exit([&] { ::FindVolumeClose(vol_iter); });
- std::optional<std::string> result;
do {
- // FindFirstVolumeW yields names of the form "\\?\Volume{GUID}\".
+ // FindFirstVolumeW yields "\\?\Volume{GUID}\".
// QueryDosDeviceW expects "Volume{GUID}".
- size_t vlen = ::wcsnlen(volume_name.data(), volume_name.size());
- if (vlen < 5 || volume_name[vlen - 1] != L'\\')
+ size_t vol_len = ::wcsnlen(vol_name.data(), vol_name.size());
+ if (vol_len < 5 || vol_name[vol_len - 1] != L'\\')
continue;
- volume_name[vlen - 1] = L'\0';
- std::array<wchar_t, MAX_PATH> device_name;
- BOOL ok = ::QueryDosDeviceW(volume_name.data() + 4, device_name.data(),
- device_name.size());
- volume_name[vlen - 1] = L'\\';
+ vol_name[vol_len - 1] = L'\0'; // strip trailing '\' for QueryDosDeviceW
+ llvm::SmallVector<wchar_t, MAX_PATH> dev_name(MAX_PATH);
+ bool ok = ::QueryDosDeviceW(vol_name.data() + 4, // skip "\\?\"
+ dev_name.data(), dev_name.size());
+ vol_name[vol_len - 1] = L'\\'; // restore
if (!ok)
continue;
- size_t device_name_len = ::wcsnlen(device_name.data(), device_name.size());
- if (device_name_len == 0 || device_name_len >= nt_path.size())
+ // Check that nt_path begins with this device name followed by '\'.
+ size_t dev_len = ::wcsnlen(dev_name.data(), dev_name.size());
+ if (dev_len == 0 || dev_len >= nt_path.size())
continue;
- if (_wcsnicmp(nt_path.data(), device_name.data(), device_name_len) != 0)
+ if (_wcsnicmp(nt_path.data(), dev_name.data(), dev_len) != 0)
continue;
- if (nt_path[device_name_len] != L'\\')
+ if (nt_path[dev_len] != L'\\')
continue;
- std::wstring rebuilt;
+ // Prefer a drive-letter/mount-point over the raw volume GUID path.
+ llvm::ArrayRef<wchar_t> mount(vol_name.data(), vol_len);
+ llvm::SmallVector<wchar_t> mount_names;
DWORD names_size = 0;
- ::GetVolumePathNamesForVolumeNameW(volume_name.data(), nullptr, 0,
+ ::GetVolumePathNamesForVolumeNameW(vol_name.data(), nullptr, 0,
&names_size);
if (names_size > 1) {
- std::vector<wchar_t> names(names_size);
- DWORD got_size = 0;
- if (::GetVolumePathNamesForVolumeNameW(volume_name.data(), names.data(),
- names_size, &got_size) &&
- names[0] != L'\0') {
- rebuilt = std::wstring(names.data());
+ mount_names.resize(names_size);
+ DWORD written = 0;
+ if (::GetVolumePathNamesForVolumeNameW(
+ vol_name.data(), mount_names.data(), names_size, &written) &&
+ mount_names[0] != L'\0') {
+ mount = llvm::ArrayRef<wchar_t>(
+ mount_names.data(),
+ ::wcsnlen(mount_names.data(), mount_names.size()));
}
}
- if (rebuilt.empty())
- rebuilt = std::wstring(volume_name.data(), vlen);
- // The mount point / volume name ends with a backslash, and so does the
- // separator at nt_path[device_name_len]. Drop one to avoid doubling.
- if (!rebuilt.empty() && rebuilt.back() == L'\\')
- rebuilt.pop_back();
- rebuilt.append(&nt_path[device_name_len]);
+ // Build the final path: mount point + rest of nt_path.
+ llvm::SmallVector<wchar_t> dos_wide(mount.begin(), mount.end());
+ if (!dos_wide.empty() && dos_wide.back() == L'\\')
+ dos_wide.pop_back();
+ dos_wide.append(nt_path.begin() + dev_len, nt_path.end());
- std::string path_utf8;
- llvm::convertWideToUTF8(rebuilt, path_utf8);
- result = path_utf8;
- break;
- } while (::FindNextVolumeW(hVolFind, volume_name.data(), volume_name.size()));
-
- ::FindVolumeClose(hVolFind);
- if (!result)
- LLDB_LOG(log, "ConvertNtDevicePathToDosPath: no matching volume found");
- return result;
+ std::string result;
+ llvm::convertWideToUTF8(std::wstring(dos_wide.begin(), dos_wide.end()),
+ result);
+ return result;
+ } while (::FindNextVolumeW(vol_iter, vol_name.data(), vol_name.size()));
+
+ LLDB_LOG(log, "ConvertNtDevicePathToDosPath: no matching volume found");
+ return std::nullopt;
}
static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) {
More information about the lldb-commits
mailing list