]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsblk-properties.c
lsblk: add ID-LINK column
[thirdparty/util-linux.git] / misc-utils / lsblk-properties.c
CommitLineData
ccafadb7
KZ
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"
7408a5d9 11#include "path.h"
107e9559 12#include "nls.h"
1775aaf1 13#include "strutils.h"
ccafadb7
KZ
14
15#include "lsblk.h"
16
17#ifdef HAVE_LIBUDEV
18static struct udev *udev;
19#endif
20
21void lsblk_device_free_properties(struct lsblk_devprop *p)
22{
23 if (!p)
24 return;
25
26 free(p->fstype);
9cca1ef2 27 free(p->fsversion);
ccafadb7
KZ
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);
7408a5d9 38 free(p->partflags);
ef7d50a0 39 free(p->byid);
ccafadb7 40
6f74ede5
KZ
41 free(p->mode);
42 free(p->owner);
43 free(p->group);
44
ccafadb7
KZ
45 free(p);
46}
47
48#ifndef HAVE_LIBUDEV
fed34a1e 49static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *dev
ccafadb7
KZ
50 __attribute__((__unused__)))
51{
52 return NULL;
53}
54#else
ef7d50a0
KZ
55
56#define LSBLK_UDEV_BYID_PREFIX "/dev/disk/by-id/"
57#define LSBLK_UDEV_BYID_PREFIXSZ (sizeof(LSBLK_UDEV_BYID_PREFIX) - 1)
58
fed34a1e 59static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *ld)
ccafadb7
KZ
60{
61 struct udev_device *dev;
ef7d50a0 62 struct udev_list_entry *le;
d1574dae
KZ
63 const char *data;
64 struct lsblk_devprop *prop;
ef7d50a0 65 size_t len;
ccafadb7 66
fed34a1e
KZ
67 if (ld->udev_requested)
68 return ld->properties;
ccafadb7 69
ccafadb7
KZ
70 if (!udev)
71 udev = udev_new(); /* global handler */
72 if (!udev)
73 goto done;
74
fed34a1e 75 dev = udev_device_new_from_subsystem_sysname(udev, "block", ld->name);
d1574dae
KZ
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);
ccafadb7
KZ
137 }
138
ef7d50a0
KZ
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->byid);
154 prop->byid = xstrdup(name);
155 }
156 }
157
d1574dae 158 udev_device_unref(dev);
ccafadb7 159done:
fed34a1e 160 ld->udev_requested = 1;
7408a5d9
KZ
161
162 DBG(DEV, ul_debugobj(ld, " from udev"));
fed34a1e 163 return ld->properties;
ccafadb7
KZ
164}
165#endif /* HAVE_LIBUDEV */
166
7408a5d9
KZ
167
168static 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);
ad296391 178 if (strncmp(buf, pattern, len) != 0)
7408a5d9
KZ
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) */
197static 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
eea06b92
KZ
205 assert(lsblk->sysroot);
206
7408a5d9
KZ
207 if (ld->file_requested)
208 return ld->properties;
209
7408a5d9
KZ
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;
db9ad223 220 if (ul_path_stat(pc, &sb, 0, ld->filename) != 0 || !S_ISREG(sb.st_mode))
7408a5d9
KZ
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) {
6f74ede5 232 /* udev based */
7408a5d9
KZ
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)) ;
9cca1ef2 242 else if (lookup(buf, "ID_FS_VERSION", &prop->fsversion)) ;
7408a5d9
KZ
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)) ;
b95752ad 249 else if (lookup(buf, "SCSI_IDENT_SERIAL", &prop->serial)) ; /* serial from sg3_utils */
7408a5d9
KZ
250 else if (lookup(buf, "ID_SCSI_SERIAL", &prop->serial)) ;
251 else if (lookup(buf, "ID_SERIAL_SHORT", &prop->serial)) ;
e81d0f80 252 else if (lookup(buf, "ID_SERIAL", &prop->serial)) ;
6f74ede5
KZ
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
7408a5d9
KZ
259 else
260 continue;
261 }
262done:
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
fed34a1e 273static struct lsblk_devprop *get_properties_by_blkid(struct lsblk_device *dev)
ccafadb7
KZ
274{
275 blkid_probe pr = NULL;
276
fed34a1e
KZ
277 if (dev->blkid_requested)
278 return dev->properties;
ccafadb7 279
fed34a1e 280 if (!dev->size)
ccafadb7
KZ
281 goto done;
282 if (getuid() != 0)
283 goto done;; /* no permissions to read from the device */
284
fed34a1e 285 pr = blkid_new_probe_from_filename(dev->filename);
ccafadb7
KZ
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
fed34a1e
KZ
300 if (dev->properties)
301 lsblk_device_free_properties(dev->properties);
302 prop = dev->properties = xcalloc(1, sizeof(*dev->properties));
ccafadb7
KZ
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);
9cca1ef2
KZ
314 if (!blkid_probe_lookup_value(pr, "VERSION", &data, NULL))
315 prop->fsversion = xstrdup(data);
ccafadb7
KZ
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
fed34a1e 325 DBG(DEV, ul_debugobj(dev, "%s: found blkid properties", dev->name));
ccafadb7
KZ
326 }
327
328done:
329 blkid_free_probe(pr);
330
7408a5d9 331 DBG(DEV, ul_debugobj(dev, " from blkid"));
fed34a1e
KZ
332 dev->blkid_requested = 1;
333 return dev->properties;
ccafadb7
KZ
334}
335
fed34a1e 336struct lsblk_devprop *lsblk_device_get_properties(struct lsblk_device *dev)
ccafadb7 337{
7408a5d9 338 struct lsblk_devprop *p = NULL;
ccafadb7 339
7408a5d9
KZ
340 DBG(DEV, ul_debugobj(dev, "%s: properties requested", dev->filename));
341 if (lsblk->sysroot)
eea06b92
KZ
342 return get_properties_by_file(dev);
343
344 p = get_properties_by_udev(dev);
ccafadb7 345 if (!p)
fed34a1e 346 p = get_properties_by_blkid(dev);
ccafadb7
KZ
347 return p;
348}
349
350void lsblk_properties_deinit(void)
351{
352#ifdef HAVE_LIBUDEV
353 udev_unref(udev);
354#endif
355}
107e9559
KZ
356
357
358
359/*
360 * Partition types
361 */
362struct 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
368static 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 }
378static const struct lsblk_parttype gpt_types[] =
379{
380 #include "pt-gpt-partnames.h"
381};
382
383const 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}