Some older SCSI devices return a SCSI-2 style vpd page 0x83, instead of
a SPC-2/3 format one. The SCSI-2 page 83 format returns an IEEE WWN in
binary encoded hexi-decimal in the 16 bytes following the initial
4-byte page 83 reply header.

Check the 7th byte of the vpd page 83 buffer to determine whether this
is a SCSI-2 or SPC-2/3 confomant one. Byte 7 is the 3rd byte of first
Identification descriptor in a SPC-2/3 confromant vpd page 83. This is a
reserved field, and is guaranteed to be 0. If it is not zero, then it is
likely the 3rd byte of a SCSI-2 Identifier (The first 3 bytes of the ID
are the Organizationally Unique Identifier). Both the sg_inq and scsi_id
commands handle vpd page 83 this way. To make sure that the WWID which
multipath reads directly from the device matches, it should handle this
format as well.

Signed-off-by: Benjamin Marzinski <[email protected]>
---

Changes in v2 (suggested by Martin Wilck):
- check that the vpd page is large enough to be a valid SCSI-2 style
  one.

 libmultipath/discovery.c | 13 +++++++++
 tests/vpd.c              | 58 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+)

diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 0efb8213..f59fa6d4 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -1208,6 +1208,18 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
        if (out_len <= 1)
                return 0;
 
+       /*
+        * Not a valid SPC-2/3 vpd page 83. Assume it's a SCSI-2 style
+        * descriptor.
+        */
+       if (in_len >= 20 && in[6] != 0) {
+               len = 0;
+               vpd_type = 0x3;
+               vpd_len = in_len - 4;
+               vpd = in + 4;
+               goto decode;
+       }
+
        d = in + 4;
        while (d <= in + in_len - 4) {
                bool invalid = false;
@@ -1323,6 +1335,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
        vpd_type = vpd[1] & 0xf;
        vpd_len = vpd[3];
        vpd += 4;
+decode:
        /* untaint vpd_len for coverity */
        if (vpd_len > WWID_SIZE) {
                condlog(1, "%s: suspicious designator length %zu truncated to 
%u",
diff --git a/tests/vpd.c b/tests/vpd.c
index f9e22cd8..b0217948 100644
--- a/tests/vpd.c
+++ b/tests/vpd.c
@@ -331,6 +331,28 @@ static int create_vpd83(unsigned char *buf, size_t bufsiz, 
const char *id,
        return n + 4;
 }
 
+/**
+ * create_pre_spc3_vpd83() - create pre-SPC3 "device identification" VPD page
+ *
+ * @buf, @bufsiz: see above.
+ * @id:                input ID (third byte must be non-zero)
+ *
+ * Create a pre-SPC3 "device identification" VPD page. See comments in
+ * sg3_utils/src/sg_inq.c for details
+ *
+ * Return:     VPD page length.
+ */
+static int create_pre_spc3_vpd83(unsigned char *buf, size_t bufsize,
+                                const char *id)
+{
+       memset(buf, 0, bufsize);
+       buf[1] = 0x83;
+
+       hex2bin(buf + 4, id, 16, 32);
+       put_unaligned_be16(16, buf + 2);
+       return 20;
+}
+
 /**
  * assert_correct_wwid() - test that a retrieved WWID matches expectations
  * @test:      test name
@@ -479,6 +501,30 @@ static void test_vpd_str_ ## typ ## _ ## len ## _ ## 
wlen(void **state) \
                            test_id, vt->wwid);                         \
 }
 
+/**
+ * test_vpd_prespc3_WLEN() - test code for pre-SPC3 VPD 83
+ * @WLEN:      WWID buffer size
+ */
+#define make_test_vpd_prespc3(wlen)                                    \
+static void test_vpd_prespc3_ ## wlen(void **state)                    \
+{                                                                      \
+       struct vpdtest *vt = *state;                                    \
+       int n, ret;                                                     \
+       int exp_len;                                                    \
+                                                                       \
+       /* returned size is always uneven */                            \
+       exp_len = wlen > 33 ? 33 :                                      \
+               wlen % 2 == 0 ? wlen - 1 : wlen - 2;                    \
+                                                                       \
+       n = create_pre_spc3_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf),       \
+                                 test_id);                             \
+       wrap_will_return(WRAP_IOCTL, n);                                \
+       wrap_will_return(WRAP_IOCTL, vt->vpdbuf);                       \
+       ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);                \
+       assert_correct_wwid("test_vpd_prespc3_" #wlen, exp_len, ret,    \
+                           '3', 0, true, test_id, vt->wwid);           \
+}
+
 /**
  * test_vpd_naa_NAA_WLEN() - test code for VPD 83 NAA designation
  * @NAA:       Network Name Authority (2, 3, 5, or 6)
@@ -814,6 +860,13 @@ make_test_vpd_str(18, 20, 17)
 make_test_vpd_str(18, 20, 16)
 make_test_vpd_str(18, 20, 15)
 
+/* PRE-SPC3, WWID size: 34 */
+make_test_vpd_prespc3(40)
+make_test_vpd_prespc3(34)
+make_test_vpd_prespc3(33)
+make_test_vpd_prespc3(32)
+make_test_vpd_prespc3(20)
+
 static int test_vpd(void)
 {
        const struct CMUnitTest tests[] = {
@@ -945,6 +998,11 @@ static int test_vpd(void)
                cmocka_unit_test(test_vpd_str_18_20_17),
                cmocka_unit_test(test_vpd_str_18_20_16),
                cmocka_unit_test(test_vpd_str_18_20_15),
+               cmocka_unit_test(test_vpd_prespc3_40),
+               cmocka_unit_test(test_vpd_prespc3_34),
+               cmocka_unit_test(test_vpd_prespc3_33),
+               cmocka_unit_test(test_vpd_prespc3_32),
+               cmocka_unit_test(test_vpd_prespc3_20),
        };
        return cmocka_run_group_tests(tests, setup, teardown);
 }
-- 
2.53.0


Reply via email to