Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/winsup
diff options
context:
space:
mode:
authorChristian Franke <christian.franke@t-online.de>2023-11-17 17:22:52 +0300
committerCorinna Vinschen <corinna@vinschen.de>2023-11-17 19:09:42 +0300
commit41e13e4d9d73f00259eeb61936a1ca880fc949e6 (patch)
treeb98b103d95defbcfb11f8ed0aa99dfc0401acacf /winsup
parent6ccbfe6a3d8a2aedb2c6f80bcc03bf946acc1b6c (diff)
Cygwin: Add /dev/disk/by-label and /dev/disk/by-uuid symlinks
The new directories '/dev/disk/by-label' and '/dev/disk/by-uuid' provide symlinks for each disk related volume label and serial number: 'VOLUME_LABEL' -> '../../sdXN' 'VOLUME_SERIAL' -> '../../sdXN' Signed-off-by: Christian Franke <christian.franke@t-online.de>
Diffstat (limited to 'winsup')
-rw-r--r--winsup/cygwin/fhandler/dev_disk.cc159
-rw-r--r--winsup/cygwin/local_includes/fhandler.h3
2 files changed, 141 insertions, 21 deletions
diff --git a/winsup/cygwin/fhandler/dev_disk.cc b/winsup/cygwin/fhandler/dev_disk.cc
index 5f79ab5e9..016b4c7bc 100644
--- a/winsup/cygwin/fhandler/dev_disk.cc
+++ b/winsup/cygwin/fhandler/dev_disk.cc
@@ -14,33 +14,62 @@ details. */
#include <wctype.h>
#include <winioctl.h>
-/* Replace spaces, non-printing and unexpected characters. Remove
- leading and trailing spaces. Return remaining string length. */
+/* Replace invalid characters. Optionally remove leading and trailing
+ characters. Return remaining string length. */
+template <typename char_type, typename func_type>
static int
-sanitize_id_string (char *s)
+sanitize_string (char_type *s, char_type leading, char_type trailing,
+ char_type replace, func_type valid)
{
int first = 0;
- while (s[first] == ' ')
- first++;
- int last = -1, i;
+ if (leading)
+ while (s[first] == leading)
+ first++;
+ int len = -1, i;
for (i = 0; s[first + i]; i++)
{
- char c = s[first + i];
- if (c != ' ')
- last = -1;
- else if (last < 0)
- last = i;
- if (!(('0' <= c && c <= '9') || c == '.' || c == '-'
- || ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
- c = '_';
+ char_type c = s[first + i];
+ if (c != trailing)
+ len = -1;
+ else if (len < 0)
+ len = i;
+ if (!valid (c))
+ c = replace;
else if (!first)
continue;
s[i] = c;
}
- if (last < 0)
- last = i;
- s[last] = '\0';
- return last;
+ if (len < 0)
+ len = i;
+ s[len] = (char_type) 0;
+ return len;
+}
+
+/* Variant for device identify strings. */
+static int
+sanitize_id_string (char *s)
+{
+ return sanitize_string (s, ' ', ' ', '_', [] (char c) -> bool
+ {
+ return (('0' <= c && c <= '9') || c == '.' || c == '-'
+ || ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'));
+ }
+ );
+}
+
+/* Variant for volume labels. */
+static int
+sanitize_label_string (WCHAR *s)
+{
+ /* Linux does not skip leading spaces. */
+ return sanitize_string (s, L'\0', L' ', L'_', [] (WCHAR c) -> bool
+ {
+ /* Labels may contain characters not allowed in filenames.
+ Linux replaces spaces with \x20 which is not an option here. */
+ return !((0 <= c && c <= L' ') || c == L':' || c == L'/' || c == L'\\'
+ || c == L'"');
+ }
+ );
}
/* Fetch storage properties and create the ID string.
@@ -244,6 +273,79 @@ partition_to_voluuid(const UNICODE_STRING *drive_uname, DWORD part_num,
return true;
}
+/* ("HarddiskN", PART_NUM) -> "VOLUME_LABEL" or "VOLUME_SERIAL" */
+static bool
+partition_to_label_or_uuid(bool uuid, const UNICODE_STRING *drive_uname,
+ DWORD part_num, char *ioctl_buf, char *name)
+{
+ WCHAR wpath[MAX_PATH];
+ /* Trailing backslash is required. */
+ size_t len = __small_swprintf (wpath, L"\\Device\\%S\\Partition%u\\",
+ drive_uname, part_num);
+ len *= sizeof (WCHAR);
+ UNICODE_STRING upath = {(USHORT) len, (USHORT) (len + 1), wpath};
+ OBJECT_ATTRIBUTES attr;
+ InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, nullptr,
+ nullptr);
+ IO_STATUS_BLOCK io;
+ HANDLE volhdl;
+ NTSTATUS status = NtOpenFile (&volhdl, READ_CONTROL, &attr, &io,
+ FILE_SHARE_VALID_FLAGS, 0);
+ if (!NT_SUCCESS (status))
+ {
+ /* Fails with STATUS_UNRECOGNIZED_VOLUME (0xC000014F) if the
+ partition/filesystem type is unsupported. */
+ debug_printf ("NtOpenFile(%S), status %y", upath, status);
+ return false;
+ }
+
+ /* Check for possible 64-bit NTFS serial number first. */
+ DWORD bytes_read;
+ const NTFS_VOLUME_DATA_BUFFER *nvdb =
+ reinterpret_cast<const NTFS_VOLUME_DATA_BUFFER *>(ioctl_buf);
+ if (uuid && DeviceIoControl (volhdl, FSCTL_GET_NTFS_VOLUME_DATA, nullptr, 0,
+ ioctl_buf, NT_MAX_PATH, &bytes_read, nullptr)
+ && nvdb->VolumeSerialNumber.QuadPart)
+ {
+ /* Print without any separator as on Linux. */
+ __small_sprintf (name, "%16X", nvdb->VolumeSerialNumber.QuadPart);
+ NtClose(volhdl);
+ return true;
+ }
+
+ /* Get label and 32-bit serial number. */
+ status = NtQueryVolumeInformationFile (volhdl, &io, ioctl_buf,
+ NT_MAX_PATH, FileFsVolumeInformation);
+ NtClose(volhdl);
+ if (!NT_SUCCESS (status))
+ {
+ debug_printf ("NtQueryVolumeInformationFile(%S), status %y", upath,
+ status);
+ return false;
+ }
+
+ FILE_FS_VOLUME_INFORMATION *ffvi =
+ reinterpret_cast<FILE_FS_VOLUME_INFORMATION *>(ioctl_buf);
+ if (uuid)
+ {
+ if (!ffvi->VolumeSerialNumber)
+ return false;
+ /* Print with separator as on Linux. */
+ __small_sprintf (name, "%04x-%04x", ffvi->VolumeSerialNumber >> 16,
+ ffvi->VolumeSerialNumber & 0xffff);
+ }
+ else
+ {
+ /* Label is not null terminated. */
+ ffvi->VolumeLabel[ffvi->VolumeLabelLength / sizeof(WCHAR)] = L'\0';
+ int len = sanitize_label_string (ffvi->VolumeLabel);
+ if (!len)
+ return false;
+ __small_sprintf (name, "%W", ffvi->VolumeLabel);
+ }
+ return true;
+}
+
struct by_id_entry
{
char name[NAME_MAX + 1];
@@ -324,7 +426,11 @@ 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 ();
+ char *ioctl_buf2 = (loc == fhandler_dev_disk::disk_by_label
+ || loc == fhandler_dev_disk::disk_by_uuid ?
+ tp.c_get () : nullptr);
+ WCHAR *w_buf = (loc == fhandler_dev_disk::disk_by_drive ?
+ tp.w_get () : nullptr);
DIRECTORY_BASIC_INFORMATION *dbi_buf =
reinterpret_cast<DIRECTORY_BASIC_INFORMATION *>(tp.w_get ());
@@ -462,11 +568,23 @@ get_by_id_table (by_id_entry * &table, fhandler_dev_disk::dev_disk_location loc)
__small_sprintf (name, "%s-part%u", drive_name, part_num);
break;
+ case fhandler_dev_disk::disk_by_label:
+ if (!partition_to_label_or_uuid (false, &dbi->ObjectName,
+ part_num, ioctl_buf2, name))
+ continue;
+ break;
+
case fhandler_dev_disk::disk_by_partuuid:
if (!format_partuuid (name, pix))
continue;
break;
+ case fhandler_dev_disk::disk_by_uuid:
+ if (!partition_to_label_or_uuid (true, &dbi->ObjectName,
+ part_num, ioctl_buf2, name))
+ continue;
+ break;
+
case fhandler_dev_disk::disk_by_voluuid:
if (!partition_to_voluuid (&dbi->ObjectName, part_num, name))
continue;
@@ -521,7 +639,8 @@ 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"
+ "/by-drive", "/by-id", "/by-label",
+ "/by-partuuid", "/by-uuid", "/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
diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h
index 86c7b20a2..ca685a627 100644
--- a/winsup/cygwin/local_includes/fhandler.h
+++ b/winsup/cygwin/local_includes/fhandler.h
@@ -3198,7 +3198,8 @@ public:
enum dev_disk_location {
unknown_loc, invalid_loc, disk_dir,
/* Keep these in sync with dev_disk.cc:by_dir_names array: */
- disk_by_drive, disk_by_id, disk_by_partuuid, disk_by_voluuid
+ disk_by_drive, disk_by_id, disk_by_label,
+ disk_by_partuuid, disk_by_uuid, disk_by_voluuid
};
private: