mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
d952358636
Device quirks may cause sysfs wwid file to change what it displays, from a bogus eui... string to an nvme... string. The old wwid may be saved in system.devices, so recognizing the device requires finding the old value from libnvme. After matching the old bogus value using libnvme, system.devices is updated with the current sysfs wwid value.
309 lines
6.7 KiB
C
309 lines
6.7 KiB
C
/*
|
|
* 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/commands/toolcontext.h"
|
|
#include "lib/device/device.h"
|
|
#include "lib/device/device_id.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/stat.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
|
|
/*
|
|
* Remove leading spaces.
|
|
* Remove trailing spaces.
|
|
* Replace each space with underscore.
|
|
* Skip quotes, non-ascii, non-printable.
|
|
*/
|
|
int format_general_id(const char *in, size_t in_bytes, unsigned char *out, size_t out_bytes)
|
|
{
|
|
const char *end;
|
|
size_t end_bytes = strlen(in);
|
|
int retlen = 0;
|
|
unsigned j = 0;
|
|
unsigned i;
|
|
|
|
if (!end_bytes)
|
|
return 0;
|
|
|
|
end = in + end_bytes - 1;
|
|
while ((end > in) && (*end == ' ')) {
|
|
end--;
|
|
end_bytes--;
|
|
}
|
|
|
|
for (i = 0; i < end_bytes; i++) {
|
|
if (!in[i])
|
|
break;
|
|
if (j >= (out_bytes - 2))
|
|
break;
|
|
/* skip leading spaces */
|
|
if (!retlen && (in[i] == ' '))
|
|
continue;
|
|
/* skip non-ascii non-printable characters */
|
|
if (!isascii(in[i]) || !isprint(in[i]))
|
|
continue;
|
|
/* skip quote */
|
|
if (in[i] == '"')
|
|
continue;
|
|
/* replace each space with _ */
|
|
if (in[i] == ' ')
|
|
out[j++] = '_';
|
|
else
|
|
out[j++] = in[i];
|
|
retlen++;
|
|
}
|
|
return retlen;
|
|
}
|
|
|
|
/*
|
|
* Remove leading spaces.
|
|
* Remove trailing spaces.
|
|
* Replace series of spaces with a single _.
|
|
* Skip quotes, non-ascii, non-printable.
|
|
*/
|
|
int format_t10_id(const unsigned char *in, size_t in_bytes, unsigned char *out, size_t out_bytes)
|
|
{
|
|
int in_space = 0;
|
|
int retlen = 0;
|
|
unsigned j = 0;
|
|
unsigned 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;
|
|
/* skip non-ascii non-printable characters */
|
|
if (!isascii(in[i]) || !isprint(in[i]))
|
|
continue;
|
|
/* skip quote */
|
|
if (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;
|
|
int type;
|
|
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 ((size_t)(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;
|
|
dev_add_scsi_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;
|
|
dev_add_scsi_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;
|
|
dev_add_scsi_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;
|
|
|
|
/*
|
|
* if naa or eui ids are provided as scsi names,
|
|
* consider them to be naa/eui types.
|
|
*/
|
|
if (!memcmp(id, "eui.", 4))
|
|
type = 2;
|
|
else if (!memcmp(id, "naa.", 4))
|
|
type = 3;
|
|
else
|
|
type = 8;
|
|
|
|
/*
|
|
* 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 ((type == 2) || (type == 3)) {
|
|
unsigned i;
|
|
for (i = 0; i < strlen(id); i++)
|
|
id[i] = tolower(id[i]);
|
|
}
|
|
dev_add_scsi_wwid(id, type, ids);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return id_size;
|
|
}
|
|
|
|
int parse_vpd_serial(const unsigned char *in, char *out, size_t outsize)
|
|
{
|
|
uint8_t len_buf[2] __attribute__((aligned(8))) = { 0 };
|
|
size_t len;
|
|
|
|
/* parsing code from multipath tools */
|
|
/* ignore in[0] and in[1] */
|
|
/* len is in[2] and in[3] */
|
|
/* serial begins at in[4] */
|
|
|
|
len_buf[0] = in[2];
|
|
len_buf[1] = in[3];
|
|
len = len_buf[0] << 8 | len_buf[1];
|
|
|
|
if (outsize == 0)
|
|
return 0;
|
|
|
|
if (len > DEV_WWID_SIZE)
|
|
len = DEV_WWID_SIZE;
|
|
/*
|
|
* Strip leading and trailing whitespace
|
|
*/
|
|
while (len > 0 && in[len + 3] == ' ')
|
|
--len;
|
|
while (len > 0 && in[4] == ' ') {
|
|
++in;
|
|
--len;
|
|
}
|
|
|
|
if (len >= outsize)
|
|
len = outsize - 1;
|
|
|
|
if (len > 0) {
|
|
memcpy(out, in + 4, len);
|
|
out[len] = '\0';
|
|
}
|
|
return len;
|
|
}
|
|
|