1 /* SPDX-License-Identifier: GPL-2.0+ */
3 * compose persistent device path
5 * Logic based on Hannes Reinecke's shell script.
17 #include "alloc-util.h"
18 #include "dirent-util.h"
20 #include "libudev-util.h"
21 #include "string-util.h"
24 #include "udev-builtin.h"
27 static void path_prepend(char **path
, const char *fmt
, ...) {
29 _cleanup_free_
char *pre
= NULL
;
33 r
= vasprintf(&pre
, fmt
, va
);
43 new = strjoin(pre
, "-", *path
);
49 free_and_replace(*path
, new);
51 *path
= TAKE_PTR(pre
);
55 ** Linux only supports 32 bit luns.
56 ** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
58 static int format_lun_number(sd_device
*dev
, char **path
) {
63 r
= sd_device_get_sysnum(dev
, &sysnum
);
69 lun
= strtoul(sysnum
, NULL
, 10);
71 /* address method 0, peripheral device addressing with bus id of zero */
72 path_prepend(path
, "lun-%lu", lun
);
74 /* handle all other lun addressing methods by using a variant of the original lun format */
75 path_prepend(path
, "lun-0x%04lx%04lx00000000", lun
& 0xffff, (lun
>> 16) & 0xffff);
80 static sd_device
*skip_subsystem(sd_device
*dev
, const char *subsys
) {
86 for (parent
= dev
; ; ) {
87 const char *subsystem
;
89 if (sd_device_get_subsystem(parent
, &subsystem
) < 0)
92 if (!streq(subsystem
, subsys
))
96 if (sd_device_get_parent(dev
, &parent
) < 0)
103 static sd_device
*handle_scsi_fibre_channel(sd_device
*parent
, char **path
) {
104 sd_device
*targetdev
;
105 _cleanup_(sd_device_unrefp
) sd_device
*fcdev
= NULL
;
106 const char *port
, *sysname
;
107 _cleanup_free_
char *lun
= NULL
;
112 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target", &targetdev
) < 0)
114 if (sd_device_get_sysname(targetdev
, &sysname
) < 0)
116 if (sd_device_new_from_subsystem_sysname(&fcdev
, "fc_transport", sysname
) < 0)
118 if (sd_device_get_sysattr_value(fcdev
, "port_name", &port
) < 0)
121 format_lun_number(parent
, &lun
);
122 path_prepend(path
, "fc-%s-%s", port
, lun
);
126 static sd_device
*handle_scsi_sas_wide_port(sd_device
*parent
, char **path
) {
127 sd_device
*targetdev
, *target_parent
;
128 _cleanup_(sd_device_unrefp
) sd_device
*sasdev
= NULL
;
129 const char *sas_address
, *sysname
;
130 _cleanup_free_
char *lun
= NULL
;
135 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target", &targetdev
) < 0)
137 if (sd_device_get_parent(targetdev
, &target_parent
) < 0)
139 if (sd_device_get_sysname(target_parent
, &sysname
) < 0)
141 if (sd_device_new_from_subsystem_sysname(&sasdev
, "sas_device", sysname
) < 0)
143 if (sd_device_get_sysattr_value(sasdev
, "sas_address", &sas_address
) < 0)
146 format_lun_number(parent
, &lun
);
147 path_prepend(path
, "sas-%s-%s", sas_address
, lun
);
151 static sd_device
*handle_scsi_sas(sd_device
*parent
, char **path
) {
152 sd_device
*targetdev
, *target_parent
, *port
, *expander
;
153 _cleanup_(sd_device_unrefp
) sd_device
*target_sasdev
= NULL
, *expander_sasdev
= NULL
, *port_sasdev
= NULL
;
154 const char *sas_address
= NULL
;
156 const char *phy_count
, *sysname
;
157 _cleanup_free_
char *lun
= NULL
;
162 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target", &targetdev
) < 0)
164 if (sd_device_get_parent(targetdev
, &target_parent
) < 0)
166 if (sd_device_get_sysname(target_parent
, &sysname
) < 0)
169 if (sd_device_new_from_subsystem_sysname(&target_sasdev
, "sas_device", sysname
) < 0)
171 /* The next parent is sas port */
172 if (sd_device_get_parent(target_parent
, &port
) < 0)
174 if (sd_device_get_sysname(port
, &sysname
) < 0)
176 /* Get port device */
177 if (sd_device_new_from_subsystem_sysname(&port_sasdev
, "sas_port", sysname
) < 0)
179 if (sd_device_get_sysattr_value(port_sasdev
, "num_phys", &phy_count
) < 0)
182 /* Check if we are simple disk */
183 if (strncmp(phy_count
, "1", 2) != 0)
184 return handle_scsi_sas_wide_port(parent
, path
);
186 /* Get connected phy */
187 if (sd_device_get_sysattr_value(target_sasdev
, "phy_identifier", &phy_id
) < 0)
190 /* The port's parent is either hba or expander */
191 if (sd_device_get_parent(port
, &expander
) < 0)
194 if (sd_device_get_sysname(expander
, &sysname
) < 0)
196 /* Get expander device */
197 if (sd_device_new_from_subsystem_sysname(&expander_sasdev
, "sas_device", sysname
) >= 0) {
198 /* Get expander's address */
199 if (sd_device_get_sysattr_value(expander_sasdev
, "sas_address", &sas_address
) < 0)
203 format_lun_number(parent
, &lun
);
205 path_prepend(path
, "sas-exp%s-phy%s-%s", sas_address
, phy_id
, lun
);
207 path_prepend(path
, "sas-phy%s-%s", phy_id
, lun
);
212 static sd_device
*handle_scsi_iscsi(sd_device
*parent
, char **path
) {
213 sd_device
*transportdev
;
214 _cleanup_(sd_device_unrefp
) sd_device
*sessiondev
= NULL
, *conndev
= NULL
;
215 const char *target
, *connname
, *addr
, *port
;
216 _cleanup_free_
char *lun
= NULL
;
217 const char *sysname
, *sysnum
;
222 /* find iscsi session */
223 for (transportdev
= parent
; ; ) {
225 if (sd_device_get_parent(transportdev
, &transportdev
) < 0)
227 if (sd_device_get_sysname(transportdev
, &sysname
) < 0)
229 if (startswith(sysname
, "session"))
233 /* find iscsi session device */
234 if (sd_device_new_from_subsystem_sysname(&sessiondev
, "iscsi_session", sysname
) < 0)
237 if (sd_device_get_sysattr_value(sessiondev
, "targetname", &target
) < 0)
240 if (sd_device_get_sysnum(transportdev
, &sysnum
) < 0 || !sysnum
)
242 connname
= strjoina("connection", sysnum
, ":0");
243 if (sd_device_new_from_subsystem_sysname(&conndev
, "iscsi_connection", connname
) < 0)
246 if (sd_device_get_sysattr_value(conndev
, "persistent_address", &addr
) < 0)
248 if (sd_device_get_sysattr_value(conndev
, "persistent_port", &port
) < 0)
251 format_lun_number(parent
, &lun
);
252 path_prepend(path
, "ip-%s:%s-iscsi-%s-%s", addr
, port
, target
, lun
);
256 static sd_device
*handle_scsi_ata(sd_device
*parent
, char **path
, char **compat_path
) {
257 sd_device
*targetdev
, *target_parent
;
258 _cleanup_(sd_device_unrefp
) sd_device
*atadev
= NULL
;
259 const char *port_no
, *sysname
, *name
;
260 unsigned host
, bus
, target
, lun
;
265 if (sd_device_get_sysname(parent
, &name
) < 0)
267 if (sscanf(name
, "%u:%u:%u:%u", &host
, &bus
, &target
, &lun
) != 4)
270 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host", &targetdev
) < 0)
273 if (sd_device_get_parent(targetdev
, &target_parent
) < 0)
276 if (sd_device_get_sysname(target_parent
, &sysname
) < 0)
278 if (sd_device_new_from_subsystem_sysname(&atadev
, "ata_port", sysname
) < 0)
281 if (sd_device_get_sysattr_value(atadev
, "port_no", &port_no
) < 0)
285 /* Devices behind port multiplier have a bus != 0*/
286 path_prepend(path
, "ata-%s.%u.0", port_no
, bus
);
288 /* Master/slave are distinguished by target id */
289 path_prepend(path
, "ata-%s.%u", port_no
, target
);
291 /* old compatible persistent link for ATA devices */
293 path_prepend(compat_path
, "ata-%s", port_no
);
298 static sd_device
*handle_scsi_default(sd_device
*parent
, char **path
) {
300 int host
, bus
, target
, lun
;
301 const char *name
, *base
, *pos
;
302 _cleanup_closedir_
DIR *dir
= NULL
;
309 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host", &hostdev
) < 0)
312 if (sd_device_get_sysname(parent
, &name
) < 0)
314 if (sscanf(name
, "%d:%d:%d:%d", &host
, &bus
, &target
, &lun
) != 4)
318 * Rebase host offset to get the local relative number
320 * Note: This is by definition racy, unreliable and too simple.
321 * Please do not copy this model anywhere. It's just a left-over
322 * from the time we had no idea how things should look like in
325 * Making assumptions about a global in-kernel counter and use
326 * that to calculate a local offset is a very broken concept. It
327 * can only work as long as things are in strict order.
329 * The kernel needs to export the instance/port number of a
330 * controller directly, without the need for rebase magic like
331 * this. Manual driver unbind/bind, parallel hotplug/unplug will
332 * get into the way of this "I hope it works" logic.
335 if (sd_device_get_syspath(hostdev
, &base
) < 0)
337 pos
= strrchr(base
, '/');
341 base
= strndupa(base
, pos
- base
);
346 FOREACH_DIRENT_ALL(dent
, dir
, break) {
350 if (dent
->d_name
[0] == '.')
352 if (!IN_SET(dent
->d_type
, DT_DIR
, DT_LNK
))
354 if (!startswith(dent
->d_name
, "host"))
356 i
= strtoul(&dent
->d_name
[4], &rest
, 10);
360 * find the smallest number; the host really needs to export its
361 * own instance number per parent device; relying on the global host
362 * enumeration and plainly rebasing the numbers sounds unreliable
364 if (basenum
== -1 || i
< basenum
)
371 path_prepend(path
, "scsi-%u:%u:%u:%u", host
, bus
, target
, lun
);
375 static sd_device
*handle_scsi_hyperv(sd_device
*parent
, char **path
, size_t guid_str_len
) {
378 const char *guid_str
;
379 _cleanup_free_
char *lun
= NULL
;
385 assert(guid_str_len
< sizeof(guid
));
387 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host", &hostdev
) < 0)
390 if (sd_device_get_parent(hostdev
, &vmbusdev
) < 0)
393 if (sd_device_get_sysattr_value(vmbusdev
, "device_id", &guid_str
) < 0)
396 if (strlen(guid_str
) < guid_str_len
|| guid_str
[0] != '{' || guid_str
[guid_str_len
-1] != '}')
399 for (i
= 1, k
= 0; i
< guid_str_len
-1; i
++) {
400 if (guid_str
[i
] == '-')
402 guid
[k
++] = guid_str
[i
];
406 format_lun_number(parent
, &lun
);
407 path_prepend(path
, "vmbus-%s-%s", guid
, lun
);
411 static sd_device
*handle_scsi(sd_device
*parent
, char **path
, char **compat_path
, bool *supported_parent
) {
412 const char *devtype
, *id
, *name
;
414 if (sd_device_get_devtype(parent
, &devtype
) < 0 ||
415 !streq(devtype
, "scsi_device"))
419 if (sd_device_get_sysattr_value(parent
, "ieee1394_id", &id
) >= 0) {
420 path_prepend(path
, "ieee1394-0x%s", id
);
421 *supported_parent
= true;
422 return skip_subsystem(parent
, "scsi");
425 /* scsi sysfs does not have a "subsystem" for the transport */
426 if (sd_device_get_syspath(parent
, &name
) < 0)
429 if (strstr(name
, "/rport-")) {
430 *supported_parent
= true;
431 return handle_scsi_fibre_channel(parent
, path
);
434 if (strstr(name
, "/end_device-")) {
435 *supported_parent
= true;
436 return handle_scsi_sas(parent
, path
);
439 if (strstr(name
, "/session")) {
440 *supported_parent
= true;
441 return handle_scsi_iscsi(parent
, path
);
444 if (strstr(name
, "/ata"))
445 return handle_scsi_ata(parent
, path
, compat_path
);
447 if (strstr(name
, "/vmbus_"))
448 return handle_scsi_hyperv(parent
, path
, 37);
449 else if (strstr(name
, "/VMBUS"))
450 return handle_scsi_hyperv(parent
, path
, 38);
452 return handle_scsi_default(parent
, path
);
455 static sd_device
*handle_cciss(sd_device
*parent
, char **path
) {
457 unsigned controller
, disk
;
459 if (sd_device_get_sysname(parent
, &str
) < 0)
461 if (sscanf(str
, "c%ud%u%*s", &controller
, &disk
) != 2)
464 path_prepend(path
, "cciss-disk%u", disk
);
465 return skip_subsystem(parent
, "cciss");
468 static void handle_scsi_tape(sd_device
*dev
, char **path
) {
471 /* must be the last device in the syspath */
475 if (sd_device_get_sysname(dev
, &name
) < 0)
478 if (startswith(name
, "nst") && strchr("lma", name
[3]))
479 path_prepend(path
, "nst%c", name
[3]);
480 else if (startswith(name
, "st") && strchr("lma", name
[2]))
481 path_prepend(path
, "st%c", name
[2]);
484 static sd_device
*handle_usb(sd_device
*parent
, char **path
) {
485 const char *devtype
, *str
, *port
;
487 if (sd_device_get_devtype(parent
, &devtype
) < 0)
489 if (!STR_IN_SET(devtype
, "usb_interface", "usb_device"))
492 if (sd_device_get_sysname(parent
, &str
) < 0)
494 port
= strchr(str
, '-');
499 path_prepend(path
, "usb-0:%s", port
);
500 return skip_subsystem(parent
, "usb");
503 static sd_device
*handle_bcma(sd_device
*parent
, char **path
) {
507 if (sd_device_get_sysname(parent
, &sysname
) < 0)
509 if (sscanf(sysname
, "bcma%*u:%u", &core
) != 1)
512 path_prepend(path
, "bcma-%u", core
);
516 /* Handle devices of AP bus in System z platform. */
517 static sd_device
*handle_ap(sd_device
*parent
, char **path
) {
518 const char *type
, *func
;
523 if (sd_device_get_sysattr_value(parent
, "type", &type
) >= 0 &&
524 sd_device_get_sysattr_value(parent
, "ap_functions", &func
) >= 0)
525 path_prepend(path
, "ap-%s-%s", type
, func
);
529 if (sd_device_get_sysname(parent
, &sysname
) >= 0)
530 path_prepend(path
, "ap-%s", sysname
);
533 return skip_subsystem(parent
, "ap");
536 static int builtin_path_id(sd_device
*dev
, int argc
, char *argv
[], bool test
) {
538 _cleanup_free_
char *path
= NULL
;
539 _cleanup_free_
char *compat_path
= NULL
;
540 bool supported_transport
= false;
541 bool supported_parent
= false;
542 const char *subsystem
;
546 /* walk up the chain of devices and compose path */
549 const char *subsys
, *sysname
;
551 if (sd_device_get_subsystem(parent
, &subsys
) < 0 ||
552 sd_device_get_sysname(parent
, &sysname
) < 0) {
554 } else if (streq(subsys
, "scsi_tape")) {
555 handle_scsi_tape(parent
, &path
);
556 } else if (streq(subsys
, "scsi")) {
557 parent
= handle_scsi(parent
, &path
, &compat_path
, &supported_parent
);
558 supported_transport
= true;
559 } else if (streq(subsys
, "cciss")) {
560 parent
= handle_cciss(parent
, &path
);
561 supported_transport
= true;
562 } else if (streq(subsys
, "usb")) {
563 parent
= handle_usb(parent
, &path
);
564 supported_transport
= true;
565 } else if (streq(subsys
, "bcma")) {
566 parent
= handle_bcma(parent
, &path
);
567 supported_transport
= true;
568 } else if (streq(subsys
, "serio")) {
571 if (sd_device_get_sysnum(parent
, &sysnum
) >= 0 && sysnum
) {
572 path_prepend(&path
, "serio-%s", sysnum
);
573 parent
= skip_subsystem(parent
, "serio");
575 } else if (streq(subsys
, "pci")) {
576 path_prepend(&path
, "pci-%s", sysname
);
578 path_prepend(&compat_path
, "pci-%s", sysname
);
579 parent
= skip_subsystem(parent
, "pci");
580 supported_parent
= true;
581 } else if (streq(subsys
, "platform")) {
582 path_prepend(&path
, "platform-%s", sysname
);
584 path_prepend(&compat_path
, "platform-%s", sysname
);
585 parent
= skip_subsystem(parent
, "platform");
586 supported_transport
= true;
587 supported_parent
= true;
588 } else if (streq(subsys
, "acpi")) {
589 path_prepend(&path
, "acpi-%s", sysname
);
591 path_prepend(&compat_path
, "acpi-%s", sysname
);
592 parent
= skip_subsystem(parent
, "acpi");
593 supported_parent
= true;
594 } else if (streq(subsys
, "xen")) {
595 path_prepend(&path
, "xen-%s", sysname
);
597 path_prepend(&compat_path
, "xen-%s", sysname
);
598 parent
= skip_subsystem(parent
, "xen");
599 supported_parent
= true;
600 } else if (streq(subsys
, "virtio")) {
601 parent
= skip_subsystem(parent
, "virtio");
602 supported_transport
= true;
603 } else if (streq(subsys
, "scm")) {
604 path_prepend(&path
, "scm-%s", sysname
);
606 path_prepend(&compat_path
, "scm-%s", sysname
);
607 parent
= skip_subsystem(parent
, "scm");
608 supported_transport
= true;
609 supported_parent
= true;
610 } else if (streq(subsys
, "ccw")) {
611 path_prepend(&path
, "ccw-%s", sysname
);
613 path_prepend(&compat_path
, "ccw-%s", sysname
);
614 parent
= skip_subsystem(parent
, "ccw");
615 supported_transport
= true;
616 supported_parent
= true;
617 } else if (streq(subsys
, "ccwgroup")) {
618 path_prepend(&path
, "ccwgroup-%s", sysname
);
620 path_prepend(&compat_path
, "ccwgroup-%s", sysname
);
621 parent
= skip_subsystem(parent
, "ccwgroup");
622 supported_transport
= true;
623 supported_parent
= true;
624 } else if (streq(subsys
, "ap")) {
625 parent
= handle_ap(parent
, &path
);
626 supported_transport
= true;
627 supported_parent
= true;
628 } else if (streq(subsys
, "iucv")) {
629 path_prepend(&path
, "iucv-%s", sysname
);
631 path_prepend(&compat_path
, "iucv-%s", sysname
);
632 parent
= skip_subsystem(parent
, "iucv");
633 supported_transport
= true;
634 supported_parent
= true;
635 } else if (streq(subsys
, "nvme")) {
638 if (sd_device_get_sysattr_value(dev
, "nsid", &nsid
) >= 0) {
639 path_prepend(&path
, "nvme-%s", nsid
);
641 path_prepend(&compat_path
, "nvme-%s", nsid
);
642 parent
= skip_subsystem(parent
, "nvme");
643 supported_parent
= true;
644 supported_transport
= true;
650 if (sd_device_get_parent(parent
, &parent
) < 0)
658 * Do not return devices with an unknown parent device type. They
659 * might produce conflicting IDs if the parent does not provide a
660 * unique and predictable name.
662 if (!supported_parent
)
666 * Do not return block devices without a well-known transport. Some
667 * devices do not expose their buses and do not provide a unique
668 * and predictable name that way.
670 if (sd_device_get_subsystem(dev
, &subsystem
) >= 0 &&
671 streq(subsystem
, "block") &&
672 !supported_transport
)
676 char tag
[UTIL_NAME_SIZE
];
680 /* compose valid udev tag name */
681 for (p
= path
, i
= 0; *p
; p
++) {
682 if ((*p
>= '0' && *p
<= '9') ||
683 (*p
>= 'A' && *p
<= 'Z') ||
684 (*p
>= 'a' && *p
<= 'z') ||
690 /* skip all leading '_' */
694 /* avoid second '_' */
700 /* strip trailing '_' */
701 while (i
> 0 && tag
[i
-1] == '_')
705 udev_builtin_add_property(dev
, test
, "ID_PATH", path
);
706 udev_builtin_add_property(dev
, test
, "ID_PATH_TAG", tag
);
710 * Compatible link generation for ATA devices
711 * we assign compat_link to the env variable
715 udev_builtin_add_property(dev
, test
, "ID_PATH_ATA_COMPAT", compat_path
);
720 const UdevBuiltin udev_builtin_path_id
= {
722 .cmd
= builtin_path_id
,
723 .help
= "Compose persistent device path",