]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/lsblk-properties.c
lsblk: read ID_SCSI_IDENT_SERIAL if available
[thirdparty/util-linux.git] / misc-utils / lsblk-properties.c
1
2 #include <blkid.h>
3
4 #ifdef HAVE_LIBUDEV
5 # include <libudev.h>
6 #endif
7
8 #include "c.h"
9 #include "xalloc.h"
10 #include "mangle.h"
11 #include "path.h"
12 #include "nls.h"
13
14 #include "lsblk.h"
15
16 #ifdef HAVE_LIBUDEV
17 static struct udev *udev;
18 #endif
19
20 void lsblk_device_free_properties(struct lsblk_devprop *p)
21 {
22 if (!p)
23 return;
24
25 free(p->fstype);
26 free(p->fsversion);
27 free(p->uuid);
28 free(p->ptuuid);
29 free(p->pttype);
30 free(p->label);
31 free(p->parttype);
32 free(p->partuuid);
33 free(p->partlabel);
34 free(p->wwn);
35 free(p->serial);
36 free(p->model);
37 free(p->partflags);
38
39 free(p->mode);
40 free(p->owner);
41 free(p->group);
42
43 free(p);
44 }
45
46 #ifndef HAVE_LIBUDEV
47 static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *dev
48 __attribute__((__unused__)))
49 {
50 return NULL;
51 }
52 #else
53 static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *ld)
54 {
55 struct udev_device *dev;
56
57 if (ld->udev_requested)
58 return ld->properties;
59
60 if (!udev)
61 udev = udev_new(); /* global handler */
62 if (!udev)
63 goto done;
64
65 dev = udev_device_new_from_subsystem_sysname(udev, "block", ld->name);
66 if (dev) {
67 const char *data;
68 struct lsblk_devprop *prop;
69
70 if (ld->properties)
71 lsblk_device_free_properties(ld->properties);
72 prop = ld->properties = xcalloc(1, sizeof(*ld->properties));
73
74 if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) {
75 prop->label = xstrdup(data);
76 unhexmangle_string(prop->label);
77 }
78 if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) {
79 prop->uuid = xstrdup(data);
80 unhexmangle_string(prop->uuid);
81 }
82 if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_UUID")))
83 prop->ptuuid = xstrdup(data);
84 if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_TYPE")))
85 prop->pttype = xstrdup(data);
86 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) {
87 prop->partlabel = xstrdup(data);
88 unhexmangle_string(prop->partlabel);
89 }
90 if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
91 prop->fstype = xstrdup(data);
92 if ((data = udev_device_get_property_value(dev, "ID_FS_VERSION")))
93 prop->fsversion = xstrdup(data);
94 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_TYPE")))
95 prop->parttype = xstrdup(data);
96 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID")))
97 prop->partuuid = xstrdup(data);
98 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_FLAGS")))
99 prop->partflags = xstrdup(data);
100
101 data = udev_device_get_property_value(dev, "ID_WWN_WITH_EXTENSION");
102 if (!data)
103 data = udev_device_get_property_value(dev, "ID_WWN");
104 if (data)
105 prop->wwn = xstrdup(data);
106
107 data = udev_device_get_property_value(dev, "ID_SCSI_SERIAL");
108 if(!data)
109 data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT");
110 if(!data)
111 data = udev_device_get_property_value(dev, "ID_SERIAL");
112 if (data)
113 prop->serial = xstrdup(data);
114
115 if ((data = udev_device_get_property_value(dev, "ID_MODEL")))
116 prop->model = xstrdup(data);
117
118 udev_device_unref(dev);
119 DBG(DEV, ul_debugobj(ld, "%s: found udev properties", ld->name));
120 }
121
122 done:
123 ld->udev_requested = 1;
124
125 DBG(DEV, ul_debugobj(ld, " from udev"));
126 return ld->properties;
127 }
128 #endif /* HAVE_LIBUDEV */
129
130
131 static int lookup(char *buf, char *pattern, char **value)
132 {
133 char *p, *v;
134 int len;
135
136 /* do not re-fill value */
137 if (!buf || *value)
138 return 0;
139
140 len = strlen(pattern);
141 if (strncmp(buf, pattern, len) != 0)
142 return 0;
143
144 p = buf + len;
145 if (*p != '=')
146 return 0;
147 p++;
148 if (!*p || *p == '\n')
149 return 0;
150 v = p;
151 for (; *p && *p != '\n'; p++) ;
152 if (*p == '\n')
153 *p = '\0';
154
155 *value = xstrdup(v);
156 return 1;
157 }
158
159 /* read device properties from fake text file (used on --sysroot) */
160 static struct lsblk_devprop *get_properties_by_file(struct lsblk_device *ld)
161 {
162 struct lsblk_devprop *prop;
163 struct path_cxt *pc;
164 FILE *fp = NULL;
165 struct stat sb;
166 char buf[BUFSIZ];
167
168 assert(lsblk->sysroot);
169
170 if (ld->file_requested)
171 return ld->properties;
172
173 if (ld->properties || ld->filename) {
174 lsblk_device_free_properties(ld->properties);
175 ld->properties = NULL;
176 }
177
178 pc = ul_new_path("/");
179 if (!pc)
180 return NULL;
181 if (ul_path_set_prefix(pc, lsblk->sysroot) != 0)
182 goto done;
183 if (ul_path_stat(pc, &sb, ld->filename) != 0 || !S_ISREG(sb.st_mode))
184 goto done;
185
186 fp = ul_path_fopen(pc, "r", ld->filename);
187 if (!fp)
188 goto done;
189
190 prop = ld->properties;
191 if (!prop)
192 prop = ld->properties = xcalloc(1, sizeof(*ld->properties));
193
194 while (fgets(buf, sizeof(buf), fp) != NULL) {
195 /* udev based */
196 if (lookup(buf, "ID_FS_LABEL_ENC", &prop->label))
197 unhexmangle_string(prop->label);
198 else if (lookup(buf, "ID_FS_UUID_ENC", &prop->uuid))
199 unhexmangle_string(prop->uuid);
200 else if (lookup(buf, "ID_PART_ENTRY_NAME", &prop->partlabel))
201 unhexmangle_string(prop->partlabel);
202 else if (lookup(buf, "ID_PART_TABLE_UUID", &prop->ptuuid)) ;
203 else if (lookup(buf, "ID_PART_TABLE_TYPE", &prop->pttype)) ;
204 else if (lookup(buf, "ID_FS_TYPE", &prop->fstype)) ;
205 else if (lookup(buf, "ID_FS_VERSION", &prop->fsversion)) ;
206 else if (lookup(buf, "ID_PART_ENTRY_TYPE", &prop->parttype)) ;
207 else if (lookup(buf, "ID_PART_ENTRY_UUID", &prop->partuuid)) ;
208 else if (lookup(buf, "ID_PART_ENTRY_FLAGS", &prop->partflags)) ;
209 else if (lookup(buf, "ID_MODEL", &prop->model)) ;
210 else if (lookup(buf, "ID_WWN_WITH_EXTENSION", &prop->wwn)) ;
211 else if (lookup(buf, "ID_WWN", &prop->wwn)) ;
212 else if (lookup(buf, "ID_SCSI_IDENT_SERIAL", &prop->serial)) ; /* serial from sg3_utils */
213 else if (lookup(buf, "ID_SCSI_SERIAL", &prop->serial)) ;
214 else if (lookup(buf, "ID_SERIAL_SHORT", &prop->serial)) ;
215 else if (lookup(buf, "ID_SERIAL", &prop->serial)) ;
216
217 /* lsblk specific */
218 else if (lookup(buf, "MODE", &prop->mode)) ;
219 else if (lookup(buf, "OWNER", &prop->owner)) ;
220 else if (lookup(buf, "GROUP", &prop->group)) ;
221
222 else
223 continue;
224 }
225 done:
226 if (fp)
227 fclose(fp);
228 ul_unref_path(pc);
229 ld->file_requested = 1;
230
231 DBG(DEV, ul_debugobj(ld, " from fake-file"));
232 return ld->properties;
233 }
234
235
236 static struct lsblk_devprop *get_properties_by_blkid(struct lsblk_device *dev)
237 {
238 blkid_probe pr = NULL;
239
240 if (dev->blkid_requested)
241 return dev->properties;
242
243 if (!dev->size)
244 goto done;
245 if (getuid() != 0)
246 goto done;; /* no permissions to read from the device */
247
248 pr = blkid_new_probe_from_filename(dev->filename);
249 if (!pr)
250 goto done;
251
252 blkid_probe_enable_superblocks(pr, 1);
253 blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
254 BLKID_SUBLKS_UUID |
255 BLKID_SUBLKS_TYPE);
256 blkid_probe_enable_partitions(pr, 1);
257 blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
258
259 if (!blkid_do_safeprobe(pr)) {
260 const char *data = NULL;
261 struct lsblk_devprop *prop;
262
263 if (dev->properties)
264 lsblk_device_free_properties(dev->properties);
265 prop = dev->properties = xcalloc(1, sizeof(*dev->properties));
266
267 if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
268 prop->fstype = xstrdup(data);
269 if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
270 prop->uuid = xstrdup(data);
271 if (!blkid_probe_lookup_value(pr, "PTUUID", &data, NULL))
272 prop->ptuuid = xstrdup(data);
273 if (!blkid_probe_lookup_value(pr, "PTTYPE", &data, NULL))
274 prop->pttype = xstrdup(data);
275 if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
276 prop->label = xstrdup(data);
277 if (!blkid_probe_lookup_value(pr, "VERSION", &data, NULL))
278 prop->fsversion = xstrdup(data);
279 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &data, NULL))
280 prop->parttype = xstrdup(data);
281 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL))
282 prop->partuuid = xstrdup(data);
283 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL))
284 prop->partlabel = xstrdup(data);
285 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_FLAGS", &data, NULL))
286 prop->partflags = xstrdup(data);
287
288 DBG(DEV, ul_debugobj(dev, "%s: found blkid properties", dev->name));
289 }
290
291 done:
292 blkid_free_probe(pr);
293
294 DBG(DEV, ul_debugobj(dev, " from blkid"));
295 dev->blkid_requested = 1;
296 return dev->properties;
297 }
298
299 struct lsblk_devprop *lsblk_device_get_properties(struct lsblk_device *dev)
300 {
301 struct lsblk_devprop *p = NULL;
302
303 DBG(DEV, ul_debugobj(dev, "%s: properties requested", dev->filename));
304 if (lsblk->sysroot)
305 return get_properties_by_file(dev);
306
307 p = get_properties_by_udev(dev);
308 if (!p)
309 p = get_properties_by_blkid(dev);
310 return p;
311 }
312
313 void lsblk_properties_deinit(void)
314 {
315 #ifdef HAVE_LIBUDEV
316 udev_unref(udev);
317 #endif
318 }
319
320
321
322 /*
323 * Partition types
324 */
325 struct lsblk_parttype {
326 unsigned int code; /* type as number or zero */
327 char *name; /* description */
328 char *typestr; /* type as string or NULL */
329 };
330
331 static const struct lsblk_parttype mbr_types[] =
332 {
333 #include "pt-mbr-partnames.h"
334 };
335
336 #define DEF_GUID(_u, _n) \
337 { \
338 .typestr = (_u), \
339 .name = (_n), \
340 }
341 static const struct lsblk_parttype gpt_types[] =
342 {
343 #include "pt-gpt-partnames.h"
344 };
345
346 const char *lsblk_parttype_code_to_string(const char *code, const char *pttype)
347 {
348 size_t i;
349
350 if (!code || !pttype)
351 return NULL;
352
353 if (strcmp(pttype, "dos") == 0 || strcmp(pttype, "mbr") == 0) {
354 char *end = NULL;
355 unsigned int xcode;
356
357 errno = 0;
358 xcode = strtol(code, &end, 16);
359
360 if (errno || *end != '\0')
361 return NULL;
362
363 for (i = 0; i < ARRAY_SIZE(mbr_types); i++) {
364 const struct lsblk_parttype *t = &mbr_types[i];
365
366 if (t->name && t->code == xcode)
367 return t->name;
368 }
369
370 } else if (strcmp(pttype, "gpt") == 0) {
371 for (i = 0; i < ARRAY_SIZE(gpt_types); i++) {
372 const struct lsblk_parttype *t = &gpt_types[i];
373
374 if (t->name && t->typestr &&
375 strcasecmp(code, t->typestr) == 0)
376 return t->name;
377 }
378 }
379
380 return NULL;
381 }