diff options
Diffstat (limited to 'misc-utils/lsblk-properties.c')
-rw-r--r-- | misc-utils/lsblk-properties.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/misc-utils/lsblk-properties.c b/misc-utils/lsblk-properties.c new file mode 100644 index 0000000..ada9b03 --- /dev/null +++ b/misc-utils/lsblk-properties.c @@ -0,0 +1,392 @@ + +#include <blkid.h> + +#ifdef HAVE_LIBUDEV +# include <libudev.h> +#endif + +#include "c.h" +#include "xalloc.h" +#include "mangle.h" +#include "path.h" +#include "nls.h" +#include "strutils.h" + +#include "lsblk.h" + +#ifdef HAVE_LIBUDEV +static struct udev *udev; +#endif + +void lsblk_device_free_properties(struct lsblk_devprop *p) +{ + if (!p) + return; + + free(p->fstype); + free(p->fsversion); + free(p->uuid); + free(p->ptuuid); + free(p->pttype); + free(p->label); + free(p->parttype); + free(p->partuuid); + free(p->partlabel); + free(p->wwn); + free(p->serial); + free(p->model); + free(p->partflags); + + free(p->mode); + free(p->owner); + free(p->group); + + free(p); +} + +#ifndef HAVE_LIBUDEV +static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *dev + __attribute__((__unused__))) +{ + return NULL; +} +#else +static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *ld) +{ + struct udev_device *dev; + + if (ld->udev_requested) + return ld->properties; + + if (!udev) + udev = udev_new(); /* global handler */ + if (!udev) + goto done; + + dev = udev_device_new_from_subsystem_sysname(udev, "block", ld->name); + if (dev) { + const char *data; + struct lsblk_devprop *prop; + + if (ld->properties) + lsblk_device_free_properties(ld->properties); + prop = ld->properties = xcalloc(1, sizeof(*ld->properties)); + + if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) { + prop->label = xstrdup(data); + unhexmangle_string(prop->label); + } + if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) { + prop->uuid = xstrdup(data); + unhexmangle_string(prop->uuid); + } + if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_UUID"))) + prop->ptuuid = xstrdup(data); + if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_TYPE"))) + prop->pttype = xstrdup(data); + if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) { + prop->partlabel = xstrdup(data); + unhexmangle_string(prop->partlabel); + } + if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE"))) + prop->fstype = xstrdup(data); + if ((data = udev_device_get_property_value(dev, "ID_FS_VERSION"))) + prop->fsversion = xstrdup(data); + if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_TYPE"))) + prop->parttype = xstrdup(data); + if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID"))) + prop->partuuid = xstrdup(data); + if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_FLAGS"))) + prop->partflags = xstrdup(data); + + data = udev_device_get_property_value(dev, "ID_WWN_WITH_EXTENSION"); + if (!data) + data = udev_device_get_property_value(dev, "ID_WWN"); + if (data) + prop->wwn = xstrdup(data); + + data = udev_device_get_property_value(dev, "SCSI_IDENT_SERIAL"); /* sg3_utils do not use I_D prefix */ + if (!data) + data = udev_device_get_property_value(dev, "ID_SCSI_SERIAL"); + if(!data) + data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT"); + if(!data) + data = udev_device_get_property_value(dev, "ID_SERIAL"); + if (data) { + prop->serial = xstrdup(data); + normalize_whitespace((unsigned char *) prop->serial); + } + + if ((data = udev_device_get_property_value(dev, "ID_MODEL_ENC"))) { + prop->model = xstrdup(data); + unhexmangle_string(prop->model); + normalize_whitespace((unsigned char *) prop->model); + } else if ((data = udev_device_get_property_value(dev, "ID_MODEL"))) { + prop->model = xstrdup(data); + normalize_whitespace((unsigned char *) prop->model); + } + + udev_device_unref(dev); + DBG(DEV, ul_debugobj(ld, "%s: found udev properties", ld->name)); + } + +done: + ld->udev_requested = 1; + + DBG(DEV, ul_debugobj(ld, " from udev")); + return ld->properties; +} +#endif /* HAVE_LIBUDEV */ + + +static int lookup(char *buf, char *pattern, char **value) +{ + char *p, *v; + int len; + + /* do not re-fill value */ + if (!buf || *value) + return 0; + + len = strlen(pattern); + if (strncmp(buf, pattern, len) != 0) + return 0; + + p = buf + len; + if (*p != '=') + return 0; + p++; + if (!*p || *p == '\n') + return 0; + v = p; + for (; *p && *p != '\n'; p++) ; + if (*p == '\n') + *p = '\0'; + + *value = xstrdup(v); + return 1; +} + +/* read device properties from fake text file (used on --sysroot) */ +static struct lsblk_devprop *get_properties_by_file(struct lsblk_device *ld) +{ + struct lsblk_devprop *prop; + struct path_cxt *pc; + FILE *fp = NULL; + struct stat sb; + char buf[BUFSIZ]; + + assert(lsblk->sysroot); + + if (ld->file_requested) + return ld->properties; + + if (ld->properties || ld->filename) { + lsblk_device_free_properties(ld->properties); + ld->properties = NULL; + } + + pc = ul_new_path("/"); + if (!pc) + return NULL; + if (ul_path_set_prefix(pc, lsblk->sysroot) != 0) + goto done; + if (ul_path_stat(pc, &sb, 0, ld->filename) != 0 || !S_ISREG(sb.st_mode)) + goto done; + + fp = ul_path_fopen(pc, "r", ld->filename); + if (!fp) + goto done; + + prop = ld->properties; + if (!prop) + prop = ld->properties = xcalloc(1, sizeof(*ld->properties)); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + /* udev based */ + if (lookup(buf, "ID_FS_LABEL_ENC", &prop->label)) + unhexmangle_string(prop->label); + else if (lookup(buf, "ID_FS_UUID_ENC", &prop->uuid)) + unhexmangle_string(prop->uuid); + else if (lookup(buf, "ID_PART_ENTRY_NAME", &prop->partlabel)) + unhexmangle_string(prop->partlabel); + else if (lookup(buf, "ID_PART_TABLE_UUID", &prop->ptuuid)) ; + else if (lookup(buf, "ID_PART_TABLE_TYPE", &prop->pttype)) ; + else if (lookup(buf, "ID_FS_TYPE", &prop->fstype)) ; + else if (lookup(buf, "ID_FS_VERSION", &prop->fsversion)) ; + else if (lookup(buf, "ID_PART_ENTRY_TYPE", &prop->parttype)) ; + else if (lookup(buf, "ID_PART_ENTRY_UUID", &prop->partuuid)) ; + else if (lookup(buf, "ID_PART_ENTRY_FLAGS", &prop->partflags)) ; + else if (lookup(buf, "ID_MODEL", &prop->model)) ; + else if (lookup(buf, "ID_WWN_WITH_EXTENSION", &prop->wwn)) ; + else if (lookup(buf, "ID_WWN", &prop->wwn)) ; + else if (lookup(buf, "SCSI_IDENT_SERIAL", &prop->serial)) ; /* serial from sg3_utils */ + else if (lookup(buf, "ID_SCSI_SERIAL", &prop->serial)) ; + else if (lookup(buf, "ID_SERIAL_SHORT", &prop->serial)) ; + else if (lookup(buf, "ID_SERIAL", &prop->serial)) ; + + /* lsblk specific */ + else if (lookup(buf, "MODE", &prop->mode)) ; + else if (lookup(buf, "OWNER", &prop->owner)) ; + else if (lookup(buf, "GROUP", &prop->group)) ; + + else + continue; + } +done: + if (fp) + fclose(fp); + ul_unref_path(pc); + ld->file_requested = 1; + + DBG(DEV, ul_debugobj(ld, " from fake-file")); + return ld->properties; +} + + +static struct lsblk_devprop *get_properties_by_blkid(struct lsblk_device *dev) +{ + blkid_probe pr = NULL; + + if (dev->blkid_requested) + return dev->properties; + + if (!dev->size) + goto done; + if (getuid() != 0) + goto done;; /* no permissions to read from the device */ + + pr = blkid_new_probe_from_filename(dev->filename); + if (!pr) + goto done; + + blkid_probe_enable_superblocks(pr, 1); + blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL | + BLKID_SUBLKS_UUID | + BLKID_SUBLKS_TYPE); + blkid_probe_enable_partitions(pr, 1); + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); + + if (!blkid_do_safeprobe(pr)) { + const char *data = NULL; + struct lsblk_devprop *prop; + + if (dev->properties) + lsblk_device_free_properties(dev->properties); + prop = dev->properties = xcalloc(1, sizeof(*dev->properties)); + + if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL)) + prop->fstype = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL)) + prop->uuid = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "PTUUID", &data, NULL)) + prop->ptuuid = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "PTTYPE", &data, NULL)) + prop->pttype = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL)) + prop->label = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "VERSION", &data, NULL)) + prop->fsversion = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &data, NULL)) + prop->parttype = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL)) + prop->partuuid = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL)) + prop->partlabel = xstrdup(data); + if (!blkid_probe_lookup_value(pr, "PART_ENTRY_FLAGS", &data, NULL)) + prop->partflags = xstrdup(data); + + DBG(DEV, ul_debugobj(dev, "%s: found blkid properties", dev->name)); + } + +done: + blkid_free_probe(pr); + + DBG(DEV, ul_debugobj(dev, " from blkid")); + dev->blkid_requested = 1; + return dev->properties; +} + +struct lsblk_devprop *lsblk_device_get_properties(struct lsblk_device *dev) +{ + struct lsblk_devprop *p = NULL; + + DBG(DEV, ul_debugobj(dev, "%s: properties requested", dev->filename)); + if (lsblk->sysroot) + return get_properties_by_file(dev); + + p = get_properties_by_udev(dev); + if (!p) + p = get_properties_by_blkid(dev); + return p; +} + +void lsblk_properties_deinit(void) +{ +#ifdef HAVE_LIBUDEV + udev_unref(udev); +#endif +} + + + +/* + * Partition types + */ +struct lsblk_parttype { + unsigned int code; /* type as number or zero */ + char *name; /* description */ + char *typestr; /* type as string or NULL */ +}; + +static const struct lsblk_parttype mbr_types[] = +{ + #include "pt-mbr-partnames.h" +}; + +#define DEF_GUID(_u, _n) \ + { \ + .typestr = (_u), \ + .name = (_n), \ + } +static const struct lsblk_parttype gpt_types[] = +{ + #include "pt-gpt-partnames.h" +}; + +const char *lsblk_parttype_code_to_string(const char *code, const char *pttype) +{ + size_t i; + + if (!code || !pttype) + return NULL; + + if (strcmp(pttype, "dos") == 0 || strcmp(pttype, "mbr") == 0) { + char *end = NULL; + unsigned int xcode; + + errno = 0; + xcode = strtol(code, &end, 16); + + if (errno || *end != '\0') + return NULL; + + for (i = 0; i < ARRAY_SIZE(mbr_types); i++) { + const struct lsblk_parttype *t = &mbr_types[i]; + + if (t->name && t->code == xcode) + return t->name; + } + + } else if (strcmp(pttype, "gpt") == 0) { + for (i = 0; i < ARRAY_SIZE(gpt_types); i++) { + const struct lsblk_parttype *t = &gpt_types[i]; + + if (t->name && t->typestr && + strcasecmp(code, t->typestr) == 0) + return t->name; + } + } + + return NULL; +} |