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