After migrating from GetLogicalDrives to Find(First|Next)VolumeW, mapped network drives no longer showed up in getmntent output. To fix that, also iterate GetLogicalDriveStringsW when builing dos_drive_mappings, and merge with volume mounts (skipping any volume mounts that are just mounted on the root of a drive, and replacing the dos mounts in the mapping for a volume which is mounted on both a drive root and a directory).
Fixes: 04a5b072940cc ("Cygwin: expose all windows volume mount points.") Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257384.html Signed-off-by: Jeremy Drake <cyg...@jdrake.com> --- winsup/cygwin/mount.cc | 145 ++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 32 deletions(-) diff --git a/winsup/cygwin/mount.cc b/winsup/cygwin/mount.cc index b8d8d4a974..ab07c5abef 100644 --- a/winsup/cygwin/mount.cc +++ b/winsup/cygwin/mount.cc @@ -1986,6 +1986,40 @@ endmntent (FILE *) return 1; } +static bool +resolve_dos_device (const wchar_t *dosname, wchar_t *devpath) +{ + if (QueryDosDeviceW (dosname, devpath, NT_MAX_PATH)) + { + /* The DOS drive mapping can be another symbolic link. If so, + the mapping won't work since the section name is the name + after resolving all symlinks. Resolve symlinks here, too. */ + for (int syml_cnt = 0; syml_cnt < SYMLOOP_MAX; ++syml_cnt) + { + UNICODE_STRING upath; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + HANDLE h; + + RtlInitUnicodeString (&upath, devpath); + InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, + NULL, NULL); + status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY, &attr); + if (!NT_SUCCESS (status)) + break; + RtlInitEmptyUnicodeString (&upath, devpath, (NT_MAX_PATH - 1) + * sizeof (WCHAR)); + status = NtQuerySymbolicLinkObject (h, &upath, NULL); + NtClose (h); + if (!NT_SUCCESS (status)) + break; + devpath[upath.Length / sizeof (WCHAR)] = L'\0'; + } + return true; + } + return false; +} + dos_drive_mappings::dos_drive_mappings () : mappings(0) , cur_mapping(0) @@ -1995,6 +2029,44 @@ dos_drive_mappings::dos_drive_mappings () wchar_t vol[64]; /* Long enough for Volume GUID string */ wchar_t *devpath = tp.w_get (); wchar_t *mounts = tp.w_get (); + mapping **nextm = &mappings; + mapping *endfirstloop = NULL; + DWORD len; + + /* Iterate over all drive letters, fetch the DOS device path */ + if (!(len = GetLogicalDriveStringsW (NT_MAX_PATH - 1, mounts)) || + len >= NT_MAX_PATH) + debug_printf ("GetLogicalDriveStringsW, %E"); + else { + for (wchar_t *mount = mounts; *mount; mount += len + 2) + { + len = wcslen (mount); + mount[--len] = L'\0'; /* Drop trailing backslash */ + if (resolve_dos_device (mount, devpath)) + { + mapping *m = new mapping (); + if (m) + { + m->dos.path = wcsdup (mount); + m->ntdevpath = wcsdup (devpath); + if (!m->dos.path || !m->ntdevpath) + { + free (m->dos.path); + free (m->ntdevpath); + delete m; + continue; + } + m->dos.len = len; + m->ntlen = wcslen (m->ntdevpath); + *nextm = endfirstloop = m; + nextm = &m->next; + } + } + else + debug_printf ("Unable to determine the native mapping for %ls " + "(error %E)", mount); + } + } /* Iterate over all volumes, fetch the list of DOS paths the volume is mounted to. */ @@ -2002,43 +2074,22 @@ dos_drive_mappings::dos_drive_mappings () if (sh == INVALID_HANDLE_VALUE) debug_printf ("FindFirstVolumeW, %E"); else { - mapping **nextm = &mappings; do { - /* Skip drives which are not mounted. */ - DWORD len; + /* Skip volumes which are not mounted. */ if (!GetVolumePathNamesForVolumeNameW (vol, mounts, NT_MAX_PATH, &len) || mounts[0] == L'\0') continue; + /* Skip volumes which are only mounted to the root of a drive letter: + they were handled in the loop above */ + if (len == 5 && mounts[1] == L':' && mounts[2] == L'\\' && !mounts[3]) + continue; + *wcsrchr (vol, L'\\') = L'\0'; - if (QueryDosDeviceW (vol + 4, devpath, NT_MAX_PATH)) + if (resolve_dos_device (vol + 4, devpath)) { - /* The DOS drive mapping can be another symbolic link. If so, - the mapping won't work since the section name is the name - after resolving all symlinks. Resolve symlinks here, too. */ - for (int syml_cnt = 0; syml_cnt < SYMLOOP_MAX; ++syml_cnt) - { - UNICODE_STRING upath; - OBJECT_ATTRIBUTES attr; - NTSTATUS status; - HANDLE h; - - RtlInitUnicodeString (&upath, devpath); - InitializeObjectAttributes (&attr, &upath, - OBJ_CASE_INSENSITIVE, NULL, NULL); - status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY, - &attr); - if (!NT_SUCCESS (status)) - break; - RtlInitEmptyUnicodeString (&upath, devpath, (NT_MAX_PATH - 1) - * sizeof (WCHAR)); - status = NtQuerySymbolicLinkObject (h, &upath, NULL); - NtClose (h); - if (!NT_SUCCESS (status)) - break; - devpath[upath.Length / sizeof (WCHAR)] = L'\0'; - } mapping *m = new mapping (); + bool hadrootmount = false; if (m) { /* store mount point list */ @@ -2063,15 +2114,45 @@ dos_drive_mappings::dos_drive_mappings () dos->path = mount; dos->len = wcslen (dos->path); dos->path[--dos->len] = L'\0'; /* Drop trailing backslash */ + if (dos->len == 2 && dos->path[1] == L':') + hadrootmount = true; } m->ntlen = wcslen (m->ntdevpath); - *nextm = m; - nextm = &m->next; + if (hadrootmount) + { + /* This device has already been added to the mappings list + in the first loop above, but with only the drive root + mount. Find that entry and replace it with the complete + list of mounts. */ + hadrootmount = false; + for (mapping *m2 = mappings; + endfirstloop && m2 != endfirstloop->next; + m2 = m2->next) + { + if (m->ntlen == m2->ntlen && + !wcscmp (m->ntdevpath, m2->ntdevpath)) + { + free (m2->dos.path); + m2->dos.next = m->dos.next; + m2->dos.path = m->dos.path; + m2->dos.len = m->dos.len; + free (m->ntdevpath); + delete m; + hadrootmount = true; + break; + } + } + } + if (!hadrootmount) + { + *nextm = m; + nextm = &m->next; + } } } else debug_printf ("Unable to determine the native mapping for %ls " - "(error %u)", vol, GetLastError ()); + "(error %E)", vol); } while (FindNextVolumeW (sh, vol, 64)); FindVolumeClose (sh); -- 2.48.1.windows.1