Blame SOURCES/0053-filter-mpath-get-wwids-from-sysfs-vpd_pg83.patch

38b7b2
From e36b180a6983c4fa07d6714a0bf81e6935487359 Mon Sep 17 00:00:00 2001
38b7b2
From: David Teigland <teigland@redhat.com>
38b7b2
Date: Mon, 6 Jun 2022 14:04:20 -0500
38b7b2
Subject: [PATCH 53/54] filter-mpath: get wwids from sysfs vpd_pg83
38b7b2
38b7b2
to compare with wwids in /etc/multipath/wwids when
38b7b2
excluding multipath components.  The wwid printed
38b7b2
from the sysfs wwid file may not be the wwid used
38b7b2
in multipath wwids.  Save the wwids found for each
38b7b2
device on dev->wwids to avoid repeating reading
38b7b2
and parsing the sysfs files.
38b7b2
---
38b7b2
 lib/Makefile.in        |   1 +
38b7b2
 lib/device/dev-cache.c |  18 ++++
38b7b2
 lib/device/dev-cache.h |   1 +
38b7b2
 lib/device/dev-mpath.c | 232 ++++++++++++++++++++++++++++++++++-------
38b7b2
 lib/device/device.h    |  13 +++
38b7b2
 lib/device/device_id.c |  31 +++++-
38b7b2
 lib/device/device_id.h |   2 +
38b7b2
 lib/device/parse_vpd.c | 199 +++++++++++++++++++++++++++++++++++
38b7b2
 8 files changed, 454 insertions(+), 43 deletions(-)
38b7b2
 create mode 100644 lib/device/parse_vpd.c
38b7b2
38b7b2
diff --git a/lib/Makefile.in b/lib/Makefile.in
38b7b2
index 8b3eac60a..3077825d2 100644
38b7b2
--- a/lib/Makefile.in
38b7b2
+++ b/lib/Makefile.in
38b7b2
@@ -40,6 +40,7 @@ SOURCES =\
38b7b2
 	device/dev-luks.c \
38b7b2
 	device/dev-dasd.c \
38b7b2
 	device/dev-lvm1-pool.c \
38b7b2
+	device/parse_vpd.c \
38b7b2
 	display/display.c \
38b7b2
 	error/errseg.c \
38b7b2
 	unknown/unknown.c \
38b7b2
diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
38b7b2
index 0eb2568b5..65e1cb138 100644
38b7b2
--- a/lib/device/dev-cache.c
38b7b2
+++ b/lib/device/dev-cache.c
38b7b2
@@ -80,6 +80,7 @@ static void _dev_init(struct device *dev)
38b7b2
 
38b7b2
 	dm_list_init(&dev->aliases);
38b7b2
 	dm_list_init(&dev->ids);
38b7b2
+	dm_list_init(&dev->wwids);
38b7b2
 }
38b7b2
 
38b7b2
 void dev_destroy_file(struct device *dev)
38b7b2
@@ -383,6 +384,22 @@ out:
38b7b2
 	return 1;
38b7b2
 }
38b7b2
 
38b7b2
+int get_sysfs_binary(const char *path, char *buf, size_t buf_size, int *retlen)
38b7b2
+{
38b7b2
+	int ret;
38b7b2
+	int fd;
38b7b2
+
38b7b2
+	fd = open(path, O_RDONLY);
38b7b2
+	if (fd < 0)
38b7b2
+		return 0;
38b7b2
+	ret = read(fd, buf, buf_size);
38b7b2
+	close(fd);
38b7b2
+	if (ret <= 0)
38b7b2
+		return 0;
38b7b2
+	*retlen = ret;
38b7b2
+	return 1;
38b7b2
+}
38b7b2
+
38b7b2
 int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value)
38b7b2
 {
38b7b2
 	FILE *fp;
38b7b2
@@ -1336,6 +1353,7 @@ int dev_cache_exit(void)
38b7b2
 		dm_hash_iterate(n, _cache.names) {
38b7b2
 			dev = (struct device *) dm_hash_get_data(_cache.names, n);
38b7b2
 			free_dids(&dev->ids);
38b7b2
+			free_wwids(&dev->wwids);
38b7b2
 		}
38b7b2
 	}
38b7b2
 
38b7b2
diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h
38b7b2
index 321a56d7b..c49e6265d 100644
38b7b2
--- a/lib/device/dev-cache.h
38b7b2
+++ b/lib/device/dev-cache.h
38b7b2
@@ -74,6 +74,7 @@ void dev_cache_failed_path(struct device *dev, const char *path);
38b7b2
 bool dev_cache_has_md_with_end_superblock(struct dev_types *dt);
38b7b2
 
38b7b2
 int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value);
38b7b2
+int get_sysfs_binary(const char *path, char *buf, size_t buf_size, int *retlen);
38b7b2
 int get_dm_uuid_from_sysfs(char *buf, size_t buf_size, int major, int minor);
38b7b2
 
38b7b2
 int setup_devices_file(struct cmd_context *cmd);
38b7b2
diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
38b7b2
index 7abbfb289..3795c992d 100644
38b7b2
--- a/lib/device/dev-mpath.c
38b7b2
+++ b/lib/device/dev-mpath.c
38b7b2
@@ -200,11 +200,12 @@ static void _read_wwid_exclusions(void)
38b7b2
 		log_debug("multipath config ignored %d wwids", rem_count);
38b7b2
 }
38b7b2
 
38b7b2
-static void _read_wwid_file(const char *config_wwids_file)
38b7b2
+static void _read_wwid_file(const char *config_wwids_file, int *entries)
38b7b2
 {
38b7b2
 	FILE *fp;
38b7b2
 	char line[MAX_WWID_LINE];
38b7b2
 	char *wwid, *p;
38b7b2
+	char typestr[2] = { 0 };
38b7b2
 	int count = 0;
38b7b2
 
38b7b2
 	if (config_wwids_file[0] != '/') {
38b7b2
@@ -226,8 +227,17 @@ static void _read_wwid_file(const char *config_wwids_file)
38b7b2
 		if (line[0] == '/')
38b7b2
 			wwid++;
38b7b2
 
38b7b2
-		/* skip the initial '3' */
38b7b2
-		wwid++;
38b7b2
+
38b7b2
+		/*
38b7b2
+		 * the initial character is the id type,
38b7b2
+		 * 1 is t10, 2 is eui, 3 is naa, 8 is scsi name.
38b7b2
+		 * wwids are stored in the hash table without the type charater.
38b7b2
+		 * It seems that sometimes multipath does not include
38b7b2
+		 * the type charater (seen with t10 scsi_debug devs).
38b7b2
+		 */
38b7b2
+		typestr[0] = *wwid;
38b7b2
+		if (typestr[0] == '1' || typestr[0] == '2' || typestr[0] == '3')
38b7b2
+			wwid++;
38b7b2
 
38b7b2
 		if ((p = strchr(wwid, '/')))
38b7b2
 			*p = '\0';
38b7b2
@@ -240,6 +250,7 @@ static void _read_wwid_file(const char *config_wwids_file)
38b7b2
 		stack;
38b7b2
 
38b7b2
 	log_debug("multipath wwids read %d from %s", count, config_wwids_file);
38b7b2
+	*entries = count;
38b7b2
 }
38b7b2
 
38b7b2
 int dev_mpath_init(const char *config_wwids_file)
38b7b2
@@ -247,6 +258,7 @@ int dev_mpath_init(const char *config_wwids_file)
38b7b2
 	struct dm_pool *mem;
38b7b2
 	struct dm_hash_table *minor_tab;
38b7b2
 	struct dm_hash_table *wwid_tab;
38b7b2
+	int entries = 0;
38b7b2
 
38b7b2
 	dm_list_init(&_ignored);
38b7b2
 	dm_list_init(&_ignored_exceptions);
38b7b2
@@ -283,10 +295,16 @@ int dev_mpath_init(const char *config_wwids_file)
38b7b2
 	_wwid_hash_tab = wwid_tab;
38b7b2
 
38b7b2
 	if (config_wwids_file) {
38b7b2
-		_read_wwid_file(config_wwids_file);
38b7b2
+		_read_wwid_file(config_wwids_file, &entries);
38b7b2
 		_read_wwid_exclusions();
38b7b2
 	}
38b7b2
 
38b7b2
+	if (!entries) {
38b7b2
+		/* reading dev wwids is skipped with null wwid_hash_tab */
38b7b2
+		dm_hash_destroy(_wwid_hash_tab);
38b7b2
+		_wwid_hash_tab = NULL;
38b7b2
+	}
38b7b2
+
38b7b2
 	return 1;
38b7b2
 }
38b7b2
 
38b7b2
@@ -432,10 +450,10 @@ static int _dev_is_mpath_component_udev(struct device *dev)
38b7b2
 }
38b7b2
 #endif
38b7b2
 
38b7b2
-static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device *dev)
38b7b2
+static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device *dev,
38b7b2
+					 int primary_result, dev_t primary_dev)
38b7b2
 {
38b7b2
 	struct dev_types *dt = cmd->dev_types;
38b7b2
-	const char *part_name;
38b7b2
 	const char *name;               /* e.g. "sda" for "/dev/sda" */
38b7b2
 	char link_path[PATH_MAX];       /* some obscure, unpredictable sysfs path */
38b7b2
 	char holders_path[PATH_MAX];    /* e.g. "/sys/block/sda/holders/" */
38b7b2
@@ -449,25 +467,15 @@ static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device
38b7b2
 	int dm_dev_major;
38b7b2
 	int dm_dev_minor;
38b7b2
 	struct stat info;
38b7b2
-	dev_t primary_dev;
38b7b2
 	int is_mpath_component = 0;
38b7b2
 
38b7b2
-	/* multipathing is only known to exist for SCSI or NVME devices */
38b7b2
-	if (!major_is_scsi_device(dt, dev_major) && !dev_is_nvme(dt, dev))
38b7b2
-		return 0;
38b7b2
-
38b7b2
-	switch (dev_get_primary_dev(dt, dev, &primary_dev)) {
38b7b2
+	switch (primary_result) {
38b7b2
 
38b7b2
 	case 2: /* The dev is partition. */
38b7b2
-		part_name = dev_name(dev); /* name of original dev for log_debug msg */
38b7b2
 
38b7b2
 		/* gets "foo" for "/dev/foo" where "/dev/foo" comes from major:minor */
38b7b2
 		if (!(name = _get_sysfs_name_by_devt(sysfs_dir, primary_dev, link_path, sizeof(link_path))))
38b7b2
 			return_0;
38b7b2
-
38b7b2
-		log_debug_devs("%s: Device is a partition, using primary "
38b7b2
-			       "device %s for mpath component detection",
38b7b2
-			       part_name, name);
38b7b2
 		break;
38b7b2
 
38b7b2
 	case 1: /* The dev is already a primary dev. Just continue with the dev. */
38b7b2
@@ -589,47 +597,189 @@ static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device
38b7b2
 	return is_mpath_component;
38b7b2
 }
38b7b2
 
38b7b2
-static int _dev_in_wwid_file(struct cmd_context *cmd, struct device *dev)
38b7b2
+static int _read_sys_wwid(struct cmd_context *cmd, struct device *dev,
38b7b2
+			  char *idbuf, int idbufsize)
38b7b2
 {
38b7b2
-	char sysbuf[PATH_MAX] = { 0 };
38b7b2
-	char *wwid;
38b7b2
-	long look;
38b7b2
+	char idtmp[DEV_WWID_SIZE];
38b7b2
 
38b7b2
-	if (!_wwid_hash_tab)
38b7b2
+	if (!read_sys_block(cmd, dev, "device/wwid", idbuf, idbufsize)) {
38b7b2
+		/* the wwid file is not under device for nvme devs */
38b7b2
+		if (!read_sys_block(cmd, dev, "wwid", idbuf, idbufsize))
38b7b2
+			return 0;
38b7b2
+	}
38b7b2
+	if (!idbuf[0])
38b7b2
 		return 0;
38b7b2
 
38b7b2
-	if (!read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf)))
38b7b2
+	/* in t10 id, replace series of spaces with one _ like multipath */
38b7b2
+	if (!strncmp(idbuf, "t10.", 4) && strchr(idbuf, ' ')) {
38b7b2
+		if (idbufsize < DEV_WWID_SIZE)
38b7b2
+			return 0;
38b7b2
+		memcpy(idtmp, idbuf, DEV_WWID_SIZE);
38b7b2
+		memset(idbuf, 0, idbufsize);
38b7b2
+		format_t10_id((const unsigned char *)idtmp, DEV_WWID_SIZE, (unsigned char *)idbuf, idbufsize);
38b7b2
+	}
38b7b2
+	return 1;
38b7b2
+}
38b7b2
+
38b7b2
+#define VPD_SIZE 4096
38b7b2
+
38b7b2
+static int _read_sys_vpd_wwids(struct cmd_context *cmd, struct device *dev,
38b7b2
+			       struct dm_list *ids)
38b7b2
+{
38b7b2
+	unsigned char vpd_data[VPD_SIZE] = { 0 };
38b7b2
+	int vpd_datalen = 0;
38b7b2
+
38b7b2
+	if (!read_sys_block_binary(cmd, dev, "device/vpd_pg83", (char *)vpd_data, VPD_SIZE, &vpd_datalen))
38b7b2
+		return 0;
38b7b2
+	if (!vpd_datalen)
38b7b2
 		return 0;
38b7b2
 
38b7b2
-	if (!sysbuf[0])
38b7b2
+	/* adds dev_wwid entry to dev->wwids for each id in vpd data */
38b7b2
+	parse_vpd_ids(vpd_data, vpd_datalen, ids);
38b7b2
+	return 1;
38b7b2
+}
38b7b2
+
38b7b2
+void free_wwids(struct dm_list *ids)
38b7b2
+{
38b7b2
+	struct dev_wwid *dw, *safe;
38b7b2
+
38b7b2
+	dm_list_iterate_items_safe(dw, safe, ids) {
38b7b2
+		dm_list_del(&dw->list);
38b7b2
+		free(dw);
38b7b2
+	}
38b7b2
+}
38b7b2
+
38b7b2
+static int _wwid_type_num(char *id)
38b7b2
+{
38b7b2
+	if (!strncmp(id, "naa.", 4))
38b7b2
+		return 3;
38b7b2
+	else if (!strncmp(id, "eui.", 4))
38b7b2
+		return 2;
38b7b2
+	else if (!strncmp(id, "t10.", 4))
38b7b2
+		return 1;
38b7b2
+	else
38b7b2
+		return -1;
38b7b2
+}
38b7b2
+
38b7b2
+/*
38b7b2
+ * TODO: if each of the different wwid types (naa/eui/t10) were
38b7b2
+ * represented by different DEV_ID_TYPE_FOO values, and used
38b7b2
+ * as device_id types, then we could drop struct dev_wwid and
38b7b2
+ * drop dev->wwids, and just use dev->ids for each of the
38b7b2
+ * different wwids found in vpd_pg83.  This would also require
38b7b2
+ * the ability to handle both the original method of replacing
38b7b2
+ * every space in the id string with _ and the new/multipath
38b7b2
+ * format_t10_id replacing series of spaces with one _.
38b7b2
+ */
38b7b2
+struct dev_wwid *add_wwid(char *id, int id_type, struct dm_list *ids)
38b7b2
+{
38b7b2
+	struct dev_wwid *dw;
38b7b2
+	int len;
38b7b2
+
38b7b2
+	if (!id_type) {
38b7b2
+		id_type = _wwid_type_num(id);
38b7b2
+		if (id_type == -1)
38b7b2
+			log_debug("unknown wwid type %s", id);
38b7b2
+	}
38b7b2
+
38b7b2
+	if (!(dw = zalloc(sizeof(struct dev_wwid))))
38b7b2
+		return NULL;
38b7b2
+	len = strlen(id);
38b7b2
+	if (len >= DEV_WWID_SIZE)
38b7b2
+		len = DEV_WWID_SIZE - 1;
38b7b2
+	memcpy(dw->id, id, len);
38b7b2
+	dw->type = id_type;
38b7b2
+	dm_list_add(ids, &dw->list);
38b7b2
+	return dw;
38b7b2
+}
38b7b2
+
38b7b2
+/*
38b7b2
+ * we save ids with format: naa.<value>, eui.<value>, t10.<value>.
38b7b2
+ * multipath wwids file uses format: 3<value>, 2<value>, 1<value>.
38b7b2
+ * The values are saved in wwid_hash_tab without the type prefix.
38b7b2
+ */
38b7b2
+
38b7b2
+static int _dev_in_wwid_file(struct cmd_context *cmd, struct device *dev,
38b7b2
+			     int primary_result, dev_t primary_dev)
38b7b2
+{
38b7b2
+	char idbuf[DEV_WWID_SIZE] = { 0 };
38b7b2
+	struct dev_wwid *dw;
38b7b2
+	char *wwid;
38b7b2
+
38b7b2
+	if (!_wwid_hash_tab)
38b7b2
 		return 0;
38b7b2
 
38b7b2
 	/*
38b7b2
-	 * sysfs prints wwid as <typestr>.<value>
38b7b2
-	 * multipath wwid uses '3'<value>
38b7b2
-	 * does "<typestr>." always correspond to "3"?
38b7b2
+	 * Check the primary device, not the partition.
38b7b2
 	 */
38b7b2
-	if (!(wwid = strchr(sysbuf, '.')))
38b7b2
-		return 0;
38b7b2
+	if (primary_result == 2) {
38b7b2
+		if (!(dev = dev_cache_get_by_devt(cmd, primary_dev))) {
38b7b2
+			log_debug("dev_is_mpath_component %s no primary dev", dev_name(dev));
38b7b2
+			return 0;
38b7b2
+		}
38b7b2
+	}
38b7b2
 
38b7b2
-	/* skip the type and dot, just as '3' was skipped from wwids entry */
38b7b2
-	wwid++;
38b7b2
-	
38b7b2
-	look = (long) dm_hash_lookup_binary(_wwid_hash_tab, wwid, strlen(wwid));
38b7b2
+	/*
38b7b2
+	 * This function may be called multiple times for the same device, in
38b7b2
+	 * particular if partitioned for each partition.
38b7b2
+	 */
38b7b2
+	if (!dm_list_empty(&dev->wwids))
38b7b2
+		goto lookup;
38b7b2
 
38b7b2
-	if (look) {
38b7b2
-		log_debug_devs("dev_is_mpath_component %s multipath wwid %s", dev_name(dev), wwid);
38b7b2
-		return 1;
38b7b2
+	/*
38b7b2
+	 * Get all the ids for the device from vpd_pg83 and check if any of
38b7b2
+	 * those are in /etc/multipath/wwids.  These ids should include the
38b7b2
+	 * value printed from the sysfs wwid file.
38b7b2
+	 */
38b7b2
+	_read_sys_vpd_wwids(cmd, dev, &dev->wwids);
38b7b2
+	if (!dm_list_empty(&dev->wwids))
38b7b2
+		goto lookup;
38b7b2
+
38b7b2
+	/*
38b7b2
+	 * This will read the sysfs wwid file, nvme devices in particular have
38b7b2
+	 * a wwid file but not a vpd_pg83 file.
38b7b2
+	 */
38b7b2
+	if (_read_sys_wwid(cmd, dev, idbuf, sizeof(idbuf)))
38b7b2
+		add_wwid(idbuf, 0, &dev->wwids);
38b7b2
+
38b7b2
+ lookup:
38b7b2
+	dm_list_iterate_items(dw, &dev->wwids) {
38b7b2
+		if (dw->type == 1 || dw->type == 2 || dw->type == 3)
38b7b2
+			wwid = &dw->id[4];
38b7b2
+		else
38b7b2
+			wwid = dw->id;
38b7b2
+
38b7b2
+		if (dm_hash_lookup_binary(_wwid_hash_tab, wwid, strlen(wwid))) {
38b7b2
+			log_debug_devs("dev_is_mpath_component %s %s in wwids file", dev_name(dev), dw->id);
38b7b2
+			return 1;
38b7b2
+		}
38b7b2
 	}
38b7b2
+
38b7b2
 	return 0;
38b7b2
 }
38b7b2
 
38b7b2
 int dev_is_mpath_component(struct cmd_context *cmd, struct device *dev)
38b7b2
 {
38b7b2
-	if (_dev_is_mpath_component_sysfs(cmd, dev) == 1)
38b7b2
+	struct dev_types *dt = cmd->dev_types;
38b7b2
+	int primary_result;
38b7b2
+	dev_t primary_dev;
38b7b2
+
38b7b2
+	/*
38b7b2
+	 * multipath only uses SCSI or NVME devices
38b7b2
+	 */
38b7b2
+	if (!major_is_scsi_device(dt, MAJOR(dev->dev)) && !dev_is_nvme(dt, dev))
38b7b2
+		return 0;
38b7b2
+
38b7b2
+	/*
38b7b2
+	 * primary_result 2: dev is a partition, primary_dev is the whole device
38b7b2
+	 * primary_result 1: dev is a whole device
38b7b2
+	 */
38b7b2
+	primary_result = dev_get_primary_dev(dt, dev, &primary_dev);
38b7b2
+
38b7b2
+	if (_dev_is_mpath_component_sysfs(cmd, dev, primary_result, primary_dev) == 1)
38b7b2
 		goto found;
38b7b2
 
38b7b2
-	if (_dev_in_wwid_file(cmd, dev))
38b7b2
+	if (_dev_in_wwid_file(cmd, dev, primary_result, primary_dev))
38b7b2
 		goto found;
38b7b2
 
38b7b2
 	if (external_device_info_source() == DEV_EXT_UDEV) {
38b7b2
@@ -637,6 +787,12 @@ int dev_is_mpath_component(struct cmd_context *cmd, struct device *dev)
38b7b2
 			goto found;
38b7b2
 	}
38b7b2
 
38b7b2
+	/*
38b7b2
+	 * TODO: save the result of this function in dev->flags and use those
38b7b2
+	 * flags on repeated calls to avoid repeating the work multiple times
38b7b2
+	 * for the same device when there are partitions on the device.
38b7b2
+	 */
38b7b2
+
38b7b2
 	return 0;
38b7b2
 found:
38b7b2
 	return 1;
38b7b2
diff --git a/lib/device/device.h b/lib/device/device.h
38b7b2
index 572994bb9..1c85f37a9 100644
38b7b2
--- a/lib/device/device.h
38b7b2
+++ b/lib/device/device.h
38b7b2
@@ -59,6 +59,14 @@ struct dev_ext {
38b7b2
 	void *handle;
38b7b2
 };
38b7b2
 
38b7b2
+#define DEV_WWID_SIZE 128
38b7b2
+
38b7b2
+struct dev_wwid {
38b7b2
+	struct dm_list list;
38b7b2
+	int type;
38b7b2
+	char id[DEV_WWID_SIZE];
38b7b2
+};
38b7b2
+
38b7b2
 #define DEV_ID_TYPE_SYS_WWID   0x0001
38b7b2
 #define DEV_ID_TYPE_SYS_SERIAL 0x0002
38b7b2
 #define DEV_ID_TYPE_MPATH_UUID 0x0003
38b7b2
@@ -105,6 +113,7 @@ struct dev_use {
38b7b2
  */
38b7b2
 struct device {
38b7b2
 	struct dm_list aliases;	/* struct dm_str_list */
38b7b2
+	struct dm_list wwids; /* struct dev_wwid, used for multipath component detection */
38b7b2
 	struct dm_list ids; /* struct dev_id, different entries for different idtypes */
38b7b2
 	struct dev_id *id; /* points to the the ids entry being used for this dev */
38b7b2
 	dev_t dev;
38b7b2
@@ -206,5 +215,9 @@ void dev_destroy_file(struct device *dev);
38b7b2
 
38b7b2
 int dev_mpath_init(const char *config_wwids_file);
38b7b2
 void dev_mpath_exit(void);
38b7b2
+struct dev_wwid *add_wwid(char *id, int id_type, struct dm_list *ids);
38b7b2
+void free_wwids(struct dm_list *ids);
38b7b2
+int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list *ids);
38b7b2
+int format_t10_id(const unsigned char *in, int in_bytes, unsigned char *out, int out_bytes);
38b7b2
 
38b7b2
 #endif
38b7b2
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
38b7b2
index 20901ab90..4d8fa5c9c 100644
38b7b2
--- a/lib/device/device_id.c
38b7b2
+++ b/lib/device/device_id.c
38b7b2
@@ -182,7 +182,9 @@ void free_dids(struct dm_list *ids)
38b7b2
 	}
38b7b2
 }
38b7b2
 
38b7b2
-int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suffix, char *sysbuf, int sysbufsize)
38b7b2
+static int _read_sys_block(struct cmd_context *cmd, struct device *dev,
38b7b2
+			   const char *suffix, char *sysbuf, int sysbufsize,
38b7b2
+			   int binary, int *retlen)
38b7b2
 {
38b7b2
 	char path[PATH_MAX];
38b7b2
 	dev_t devt = dev->dev;
38b7b2
@@ -196,11 +198,17 @@ int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suff
38b7b2
 		return 0;
38b7b2
 	}
38b7b2
 
38b7b2
-	get_sysfs_value(path, sysbuf, sysbufsize, 0);
38b7b2
+	if (binary) {
38b7b2
+		ret = get_sysfs_binary(path, sysbuf, sysbufsize, retlen);
38b7b2
+		if (ret && !*retlen)
38b7b2
+			ret = 0;
38b7b2
+	} else {
38b7b2
+		ret = get_sysfs_value(path, sysbuf, sysbufsize, 0);
38b7b2
+		if (ret && !sysbuf[0])
38b7b2
+			ret = 0;
38b7b2
+	}
38b7b2
 
38b7b2
-	if (sysbuf[0]) {
38b7b2
-		if (prim)
38b7b2
-			log_debug("Using primary device_id for partition %s.", dev_name(dev));
38b7b2
+	if (ret) {
38b7b2
 		sysbuf[sysbufsize - 1] = '\0';
38b7b2
 		return 1;
38b7b2
 	}
38b7b2
@@ -220,6 +228,19 @@ int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suff
38b7b2
 	return 0;
38b7b2
 }
38b7b2
 
38b7b2
+int read_sys_block(struct cmd_context *cmd, struct device *dev,
38b7b2
+		   const char *suffix, char *sysbuf, int sysbufsize)
38b7b2
+{
38b7b2
+	return _read_sys_block(cmd, dev, suffix, sysbuf, sysbufsize, 0, NULL);
38b7b2
+}
38b7b2
+
38b7b2
+int read_sys_block_binary(struct cmd_context *cmd, struct device *dev,
38b7b2
+			  const char *suffix, char *sysbuf, int sysbufsize,
38b7b2
+			  int *retlen)
38b7b2
+{
38b7b2
+	return _read_sys_block(cmd, dev, suffix, sysbuf, sysbufsize, 1, retlen);
38b7b2
+}
38b7b2
+
38b7b2
 static int _dm_uuid_has_prefix(char *sysbuf, const char *prefix)
38b7b2
 {
38b7b2
 	if (!strncmp(sysbuf, prefix, strlen(prefix)))
38b7b2
diff --git a/lib/device/device_id.h b/lib/device/device_id.h
38b7b2
index 2cd2fd7c6..e049e2333 100644
38b7b2
--- a/lib/device/device_id.h
38b7b2
+++ b/lib/device/device_id.h
38b7b2
@@ -55,6 +55,8 @@ void devices_file_exit(struct cmd_context *cmd);
38b7b2
 void unlink_searched_devnames(struct cmd_context *cmd);
38b7b2
 
38b7b2
 int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suffix, char *sysbuf, int sysbufsize);
38b7b2
+int read_sys_block_binary(struct cmd_context *cmd, struct device *dev,
38b7b2
+			  const char *suffix, char *sysbuf, int sysbufsize, int *retlen);
38b7b2
 
38b7b2
 int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out);
38b7b2
 
38b7b2
diff --git a/lib/device/parse_vpd.c b/lib/device/parse_vpd.c
38b7b2
new file mode 100644
38b7b2
index 000000000..4bafa7b9e
38b7b2
--- /dev/null
38b7b2
+++ b/lib/device/parse_vpd.c
38b7b2
@@ -0,0 +1,199 @@
38b7b2
+/*
38b7b2
+ * Copyright (C) 2022 Red Hat, Inc. All rights reserved.
38b7b2
+ *
38b7b2
+ * This file is part of LVM2.
38b7b2
+ *
38b7b2
+ * This copyrighted material is made available to anyone wishing to use,
38b7b2
+ * modify, copy, or redistribute it subject to the terms and conditions
38b7b2
+ * of the GNU Lesser General Public License v.2.1.
38b7b2
+ *
38b7b2
+ * You should have received a copy of the GNU Lesser General Public License
38b7b2
+ * along with this program; if not, write to the Free Software Foundation,
38b7b2
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
38b7b2
+ */
38b7b2
+
38b7b2
+#include "base/memory/zalloc.h"
38b7b2
+#include "lib/misc/lib.h"
38b7b2
+#include "lib/device/device.h"
38b7b2
+
38b7b2
+#include <stdio.h>
38b7b2
+#include <unistd.h>
38b7b2
+#include <stdint.h>
38b7b2
+#include <stdlib.h>
38b7b2
+#include <stdarg.h>
38b7b2
+#include <string.h>
38b7b2
+#include <inttypes.h>
38b7b2
+#include <sys/types.h>
38b7b2
+#include <sys/ioctl.h>
38b7b2
+#include <sys/stat.h>
38b7b2
+#include <fcntl.h>
38b7b2
+#include <ctype.h>
38b7b2
+#include <limits.h>
38b7b2
+#include <dirent.h>
38b7b2
+#include <errno.h>
38b7b2
+#include <stdbool.h>
38b7b2
+#include <assert.h>
38b7b2
+
38b7b2
+/*
38b7b2
+ * Replace series of spaces with a single _.
38b7b2
+ */
38b7b2
+int format_t10_id(const unsigned char *in, int in_bytes, unsigned char *out, int out_bytes)
38b7b2
+{
38b7b2
+	int in_space = 0;
38b7b2
+	int retlen = 0;
38b7b2
+	int j = 0;
38b7b2
+	int i;
38b7b2
+
38b7b2
+	for (i = 0; i < in_bytes; i++) {
38b7b2
+		if (!in[i])
38b7b2
+			break;
38b7b2
+		if (j >= (out_bytes - 2))
38b7b2
+			break;
38b7b2
+		/* skip leading spaces */
38b7b2
+		if (!retlen && (in[i] == ' '))
38b7b2
+			continue;
38b7b2
+		/* replace one or more spaces with _ */
38b7b2
+		if (in[i] == ' ') {
38b7b2
+			in_space = 1;
38b7b2
+			continue;
38b7b2
+		}
38b7b2
+		/* spaces are finished so insert _ */
38b7b2
+		if (in_space) {
38b7b2
+			out[j++] = '_';
38b7b2
+			in_space = 0;
38b7b2
+			retlen++;
38b7b2
+		}
38b7b2
+		out[j++] = in[i];
38b7b2
+		retlen++;
38b7b2
+	}
38b7b2
+	return retlen;
38b7b2
+}
38b7b2
+
38b7b2
+static int _to_hex(const unsigned char *in, int in_bytes, unsigned char *out, int out_bytes)
38b7b2
+{
38b7b2
+	int off = 0;
38b7b2
+	int num;
38b7b2
+	int i;
38b7b2
+
38b7b2
+	for (i = 0; i < in_bytes; i++) {
38b7b2
+		num = sprintf((char *)out + off, "%02x", in[i]);
38b7b2
+		if (num < 0)
38b7b2
+			break;
38b7b2
+		off += num;
38b7b2
+		if (off + 2 >= out_bytes)
38b7b2
+			break;
38b7b2
+	}
38b7b2
+	return off;
38b7b2
+}
38b7b2
+
38b7b2
+#define ID_BUFSIZE 1024
38b7b2
+
38b7b2
+/*
38b7b2
+ * based on linux kernel function
38b7b2
+ */
38b7b2
+int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list *ids)
38b7b2
+{
38b7b2
+	char id[ID_BUFSIZE];
38b7b2
+	unsigned char tmp_str[ID_BUFSIZE];
38b7b2
+	const unsigned char *d, *cur_id_str;
38b7b2
+	size_t id_len = ID_BUFSIZE;
38b7b2
+	int id_size = -1;
38b7b2
+	uint8_t cur_id_size = 0;
38b7b2
+
38b7b2
+	memset(id, 0, ID_BUFSIZE);
38b7b2
+	for (d = vpd_data + 4;
38b7b2
+	     d < vpd_data + vpd_datalen;
38b7b2
+	     d += d[3] + 4) {
38b7b2
+		memset(tmp_str, 0, sizeof(tmp_str));
38b7b2
+
38b7b2
+		switch (d[1] & 0xf) {
38b7b2
+		case 0x1:
38b7b2
+			/* T10 Vendor ID */
38b7b2
+			cur_id_size = d[3];
38b7b2
+			if (cur_id_size + 4 > id_len)
38b7b2
+				cur_id_size = id_len - 4;
38b7b2
+			cur_id_str = d + 4;
38b7b2
+			format_t10_id(cur_id_str, cur_id_size, tmp_str, sizeof(tmp_str));
38b7b2
+			id_size = snprintf(id, ID_BUFSIZE, "t10.%s", tmp_str);
38b7b2
+			if (id_size < 0)
38b7b2
+				break;
38b7b2
+			if (id_size >= ID_BUFSIZE)
38b7b2
+				id_size = ID_BUFSIZE - 1;
38b7b2
+			add_wwid(id, 1, ids);
38b7b2
+			break;
38b7b2
+		case 0x2:
38b7b2
+			/* EUI-64 */
38b7b2
+			cur_id_size = d[3];
38b7b2
+			cur_id_str = d + 4;
38b7b2
+			switch (cur_id_size) {
38b7b2
+			case 8:
38b7b2
+				_to_hex(cur_id_str, 8, tmp_str, sizeof(tmp_str));
38b7b2
+				id_size = snprintf(id, ID_BUFSIZE, "eui.%s", tmp_str);
38b7b2
+				break;
38b7b2
+			case 12:
38b7b2
+				_to_hex(cur_id_str, 12, tmp_str, sizeof(tmp_str));
38b7b2
+				id_size = snprintf(id, ID_BUFSIZE, "eui.%s", tmp_str);
38b7b2
+				break;
38b7b2
+			case 16:
38b7b2
+				_to_hex(cur_id_str, 16, tmp_str, sizeof(tmp_str));
38b7b2
+				id_size = snprintf(id, ID_BUFSIZE, "eui.%s", tmp_str);
38b7b2
+				break;
38b7b2
+			default:
38b7b2
+				break;
38b7b2
+			}
38b7b2
+			if (id_size < 0)
38b7b2
+				break;
38b7b2
+			if (id_size >= ID_BUFSIZE)
38b7b2
+				id_size = ID_BUFSIZE - 1;
38b7b2
+			add_wwid(id, 2, ids);
38b7b2
+			break;
38b7b2
+		case 0x3:
38b7b2
+			/* NAA */
38b7b2
+			cur_id_size = d[3];
38b7b2
+			cur_id_str = d + 4;
38b7b2
+			switch (cur_id_size) {
38b7b2
+			case 8:
38b7b2
+				_to_hex(cur_id_str, 8, tmp_str, sizeof(tmp_str));
38b7b2
+				id_size = snprintf(id, ID_BUFSIZE, "naa.%s", tmp_str);
38b7b2
+				break;
38b7b2
+			case 16:
38b7b2
+				_to_hex(cur_id_str, 16, tmp_str, sizeof(tmp_str));
38b7b2
+				id_size = snprintf(id, ID_BUFSIZE, "naa.%s", tmp_str);
38b7b2
+				break;
38b7b2
+			default:
38b7b2
+				break;
38b7b2
+			}
38b7b2
+			if (id_size < 0)
38b7b2
+				break;
38b7b2
+			if (id_size >= ID_BUFSIZE)
38b7b2
+				id_size = ID_BUFSIZE - 1;
38b7b2
+			add_wwid(id, 3, ids);
38b7b2
+			break;
38b7b2
+		case 0x8:
38b7b2
+			/* SCSI name string */
38b7b2
+			cur_id_size = d[3];
38b7b2
+			cur_id_str = d + 4;
38b7b2
+			if (cur_id_size >= id_len)
38b7b2
+				cur_id_size = id_len - 1;
38b7b2
+			memcpy(id, cur_id_str, cur_id_size);
38b7b2
+			id_size = cur_id_size;
38b7b2
+
38b7b2
+			/*
38b7b2
+			 * Not in the kernel version, copying multipath code,
38b7b2
+			 * which checks if this string begins with naa or eui
38b7b2
+			 * and if so does tolower() on the chars.
38b7b2
+			 */
38b7b2
+			if (!strncmp(id, "naa.", 4) || !strncmp(id, "eui.", 4)) {
38b7b2
+				int i;
38b7b2
+				for (i = 0; i < id_size; i++)
38b7b2
+					id[i] = tolower(id[i]);
38b7b2
+			}
38b7b2
+			add_wwid(id, 8, ids);
38b7b2
+			break;
38b7b2
+		default:
38b7b2
+			break;
38b7b2
+		}
38b7b2
+	}
38b7b2
+
38b7b2
+	return id_size;
38b7b2
+}
38b7b2
-- 
38b7b2
2.34.3
38b7b2