[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