[PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks

Christian Franke Christian.Franke@t-online.de
Thu Nov 16 17:02:44 GMT 2023


Corinna Vinschen wrote:
> On Nov 16 12:50, Christian Franke wrote:
> ...
>>> I also tried an NTFS partition and the output looks like this:
>>>
>>>     0FD4F62866CFBF09 -> ../../sdc1
>>>
>>> This is the 64 bit volume serial number as returned by
>>> DeviceIoControl(FSCTL_GET_NTFS_VOLUME_DATA)(*).
>>>
>>> Wouldn't that be what we want to see, too?
>> Hmm...... yes. Should both information be provided in by-uuid or only the
>> serial numbers? In the latter case, should we add e.g. by-voluuid for the
>> volume GUIDs ?
> Good question... by-voluuid sounds like a nice idea.  It's a Windows-only
> concept anyway, so it might make sense to present it in its own directory.

Then by-voluuid is easy, changed patch is attached. I try to provide a 
patch for a new by-uuid with filesystem serial numbers soon.

-------------- next part --------------
From 39cdbadeacfb226ad082f4f635285298aa5ad1bb Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.franke@t-online.de>
Date: Thu, 16 Nov 2023 17:51:08 +0100
Subject: [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-voluuid
 symlinks

The new directory '/dev/disk/by-drive' provides symlinks for each
disk related drive letter:
'x' -> '../../sdXN'
The new directory '/dev/disk/by-voluuid' provides symlinks for each
disk related storage volume:
'MBR_SERIAL-OFFSET' -> '../../sdXN'
'VOLUME_GUID' -> '../../sdXN'
Both directories provide Windows specific information and do not
exist on Linux.

Signed-off-by: Christian Franke <christian.franke@t-online.de>
---
 winsup/cygwin/fhandler/dev_disk.cc      | 143 ++++++++++++++++++++----
 winsup/cygwin/local_includes/fhandler.h |   3 +-
 2 files changed, 126 insertions(+), 20 deletions(-)

diff --git a/winsup/cygwin/fhandler/dev_disk.cc b/winsup/cygwin/fhandler/dev_disk.cc
index 11b24042f..5f79ab5e9 100644
--- a/winsup/cygwin/fhandler/dev_disk.cc
+++ b/winsup/cygwin/fhandler/dev_disk.cc
@@ -158,6 +158,92 @@ storprop_to_id_name (HANDLE devhdl, const UNICODE_STRING *upath,
   return 1;
 }
 
+/* ("HarddiskN", PART_NUM) -> "\\\\?\\Volume{GUID}\\" */
+static bool
+partition_to_volpath (const UNICODE_STRING *drive_uname, DWORD part_num,
+		      WCHAR (& volpath)[MAX_PATH])
+{
+  WCHAR gpath[MAX_PATH];
+  __small_swprintf (gpath, L"\\\\?\\GLOBALROOT\\Device\\%S\\Partition%u\\",
+		    drive_uname, part_num);
+  if (!GetVolumeNameForVolumeMountPointW (gpath, volpath, sizeof(volpath)))
+    {
+      debug_printf ("GetVolumeNameForVolumeMountPointW(%W): %E", gpath);
+      return false;
+    }
+  debug_printf ("%W -> %W", gpath, volpath);
+  return true;
+}
+
+/* ("HarddiskN", PART_NUM) -> "x" */
+static bool
+partition_to_drive(const UNICODE_STRING *drive_uname, DWORD part_num,
+		   WCHAR *w_buf, char *name)
+{
+  WCHAR volpath[MAX_PATH];
+  if (!partition_to_volpath (drive_uname, part_num, volpath))
+    return false;
+
+  DWORD len;
+  if (!GetVolumePathNamesForVolumeNameW (volpath, w_buf, NT_MAX_PATH, &len))
+    {
+      debug_printf ("GetVolumePathNamesForVolumeNameW(%W): %E", volpath);
+      return false;
+    }
+  debug_printf ("%W -> '%W'%s", volpath, w_buf,
+		(w_buf[0] && wcschr (w_buf, L'\0')[1] ? ", ..." : ""));
+
+  /* Find first "X:\\", skip if not found.
+     FIXME: Support multiple drive letters. */
+  WCHAR *p;
+  for (p = w_buf; ; p = wcschr (p, L'\0') + 1)
+    {
+	if (!*p)
+	  return false;
+	if (L'A' <= p[0] && p[0] <= L'Z' && p[1] == L':' && p[2] == L'\\' && !p[3])
+	  break;
+    }
+  name[0] = (p[0] - L'A') + 'a';
+  name[1] = '\0';
+  return true;
+}
+
+/* ("HarddiskN", PART_NUM) -> "VOLUME_GUID" */
+static bool
+partition_to_voluuid(const UNICODE_STRING *drive_uname, DWORD part_num,
+		     char *name)
+{
+  WCHAR volpath[MAX_PATH];
+  if (!partition_to_volpath (drive_uname, part_num, volpath))
+    return false;
+
+  /* Skip if not "\\\\?\\Volume{GUID}...". */
+  static const WCHAR prefix[] = L"\\\\?\\Volume{";
+  const size_t prefix_len = sizeof (prefix) / sizeof(WCHAR) - 1, uuid_len = 36;
+  if (!(!wcsncmp (volpath, prefix, prefix_len)
+      && volpath[prefix_len + uuid_len] == L'}'))
+    return false;
+
+  /* Extract GUID. */
+  volpath[prefix_len + uuid_len] = 0;
+  __small_sprintf (name, "%W", volpath + prefix_len);
+
+  if (!strncmp (name + 9, "0000-0000-00", 12) && !strcmp (name + 32, "0000"))
+    {
+      /* MBR "GUID": Use same SERIAL-OFFSET format as in by-partuuid directory.
+         SERIAL-0000-0000-009a-785634120000 -> SERIAL-123456789a00 */
+      for (int i = 9, j = 30; i < 19; i += 2, j -= 2)
+	{
+	  if (j == 22) // name[j + 1] == '-'
+	    j--;
+	  name[i] = name[j];
+	  name[i + 1] = name[j + 1];
+	}
+      name[21] = '\0';
+    }
+  return true;
+}
+
 struct by_id_entry
 {
   char name[NAME_MAX + 1];
@@ -208,6 +294,7 @@ format_partuuid (char *name, const PARTITION_INFORMATION_EX *pix)
 		    guid->Data1, guid->Data2, guid->Data3,
 		    guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
 		    guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+
    return true;
 }
 
@@ -237,6 +324,7 @@ get_by_id_table (by_id_entry * &table, fhandler_dev_disk::dev_disk_location loc)
   unsigned alloc_size = 0, table_size = 0;
   tmp_pathbuf tp;
   char *ioctl_buf = tp.c_get ();
+  WCHAR *w_buf = tp.w_get ();
   DIRECTORY_BASIC_INFORMATION *dbi_buf =
     reinterpret_cast<DIRECTORY_BASIC_INFORMATION *>(tp.w_get ());
 
@@ -365,6 +453,11 @@ get_by_id_table (by_id_entry * &table, fhandler_dev_disk::dev_disk_location loc)
 	      char *name = table[table_size].name;
 	      switch (loc)
 		{
+		  case fhandler_dev_disk::disk_by_drive:
+		    if (!partition_to_drive (&dbi->ObjectName, part_num, w_buf, name))
+		      continue;
+		    break;
+
 		  case fhandler_dev_disk::disk_by_id:
 		    __small_sprintf (name, "%s-part%u", drive_name, part_num);
 		    break;
@@ -374,6 +467,11 @@ get_by_id_table (by_id_entry * &table, fhandler_dev_disk::dev_disk_location loc)
 		      continue;
 		    break;
 
+		  case fhandler_dev_disk::disk_by_voluuid:
+		    if (!partition_to_voluuid (&dbi->ObjectName, part_num, name))
+		      continue;
+		    break;
+
 		  default: continue; /* Should not happen. */
 		}
 	      table[table_size].drive = drive_num;
@@ -417,10 +515,17 @@ get_by_id_table (by_id_entry * &table, fhandler_dev_disk::dev_disk_location loc)
 
 const char dev_disk[] = "/dev/disk";
 const size_t dev_disk_len = sizeof (dev_disk) - 1;
-static const char by_id[] = "/by-id";
-const size_t by_id_len = sizeof(by_id) - 1;
-static const char by_partuuid[] = "/by-partuuid";
-const size_t by_partuuid_len = sizeof(by_partuuid) - 1;
+static const char by_drive[] = "/by-drive";
+const size_t by_drive_len = sizeof(by_drive) - 1;
+
+/* Keep this in sync with enum fhandler_dev_disk::dev_disk_location starting
+   at disk_by_drive. */
+static const char * const by_dir_names[] {
+  "/by-drive", "/by-id", "/by-partuuid", "/by-voluuid"
+};
+const size_t by_dir_names_size = sizeof(by_dir_names) / sizeof(by_dir_names[0]);
+static_assert((size_t) fhandler_dev_disk::disk_by_drive + by_dir_names_size - 1
+	      == (size_t) fhandler_dev_disk::disk_by_voluuid);
 
 fhandler_dev_disk::fhandler_dev_disk ():
   fhandler_virtual (),
@@ -440,22 +545,23 @@ fhandler_dev_disk::init_dev_disk ()
   /* Determine location. */
   const char *path = get_name ();
   size_t dirlen = 0;
+  loc = invalid_loc; // "/dev/disk/invalid"
   if (!path_prefix_p (dev_disk, path, dev_disk_len, false))
-    loc = invalid_loc; // should not happen
+    ; // should not happen
   else if (!path[dev_disk_len])
     loc = disk_dir; // "/dev/disk"
-  else if (path_prefix_p (by_id, path + dev_disk_len, by_id_len, false))
-    {
-      loc = disk_by_id; // "/dev/disk/by-id.."
-      dirlen = dev_disk_len + by_id_len;
-    }
-  else if (path_prefix_p (by_partuuid, path + dev_disk_len, by_partuuid_len, false))
-    {
-      loc = disk_by_partuuid; // "/dev/disk/by-partuuid..."
-      dirlen = dev_disk_len + by_partuuid_len;
-    }
   else
-      loc = invalid_loc; // "/dev/disk/invalid"
+    for (size_t i = 0; i < by_dir_names_size; i++)
+      {
+	const char *dir = by_dir_names[i];
+	size_t len = strlen(dir);
+	if (path_prefix_p (dir, path + dev_disk_len, len, false))
+	  {
+	    loc = (dev_disk_location) (disk_by_drive + i); // "/dev/disk/by-..."
+	    dirlen = dev_disk_len + len;
+	    break;
+	  }
+      }
 
   loc_is_link = false;
   if (dirlen)
@@ -594,10 +700,9 @@ fhandler_dev_disk::readdir (DIR *dir, dirent *de)
       dir->__d_position++;
       res = 0;
     }
-  else if (loc == disk_dir && dir->__d_position < 2 + 2)
+  else if (loc == disk_dir && dir->__d_position < 2 + (int) by_dir_names_size)
     {
-      static const char * const names[2] {by_id, by_partuuid};
-      strcpy (de->d_name, names[dir->__d_position - 2] + 1);
+      strcpy (de->d_name, by_dir_names[dir->__d_position - 2] + 1);
       de->d_type = DT_DIR;
       dir->__d_position++;
       res = 0;
diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h
index 6013496d5..86c7b20a2 100644
--- a/winsup/cygwin/local_includes/fhandler.h
+++ b/winsup/cygwin/local_includes/fhandler.h
@@ -3197,7 +3197,8 @@ class fhandler_dev_disk: public fhandler_virtual
 public:
   enum dev_disk_location {
     unknown_loc, invalid_loc, disk_dir,
-    disk_by_id, disk_by_partuuid
+    /* Keep these in sync with dev_disk.cc:by_dir_names array: */
+    disk_by_drive, disk_by_id, disk_by_partuuid, disk_by_voluuid
   };
 
 private:
-- 
2.42.1



More information about the Cygwin-patches mailing list