1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * compose persistent device path
5 * Logic based on Hannes Reinecke's shell script.
12 #include <linux/usb/ch11.h>
17 #include "alloc-util.h"
18 #include "device-util.h"
19 #include "dirent-util.h"
21 #include "parse-util.h"
22 #include "string-util.h"
25 #include "udev-builtin.h"
26 #include "udev-util.h"
29 static void path_prepend(char **path
, const char *fmt
, ...) {
31 _cleanup_free_
char *pre
= NULL
;
35 r
= vasprintf(&pre
, fmt
, va
);
45 new = strjoin(pre
, "-", *path
);
51 free_and_replace(*path
, new);
53 *path
= TAKE_PTR(pre
);
57 ** Linux only supports 32 bit luns.
58 ** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
60 static int format_lun_number(sd_device
*dev
, char **path
) {
65 r
= sd_device_get_sysnum(dev
, &sysnum
);
71 r
= safe_atolu_full(sysnum
, 10, &lun
);
75 /* address method 0, peripheral device addressing with bus id of zero */
76 path_prepend(path
, "lun-%lu", lun
);
78 /* handle all other lun addressing methods by using a variant of the original lun format */
79 path_prepend(path
, "lun-0x%04lx%04lx00000000", lun
& 0xffff, (lun
>> 16) & 0xffff);
84 static sd_device
*skip_subsystem(sd_device
*dev
, const char *subsys
) {
90 /* Unlike the function name, this drops multiple parent devices EXCEPT FOR THE LAST ONE.
91 * The last one will be dropped at the end of the loop in builtin_path_id().
93 * Input: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0
94 * Output: /sys/devices/pci0000:00/0000:00:14.0/usb1
97 for (parent
= dev
; ; ) {
98 if (!device_in_subsystem(parent
, subsys
))
102 if (sd_device_get_parent(dev
, &parent
) < 0)
109 static sd_device
*handle_scsi_fibre_channel(sd_device
*parent
, char **path
) {
110 sd_device
*targetdev
;
111 _cleanup_(sd_device_unrefp
) sd_device
*fcdev
= NULL
;
112 const char *port
, *sysname
;
113 _cleanup_free_
char *lun
= NULL
;
118 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target", &targetdev
) < 0)
120 if (sd_device_get_sysname(targetdev
, &sysname
) < 0)
122 if (sd_device_new_from_subsystem_sysname(&fcdev
, "fc_transport", sysname
) < 0)
124 if (sd_device_get_sysattr_value(fcdev
, "port_name", &port
) < 0)
127 format_lun_number(parent
, &lun
);
128 path_prepend(path
, "fc-%s-%s", port
, lun
);
132 static sd_device
*handle_scsi_sas_wide_port(sd_device
*parent
, char **path
) {
133 sd_device
*targetdev
, *target_parent
;
134 _cleanup_(sd_device_unrefp
) sd_device
*sasdev
= NULL
;
135 const char *sas_address
, *sysname
;
136 _cleanup_free_
char *lun
= NULL
;
141 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target", &targetdev
) < 0)
143 if (sd_device_get_parent(targetdev
, &target_parent
) < 0)
145 if (sd_device_get_sysname(target_parent
, &sysname
) < 0)
147 if (sd_device_new_from_subsystem_sysname(&sasdev
, "sas_device", sysname
) < 0)
149 if (sd_device_get_sysattr_value(sasdev
, "sas_address", &sas_address
) < 0)
152 format_lun_number(parent
, &lun
);
153 path_prepend(path
, "sas-%s-%s", sas_address
, lun
);
157 static sd_device
*handle_scsi_sas(sd_device
*parent
, char **path
) {
158 sd_device
*targetdev
, *target_parent
, *port
, *expander
;
159 _cleanup_(sd_device_unrefp
) sd_device
*target_sasdev
= NULL
, *expander_sasdev
= NULL
, *port_sasdev
= NULL
;
160 const char *sas_address
= NULL
;
162 const char *phy_count
, *sysname
;
163 _cleanup_free_
char *lun
= NULL
;
168 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target", &targetdev
) < 0)
170 if (sd_device_get_parent(targetdev
, &target_parent
) < 0)
172 if (sd_device_get_sysname(target_parent
, &sysname
) < 0)
175 if (sd_device_new_from_subsystem_sysname(&target_sasdev
, "sas_device", sysname
) < 0)
177 /* The next parent is sas port */
178 if (sd_device_get_parent(target_parent
, &port
) < 0)
180 if (sd_device_get_sysname(port
, &sysname
) < 0)
182 /* Get port device */
183 if (sd_device_new_from_subsystem_sysname(&port_sasdev
, "sas_port", sysname
) < 0)
185 if (sd_device_get_sysattr_value(port_sasdev
, "num_phys", &phy_count
) < 0)
188 /* Check if we are simple disk */
189 if (strncmp(phy_count
, "1", 2) != 0)
190 return handle_scsi_sas_wide_port(parent
, path
);
192 /* Get connected phy */
193 if (sd_device_get_sysattr_value(target_sasdev
, "phy_identifier", &phy_id
) < 0)
196 /* The port's parent is either hba or expander */
197 if (sd_device_get_parent(port
, &expander
) < 0)
200 if (sd_device_get_sysname(expander
, &sysname
) < 0)
202 /* Get expander device */
203 if (sd_device_new_from_subsystem_sysname(&expander_sasdev
, "sas_device", sysname
) >= 0) {
204 /* Get expander's address */
205 if (sd_device_get_sysattr_value(expander_sasdev
, "sas_address", &sas_address
) < 0)
209 format_lun_number(parent
, &lun
);
211 path_prepend(path
, "sas-exp%s-phy%s-%s", sas_address
, phy_id
, lun
);
213 path_prepend(path
, "sas-phy%s-%s", phy_id
, lun
);
218 static sd_device
*handle_scsi_iscsi(sd_device
*parent
, char **path
) {
219 sd_device
*transportdev
;
220 _cleanup_(sd_device_unrefp
) sd_device
*sessiondev
= NULL
, *conndev
= NULL
;
221 const char *target
, *connname
, *addr
, *port
;
222 _cleanup_free_
char *lun
= NULL
;
223 const char *sysname
, *sysnum
;
228 /* find iscsi session */
229 for (transportdev
= parent
; ; ) {
231 if (sd_device_get_parent(transportdev
, &transportdev
) < 0)
233 if (sd_device_get_sysname(transportdev
, &sysname
) < 0)
235 if (startswith(sysname
, "session"))
239 /* find iscsi session device */
240 if (sd_device_new_from_subsystem_sysname(&sessiondev
, "iscsi_session", sysname
) < 0)
243 if (sd_device_get_sysattr_value(sessiondev
, "targetname", &target
) < 0)
246 if (sd_device_get_sysnum(transportdev
, &sysnum
) < 0 || !sysnum
)
248 connname
= strjoina("connection", sysnum
, ":0");
249 if (sd_device_new_from_subsystem_sysname(&conndev
, "iscsi_connection", connname
) < 0)
252 if (sd_device_get_sysattr_value(conndev
, "persistent_address", &addr
) < 0)
254 if (sd_device_get_sysattr_value(conndev
, "persistent_port", &port
) < 0)
257 format_lun_number(parent
, &lun
);
258 path_prepend(path
, "ip-%s:%s-iscsi-%s-%s", addr
, port
, target
, lun
);
262 static sd_device
*handle_scsi_ata(sd_device
*parent
, char **path
, char **compat_path
) {
263 sd_device
*targetdev
, *target_parent
;
264 _cleanup_(sd_device_unrefp
) sd_device
*atadev
= NULL
;
265 const char *port_no
, *sysname
, *name
;
266 unsigned host
, bus
, target
, lun
;
271 if (sd_device_get_sysname(parent
, &name
) < 0)
273 if (sscanf(name
, "%u:%u:%u:%u", &host
, &bus
, &target
, &lun
) != 4)
276 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host", &targetdev
) < 0)
279 if (sd_device_get_parent(targetdev
, &target_parent
) < 0)
282 if (sd_device_get_sysname(target_parent
, &sysname
) < 0)
284 if (sd_device_new_from_subsystem_sysname(&atadev
, "ata_port", sysname
) < 0)
287 if (sd_device_get_sysattr_value(atadev
, "port_no", &port_no
) < 0)
291 /* Devices behind port multiplier have a bus != 0 */
292 path_prepend(path
, "ata-%s.%u.0", port_no
, bus
);
294 /* Master/slave are distinguished by target id */
295 path_prepend(path
, "ata-%s.%u", port_no
, target
);
297 /* old compatible persistent link for ATA devices */
299 path_prepend(compat_path
, "ata-%s", port_no
);
304 static sd_device
*handle_scsi_default(sd_device
*parent
, char **path
) {
306 int host
, bus
, target
, lun
;
307 const char *name
, *base
, *pos
;
308 _cleanup_closedir_
DIR *dir
= NULL
;
314 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host", &hostdev
) < 0)
317 if (sd_device_get_sysname(parent
, &name
) < 0)
319 if (sscanf(name
, "%d:%d:%d:%d", &host
, &bus
, &target
, &lun
) != 4)
323 * Rebase host offset to get the local relative number
325 * Note: This is by definition racy, unreliable and too simple.
326 * Please do not copy this model anywhere. It's just a left-over
327 * from the time we had no idea how things should look like in
330 * Making assumptions about a global in-kernel counter and use
331 * that to calculate a local offset is a very broken concept. It
332 * can only work as long as things are in strict order.
334 * The kernel needs to export the instance/port number of a
335 * controller directly, without the need for rebase magic like
336 * this. Manual driver unbind/bind, parallel hotplug/unplug will
337 * get into the way of this "I hope it works" logic.
340 if (sd_device_get_syspath(hostdev
, &base
) < 0)
342 pos
= strrchr(base
, '/');
346 base
= strndupa_safe(base
, pos
- base
);
351 FOREACH_DIRENT_ALL(de
, dir
, break) {
354 if (de
->d_name
[0] == '.')
356 if (!IN_SET(de
->d_type
, DT_DIR
, DT_LNK
))
358 if (!startswith(de
->d_name
, "host"))
360 if (safe_atou_full(&de
->d_name
[4], 10, &i
) < 0)
363 * find the smallest number; the host really needs to export its
364 * own instance number per parent device; relying on the global host
365 * enumeration and plainly rebasing the numbers sounds unreliable
367 if (basenum
== -1 || (int) i
< basenum
)
374 path_prepend(path
, "scsi-%i:%i:%i:%i", host
, bus
, target
, lun
);
378 static sd_device
*handle_scsi_hyperv(sd_device
*parent
, char **path
, size_t guid_str_len
) {
381 const char *guid_str
;
382 _cleanup_free_
char *lun
= NULL
;
387 assert(guid_str_len
< sizeof(guid
));
389 if (sd_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host", &hostdev
) < 0)
392 if (sd_device_get_parent(hostdev
, &vmbusdev
) < 0)
395 if (sd_device_get_sysattr_value(vmbusdev
, "device_id", &guid_str
) < 0)
398 if (strlen(guid_str
) < guid_str_len
|| guid_str
[0] != '{' || guid_str
[guid_str_len
-1] != '}')
402 for (size_t i
= 1; i
< guid_str_len
-1; i
++) {
403 if (guid_str
[i
] == '-')
405 guid
[k
++] = guid_str
[i
];
409 format_lun_number(parent
, &lun
);
410 path_prepend(path
, "vmbus-%s-%s", guid
, lun
);
414 static sd_device
*handle_scsi(sd_device
*parent
, char **path
, char **compat_path
, bool *supported_parent
) {
415 const char *id
, *name
;
417 if (!device_is_devtype(parent
, "scsi_device"))
421 if (sd_device_get_sysattr_value(parent
, "ieee1394_id", &id
) >= 0) {
422 path_prepend(path
, "ieee1394-0x%s", id
);
423 *supported_parent
= true;
424 return skip_subsystem(parent
, "scsi");
427 /* scsi sysfs does not have a "subsystem" for the transport */
428 if (sd_device_get_syspath(parent
, &name
) < 0)
431 if (strstr(name
, "/rport-")) {
432 *supported_parent
= true;
433 return handle_scsi_fibre_channel(parent
, path
);
436 if (strstr(name
, "/end_device-")) {
437 *supported_parent
= true;
438 return handle_scsi_sas(parent
, path
);
441 if (strstr(name
, "/session")) {
442 *supported_parent
= true;
443 return handle_scsi_iscsi(parent
, path
);
446 if (strstr(name
, "/ata"))
447 return handle_scsi_ata(parent
, path
, compat_path
);
449 if (strstr(name
, "/vmbus_"))
450 return handle_scsi_hyperv(parent
, path
, 37);
451 else if (strstr(name
, "/VMBUS"))
452 return handle_scsi_hyperv(parent
, path
, 38);
454 return handle_scsi_default(parent
, path
);
457 static sd_device
*handle_cciss(sd_device
*parent
, char **path
) {
459 unsigned controller
, disk
;
461 if (sd_device_get_sysname(parent
, &str
) < 0)
463 if (sscanf(str
, "c%ud%u%*s", &controller
, &disk
) != 2)
466 path_prepend(path
, "cciss-disk%u", disk
);
467 return skip_subsystem(parent
, "cciss");
470 static void handle_scsi_tape(sd_device
*dev
, char **path
) {
473 /* must be the last device in the syspath */
477 if (sd_device_get_sysname(dev
, &name
) < 0)
480 if (startswith(name
, "nst") && strchr("lma", name
[3]))
481 path_prepend(path
, "nst%c", name
[3]);
482 else if (startswith(name
, "st") && strchr("lma", name
[2]))
483 path_prepend(path
, "st%c", name
[2]);
486 static int get_usb_revision(sd_device
*dev
) {
493 /* Returns usb revision 1, 2, or 3. */
495 r
= sd_device_get_sysattr_value(dev
, "bDeviceProtocol", &s
);
499 r
= safe_atou8_full(s
, 16, &protocol
);
504 case USB_HUB_PR_HS_NO_TT
: /* Full speed hub (USB1) or Hi-speed hub without TT (USB2) */
506 /* See speed_show() in drivers/usb/core/sysfs.c of the kernel. */
507 r
= sd_device_get_sysattr_value(dev
, "speed", &s
);
516 case USB_HUB_PR_HS_SINGLE_TT
: /* Hi-speed hub with single TT */
517 case USB_HUB_PR_HS_MULTI_TT
: /* Hi-speed hub with multiple TT */
520 case USB_HUB_PR_SS
: /* Super speed hub */
524 return -EPROTONOSUPPORT
;
528 static sd_device
*handle_usb(sd_device
*parent
, char **path
) {
529 const char *str
, *port
;
532 if (!device_is_devtype(parent
, "usb_interface") && !device_is_devtype(parent
, "usb_device"))
535 if (sd_device_get_sysname(parent
, &str
) < 0)
537 port
= strchr(str
, '-');
542 parent
= skip_subsystem(parent
, "usb");
546 /* USB host number may change across reboots (and probably even without reboot). The part after USB
547 * host number is determined by device topology and so does not change. Hence, drop the host number
548 * and always use '0' instead.
550 * xHCI host controllers may register two (or more?) USB root hubs for USB 2.0 and USB 3.0, and the
551 * sysname, whose host number replaced with 0, of a device under the hubs may conflict with others.
552 * To avoid the conflict, let's include the USB revision of the root hub to the PATH_ID.
553 * See issue https://github.com/systemd/systemd/issues/19406 for more details. */
554 r
= get_usb_revision(parent
);
556 log_device_debug_errno(parent
, r
, "Failed to get the USB revision number, ignoring: %m");
557 path_prepend(path
, "usb-0:%s", port
);
560 path_prepend(path
, "usbv%i-0:%s", r
, port
);
566 static sd_device
*handle_bcma(sd_device
*parent
, char **path
) {
570 if (sd_device_get_sysname(parent
, &sysname
) < 0)
572 if (sscanf(sysname
, "bcma%*u:%u", &core
) != 1)
575 path_prepend(path
, "bcma-%u", core
);
579 /* Handle devices of AP bus in System z platform. */
580 static sd_device
*handle_ap(sd_device
*parent
, char **path
) {
581 const char *type
, *func
;
586 if (sd_device_get_sysattr_value(parent
, "type", &type
) >= 0 &&
587 sd_device_get_sysattr_value(parent
, "ap_functions", &func
) >= 0)
588 path_prepend(path
, "ap-%s-%s", type
, func
);
592 if (sd_device_get_sysname(parent
, &sysname
) >= 0)
593 path_prepend(path
, "ap-%s", sysname
);
596 return skip_subsystem(parent
, "ap");
599 static int find_real_nvme_parent(sd_device
*dev
, sd_device
**ret
) {
600 _cleanup_(sd_device_unrefp
) sd_device
*nvme
= NULL
;
601 const char *sysname
, *end
, *devpath
;
604 /* If the device belongs to "nvme-subsystem" (not to be confused with "nvme"), which happens when
605 * NVMe multipathing is enabled in the kernel (/sys/module/nvme_core/parameters/multipath is Y),
606 * then the syspath is something like the following:
607 * /sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1
608 * Hence, we need to find the 'real parent' in "nvme" subsystem, e.g,
609 * /sys/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0 */
614 r
= sd_device_get_sysname(dev
, &sysname
);
618 /* The sysname format of nvme block device is nvme%d[c%d]n%d[p%d], e.g. nvme0n1p2 or nvme0c1n2.
619 * (Note, nvme device with 'c' can be ignored, as they are hidden. )
620 * The sysname format of nvme subsystem device is nvme%d.
621 * See nvme_alloc_ns() and nvme_init_ctrl() in drivers/nvme/host/core.c for more details. */
622 end
= startswith(sysname
, "nvme");
626 end
+= strspn(end
, DIGITS
);
627 sysname
= strndupa_safe(sysname
, end
- sysname
);
629 r
= sd_device_new_from_subsystem_sysname(&nvme
, "nvme", sysname
);
633 r
= sd_device_get_devpath(nvme
, &devpath
);
637 /* If the 'real parent' is (still) virtual, e.g. for nvmf disks, refuse to set ID_PATH. */
638 if (path_startswith(devpath
, "/devices/virtual/"))
641 *ret
= TAKE_PTR(nvme
);
645 static void add_id_with_usb_revision(sd_device
*dev
, bool test
, char *path
) {
652 /* When the path contains the USB revision, let's adds ID_PATH_WITH_USB_REVISION property and
653 * drop the version specifier for later use. */
655 p
= strstrafter(path
, "-usbv");
658 if (!ascii_isdigit(p
[0]))
663 r
= udev_builtin_add_property(dev
, test
, "ID_PATH_WITH_USB_REVISION", path
);
665 log_device_debug_errno(dev
, r
, "Failed to add ID_PATH_WITH_USB_REVISION property, ignoring: %m");
667 /* Drop the USB revision specifier for backward compatibility. */
668 memmove(p
- 1, p
+ 1, strlen(p
+ 1) + 1);
671 static void add_id_tag(sd_device
*dev
, bool test
, const char *path
) {
672 char tag
[UDEV_NAME_SIZE
];
676 /* compose valid udev tag name */
677 for (const char *p
= path
; *p
; p
++) {
678 if (ascii_isdigit(*p
) ||
685 /* skip all leading '_' */
689 /* avoid second '_' */
695 /* strip trailing '_' */
696 while (i
> 0 && tag
[i
-1] == '_')
700 r
= udev_builtin_add_property(dev
, test
, "ID_PATH_TAG", tag
);
702 log_device_debug_errno(dev
, r
, "Failed to add ID_PATH_TAG property, ignoring: %m");
705 static int builtin_path_id(UdevEvent
*event
, int argc
, char *argv
[], bool test
) {
706 sd_device
*dev
= ASSERT_PTR(ASSERT_PTR(event
)->dev
);
707 _cleanup_(sd_device_unrefp
) sd_device
*dev_other_branch
= NULL
;
708 _cleanup_free_
char *path
= NULL
, *compat_path
= NULL
;
709 bool supported_transport
= false, supported_parent
= false;
712 /* walk up the chain of devices and compose path */
713 for (sd_device
*parent
= dev
; parent
; ) {
716 if (sd_device_get_sysname(parent
, &sysname
) < 0) {
718 } else if (device_in_subsystem(parent
, "scsi_tape")) {
719 handle_scsi_tape(parent
, &path
);
720 } else if (device_in_subsystem(parent
, "scsi")) {
721 parent
= handle_scsi(parent
, &path
, &compat_path
, &supported_parent
);
722 supported_transport
= true;
723 } else if (device_in_subsystem(parent
, "cciss")) {
724 parent
= handle_cciss(parent
, &path
);
725 supported_transport
= true;
726 } else if (device_in_subsystem(parent
, "usb")) {
727 parent
= handle_usb(parent
, &path
);
728 supported_transport
= true;
729 } else if (device_in_subsystem(parent
, "bcma")) {
730 parent
= handle_bcma(parent
, &path
);
731 supported_transport
= true;
732 } else if (device_in_subsystem(parent
, "serio")) {
735 if (sd_device_get_sysnum(parent
, &sysnum
) >= 0 && sysnum
) {
736 path_prepend(&path
, "serio-%s", sysnum
);
737 parent
= skip_subsystem(parent
, "serio");
739 } else if (device_in_subsystem(parent
, "pci")) {
740 path_prepend(&path
, "pci-%s", sysname
);
742 path_prepend(&compat_path
, "pci-%s", sysname
);
743 parent
= skip_subsystem(parent
, "pci");
744 supported_parent
= true;
745 } else if (device_in_subsystem(parent
, "platform")) {
746 path_prepend(&path
, "platform-%s", sysname
);
748 path_prepend(&compat_path
, "platform-%s", sysname
);
749 parent
= skip_subsystem(parent
, "platform");
750 supported_transport
= true;
751 supported_parent
= true;
752 } else if (device_in_subsystem(parent
, "amba")) {
753 path_prepend(&path
, "amba-%s", sysname
);
755 path_prepend(&compat_path
, "amba-%s", sysname
);
756 parent
= skip_subsystem(parent
, "amba");
757 supported_transport
= true;
758 supported_parent
= true;
759 } else if (device_in_subsystem(parent
, "acpi")) {
760 path_prepend(&path
, "acpi-%s", sysname
);
762 path_prepend(&compat_path
, "acpi-%s", sysname
);
763 parent
= skip_subsystem(parent
, "acpi");
764 supported_parent
= true;
765 } else if (device_in_subsystem(parent
, "xen")) {
766 path_prepend(&path
, "xen-%s", sysname
);
768 path_prepend(&compat_path
, "xen-%s", sysname
);
769 parent
= skip_subsystem(parent
, "xen");
770 supported_parent
= true;
771 } else if (device_in_subsystem(parent
, "virtio")) {
772 parent
= skip_subsystem(parent
, "virtio");
773 supported_transport
= true;
774 } else if (device_in_subsystem(parent
, "scm")) {
775 path_prepend(&path
, "scm-%s", sysname
);
777 path_prepend(&compat_path
, "scm-%s", sysname
);
778 parent
= skip_subsystem(parent
, "scm");
779 supported_transport
= true;
780 supported_parent
= true;
781 } else if (device_in_subsystem(parent
, "ccw")) {
782 path_prepend(&path
, "ccw-%s", sysname
);
784 path_prepend(&compat_path
, "ccw-%s", sysname
);
785 parent
= skip_subsystem(parent
, "ccw");
786 supported_transport
= true;
787 supported_parent
= true;
788 } else if (device_in_subsystem(parent
, "ccwgroup")) {
789 path_prepend(&path
, "ccwgroup-%s", sysname
);
791 path_prepend(&compat_path
, "ccwgroup-%s", sysname
);
792 parent
= skip_subsystem(parent
, "ccwgroup");
793 supported_transport
= true;
794 supported_parent
= true;
795 } else if (device_in_subsystem(parent
, "ap")) {
796 parent
= handle_ap(parent
, &path
);
797 supported_transport
= true;
798 supported_parent
= true;
799 } else if (device_in_subsystem(parent
, "iucv")) {
800 path_prepend(&path
, "iucv-%s", sysname
);
802 path_prepend(&compat_path
, "iucv-%s", sysname
);
803 parent
= skip_subsystem(parent
, "iucv");
804 supported_transport
= true;
805 supported_parent
= true;
806 } else if (device_in_subsystem(parent
, "nvme") || device_in_subsystem(parent
, "nvme-subsystem")) {
809 if (sd_device_get_sysattr_value(dev
, "nsid", &nsid
) >= 0) {
810 path_prepend(&path
, "nvme-%s", nsid
);
812 path_prepend(&compat_path
, "nvme-%s", nsid
);
814 if (device_in_subsystem(parent
, "nvme-subsystem")) {
815 r
= find_real_nvme_parent(dev
, &dev_other_branch
);
819 parent
= dev_other_branch
;
822 parent
= skip_subsystem(parent
, "nvme");
823 supported_parent
= true;
824 supported_transport
= true;
826 } else if (device_in_subsystem(parent
, "spi")) {
829 if (sd_device_get_sysnum(parent
, &sysnum
) >= 0 && sysnum
) {
830 path_prepend(&path
, "cs-%s", sysnum
);
831 parent
= skip_subsystem(parent
, "spi");
837 if (sd_device_get_parent(parent
, &parent
) < 0)
845 * Do not return devices with an unknown parent device type. They
846 * might produce conflicting IDs if the parent does not provide a
847 * unique and predictable name.
849 if (!supported_parent
)
853 * Do not return block devices without a well-known transport. Some
854 * devices do not expose their buses and do not provide a unique
855 * and predictable name that way.
857 if (device_in_subsystem(dev
, "block") && !supported_transport
)
860 add_id_with_usb_revision(dev
, test
, path
);
862 r
= udev_builtin_add_property(dev
, test
, "ID_PATH", path
);
864 log_device_debug_errno(dev
, r
, "Failed to add ID_PATH property, ignoring: %m");
866 add_id_tag(dev
, test
, path
);
869 * Compatible link generation for ATA devices
870 * we assign compat_link to the env variable
874 udev_builtin_add_property(dev
, test
, "ID_PATH_ATA_COMPAT", compat_path
);
879 const UdevBuiltin udev_builtin_path_id
= {
881 .cmd
= builtin_path_id
,
882 .help
= "Compose persistent device path",