From 26bd423dd62d90b53491b869e2c663581f7789ab Mon Sep 17 00:00:00 2001
From: chrfranke <chrfranke@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Sat, 21 Jul 2007 20:59:41 +0000
Subject: [PATCH] Added '-F swapid' to fix ATA identify string byte ordering.
 Added '-q noserial' to suppress serial number output.

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@2417 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/CHANGELOG     |  5 ++++-
 sm5/atacmds.cpp   | 41 ++++++++++++++++++++++++++++++++---------
 sm5/ataprint.cpp  | 32 ++++++++++++++------------------
 sm5/extern.h      |  4 +++-
 sm5/scsiprint.cpp | 36 +++++++++++++++++++-----------------
 sm5/smartctl.8.in | 18 +++++++++++++-----
 sm5/smartctl.cpp  | 23 +++++++++++++++--------
 7 files changed, 100 insertions(+), 59 deletions(-)

diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index ecb7bffcb..b53788111 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.623 2007/07/20 21:00:42 chrfranke Exp $
+$Id: CHANGELOG,v 1.624 2007/07/21 20:59:41 chrfranke Exp $
 
 The most recent version of this file is:
 http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup
@@ -33,6 +33,9 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] smartctl: Added '-F swapid' to fix ATA identify string byte
+       ordering. Added '-q noserial' to suppress serial number output.
+
   [CF] Windows: Added '/dev/n?st<n>' as alternate device names for SCSI
        tapes. These names are also used by Cygwin's /dev emulation layer.
        Thanks to Corinna Vinschen (Cygwin project lead) for pointing this
diff --git a/sm5/atacmds.cpp b/sm5/atacmds.cpp
index 740fb4bbc..a3af95fd9 100644
--- a/sm5/atacmds.cpp
+++ b/sm5/atacmds.cpp
@@ -36,7 +36,7 @@
 #include "extern.h"
 #include "utility.h"
 
-const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.185 2007/07/18 21:18:09 chrfranke Exp $"
+const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.186 2007/07/21 20:59:41 chrfranke Exp $"
 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
 
 // to hold onto exit code for atexit routine
@@ -514,6 +514,25 @@ void swap8(char *location){
   return;
 }
 
+// Invalidate serial number and adjust checksum in IDENTIFY data
+static void invalidate_serno(ata_identify_device * id){
+  unsigned char sum = 0;
+  for (unsigned i = 0; i < sizeof(id->serial_no); i++) {
+    sum += id->serial_no[i]; sum -= id->serial_no[i] = 'X';
+  }
+#ifndef __NetBSD__
+  bool must_swap = !!isbigendian();
+  if (must_swap)
+    swapx(id->words088_255+255-88);
+#endif
+  if ((id->words088_255[255-88] & 0x00ff) == 0x00a5)
+    id->words088_255[255-88] += sum << 8;
+#ifndef __NetBSD__
+  if (must_swap)
+    swapx(id->words088_255+255-88);
+#endif
+}
+
 static char *commandstrings[]={
   "SMART ENABLE",
   "SMART DISABLE",
@@ -532,15 +551,15 @@ static char *commandstrings[]={
   "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n"
 };
 
-void prettyprint(unsigned char *stuff, char *name){
-  int i,j;
+static void prettyprint(const unsigned char *p, const char *name){
   pout("\n===== [%s] DATA START (BASE-16) =====\n", name);
-  for (i=0; i<32; i++){
-    pout("%03d-%03d: ", 16*i, 16*(i+1)-1);
-    for (j=0; j<15; j++)
-      pout("%02x ",*stuff++);
-    pout("%02x\n",*stuff++);
-  }
+  for (int i=0; i<512; i+=16, p+=16)
+    // print complete line to avoid slow tty output and extra lines in syslog.
+    pout("%03d-%03d: %02x %02x %02x %02x %02x %02x %02x %02x "
+                    "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+         i, i+16-1,
+         p[ 0], p[ 1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7],
+         p[ 8], p[ 9], p[10], p[11], p[12], p[13], p[14], p[15]);
   pout("===== [%s] DATA END (512 Bytes) =====\n\n", name);
 }
 
@@ -621,6 +640,10 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
     retval=ata_command_interface(device, command, select, data);
   }
 
+  // If requested, invalidate serial number before any printing is done
+  if ((command == IDENTIFY || command == PIDENTIFY) && !retval && con->dont_print_serial)
+    invalidate_serno((ata_identify_device *)data);
+
   // If reporting is enabled, say what output was produced by the command
   if (con->reportataioctl){
     if (errno)
diff --git a/sm5/ataprint.cpp b/sm5/ataprint.cpp
index 28a6f2008..72bef8942 100644
--- a/sm5/ataprint.cpp
+++ b/sm5/ataprint.cpp
@@ -41,7 +41,7 @@
 #include "utility.h"
 #include "knowndrives.h"
 
-const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.182 2007/07/20 15:56:42 shattered Exp $"
+const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.183 2007/07/21 20:59:41 chrfranke Exp $"
 ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
@@ -93,27 +93,25 @@ void trim(char *out, const char *in)
 // Convenience function for formatting strings from ata_identify_device
 void format_ata_string(char *out, const char *in, int n)
 {
-  char tmp[65];
-
-  n = n > 64 ? 64 : n;
+  bool must_swap = !con->fixswappedid;
 #ifndef __NetBSD__
-  swapbytes(tmp, in, n);
-#else
   /* NetBSD kernel delivers IDENTIFY data in host byte order (but all else is LE) */
   if (isbigendian())
+    must_swap = !must_swap;
+#endif
+
+  char tmp[65];
+  n = n > 64 ? 64 : n;
+  if (!must_swap)
     strncpy(tmp, in, n);
   else
     swapbytes(tmp, in, n);
-#endif
   tmp[n] = '\0';
   trim(out, tmp);
 }
 
-void infofound(char *output) {
-  if (*output)
-    pout("%s\n", output);
-  else
-    pout("[No Information Found]\n");
+static const char * infofound(const char *output) {
+  return (*output ? output : "[No Information Found]");
 }
 
 
@@ -532,12 +530,10 @@ int ataPrintDriveInfo (struct ata_identify_device *drive){
   if (drivetype>=0 && knowndrives[drivetype].modelfamily)
     pout("Model Family:     %s\n", knowndrives[drivetype].modelfamily);
 
-  pout("Device Model:     ");
-  infofound(model);
-  pout("Serial Number:    ");
-  infofound(serial);
-  pout("Firmware Version: ");
-  infofound(firm);
+  pout("Device Model:     %s\n", infofound(model));
+  if (!con->dont_print_serial)
+    pout("Serial Number:    %s\n", infofound(serial));
+  pout("Firmware Version: %s\n", infofound(firm));
 
   if (determine_capacity(drive, capacity))
     pout("User Capacity:    %s bytes\n", capacity);
diff --git a/sm5/extern.h b/sm5/extern.h
index 69a1ac1c7..97e3bcfd3 100644
--- a/sm5/extern.h
+++ b/sm5/extern.h
@@ -25,7 +25,7 @@
 #ifndef EXTERN_H_
 #define EXTERN_H_
 
-#define EXTERN_H_CVSID "$Id: extern.h,v 1.52 2007/02/12 21:58:31 chrfranke Exp $\n"
+#define EXTERN_H_CVSID "$Id: extern.h,v 1.53 2007/07/21 20:59:41 chrfranke Exp $\n"
 
 // Possible values for fixfirmwarebug.  If user has NOT specified -F at
 // all, then value is 0.
@@ -87,6 +87,7 @@ typedef struct smartmonctrl_s {
   unsigned char smartautosavedisable;
   unsigned char printing_switchable;
   unsigned char dont_print;
+  unsigned char dont_print_serial;
   unsigned char permissive;
   unsigned char conservative;
   unsigned char checksumfail;
@@ -94,6 +95,7 @@ typedef struct smartmonctrl_s {
   unsigned char reportataioctl;
   unsigned char reportscsiioctl;
   unsigned char fixfirmwarebug;
+  unsigned char fixswappedid;
   unsigned char satpassthrulen;
   // Controller type (device type) has been specified explicitly
   unsigned char controller_explicit;
diff --git a/sm5/scsiprint.cpp b/sm5/scsiprint.cpp
index 4f7c5b372..6423ee7eb 100644
--- a/sm5/scsiprint.cpp
+++ b/sm5/scsiprint.cpp
@@ -3,11 +3,11 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-7 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * Additional SCSI work:
- * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2003-7 Douglas Gilbert <dougg@torque.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -42,7 +42,7 @@
 
 #define GBUF_SIZE 65535
 
-const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.119 2006/11/12 04:47:23 dpgilbert Exp $"
+const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.120 2007/07/21 20:59:41 chrfranke Exp $"
 CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // control block which points to external global control variables
@@ -1069,20 +1069,22 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     } 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) {
-        PRINT_ON(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);
-        PRINT_OFF(con);
+    if (!con->dont_print_serial) {
+        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) {
+            PRINT_ON(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);
+            PRINT_OFF(con);
+        }
     }
 
     // print SCSI peripheral device type
diff --git a/sm5/smartctl.8.in b/sm5/smartctl.8.in
index 460b37c6f..ece6ffdf1 100644
--- a/sm5/smartctl.8.in
+++ b/sm5/smartctl.8.in
@@ -1,7 +1,7 @@
 .ig
  Copyright (C) 2002-7 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
- $Id: smartctl.8.in,v 1.98 2007/07/20 21:00:43 chrfranke Exp $
+ $Id: smartctl.8.in,v 1.99 2007/07/21 20:59:41 chrfranke Exp $
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the Free
@@ -195,6 +195,10 @@ which failed either now or in the past.
 .I silent
 \- print no output.  The only way to learn about what was found is to
 use the exit status of \fBsmartctl\fP (see RETURN VALUES below).
+
+.I noserial
+\- Do not print the serial number of the device.
+
 .TP
 .B \-d TYPE, \-\-device=TYPE
 Specifies the type of the device.  The valid arguments to this option
@@ -925,9 +929,9 @@ value for Attribute 123 in this form.
 .TP
 .B \-F TYPE, \-\-firmwarebug=TYPE
 Modifies the behavior of \fBsmartctl\fP to compensate for some known
-and understood device firmware bug.  The arguments to this option are
-exclusive, so that only the final option given is used.  The valid
-values are:
+and understood device firmware or driver bug.  Except \'swapid\',
+the arguments to this option are exclusive, so that only the final
+option given is used.  The valid values are:
 
 .I none
 \- Assume that the device firmware obeys the ATA specifications.  This
@@ -964,6 +968,10 @@ Note that an explicit \'\-F\' option on the command line will
 over\-ride any preset values for \'\-F\' (see the \'\-P\' option
 below).
 
+.I swapid
+\- Fixes byte swapped ATA identify strings (device name, serial number,
+firmware version) returned by some buggy device drivers.
+
 .TP
 .B \-P TYPE, \-\-presets=TYPE
 Specifies whether \fBsmartctl\fP should use any preset options that
@@ -1477,7 +1485,7 @@ these documents may be found in the References section of the
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartctl.8.in,v 1.98 2007/07/20 21:00:43 chrfranke Exp $
+$Id: smartctl.8.in,v 1.99 2007/07/21 20:59:41 chrfranke Exp $
 .\" Local Variables:	         
 .\" mode: nroff         
 .\" End:
diff --git a/sm5/smartctl.cpp b/sm5/smartctl.cpp
index bce6e3e11..2fdbec905 100644
--- a/sm5/smartctl.cpp
+++ b/sm5/smartctl.cpp
@@ -50,7 +50,7 @@
 extern const char *os_solaris_ata_s_cvsid;
 #endif
 extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
-const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.164 2007/07/09 01:57:31 ballen4705 Exp $"
+const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.165 2007/07/21 20:59:41 chrfranke Exp $"
 ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // This is a block containing all the "control variables".  We declare
@@ -141,7 +141,7 @@ void Usage (void){
 #ifdef HAVE_GETOPT_LONG
   printf(
 "  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
-"         Set smartctl quiet mode to one of: errorsonly, silent\n\n"
+"         Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n"
 "  -d TYPE, --device=TYPE\n"
 "         Specify device type to one of: ata, scsi, marvell, sat, 3ware,N\n\n"
 "  -T TYPE, --tolerance=TYPE                                           (ATA)\n"
@@ -155,9 +155,10 @@ void Usage (void){
   );
 #else
   printf(
-"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent     (ATA)\n"
+"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent,    (ATA)\n"
+"                                               noserial\n"
 "  -d TYPE   Specify device type to one of: ata, scsi, 3ware,N\n"
-"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA\n"
+"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA)\n"
 "  -b TYPE   Set action on bad checksum to one of: warn, exit, ignore  (ATA)\n"
 "  -r TYPE   Report transactions (see man page)\n"
 "  -n MODE   No check if: never, sleep, standby, idle (see man page)   (ATA)\n\n"
@@ -195,7 +196,8 @@ void Usage (void){
 "  -v N,OPTION , --vendorattribute=N,OPTION                            (ATA)\n"
 "        Set display OPTION for vendor Attribute N (see man page)\n\n"
 "  -F TYPE, --firmwarebug=TYPE                                         (ATA)\n"
-"        Use firmware bug workaround: none, samsung, samsung2, samsung3\n\n"
+"        Use firmware bug workaround: none, samsung, samsung2,\n"
+"                                     samsung3, swapid\n\n"
 "  -P TYPE, --presets=TYPE                                             (ATA)\n"
 "        Drive-specific presets: use, ignore, show, showall\n\n"
   );
@@ -207,7 +209,8 @@ void Usage (void){
 "  -l TYPE   Show device log. TYPE: error, selftest, selective, directory,\n"
 "                                   background, scttemp[sts,hist]\n"
 "  -v N,OPT  Set display OPTion for vendor Attribute N (see man page)   (ATA)\n"
-"  -F TYPE   Use firmware bug workaround: none, samsung, samsung[23]    (ATA)\n"
+"  -F TYPE   Use firmware bug workaround: none, samsung, samsung2,      (ATA)\n"
+"                                         samsung3, swapid\n"
 "  -P TYPE   Drive-specific presets: use, ignore, show, showall         (ATA)\n\n"
   );
 #endif
@@ -239,7 +242,7 @@ void Usage (void){
 const char *getvalidarglist(char opt) {
   switch (opt) {
   case 'q':
-    return "errorsonly, silent";
+    return "errorsonly, silent, noserial";
   case 'd':
     return "ata, scsi, marvell, sat, 3ware,N, hpt,L/M/N cciss,N";
   case 'T':
@@ -259,7 +262,7 @@ const char *getvalidarglist(char opt) {
   case 't':
     return "offline, short, long, conveyance, select,M-N, pending,N, afterselect,[on|off], scttempint,N[,p]";
   case 'F':
-    return "none, samsung, samsung2, samsung3";
+    return "none, samsung, samsung2, samsung3, swapid";
   case 'n':
     return "never, sleep, standby, idle";
   case 'v':
@@ -370,6 +373,8 @@ void ParseOpts (int argc, char** argv){
       } else if (!strcmp(optarg,"silent")) {
         con->printing_switchable     = FALSE;
         con->dont_print = TRUE;
+      } else if (!strcmp(optarg,"noserial")) {
+        con->dont_print_serial = TRUE;
       } else {
         badarg = TRUE;
       }
@@ -592,6 +597,8 @@ void ParseOpts (int argc, char** argv){
         con->fixfirmwarebug = FIX_SAMSUNG2;
       } else if (!strcmp(optarg,"samsung3")) {
         con->fixfirmwarebug = FIX_SAMSUNG3;
+      } else if (!strcmp(optarg,"swapid")) {
+        con->fixswappedid = TRUE;
       } else {
         badarg = TRUE;
       }
-- 
GitLab