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