From e67b79e9515b0d6c5a39f02a43b37908fc45b81a Mon Sep 17 00:00:00 2001
From: dpgilbert <dpgilbert@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Mon, 17 Nov 2003 11:54:32 +0000
Subject: [PATCH] bail out early (before VPD INQUIRY + LOG SENSE) if MODE SENSE
 response malformed (heuristic to skip USB devices before we lock them up)

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@1247 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/scsiprint.c   | 178 +++++++++++++++++++++++++---------------------
 sm5/scsiprint.cpp | 178 +++++++++++++++++++++++++---------------------
 2 files changed, 194 insertions(+), 162 deletions(-)

diff --git a/sm5/scsiprint.c b/sm5/scsiprint.c
index 84abb8345..33c125f0f 100644
--- a/sm5/scsiprint.c
+++ b/sm5/scsiprint.c
@@ -40,7 +40,7 @@
 
 #define GBUF_SIZE 65535
 
-const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.61 2003/11/17 03:10:40 ballen4705 Exp $"
+const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.62 2003/11/17 11:54:32 dpgilbert Exp $"
 EXTERN_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // control block which points to external global control variables
@@ -69,6 +69,33 @@ static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
 static int modese_len = 0;
 
 
+// Compares failure type to policy in effect, and either exits or
+// simply returns to the calling routine.
+static void failuretest(int type, int returnvalue)
+{
+    // If this is an error in an "optional" SMART command
+    if (type == OPTIONAL_CMD) {
+        if (con->conservative) {
+            pout("An optional SMART command has failed: exiting.\n"
+                 "To continue, set the tolerance level to something other "
+                 "than 'conservative'\n");
+            EXIT(returnvalue);
+        }
+        return;
+    }
+    // If this is an error in a "mandatory" SMART command
+    if (type==MANDATORY_CMD) {
+        if (con->permissive)
+            return;
+        pout("A mandatory SMART command has failed: exiting. To continue, "
+             "use the -T option to set the tolerance level to 'permissive'\n");
+        exit(returnvalue);
+    }
+    pout("Smartctl internal error in failuretest(type=%d). Please contact "
+         "%s\n",type,PROJECTHOME);
+    exit(returnvalue|FAILCMD);
+}
+
 static void scsiGetSupportedLogPages(int device)
 {
     int i, err;
@@ -571,6 +598,25 @@ static const char * peripheral_dt_arr[] = {
         "simplified disk",
         "optical card reader"
 };
+
+static const char * transport_proto_arr[] = {
+        "Fibre channel (FCP-2)",
+        "Parallel SCSI (SPI-4)",
+        "SSA",
+        "IEEE 1394 (SBP-2)",
+        "RDMA (SRP)",
+        "iSCSI",
+        "SAS",
+        "ADT",
+        "0x8",
+        "0x9",
+        "0xa",
+        "0xb",
+        "0xc",
+        "0xd",
+        "0xe",
+        "0xf"
+};
  
 /* Returns 0 on success */
 static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
@@ -580,7 +626,7 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     char revision[5];
     char timedatetz[64];
     struct scsi_iec_mode_page iec;
-    int err, len;
+    int err, iec_err, len, val;
     int is_tape = 0;
     int peri_dt = 0;
         
@@ -598,52 +644,51 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     if (! all)
 	return 0;
 
-    if (len >= 36) {
-        memset(manufacturer, 0, sizeof(manufacturer));
-        strncpy(manufacturer, (char *)&gBuf[8], 8);
-     
-        memset(product, 0, sizeof(product));
-        strncpy(product, (char *)&gBuf[16], 16);
-            
-        memset(revision, 0, sizeof(revision));
-        strncpy(revision, (char *)&gBuf[32], 4);
-        pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
-
-	/* 
-	   Doug: for a bad USB device, the code hangs in the following
-	   line within scsiInquiryVpd():
-
-	   status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-	   
-	   and within do_scsi_cmnd_io() it hangs in the line:
-
-	   status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND , &wrk);
-
-	   which never returns.  Would it be possible to put in a
-	   sanity check to detect such devices and exit with an error
-	   message, before calling scsiInquiryVpd()?
-
-	*/
-        if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
-            /* should use VPD page 0x83 and fall back to this page (0x80)
-             * if 0x83 not supported. NAA requires a lot of decoding code */
-            len = gBuf[3];
-            gBuf[4 + len] = '\0';
-            pout("Serial number: %s\n", &gBuf[4]);
-        }
-        else if (con->reportscsiioctl > 0) {
-            QUIETON(con);
-            if (SIMPLE_ERR_BAD_RESP == err)
-                pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
-            else
-                pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
+    if (len < 36) {
+        QUIETON(con);
+        pout("Short INQUIRY response, skip product id\n");
+        QUIETOFF(con);
+        return 1;
+    }
+    memset(manufacturer, 0, sizeof(manufacturer));
+    strncpy(manufacturer, (char *)&gBuf[8], 8);
+ 
+    memset(product, 0, sizeof(product));
+    strncpy(product, (char *)&gBuf[16], 16);
+        
+    memset(revision, 0, sizeof(revision));
+    strncpy(revision, (char *)&gBuf[32], 4);
+    pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
+
+    /* Do this here to try and detect badly conforming devices (some USB
+       keys) that will lock up on a InquiryVpd or log sense or ... */
+    if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        if (SIMPLE_ERR_BAD_RESP == iec_err) {
+            pout(">> Terminate command early due to bad response to IEC "
+                 "mode page\n");
             QUIETOFF(con);
+            gIecMPage = 0;
+            return 1;
         }
-    } else {
+    } else
+        modese_len = iec.modese_len;
+
+    if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
+        /* should use VPD page 0x83 and fall back to this page (0x80)
+         * if 0x83 not supported. NAA requires a lot of decoding code */
+        len = gBuf[3];
+        gBuf[4 + len] = '\0';
+        pout("Serial number: %s\n", &gBuf[4]);
+    }
+    else if (con->reportscsiioctl > 0) {
         QUIETON(con);
-        pout("Short INQUIRY response, skip product id\n");
+        if (SIMPLE_ERR_BAD_RESP == err)
+            pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
+        else
+            pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
         QUIETOFF(con);
     }
+
     // print SCSI peripheral device type
     if (peri_dt < (int)(sizeof(peripheral_dt_arr) / 
                         sizeof(peripheral_dt_arr[0])))
@@ -651,6 +696,11 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     else
         pout("Device type: <%d>\n", peri_dt);
 
+    // See if transport protocol is known
+    val = scsiFetchTransportProtocol(device, modese_len);
+    if ((val >= 0) && (val <= 0xf))
+        pout("Transport protocol: %s\n", transport_proto_arr[val]);
+
     // print current time and date and timezone
     dateandtimezone(timedatetz);
     pout("Local Time is: %s\n", timedatetz);
@@ -672,27 +722,20 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
 	return 0;
     }
    
-    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+    if (iec_err) {
 	if (!is_tape) {
             QUIETON(con);
 	    pout("Device does not support SMART");
             if (con->reportscsiioctl > 0)
-	        pout(" [%s]\n", scsiErrString(err));
+	        pout(" [%s]\n", scsiErrString(iec_err));
             else
 	        pout("\n");
-            if (SIMPLE_ERR_BAD_RESP == err) {
-                pout(">> Terminate command early due to bad response to IEC "
-                     "mode page\n");
-                QUIETOFF(con);
-                gIecMPage = 0;
-                return 1;
-            }
             QUIETOFF(con);
         }
         gIecMPage = 0;
         return 0;
-    } else
-        modese_len = iec.modese_len;
+    }
+
     if (!is_tape)
         pout("Device supports SMART and is %s\n",
              (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
@@ -792,33 +835,6 @@ void scsiPrintTemp(int device)
         pout("Drive Trip Temperature:        %d C\n", trip);
 }
 
-// Compares failure type to policy in effect, and either exits or
-// simply returns to the calling routine.
-static void failuretest(int type, int returnvalue)
-{
-    // If this is an error in an "optional" SMART command
-    if (type == OPTIONAL_CMD) {
-        if (con->conservative) {
-            pout("An optional SMART command has failed: exiting.\n"
-                 "To continue, set the tolerance level to something other "
-                 "than 'conservative'\n");
-            EXIT(returnvalue);
-        }
-        return;
-    }
-    // If this is an error in a "mandatory" SMART command
-    if (type==MANDATORY_CMD) {
-        if (con->permissive)
-            return;
-        pout("A mandatory SMART command has failed: exiting. To continue, "
-             "use the -T option to set the tolerance level to 'permissive'\n");
-        exit(returnvalue);
-    }
-    pout("Smartctl internal error in failuretest(type=%d). Please contact "
-         "%s\n",type,PROJECTHOME);
-    exit(returnvalue|FAILCMD);
-}
-
 /* Main entry point used by smartctl command. Return 0 for success */
 int scsiPrintMain(int fd)
 {
diff --git a/sm5/scsiprint.cpp b/sm5/scsiprint.cpp
index 236d3efbc..7b23742d8 100644
--- a/sm5/scsiprint.cpp
+++ b/sm5/scsiprint.cpp
@@ -40,7 +40,7 @@
 
 #define GBUF_SIZE 65535
 
-const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.61 2003/11/17 03:10:40 ballen4705 Exp $"
+const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.62 2003/11/17 11:54:32 dpgilbert Exp $"
 EXTERN_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // control block which points to external global control variables
@@ -69,6 +69,33 @@ static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
 static int modese_len = 0;
 
 
+// Compares failure type to policy in effect, and either exits or
+// simply returns to the calling routine.
+static void failuretest(int type, int returnvalue)
+{
+    // If this is an error in an "optional" SMART command
+    if (type == OPTIONAL_CMD) {
+        if (con->conservative) {
+            pout("An optional SMART command has failed: exiting.\n"
+                 "To continue, set the tolerance level to something other "
+                 "than 'conservative'\n");
+            EXIT(returnvalue);
+        }
+        return;
+    }
+    // If this is an error in a "mandatory" SMART command
+    if (type==MANDATORY_CMD) {
+        if (con->permissive)
+            return;
+        pout("A mandatory SMART command has failed: exiting. To continue, "
+             "use the -T option to set the tolerance level to 'permissive'\n");
+        exit(returnvalue);
+    }
+    pout("Smartctl internal error in failuretest(type=%d). Please contact "
+         "%s\n",type,PROJECTHOME);
+    exit(returnvalue|FAILCMD);
+}
+
 static void scsiGetSupportedLogPages(int device)
 {
     int i, err;
@@ -571,6 +598,25 @@ static const char * peripheral_dt_arr[] = {
         "simplified disk",
         "optical card reader"
 };
+
+static const char * transport_proto_arr[] = {
+        "Fibre channel (FCP-2)",
+        "Parallel SCSI (SPI-4)",
+        "SSA",
+        "IEEE 1394 (SBP-2)",
+        "RDMA (SRP)",
+        "iSCSI",
+        "SAS",
+        "ADT",
+        "0x8",
+        "0x9",
+        "0xa",
+        "0xb",
+        "0xc",
+        "0xd",
+        "0xe",
+        "0xf"
+};
  
 /* Returns 0 on success */
 static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
@@ -580,7 +626,7 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     char revision[5];
     char timedatetz[64];
     struct scsi_iec_mode_page iec;
-    int err, len;
+    int err, iec_err, len, val;
     int is_tape = 0;
     int peri_dt = 0;
         
@@ -598,52 +644,51 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     if (! all)
 	return 0;
 
-    if (len >= 36) {
-        memset(manufacturer, 0, sizeof(manufacturer));
-        strncpy(manufacturer, (char *)&gBuf[8], 8);
-     
-        memset(product, 0, sizeof(product));
-        strncpy(product, (char *)&gBuf[16], 16);
-            
-        memset(revision, 0, sizeof(revision));
-        strncpy(revision, (char *)&gBuf[32], 4);
-        pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
-
-	/* 
-	   Doug: for a bad USB device, the code hangs in the following
-	   line within scsiInquiryVpd():
-
-	   status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-	   
-	   and within do_scsi_cmnd_io() it hangs in the line:
-
-	   status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND , &wrk);
-
-	   which never returns.  Would it be possible to put in a
-	   sanity check to detect such devices and exit with an error
-	   message, before calling scsiInquiryVpd()?
-
-	*/
-        if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
-            /* should use VPD page 0x83 and fall back to this page (0x80)
-             * if 0x83 not supported. NAA requires a lot of decoding code */
-            len = gBuf[3];
-            gBuf[4 + len] = '\0';
-            pout("Serial number: %s\n", &gBuf[4]);
-        }
-        else if (con->reportscsiioctl > 0) {
-            QUIETON(con);
-            if (SIMPLE_ERR_BAD_RESP == err)
-                pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
-            else
-                pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
+    if (len < 36) {
+        QUIETON(con);
+        pout("Short INQUIRY response, skip product id\n");
+        QUIETOFF(con);
+        return 1;
+    }
+    memset(manufacturer, 0, sizeof(manufacturer));
+    strncpy(manufacturer, (char *)&gBuf[8], 8);
+ 
+    memset(product, 0, sizeof(product));
+    strncpy(product, (char *)&gBuf[16], 16);
+        
+    memset(revision, 0, sizeof(revision));
+    strncpy(revision, (char *)&gBuf[32], 4);
+    pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
+
+    /* Do this here to try and detect badly conforming devices (some USB
+       keys) that will lock up on a InquiryVpd or log sense or ... */
+    if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        if (SIMPLE_ERR_BAD_RESP == iec_err) {
+            pout(">> Terminate command early due to bad response to IEC "
+                 "mode page\n");
             QUIETOFF(con);
+            gIecMPage = 0;
+            return 1;
         }
-    } else {
+    } else
+        modese_len = iec.modese_len;
+
+    if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
+        /* should use VPD page 0x83 and fall back to this page (0x80)
+         * if 0x83 not supported. NAA requires a lot of decoding code */
+        len = gBuf[3];
+        gBuf[4 + len] = '\0';
+        pout("Serial number: %s\n", &gBuf[4]);
+    }
+    else if (con->reportscsiioctl > 0) {
         QUIETON(con);
-        pout("Short INQUIRY response, skip product id\n");
+        if (SIMPLE_ERR_BAD_RESP == err)
+            pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
+        else
+            pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
         QUIETOFF(con);
     }
+
     // print SCSI peripheral device type
     if (peri_dt < (int)(sizeof(peripheral_dt_arr) / 
                         sizeof(peripheral_dt_arr[0])))
@@ -651,6 +696,11 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     else
         pout("Device type: <%d>\n", peri_dt);
 
+    // See if transport protocol is known
+    val = scsiFetchTransportProtocol(device, modese_len);
+    if ((val >= 0) && (val <= 0xf))
+        pout("Transport protocol: %s\n", transport_proto_arr[val]);
+
     // print current time and date and timezone
     dateandtimezone(timedatetz);
     pout("Local Time is: %s\n", timedatetz);
@@ -672,27 +722,20 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
 	return 0;
     }
    
-    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+    if (iec_err) {
 	if (!is_tape) {
             QUIETON(con);
 	    pout("Device does not support SMART");
             if (con->reportscsiioctl > 0)
-	        pout(" [%s]\n", scsiErrString(err));
+	        pout(" [%s]\n", scsiErrString(iec_err));
             else
 	        pout("\n");
-            if (SIMPLE_ERR_BAD_RESP == err) {
-                pout(">> Terminate command early due to bad response to IEC "
-                     "mode page\n");
-                QUIETOFF(con);
-                gIecMPage = 0;
-                return 1;
-            }
             QUIETOFF(con);
         }
         gIecMPage = 0;
         return 0;
-    } else
-        modese_len = iec.modese_len;
+    }
+
     if (!is_tape)
         pout("Device supports SMART and is %s\n",
              (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
@@ -792,33 +835,6 @@ void scsiPrintTemp(int device)
         pout("Drive Trip Temperature:        %d C\n", trip);
 }
 
-// Compares failure type to policy in effect, and either exits or
-// simply returns to the calling routine.
-static void failuretest(int type, int returnvalue)
-{
-    // If this is an error in an "optional" SMART command
-    if (type == OPTIONAL_CMD) {
-        if (con->conservative) {
-            pout("An optional SMART command has failed: exiting.\n"
-                 "To continue, set the tolerance level to something other "
-                 "than 'conservative'\n");
-            EXIT(returnvalue);
-        }
-        return;
-    }
-    // If this is an error in a "mandatory" SMART command
-    if (type==MANDATORY_CMD) {
-        if (con->permissive)
-            return;
-        pout("A mandatory SMART command has failed: exiting. To continue, "
-             "use the -T option to set the tolerance level to 'permissive'\n");
-        exit(returnvalue);
-    }
-    pout("Smartctl internal error in failuretest(type=%d). Please contact "
-         "%s\n",type,PROJECTHOME);
-    exit(returnvalue|FAILCMD);
-}
-
 /* Main entry point used by smartctl command. Return 0 for success */
 int scsiPrintMain(int fd)
 {
-- 
GitLab