1 /* fhandler/dev_disk.cc: fhandler for the /dev/disk/by-id/... symlinks.
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
17 /* Replace invalid characters. Optionally remove leading and trailing
18 characters. Return remaining string length. */
19 template <typename char_type
, typename func_type
>
21 sanitize_string (char_type
*s
, char_type leading
, char_type trailing
,
22 char_type replace
, func_type valid
)
26 while (s
[first
] == leading
)
29 for (i
= 0; s
[first
+ i
]; i
++)
31 char_type c
= s
[first
+ i
];
44 s
[len
] = (char_type
) 0;
48 /* Variant for device identify strings. */
50 sanitize_id_string (char *s
)
52 return sanitize_string (s
, ' ', ' ', '_', [] (char c
) -> bool
54 return (('0' <= c
&& c
<= '9') || c
== '.' || c
== '-'
55 || ('A' <= c
&& c
<= 'Z') || ('a' <= c
&& c
<= 'z'));
60 /* Variant for volume labels. */
62 sanitize_label_string (WCHAR
*s
)
64 /* Linux does not skip leading spaces. */
65 return sanitize_string (s
, L
'\0', L
' ', L
'_', [] (WCHAR c
) -> bool
67 /* Labels may contain characters not allowed in filenames. Also
68 replace '#' to avoid that duplicate markers introduce new
69 duplicates. Linux replaces spaces with \x20 which is not an
71 return !(c
== L
'/' || c
== L
'\\' || c
== L
'#');
76 /* Fetch storage properties and create the ID string.
77 returns: 1: success, 0: device ignored, -1: IoControl error. */
79 storprop_to_id_name (HANDLE devhdl
, const UNICODE_STRING
*upath
,
80 char *ioctl_buf
, char (& name
)[NAME_MAX
+ 1])
83 STORAGE_PROPERTY_QUERY descquery
=
84 { StorageDeviceProperty
, PropertyStandardQuery
, { 0 } };
85 if (!DeviceIoControl (devhdl
, IOCTL_STORAGE_QUERY_PROPERTY
,
86 &descquery
, sizeof (descquery
),
87 ioctl_buf
, NT_MAX_PATH
,
88 &bytes_read
, nullptr))
90 __seterrno_from_win_error (GetLastError ());
91 debug_printf ("DeviceIoControl (%S, IOCTL_STORAGE_QUERY_PROPERTY,"
92 " {StorageDeviceProperty}): %E", upath
);
96 const STORAGE_DEVICE_DESCRIPTOR
*desc
=
97 reinterpret_cast<const STORAGE_DEVICE_DESCRIPTOR
*>(ioctl_buf
);
98 int vendor_len
= 0, product_len
= 0, serial_len
= 0;
99 if (desc
->VendorIdOffset
)
100 vendor_len
= sanitize_id_string (ioctl_buf
+ desc
->VendorIdOffset
);
101 if (desc
->ProductIdOffset
)
102 product_len
= sanitize_id_string (ioctl_buf
+ desc
->ProductIdOffset
);
103 if (desc
->SerialNumberOffset
)
104 serial_len
= sanitize_id_string (ioctl_buf
+ desc
->SerialNumberOffset
);
106 /* Ignore drive if information is empty or too long (should not happen). */
107 if (!((vendor_len
|| product_len
) && (20/*bustype*/ + vendor_len
+ 1 +
108 product_len
+ 1 + serial_len
+ 1 + 34/*hash*/ + 1 + 10/*partN*/
109 < (int) sizeof (name
))))
112 /* Translate bus types. */
114 switch (desc
->BusType
)
116 case BusTypeAta
: bus
= "ata-"; break;
117 case BusTypeFibre
: bus
= "fibre-"; break;
118 case BusTypeNvme
: bus
= "nvme-"; break;
119 case BusTypeRAID
: bus
= "raid-"; break;
120 case BusTypeSas
: bus
= "sas-"; break;
121 case BusTypeSata
: bus
= "sata-"; break;
122 case BusTypeScsi
: bus
= "scsi-"; break;
123 case BusTypeUsb
: bus
= "usb-"; break;
124 case BusTypeVirtual
: bus
= "virtual-"; break;
125 default: bus
= nullptr; break;
128 /* Create "BUSTYPE-[VENDOR_]PRODUCT[_SERIAL]" string. */
131 cp
= stpcpy (cp
, bus
);
133 cp
+= __small_sprintf (cp
, "bustype%02_y-", desc
->BusType
);
136 cp
= stpcpy (cp
, ioctl_buf
+ desc
->VendorIdOffset
);
140 cp
= stpcpy (cp
, "_");
141 cp
= stpcpy (cp
, ioctl_buf
+ desc
->ProductIdOffset
);
145 cp
= stpcpy (cp
, "_");
146 cp
= stpcpy (cp
, ioctl_buf
+ desc
->SerialNumberOffset
);
149 /* Add hash if information is too short (e.g. missing serial number). */
150 bool add_hash
= !(4 <= vendor_len
+ product_len
&& 4 <= serial_len
);
151 debug_printf ("%S: bustype: %02_y, add_hash: %d, id: '%s' '%s' '%s' ",
152 upath
, (unsigned) desc
->BusType
, (int) add_hash
,
153 (vendor_len
? ioctl_buf
+ desc
->VendorIdOffset
: ""),
154 (product_len
? ioctl_buf
+ desc
->ProductIdOffset
: ""),
155 (serial_len
? ioctl_buf
+ desc
->SerialNumberOffset
: ""));
159 /* The call below also returns the STORAGE_DEVICE_DESCRIPTOR used above.
160 MSDN documentation for STORAGE_DEVICE_UNIQUE_IDENTIFIER says:
161 "The device descriptor contains IDs that are extracted from non-VPD
162 inquiry data." This may mean that the serial number (part of
163 SCSI/SAS VPD data) may not always be provided. Therefore a separate
164 call to retrieve STORAGE_DEVICE_DESCRIPTOR only is done above. */
165 STORAGE_PROPERTY_QUERY idquery
=
166 { StorageDeviceUniqueIdProperty
, PropertyStandardQuery
, { 0 } };
167 if (!DeviceIoControl (devhdl
, IOCTL_STORAGE_QUERY_PROPERTY
,
168 &idquery
, sizeof (idquery
),
169 ioctl_buf
, NT_MAX_PATH
,
170 &bytes_read
, nullptr))
172 __seterrno_from_win_error (GetLastError ());
173 debug_printf ("DeviceIoControl (%S, IOCTL_STORAGE_QUERY_PROPERTY,"
174 " {StorageDeviceUniqueIdProperty}): %E", upath
);
178 /* Utilize the DUID as defined by MSDN to generate a hash. */
179 const STORAGE_DEVICE_UNIQUE_IDENTIFIER
*id
=
180 reinterpret_cast<const STORAGE_DEVICE_UNIQUE_IDENTIFIER
*>(ioctl_buf
);
181 debug_printf ("STORAGE_DEVICE_UNIQUE_IDENTIFIER.Size: %u", id
->Size
);
184 for (ULONG i
= 0; i
< id
->Size
; i
++)
185 hash
= ioctl_buf
[i
] + (hash
<< 6) + (hash
<< 16) - hash
;
186 __small_sprintf (cp
, "_%016_Y%016_X", (unsigned long long) (hash
>> 64),
187 (unsigned long long) hash
);
191 /* ("HarddiskN", PART_NUM) -> "\\\\?\\Volume{GUID}\\" */
193 partition_to_volpath (const UNICODE_STRING
*drive_uname
, DWORD part_num
,
194 WCHAR (& volpath
)[MAX_PATH
])
196 WCHAR gpath
[MAX_PATH
];
197 __small_swprintf (gpath
, L
"\\\\?\\GLOBALROOT\\Device\\%S\\Partition%u\\",
198 drive_uname
, part_num
);
199 if (!GetVolumeNameForVolumeMountPointW (gpath
, volpath
, sizeof(volpath
)))
201 debug_printf ("GetVolumeNameForVolumeMountPointW(%W): %E", gpath
);
204 debug_printf ("%W -> %W", gpath
, volpath
);
208 /* ("HarddiskN", PART_NUM) -> "x" */
210 partition_to_drive(const UNICODE_STRING
*drive_uname
, DWORD part_num
,
211 WCHAR
*w_buf
, char *name
)
213 WCHAR volpath
[MAX_PATH
];
214 if (!partition_to_volpath (drive_uname
, part_num
, volpath
))
218 if (!GetVolumePathNamesForVolumeNameW (volpath
, w_buf
, NT_MAX_PATH
, &len
))
220 debug_printf ("GetVolumePathNamesForVolumeNameW(%W): %E", volpath
);
223 debug_printf ("%W -> '%W'%s", volpath
, w_buf
,
224 (w_buf
[0] && wcschr (w_buf
, L
'\0')[1] ? ", ..." : ""));
226 /* Find first "X:\\", skip if not found.
227 FIXME: Support multiple drive letters. */
229 for (p
= w_buf
; ; p
= wcschr (p
, L
'\0') + 1)
233 if (L
'A' <= p
[0] && p
[0] <= L
'Z' && p
[1] == L
':' && p
[2] == L
'\\' && !p
[3])
236 name
[0] = (p
[0] - L
'A') + 'a';
241 /* ("HarddiskN", PART_NUM) -> "VOLUME_GUID" */
243 partition_to_voluuid(const UNICODE_STRING
*drive_uname
, DWORD part_num
,
246 WCHAR volpath
[MAX_PATH
];
247 if (!partition_to_volpath (drive_uname
, part_num
, volpath
))
250 /* Skip if not "\\\\?\\Volume{GUID}...". */
251 static const WCHAR prefix
[] = L
"\\\\?\\Volume{";
252 const size_t prefix_len
= sizeof (prefix
) / sizeof(WCHAR
) - 1, uuid_len
= 36;
253 if (!(!wcsncmp (volpath
, prefix
, prefix_len
)
254 && volpath
[prefix_len
+ uuid_len
] == L
'}'))
258 volpath
[prefix_len
+ uuid_len
] = 0;
259 __small_sprintf (name
, "%W", volpath
+ prefix_len
);
261 if (!strncmp (name
+ 9, "0000-0000-00", 12) && !strcmp (name
+ 32, "0000"))
263 /* MBR "GUID": Use same SERIAL-OFFSET format as in by-partuuid directory.
264 SERIAL-0000-0000-009a-785634120000 -> SERIAL-123456789a00 */
265 for (int i
= 9, j
= 30; i
< 19; i
+= 2, j
-= 2)
267 if (j
== 22) // name[j + 1] == '-'
270 name
[i
+ 1] = name
[j
+ 1];
277 /* ("HarddiskN", PART_NUM) -> "VOLUME_LABEL" or "VOLUME_SERIAL" */
279 partition_to_label_or_uuid(bool uuid
, const UNICODE_STRING
*drive_uname
,
280 DWORD part_num
, char *ioctl_buf
, char *name
)
282 WCHAR wpath
[MAX_PATH
];
283 /* Trailing backslash is required. */
284 size_t len
= __small_swprintf (wpath
, L
"\\Device\\%S\\Partition%u\\",
285 drive_uname
, part_num
);
286 len
*= sizeof (WCHAR
);
287 UNICODE_STRING upath
= {(USHORT
) len
, (USHORT
) (len
+ 1), wpath
};
288 OBJECT_ATTRIBUTES attr
;
289 InitializeObjectAttributes (&attr
, &upath
, OBJ_CASE_INSENSITIVE
, nullptr,
293 NTSTATUS status
= NtOpenFile (&volhdl
, READ_CONTROL
, &attr
, &io
,
294 FILE_SHARE_VALID_FLAGS
, 0);
295 if (!NT_SUCCESS (status
))
297 /* Fails with STATUS_UNRECOGNIZED_VOLUME (0xC000014F) if the
298 partition/filesystem type is unsupported. */
299 debug_printf ("NtOpenFile(%S), status %y", upath
, status
);
303 /* Check for possible 64-bit NTFS serial number first. */
305 const NTFS_VOLUME_DATA_BUFFER
*nvdb
=
306 reinterpret_cast<const NTFS_VOLUME_DATA_BUFFER
*>(ioctl_buf
);
307 if (uuid
&& DeviceIoControl (volhdl
, FSCTL_GET_NTFS_VOLUME_DATA
, nullptr, 0,
308 ioctl_buf
, NT_MAX_PATH
, &bytes_read
, nullptr))
310 /* Print without any separator as on Linux. */
311 __small_sprintf (name
, "%016X", nvdb
->VolumeSerialNumber
.QuadPart
);
316 /* Get label and 32-bit serial number. */
317 status
= NtQueryVolumeInformationFile (volhdl
, &io
, ioctl_buf
,
318 NT_MAX_PATH
, FileFsVolumeInformation
);
320 if (!NT_SUCCESS (status
))
322 debug_printf ("NtQueryVolumeInformationFile(%S), status %y", upath
,
327 FILE_FS_VOLUME_INFORMATION
*ffvi
=
328 reinterpret_cast<FILE_FS_VOLUME_INFORMATION
*>(ioctl_buf
);
330 /* Print with separator as on Linux. */
331 __small_sprintf (name
, "%04x-%04x", ffvi
->VolumeSerialNumber
>> 16,
332 ffvi
->VolumeSerialNumber
& 0xffff);
335 /* Label is not null terminated. */
336 ffvi
->VolumeLabel
[ffvi
->VolumeLabelLength
/ sizeof(WCHAR
)] = L
'\0';
337 int len
= sanitize_label_string (ffvi
->VolumeLabel
);
340 __small_sprintf (name
, "%W", ffvi
->VolumeLabel
);
347 char name
[NAME_MAX
+ 1];
353 by_id_compare_name (const void *a
, const void *b
)
355 const by_id_entry
*ap
= reinterpret_cast<const by_id_entry
*>(a
);
356 const by_id_entry
*bp
= reinterpret_cast<const by_id_entry
*>(b
);
357 return strcmp (ap
->name
, bp
->name
);
361 by_id_compare_name_drive_part (const void *a
, const void *b
)
363 const by_id_entry
*ap
= reinterpret_cast<const by_id_entry
*>(a
);
364 const by_id_entry
*bp
= reinterpret_cast<const by_id_entry
*>(b
);
365 int cmp
= strcmp (ap
->name
, bp
->name
);
368 cmp
= ap
->drive
- bp
->drive
;
371 return ap
->part
- bp
->part
;
375 by_id_realloc (by_id_entry
*p
, size_t n
)
377 void *p2
= realloc (p
, n
* sizeof (*p
));
380 return reinterpret_cast<by_id_entry
*>(p2
);
384 format_partuuid (char *name
, const PARTITION_INFORMATION_EX
*pix
)
387 switch (pix
->PartitionStyle
)
389 case PARTITION_STYLE_MBR
: guid
= &pix
->Mbr
.PartitionId
; break;
390 case PARTITION_STYLE_GPT
: guid
= &pix
->Gpt
.PartitionId
; break;
391 default: return false;
394 if (pix
->PartitionStyle
== PARTITION_STYLE_MBR
&& !guid
->Data2
395 && !guid
->Data3
&& !guid
->Data4
[6] && !guid
->Data4
[7])
396 /* MBR "GUID": SERIAL-0000-0000-00NN-NNNNNNN00000
397 Byte offset in LE order -----^^^^-^^^^^^^
398 Print as: SERIAL-OFFSET */
399 __small_sprintf(name
,
400 "%08_x-%02_x%02_x%02_x%02_x%02_x%02_x",
401 guid
->Data1
, guid
->Data4
[5], guid
->Data4
[4], guid
->Data4
[3],
402 guid
->Data4
[2], guid
->Data4
[1], guid
->Data4
[0]);
404 __small_sprintf(name
,
405 "%08_x-%04_x-%04_x-%02_x%02_x-%02_x%02_x%02_x%02_x%02_x%02_x",
406 guid
->Data1
, guid
->Data2
, guid
->Data3
,
407 guid
->Data4
[0], guid
->Data4
[1], guid
->Data4
[2], guid
->Data4
[3],
408 guid
->Data4
[4], guid
->Data4
[5], guid
->Data4
[6], guid
->Data4
[7]);
413 /* Create sorted name -> drive mapping table. Must be freed by caller. */
415 get_by_id_table (by_id_entry
* &table
, fhandler_dev_disk::dev_disk_location loc
)
419 /* Open \Device object directory. */
420 wchar_t wpath
[MAX_PATH
] = L
"\\Device";
421 UNICODE_STRING upath
= {14, 16, wpath
};
422 OBJECT_ATTRIBUTES attr
;
423 InitializeObjectAttributes (&attr
, &upath
, OBJ_CASE_INSENSITIVE
, nullptr, nullptr);
425 NTSTATUS status
= NtOpenDirectoryObject (&dirhdl
, DIRECTORY_QUERY
, &attr
);
426 if (!NT_SUCCESS (status
))
428 debug_printf ("NtOpenDirectoryObject, status %y", status
);
429 __seterrno_from_nt_status (status
);
433 /* Limit disk and partition numbers like fhandler_dev::readdir (). */
434 const unsigned max_drive_num
= 127, max_part_num
= 63;
436 unsigned alloc_size
= 0, table_size
= 0;
438 char *ioctl_buf
= tp
.c_get ();
439 char *ioctl_buf2
= (loc
== fhandler_dev_disk::disk_by_label
440 || loc
== fhandler_dev_disk::disk_by_uuid
?
441 tp
.c_get () : nullptr);
442 WCHAR
*w_buf
= (loc
== fhandler_dev_disk::disk_by_drive
?
443 tp
.w_get () : nullptr);
444 DIRECTORY_BASIC_INFORMATION
*dbi_buf
=
445 reinterpret_cast<DIRECTORY_BASIC_INFORMATION
*>(tp
.w_get ());
447 /* Traverse \Device directory ... */
448 bool errno_set
= false;
449 HANDLE devhdl
= nullptr;
450 BOOLEAN restart
= TRUE
;
451 bool last_run
= false;
457 /* Close handle from previous run. */
462 status
= NtQueryDirectoryObject (dirhdl
, dbi_buf
, 65536, FALSE
, restart
,
464 if (!NT_SUCCESS (status
))
466 __seterrno_from_nt_status (status
);
468 debug_printf ("NtQueryDirectoryObject, status %y", status
);
471 if (status
!= STATUS_MORE_ENTRIES
)
474 for (const DIRECTORY_BASIC_INFORMATION
*dbi
= dbi_buf
;
475 dbi
->ObjectName
.Length
> 0;
478 /* ... and check for a "Harddisk[0-9]*" entry. */
479 if (dbi
->ObjectName
.Length
< 9 * sizeof (WCHAR
)
480 || wcsncasecmp (dbi
->ObjectName
.Buffer
, L
"Harddisk", 8) != 0
481 || !iswdigit (dbi
->ObjectName
.Buffer
[8]))
483 /* Got it. Now construct the path to the entire disk, which is
484 "\\Device\\HarddiskX\\Partition0", and open the disk with
485 minimum permissions. */
486 unsigned long drive_num
= wcstoul (dbi
->ObjectName
.Buffer
+ 8,
488 if (drive_num
> max_drive_num
)
490 wcscpy (wpath
, dbi
->ObjectName
.Buffer
);
491 PWCHAR wpart
= wpath
+ dbi
->ObjectName
.Length
/ sizeof (WCHAR
);
492 wcpcpy (wpart
, L
"\\Partition0");
493 upath
.Length
= dbi
->ObjectName
.Length
+ 22;
494 upath
.MaximumLength
= upath
.Length
+ sizeof (WCHAR
);
495 InitializeObjectAttributes (&attr
, &upath
, OBJ_CASE_INSENSITIVE
,
497 /* SYNCHRONIZE access is required for IOCTL_STORAGE_QUERY_PROPERTY
498 for drives behind some drivers (nvmestor.sys). */
500 status
= NtOpenFile (&devhdl
, READ_CONTROL
| SYNCHRONIZE
, &attr
, &io
,
501 FILE_SHARE_VALID_FLAGS
, 0);
502 if (!NT_SUCCESS (status
))
505 __seterrno_from_nt_status (status
);
507 debug_printf ("NtOpenFile(%S), status %y", &upath
, status
);
511 /* Add table space for drive, partitions and end marker. */
512 if (alloc_size
<= table_size
+ max_part_num
)
514 alloc_size
= table_size
+ max_part_num
+ 8;
515 table
= by_id_realloc (table
, alloc_size
);
524 const char *drive_name
= "";
525 if (loc
== fhandler_dev_disk::disk_by_id
)
527 /* Fetch storage properties and create the ID string. */
528 int rc
= storprop_to_id_name (devhdl
, &upath
, ioctl_buf
,
529 table
[table_size
].name
);
536 drive_name
= table
[table_size
].name
;
537 table
[table_size
].drive
= drive_num
;
538 table
[table_size
].part
= 0;
542 /* Fetch drive layout info to information of all partitions on disk. */
544 if (!DeviceIoControl (devhdl
, IOCTL_DISK_GET_DRIVE_LAYOUT_EX
, nullptr, 0,
545 ioctl_buf
, NT_MAX_PATH
, &bytes_read
, nullptr))
547 __seterrno_from_win_error (GetLastError ());
549 debug_printf ("DeviceIoControl(%S, "
550 "IOCTL_DISK_GET_DRIVE_LAYOUT_EX): %E", &upath
);
554 /* Loop over partitions. */
555 const DRIVE_LAYOUT_INFORMATION_EX
*dlix
=
556 reinterpret_cast<const DRIVE_LAYOUT_INFORMATION_EX
*>(ioctl_buf
);
557 for (DWORD i
= 0; i
< dlix
->PartitionCount
; i
++)
559 const PARTITION_INFORMATION_EX
*pix
= &dlix
->PartitionEntry
[i
];
560 DWORD part_num
= pix
->PartitionNumber
;
561 /* A partition number of 0 denotes an extended partition or a
562 filler entry as described in
563 fhandler_dev_floppy::lock_partition. Just skip. */
566 if (part_num
> max_part_num
)
569 char *name
= table
[table_size
].name
;
572 case fhandler_dev_disk::disk_by_drive
:
573 if (!partition_to_drive (&dbi
->ObjectName
, part_num
, w_buf
, name
))
577 case fhandler_dev_disk::disk_by_id
:
578 __small_sprintf (name
, "%s-part%u", drive_name
, part_num
);
581 case fhandler_dev_disk::disk_by_label
:
582 if (!partition_to_label_or_uuid (false, &dbi
->ObjectName
,
583 part_num
, ioctl_buf2
, name
))
587 case fhandler_dev_disk::disk_by_partuuid
:
588 if (!format_partuuid (name
, pix
))
592 case fhandler_dev_disk::disk_by_uuid
:
593 if (!partition_to_label_or_uuid (true, &dbi
->ObjectName
,
594 part_num
, ioctl_buf2
, name
))
598 case fhandler_dev_disk::disk_by_voluuid
:
599 if (!partition_to_voluuid (&dbi
->ObjectName
, part_num
, name
))
603 default: continue; /* Should not happen. */
605 table
[table_size
].drive
= drive_num
;
606 table
[table_size
].part
= part_num
;
615 if (!table_size
&& table
)
621 return (errno_set
? -1 : 0);
623 /* Sort by {name, drive, part} to ensure stable sort order. */
624 qsort (table
, table_size
, sizeof (*table
), by_id_compare_name_drive_part
);
625 /* Mark duplicate names. */
626 for (unsigned i
= 0; i
< table_size
; i
++)
629 while (j
< table_size
&& !strcmp (table
[i
].name
, table
[j
].name
))
633 /* Duplicate(s) found, append "#N" to all entries. This never
634 introduces new duplicates because '#' never occurs in the
636 debug_printf ("mark duplicates %u-%u of '%s'", i
, j
- 1, table
[i
].name
);
637 size_t len
= strlen (table
[i
].name
);
638 for (unsigned k
= i
; k
< j
; k
++)
639 __small_sprintf (table
[k
].name
+ len
, "#%u", k
- i
);
642 debug_printf ("table_size: %d", table_size
);
646 const char dev_disk
[] = "/dev/disk";
647 const size_t dev_disk_len
= sizeof (dev_disk
) - 1;
648 static const char by_drive
[] = "/by-drive";
649 const size_t by_drive_len
= sizeof(by_drive
) - 1;
651 /* Keep this in sync with enum fhandler_dev_disk::dev_disk_location starting
653 static const char * const by_dir_names
[] {
654 "/by-drive", "/by-id", "/by-label",
655 "/by-partuuid", "/by-uuid", "/by-voluuid"
657 const size_t by_dir_names_size
= sizeof(by_dir_names
) / sizeof(by_dir_names
[0]);
658 static_assert((size_t) fhandler_dev_disk::disk_by_drive
+ by_dir_names_size
- 1
659 == (size_t) fhandler_dev_disk::disk_by_voluuid
);
661 fhandler_dev_disk::fhandler_dev_disk ():
671 fhandler_dev_disk::init_dev_disk ()
673 if (loc
!= unknown_loc
)
676 /* Determine location. */
677 const char *path
= get_name ();
679 loc
= invalid_loc
; // "/dev/disk/invalid"
680 if (!path_prefix_p (dev_disk
, path
, dev_disk_len
, false))
681 ; // should not happen
682 else if (!path
[dev_disk_len
])
683 loc
= disk_dir
; // "/dev/disk"
685 for (size_t i
= 0; i
< by_dir_names_size
; i
++)
687 const char *dir
= by_dir_names
[i
];
688 size_t len
= strlen(dir
);
689 if (path_prefix_p (dir
, path
+ dev_disk_len
, len
, false))
691 loc
= (dev_disk_location
) (disk_by_drive
+ i
); // "/dev/disk/by-..."
692 dirlen
= dev_disk_len
+ len
;
701 ; // "/dev/disk/by-..."
702 else if (!strchr (path
+ dirlen
+ 1, '/'))
703 loc_is_link
= true; // "/dev/disk/by-.../LINK"
705 loc
= invalid_loc
; // "/dev/disk/by-.../dir/invalid"
708 debug_printf ("%s: loc %d, loc_is_link %d", path
, (int) loc
, (int) loc_is_link
);
710 /* Done if directory or invalid. */
714 /* Check whether "/dev/disk/by-.../LINK" exists. */
716 int table_size
= get_by_id_table (table
, loc
);
724 strcpy (key
.name
, path
+ dirlen
+ 1);
725 const void *found
= bsearch (&key
, table
, table_size
, sizeof (*table
),
729 /* Preserve drive and partition numbers for fillbuf (). */
730 const by_id_entry
*e
= reinterpret_cast<const by_id_entry
*>(found
);
731 drive_from_id
= e
->drive
;
732 part_from_id
= e
->part
;
740 fhandler_dev_disk::exists ()
742 debug_printf ("exists (%s)", get_name ());
744 if (loc
== invalid_loc
)
746 else if (loc_is_link
)
749 return virt_directory
;
753 fhandler_dev_disk::fstat (struct stat
*buf
)
755 debug_printf ("fstat (%s)", get_name ());
757 if (loc
== invalid_loc
)
763 fhandler_base::fstat (buf
);
764 buf
->st_mode
= (loc_is_link
? S_IFLNK
| S_IWUSR
| S_IWGRP
| S_IWOTH
765 : S_IFDIR
) | STD_RBITS
| STD_XBITS
;
766 buf
->st_ino
= get_ino ();
770 static inline by_id_entry
**
771 dir_id_table (DIR *dir
)
773 return reinterpret_cast<by_id_entry
**>(&dir
->__d_internal
);
777 fhandler_dev_disk::opendir (int fd
)
786 by_id_entry
*table
= nullptr;
789 int table_size
= get_by_id_table (table
, loc
);
791 return nullptr; /* errno is set. */
794 /* Shrink to required table_size. */
795 table
= by_id_realloc (table
, table_size
+ 1);
797 return nullptr; /* Should not happen. */
798 /* Mark end of table for readdir (). */
799 table
[table_size
].name
[0] = '\0';
803 DIR *dir
= fhandler_virtual::opendir (fd
);
809 dir
->__flags
= dirent_saw_dot
| dirent_saw_dot_dot
;
810 *dir_id_table (dir
) = table
;
815 fhandler_dev_disk::closedir (DIR *dir
)
817 free (*dir_id_table (dir
));
818 return fhandler_virtual::closedir (dir
);
822 fhandler_dev_disk::readdir (DIR *dir
, dirent
*de
)
825 if (dir
->__d_position
< 2)
828 de
->d_name
[1] = (dir
->__d_position
? '.' : '\0');
829 de
->d_name
[2] = '\0';
834 else if (loc
== disk_dir
&& dir
->__d_position
< 2 + (int) by_dir_names_size
)
836 strcpy (de
->d_name
, by_dir_names
[dir
->__d_position
- 2] + 1);
841 else if (*dir_id_table (dir
))
843 const char *name
= (*dir_id_table (dir
))[dir
->__d_position
- 2].name
;
846 strcpy (de
->d_name
, name
);
857 syscall_printf ("%d = readdir(%p, %p) (%s)", res
, dir
, de
,
858 (!res
? de
->d_name
: ""));
863 fhandler_dev_disk::open (int flags
, mode_t mode
)
867 if (!fhandler_virtual::open (flags
, mode
))
869 /* else if (loc_is_link) {} */ /* should not happen. */
870 else if (loc
!= invalid_loc
)
872 if ((flags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
))
874 else if (flags
& O_WRONLY
)
901 syscall_printf ("%d = fhandler_dev_disk::open(%y, 0%o)", res
, flags
, mode
);
906 fhandler_dev_disk::fill_filebuf ()
908 debug_printf ("fill_filebuf (%s)", get_name ());
910 if (!(loc_is_link
&& drive_from_id
>= 0))
915 if (drive_from_id
+ 'a' <= 'z')
916 len
= __small_sprintf (buf
, "../../sd%c", drive_from_id
+ 'a');
918 len
= __small_sprintf (buf
, "../../sd%c%c",
919 drive_from_id
/ ('z' - 'a' + 1) - 1 + 'a',
920 drive_from_id
% ('z' - 'a' + 1) + 'a');
922 __small_sprintf (buf
+ len
, "%d", part_from_id
);
926 filebuf
= cstrdup (buf
);