From bbe3bac923cd8c0f69c0d0ebac92f8ae386c4bcc Mon Sep 17 00:00:00 2001 From: Alexander Shaduri Date: Mon, 5 May 2014 08:59:13 +0000 Subject: [PATCH] Make sure we write some data from the Info window on Save Output if only basic data is available. Fixed a harmless "Number of written bytes doesn't match the data size" warning. Added support for SSD-only and HDD-only vendor attributes. Added the latest (r3897 / 2014-04-28) attributes from smartmontools. Increased the Info window size a bit. --- gsmartcontrol/src/applib/selftest.cpp | 3 +- gsmartcontrol/src/applib/smartctl_parser.cpp | 10 +- gsmartcontrol/src/applib/smartctl_parser.h | 7 +- .../src/applib/smartctl_parser_test.cpp | 2 +- gsmartcontrol/src/applib/storage_device.cpp | 26 +- gsmartcontrol/src/applib/storage_device.h | 4 + gsmartcontrol/src/applib/storage_property.h | 7 + .../src/applib/storage_property_descr.cpp | 515 ++++++++++++------ .../src/applib/storage_property_descr.h | 2 +- gsmartcontrol/src/gsc_info_window.cpp | 6 +- gsmartcontrol/src/hz/fs_file.h | 2 +- gsmartcontrol/src/res/gsc_info_window.glade | 4 +- 12 files changed, 405 insertions(+), 183 deletions(-) diff --git a/gsmartcontrol/src/applib/selftest.cpp b/gsmartcontrol/src/applib/selftest.cpp index 62ac2af..7f7b134 100644 --- a/gsmartcontrol/src/applib/selftest.cpp +++ b/gsmartcontrol/src/applib/selftest.cpp @@ -213,8 +213,9 @@ std::string SelfTest::update(hz::intrusive_ptr smartctl_ex) if (!error_msg.empty()) // checks for empty output too return error_msg; + StorageAttribute::DiskType disk_type = drive_->get_is_hdd() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD; SmartctlParser ps; - if (!ps.parse_full(output)) { // try to parse it + if (!ps.parse_full(output, disk_type)) { // try to parse it return ps.get_error_msg(); } diff --git a/gsmartcontrol/src/applib/smartctl_parser.cpp b/gsmartcontrol/src/applib/smartctl_parser.cpp index c5395d1..312d21e 100644 --- a/gsmartcontrol/src/applib/smartctl_parser.cpp +++ b/gsmartcontrol/src/applib/smartctl_parser.cpp @@ -66,15 +66,21 @@ namespace { +SmartctlParser::SmartctlParser() + : disk_type_(StorageAttribute::DiskAny) +{ } + // Parse full "smartctl -a" output -bool SmartctlParser::parse_full(const std::string& full) +bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskType disk_type) { this->clear(); // clear previous data this->set_data_full(full); + disk_type_ = disk_type; + // -------------------- Fix the output so it doesn't interfere with proper parsing @@ -1652,7 +1658,7 @@ bool SmartctlParser::parse_section_data_subsection_selective_selftest_log(const // Yes, there's no place for this in the Parser, but whatever... void SmartctlParser::add_property(StorageProperty p) { - storage_property_autoset_description(p); + storage_property_autoset_description(p, disk_type_); storage_property_autoset_warning(p); storage_property_autoset_warning_descr(p); // append warning to description diff --git a/gsmartcontrol/src/applib/smartctl_parser.h b/gsmartcontrol/src/applib/smartctl_parser.h index 5fa4a9b..2c30955 100644 --- a/gsmartcontrol/src/applib/smartctl_parser.h +++ b/gsmartcontrol/src/applib/smartctl_parser.h @@ -30,8 +30,12 @@ class SmartctlParser { typedef std::vector prop_list_t; + /// Constructor + SmartctlParser(); + + /// Parse full "smartctl -a" output - bool parse_full(const std::string& s); + bool parse_full(const std::string& s, StorageAttribute::DiskType disk_type); /// Supply any output of smartctl here, the smartctl version will be retrieved. @@ -165,6 +169,7 @@ class SmartctlParser { std::string error_msg_; ///< This will be filled with some displayable message on error + StorageAttribute::DiskType disk_type_; ///< Disk type (HDD, SSD) }; diff --git a/gsmartcontrol/src/applib/smartctl_parser_test.cpp b/gsmartcontrol/src/applib/smartctl_parser_test.cpp index b220ac6..d7dc560 100644 --- a/gsmartcontrol/src/applib/smartctl_parser_test.cpp +++ b/gsmartcontrol/src/applib/smartctl_parser_test.cpp @@ -45,7 +45,7 @@ int main(int argc, char** argv) SmartctlParser sp; - if (!sp.parse_full(contents)) { + if (!sp.parse_full(contents, StorageAttribute::DiskAny)) { debug_out_error("app", "Cannot parse file contents: " << sp.get_error_msg() << "\n"); return EXIT_FAILURE; } diff --git a/gsmartcontrol/src/applib/storage_device.cpp b/gsmartcontrol/src/applib/storage_device.cpp index 9138a85..9473bca 100644 --- a/gsmartcontrol/src/applib/storage_device.cpp +++ b/gsmartcontrol/src/applib/storage_device.cpp @@ -269,6 +269,13 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si serial_number_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(serial), ' '); } + std::string rpm_str; + if (app_pcre_match("/^Rotation Rate:[ \\t]*(.*)$/mi", info_output_, &rpm_str)) { + int rpm = 0; + hz::string_is_numeric(rpm_str, rpm, false); + hdd_ = rpm > 0; + } + // Note: this property is present since 5.33. std::string size; @@ -282,8 +289,12 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si // Note that this may try to parse data the second time (it may already have // been parsed by parse_data() which failed at it). if (do_set_properties) { + StorageAttribute::DiskType disk_type = StorageAttribute::DiskAny; + if (hdd_.defined()) { + disk_type = hdd_.value() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD; + } SmartctlParser ps; - if (ps.parse_full(this->info_output_)) { // try to parse it + if (ps.parse_full(this->info_output_, disk_type)) { // try to parse it this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data } } @@ -341,8 +352,12 @@ std::string StorageDevice::parse_data() { this->clear_fetched(false); // clear everything fetched before, except outputs + StorageAttribute::DiskType disk_type = StorageAttribute::DiskAny; + if (hdd_.defined()) { + disk_type = hdd_.value() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD; + } SmartctlParser ps; - if (ps.parse_full(this->full_output_)) { // try to parse it (parse only, set the properties after basic parsing). + if (ps.parse_full(this->full_output_, disk_type)) { // try to parse it (parse only, set the properties after basic parsing). // refresh basic info too this->info_output_ = ps.get_data_full(); // put data including version information @@ -703,6 +718,13 @@ string StorageDevice::get_serial_number() const +bool StorageDevice::get_is_hdd() const +{ + return hdd_.defined() ? hdd_.value() : false; +} + + + void StorageDevice::set_info_output(const string& s) { info_output_ = s; diff --git a/gsmartcontrol/src/applib/storage_device.h b/gsmartcontrol/src/applib/storage_device.h index 2b24d4e..884fdc2 100644 --- a/gsmartcontrol/src/applib/storage_device.h +++ b/gsmartcontrol/src/applib/storage_device.h @@ -197,6 +197,9 @@ class StorageDevice : public hz::intrusive_ptr_referenced { /// \return empty string if not found std::string get_serial_number() const; + /// Check whether this drive is a rotational HDD. + bool get_is_hdd() const; + /// Set "info" output to parse void set_info_output(const std::string& s); @@ -281,6 +284,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced { hz::OptionalValue family_name_; ///< Family name hz::OptionalValue serial_number_; ///< Serial number hz::OptionalValue size_; ///< Formatted size + hz::OptionalValue hdd_; ///< Whether it's a rotational drive (HDD) or something else (SSD, flash, etc...) mutable hz::OptionalValue health_property_; ///< Cached health property. SmartctlParser::prop_list_t properties_; ///< Smart properties. Detected through full output. diff --git a/gsmartcontrol/src/applib/storage_property.h b/gsmartcontrol/src/applib/storage_property.h index dc9d1b5..3674d0f 100644 --- a/gsmartcontrol/src/applib/storage_property.h +++ b/gsmartcontrol/src/applib/storage_property.h @@ -52,6 +52,13 @@ std::ostream& operator<< (std::ostream& os, const StorageCapability& p); class StorageAttribute { public: + /// Disk type the attribute may match + enum DiskType { + DiskAny, ///< Any disk type + DiskHDD, ///< HDD (rotational) only + DiskSSD ///< SSD only + }; + /// Attribute pre-failure / old-age type enum attr_t { attr_type_unknown, ///< Unknown diff --git a/gsmartcontrol/src/applib/storage_property_descr.cpp b/gsmartcontrol/src/applib/storage_property_descr.cpp index 2f09ef9..3944508 100644 --- a/gsmartcontrol/src/applib/storage_property_descr.cpp +++ b/gsmartcontrol/src/applib/storage_property_descr.cpp @@ -25,17 +25,18 @@ namespace { /// Attribute description for attribute database struct AttributeDescription { /// Constructor - AttributeDescription() : id(-1) + AttributeDescription() : id(-1), disk_type(StorageAttribute::DiskAny) { } /// Constructor - AttributeDescription(int32_t id_, const std::string& smartctl_name_, + AttributeDescription(int32_t id_, StorageAttribute::DiskType type, const std::string& smartctl_name_, const std::string& readable_name_, const std::string& generic_name_, const std::string& description_) - : id(id_), smartctl_name(smartctl_name_), readable_name(readable_name_), + : id(id_), disk_type(type), smartctl_name(smartctl_name_), readable_name(readable_name_), generic_name(generic_name_), description(description_) { } int32_t id; ///< e.g. 190 + StorageAttribute::DiskType disk_type; ///< HDD-only, SSD-only or universal attribute std::string smartctl_name; ///< e.g. Airflow_Temperature_Cel std::string readable_name; ///< e.g. Airflow Temperature (C) std::string generic_name; ///< Generic name to be set on the property. @@ -52,9 +53,9 @@ namespace { AttributeDatabase() { // Note: The first one with the same ID is the one displayed in case smartctl - // doesn't return a name. See atacmds.cpp in smartmontools. The rest are - // from drivedb.h, which contain overrides. - // Based on: smartmontools 5.41. + // doesn't return a name. See atacmds.cpp (get_default_attr_name()) in smartmontools. + // The rest are from drivedb.h, which contains overrides. + // Based on: smartmontools r3897, 2014-04-28. // "default" means it's in the default smartctl DB. // "non-default" means it's in drivedb.h. @@ -78,8 +79,8 @@ namespace { // Raw read error rate (default) add(1, "Raw_Read_Error_Rate", "Raw Read Error Rate", "", - "Indicates the rate of read errors that occurred while reading data from a disk surface. A non-zero Raw value may indicate a problem with either the disk surface or read/write heads. " - "Note: Some drives (e.g. Seagate) are known to report very high Raw values for this attribute, and it's not an indication of a problem."); + "Indicates the rate of read errors that occurred while reading the data. A non-zero Raw value may indicate a problem with either the disk surface or read/write heads. " + "Note: Some drives (e.g. Seagate) are known to report very high Raw values for this attribute; this is not an indication of a problem."); // Throughput Performance (default) add(2, "Throughput_Performance", "Throughput Performance", "", "Average efficiency of a drive. Reduction of this attribute value can signal various internal problems."); @@ -94,30 +95,33 @@ namespace { "Number of reallocated sectors (Raw value). Non-zero Raw value indicates a disk surface failure." "\n\n" + unc_text); // SandForce SSD: Retired_Block_Count (non-default) - add(5, "Retired_Block_Count", "Retired Block Rate", "ssd_life_left", + add(5, StorageAttribute::DiskSSD, "Retired_Block_Count", "Retired Block Rate", "ssd_life_left", "Indicates estimated remaining life of the drive. Normalized value is (100-100*RBC/MRB) where RBC is the number of retired blocks and MRB is the minimum required blocks."); + // OCZ SSD (non-default + add(5, StorageAttribute::DiskSSD, "Runtime_Bad_Block", "Runtime Bad Block Count", "", + ""); // Read Channel Margin (default) - add(6, "Read_Channel_Margin", "Read Channel Margin", "", + add(6, StorageAttribute::DiskHDD, "Read_Channel_Margin", "Read Channel Margin", "", "Margin of a channel while reading data. The function of this attribute is not specified."); // Seek Error Rate (default) - add(7, "Seek_Error_Rate", "Seek Error Rate", "", + add(7, StorageAttribute::DiskHDD, "Seek_Error_Rate", "Seek Error Rate", "", "Frequency of errors appearance while positioning. When a drive reads data, it positions heads in the needed place. If there is a failure in the mechanical positioning system, a seek error arises. More seek errors indicate worse condition of a disk surface and disk mechanical subsystem. The exact meaning of the Raw value is manufacturer-dependent."); // Seek Time Performance (default) - add(8, "Seek_Time_Performance", "Seek Time Performance", "", + add(8, StorageAttribute::DiskHDD, "Seek_Time_Performance", "Seek Time Performance", "", "Average efficiency of seek operations of the magnetic heads. If this value is decreasing, it is a sign of problems in the hard disk drive mechanical subsystem."); // Power-On Hours (default) (Maxtor may use minutes, Fujitsu may use seconds, some even temperature?) add(9, "Power_On_Hours", "Power-On Time", "", "Number of hours in power-on state. Raw value shows total count of hours (or minutes, or half-minutes, or seconds, depending on manufacturer) in power-on state."); // SandForce, Intel SSD: Power_On_Hours_and_Msec (non-default) (description?) - add(9, "Power_On_Hours_and_Msec"); + add(9, StorageAttribute::DiskSSD, "Power_On_Hours_and_Msec"); // Smart Storage Systems SSD (non-default) - add(9, "Proprietary_9", "Internal Attribute", "", + add(9, StorageAttribute::DiskSSD, "Proprietary_9", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Spin-up Retry Count (default) - add(10, "Spin_Retry_Count", "Spin-Up Retry Count", "spin_up_retry_count", + add(10, StorageAttribute::DiskHDD, "Spin_Retry_Count", "Spin-Up Retry Count", "spin_up_retry_count", "Number of retries of spin start attempts (Raw value). An increase of this attribute value is a sign of problems in the hard disk mechanical subsystem."); // Calibration Retry Count (default) - add(11, "Calibration_Retry_Count", "Calibration Retry Count", "", + add(11, StorageAttribute::DiskHDD, "Calibration_Retry_Count", "Calibration Retry Count", "", "Number of times recalibration was requested, under the condition that the first attempt was unsuccessful (Raw value). A decrease is a sign of problems in the hard disk mechanical subsystem."); // Power Cycle Count (default) add(12, "Power_Cycle_Count", "Power Cycle Count", "", @@ -126,157 +130,208 @@ namespace { add(13, "Read_Soft_Error_Rate", "Soft Read Error Rate", "soft_read_error_rate", "Uncorrected read errors reported to the operating system (Raw value). If the value is non-zero, you should back up your data."); // Sandforce SSD: Soft_Read_Error_Rate (non-default) - add(13, "Soft_Read_Error_Rate"); + add(13, StorageAttribute::DiskSSD, "Soft_Read_Error_Rate"); // Maxtor: Average FHC (custom) (description?) - add(99, "", "Average FHC (Flying Height Control)", "", + add(99, StorageAttribute::DiskHDD, "", "Average FHC (Flying Height Control)", "", ""); // Sandforce SSD: Gigabytes_Erased (non-default) (description?) - add(100, "Gigabytes_Erased", "GiB Erased", "", + add(100, StorageAttribute::DiskSSD, "Gigabytes_Erased", "GiB Erased", "", "Number of GiB erased."); + // OCZ SSD (non-default) + add(100, StorageAttribute::DiskSSD, "Total_Blocks_Erased", "Total Blocks Erased", "", + "Number of total blocks erased."); // STEC CF: (custom) - add(100, "", "Erase / Program Cycles", "", // unused + add(100, StorageAttribute::DiskSSD, "", "Erase / Program Cycles", "", // unused "Number of Erase / Program cycles of the entire drive."); // Maxtor: Maximum FHC (custom) (description?) - add(101, "", "Maximum FHC (Flying Height Control)", "", + add(101, StorageAttribute::DiskHDD, "", "Maximum FHC (Flying Height Control)", "", ""); // Unknown (source says Maxtor, but it's an SSD thing and Maxtor doesn't have them at this point). // add(101, "", "Translation Table Rebuild", "", // "Indicates power backup fault or internal error resulting in loss of system unit tables."); // STEC CF: Translation Table Rebuild (custom) - add(103, "", "Translation Table Rebuild", "", + add(103, StorageAttribute::DiskSSD, "", "Translation Table Rebuild", "", "Indicates power backup fault or internal error resulting in loss of system unit tables."); // Smart Storage Systems SSD (non-default) (description?) - add(130, "Minimum_Spares_All_Zs", "Minimum Spares All Zs", "", + add(130, StorageAttribute::DiskSSD, "Minimum_Spares_All_Zs", "Minimum Spares All Zs", "", + ""); + // Apacer Flash (description?) (non-default) + add(160, StorageAttribute::DiskSSD, "Initial_Bad_Block_Count", "Initial Bad Block Count", "", + ""); + // Apacer Flash (description?) (non-default) + add(161, StorageAttribute::DiskSSD, "Bad_Block_Count", "Bad Block Count", "", + ""); + // Apacer Flash (description?) (non-default) + add(162, StorageAttribute::DiskSSD, "Spare_Block_Count", "Spare Bad Block Count", "", + ""); + // Apacer Flash (description?) (non-default) + add(163, StorageAttribute::DiskSSD, "Max_Erase_Count", "Max Erase Count", "", + ""); + // Apacer Flash (description?) (non-default) + add(164, StorageAttribute::DiskSSD, "Min_Erase_Count", "Min Erase Count", "", + ""); + // Apacer Flash (description?) (non-default) + add(165, StorageAttribute::DiskSSD, "Average_Erase_Count", "Average Erase Count", "", ""); // Various SSDs: (non-default) (description?) - add(168, "SATA_Phy_Error_Count", "SATA Physical Error Count", "", + add(168, StorageAttribute::DiskSSD, "SATA_Phy_Error_Count", "SATA Physical Error Count", "", ""); - // STEC CF: Reserved Block Count (non-default) - add(170, "Reserve_Block_Count", "Reserved Block Count", "", + // Intel SSD, STEC CF: Reserved Block Count (non-default) + add(170, StorageAttribute::DiskSSD, "Reserve_Block_Count", "Reserved Block Count", "", "Number of reserved (spare) blocks for bad block handling."); // Crucial / Marvell SSD: Grown Failing Block Count (non-default) (description?) - add(170, "Grown_Failing_Block_Ct", "Grown Failing Block Count", "", + add(170, StorageAttribute::DiskSSD, "Grown_Failing_Block_Ct", "Grown Failing Block Count", "", ""); // Intel SSD: (non-default) (description?) - add(170, "Available_Reservd_Space", "Available Reservd Space", "", + add(170, StorageAttribute::DiskSSD, "Available_Reservd_Space", "Available Reserved Space", "", ""); // Various SSDs: (non-default) (description?) - add(170, "Bad_Block_Count", "Bad Block Count", "", + add(170, StorageAttribute::DiskSSD, "Bad_Block_Count", "Bad Block Count", "", ""); - // Sandforce SSD, STEC CF, Crucial / Marvell SSD: Program Fail Count (non-default) - add(171, "Program_Fail_Count", "Program Fail Count", "", + // Intel SSD, Sandforce SSD, STEC CF, Crucial / Marvell SSD: Program Fail Count (non-default) + add(171, StorageAttribute::DiskSSD, "Program_Fail_Count", "Program Fail Count", "", "Number of flash program (write) failures. High values may indicate old drive age or other problems."); - // Sandforce SSD, STEC CF, Crucial / Marvell SSD: Erase Fail Count (non-default) - add(172, "Erase_Fail_Count", "Erase Fail Count", "", + // OCZ SSD (non-default) + add(171, StorageAttribute::DiskSSD, "Avail_OP_Block_Count", "Available OP Block Count", "", + ""); + // Intel SSD, Sandforce SSD, STEC CF, Crucial / Marvell SSD: Erase Fail Count (non-default) + add(172, StorageAttribute::DiskSSD, "Erase_Fail_Count", "Erase Fail Count", "", "Number of flash erase command failures. High values may indicate old drive age or other problems."); // Various SSDs (non-default) (description?) - add(173, "Erase_Count", "Erase Count", "", + add(173, StorageAttribute::DiskSSD, "Erase_Count", "Erase Count", "", ""); - // STEC CF, Crucial / Marvell SSD: Wear Leveling Count (custom) (description?) - add(173, "", "Wear Leveling Count", "", + // STEC CF, Crucial / Marvell SSD: Wear Leveling Count (non-default) (description?) + add(173, StorageAttribute::DiskSSD, "Wear_Leveling_Count", "Wear Leveling Count", "", "Indicates the difference between the most worn block and the least worn block."); - // Sandforce SSD, Crucial / Marvell SSD: Unexpected Power Loss (non-default) - add(174, "Unexpect_Power_Loss_Ct", "Unexpected Power Loss", "", + // Same as above, old smartctl + add(173, StorageAttribute::DiskSSD, "Wear_Levelling_Count", "Wear Leveling Count", "", + "Indicates the difference between the most worn block and the least worn block."); + // Intel SSD, Sandforce SSD, Crucial / Marvell SSD: Unexpected Power Loss (non-default) + add(174, StorageAttribute::DiskSSD, "Unexpect_Power_Loss_Ct", "Unexpected Power Loss", "", + "Number of unexpected power loss events."); + // OCZ SSD (non-default) + add(174, StorageAttribute::DiskSSD, "Pwr_Cycle_Ct_Unplanned", "Unexpected Power Loss", "", "Number of unexpected power loss events."); // Program_Fail_Count_Chip (default) - add(175, "Program_Fail_Count_Chip", "Program Fail Count (Chip)", "", + add(175, StorageAttribute::DiskSSD, "Program_Fail_Count_Chip", "Program Fail Count (Chip)", "", "Number of flash program (write) failures. High values may indicate old drive age or other problems."); // Various SSDs: Bad_Cluster_Table_Count (non-default) (description?) - add(175, "Bad_Cluster_Table_Count", "Bad Cluster Table Count", "", + add(175, StorageAttribute::DiskSSD, "Bad_Cluster_Table_Count", "Bad Cluster Table Count", "", + ""); + // Intel SSD (non-default) (description?) + add(175, StorageAttribute::DiskSSD, "Power_Loss_Cap_Test", "Power Loss Capacitor Test", "", ""); // Erase_Fail_Count_Chip (default) - add(176, "Erase_Fail_Count_Chip", "Erase Fail Count (Chip)", "", + add(176, StorageAttribute::DiskSSD, "Erase_Fail_Count_Chip", "Erase Fail Count (Chip)", "", "Number of flash erase command failures. High values may indicate old drive age or other problems."); // Wear_Leveling_Count (default) (same as Wear_Range_Delta?) - add(177, "Wear_Leveling_Count", "Wear Leveling Count (Chip)", "", + add(177, StorageAttribute::DiskSSD, "Wear_Leveling_Count", "Wear Leveling Count (Chip)", "", "Indicates the difference (in percent) between the most worn block and the least worn block."); // Sandforce SSD: Wear_Range_Delta (non-default) - add(177, "Wear_Range_Delta", "Wear Range Delta", "", + add(177, StorageAttribute::DiskSSD, "Wear_Range_Delta", "Wear Range Delta", "", "Indicates the difference (in percent) between the most worn block and the least worn block."); // Used_Rsvd_Blk_Cnt_Chip (default) - add(178, "Used_Rsvd_Blk_Cnt_Chip", "Used Reserved Block Count (Chip)", "", + add(178, StorageAttribute::DiskSSD, "Used_Rsvd_Blk_Cnt_Chip", "Used Reserved Block Count (Chip)", "", "Number of a chip's used reserved blocks. High values may indicate old drive age or other problems."); // Used_Rsvd_Blk_Cnt_Tot (default) (description?) - add(179, "Used_Rsvd_Blk_Cnt_Tot", "Used Reserved Block Count (Total)", "", + add(179, StorageAttribute::DiskSSD, "Used_Rsvd_Blk_Cnt_Tot", "Used Reserved Block Count (Total)", "", "Number of used reserved blocks. High values may indicate old drive age or other problems."); // Unused_Rsvd_Blk_Cnt_Tot (default) - add(180, "Unused_Rsvd_Blk_Cnt_Tot", "Unused Reserved Block Count (Total)", "", + add(180, StorageAttribute::DiskSSD, "Unused_Rsvd_Blk_Cnt_Tot", "Unused Reserved Block Count (Total)", "", "Number of unused reserved blocks. High values may indicate old drive age or other problems."); // Program_Fail_Cnt_Total (default) add(181, "Program_Fail_Cnt_Total", "Program Fail Count", "", "Number of flash program (write) failures. High values may indicate old drive age or other problems."); // Sandforce SSD: Program_Fail_Count (non-default) (Sandforce says it's identical to 171) - add(181, "Program_Fail_Count"); - // Crucial / Marvell SSD: Program_Fail_Count (non-default) (description?) - add(181, "Non4k_Aligned_Access", "Non-4k Aligned Access", "", + add(181, StorageAttribute::DiskSSD, "Program_Fail_Count"); + // Crucial / Marvell SSD (non-default) (description?) + add(181, StorageAttribute::DiskSSD, "Non4k_Aligned_Access", "Non-4k Aligned Access", "", ""); // Erase_Fail_Count_Total (default) (description?) - add(182, "Erase_Fail_Count_Total", "Erase Fail Count", "", + add(182, StorageAttribute::DiskSSD, "Erase_Fail_Count_Total", "Erase Fail Count", "", "Number of flash erase command failures. High values may indicate old drive age or other problems."); // Sandforce SSD: Erase_Fail_Count (non-default) (Sandforce says it's identical to 172) - add(182, "Erase_Fail_Count"); + add(182, StorageAttribute::DiskSSD, "Erase_Fail_Count"); // Runtime_Bad_Block (default) (description?) add(183, "Runtime_Bad_Block", "Runtime Bad Blocks", "", ""); // Samsung, WD, Crucial / Marvell SSD: SATA Downshift Error Count (non-default) (description?) - add(183, "SATA_Iface_Downshift", "SATA Downshift Error Count", "", // unused + add(183, StorageAttribute::DiskAny, "SATA_Iface_Downshift", "SATA Downshift Error Count", "", + ""); + // Intel SSD, Ubtek SSD (non-default) (description?) + add(183, StorageAttribute::DiskSSD, "SATA_Downshift_Count", "SATA Downshift Error Count", "", ""); // End to End Error (default) (description?) add(184, "End-to-End_Error", "End to End Error", "", "Indicates discrepancy of data between the host and the drive cache."); // Sandforce SSD: IO_Error_Detect_Code_Ct (non-default) - add(184, "IO_Error_Detect_Code_Ct", "Input/Output ECC Error Count", "", + add(184, StorageAttribute::DiskSSD, "IO_Error_Detect_Code_Ct", "Input/Output ECC Error Count", "", + ""); + // OCZ SSD (non-default) + add(184, StorageAttribute::DiskSSD, "Factory_Bad_Block_Count", "Factory Bad Block Count", "", ""); // Indilinx Barefoot SSD: IO_Error_Detect_Code_Ct (non-default) - add(184, "Initial_Bad_Block_Count", "Initial Bad Block Count", "", + add(184, StorageAttribute::DiskSSD, "Initial_Bad_Block_Count", "Initial Bad Block Count", "", "Factory-determined number of initial bad blocks."); // WD: Head Stability (custom) - add(185, "", "Head Stability", "", + add(185, StorageAttribute::DiskHDD, "", "Head Stability", "", ""); // WD: Induced Op-Vibration Detection (custom) - add(185, "", "Induced Op-Vibration Detection", "", // unused + add(185, StorageAttribute::DiskHDD, "", "Induced Op-Vibration Detection", "", // unused ""); // Reported Uncorrectable (default) add(187, "Reported_Uncorrect", "Reported Uncorrectable", "", "Number of errors that could not be recovered using hardware ECC (Error-Correcting Code)."); // Samsung SSD, Intel SSD: Reported Uncorrectable (non-default) - add(187, "Uncorrectable_Error_Cnt"); + add(187, StorageAttribute::DiskSSD, "Uncorrectable_Error_Cnt"); + // OCZ SSD (non-default) + add(187, StorageAttribute::DiskSSD, "Total_Unc_NAND_Reads", "Total Uncorrectable NAND Reads", "", + ""); // Command Timeout (default) add(188, "Command_Timeout", "Command Timeout", "", "Number of aborted operations due to drive timeout. High values may indicate problems with cabling or power supply."); // High Fly Writes (default) - add(189, "High_Fly_Writes", "High Fly Writes", "", + add(189, StorageAttribute::DiskHDD, "High_Fly_Writes", "High Fly Writes", "", "Some drives can detect when a recording head is flying outside its normal operating range. " "If an unsafe fly height condition is encountered, the write process is stopped, and the information " "is rewritten or reallocated to a safe region of the drive. This attribute indicates the count of " "these errors detected over the lifetime of the drive."); // Crucial / Marvell SSD (non-default) - add(189, "Factory_Bad_Block_Ct", "Factory Bad Block Count", "", + add(189, StorageAttribute::DiskSSD, "Factory_Bad_Block_Ct", "Factory Bad Block Count", "", "Factory-determined number of initial bad blocks."); + // Various SSD (non-default) + add(189, "Airflow_Temperature_Cel", "Airflow Temperature", "", + "Indicates temperature (in Celsius), 100 - temperature, or something completely different (highly depends on manufacturer and model)."); // Airflow Temperature (default) (WD Caviar (may be 50 less), Samsung). Temperature or (100 - temp.) on Seagate/Maxtor. add(190, "Airflow_Temperature_Cel", "Airflow Temperature", "", "Indicates temperature (in Celsius), 100 - temperature, or something completely different (highly depends on manufacturer and model)."); // Samsung SSD (non-default) (description?) add(190, "Temperature_Exceed_Cnt", "Temperature Exceed Count", "", ""); + // OCZ SSD (non-default) + add(190, "Temperature_Celsius", "Temperature (Celsius)", "temperature_celsius", + "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius)."); + // Intel SSD + add(190, "Temperature_Case", "Case Temperature (Celsius)", "", + "Drive case temperature. The Raw value shows built-in heat sensor registrations (in Celsius)."); // G-sense error rate (default) (same as 221?) - add(191, "G-Sense_Error_Rate", "G-Sense Error Rate", "", + add(191, StorageAttribute::DiskHDD, "G-Sense_Error_Rate", "G-Sense Error Rate", "", "Number of errors caused by externally-induced shock and vibration (Raw value). May indicate incorrect installation."); // Power-Off Retract Cycle (default) - add(192, "Power-Off_Retract_Count", "Head Retract Cycle Count", "", + add(192, StorageAttribute::DiskHDD, "Power-Off_Retract_Count", "Head Retract Cycle Count", "", "Number of times the heads were loaded off the media (during power-offs or emergency conditions)."); // Intel SSD: Unsafe_Shutdown_Count (non-default) - add(192, "Unsafe_Shutdown_Count", "Unsafe Shutdown Count", "", + add(192, StorageAttribute::DiskSSD, "Unsafe_Shutdown_Count", "Unsafe Shutdown Count", "", "Raw value indicates the number of unsafe (unclean) shutdown events over the drive lifetime. " "An unsafe shutdown occurs whenever the device is powered off without " "STANDBY IMMEDIATE being the last command."); // Various SSDs (non-default) add(192, "Unexpect_Power_Loss_Ct"); // Fujitsu: Emergency Retract Cycle Count (non-default) - add(192, "Emerg_Retract_Cycle_Ct", "Emergency Retract Cycle Count", "", + add(192, StorageAttribute::DiskHDD, "Emerg_Retract_Cycle_Ct", "Emergency Retract Cycle Count", "", "Number of times the heads were loaded off the media during emergency conditions."); // Load/Unload Cycle (default) - add(193, "Load_Cycle_Count", "Load / Unload Cycle", "", + add(193, StorageAttribute::DiskHDD, "Load_Cycle_Count", "Load / Unload Cycle", "", "Number of load / unload cycles into Landing Zone position."); // Temperature Celsius (default) (same as 231). This is the most common one. Some Samsungs: 10xTemp. add(194, "Temperature_Celsius", "Temperature (Celsius)", "temperature_celsius", @@ -287,41 +342,53 @@ namespace { add(194, "Temperature_Celsius_x10", "Temperature (Celsius) x 10", "temperature_celsius_x10", "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius * 10). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors)."); // Smart Storage Systems SSD (non-default) - add(194, "Proprietary_194", "Internal Attribute", "", + add(194, StorageAttribute::DiskSSD, "Proprietary_194", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); + // Intel SSD (non-default) + add(194, "Temperature_Internal", "Internal Temperature (Celsius)", "temperature_celsius", + "Drive case temperature. The Raw value shows built-in heat sensor registrations (in Celsius).."); // Hardware ECC Recovered (default) add(195, "Hardware_ECC_Recovered", "Hardware ECC Recovered", "", "Number of ECC on the fly errors (Raw value). Users are advised to ignore this attribute."); // Fujitsu: ECC_On_The_Fly_Count (non-default) - add(195, "ECC_On_The_Fly_Count"); + add(195, StorageAttribute::DiskHDD, "ECC_On_The_Fly_Count"); // Sandforce SSD: ECC_Uncorr_Error_Count (non-default) (description?) - add(195, "ECC_Uncorr_Error_Count", "Uncorrected ECC Error Count", "", + add(195, StorageAttribute::DiskSSD, "ECC_Uncorr_Error_Count", "Uncorrected ECC Error Count", "", "Number of uncorrectable errors (UECC)."); // Samsung SSD (non-default) (description?) - add(195, "ECC_Rate", "Uncorrected ECC Error Rate", "", + add(195, StorageAttribute::DiskSSD, "ECC_Rate", "Uncorrected ECC Error Rate", "", + ""); + // OCZ SSD (non-default) + add(195, StorageAttribute::DiskSSD, "Total_Prog_Failures", "Total Program Failures", "", ""); // Indilinx Barefoot SSD: Program_Failure_Blk_Ct (non-default) (description?) - add(195, "Program_Failure_Blk_Ct", "Program Failure Block Count", "", + add(195, StorageAttribute::DiskSSD, "Program_Failure_Blk_Ct", "Program Failure Block Count", "", "Number of flash program (write) failures."); // Reallocation Event Count (default) - add(196, "Reallocated_Event_Count", "Reallocation Event Count", "reallocation_event_count", + add(196, StorageAttribute::DiskAny, "Reallocated_Event_Count", "Reallocation Event Count", "reallocation_event_count", "Number of reallocation (remap) operations. Raw value should show the total number of attempts (both successful and unsuccessful) to reallocate sectors. An increase in Raw value indicates a disk surface failure." "\n\n" + unc_text); // Indilinx Barefoot SSD: Erase_Failure_Blk_Ct (non-default) (description?) - add(196, "Erase_Failure_Blk_Ct", "Erase Failure Block Count", "", + add(196, StorageAttribute::DiskSSD, "Erase_Failure_Blk_Ct", "Erase Failure Block Count", "", "Number of flash erase failures."); + // OCZ SSD (non-default) + add(196, StorageAttribute::DiskSSD, "Total_Erase_Failures", "Total Erase Failures", "", + ""); // Current Pending Sector Count (default) add(197, "Current_Pending_Sector", "Current Pending Sector Count", "current_pending_sector_count", "Number of "unstable" (waiting to be remapped) sectors (Raw value). If the unstable sector is subsequently read from or written to successfully, this value is decreased and the sector is not remapped. An increase in Raw value indicates a disk surface failure." "\n\n" + unc_text); // Indilinx Barefoot SSD: Read_Failure_Blk_Ct (non-default) (description?) - add(197, "Read_Failure_Blk_Ct", "Read Failure Block Count", "", + add(197, StorageAttribute::DiskSSD, "Read_Failure_Blk_Ct", "Read Failure Block Count", "", "Number of blocks that failed to be read."); // Samsung: Total_Pending_Sectors (non-default). From smartctl man page: // unlike Current_Pending_Sector, this won't decrease on reallocation. add(197, "Total_Pending_Sectors", "Total Pending Sectors", "total_pending_sectors", "Number of "unstable" (waiting to be remapped) sectors and already remapped sectors (Raw value). An increase in Raw value indicates a disk surface failure." "\n\n" + unc_text); + // OCZ SSD (non-default) + add(197, StorageAttribute::DiskSSD, "Total_Unc_Read_Failures", "Total Uncorrectable Read Failures", "", + ""); // Offline Uncorrectable (default) add(198, "Offline_Uncorrectable", "Offline Uncorrectable", "offline_uncorrectable", "Number of sectors which couldn't be corrected during Offline Data Collection (Raw value). An increase in Raw value indicates a disk surface failure. " @@ -333,276 +400,345 @@ namespace { "Number of sectors which couldn't be corrected during Offline Data Collection (Raw value), currently and in the past. An increase in Raw value indicates a disk surface failure." "\n\n" + unc_text); // Sandforce SSD: Uncorrectable_Sector_Ct (non-default) (same description?) - add(198, "Uncorrectable_Sector_Ct"); + add(198, StorageAttribute::DiskSSD, "Uncorrectable_Sector_Ct"); // Indilinx Barefoot SSD: Read_Sectors_Tot_Ct (non-default) (description?) - add(198, "Read_Sectors_Tot_Ct", "Total Read Sectors", "", + add(198, StorageAttribute::DiskSSD, "Read_Sectors_Tot_Ct", "Total Read Sectors", "", "Total count of read sectors."); + // OCZ SSD + add(198, StorageAttribute::DiskSSD, "Host_Reads_GiB", "Host Read GiB", "", + "Total volume of read data."); // Fujitsu: Offline_Scan_UNC_SectCt (non-default) - add(198, "Offline_Scan_UNC_SectCt"); + add(198, StorageAttribute::DiskHDD, "Offline_Scan_UNC_SectCt"); // Fujitsu version of Offline Uncorrectable (non-default) (old, not in current smartctl) - add(198, "Off-line_Scan_UNC_Sector_Ct"); + add(198, StorageAttribute::DiskHDD, "Off-line_Scan_UNC_Sector_Ct"); // UDMA CRC Error Count (default) add(199, "UDMA_CRC_Error_Count", "UDMA CRC Error Count", "", "Number of errors in data transfer via the interface cable in UDMA mode, as determined by ICRC (Interface Cyclic Redundancy Check) (Raw value)."); // Sandforce SSD: SATA_CRC_Error_Count (non-default) (description?) add(199, "SATA_CRC_Error_Count", "SATA CRC Error Count", "", "Number of errors in data transfer via the SATA interface cable (Raw value)."); - // Samsung SSD (non-default) (description?) + // Intel SSD, Samsung SSD (non-default) (description?) add(199, "CRC_Error_Count", "CRC Error Count", "", "Number of errors in data transfer via the interface cable (Raw value)."); // Indilinx Barefoot SSD: Write_Sectors_Tot_Ct (non-default) (description?) - add(199, "Write_Sectors_Tot_Ct", "Total Written Sectors", "", + add(199, StorageAttribute::DiskSSD, "Write_Sectors_Tot_Ct", "Total Written Sectors", "", "Total count of written sectors."); + // OCZ SSD + add(198, StorageAttribute::DiskSSD, "Host_Writes_GiB", "Host Written GiB", "", + "Total volume of written data."); // WD: Multi-Zone Error Rate (default). (maybe head flying height too (?)) - add(200, "Multi_Zone_Error_Rate", "Multi Zone Error Rate", "", + add(200, StorageAttribute::DiskHDD, "Multi_Zone_Error_Rate", "Multi Zone Error Rate", "", "Number of errors found when writing to sectors (Raw value). The higher the value, the worse the disk surface condition and/or mechanical subsystem is."); // Fujitsu: Write Error Rate (non-default) - add(200, "Write_Error_Count", "Write Error Count", "", + add(200, StorageAttribute::DiskHDD, "Write_Error_Count", "Write Error Count", "", "Number of errors found when writing to sectors (Raw value). The higher the value, the worse the disk surface condition and/or mechanical subsystem is."); // Indilinx Barefoot SSD: Read_Commands_Tot_Ct (non-default) (description?) - add(200, "Read_Commands_Tot_Ct", "Total Read Commands Issued", "", + add(200, StorageAttribute::DiskSSD, "Read_Commands_Tot_Ct", "Total Read Commands Issued", "", "Total count of read commands issued."); // Soft Read Error Rate (default) (description?) - add(201, "Soft_Read_Error_Rate", "Soft Read Error Rate", "soft_read_error_rate", + add(201, StorageAttribute::DiskHDD, "Soft_Read_Error_Rate", "Soft Read Error Rate", "soft_read_error_rate", "Uncorrected read errors reported to the operating system (Raw value). If the value is non-zero, you should back up your data."); // Sandforce SSD: Unc_Soft_Read_Err_Rate (non-default) - add(201, "Unc_Soft_Read_Err_Rate"); + add(201, StorageAttribute::DiskSSD, "Unc_Soft_Read_Err_Rate"); // Samsung SSD: (non-default) (description?) - add(201, "Supercap_Status", "Supercapacitor Health", "", + add(201, StorageAttribute::DiskSSD, "Supercap_Status", "Supercapacitor Health", "", ""); // Maxtor: Off Track Errors (custom) - add(201, "", "Off Track Errors", "", // unused - ""); +// add(201, StorageAttribute::DiskHDD, "", "Off Track Errors", "", // unused +// ""); // Fujitsu: Detected TA Count (non-default) (description?) - add(201, "Detected_TA_Count", "Torque Amplification Count", "", + add(201, StorageAttribute::DiskHDD, "Detected_TA_Count", "Torque Amplification Count", "", "Number of attempts to compensate for platter speed variations."); // Indilinx Barefoot SSD: Write_Commands_Tot_Ct (non-default) (description?) - add(201, "Write_Commands_Tot_Ct", "Total Write Commands Issued", "", + add(201, StorageAttribute::DiskSSD, "Write_Commands_Tot_Ct", "Total Write Commands Issued", "", "Total count of write commands issued."); // WD: Data Address Mark Errors (default) - add(202, "Data_Address_Mark_Errs", "Data Address Mark Errors", "", + add(202, StorageAttribute::DiskHDD, "Data_Address_Mark_Errs", "Data Address Mark Errors", "", "Frequency of the Data Address Mark errors."); // Fujitsu: TA Increase Count (same as 227?) - add(202, "TA_Increase_Count", "TA Increase Count", "", + add(202, StorageAttribute::DiskHDD, "TA_Increase_Count", "TA Increase Count", "", "Number of attempts to compensate for platter speed variations."); // Indilinx Barefoot SSD: Error_Bits_Flash_Tot_Ct (non-default) (description?) - add(202, "Error_Bits_Flash_Tot_Ct", "Total Count of Error Bits", "", + add(202, StorageAttribute::DiskSSD, "Error_Bits_Flash_Tot_Ct", "Total Count of Error Bits", "", ""); // Crucial / Marvell SSD: Perc_Rated_Life_Used (non-default) (description?) - add(202, "Perc_Rated_Life_Used", "Rated life used (percent)", "", + add(202, StorageAttribute::DiskSSD, "Perc_Rated_Life_Used", "Rated life used (percent)", "", ""); // Samsung SSD: (non-default) (description?) - add(202, "Exception_Mode_Status", "Exception Mode Status", "", + add(202, StorageAttribute::DiskSSD, "Exception_Mode_Status", "Exception Mode Status", "", + ""); + // OCZ SSD (non-default) (description?) + add(202, StorageAttribute::DiskSSD, "Total_Read_Bits_Corr_Ct", "Total Read Bits Corrected", "", ""); // Run Out Cancel (default). (description?) add(203, "Run_Out_Cancel", "Run Out Cancel", "", "Number of ECC errors."); // Maxtor: ECC Errors (non-default) (description?) - add(203, "Corr_Read_Errors_Tot_Ct", "ECC Errors", "", + add(203, StorageAttribute::DiskHDD, "Corr_Read_Errors_Tot_Ct", "ECC Errors", "", "Number of ECC errors."); // Indilinx Barefoot SSD: Corr_Read_Errors_Tot_Ct (non-default) (description?) - add(203, "Corr_Read_Errors_Tot_Ct", "Total Corrected Read Errors", "", + add(203, StorageAttribute::DiskSSD, "Corr_Read_Errors_Tot_Ct", "Total Corrected Read Errors", "", "Total cound of read sectors with correctable errors."); // Maxtor: Soft ECC Correction (default) - add(204, "Soft_ECC_Correction", "Soft ECC Correction", "", + add(204, StorageAttribute::DiskHDD, "Soft_ECC_Correction", "Soft ECC Correction", "", "Number of errors corrected by software ECC (Error-Correcting Code)."); // Fujitsu: Shock_Count_Write_Opern (non-default) (description?) - add(204, "Shock_Count_Write_Opern", "Shock Count During Write Operation", "", + add(204, StorageAttribute::DiskHDD, "Shock_Count_Write_Opern", "Shock Count During Write Operation", "", ""); // Sandforce SSD: Soft_ECC_Correct_Rate (non-default) (description?) - add(204, "Soft_ECC_Correct_Rate", "Soft ECC Correction Rate", "", + add(204, StorageAttribute::DiskSSD, "Soft_ECC_Correct_Rate", "Soft ECC Correction Rate", "", ""); // Indilinx Barefoot SSD: Bad_Block_Full_Flag (non-default) (description?) - add(204, "Bad_Block_Full_Flag", "Bad Block Area Is Full", "", + add(204, StorageAttribute::DiskSSD, "Bad_Block_Full_Flag", "Bad Block Area Is Full", "", "Indicates whether the bad block (reserved) area is full or not."); // Thermal Asperity Rate (TAR) (default) add(205, "Thermal_Asperity_Rate", "Thermal Asperity Rate", "", "Number of problems caused by high temperature."); // Fujitsu: Shock_Rate_Write_Opern (non-default) (description?) - add(205, "Shock_Rate_Write_Opern", "Shock Rate During Write Operation", "", + add(205, StorageAttribute::DiskHDD, "Shock_Rate_Write_Opern", "Shock Rate During Write Operation", "", ""); // Indilinx Barefoot SSD: Max_PE_Count_Spec (non-default) (description?) - add(205, "Max_PE_Count_Spec", "Maximum PE Count Specification", "", + add(205, StorageAttribute::DiskSSD, "Max_PE_Count_Spec", "Maximum PE Count Specification", "", + "Maximum Program / Erase cycle count as per specification."); + // OCZ SSD (non-default) + add(205, StorageAttribute::DiskSSD, "Max_Rated_PE_Count", "Maximum Rated PE Count", "", "Maximum Program / Erase cycle count as per specification."); // Flying Height (default) - add(206, "Flying_Height", "Head Flying Height", "", + add(206, StorageAttribute::DiskHDD, "Flying_Height", "Head Flying Height", "", "The height of the disk heads above the disk surface. A downward trend will often predict a head crash, " "while high values may cause read / write errors."); - // Indilinx Barefoot SSD: Min_Erase_Count (non-default) (description?) - add(206, "Min_Erase_Count", "Minimum Erase Count", "", + // Indilinx Barefoot SSD, OCZ SSD: Min_Erase_Count (non-default) (description?) + add(206, StorageAttribute::DiskSSD, "Min_Erase_Count", "Minimum Erase Count", "", "The minimum of individual erase counts of all the blocks."); // Crucial / Marvell SSD: Write_Error_Rate (non-default) (description?) - add(206, "Write_Error_Rate", "Write Error Rate", "", + add(206, StorageAttribute::DiskSSD, "Write_Error_Rate", "Write Error Rate", "", ""); // Spin High Current (default) - add(207, "Spin_High_Current", "Spin High Current", "", + add(207, StorageAttribute::DiskHDD, "Spin_High_Current", "Spin High Current", "", "Amount of high current needed or used to spin up the drive."); - // Indilinx Barefoot SSD: Max_Erase_Count (non-default) (description?) - add(207, "Max_Erase_Count", "Maximum Erase Count", "", + // Indilinx Barefoot SSD, OCZ SSD: Max_Erase_Count (non-default) (description?) + add(207, StorageAttribute::DiskSSD, "Max_Erase_Count", "Maximum Erase Count", "", "The maximum of individual erase counts of all the blocks."); // Spin Buzz (default) - add(208, "Spin_Buzz", "Spin Buzz", "", + add(208, StorageAttribute::DiskHDD, "Spin_Buzz", "Spin Buzz", "", "Number of buzz routines (retries because of low current) to spin up the drive."); - // Indilinx Barefoot SSD: Average_Erase_Count (non-default) (description?) - add(208, "Average_Erase_Count", "Average Erase Count", "", + // Indilinx Barefoot SSD, OCZ SSD: Average_Erase_Count (non-default) (description?) + add(208, StorageAttribute::DiskSSD, "Average_Erase_Count", "Average Erase Count", "", "The average of individual erase counts of all the blocks."); // Offline Seek Performance (default) (description?) - add(209, "Offline_Seek_Performnce", "Offline Seek Performance", "", + add(209, StorageAttribute::DiskHDD, "Offline_Seek_Performnce", "Offline Seek Performance", "", "Seek performance during Offline Data Collection operations."); // Indilinx Barefoot SSD, OCZ SSD: Remaining_Lifetime_Perc (non-default) (description?) - add(209, "Remaining_Lifetime_Perc", "Remaining Lifetime %", "ssd_life_left", + add(209, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime %", "ssd_life_left", "Remaining drive life in % (usually by erase count)."); // Vibration During Write (custom). wikipedia says 211, but it's wrong. (description?) add(210, "", "Vibration During Write", "", "Vibration encountered during write operations."); + // OCZ SSD (non-default) + add(210, StorageAttribute::DiskSSD, "SATA_CRC_Error_Count", "SATA CRC Error Count", "", + ""); // Indilinx Barefoot SSD: Indilinx_Internal (non-default) (description?) - add(210, "Indilinx_Internal", "Internal Attribute", "", + add(210, StorageAttribute::DiskSSD, "Indilinx_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Vibration During Read (description?) - add(211, "", "Vibration During Read", "", + add(211, StorageAttribute::DiskHDD, "", "Vibration During Read", "", "Vibration encountered during read operations."); - // Indilinx Barefoot SSD: Erase_Failure_Blk_Ct (non-default) (description?) - add(211, "SATA_Error_Ct_CRC", "SATA CRC Error Count", "", + // Indilinx Barefoot SSD (non-default) (description?) + add(211, StorageAttribute::DiskSSD, "SATA_Error_Ct_CRC", "SATA CRC Error Count", "", + "Number of errors in data transfer via the SATA interface cable"); + // OCZ SSD (non-default) (description?) + add(211, StorageAttribute::DiskSSD, "SATA_UNC_Count", "SATA Uncorrectable Error Count", "", "Number of errors in data transfer via the SATA interface cable"); // Shock During Write (custom) (description?) - add(212, "", "Shock During Write", "", + add(212, StorageAttribute::DiskHDD, "", "Shock During Write", "", "Shock encountered during write operations"); // Indilinx Barefoot SSD: SATA_Error_Ct_Handshake (non-default) (description?) - add(212, "SATA_Error_Ct_Handshake", "SATA Handshake Error Count", "", + add(212, StorageAttribute::DiskSSD, "SATA_Error_Ct_Handshake", "SATA Handshake Error Count", "", "Number of errors occurring during SATA handshake."); + // OCZ SSD (non-default) (description?) + add(211, StorageAttribute::DiskSSD, "NAND_Reads_with_Retry", "Number of NAND Reads with Retry", "", + ""); // Indilinx Barefoot SSD: Indilinx_Internal (non-default) (description?) - add(213, "Indilinx_Internal", "Internal Attribute", "", + add(213, StorageAttribute::DiskSSD, "Indilinx_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); + // OCZ SSD (non-default) (description?) + add(213, StorageAttribute::DiskSSD, "Simple_Rd_Rtry_Attempts", "Simple Read Retry Attempts", "", + ""); + // OCZ SSD (non-default) (description?) + add(213, StorageAttribute::DiskSSD, "Adaptv_Rd_Rtry_Attempts", "Adaptive Read Retry Attempts", "", + ""); // Disk Shift (default) // Note: There's also smartctl shortcut option "-v 220,temp" (possibly for Temperature Celsius), // but it's not used anywhere, so we ignore it. - add(220, "Disk_Shift", "Disk Shift", "", + add(220, StorageAttribute::DiskHDD, "Disk_Shift", "Disk Shift", "", "Shift of disks towards spindle. Shift of disks is possible as a result of a strong shock or a fall, high temperature, or some other reasons."); // G-sense error rate (default) - add(221, "G-Sense_Error_Rate", "G-Sense Error Rate", "", + add(221, StorageAttribute::DiskHDD, "G-Sense_Error_Rate", "G-Sense Error Rate", "", "Number of errors resulting from externally-induced shock and vibration (Raw value). May indicate incorrect installation."); + // OCZ SSD (non-default) (description?) + add(213, StorageAttribute::DiskSSD, "Int_Data_Path_Prot_Unc", "Internal Data Path Protection Uncorrectable", "", + ""); // Loaded Hours (default) - add(222, "Loaded_Hours", "Loaded Hours", "", + add(222, StorageAttribute::DiskHDD, "Loaded_Hours", "Loaded Hours", "", "Number of hours spent operating under load (movement of magnetic head armature) (Raw value)"); + // OCZ SSD (non-default) (description?) + add(222, StorageAttribute::DiskSSD, "RAID_Recovery_Count", "RAID Recovery Count", "", + ""); // Load/Unload Retry Count (default) (description?) - add(223, "Load_Retry_Count", "Load / Unload Retry Count", "", + add(223, StorageAttribute::DiskHDD, "Load_Retry_Count", "Load / Unload Retry Count", "", "Number of times the head armature entered / left the data zone."); // Load Friction (default) - add(224, "Load_Friction", "Load Friction", "", + add(224, StorageAttribute::DiskHDD, "Load_Friction", "Load Friction", "", "Resistance caused by friction in mechanical parts while operating. An increase of Raw value may mean that there is a problem with the mechanical subsystem of the drive."); // Load/Unload Cycle Count (default) (description?) - add(225, "Load_Cycle_Count", "Load / Unload Cycle Count", "", + add(225, StorageAttribute::DiskHDD, "Load_Cycle_Count", "Load / Unload Cycle Count", "", "Total number of load cycles."); // Intel SSD: Host_Writes_32MiB (non-default) (description?) - add(225, "Host_Writes_32MiB", "Host Writes (32 MiB)", "", + add(225, StorageAttribute::DiskSSD, "Host_Writes_32MiB", "Host Writes (32 MiB)", "", "Total number of sectors written by the host system. The Raw value is increased by 1 for every 32 MiB written by the host."); // Load-in Time (default) - add(226, "Load-in_Time", "Load-in Time", "", + add(226, StorageAttribute::DiskHDD, "Load-in_Time", "Load-in Time", "", "Total time of loading on the magnetic heads actuator. Indicates total time in which the drive was under load (on the assumption that the magnetic heads were in operating mode and out of the parking area)."); // Intel SSD: Intel_Internal (non-default) - add(226, "Intel_Internal", "Internal Attribute", "", + add(226, StorageAttribute::DiskSSD, "Intel_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD: Workld_Media_Wear_Indic (non-default) - add(226, "Workld_Media_Wear_Indic", "Timed Workload Media Wear", "", + add(226, StorageAttribute::DiskSSD, "Workld_Media_Wear_Indic", "Timed Workload Media Wear", "", "Timed workload media wear indicator (percent*1024)"); // Torque Amplification Count (aka TA) (default) - add(227, "Torq-amp_Count", "Torque Amplification Count", "", + add(227, StorageAttribute::DiskHDD, "Torq-amp_Count", "Torque Amplification Count", "", "Number of attempts to compensate for platter speed variations."); // Intel SSD: Intel_Internal (non-default) - add(227, "Intel_Internal", "Internal Attribute", "", + add(227, StorageAttribute::DiskSSD, "Intel_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD: Workld_Host_Reads_Perc (non-default) - add(227, "Workld_Host_Reads_Perc", "Timed Workload Host Reads %", "", + add(227, StorageAttribute::DiskSSD, "Workld_Host_Reads_Perc", "Timed Workload Host Reads %", "", ""); // Power-Off Retract Count (default) add(228, "Power-off_Retract_Count", "Power-Off Retract Count", "", "Number of times the magnetic armature was retracted automatically as a result of power loss."); // Intel SSD: Intel_Internal (non-default) - add(228, "Intel_Internal", "Internal Attribute", "", + add(228, StorageAttribute::DiskSSD, "Intel_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD: Workload_Minutes (non-default) - add(228, "Workload_Minutes", "Workload (Minutes)", "", + add(228, StorageAttribute::DiskSSD, "Workload_Minutes", "Workload (Minutes)", "", ""); // Transcend SSD: Halt_System_ID (non-default) (description?) - add(229, "Halt_System_ID", "Halt System ID", "", + add(229, StorageAttribute::DiskSSD, "Halt_System_ID", "Halt System ID", "", "Halt system ID and flash ID"); + // InnoDisk SSD (non-default) + add(229, StorageAttribute::DiskSSD, "Flash_ID", "Flash ID", "", + "Flash ID"); // IBM: GMR Head Amplitude (default) - add(230, "Head_Amplitude", "GMR Head Amplitude", "", + add(230, StorageAttribute::DiskHDD, "Head_Amplitude", "GMR Head Amplitude", "", "Amplitude of heads trembling (GMR-head) in running mode."); // Sandforce SSD: Life_Curve_Status (non-default) (description?) - add(230, "Life_Curve_Status", "Life Curve Status", "", + add(230, StorageAttribute::DiskSSD, "Life_Curve_Status", "Life Curve Status", "", "Current state of drive operation based upon the Life Curve."); + // OCZ SSD (non-default) (description?) + add(230, StorageAttribute::DiskSSD, "SuperCap_Charge_Status", "Super-Capacitor Charge Status", "", + "0 means not charged, 1 - fully charged, 2 - unknown."); // Temperature (Some drives) (default) add(231, "Temperature_Celsius", "Temperature", "temperature_celsius", "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors)."); // Sandforce SSD: SSD_Life_Left - add(231, "SSD_Life_Left", "SSD Life Left", "ssd_life_left", + add(231, StorageAttribute::DiskSSD, "SSD_Life_Left", "SSD Life Left", "ssd_life_left", "A measure of drive's estimated life left. A Normalized value of 100 indicates a new drive. " "10 means there are reserved blocks left but Program / Erase cycles have been used. " "0 means insufficient reserved blocks, drive may be in read-only mode to allow recovery of the data."); // Intel SSD: Available_Reservd_Space (default) (description?) - add(232, "Available_Reservd_Space", "Available reserved space", "", + add(232, StorageAttribute::DiskSSD, "Available_Reservd_Space", "Available reserved space", "", "Number of reserved blocks remaining. The Normalized value indicates percentage, with 100 meaning new and 10 meaning the drive being close to its end of life."); // Transcend SSD: Firmware_Version_information (non-default) (description?) - add(232, "Firmware_Version_Info", "Firmware Version Information", "", + add(232, StorageAttribute::DiskSSD, "Firmware_Version_Info", "Firmware Version Information", "", "Firmware version information (year, month, day, channels, banks)."); // Same as Firmware_Version_Info, but in older smartctl versions. - add(232, "Firmware_Version_information", "Firmware Version Information", "", + add(232, StorageAttribute::DiskSSD, "Firmware_Version_information", "Firmware Version Information", "", "Firmware version information (year, month, day, channels, banks)."); + // OCZ SSD (description?) (non-default) + add(232, StorageAttribute::DiskSSD, "Lifetime_Writes", "Lifetime_Writes", "", + ""); // Intel SSD: Media_Wearout_Indicator (default) (description?) - add(233, "Media_Wearout_Indicator", "Media Wear Out Indicator", "ssd_life_left", + add(233, StorageAttribute::DiskSSD, "Media_Wearout_Indicator", "Media Wear Out Indicator", "ssd_life_left", "Number of cycles the NAND media has experienced. The Normalized value decreases linearly from 100 to 1 as the average erase cycle " "count increases from 0 to the maximum rated cycles."); + // OCZ SSD + add(233, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime %", "ssd_life_left", + "Remaining drive life in % (usually by erase count)."); // Sandforce SSD: SandForce_Internal (non-default) (description?) - add(233, "SandForce_Internal", "Internal Attribute", "", + add(233, StorageAttribute::DiskSSD, "SandForce_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Transcend SSD: ECC_Fail_Record (non-default) (description?) - add(233, "ECC_Fail_Record", "ECC Failure Record", "", + add(233, StorageAttribute::DiskSSD, "ECC_Fail_Record", "ECC Failure Record", "", "Indicates rate of ECC (error-correcting code) failures."); // Sandforce SSD: SandForce_Internal (non-default) (description?) - add(234, "SandForce_Internal", "Internal Attribute", "", + add(234, StorageAttribute::DiskSSD, "SandForce_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); + // Intel SSD (non-default) + add(234, StorageAttribute::DiskSSD, "Thermal_Throttle", "Thermal Throttle", "", + ""); // Transcend SSD: Erase_Count_Avg (non-default) (description?) - add(234, "Erase_Count_Avg/Max", "Erase Count Average / Maximum", "", + add(234, StorageAttribute::DiskSSD, "Erase_Count_Avg/Max", "Erase Count Average / Maximum", "", ""); // Sandforce SSD: SuperCap_Health (non-default) (description?) - add(235, "SuperCap_Health", "Supercapacitor Health", "", + add(235, StorageAttribute::DiskSSD, "SuperCap_Health", "Supercapacitor Health", "", ""); // Transcend SSD: Block_Count_Good/System (non-default) (description?) - add(235, "Block_Count_Good/System", "Good / System Free Block Count", "", + add(235, StorageAttribute::DiskSSD, "Block_Count_Good/System", "Good / System Free Block Count", "", "Good block count and system free block count."); + // InnoDisk SSD (non-default). (description / name?) + add(235, StorageAttribute::DiskSSD, "Later_Bad_Block", "Later Bad Block", "", + ""); + // InnoDisk SSD (non-default). (description / name?) + add(235, StorageAttribute::DiskSSD, "Unstable_Power_Count", "Unstable Power Count", "", + ""); // Head Flying Hours (default) - add(240, "Head_Flying_Hours", "Head Flying Hours", "", + add(240, StorageAttribute::DiskHDD, "Head_Flying_Hours", "Head Flying Hours", "", "Time spent on head is positioning."); // Fujitsu: Transfer_Error_Rate (non-default) (description?) - add(240, "Transfer_Error_Rate", "Transfer Error Rate", "", + add(240, StorageAttribute::DiskHDD, "Transfer_Error_Rate", "Transfer Error Rate", "", + ""); + // InnoDisk SSD (non-default). (description / name?) + add(235, StorageAttribute::DiskSSD, "Write_Head", "Write Head", "", ""); // Total_LBAs_Written (default) (description?) add(241, "Total_LBAs_Written", "Total LBAs Written", "", "Logical blocks written during lifetime."); // Sandforce SSD: Lifetime_Writes_GiB (non-default) (maybe in 64GiB increments?) - add(241, "Lifetime_Writes_GiB", "Total GiB Written", "", + add(241, StorageAttribute::DiskSSD, "Lifetime_Writes_GiB", "Total GiB Written", "", "Total GiB written during lifetime."); // Intel SSD: Host_Writes_32MiB (non-default) (description?) - add(241, "Host_Writes_32MiB", "Host Writes (32 MiB)", "", + add(241, StorageAttribute::DiskSSD, "Host_Writes_32MiB", "Host Writes (32 MiB)", "", "Total number of sectors written by the host system. The Raw value is increased by 1 for every 32 MiB written by the host."); + // OCZ SSD (non-default) + add(241, StorageAttribute::DiskSSD, "Host_Writes_GiB", "Host Writes (GiB)", "", + "Total number of sectors written by the host system. The Raw value is increased by 1 for every GiB written by the host."); // Total_LBAs_Read (default) (description?) add(242, "Total_LBAs_Read", "Total LBAs Read", "", "Logical blocks read during lifetime."); // Sandforce SSD: Lifetime_Writes_GiB (non-default) (maybe in 64GiB increments?) - add(242, "Lifetime_Reads_GiB", "Total GiB Read", "", + add(242, StorageAttribute::DiskSSD, "Lifetime_Reads_GiB", "Total GiB Read", "", "Total GiB read during lifetime."); // Intel SSD: Host_Reads_32MiB (non-default) (description?) - add(242, "Host_Reads_32MiB", "Host Reads (32 MiB)", "", - "Total number of sectors written by the host system. The Raw value is increased by 1 for every 32 MiB read by the host."); + add(242, StorageAttribute::DiskSSD, "Host_Reads_32MiB", "Host Reads (32 MiB)", "", + "Total number of sectors read by the host system. The Raw value is increased by 1 for every 32 MiB read by the host."); + // OCZ SSD (non-default) + add(242, StorageAttribute::DiskSSD, "Host_Reads_GiB", "Host Reads (GiB)", "", + "Total number of sectors read by the host system. The Raw value is increased by 1 for every GiB read by the host."); // Intel SSD: NAND_Writes_1GiB (non-default) (description?) - add(249, "NAND_Writes_1GiB", "NAND Writes (1GiB)", "", + add(249, StorageAttribute::DiskSSD, "NAND_Writes_1GiB", "NAND Writes (1GiB)", "", + ""); + // OCZ SSD: Total_NAND_Prog_Ct_GiB (non-default) (description?) + add(249, StorageAttribute::DiskSSD, "Total_NAND_Prog_Ct_GiB", "Total NAND Writes (1GiB)", "", ""); // Read Error Retry Rate (default) (description?) add(250, "Read_Error_Retry_Rate", "Read Error Retry Rate", "", "Number of errors found while reading."); + // OCZ SSD (non-default) (description?) + add(251, StorageAttribute::DiskSSD, "Total_NAND_Read_Ct_GiB", "Total NAND Reads (1GiB)", "", + ""); // Free Fall Protection (default) (seagate laptop drives) - add(254, "Free_Fall_Sensor", "Free Fall Protection", "", + add(254, StorageAttribute::DiskHDD, "Free_Fall_Sensor", "Free Fall Protection", "", "Number of free fall events detected by accelerometer sensor."); } @@ -611,7 +747,7 @@ namespace { void add(int32_t id, const std::string& smartctl_name, const std::string& readable_name, const std::string& generic_name, const std::string& description) { - add(AttributeDescription(id, smartctl_name, readable_name, generic_name, description)); + add(AttributeDescription(id, StorageAttribute::DiskAny, smartctl_name, readable_name, generic_name, description)); } @@ -623,7 +759,27 @@ namespace { DBG_ASSERT(iter != id_db.end() && !iter->second.empty()); if (iter != id_db.end() || iter->second.empty()) { AttributeDescription attr = iter->second.front(); - add(AttributeDescription(id, smartctl_name, attr.readable_name, attr.generic_name, attr.description)); + add(AttributeDescription(id, StorageAttribute::DiskAny, smartctl_name, attr.readable_name, attr.generic_name, attr.description)); + } + } + + /// Add an attribute description to the attribute database + void add(int32_t id, StorageAttribute::DiskType type, const std::string& smartctl_name, const std::string& readable_name, + const std::string& generic_name, const std::string& description) + { + add(AttributeDescription(id, type, smartctl_name, readable_name, generic_name, description)); + } + + + /// Add a previously added description to the attribute database under a + /// different smartctl name (fill the other members from the previous attribute). + void add(int32_t id, StorageAttribute::DiskType type, const std::string& smartctl_name) + { + std::map >::iterator iter = id_db.find(id); + DBG_ASSERT(iter != id_db.end() && !iter->second.empty()); + if (iter != id_db.end() || iter->second.empty()) { + AttributeDescription attr = iter->second.front(); + add(AttributeDescription(id, type, smartctl_name, attr.readable_name, attr.generic_name, attr.description)); } } @@ -636,7 +792,7 @@ namespace { /// Find the description by smartctl name or id, merging them if they're partial. - AttributeDescription find(const std::string& smartctl_name, int32_t id) const + AttributeDescription find(const std::string& smartctl_name, int32_t id, StorageAttribute::DiskType type) const { // search by ID first std::map< int32_t, std::vector >::const_iterator id_iter = id_db.find(id); @@ -648,16 +804,26 @@ namespace { return AttributeDescription(); // invalid DB? } - // search by smartctl name in ID-supplied vector + std::vector type_matched; for (std::vector::const_iterator attr_iter = id_iter->second.begin(); attr_iter != id_iter->second.end(); ++attr_iter) { + if (attr_iter->disk_type == type || attr_iter->disk_type == StorageAttribute::DiskAny || type == StorageAttribute::DiskAny) { + type_matched.push_back(*attr_iter); + } + } + if (type_matched.empty()) { + return AttributeDescription(); // not found + } + + // search by smartctl name in ID-supplied vector + for (std::vector::const_iterator attr_iter = type_matched.begin(); attr_iter != type_matched.end(); ++attr_iter) { // compare them case-insensitively, just in case - if (hz::string_to_lower_copy(attr_iter->smartctl_name) == hz::string_to_lower_copy(smartctl_name)) { + if ( hz::string_to_lower_copy(attr_iter->smartctl_name) == hz::string_to_lower_copy(smartctl_name)) { return *attr_iter; // found it } } // nothing was found by name, return the first one by that ID. - return id_iter->second.front(); + return type_matched.front(); } @@ -707,12 +873,13 @@ namespace { /// Find a property's attribute in the attribute database and fill the property /// with all the readable information we can gather. - inline void auto_set_attr(StorageProperty& p) + inline void auto_set_attr(StorageProperty& p, StorageAttribute::DiskType disk_type) { - AttributeDescription attr = s_attribute_db.find(p.reported_name, p.value_attribute.id); + AttributeDescription attr = s_attribute_db.find(p.reported_name, p.value_attribute.id, disk_type); std::string humanized_smartctl_name; - bool known_by_smartctl = !app_pcre_match("/Unknown_Attribute/i", p.reported_name); + std::string ssd_hdd_str; + bool known_by_smartctl = !app_pcre_match("/Unknown_(HDD|SSD)_?Attribute/i", p.reported_name, &ssd_hdd_str); if (known_by_smartctl) { humanized_smartctl_name = " " + p.reported_name + " "; // spaces are for easy replacements @@ -762,8 +929,14 @@ namespace { if (!humanized_smartctl_name.empty()) { attr.readable_name = humanized_smartctl_name; - } else { // unknown by smartctl - attr.readable_name = "Unknown Attribute"; + } else { // unknown to smartctl + if (hz::string_to_upper_copy(ssd_hdd_str) == "SSD") { + attr.readable_name = "Unknown SSD Attribute"; + } else if (hz::string_to_upper_copy(ssd_hdd_str) == "HDD") { + attr.readable_name = "Unknown HDD Attribute"; + } else { + attr.readable_name = "Unknown Attribute"; + } } } @@ -817,7 +990,7 @@ namespace { -bool storage_property_autoset_description(StorageProperty& p) +bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::DiskType disk_type) { bool found = false; @@ -864,7 +1037,7 @@ bool storage_property_autoset_description(StorageProperty& p) } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_attributes) { found = auto_set(p, "data_structure_version", p.readable_name.c_str()); if (!found) { - auto_set_attr(p); + auto_set_attr(p, disk_type); found = true; // true, because auto_set_attr() may set "Unknown attribute", which is still "found". } diff --git a/gsmartcontrol/src/applib/storage_property_descr.h b/gsmartcontrol/src/applib/storage_property_descr.h index f8bf9c9..cd4e14d 100644 --- a/gsmartcontrol/src/applib/storage_property_descr.h +++ b/gsmartcontrol/src/applib/storage_property_descr.h @@ -17,7 +17,7 @@ /// Fill the property with all the information we can gather (description, etc...). -bool storage_property_autoset_description(StorageProperty& p); +bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::DiskType disk_type); /// Do some basic checks on the property and set warnings if needed. diff --git a/gsmartcontrol/src/gsc_info_window.cpp b/gsmartcontrol/src/gsc_info_window.cpp index e1212d3..a253663 100644 --- a/gsmartcontrol/src/gsc_info_window.cpp +++ b/gsmartcontrol/src/gsc_info_window.cpp @@ -1356,7 +1356,11 @@ void GscInfoWindow::on_save_info_button_clicked() std::string file = dialog.get_filename(); hz::File f(file); - if (!f.put_contents(this->drive->get_full_output())) { // this will send to debug_ too. + std::string data = this->drive->get_full_output(); + if (data.empty()) { + data = this->drive->get_info_output(); + } + if (!f.put_contents(data)) { // this will send to debug_ too. gui_show_error_dialog("Cannot save SMART data to file", f.get_error_utf8(), this); } break; diff --git a/gsmartcontrol/src/hz/fs_file.h b/gsmartcontrol/src/hz/fs_file.h index a83b5ed..88bd8cd 100644 --- a/gsmartcontrol/src/hz/fs_file.h +++ b/gsmartcontrol/src/hz/fs_file.h @@ -533,7 +533,7 @@ inline bool File::put_contents(const unsigned char* data, file_size_t data_size) } // write the remainder - if (!write_error && std::fwrite(data + data_size - left_to_write, static_cast(left_to_write), 1, f) != 1) + if (!write_error && left_to_write > 0 && std::fwrite(data + data_size - left_to_write, static_cast(left_to_write), 1, f) != 1) write_error = true; if (write_error) { diff --git a/gsmartcontrol/src/res/gsc_info_window.glade b/gsmartcontrol/src/res/gsc_info_window.glade index 280297c..a410588 100644 --- a/gsmartcontrol/src/res/gsc_info_window.glade +++ b/gsmartcontrol/src/res/gsc_info_window.glade @@ -4,8 +4,8 @@ Device Information - GSmartControl GTK_WIN_POS_CENTER_ON_PARENT - 700 - 500 + 850 + 600 True