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