They are exposed via the getmntent API and proc filesystem entries
dealing with mounts.  This allows things like `df` to show volumes
that are only mounted on directories, not on drive letters.

Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257251.html
Signed-off-by: Jeremy Drake <cyg...@jdrake.com>
---
Rather amazingly, this seemed to work as I expected.  Kind of gross due to
keeping state in the _mytls.locals struct, but it seems to do the job.
Does this approach make sense, or is there something I'm missing?

 winsup/cygwin/cygtls.cc               |   7 ++
 winsup/cygwin/fhandler/process.cc     |  14 ++-
 winsup/cygwin/local_includes/cygtls.h |   9 +-
 winsup/cygwin/local_includes/mount.h  |   1 +
 winsup/cygwin/mount.cc                | 168 ++++++++++++++++++++------
 5 files changed, 158 insertions(+), 41 deletions(-)

diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc
index 1134adc3e2..903ddb12f5 100644
--- a/winsup/cygwin/cygtls.cc
+++ b/winsup/cygwin/cygtls.cc
@@ -121,6 +121,13 @@ _cygtls::remove (DWORD wait)
       CloseHandle (h);
     }

+  /* Close handle and free memory used by getmntent */
+  if (locals.volumesearchhandle)
+    {
+      FindVolumeClose (locals.volumesearchhandle);
+      locals.volumesearchhandle = NULL;
+    }
+  free_local (volumemountpoints);
   /* Close handle and free memory used by select. */
   if (locals.select.sockevt)
     {
diff --git a/winsup/cygwin/fhandler/process.cc 
b/winsup/cygwin/fhandler/process.cc
index f779028116..555d1ddca2 100644
--- a/winsup/cygwin/fhandler/process.cc
+++ b/winsup/cygwin/fhandler/process.cc
@@ -1397,7 +1397,12 @@ format_process_mountstuff (void *data, char *&destbuf, 
bool mountinfo)

   /* Store old value of _my_tls.locals here. */
   int iteration = _my_tls.locals.iteration;
-  unsigned available_drives = _my_tls.locals.available_drives;
+  int volumemountpointoffset = _my_tls.locals.volumemountpointoffset;
+  HANDLE volumesearchhandle = _my_tls.locals.volumesearchhandle;
+  WCHAR *volumemountpoints = _my_tls.locals.volumemountpoints;
+  DWORD volumemountpointslen = _my_tls.locals.volumemountpointslen;
+  _my_tls.locals.volumesearchhandle = NULL;
+  _my_tls.locals.volumemountpoints = NULL;
   /* This reinitializes the above values in _my_tls. */
   setmntent (NULL, NULL);
   /* Restore iteration immediately since it's not used below.  We use the
@@ -1470,8 +1475,11 @@ format_process_mountstuff (void *data, char *&destbuf, 
bool mountinfo)
        }
     }

-  /* Restore available_drives */
-  _my_tls.locals.available_drives = available_drives;
+  /* Restore old values of _my_tls.locals here. */
+  _my_tls.locals.volumemountpointoffset = volumemountpointoffset;
+  _my_tls.locals.volumesearchhandle = volumesearchhandle;
+  _my_tls.locals.volumemountpoints = volumemountpoints;
+  _my_tls.locals.volumemountpointslen = volumemountpointslen;

   if (u_hdl) /* Only not-NULL if open_shared has been called. */
     {
diff --git a/winsup/cygwin/local_includes/cygtls.h 
b/winsup/cygwin/local_includes/cygtls.h
index d814814b19..ba9e980ec1 100644
--- a/winsup/cygwin/local_includes/cygtls.h
+++ b/winsup/cygwin/local_includes/cygtls.h
@@ -93,10 +93,13 @@ struct _local_storage
   int dl_error;
   char dl_buffer[256];

-  /* path.cc */
+  /* mount.cc */
   struct mntent mntbuf;
   int iteration;
-  unsigned available_drives;
+  int volumemountpointoffset;
+  HANDLE volumesearchhandle;
+  WCHAR *volumemountpoints; // note: malloced
+  DWORD volumemountpointslen;
   char mnt_type[80];
   char mnt_opts[80];
   char mnt_fsname[CYG_MAX_PATH];
@@ -181,7 +184,7 @@ public: /* Do NOT remove this public: line, it's a marker 
for gentls_offsets. */
   siginfo_t *sigwait_info;
   HANDLE signal_arrived;
   bool will_wait_for_signal;
-#if 1
+#if 0
   long __align;                        /* Needed to align context to 16 byte. 
*/
 #endif
   /* context MUST be aligned to 16 byte, otherwise RtlCaptureContext fails.
diff --git a/winsup/cygwin/local_includes/mount.h 
b/winsup/cygwin/local_includes/mount.h
index b2acdf08b4..7120281069 100644
--- a/winsup/cygwin/local_includes/mount.h
+++ b/winsup/cygwin/local_includes/mount.h
@@ -216,6 +216,7 @@ class mount_info
   bool from_fstab (bool user, WCHAR [], PWCHAR);

   int cygdrive_win32_path (const char *src, char *dst, int& unit);
+  struct mntent *cygdrive_getmntent ();
 };

 class dos_drive_mappings
diff --git a/winsup/cygwin/mount.cc b/winsup/cygwin/mount.cc
index bf26c4af3e..3ca0b94e45 100644
--- a/winsup/cygwin/mount.cc
+++ b/winsup/cygwin/mount.cc
@@ -1645,14 +1645,8 @@ fillout_mntent (const char *native_path, const char 
*posix_path, unsigned flags)
   struct mntent& ret=_my_tls.locals.mntbuf;
   bool append_bs = false;

-  /* Remove drivenum from list if we see a x: style path */
   if (strlen (native_path) == 2 && native_path[1] == ':')
-    {
-      int drivenum = cyg_tolower (native_path[0]) - 'a';
-      if (drivenum >= 0 && drivenum <= 31)
-       _my_tls.locals.available_drives &= ~(1 << drivenum);
       append_bs = true;
-    }

   /* Pass back pointers to mount_table strings reserved for use by
      getmntent rather than pointers to strings in the internal mount
@@ -1744,41 +1738,133 @@ mount_item::getmntent ()
   return fillout_mntent (native_path, posix_path, flags);
 }

-static struct mntent *
-cygdrive_getmntent ()
+struct mntent *
+mount_info::cygdrive_getmntent ()
 {
-  char native_path[4];
-  char posix_path[CYG_MAX_PATH];
-  DWORD mask = 1, drive = 'a';
-  struct mntent *ret = NULL;
+  tmp_pathbuf tp;
+  char *win32_path, *posix_path;
+  DWORD endpos;
+  int err;
+  WCHAR volumename[64];
+  volumename[0] = L'\0';
+  if (!_my_tls.locals.volumesearchhandle)
+    {
+      _my_tls.locals.volumesearchhandle = FindFirstVolumeW (volumename,
+                                         sizeof (volumename) / sizeof (WCHAR));
+      if (_my_tls.locals.volumesearchhandle == INVALID_HANDLE_VALUE)
+       {
+         __seterrno();
+         _my_tls.locals.volumesearchhandle = NULL;
+         goto cleanup;
+       }
+    }

-  while (_my_tls.locals.available_drives)
+  if (!_my_tls.locals.volumemountpoints)
     {
-      for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
-       if (_my_tls.locals.available_drives & mask)
-         break;
+      _my_tls.locals.volumemountpoints = (WCHAR *) malloc (NT_MAX_PATH * 
sizeof (WCHAR));
+      _my_tls.locals.volumemountpoints[0] = L'\0';
+      _my_tls.locals.volumemountpointoffset = 0;
+      _my_tls.locals.volumemountpointslen = NT_MAX_PATH;
+    }
+
+  while 
(!_my_tls.locals.volumemountpoints[_my_tls.locals.volumemountpointoffset])
+    {
+      if (!volumename[0] &&
+         !FindNextVolumeW (_my_tls.locals.volumesearchhandle, volumename,
+                           sizeof (volumename) / sizeof (WCHAR)))
+       {
+         __seterrno ();
+         goto cleanup;
+       }

-      __small_sprintf (native_path, "%c:\\", cyg_toupper (drive));
-      if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
+      WCHAR *volumemountpoints = _my_tls.locals.volumemountpoints;
+      DWORD volumemountpointslen = _my_tls.locals.volumemountpointslen;
+      BOOL success;
+      while (!(success = GetVolumePathNamesForVolumeNameW (volumename,
+               volumemountpoints, volumemountpointslen,
+               &volumemountpointslen)) &&
+            GetLastError () == ERROR_MORE_DATA &&
+            (volumemountpoints = (WCHAR *) realloc (volumemountpoints,
+                                      volumemountpointslen * sizeof (WCHAR))))
        {
-         _my_tls.locals.available_drives &= ~mask;
-         continue;
+         _my_tls.locals.volumemountpoints = volumemountpoints;
+         _my_tls.locals.volumemountpointslen = volumemountpointslen;
+       }
+
+      if (!volumemountpoints)
+       {
+         set_errno (ENOMEM);
+         goto cleanup;
+       }
+
+      if (!success)
+       {
+         if (GetLastError () != ERROR_NO_MORE_FILES)
+           __seterrno ();
+         else
+           set_errno (0);
+         goto cleanup;
        }
-      native_path[2] = '\0';
-      __small_sprintf (posix_path, "%s%c", mount_table->cygdrive, drive);
-      ret = fillout_mntent (native_path, posix_path, 
mount_table->cygdrive_flags);
-      break;
+
+      _my_tls.locals.volumemountpointoffset = 0;
+      volumename[0] = L'\0';
     }

-  return ret;
+  endpos = _my_tls.locals.volumemountpointoffset +
+                               wcslen (_my_tls.locals.volumemountpoints +
+                                       _my_tls.locals.volumemountpointoffset);
+  if (_my_tls.locals.volumemountpoints[endpos - 1] == L'\\')
+    _my_tls.locals.volumemountpoints[endpos - 1] = L'\0';
+  win32_path = tp.c_get ();
+  sys_wcstombs (win32_path, NT_MAX_PATH, _my_tls.locals.volumemountpoints +
+                                       _my_tls.locals.volumemountpointoffset);
+  _my_tls.locals.volumemountpointoffset = endpos + 1;
+
+  posix_path = tp.c_get ();
+  if ((err = conv_to_posix_path(win32_path, posix_path, 0)))
+    {
+      set_errno (err);
+      goto cleanup;
+    }
+
+  return fillout_mntent (win32_path, posix_path, cygdrive_flags);
+
+cleanup:
+  save_errno errno_saver;
+
+  if (_my_tls.locals.volumesearchhandle)
+  {
+    FindVolumeClose (_my_tls.locals.volumesearchhandle);
+    _my_tls.locals.volumesearchhandle = NULL;
+  }
+  if (_my_tls.locals.volumemountpoints)
+  {
+    free (_my_tls.locals.volumemountpoints);
+    _my_tls.locals.volumemountpoints = NULL;
+  }
+  return NULL;
 }

 struct mntent *
 mount_info::getmntent (int x)
 {
   if (x < 0 || x >= nmounts)
-    return cygdrive_getmntent ();
-
+    {
+      struct mntent *ret;
+      /* de-duplicate against explicit mount entries */
+      while ((ret = cygdrive_getmntent ()))
+       {
+         for (int i = 0; i < nmounts; ++i)
+           {
+             if (!strcmp (ret->mnt_dir, mount[i].posix_path) &&
+                 strcasematch (ret->mnt_fsname, mount[i].native_path))
+               goto cygdrive_mntent_continue;
+           }
+         break;
+cygdrive_mntent_continue:;
+       }
+      return ret;
+    }
   return mount[native_sorted[x]].getmntent ();
 }

@@ -1943,14 +2029,16 @@ extern "C" FILE *
 setmntent (const char *filep, const char *)
 {
   _my_tls.locals.iteration = 0;
-  _my_tls.locals.available_drives = GetLogicalDrives ();
-  /* Filter floppy drives on A: and B: */
-  if ((_my_tls.locals.available_drives & 1)
-      && get_disk_type (L"A:") == DT_FLOPPY)
-    _my_tls.locals.available_drives &= ~1;
-  if ((_my_tls.locals.available_drives & 2)
-      && get_disk_type (L"B:") == DT_FLOPPY)
-    _my_tls.locals.available_drives &= ~2;
+  if (_my_tls.locals.volumesearchhandle)
+  {
+    FindVolumeClose (_my_tls.locals.volumesearchhandle);
+    _my_tls.locals.volumesearchhandle = NULL;
+  }
+  if (_my_tls.locals.volumemountpoints)
+  {
+    free (_my_tls.locals.volumemountpoints);
+    _my_tls.locals.volumemountpoints = NULL;
+  }
   return (FILE *) filep;
 }

@@ -1991,6 +2079,16 @@ getmntent_r (FILE *, struct mntent *mntbuf, char *buf, 
int buflen)
 extern "C" int
 endmntent (FILE *)
 {
+  if (_my_tls.locals.volumesearchhandle)
+  {
+    FindVolumeClose (_my_tls.locals.volumesearchhandle);
+    _my_tls.locals.volumesearchhandle = NULL;
+  }
+  if (_my_tls.locals.volumemountpoints)
+  {
+    free (_my_tls.locals.volumemountpoints);
+    _my_tls.locals.volumemountpoints = NULL;
+  }
   return 1;
 }

-- 
2.47.1.windows.2

Reply via email to