X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=knowndrives.cpp;h=25d3babcb7d0a321ea1161245ebc50c97bf4ee18;hb=fb337c9cb7f2443edb74813df8f6d69501c0eef0;hp=9fea5cd69d36c26baabdb93a83ab470367d6dccd;hpb=cdafb78b23fff3ab7270db93413558e6cf05e2d2;p=mirror_smartmontools-debian.git diff --git a/knowndrives.cpp b/knowndrives.cpp index 9fea5cd..25d3bab 100644 --- a/knowndrives.cpp +++ b/knowndrives.cpp @@ -4,7 +4,8 @@ * Home page of code is: http://smartmontools.sourceforge.net * Address of support mailing list: smartmontools-support@lists.sourceforge.net * - * Copyright (C) 2003-6 Philip Williams, Bruce Allen + * Copyright (C) 2003-11 Philip Williams, Bruce Allen + * Copyright (C) 2008-12 Christian Franke * * 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 @@ -12,8 +13,7 @@ * any later version. * * You should have received a copy of the GNU General Public License - * (for example COPYING); if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * (for example COPYING); If not, see . * */ @@ -21,1088 +21,461 @@ #include "int64.h" #include #include "atacmds.h" -#include "ataprint.h" -#include "extern.h" #include "knowndrives.h" -#include "utility.h" // includes +#include "utility.h" -const char *knowndrives_c_cvsid="$Id: knowndrives.cpp,v 1.144 2006/09/24 16:32:25 chrfranke Exp $" -ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID UTILITY_H_CVSID; +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _WIN32 +#include // access() +#endif + +#include + +const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3719 2012-12-03 21:19:33Z chrfranke $" + KNOWNDRIVES_H_CVSID; #define MODEL_STRING_LENGTH 40 #define FIRMWARE_STRING_LENGTH 8 #define TABLEPRINTWIDTH 19 -// See vendorattributeargs[] array in atacmds.cpp for definitions. -#define PRESET_9_MINUTES { 9, 1 } -#define PRESET_9_TEMP { 9, 2 } -#define PRESET_9_SECONDS { 9, 3 } -#define PRESET_9_HALFMINUTES { 9, 4 } -#define PRESET_192_EMERGENCYRETRACTCYCLECT { 192, 1 } -#define PRESET_193_LOADUNLOAD { 193, 1 } -#define PRESET_194_10XCELSIUS { 194, 1 } -#define PRESET_194_UNKNOWN { 194, 2 } -#define PRESET_198_OFFLINESCANUNCSECTORCT { 198, 1 } -#define PRESET_200_WRITEERRORCOUNT { 200, 1 } -#define PRESET_201_DETECTEDTACOUNT { 201, 1 } -#define PRESET_220_TEMP { 220, 1 } - -/* Arrays of preset vendor-specific attribute options for use in - * knowndrives[]. */ - -extern int64_t bytes; - -// to hold onto exit code for atexit routine -extern int exitstatus; - -// These three are common to several models. -const unsigned char vendoropts_9_minutes[][2] = { - PRESET_9_MINUTES, - {0,0} -}; -const unsigned char vendoropts_9_halfminutes[][2] = { - PRESET_9_HALFMINUTES, - {0,0} -}; -const unsigned char vendoropts_9_seconds[][2] = { - PRESET_9_SECONDS, - {0,0} -}; -const unsigned char vendoropts_Maxtor_4D080H4[][2] = { - PRESET_9_MINUTES, - PRESET_194_UNKNOWN, - {0,0} +// Builtin table of known drives. +// Used as a default if not read from +// "/usr/{,/local}share/smartmontools/drivedb.h" +// or any other file specified by '-B' option, +// see read_default_drive_databases() below. +// The drive_settings structure is described in drivedb.h. +const drive_settings builtin_knowndrives[] = { +#include "drivedb.h" }; -const unsigned char vendoropts_Fujitsu_MHS2020AT[][2] = { - PRESET_9_SECONDS, - PRESET_192_EMERGENCYRETRACTCYCLECT, - PRESET_198_OFFLINESCANUNCSECTORCT, - PRESET_200_WRITEERRORCOUNT, - PRESET_201_DETECTEDTACOUNT, - {0,0} -}; -const unsigned char vendoropts_Fujitsu_MHR2040AT[][2] = { - PRESET_9_SECONDS, - PRESET_192_EMERGENCYRETRACTCYCLECT, - PRESET_198_OFFLINESCANUNCSECTORCT, - PRESET_200_WRITEERRORCOUNT, - {0,0} -}; +/// Drive database class. Stores custom entries read from file. +/// Provides transparent access to concatenation of custom and +/// default table. +class drive_database +{ +public: + drive_database(); -const unsigned char vendoropts_Samsung_SV4012H[][2] = { - PRESET_9_HALFMINUTES, - {0,0} -}; + ~drive_database(); -const unsigned char vendoropts_Samsung_SV1204H[][2] = { - PRESET_9_HALFMINUTES, - PRESET_194_10XCELSIUS, - {0,0} -}; + /// Get total number of entries. + unsigned size() const + { return m_custom_tab.size() + m_builtin_size; } + + /// Get number of custom entries. + unsigned custom_size() const + { return m_custom_tab.size(); } + + /// Array access. + const drive_settings & operator[](unsigned i); + + /// Append new custom entry. + void push_back(const drive_settings & src); + + /// Append builtin table. + void append(const drive_settings * builtin_tab, unsigned builtin_size) + { m_builtin_tab = builtin_tab; m_builtin_size = builtin_size; } -const unsigned char vendoropts_Hitachi_DK23XX[][2] = { - PRESET_9_MINUTES, - PRESET_193_LOADUNLOAD, - {0,0} +private: + const drive_settings * m_builtin_tab; + unsigned m_builtin_size; + + std::vector m_custom_tab; + std::vector m_custom_strings; + + const char * copy_string(const char * str); + + drive_database(const drive_database &); + void operator=(const drive_database &); }; -const char same_as_minus_F[]="Fixes byte order in some SMART data (same as -F samsung)"; -const char same_as_minus_F2[]="Fixes byte order in some SMART data (same as -F samsung2)"; +drive_database::drive_database() +: m_builtin_tab(0), m_builtin_size(0) +{ +} -const char may_need_minus_F_disabled[] ="May need -F samsung disabled; see manual for details."; -const char may_need_minus_F2_disabled[]="May need -F samsung2 disabled; see manual for details."; -const char may_need_minus_F2_enabled[] ="May need -F samsung2 enabled; see manual for details."; -const char may_need_minus_F_enabled[] ="May need -F samsung or -F samsung2 enabled; see manual for details."; +drive_database::~drive_database() +{ + for (unsigned i = 0; i < m_custom_strings.size(); i++) + delete [] m_custom_strings[i]; +} -/* Special-purpose functions for use in knowndrives[]. */ -void specialpurpose_reverse_samsung(smartmonctrl *con) +const drive_settings & drive_database::operator[](unsigned i) { - if (con->fixfirmwarebug==FIX_NOTSPECIFIED) - con->fixfirmwarebug = FIX_SAMSUNG; + return (i < m_custom_tab.size() ? m_custom_tab[i] + : m_builtin_tab[i - m_custom_tab.size()] ); } -void specialpurpose_reverse_samsung2(smartmonctrl *con) + +void drive_database::push_back(const drive_settings & src) { - if (con->fixfirmwarebug==FIX_NOTSPECIFIED) - con->fixfirmwarebug = FIX_SAMSUNG2; + drive_settings dest; + dest.modelfamily = copy_string(src.modelfamily); + dest.modelregexp = copy_string(src.modelregexp); + dest.firmwareregexp = copy_string(src.firmwareregexp); + dest.warningmsg = copy_string(src.warningmsg); + dest.presets = copy_string(src.presets); + m_custom_tab.push_back(dest); } -/* Table of settings for known drives terminated by an element containing all - * zeros. The drivesettings structure is described in knowndrives.h. Note - * that lookupdrive() will search knowndrives[] from the start to end or - * until it finds the first match, so the order in knowndrives[] is important - * for distinct entries that could match the same drive. */ - -// Note that the table just below uses EXTENDED REGULAR EXPRESSIONS. -// A good on-line reference for these is: -// http://www.zeus.com/extra/docsystem/docroot/apps/web/docs/modules/access/regex.html - -const drivesettings knowndrives[] = { - { "IBM Deskstar 60GXP series", // ER60A46A firmware - "(IBM-|Hitachi )?IC35L0[12346]0AVER07", - "^ER60A46A$", - NULL, NULL, NULL, NULL - }, - { "IBM Deskstar 60GXP series", // All other firmware - "(IBM-|Hitachi )?IC35L0[12346]0AVER07", - ".*", - "IBM Deskstar 60GXP drives may need upgraded SMART firmware.\n" - "Please see http://www.geocities.com/dtla_update/index.html#rel and\n" - "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n" - "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215", - NULL, NULL, NULL - }, - { "IBM Deskstar 40GV & 75GXP series (A5AA/A6AA firmware)", - "(IBM-)?DTLA-30[57]0[123467][05]", - "^T[WX][123468AG][OF]A[56]AA$", - NULL, NULL, NULL, NULL - }, - { "IBM Deskstar 40GV & 75GXP series (all other firmware)", - "(IBM-)?DTLA-30[57]0[123467][05]", - ".*", - "IBM Deskstar 40GV and 75GXP drives may need upgraded SMART firmware.\n" - "Please see http://www.geocities.com/dtla_update/ and\n" - "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n" - "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215", - NULL, NULL, NULL - }, - { NULL, // ExcelStor J240, J340, J360, J680, and J880 - "^ExcelStor Technology J(24|34|36|68|88)0$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // Fujitsu M1623TAU - "^FUJITSU M1623TAU$", - ".*", - NULL, - vendoropts_9_seconds, - NULL, NULL - }, - { "Fujitsu MHG..MHK, MHT, MHU series", - "^FUJITSU MH[GHJKTU]2...ATU?", - ".*", - NULL, - vendoropts_9_seconds, - NULL, NULL - }, - { NULL, // Fujitsu MHN2300AT - "^FUJITSU MHN2300AT$", - ".*", - NULL, - vendoropts_9_seconds, - NULL, NULL - }, - { NULL, // Fujitsu MHR2040AT - "^FUJITSU MHR2040AT$", - ".*", // Tested on 40BA - NULL, - vendoropts_Fujitsu_MHR2040AT, - NULL, NULL - }, - { NULL, // Fujitsu MHR2020AT - "^FUJITSU MHR2020AT$", - ".*", - NULL, - vendoropts_9_seconds, - NULL, NULL - }, - { "Fujitsu MHSxxxxAT family", - "^FUJITSU MHS20[6432]0AT( .)?$", - ".*", - NULL, - vendoropts_Fujitsu_MHS2020AT, - NULL, NULL - }, - { NULL, // Fujitsu MHL2300AT, MHM2200AT, MHM2100AT, MHM2150AT - "^FUJITSU MH(L230|M2(20|10|15))0AT$", - ".*", - "This drive's firmware has a harmless Drive Identity Structure\n" - "checksum error bug.", - vendoropts_9_seconds, - NULL, NULL - }, - { "Fujitsu MPA..MPG series", - "^FUJITSU MP[A-G]3...A[HTEV]U?", - ".*", - NULL, - vendoropts_9_seconds, - NULL, NULL - }, - { NULL, // Samsung SV4012H (known firmware) - "^SAMSUNG SV4012H$", - "^RM100-08$", - NULL, - vendoropts_Samsung_SV4012H, - specialpurpose_reverse_samsung, - same_as_minus_F - }, - { NULL, // Samsung SV4012H (all other firmware) - "^SAMSUNG SV4012H$", - ".*", - may_need_minus_F_disabled, - vendoropts_Samsung_SV4012H, - specialpurpose_reverse_samsung, - same_as_minus_F - }, - { NULL, // Samsung SV0412H (known firmware) - "^SAMSUNG SV0412H$", - "^SK100-01$", - NULL, - vendoropts_Samsung_SV1204H, - specialpurpose_reverse_samsung, - same_as_minus_F - }, - { NULL, // Samsung SV0412H (all other firmware) - "^SAMSUNG SV0412H$", - ".*", - may_need_minus_F_disabled, - vendoropts_Samsung_SV1204H, - specialpurpose_reverse_samsung, - same_as_minus_F - }, - { NULL, // Samsung SV1204H (known firmware) - "^SAMSUNG SV1204H$", - "^RK100-1[3-5]$", - NULL, - vendoropts_Samsung_SV1204H, - specialpurpose_reverse_samsung, - same_as_minus_F - }, - { NULL, // Samsung SV1204H (all other firmware) - "^SAMSUNG SV1204H$", - ".*", - may_need_minus_F_disabled, - vendoropts_Samsung_SV1204H, - specialpurpose_reverse_samsung, - same_as_minus_F - }, - { NULL, // SAMSUNG SV0322A tested with FW JK200-35 - "^SAMSUNG SV0322A$", - ".*", - NULL, - NULL, - NULL, - NULL - }, - { NULL, // SAMSUNG SP40A2H with RR100-07 firmware - "^SAMSUNG SP40A2H$", - "^RR100-07$", - NULL, - vendoropts_9_halfminutes, - specialpurpose_reverse_samsung, - same_as_minus_F - }, - { "SAMSUNG SpinPoint T133 series", // tested with HD300LJ/ZT100-12, HD400LJ/ZZ100-14, HD401LJ/ZZ100-15 - "^SAMSUNG HD[34][02][01]L[DJ]$", - ".*", - NULL, - NULL, - NULL, - NULL - }, - { - NULL, // Any other Samsung disk with *-23 *-24 firmware - // SAMSUNG SP1213N (TL100-23 firmware) - // SAMSUNG SP0802N (TK100-23 firmware) - // Samsung SP1604N, tested with FW TM100-23 and TM100-24 - "^SAMSUNG .*$", - ".*-2[34]$", - NULL, - vendoropts_Samsung_SV4012H, - specialpurpose_reverse_samsung2, - same_as_minus_F2 - }, - { NULL, // All Samsung drives with '.*-25' firmware - "^SAMSUNG.*", - ".*-25$", - may_need_minus_F2_disabled, - vendoropts_Samsung_SV4012H, - specialpurpose_reverse_samsung2, - same_as_minus_F2 - }, - { NULL, // All Samsung drives with '.*-26 or later (currently to -39)' firmware - "^SAMSUNG.*", - ".*-(2[6789]|3[0-9])$", - NULL, - vendoropts_Samsung_SV4012H, - NULL, - NULL - }, - { NULL, // Samsung ALL OTHER DRIVES - "^SAMSUNG.*", - ".*", - may_need_minus_F_enabled, - NULL, NULL, NULL - }, - { "Maxtor Fireball 541DX family", - "^Maxtor 2B0(0[468]|1[05]|20)H1$", - ".*", - NULL, - vendoropts_Maxtor_4D080H4, - NULL, NULL - }, - { "Maxtor Fireball 3 family", - "^Maxtor 2F0[234]0[JL]0$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 2160 Ultra ATA family", - "^Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 2880 Ultra ATA family", - "^Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 3400 Ultra ATA family", - "^Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax D540X-4G family", - "^Maxtor 4G(120J6|160J[68])$", - ".*", - NULL, - vendoropts_Maxtor_4D080H4, - NULL, NULL - }, - { "Maxtor DiamondMax D540X-4K family", - "^MAXTOR 4K(020H1|040H2|060H3|080H4)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Maxtor DiamondMax Plus D740X family", - "^MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Maxtor DiamondMax Plus 5120 Ultra ATA 33 family", - "^Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax Plus 6800 Ultra ATA 66 family", - "^Maxtor 9(2732U8|2390U7|2049U6|1707U5|1366U4|1024U3|0845U3|0683U2)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax D540X-4D", - "^Maxtor 4D0(20H1|40H2|60H3|80H4)$", - ".*", - NULL, - vendoropts_Maxtor_4D080H4, - NULL, NULL - }, - { "Maxtor DiamondMax 16 family", - "^Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 4320 family", - "^Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D4|90432D2)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 17 VL family", - "^Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 20 VL family", - "^Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax VL 30 family", - "^Maxtor (33073U4|32049U3|31536U2|30768U1)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 36 family", - "^Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 40 ATA 66 series", - "^Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax Plus 40 series (Ultra ATA 66 and Ultra ATA 100)", - "^Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 40 VL Ultra ATA 100 series", - "^Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax Plus 45 Ulta ATA 100 family", - "^Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax Plus 60 family", - "^Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 80 family", - "^Maxtor (98196H8|96147H6)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 536DX family", - "^Maxtor 4W(100H6|080H6|060H4|040H3|030H2)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax Plus 8 family", - "^Maxtor 6E0[234]0L0$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax 10 family", - "^Maxtor 6(B(30|25|20|16|12|08)0[MPRS]|L(100P|120[MP]|160M|200[MPRS]|250[RS]|300[RS]))0$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor DiamondMax Plus 9 family", - "^Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor MaXLine Plus II", - "^Maxtor 7Y250[PM]0$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { "Maxtor MaXLine II family", - "^Maxtor [45]A(25|30|32)0[JN]0$", - ".*", - NULL, - vendoropts_9_minutes, - NULL, NULL - }, - { NULL, // HITACHI_DK14FA-20B - "^HITACHI_DK14FA-20B$", - ".*", - NULL, - vendoropts_Hitachi_DK23XX, - NULL, NULL - }, - { "HITACHI Travelstar DK23XX/DK23XXB series", - "^HITACHI_DK23..-..B?$", - ".*", - NULL, - vendoropts_Hitachi_DK23XX, - NULL, NULL - }, - { "Hitachi Endurastar J4K20/N4K20 (formerly DK23FA-20J)", - "^(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)$", - ".*", - NULL, - vendoropts_Hitachi_DK23XX, - NULL, NULL - }, - { "IBM Deskstar 14GXP and 16GP series", - "^IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM Deskstar 25GP and 22GXP family", - "^IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM Travelstar 25GS, 18GT, and 12GN family", - "^IBM-DARA-2(25|18|15|12|09|06)000$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM Travelstar 48GH, 30GN, and 15GN family", - "^(IBM-|Hitachi )?IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM Travelstar 32GH, 30GT, and 20GN family", - "^IBM-DJSA-2(32|30|20|10|05)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM Deskstar 37GP and 34GXP family", - "^IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM/Hitachi Travelstar 60GH and 40GN family", - "^(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM/Hitachi Travelstar 40GNX family", - "^(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Travelstar 80GN family", - "^(Hitachi )?IC25N0[23468]0ATMR04-.$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Travelstar 5K80 family", - "^HTS5480[8642]0M9AT00$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Travelstar 5K100 series", - "^HTS5410[1864]0G9(AT|SA)00$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Travelstar 7K60", - "^HTS726060M9AT00$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Travelstar E7K60 family", - "^HTE7260[46]0M9AT00$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM/Hitachi Deskstar 120GXP family", - "^(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM/Hitachi Deskstar GXP-180 family", - "^(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM Travelstar 14GS", - "^IBM-DCYA-214000$", - ".*", - NULL, NULL, NULL, NULL - }, - { "IBM Travelstar 4LP", - "^IBM-DTNA-2(180|216)0$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Deskstar 7K80 series", - "^(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Deskstar 7K250 series", - "^(Hitachi )?HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Hitachi Deskstar 7K400 series", - "^(Hitachi )?HDS724040KL(AT|SA)80$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK4025GAS - "^TOSHIBA MK4025GAS$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Toshiba 2.5\" HDD series", // TOSHIBA MK6021GAS [Bruce -- use for testing on laptop] - "^TOSHIBA MK6021GAS$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK6022GAX - "^TOSHIBA MK6022GAX$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK4019GAX/MK4019GAXB - "^TOSHIBA MK4019GAXB?$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK6409MAV - "^TOSHIBA MK6409MAV$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOS MK3019GAXB SUN30G - "^TOS MK3019GAXB SUN30G$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK2016GAP, MK2017GAP, MK2018GAP, MK2018GAS, MK2023GAS - "^TOSHIBA MK20(1[678]GAP|(18|23)GAS)$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK4018GAS, MK4018GAP - "^TOSHIBA MK4018GA[SP]$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK3017GAP - "^TOSHIBA MK3017GAP$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // TOSHIBA MK8026GAX - "^TOSHIBA MK8026GAX$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Momentus family", - "^ST9(20|28|40|48)11A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Momentus 4200.2 Series", - "^ST9(100822|808210|60821|50212|402113|30219)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Momentus 5400.2 series", - "^ST9(9808211|960822|808211|408114|308110|120821|10082[34]|98823|96812|94813|93811|60822)AS?$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Medalist 8641 family", - "^ST3(2110|3221|4312|6531|8641)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate U Series X family", - "^ST3(10014A(CE)?|20014A)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate U Series 6 family", - "^ST3(8002|6002|4081|3061|2041)0A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate U Series 5 family", - "^ST3(40823|30621|20413|15311|10211)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate U4 family", - "^ST3(2112|4311|6421|8421)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate U8 family", - "^ST3(8410|4313|17221|13021)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate U10 family", - "^ST3(20423|15323|10212)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Barracuda ATA II family", - "^ST3(3063|2042|1532|1021)0A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Barracuda ATA III family", - "^ST3(40824|30620|20414|15310|10215)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Barracuda ATA IV family", - "^ST3(20011|30011|40016|60021|80021)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Barracuda ATA V family", - "^ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Barracuda 5400.1", - "^ST340015A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Barracuda 7200.7 and 7200.7 Plus family", - "^ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]0827AS|8001[13]AS?|80817AS|60014A|40014AS?)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Barracuda 7200.8 family", - "^ST3(400832|300831|250823|200826)AS?$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Medalist 17240, 13030, 10231, 8420, and 4310", - "^ST3(17240|13030|10231|8420|4310)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Seagate Medalist 17242, 13032, 10232, 8422, and 4312", - "^ST3(1724|1303|1023|842|431)2A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Protege", - /* Western Digital drives with this comment all appear to use Attribute 9 in - * a non-standard manner. These entries may need to be updated when it - * is understood exactly how Attribute 9 should be interpreted. - * UPDATE: this is probably explained by the WD firmware bug described in the - * smartmontools FAQ */ - "^WDC WD([2468]00E|1[26]00A)B-.*$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Caviar family", - /* Western Digital drives with this comment all appear to use Attribute 9 in - * a non-standard manner. These entries may need to be updated when it - * is understood exactly how Attribute 9 should be interpreted. - * UPDATE: this is probably explained by the WD firmware bug described in the - * smartmontools FAQ */ - "^WDC WD(2|3|4|6|8|10|12|16|18|20|25)00BB-.*$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Caviar WDxxxAB series", - /* Western Digital drives with this comment all appear to use Attribute 9 in - * a non-standard manner. These entries may need to be updated when it - * is understood exactly how Attribute 9 should be interpreted. - * UPDATE: this is probably explained by the WD firmware bug described in the - * smartmontools FAQ */ - "^WDC WD(3|4|6)00AB-.*$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Caviar WDxxxAA series", - /* Western Digital drives with this comment all appear to use Attribute 9 in - * a non-standard manner. These entries may need to be updated when it - * is understood exactly how Attribute 9 should be interpreted. - * UPDATE: this is probably explained by the WD firmware bug described in the - * smartmontools FAQ */ - "^WDC WD...?AA(-.*)?$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Caviar WDxxxBA series", - /* Western Digital drives with this comment all appear to use Attribute 9 in - * a non-standard manner. These entries may need to be updated when it - * is understood exactly how Attribute 9 should be interpreted. - * UPDATE: this is probably explained by the WD firmware bug described in the - * smartmontools FAQ */ - "^WDC WD...BA$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // Western Digital Caviar AC12500, AC14300, AC23200, AC24300, AC25100, - // AC36400, AC38400 - "^WDC AC(125|143|232|243|251|364|384)00.?", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Caviar SE family", - /* Western Digital drives with this comment all appear to use Attribute 9 in - * a non-standard manner. These entries may need to be updated when it - * is understood exactly how Attribute 9 should be interpreted. - * UPDATE: this is probably explained by the WD firmware bug described in the - * smartmontools FAQ */ - "^WDC WD((4|6|8|10|12|16|18|20|25|30|32)00JB|(12|20|25)00PB)-.*$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Caviar SE (Serial ATA) family", - "^WDC WD(4|8|12|16|20|25)00JD-.*$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Caviar RE Serial ATA series", - "^WDC WD((12|16|25|32)00SD|4000YR)-.*$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Western Digital Raptor family", - "^WDC WD(360|740)GD", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM BIGFOOT TS10.0A - "^QUANTUM BIGFOOT TS10.0A$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALLlct15 20 and QUANTUM FIREBALLlct15 30 - "^QUANTUM FIREBALLlct15 [23]0$", - ".*", - NULL, NULL, NULL, NULL - }, - { "QUANTUM FIREBALLlct20 series", - "^QUANTUM FIREBALLlct20 [234]0$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALL CX10.2A - "^QUANTUM FIREBALL CX10.2A$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Quantum Fireball Plus LM series", - "^QUANTUM FIREBALLP LM(10.2|15|20.5|30)$", - ".*", - NULL, NULL, NULL, NULL - }, - { "Quantum Fireball CR series", - "^QUANTUM FIREBALL CR(4.3|8.4)A$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALLP AS10.2, AS20.5, and AS40.0 - "^QUANTUM FIREBALLP AS(10.2|20.5|40.0)$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALL EX6.4A - "^QUANTUM FIREBALL EX6.4A$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALL ST3.2A - "^QUANTUM FIREBALL ST3.2A$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALL EX3.2A - "^QUANTUM FIREBALL EX3.2A$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALLP KX27.3 - "^QUANTUM FIREBALLP KX27.3$", - ".*", - NULL, NULL, NULL, NULL - }, - { NULL, // QUANTUM FIREBALLP KA10.1 - "^QUANTUM FIREBALLP KA10.1$", - ".*", - NULL, NULL, NULL, NULL - }, - /*------------------------------------------------------------ - * End of table. Do not add entries below this marker. - *------------------------------------------------------------ */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL} -}; +const char * drive_database::copy_string(const char * src) +{ + size_t len = strlen(src); + char * dest = new char[len+1]; + memcpy(dest, src, len+1); + try { + m_custom_strings.push_back(dest); + } + catch (...) { + delete [] dest; throw; + } + return dest; +} + + +/// The drive database. +static drive_database knowndrives; + + +// Return true if modelfamily string describes entry for USB ID +static bool is_usb_modelfamily(const char * modelfamily) +{ + return !strncmp(modelfamily, "USB:", 4); +} + +// Return true if entry for USB ID +static inline bool is_usb_entry(const drive_settings * dbentry) +{ + return is_usb_modelfamily(dbentry->modelfamily); +} + +// Compile regular expression, print message on failure. +static bool compile(regular_expression & regex, const char *pattern) +{ + if (!regex.compile(pattern, REG_EXTENDED)) { + pout("Internal error: unable to compile regular expression \"%s\": %s\n" + "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", + pattern, regex.get_errmsg()); + return false; + } + return true; +} + +// Compile & match a regular expression. +static bool match(const char * pattern, const char * str) +{ + regular_expression regex; + if (!compile(regex, pattern)) + return false; + return regex.full_match(str); +} // Searches knowndrives[] for a drive with the given model number and firmware // string. If either the drive's model or firmware strings are not set by the -// manufacturer then values of NULL may be used. Returns the index of the -// first match in knowndrives[] or -1 if no match if found. -int lookupdrive(const char *model, const char *firmware) +// manufacturer then values of NULL may be used. Returns the entry of the +// first match in knowndrives[] or 0 if no match if found. +static const drive_settings * lookup_drive(const char * model, const char * firmware) { - regex_t regex; - int i, index; - const char *empty = ""; + if (!model) + model = ""; + if (!firmware) + firmware = ""; + + for (unsigned i = 0; i < knowndrives.size(); i++) { + // Skip USB entries + if (is_usb_entry(&knowndrives[i])) + continue; - model = model ? model : empty; - firmware = firmware ? firmware : empty; + // Check whether model matches the regular expression in knowndrives[i]. + if (!match(knowndrives[i].modelregexp, model)) + continue; - for (i = 0, index = -1; index == -1 && knowndrives[i].modelregexp; i++) { - // Attempt to compile regular expression. - if (compileregex(®ex, knowndrives[i].modelregexp, REG_EXTENDED)) - goto CONTINUE; + // Model matches, now check firmware. "" matches always. + if (!( !*knowndrives[i].firmwareregexp + || match(knowndrives[i].firmwareregexp, firmware))) + continue; - // Check whether model matches the regular expression in knowndrives[i]. - if (!regexec(®ex, model, 0, NULL, 0)) { - // model matches, now check firmware. - if (!knowndrives[i].firmwareregexp) - // The firmware regular expression in knowndrives[i] is NULL, which is - // considered a match. - index = i; - else { - // Compare firmware against the regular expression in knowndrives[i]. - regfree(®ex); // Recycle regex. - if (compileregex(®ex, knowndrives[i].firmwareregexp, REG_EXTENDED)) - goto CONTINUE; - if (!regexec(®ex, firmware, 0, NULL, 0)) - index = i; - } + // Found + return &knowndrives[i]; + } + + // Not found + return 0; +} + + +// Parse drive or USB options in preset string, return false on error. +static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs, + firmwarebug_defs * firmwarebugs, std::string * type) +{ + for (int i = 0; ; ) { + i += strspn(presets+i, " \t"); + if (!presets[i]) + break; + char opt, arg[80+1+13]; int len = -1; + if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0)) + return false; + if (opt == 'v' && defs) { + // Parse "-v N,format[,name]" + if (!parse_attribute_def(arg, *defs, PRIOR_DATABASE)) + return false; + } + else if (opt == 'F' && firmwarebugs) { + firmwarebug_defs bug; + if (!parse_firmwarebug_def(arg, bug)) + return false; + // Don't set if user specified '-F none'. + if (!firmwarebugs->is_set(BUG_NONE)) + firmwarebugs->set(bug); } - CONTINUE: - regfree(®ex); + else if (opt == 'd' && type) { + // TODO: Check valid types + *type = arg; + } + else + return false; + + i += len; } + return true; +} - return index; +// Parse '-v' and '-F' options in preset string, return false on error. +static inline bool parse_presets(const char * presets, + ata_vendor_attr_defs & defs, + firmwarebug_defs & firmwarebugs) +{ + return parse_db_presets(presets, &defs, &firmwarebugs, 0); } +// Parse '-d' option in preset string, return false on error. +static inline bool parse_usb_type(const char * presets, std::string & type) +{ + return parse_db_presets(presets, 0, 0, &type); +} -// Shows all presets for drives in knowndrives[]. -void showonepreset(const drivesettings *drivetable){ - - const unsigned char (* presets)[2] = drivetable->vendoropts; - int first_preset = 1; - +// Parse "USB: [DEVICE] ; [BRIDGE]" string +static void parse_usb_names(const char * names, usb_dev_info & info) +{ + int n1 = -1, n2 = -1, n3 = -1; + sscanf(names, "USB: %n%*[^;]%n; %n", &n1, &n2, &n3); + if (0 < n1 && n1 < n2) + info.usb_device.assign(names+n1, n2-n1); + else + sscanf(names, "USB: ; %n", &n3); + if (0 < n3) + info.usb_bridge = names+n3; +} + +// Search drivedb for USB device with vendor:product ID. +int lookup_usb_device(int vendor_id, int product_id, int bcd_device, + usb_dev_info & info, usb_dev_info & info2) +{ + // Format strings to match + char usb_id_str[16], bcd_dev_str[16]; + snprintf(usb_id_str, sizeof(usb_id_str), "0x%04x:0x%04x", vendor_id, product_id); + if (bcd_device >= 0) + snprintf(bcd_dev_str, sizeof(bcd_dev_str), "0x%04x", bcd_device); + else + bcd_dev_str[0] = 0; + + int found = 0; + for (unsigned i = 0; i < knowndrives.size(); i++) { + const drive_settings & dbentry = knowndrives[i]; + + // Skip drive entries + if (!is_usb_entry(&dbentry)) + continue; + + // Check whether USB vendor:product ID matches + if (!match(dbentry.modelregexp, usb_id_str)) + continue; + + // Parse '-d type' + usb_dev_info d; + if (!parse_usb_type(dbentry.presets, d.usb_type)) + return 0; // Syntax error + parse_usb_names(dbentry.modelfamily, d); + + // If two entries with same vendor:product ID have different + // types, use bcd_device (if provided by OS) to select entry. + if ( *dbentry.firmwareregexp && *bcd_dev_str + && match(dbentry.firmwareregexp, bcd_dev_str)) { + // Exact match including bcd_device + info = d; found = 1; + break; + } + else if (!found) { + // First match without bcd_device + info = d; found = 1; + } + else if (info.usb_type != d.usb_type) { + // Another possible match with different type + info2 = d; found = 2; + break; + } + + // Stop search at first matching entry with empty bcd_device + if (!*dbentry.firmwareregexp) + break; + } + + return found; +} + +// Shows one entry of knowndrives[], returns #errors. +static int showonepreset(const drive_settings * dbentry) +{ // Basic error check - if (!drivetable || !drivetable->modelregexp){ - pout("Null known drive table pointer. Please report\n" + if (!( dbentry + && dbentry->modelfamily + && dbentry->modelregexp && *dbentry->modelregexp + && dbentry->firmwareregexp + && dbentry->warningmsg + && dbentry->presets )) { + pout("Invalid drive database entry. Please report\n" "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n"); - return; - } - - // print model and firmware regular expressions - pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", drivetable->modelregexp); - pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", drivetable->firmwareregexp ? - drivetable->firmwareregexp : ""); - pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", drivetable->modelfamily ? - drivetable->modelfamily : ""); - - // if there are any presets, then show them - if (presets && (*presets)[0]) while (1) { - char out[256]; - const int attr = (*presets)[0], val = (*presets)[1]; - unsigned char fakearray[MAX_ATTRIBUTE_NUM]; - - // if we are at the end of the attribute list, break out - if (!attr) - break; - - // This is a hack. ataPrintSmartAttribName() needs a pointer to an - // "array" to dereference, so we provide such a pointer. - fakearray[attr]=val; - ataPrintSmartAttribName(out, attr, fakearray); - - // Use leading zeros instead of spaces so that everything lines up. - out[0] = (out[0] == ' ') ? '0' : out[0]; - out[1] = (out[1] == ' ') ? '0' : out[1]; - pout("%-*s %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", out); - first_preset = 0; - presets++; + return 1; } - else - pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required."); - - // Is a special purpose function defined? If so, describe it - if (drivetable->specialpurpose){ - pout("%-*s ", TABLEPRINTWIDTH, "OTHER PRESETS:"); - pout("%s\n", drivetable->functiondesc ? - drivetable->functiondesc : "A special purpose function " - "is defined for this drive"); + bool usb = is_usb_entry(dbentry); + + // print and check model and firmware regular expressions + int errcnt = 0; + regular_expression regex; + pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "MODEL REGEXP:" : "USB Vendor:Product:"), + dbentry->modelregexp); + if (!compile(regex, dbentry->modelregexp)) + errcnt++; + + pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "FIRMWARE REGEXP:" : "USB bcdDevice:"), + *dbentry->firmwareregexp ? dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change) + if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp)) + errcnt++; + + if (!usb) { + pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily); + + // if there are any presets, then show them + firmwarebug_defs firmwarebugs; + bool first_preset = true; + if (*dbentry->presets) { + ata_vendor_attr_defs defs; + if (!parse_presets(dbentry->presets, defs, firmwarebugs)) { + pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); + errcnt++; + } + for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) { + if (defs[i].priority != PRIOR_DEFAULT) { + std::string name = ata_get_smart_attr_name(i, defs); + // Use leading zeros instead of spaces so that everything lines up. + pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", + i, name.c_str()); + // Check max name length suitable for smartctl -A output + const unsigned maxlen = 23; + if (name.size() > maxlen) { + pout("%*s\n", TABLEPRINTWIDTH+6+maxlen, "Error: Attribute name too long ------^"); + errcnt++; + } + first_preset = false; + } + } + } + if (first_preset) + pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required."); + + // describe firmwarefix + for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) { + if (!firmwarebugs.is_set((firmwarebug_t)b)) + continue; + const char * fixdesc; + switch ((firmwarebug_t)b) { + case BUG_NOLOGDIR: + fixdesc = "Avoids reading GP/SMART Log Directories (same as -F nologdir)"; + break; + case BUG_SAMSUNG: + fixdesc = "Fixes byte order in some SMART data (same as -F samsung)"; + break; + case BUG_SAMSUNG2: + fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)"; + break; + case BUG_SAMSUNG3: + fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)"; + break; + case BUG_XERRORLBA: + fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)"; + break; + default: + fixdesc = "UNKNOWN"; errcnt++; + break; + } + pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc); + } } - - // Print any special warnings - if (drivetable->warningmsg){ - pout("%-*s ", TABLEPRINTWIDTH, "WARNINGS:"); - pout("%s\n", drivetable->warningmsg); + else { + // Print USB info + usb_dev_info info; parse_usb_names(dbentry->modelfamily, info); + pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Device:", + (!info.usb_device.empty() ? info.usb_device.c_str() : "[unknown]")); + pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Bridge:", + (!info.usb_bridge.empty() ? info.usb_bridge.c_str() : "[unknown]")); + + if (*dbentry->presets && !parse_usb_type(dbentry->presets, info.usb_type)) { + pout("Syntax error in USB type string \"%s\"\n", dbentry->presets); + errcnt++; + } + pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Type", + (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]")); } - - return; + + // Print any special warnings + if (*dbentry->warningmsg) + pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg); + return errcnt; } // Shows all presets for drives in knowndrives[]. -// Returns <0 on syntax error in regular expressions. -int showallpresets(void){ - int i; - int rc = 0; - regex_t regex; - +// Returns #syntax errors. +int showallpresets() +{ // loop over all entries in the knowndrives[] table, printing them // out in a nice format - for (i=0; knowndrives[i].modelregexp; i++){ - showonepreset(&knowndrives[i]); + int errcnt = 0; + for (unsigned i = 0; i < knowndrives.size(); i++) { + errcnt += showonepreset(&knowndrives[i]); pout("\n"); } - // Check all regular expressions - for (i=0; knowndrives[i].modelregexp; i++){ - if (compileregex(®ex, knowndrives[i].modelregexp, REG_EXTENDED)) - rc = -1; - if (knowndrives[i].firmwareregexp) { - if (compileregex(®ex, knowndrives[i].firmwareregexp, REG_EXTENDED)) - rc = -1; - } - } + pout("Total number of entries :%5u\n" + "Entries read from file(s):%5u\n\n", + knowndrives.size(), knowndrives.custom_size()); + pout("For information about adding a drive to the database see the FAQ on the\n"); pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n"); - return rc; + + if (errcnt > 0) + pout("\nFound %d syntax error(s) in database.\n" + "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", errcnt); + return errcnt; } // Shows all matching presets for a drive in knowndrives[]. // Returns # matching entries. -int showmatchingpresets(const char *model, const char *firmware){ - int i; +int showmatchingpresets(const char *model, const char *firmware) +{ int cnt = 0; const char * firmwaremsg = (firmware ? firmware : "(any)"); - regex_t regex; - for (i=0; knowndrives[i].modelregexp; i++){ - if (i > 0) - regfree(®ex); - if (compileregex(®ex, knowndrives[i].modelregexp, REG_EXTENDED)) - continue; - if (regexec(®ex, model, 0, NULL, 0)) + for (unsigned i = 0; i < knowndrives.size(); i++) { + if (!match(knowndrives[i].modelregexp, model)) continue; - if (firmware && knowndrives[i].firmwareregexp) { - regfree(®ex); - if (compileregex(®ex, knowndrives[i].firmwareregexp, REG_EXTENDED)) - continue; - if (regexec(®ex, firmware, 0, NULL, 0)) + if ( firmware && *knowndrives[i].firmwareregexp + && !match(knowndrives[i].firmwareregexp, firmware)) continue; - } + // Found if (++cnt == 1) pout("Drive found in smartmontools Database. Drive identity strings:\n" "%-*s %s\n" @@ -1114,7 +487,6 @@ int showmatchingpresets(const char *model, const char *firmware){ showonepreset(&knowndrives[i]); pout("\n"); } - regfree(®ex); if (cnt == 0) pout("No presets are defined for this drive. Its identity strings:\n" "MODEL: %s\n" @@ -1125,16 +497,17 @@ int showmatchingpresets(const char *model, const char *firmware){ } // Shows the presets (if any) that are available for the given drive. -void showpresets(const struct ata_identify_device *drive){ - int i; +void show_presets(const ata_identify_device * drive) +{ char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; // get the drive's model/firmware strings - formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH); - formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH); - + ata_format_id_string(model, drive->model, sizeof(model)-1); + ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1); + // and search to see if they match values in the table - if ((i = lookupdrive(model, firmware)) < 0) { + const drive_settings * dbentry = lookup_drive(model, firmware); + if (!dbentry) { // no matches found pout("No presets are defined for this drive. Its identity strings:\n" "MODEL: %s\n" @@ -1151,67 +524,362 @@ void showpresets(const struct ata_identify_device *drive){ "%-*s %s\n" "match smartmontools Drive Database entry:\n", TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware); - showonepreset(&knowndrives[i]); - return; + showonepreset(dbentry); } -// Sets preset vendor attribute options in opts by finding the entry -// (if any) for the given drive in knowndrives[]. Values that have -// already been set in opts will not be changed. Returns <0 if drive -// not recognized else index >=0 into drive database. -int applypresets(const struct ata_identify_device *drive, unsigned char **optsptr, - smartmonctrl *con) { - int i; - unsigned char *opts; - char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; - - if (*optsptr==NULL) - bytes+=MAX_ATTRIBUTE_NUM; - - if (*optsptr==NULL && !(*optsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))){ - pout("Unable to allocate memory in applypresets()"); - bytes-=MAX_ATTRIBUTE_NUM; - EXIT(1); - } - - opts=*optsptr; - +// Searches drive database and sets preset vendor attribute +// options in defs and firmwarebugs. +// Values that have already been set will not be changed. +// Returns pointer to database entry or nullptr if none found +const drive_settings * lookup_drive_apply_presets( + const ata_identify_device * drive, ata_vendor_attr_defs & defs, + firmwarebug_defs & firmwarebugs) +{ // get the drive's model/firmware strings - formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH); - formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH); - + char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; + ata_format_id_string(model, drive->model, sizeof(model)-1); + ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1); + // Look up the drive in knowndrives[]. - if ((i = lookupdrive(model, firmware)) >= 0) { - - // if vendoropts is non-NULL then Attribute interpretation presets - if (knowndrives[i].vendoropts) { - const unsigned char (* presets)[2]; - - // For each attribute in list of attribute/val pairs... - presets = knowndrives[i].vendoropts; - while (1) { - const int attr = (*presets)[0]; - const int val = (*presets)[1]; - - if (!attr) - break; - - // ... set attribute if user hasn't already done so. - if (!opts[attr]) - opts[attr] = val; - presets++; + const drive_settings * dbentry = lookup_drive(model, firmware); + if (!dbentry) + return 0; + + if (*dbentry->presets) { + // Apply presets + if (!parse_presets(dbentry->presets, defs, firmwarebugs)) + pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); + } + return dbentry; +} + + +///////////////////////////////////////////////////////////////////////////// +// Parser for drive database files + +// Abstract pointer to read file input. +// Operations supported: c = *p; c = p[1]; ++p; +class stdin_iterator +{ +public: + explicit stdin_iterator(FILE * f) + : m_f(f) { get(); get(); } + + stdin_iterator & operator++() + { get(); return *this; } + + char operator*() const + { return m_c; } + + char operator[](int i) const + { + if (i != 1) + fail(); + return m_next; + } + +private: + FILE * m_f; + char m_c, m_next; + void get(); + void fail() const; +}; + +void stdin_iterator::get() +{ + m_c = m_next; + int ch = getc(m_f); + m_next = (ch != EOF ? ch : 0); +} + +void stdin_iterator::fail() const +{ + throw std::runtime_error("stdin_iterator: wrong usage"); +} + + +// Use above as parser input 'pointer'. Can easily be changed later +// to e.g. 'const char *' if above is too slow. +typedef stdin_iterator parse_ptr; + +// Skip whitespace and comments. +static parse_ptr skip_white(parse_ptr src, const char * path, int & line) +{ + for ( ; ; ++src) switch (*src) { + case ' ': case '\t': + continue; + + case '\n': + ++line; + continue; + + case '/': + switch (src[1]) { + case '/': + // skip '// comment' + ++src; ++src; + while (*src && *src != '\n') + ++src; + if (*src) + ++line; + break; + case '*': + // skip '/* comment */' + ++src; ++src; + for (;;) { + if (!*src) { + pout("%s(%d): Missing '*/'\n", path, line); + return src; + } + char c = *src; ++src; + if (c == '\n') + ++line; + else if (c == '*' && *src == '/') + break; + } + break; + default: + return src; } + continue; + + default: + return src; + } +} + +// Info about a token. +struct token_info +{ + char type; + int line; + std::string value; + + token_info() : type(0), line(0) { } +}; + +// Get next token. +static parse_ptr get_token(parse_ptr src, token_info & token, const char * path, int & line) +{ + src = skip_white(src, path, line); + switch (*src) { + case '{': case '}': case ',': + // Simple token + token.type = *src; token.line = line; + ++src; + break; + + case '"': + // String constant + token.type = '"'; token.line = line; + token.value = ""; + do { + for (++src; *src != '"'; ++src) { + char c = *src; + if (!c || c == '\n' || (c == '\\' && !src[1])) { + pout("%s(%d): Missing terminating '\"'\n", path, line); + token.type = '?'; token.line = line; + return src; + } + if (c == '\\') { + c = *++src; + switch (c) { + case 'n' : c = '\n'; break; + case '\n': ++line; break; + case '\\': case '"': break; + default: + pout("%s(%d): Unknown escape sequence '\\%c'\n", path, line, c); + token.type = '?'; token.line = line; + continue; + } + } + token.value += c; + } + // Lookahead to detect string constant concatentation + src = skip_white(++src, path, line); + } while (*src == '"'); + break; + + case 0: + // EOF + token.type = 0; token.line = line; + break; + + default: + pout("%s(%d): Syntax error, invalid char '%c'\n", path, line, *src); + token.type = '?'; token.line = line; + while (*src && *src != '\n') + ++src; + break; + } + + return src; +} + +// Parse drive database from abstract input pointer. +static bool parse_drive_database(parse_ptr src, drive_database & db, const char * path) +{ + int state = 0, field = 0; + std::string values[5]; + bool ok = true; + + token_info token; int line = 1; + src = get_token(src, token, path, line); + for (;;) { + // EOF is ok after '}', trailing ',' is also allowed. + if (!token.type && (state == 0 || state == 4)) + break; + + // Check expected token + const char expect[] = "{\",},"; + if (token.type != expect[state]) { + if (token.type != '?') + pout("%s(%d): Syntax error, '%c' expected\n", path, token.line, expect[state]); + ok = false; + // Skip to next entry + while (token.type && token.type != '{') + src = get_token(src, token, path, line); + state = 0; + if (token.type) + continue; + break; + } + + // Interpret parser state + switch (state) { + case 0: // ... ^{...} + state = 1; field = 0; + break; + case 1: // {... ^"..." ...} + switch (field) { + case 1: case 2: + if (!token.value.empty()) { + regular_expression regex; + if (!regex.compile(token.value.c_str(), REG_EXTENDED)) { + pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg()); + ok = false; + } + } + else if (field == 1) { + pout("%s(%d): Missing regular expression for drive model\n", path, token.line); + ok = false; + } + break; + case 4: + if (!token.value.empty()) { + if (!is_usb_modelfamily(values[0].c_str())) { + ata_vendor_attr_defs defs; firmwarebug_defs fix; + if (!parse_presets(token.value.c_str(), defs, fix)) { + pout("%s(%d): Syntax error in preset option string\n", path, token.line); + ok = false; + } + } + else { + std::string type; + if (!parse_usb_type(token.value.c_str(), type)) { + pout("%s(%d): Syntax error in USB type string\n", path, token.line); + ok = false; + } + } + } + break; + } + values[field] = token.value; + state = (++field < 5 ? 2 : 3); + break; + case 2: // {... "..."^, ...} + state = 1; + break; + case 3: // {...^}, ... + { + drive_settings entry; + entry.modelfamily = values[0].c_str(); + entry.modelregexp = values[1].c_str(); + entry.firmwareregexp = values[2].c_str(); + entry.warningmsg = values[3].c_str(); + entry.presets = values[4].c_str(); + db.push_back(entry); + } + state = 4; + break; + case 4: // {...}^, ... + state = 0; + break; + default: + pout("Bad state %d\n", state); + return false; } - - // If a special-purpose function is defined for this drive then - // call it. Note that if command line arguments or Directives - // over-ride this choice, then the specialpurpose function that is - // called must deal with this. - if (knowndrives[i].specialpurpose) - (*knowndrives[i].specialpurpose)(con); + src = get_token(src, token, path, line); } - - // return <0 if drive wasn't recognized, or index>=0 into database - // if it was - return i; + return ok; +} + +// Read drive database from file. +bool read_drive_database(const char * path) +{ + stdio_file f(path, "r" +#ifdef __CYGWIN__ // Allow files with '\r\n'. + "t" +#endif + ); + if (!f) { + pout("%s: cannot open drive database file\n", path); + return false; + } + + return parse_drive_database(parse_ptr(f), knowndrives, path); +} + +// Get path for additional database file +const char * get_drivedb_path_add() +{ +#ifndef _WIN32 + return SMARTMONTOOLS_SYSCONFDIR"/smart_drivedb.h"; +#else + static std::string path = get_exe_dir() + "/drivedb-add.h"; + return path.c_str(); +#endif +} + +#ifdef SMARTMONTOOLS_DRIVEDBDIR + +// Get path for default database file +const char * get_drivedb_path_default() +{ +#ifndef _WIN32 + return SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h"; +#else + static std::string path = get_exe_dir() + "/drivedb.h"; + return path.c_str(); +#endif +} + +#endif + +// Read drive databases from standard places. +bool read_default_drive_databases() +{ + // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h + const char * db1 = get_drivedb_path_add(); + if (!access(db1, 0)) { + if (!read_drive_database(db1)) + return false; + } + +#ifdef SMARTMONTOOLS_DRIVEDBDIR + // Read file from package: /usr/{,local/}share/smartmontools/drivedb.h + const char * db2 = get_drivedb_path_default(); + if (!access(db2, 0)) { + if (!read_drive_database(db2)) + return false; + } + else +#endif + { + // Append builtin table. + knowndrives.append(builtin_knowndrives, + sizeof(builtin_knowndrives)/sizeof(builtin_knowndrives[0])); + } + + return true; }