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>
This commit is contained in:
Christian Franke 2023-11-16 17:51:08 +01:00 committed by Corinna Vinschen
parent 324ace778f
commit 2cc109be1d
2 changed files with 126 additions and 20 deletions

View File

@ -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;

View File

@ -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: