public inbox for cygwin-patches@cygwin.com
 help / color / mirror / Atom feed
* [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks
@ 2023-11-15 17:23 Christian Franke
  2023-11-16 10:36 ` Corinna Vinschen
  0 siblings, 1 reply; 6+ messages in thread
From: Christian Franke @ 2023-11-15 17:23 UTC (permalink / raw)
  To: cygwin-patches

[-- Attachment #1: Type: text/plain, Size: 580 bytes --]

This is the next (and possibly last for now) extension to the /dev/disk 
directory. Limited to disk related entries which allowed a 
straightforward extension of the existing code.

My original idea was to add also other drive letters and volume GUIDs. 
Too complex for now.

Interestingly the volume GUID (by-uuid) for partitions on MBR disks is 
sometimes identical to the partition "GUID" (by-partuuid), sometimes 
(always for C:?) not. With GPT disks, both GUIDs are possibly always 
identical.

I will provide a related new-features doc patch later.

-- 
Regards,
Christian


[-- Attachment #2: 0001-Cygwin-Add-dev-disk-by-drive-and-dev-disk-by-uuid-sy.patch --]
[-- Type: text/plain, Size: 8522 bytes --]

From 1dc3d3a8378ab2aeff766ea2d211750079fd27f8 Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.franke@t-online.de>
Date: Wed, 15 Nov 2023 17:54:18 +0100
Subject: [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks

The new directory '/dev/disk/by-drive' provides symlinks for each
disk related drive letter:
'x' -> '../../sdXN'
The new directory '/dev/disk/by-uuid' provides symlinks for each
disk related storage volume:
'MBR_SERIAL-OFFSET' -> '../../sdXN'
'VOLUME_GUID' -> '../../sdXN'

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..977e8a64f 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_uuid:
+		    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-uuid"
+};
+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_uuid);
 
 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..ce3a03d0f 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_uuid
   };
 
 private:
-- 
2.42.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks
  2023-11-15 17:23 [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks Christian Franke
@ 2023-11-16 10:36 ` Corinna Vinschen
  2023-11-16 11:50   ` Christian Franke
  0 siblings, 1 reply; 6+ messages in thread
From: Corinna Vinschen @ 2023-11-16 10:36 UTC (permalink / raw)
  To: cygwin-patches

Hi Christian,

On Nov 15 18:23, Christian Franke wrote:
> This is the next (and possibly last for now) extension to the /dev/disk
> directory. Limited to disk related entries which allowed a straightforward
> extension of the existing code.
> 
> My original idea was to add also other drive letters and volume GUIDs. Too
> complex for now.
> 
> Interestingly the volume GUID (by-uuid) for partitions on MBR disks is
> sometimes identical to the partition "GUID" (by-partuuid), sometimes (always
> for C:?) not. With GPT disks, both GUIDs are possibly always identical.

That looks great, but in terms of by-uuid, I'm not sure it's the
right thing to do.  On Linux I have a vfat partition (/boot/efi).
The uuid in /dev/disk/by-uuid is the volume serial number, just
with an extra dash, i.e.

  057A-B3A7 -> ../../sda1

That's what you get for FAT/FAT32/exFAT.

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?


Thanks,
Corinna


(*) Incidentally the last 8 digits represent the crippled 4 byte
    serial number returned by
    NtQueryVolumeInformationFile(..., FileFsVolumeInformation).

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks
  2023-11-16 10:36 ` Corinna Vinschen
@ 2023-11-16 11:50   ` Christian Franke
  2023-11-16 12:19     ` Corinna Vinschen
  0 siblings, 1 reply; 6+ messages in thread
From: Christian Franke @ 2023-11-16 11:50 UTC (permalink / raw)
  To: cygwin-patches

Corinna Vinschen wrote:
> Hi Christian,
>
> On Nov 15 18:23, Christian Franke wrote:
>> This is the next (and possibly last for now) extension to the /dev/disk
>> directory. Limited to disk related entries which allowed a straightforward
>> extension of the existing code.
>>
>> My original idea was to add also other drive letters and volume GUIDs. Too
>> complex for now.
>>
>> Interestingly the volume GUID (by-uuid) for partitions on MBR disks is
>> sometimes identical to the partition "GUID" (by-partuuid), sometimes (always
>> for C:?) not. With GPT disks, both GUIDs are possibly always identical.
> That looks great, but in terms of by-uuid, I'm not sure it's the
> right thing to do.  On Linux I have a vfat partition (/boot/efi).
> The uuid in /dev/disk/by-uuid is the volume serial number, just
> with an extra dash, i.e.
>
>    057A-B3A7 -> ../../sda1
>
> That's what you get for FAT/FAT32/exFAT.

What is the best way to retrieve a FAT* serial? There is 
GetVolumeInformation{ByHandleW}(), but this may not work with the NT 
Layer pathnames / handles used here. In Cygwin tree, 
GetVolumeInformation only appears in cygcheck.cc and very old ChangeLogs.


> 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 ?

Christian


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks
  2023-11-16 11:50   ` Christian Franke
@ 2023-11-16 12:19     ` Corinna Vinschen
  2023-11-16 17:02       ` Christian Franke
  0 siblings, 1 reply; 6+ messages in thread
From: Corinna Vinschen @ 2023-11-16 12:19 UTC (permalink / raw)
  To: cygwin-patches

On Nov 16 12:50, Christian Franke wrote:
> Corinna Vinschen wrote:
> > Hi Christian,
> > 
> > On Nov 15 18:23, Christian Franke wrote:
> > > This is the next (and possibly last for now) extension to the /dev/disk
> > > directory. Limited to disk related entries which allowed a straightforward
> > > extension of the existing code.
> > > 
> > > My original idea was to add also other drive letters and volume GUIDs. Too
> > > complex for now.
> > > 
> > > Interestingly the volume GUID (by-uuid) for partitions on MBR disks is
> > > sometimes identical to the partition "GUID" (by-partuuid), sometimes (always
> > > for C:?) not. With GPT disks, both GUIDs are possibly always identical.
> > That looks great, but in terms of by-uuid, I'm not sure it's the
> > right thing to do.  On Linux I have a vfat partition (/boot/efi).
> > The uuid in /dev/disk/by-uuid is the volume serial number, just
> > with an extra dash, i.e.
> > 
> >    057A-B3A7 -> ../../sda1
> > 
> > That's what you get for FAT/FAT32/exFAT.
> 
> What is the best way to retrieve a FAT* serial? There is
> GetVolumeInformation{ByHandleW}(), but this may not work with the NT Layer
> pathnames / handles used here.

It will work, because Windows and NT handles are the same thing as long
as you created the handle using an operation opening kernel objects, as,
for instance, files or volumes.

Actually, GetVolumeInformation() is the combination of multiple
NtQueryVolumeInformationFile calls.  The call retrieving the
serial number is NtQueryVolumeInformationFile(..., FileFsVolumeInformation),
as is used in fs_info::update, mount.cc, line 250 ATM.

> > 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.


Corinna

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks
  2023-11-16 12:19     ` Corinna Vinschen
@ 2023-11-16 17:02       ` Christian Franke
  2023-11-17 12:11         ` Corinna Vinschen
  0 siblings, 1 reply; 6+ messages in thread
From: Christian Franke @ 2023-11-16 17:02 UTC (permalink / raw)
  To: cygwin-patches

[-- Attachment #1: Type: text/plain, Size: 809 bytes --]

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.


[-- Attachment #2: 0001-Cygwin-Add-dev-disk-by-drive-and-dev-disk-by-voluuid.patch --]
[-- Type: text/plain, Size: 8622 bytes --]

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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks
  2023-11-16 17:02       ` Christian Franke
@ 2023-11-17 12:11         ` Corinna Vinschen
  0 siblings, 0 replies; 6+ messages in thread
From: Corinna Vinschen @ 2023-11-17 12:11 UTC (permalink / raw)
  To: cygwin-patches

On Nov 16 18:02, Christian Franke wrote:
> 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.

Pushed.


> I try to provide a patch
> for a new by-uuid with filesystem serial numbers soon.

Cool!


Thanks,
Corinna

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-11-17 12:11 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-15 17:23 [PATCH] Cygwin: Add /dev/disk/by-drive and /dev/disk/by-uuid symlinks Christian Franke
2023-11-16 10:36 ` Corinna Vinschen
2023-11-16 11:50   ` Christian Franke
2023-11-16 12:19     ` Corinna Vinschen
2023-11-16 17:02       ` Christian Franke
2023-11-17 12:11         ` Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).