Package: efivar / 37-6

0003-Try-even-harder-to-find-disk-device-symlinks-in-sysf.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
From c9c1424d0e09bf33b747d37c43177c63367b1290 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Fri, 11 Oct 2019 14:20:54 -0400
Subject: [PATCH] Try even harder to find disk device symlinks in sysfs.

Today's realization is that the thing encoded into the structure of
sysfs is, in the best case, the dependency graph of the makefile targets
to build a device driver.

In the case of nvme-fabric, or really wherever the kernel has
class_create() and device_create() in the same function, there's an
extra level of indirection.

Anyway, in this patch we stop pretending sysfs isn't completely absurd,
and just try adding "/device" in the middle of the driver symlink path,
until we actually either get ENOENT on the device symlink or find a
device symlink that actually has a driver symlink under it.

Signed-off-by: Peter Jones <pjones@redhat.com>

[ dannf: Context adjustments due to upstream whitespace cleanup;
  Adjust to omit efi_error_pop() call that didn't exist in v37;
  included to simplify application of later patches in series ]

Bug-Ubuntu: https://bugs.launchpad.net/bugs/1891718
Bug: https://github.com/rhboot/efivar/issues/157
Origin: upstream, https://github.com/rhboot/efivar/commit/c9c1424d0e09bf33b747d37c43177c63367b1290
Last-Updated: 2020-09-30

Index: efivar-37/src/linux-nvme.c
===================================================================
--- efivar-37.orig/src/linux-nvme.c
+++ efivar-37/src/linux-nvme.c
@@ -88,13 +88,12 @@ parse_nvme(struct device *dev, const cha
         /*
          * now fish the eui out of sysfs is there is one...
          */
-        rc = read_sysfs_file(&filebuf,
-                             "class/block/nvme%dn%d/eui",
-                             ctrl_id, ns_id);
-        if ((rc < 0 && errno == ENOENT) || filebuf == NULL) {
-                rc = read_sysfs_file(&filebuf,
-                             "class/block/nvme%dn%d/device/eui",
-                             ctrl_id, ns_id);
+	char *euipath = NULL;
+	rc = read_sysfs_file(&filebuf, "class/block/nvme%dn%d/eui", ctrl_id, ns_id);
+	if (rc < 0 && (errno == ENOENT || errno == ENOTDIR)) {
+		rc = find_device_file(&euipath, "eui", "class/block/nvme%dn%d", ctrl_id, ns_id);
+		if (rc >= 0 && euipath != NULL)
+			rc = read_sysfs_file(&filebuf, "%s", euipath);
         }
         if (rc >= 0 && filebuf != NULL) {
                 uint8_t eui[8];
Index: efivar-37/src/linux.c
===================================================================
--- efivar-37.orig/src/linux.c
+++ efivar-37/src/linux.c
@@ -410,24 +410,32 @@ struct device HIDDEN
                 goto err;
         }
 
-        if (dev->device[0] != 0) {
-                rc = sysfs_readlink(&tmpbuf, "block/%s/device/driver", dev->disk_name);
+	/*
+	 * So, on a normal disk, you get something like:
+	 * /sys/block/sda/device -> ../../0:0:0:0
+	 * /sys/block/sda/device/driver -> ../../../../../../../bus/scsi/drivers/sd
+	 *
+	 * On a directly attached nvme device you get:
+	 * /sys/block/nvme0n1/device -> ../../nvme0
+	 * /sys/block/nvme0n1/device/device -> ../../../0000:6e:00.0
+	 * /sys/block/nvme0n1/device/device/driver -> ../../../../bus/pci/drivers/nvme
+	 *
+	 * On a fabric-attached nvme device, you get something like:
+	 * /sys/block/nvme0n1/device -> ../../nvme0
+	 * /sys/block/nvme0n1/device/device -> ../../ctl
+	 * /sys/block/nvme0n1/device/device/device -> ../../../../../0000:6e:00.0
+	 * /sys/block/nvme0n1/device/device/device/driver -> ../../../../../../bus/pci/drivers/nvme-fabrics
+	 *
+	 * ... I think?  I don't have one in front of me.
+	 */
+
+	char *filepath = NULL;
+	rc = find_device_file(&filepath, "driver", "block/%s", dev->disk_name);
+	if (rc >= 0) {
+		rc = sysfs_readlink(&tmpbuf, "%s", filepath);
                 if (rc < 0 || !tmpbuf) {
-                        if (errno == ENOENT) {
-                                /*
-                                 * nvme, for example, will have nvme0n1/device point
-                                 * at nvme0, and we need to look for device/driver
-                                 * there.
-                                 */
-                                rc = sysfs_readlink(&tmpbuf,
-                                                    "block/%s/device/device/driver",
-                                                    dev->disk_name);
-                        }
-                        if (rc < 0 || !tmpbuf) {
-                                efi_error("readlink of /sys/block/%s/device/driver failed",
-                                          dev->disk_name);
-                                goto err;
-                        }
+			efi_error("readlink of /sys/%s failed", filepath);
+	                goto err;
                 }
 
                 linkbuf = pathseg(tmpbuf, -1);
@@ -438,7 +446,7 @@ struct device HIDDEN
 
                 dev->driver = strdup(linkbuf);
         } else {
-                dev->driver = strdup("");
+		dev->driver = strdup("");
         }
 
         if (!dev->driver) {
Index: efivar-37/src/linux.h
===================================================================
--- efivar-37.orig/src/linux.h
+++ efivar-37/src/linux.h
@@ -1,6 +1,6 @@
 /*
  * libefiboot - library for the manipulation of EFI boot variables
- * Copyright 2012-2015 Red Hat, Inc.
+ * Copyright 2012-2019 Red Hat, Inc.
  * Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.com>
  *
  * This library is free software; you can redistribute it and/or
@@ -196,6 +196,22 @@ extern ssize_t HIDDEN make_mac_path(uint
                 bufsize_;                                               \
         })
 
+#define sysfs_access(mode, fmt, args...)				\
+	({								\
+		int rc_;						\
+		char *pn_;						\
+									\
+		rc_ = asprintfa(&pn_, "/sys/" fmt, ## args);		\
+		if (rc_ >= 0) {						\
+			rc_ = access(pn_, mode);			\
+			if (rc_ < 0)					\
+				efi_error("could not access %s", pn_);  \
+		} else {						\
+			efi_error("could not allocate memory");		\
+		}							\
+		rc_;							\
+	})
+
 #define sysfs_readlink(linkbuf, fmt, args...)                           \
         ({                                                              \
                 char *_lb = alloca(PATH_MAX+1);                         \
@@ -251,6 +267,57 @@ extern ssize_t HIDDEN make_mac_path(uint
                 dir_;                                                   \
         })
 
+/*
+ * Iterate a /sys/block directory looking for device/foo, device/device/foo,
+ * etc.  I'm not proud of this method.
+ */
+#define find_device_file(result, name, fmt, args...)				\
+	({									\
+		int rc_ = 0;							\
+		debug("searching for %s from in %s", name, dev->disk_name);	\
+		for (unsigned int try_ = 0; true; try_++) {			\
+			char slashdev_[sizeof("device")				\
+				       + try_ * strlen("/device")];		\
+										\
+			char *nul_ = stpcpy(slashdev_, "device");		\
+			for (unsigned int i_ = 0; i_ < try_; i_++)		\
+				nul_ = stpcpy(nul_, "/device");			\
+										\
+			debug("trying /sys/" fmt "/%s/%s",			\
+			      ## args, slashdev_, name);			\
+										\
+			rc_ = sysfs_access(F_OK, fmt "/%s", ## args, slashdev_);\
+			if (rc_ < 0) {						\
+				if (errno == ENOENT) {				\
+					break;					\
+				}						\
+				efi_error("cannot access /sys/"fmt"/%s: %m",	\
+					  ## args, slashdev_);			\
+				goto find_device_link_err_;			\
+			}							\
+										\
+			rc_ = sysfs_access(F_OK, fmt "/%s/%s",			\
+					   ## args, slashdev_, name);		\
+			if (rc_ < 0) {						\
+				if (errno == ENOENT) {				\
+					break;					\
+				}						\
+				efi_error("cannot access /sys/"fmt"/%s/%s: %m",	\
+					  ## args, slashdev_, name);		\
+				goto find_device_link_err_;			\
+			}							\
+										\
+			rc_ = asprintfa(result, fmt "/%s/%s",			\
+					## args, slashdev_, name);		\
+			if (rc_ < 0) {						\
+				efi_error("cannot allocate memory: %m");	\
+				goto find_device_link_err_;			\
+			}							\
+		}								\
+find_device_link_err_:								\
+		rc_;								\
+	})
+
 #define DEV_PROVIDES_ROOT       1
 #define DEV_PROVIDES_HD         2
 #define DEV_ABBREV_ONLY         4