From 78a9ede36ca5ad787971b522828a2de0713e001a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20=C5=A0tikonas?= Date: Wed, 27 Dec 2017 16:16:11 +0000 Subject: [PATCH 01/10] Do not install internal backend headers. --- src/backend/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index dc004d0..8f74140 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -7,7 +7,5 @@ set(BACKEND_SRC set(BACKEND_LIB_HDRS backend/corebackend.h - backend/corebackenddevice.h backend/corebackendmanager.h - backend/corebackendpartitiontable.h ) From 4773f49edc6c436459283fe7aedffa00d3e5e957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20=C5=A0tikonas?= Date: Fri, 29 Dec 2017 20:01:06 +0000 Subject: [PATCH 02/10] Initial support for resizing LUKS2 volumes. Does not yet work when LUKS2 is used with dm-integrity. --- src/fs/luks.cpp | 31 ++++++++++++--- src/fs/luks.h | 17 ++++++++- src/fs/luks2.cpp | 67 +++++++++++++++++++++++++++++++++ src/fs/luks2.h | 4 ++ src/jobs/setpartgeometryjob.cpp | 1 - 5 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/fs/luks.cpp b/src/fs/luks.cpp index eb36ad0..3d63f05 100644 --- a/src/fs/luks.cpp +++ b/src/fs/luks.cpp @@ -143,6 +143,7 @@ bool luks::create(Report& report, const QString& deviceNode) if (!( openCmd.start(-1) && openCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && openCmd.waitFor())) return false; + setPayloadSize(); scan(deviceNode); if (mapperName().isEmpty()) @@ -259,6 +260,7 @@ bool luks::cryptOpen(QWidget* parent, const QString& deviceNode) QString passphrase = dlg.password(); ExternalCommand openCmd(QStringLiteral("cryptsetup"), { QStringLiteral("open"), + QStringLiteral("--tries"), QStringLiteral("1"), deviceNode, suggestedMapperName(deviceNode) }); @@ -490,19 +492,18 @@ bool luks::resize(Report& report, const QString& deviceNode, qint64 newLength) c if (mapperName().isEmpty()) return false; - qint64 payloadLength = newLength - payloadOffset(); if ( newLength - length() * sectorSize() > 0 ) { ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("resize"), mapperName() }); report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition %1.", deviceNode); if (cryptResizeCmd.run(-1) && cryptResizeCmd.exitCode() == 0) - return m_innerFs->resize(report, mapperName(), payloadLength); + return m_innerFs->resize(report, mapperName(), m_PayloadSize); } - else if (m_innerFs->resize(report, mapperName(), payloadLength)) + else if (m_innerFs->resize(report, mapperName(), m_PayloadSize)) { ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), - { QStringLiteral("--size"), QString::number(payloadLength / 512), // LUKS payload length is specified in multiples of 512 bytes + { QStringLiteral("--size"), QString::number(m_PayloadSize / 512), // LUKS1 payload length is specified in multiples of 512 bytes QStringLiteral("resize"), mapperName() }); report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition %1.", deviceNode); if (cryptResizeCmd.run(-1) && cryptResizeCmd.exitCode() == 0) @@ -589,8 +590,7 @@ void luks::getMapperName(const QString& deviceNode) void luks::getLuksInfo(const QString& deviceNode) { - ExternalCommand cmd(QStringLiteral("cryptsetup"), - { QStringLiteral("luksDump"), deviceNode }); + ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("luksDump"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Cipher name:\\s+(\\w+)")); QRegularExpressionMatch rem = re.match(cmd.output()); @@ -667,6 +667,7 @@ bool luks::canEncryptType(FileSystem::Type type) void luks::initLUKS() { + setPayloadSize(); QString mapperNode = mapperName(); bool isCryptOpen = !mapperNode.isEmpty(); setCryptOpen(isCryptOpen); @@ -676,4 +677,22 @@ void luks::initLUKS() } } +void luks::setPayloadSize() +{ + ExternalCommand dmsetupCmd(QStringLiteral("dmsetup"), { QStringLiteral("table"), mapperName() }); + dmsetupCmd.run(); + QRegularExpression re(QStringLiteral("\\d+ (\\d+)")); + QRegularExpressionMatch rem = re.match(dmsetupCmd.output()); + if (rem.hasMatch()) + m_PayloadSize = rem.captured(1).toLongLong() * sectorSize(); +} + +bool luks::testPassphrase(const QString& deviceNode, const QString& passphrase) const { + ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("open"), QStringLiteral("--tries"), QStringLiteral("1"), QStringLiteral("--test-passphrase"), deviceNode }); + if (cmd.start(-1) && cmd.write(passphrase.toLocal8Bit() + '\n') == passphrase.toLocal8Bit().length() + 1 && cmd.waitFor() && cmd.exitCode() == 0) + return true; + + return false; +} + } diff --git a/src/fs/luks.h b/src/fs/luks.h index e7221c8..9a6ea4e 100644 --- a/src/fs/luks.h +++ b/src/fs/luks.h @@ -43,6 +43,12 @@ public: luks(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Luks); ~luks() override; + enum KeyLocation { + unknown, + dmcrypt, + keyring + }; + public: void init() override; void scan(const QString& deviceNode) override; @@ -167,9 +173,9 @@ public: QString suggestedMapperName(const QString& deviceNode) const; void getMapperName(const QString& deviceNode); - void getLuksInfo(const QString& deviceNode); + virtual void getLuksInfo(const QString& deviceNode); - FileSystem* innerFS() const { return m_innerFs; } // avoid calling this unless necessary + FileSystem* innerFS() const { return m_innerFs; } QString outerUuid() const; QString mapperName() const { return m_MapperName; } @@ -182,8 +188,12 @@ public: static bool canEncryptType(FileSystem::Type type); void initLUKS(); + virtual luks::KeyLocation keyLocation() { return luks::dmcrypt; }; + bool testPassphrase(const QString& deviceNode, const QString& passphrase) const; + protected: virtual QString readOuterUUID(const QString& deviceNode) const; + void setPayloadSize(); public: static CommandSupportType m_GetUsed; @@ -213,7 +223,10 @@ protected: QString m_HashName; qint64 m_KeySize; qint64 m_PayloadOffset; + qint64 m_PayloadSize; QString m_outerUuid; + + luks::KeyLocation m_KeyLocation = unknown; }; } diff --git a/src/fs/luks2.cpp b/src/fs/luks2.cpp index a895517..9301f17 100644 --- a/src/fs/luks2.cpp +++ b/src/fs/luks2.cpp @@ -17,6 +17,13 @@ #include "fs/luks2.h" +#include "util/externalcommand.h" +#include "util/report.h" + +#include + +#include + namespace FS { @@ -36,4 +43,64 @@ FileSystem::Type luks2::type() const return FileSystem::Luks2; } +bool luks2::resize(Report& report, const QString& deviceNode, qint64 newLength) const +{ + Q_ASSERT(m_innerFs); + + if (mapperName().isEmpty()) + return false; + + if ( newLength - length() * sectorSize() > 0 ) + { + ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("resize"), mapperName() }); + report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition %1.", deviceNode); + + cryptResizeCmd.start(-1); + if (m_KeyLocation == keyring) { + if (m_passphrase.isEmpty()) + return false; + cryptResizeCmd.write(m_passphrase.toLocal8Bit() + '\n'); + } + cryptResizeCmd.waitFor(); + if ( cryptResizeCmd.exitCode() == 0 ) + return m_innerFs->resize(report, mapperName(), m_PayloadSize); + } + else if (m_innerFs->resize(report, mapperName(), m_PayloadSize)) + { + ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), + { QStringLiteral("--size"), QString::number(m_PayloadSize / 512), // FIXME, LUKS2 can have different sector sizes + QStringLiteral("resize"), mapperName() }); + report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition %1.", deviceNode); + cryptResizeCmd.start(-1); + if (m_KeyLocation == keyring) { + if (m_passphrase.isEmpty()) + return false; + cryptResizeCmd.write(m_passphrase.toLocal8Bit() + '\n'); + } + cryptResizeCmd.waitFor(); + if ( cryptResizeCmd.exitCode() == 0 ) + return true; + } + report.line() << xi18nc("@info:progress", "Resizing encrypted file system on partition %1 failed.", deviceNode); + return false; +} + +luks::KeyLocation luks2::keyLocation() +{ + m_KeyLocation = unknown; + ExternalCommand statusCmd(QStringLiteral("cryptsetup"), { QStringLiteral("status"), mapperName() }); + if (statusCmd.run(-1) && statusCmd.exitCode() == 0) { + QRegularExpression re(QStringLiteral("key location:\\s+(\\w+)")); + QRegularExpressionMatch rem = re.match(statusCmd.output()); + if (rem.hasMatch()) { + if (rem.captured(1) == QStringLiteral("keyring")) + m_KeyLocation = keyring; + else if (rem.captured(1) == QStringLiteral("dm-crypt")) + m_KeyLocation = dmcrypt; + } + } + + return m_KeyLocation; +} + } diff --git a/src/fs/luks2.h b/src/fs/luks2.h index f8151cb..5f0e12e 100644 --- a/src/fs/luks2.h +++ b/src/fs/luks2.h @@ -38,7 +38,11 @@ public: luks2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); ~luks2() override; + bool resize(Report& report, const QString& deviceNode, qint64 length) const override; + FileSystem::Type type() const override; + + luks::KeyLocation keyLocation() override; }; } diff --git a/src/jobs/setpartgeometryjob.cpp b/src/jobs/setpartgeometryjob.cpp index 596e14a..7a4fbd7 100644 --- a/src/jobs/setpartgeometryjob.cpp +++ b/src/jobs/setpartgeometryjob.cpp @@ -73,7 +73,6 @@ bool SetPartGeometryJob::run(Report& parent) delete backendPartitionTable; } - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Could not open device %1 while trying to resize/move partition %2.", device().deviceNode(), partition().deviceNode()); From 1802b7ad05d22b24b74c9b14abcdbf421d00140f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20=C5=A0tikonas?= Date: Fri, 29 Dec 2017 20:38:36 +0000 Subject: [PATCH 03/10] Add luks2::create method. --- src/fs/luks.cpp | 1 + src/fs/luks2.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/fs/luks2.h | 1 + 3 files changed, 42 insertions(+) diff --git a/src/fs/luks.cpp b/src/fs/luks.cpp index 3d63f05..12ace19 100644 --- a/src/fs/luks.cpp +++ b/src/fs/luks.cpp @@ -126,6 +126,7 @@ bool luks::create(Report& report, const QString& deviceNode) QStringLiteral("512"), QStringLiteral("--batch-mode"), QStringLiteral("--force-password"), + QStringLiteral("--type"), QStringLiteral("luks1"), QStringLiteral("luksFormat"), deviceNode }); if (!( createCmd.start(-1) && diff --git a/src/fs/luks2.cpp b/src/fs/luks2.cpp index 9301f17..72e5554 100644 --- a/src/fs/luks2.cpp +++ b/src/fs/luks2.cpp @@ -43,6 +43,46 @@ FileSystem::Type luks2::type() const return FileSystem::Luks2; } +bool luks2::create(Report& report, const QString& deviceNode) +{ + Q_ASSERT(m_innerFs); + Q_ASSERT(!m_passphrase.isEmpty()); + + ExternalCommand createCmd(report, QStringLiteral("cryptsetup"), + { QStringLiteral("-s"), + QStringLiteral("512"), + QStringLiteral("--batch-mode"), + QStringLiteral("--force-password"), + QStringLiteral("--type"), QStringLiteral("luks2"), + QStringLiteral("luksFormat"), + deviceNode }); + if (!( createCmd.start(-1) && + createCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && + createCmd.waitFor() && createCmd.exitCode() == 0)) + { + return false; + } + + ExternalCommand openCmd(report, QStringLiteral("cryptsetup"), + { QStringLiteral("open"), + deviceNode, + suggestedMapperName(deviceNode) }); + + if (!( openCmd.start(-1) && openCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && openCmd.waitFor())) + return false; + + setPayloadSize(); + scan(deviceNode); + + if (mapperName().isEmpty()) + return false; + + if (!m_innerFs->create(report, mapperName())) + return false; + + return true; +} + bool luks2::resize(Report& report, const QString& deviceNode, qint64 newLength) const { Q_ASSERT(m_innerFs); diff --git a/src/fs/luks2.h b/src/fs/luks2.h index 5f0e12e..9b7ffc1 100644 --- a/src/fs/luks2.h +++ b/src/fs/luks2.h @@ -38,6 +38,7 @@ public: luks2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); ~luks2() override; + bool create(Report& report, const QString& deviceNode) override; bool resize(Report& report, const QString& deviceNode, qint64 length) const override; FileSystem::Type type() const override; From 8b9451c8d7efecaa69b8ed1cdba924840057b9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20=C5=A0tikonas?= Date: Fri, 29 Dec 2017 22:53:02 +0000 Subject: [PATCH 04/10] We don't need keyLocation for luks1 file system it is always dm-crypt. --- src/fs/luks.h | 1 - src/fs/luks2.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fs/luks.h b/src/fs/luks.h index 9a6ea4e..9ee8ea4 100644 --- a/src/fs/luks.h +++ b/src/fs/luks.h @@ -188,7 +188,6 @@ public: static bool canEncryptType(FileSystem::Type type); void initLUKS(); - virtual luks::KeyLocation keyLocation() { return luks::dmcrypt; }; bool testPassphrase(const QString& deviceNode, const QString& passphrase) const; protected: diff --git a/src/fs/luks2.h b/src/fs/luks2.h index 9b7ffc1..cb014f8 100644 --- a/src/fs/luks2.h +++ b/src/fs/luks2.h @@ -43,7 +43,7 @@ public: FileSystem::Type type() const override; - luks::KeyLocation keyLocation() override; + luks::KeyLocation keyLocation(); }; } From 2f4aeaf85120fe3a8dcf379bb615e4f976da24dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 3 Jan 2018 20:09:45 +0200 Subject: [PATCH 05/10] Add support for reading capacity and changing label/uuid on UDF filesystem --- src/fs/udf.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- src/fs/udf.h | 15 +++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/fs/udf.cpp b/src/fs/udf.cpp index a33ae44..f34c5f3 100644 --- a/src/fs/udf.cpp +++ b/src/fs/udf.cpp @@ -30,9 +30,12 @@ namespace FS { -constexpr qint64 MIN_UDF_BLOCKS = 282; +constexpr qint64 MIN_UDF_BLOCKS = 300; constexpr qint64 MAX_UDF_BLOCKS = ((1ULL << 32) - 1); +FileSystem::CommandSupportType udf::m_GetUsed = FileSystem::cmdSupportNone; +FileSystem::CommandSupportType udf::m_SetLabel = FileSystem::cmdSupportNone; +FileSystem::CommandSupportType udf::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType udf::m_Create = FileSystem::cmdSupportNone; bool udf::oldMkudffsVersion = false; @@ -43,6 +46,8 @@ udf::udf(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QStrin void udf::init() { + m_GetUsed = findExternal(QStringLiteral("udfinfo"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone; + m_SetLabel = m_UpdateUUID = findExternal(QStringLiteral("udflabel"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone; m_Create = findExternal(QStringLiteral("mkudffs"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone; if (m_Create == cmdSupportFileSystem) { @@ -54,7 +59,11 @@ void udf::init() bool udf::supportToolFound() const { - return m_Create != cmdSupportNone; + return + m_GetUsed != cmdSupportNone && + m_SetLabel != cmdSupportNone && + m_UpdateUUID != cmdSupportNone && + m_Create != cmdSupportNone; } FileSystem::SupportTool udf::supportToolName() const @@ -93,6 +102,36 @@ QValidator* udf::labelValidator(QObject *parent) const return m_LabelValidator; } +qint64 udf::readUsedCapacity(const QString& deviceNode) const +{ + ExternalCommand cmd(QStringLiteral("udfinfo"), { QStringLiteral("--utf8"), deviceNode }); + if (!cmd.run(-1) || cmd.exitCode() != 0) + return -1; + + QRegularExpressionMatch reBlockSize = QRegularExpression(QStringLiteral("^blocksize=([0-9]+)$"), QRegularExpression::MultilineOption).match(cmd.output()); + QRegularExpressionMatch reUsedBlocks = QRegularExpression(QStringLiteral("^usedblocks=([0-9]+)$"), QRegularExpression::MultilineOption).match(cmd.output()); + + if (!reBlockSize.hasMatch() || !reUsedBlocks.hasMatch()) + return -1; + + qint64 blockSize = reBlockSize.captured(1).toLongLong(); + qint64 usedBlocks = reUsedBlocks.captured(1).toLongLong(); + + return usedBlocks * blockSize; +} + +bool udf::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) +{ + ExternalCommand cmd(report, QStringLiteral("udflabel"), { QStringLiteral("--utf8"), deviceNode, newLabel }); + return cmd.run(-1) && cmd.exitCode() == 0; +} + +bool udf::updateUUID(Report& report, const QString& deviceNode) const +{ + ExternalCommand cmd(report, QStringLiteral("udflabel"), { QStringLiteral("--utf8"), QStringLiteral("--uuid=random"), deviceNode }); + return cmd.run(-1) && cmd.exitCode() == 0; +} + bool udf::create(Report& report, const QString& deviceNode) { return createWithLabel(report, deviceNode, QString()); diff --git a/src/fs/udf.h b/src/fs/udf.h index bc2a5c9..f49df36 100644 --- a/src/fs/udf.h +++ b/src/fs/udf.h @@ -42,9 +42,15 @@ public: public: void init() override; + qint64 readUsedCapacity(const QString& deviceNode) const override; bool create(Report& report, const QString& deviceNode) override; bool createWithLabel(Report& report, const QString& deviceNode, const QString& label) override; + bool writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) override; + bool updateUUID(Report& report, const QString& deviceNode) const override; + CommandSupportType supportGetUsed() const override { + return m_GetUsed; + } CommandSupportType supportGetLabel() const override { return cmdSupportCore; } @@ -63,6 +69,12 @@ public: CommandSupportType supportBackup() const override { return cmdSupportCore; } + CommandSupportType supportSetLabel() const override { + return m_SetLabel; + } + CommandSupportType supportUpdateUUID() const override { + return m_UpdateUUID; + } CommandSupportType supportGetUUID() const override { return cmdSupportCore; } @@ -75,6 +87,9 @@ public: bool supportToolFound() const override; public: + static CommandSupportType m_GetUsed; + static CommandSupportType m_SetLabel; + static CommandSupportType m_UpdateUUID; static CommandSupportType m_Create; private: From ba3676e1651c7f17bf157eead8c60feec6e36a24 Mon Sep 17 00:00:00 2001 From: l10n daemon script Date: Thu, 4 Jan 2018 04:49:48 +0100 Subject: [PATCH 06/10] SVN_SILENT made messages (.desktop file) - always resolve ours In case of conflict in i18n, keep the version of the branch "ours" To resolve a particular conflict, "git checkout --ours path/to/file.desktop" --- src/plugins/sfdisk/pmsfdiskbackendplugin.desktop | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop b/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop index cc325d0..1466c74 100644 --- a/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop +++ b/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop @@ -1,7 +1,21 @@ [Desktop Entry] Encoding=UTF-8 Name=KDE Partition Manager sfdisk Backend +Name[ca]=Dorsal «sfdisk» del gestor de particions del KDE +Name[de]=KDE-Partitionsverwaltung sfdisk-Backend +Name[nl]=Sfdisk-backend van KDE-partitiebeheerder +Name[pt]=Infra-Estrutura do 'sfdisk' do Gestor de Partições do KDE +Name[sv]=KDE:s partitionshanterare sfdisk bakgrundsprogram +Name[uk]=Додаток sfdisk сервера Керування розділами KDE +Name[x-test]=xxKDE Partition Manager sfdisk Backendxx Comment=A KDE Partition Manager sfdisk backend. +Comment[ca]=Un dorsal «sfdisk» del gestor de particions del KDE. +Comment[de]=Ein sfdisk-Backend für die KDE-Partitionsverwaltung. +Comment[nl]=Sfdisk-backend van KDE-partitiebeheerder +Comment[pt]=A infra-estrutura do 'sfdisk' do Gestor de Partições do KDE. +Comment[sv]=KDE:s partitionshanterare sfdisk bakgrundsprogram +Comment[uk]=Додаток sfdisk сервера Керування розділами KDE. +Comment[x-test]=xxA KDE Partition Manager sfdisk backend.xx Type=Service ServiceTypes=PartitionManager/Plugin Icon=preferences-plugin From 537680815934204ce888d7d102483a7c20dcc3c1 Mon Sep 17 00:00:00 2001 From: l10n daemon script Date: Sun, 7 Jan 2018 04:56:23 +0100 Subject: [PATCH 07/10] SVN_SILENT made messages (.desktop file) - always resolve ours In case of conflict in i18n, keep the version of the branch "ours" To resolve a particular conflict, "git checkout --ours path/to/file.desktop" --- src/plugins/sfdisk/pmsfdiskbackendplugin.desktop | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop b/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop index 1466c74..18d85e7 100644 --- a/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop +++ b/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop @@ -2,17 +2,31 @@ Encoding=UTF-8 Name=KDE Partition Manager sfdisk Backend Name[ca]=Dorsal «sfdisk» del gestor de particions del KDE +Name[ca@valencia]=Dorsal «sfdisk» del gestor de particions del KDE Name[de]=KDE-Partitionsverwaltung sfdisk-Backend +Name[gl]=Infraestrutura de sfdisk do xestor de particións de KDE Name[nl]=Sfdisk-backend van KDE-partitiebeheerder +Name[pl]=Silnik sfdisk zarządzania partycjami KDE Name[pt]=Infra-Estrutura do 'sfdisk' do Gestor de Partições do KDE +Name[sr]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција +Name[sr@ijekavian]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција +Name[sr@ijekavianlatin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija +Name[sr@latin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija Name[sv]=KDE:s partitionshanterare sfdisk bakgrundsprogram Name[uk]=Додаток sfdisk сервера Керування розділами KDE Name[x-test]=xxKDE Partition Manager sfdisk Backendxx Comment=A KDE Partition Manager sfdisk backend. Comment[ca]=Un dorsal «sfdisk» del gestor de particions del KDE. +Comment[ca@valencia]=Un dorsal «sfdisk» del gestor de particions del KDE. Comment[de]=Ein sfdisk-Backend für die KDE-Partitionsverwaltung. +Comment[gl]=Unha infraestrutura de sfdisk do xestor de particións de KDE. Comment[nl]=Sfdisk-backend van KDE-partitiebeheerder +Comment[pl]=Silnik sfdisk zarządzania partycjami KDE. Comment[pt]=A infra-estrutura do 'sfdisk' do Gestor de Partições do KDE. +Comment[sr]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција +Comment[sr@ijekavian]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција +Comment[sr@ijekavianlatin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija +Comment[sr@latin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija Comment[sv]=KDE:s partitionshanterare sfdisk bakgrundsprogram Comment[uk]=Додаток sfdisk сервера Керування розділами KDE. Comment[x-test]=xxA KDE Partition Manager sfdisk backend.xx From 8177fb9738b985a3acdbcc574a751a763b2a8906 Mon Sep 17 00:00:00 2001 From: Caio Carvalho Date: Mon, 8 Jan 2018 17:59:48 -0300 Subject: [PATCH 08/10] Adding smart JSON output support --- src/core/CMakeLists.txt | 6 + src/core/smartattribute.cpp | 64 ++- src/core/smartattribute.h | 4 +- src/core/smartattributeparseddata.cpp | 653 ++++++++++++++++++++++++++ src/core/smartattributeparseddata.h | 207 ++++++++ src/core/smartdiskinformation.cpp | 194 ++++++++ src/core/smartdiskinformation.h | 192 ++++++++ src/core/smartparser.cpp | 159 +++++++ src/core/smartparser.h | 57 +++ src/core/smartstatus.cpp | 236 ++++------ src/core/smartstatus.h | 78 +-- 11 files changed, 1654 insertions(+), 196 deletions(-) create mode 100644 src/core/smartattributeparseddata.cpp create mode 100644 src/core/smartattributeparseddata.h create mode 100644 src/core/smartdiskinformation.cpp create mode 100644 src/core/smartdiskinformation.h create mode 100644 src/core/smartparser.cpp create mode 100644 src/core/smartparser.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index bb5fb6b..584c8b4 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -20,6 +20,9 @@ set(CORE_SRC core/partitiontable.cpp core/smartstatus.cpp core/smartattribute.cpp + core/smartparser.cpp + core/smartattributeparseddata.cpp + core/smartdiskinformation.cpp core/volumemanagerdevice.cpp ) @@ -38,6 +41,9 @@ set(CORE_LIB_HDRS core/partitiontable.h core/smartattribute.h core/smartstatus.h + core/smartparser.h + core/smartattributeparseddata.h + core/smartdiskinformation.h core/volumemanagerdevice.h ) diff --git a/src/core/smartattribute.cpp b/src/core/smartattribute.cpp index 0346bcb..50593d1 100644 --- a/src/core/smartattribute.cpp +++ b/src/core/smartattribute.cpp @@ -18,33 +18,33 @@ #include "core/smartattribute.h" #include "core/smartstatus.h" +#include "core/smartattributeparseddata.h" #include #include #include -#include - static QString getAttrName(qint32 id); static QString getAttrDescription(qint32 id); static QString getPrettyValue(quint64 value, qint64 unit); -static SmartAttribute::Assessment getAssessment(const SkSmartAttributeParsedData* a); -static QString getRaw(const uint8_t*); +static SmartAttribute::Assessment getAssessment(const SmartAttributeParsedData& a); +static QString getRaw(quint64 raw); -SmartAttribute::SmartAttribute(const SkSmartAttributeParsedData* a) : - m_Id(a->id), - m_Name(getAttrName(a->id)), - m_Desc(getAttrDescription(a->id)), - m_FailureType(a->prefailure ? PreFailure : OldAge), - m_UpdateType(a->online ? Online : Offline), - m_Current(a->current_value_valid ? a->current_value : -1), - m_Worst(a->worst_value_valid ? a->worst_value : -1), - m_Threshold(a->threshold_valid ? a->threshold : -1), - m_Raw(getRaw(a->raw)), +SmartAttribute::SmartAttribute(const SmartAttributeParsedData& a) : + m_Id(a.id()), + m_Name(getAttrName(a.id())), + m_Desc(getAttrDescription(a.id())), + m_FailureType(a.prefailure() ? PreFailure : OldAge), + m_UpdateType(a.online() ? Online : Offline), + m_Current(a.currentValueValid() ? a.currentValue() : -1), + m_Worst(a.worstValueValid() ? a.worstValue() : -1), + m_Threshold(a.thresholdValid() ? a.threshold() : -1), + m_Raw(getRaw(a.raw())), m_Assessment(getAssessment(a)), - m_Value(getPrettyValue(a->pretty_value, a->pretty_unit)) + m_Value(getPrettyValue(a.prettyValue(), a.prettyUnit())) { + } QString SmartAttribute::assessmentToString(Assessment a) @@ -73,23 +73,23 @@ static QString getPrettyValue(quint64 value, qint64 unit) QString rval; switch (unit) { - case SK_SMART_ATTRIBUTE_UNIT_MSECONDS: + case SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS: rval = KFormat().formatDuration(value); break; - case SK_SMART_ATTRIBUTE_UNIT_SECTORS: + case SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_SECTORS: rval = xi18ncp("@item:intable", "%1 sector", "%1 sectors", value); break; - case SK_SMART_ATTRIBUTE_UNIT_MKELVIN: + case SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MKELVIN: rval = SmartStatus::tempToString(value); break; - case SK_SMART_ATTRIBUTE_UNIT_NONE: + case SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE: rval = QLocale().toString(value); break; - case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN: + case SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN: default: rval = xi18nc("@item:intable not applicable", "N/A"); break; @@ -214,23 +214,23 @@ static QString getAttrDescription(qint32 id) return QString(); } -static SmartAttribute::Assessment getAssessment(const SkSmartAttributeParsedData* a) +static SmartAttribute::Assessment getAssessment(const SmartAttributeParsedData& a) { SmartAttribute::Assessment rval = SmartAttribute::NotApplicable; bool failed = false; bool hasFailed = false; - if (a->prefailure) { - if (a->good_now_valid && !a->good_now) + if (a.prefailure()) { + if (a.goodNowValid() && !a.goodNow()) failed = true; - if (a->good_in_the_past_valid && !a->good_in_the_past) + if (a.goodInThePastValid() && !a.goodInThePast()) hasFailed = true; - } else if (a->threshold_valid) { - if (a->current_value_valid && a->current_value <= a->threshold) + } else if (a.thresholdValid()) { + if (a.currentValueValid() && a.currentValue() <= a.threshold()) failed = true; - else if (a->worst_value_valid && a->worst_value <= a->threshold) + else if (a.worstValueValid() && a.worstValue() <= a.threshold()) hasFailed = true; } @@ -238,19 +238,17 @@ static SmartAttribute::Assessment getAssessment(const SkSmartAttributeParsedData rval = SmartAttribute::Failing; else if (hasFailed) rval = SmartAttribute::HasFailed; - else if (a->warn) + else if (a.warn()) rval = SmartAttribute::Warning; - else if (a->good_now_valid) + else if (a.goodNowValid()) rval = SmartAttribute::Good; return rval; } -static QString getRaw(const uint8_t* raw) +static QString getRaw(quint64 raw) { QString rval = QStringLiteral("0x"); - for (qint32 i = 5; i >= 0; i--) - rval += QStringLiteral("%1").arg(raw[i], 2, 16, QLatin1Char('0')); - + rval += QStringLiteral("%1").arg(raw, 12, 16, QLatin1Char('0')); return rval; } diff --git a/src/core/smartattribute.h b/src/core/smartattribute.h index 226e6eb..d0d2e2a 100644 --- a/src/core/smartattribute.h +++ b/src/core/smartattribute.h @@ -20,10 +20,10 @@ #define KPMCORE_SMARTATTRIBUTE_H #include "util/libpartitionmanagerexport.h" +#include "core/smartattributeparseddata.h" #include -struct SkSmartAttributeParsedData; class LIBKPMCORE_EXPORT SmartAttribute { @@ -47,7 +47,7 @@ public: }; public: - SmartAttribute(const SkSmartAttributeParsedData* a); + SmartAttribute(const SmartAttributeParsedData& a); public: qint32 id() const { diff --git a/src/core/smartattributeparseddata.cpp b/src/core/smartattributeparseddata.cpp new file mode 100644 index 0000000..8960c92 --- /dev/null +++ b/src/core/smartattributeparseddata.cpp @@ -0,0 +1,653 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#include "smartattributeparseddata.h" +#include "core/smartdiskinformation.h" + +#include +#include +#include + +#define MKELVIN_VALID_MIN ((qint64) ((-15LL*1000LL) + 273150LL)) +#define MKELVIN_VALID_MAX ((qint64) ((100LL*1000LL) + 273150LL)) + +#define MSECOND_VALID_MIN 1ULL +#define MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL) +#define MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL) + +static const QMap tableUnit(); +static SmartAttributeParsedData::SmartQuirk *getQuirk(QString model, QString firmware); + +SmartAttributeParsedData::SmartAttributeParsedData(SmartDiskInformation *disk, + QJsonObject jsonAttribute) : + m_Id(0), + m_CurrentValue(0), + m_WorstValue(0), + m_Threshold(0), + m_Raw(0), + m_PrettyValue(0), + m_CurrentValueValid(false), + m_WorstValueValid(false), + m_ThresholdValid(false), + m_Prefailure(false), + m_Online(false), + m_GoodNow(true), + m_GoodNowValid(false), + m_GoodInThePast(true), + m_GoodInThePastValid(false), + m_Warn(false), + m_PrettyUnit(SMART_ATTRIBUTE_UNIT_UNKNOWN), + m_Disk(disk), + m_Quirk(nullptr) +{ + if (disk) + m_Quirk = getQuirk(disk->model(), disk->firmware()); + + if (!jsonAttribute.isEmpty()) { + QString id = QString::fromLocal8Bit("id"); + QString value = QString::fromLocal8Bit("value"); + QString worst = QString::fromLocal8Bit("worst"); + QString thresh = QString::fromLocal8Bit("thresh"); + QString raw = QString::fromLocal8Bit("raw"); + QString str = QString::fromLocal8Bit("string"); + QString flags = QString::fromLocal8Bit("flags"); + QString prefailure = QString::fromLocal8Bit("prefailure"); + QString online = QString::fromLocal8Bit("updated_online"); + + m_Id = jsonAttribute[id].toInt(); + m_CurrentValue = jsonAttribute[value].toInt(); + m_WorstValue = jsonAttribute[worst].toInt(); + m_Threshold = jsonAttribute[thresh].toInt(); + + QJsonObject rawObj = jsonAttribute[raw].toObject(); + + m_Raw = rawObj[str].toString().toULongLong(); + + QJsonObject flagsObj = jsonAttribute[flags].toObject(); + + m_Prefailure = flagsObj[prefailure].toBool(); + m_Online = flagsObj[online].toBool(); + + if (!updateUnit()) + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; + + makePretty(); + + validateValues(); + + verifyAttribute(); + } +} + +SmartAttributeParsedData::SmartAttributeParsedData(const SmartAttributeParsedData &other) : + m_Id(other.id()), + m_CurrentValue(other.currentValue()), + m_WorstValue(other.worstValue()), + m_Threshold(other.threshold()), + m_Raw(other.raw()), + m_PrettyValue(other.prettyValue()), + m_CurrentValueValid(other.currentValueValid()), + m_WorstValueValid(other.worstValueValid()), + m_ThresholdValid(other.thresholdValid()), + m_Prefailure(other.prefailure()), + m_Online(other.online()), + m_GoodNow(other.goodNow()), + m_GoodNowValid(other.goodNowValid()), + m_GoodInThePast(other.goodInThePast()), + m_GoodInThePastValid(other.goodInThePastValid()), + m_Warn(other.warn()), + m_PrettyUnit(other.prettyUnit()), + m_Disk(other.disk()), + m_Quirk(other.m_Quirk) +{ + +} + +void SmartAttributeParsedData::validateValues() +{ + m_CurrentValueValid = m_CurrentValue >= 1 && m_CurrentValue <= 0xFD; + m_WorstValueValid = m_WorstValue >= 1 && m_WorstValue <= 0xFD; + m_ThresholdValid = m_Threshold != 0xFE; + + if (m_Threshold >= 1 && m_Threshold <= 0xFD) { + if (m_WorstValueValid) { + m_GoodInThePast = m_GoodInThePast && (m_WorstValue > m_Threshold); + m_GoodInThePastValid = true; + } + if (m_CurrentValueValid) { + m_GoodNow = m_GoodNow && (m_CurrentValue > m_Threshold); + m_GoodNowValid = true; + } + } + + m_Warn = (m_GoodNowValid && !m_GoodNow) || (m_GoodInThePastValid && !m_GoodInThePast); +} + +void SmartAttributeParsedData::makePretty() +{ + if (m_PrettyUnit == SMART_ATTRIBUTE_UNIT_UNKNOWN) + return; + + switch (id()) { + case 3: + m_PrettyValue = raw() & 0xFFFF; + break; + + case 5: + m_PrettyValue = raw() & 0xFFFFFFFFU; + break; + + case 9: + m_PrettyValue = (raw() & 0xFFFFFFFFU) * 60 * 60 * 1000; + break; + + case 170: + m_PrettyValue = currentValue(); + break; + + case 190: + m_PrettyValue = (raw() & 0xFFFF) * 1000 + 273150; + break; + + case 194: + m_PrettyValue = (raw() & 0xFFFF) * 1000 + 273150; + break; + + case 197: + m_PrettyValue = (raw() & 0xFFFFFFFFU); + break; + + case 222: + m_PrettyValue = (raw() & 0xFFFFFFFFU) * 60 * 60 * 1000; + break; + + case 228: + m_PrettyValue = raw() * 60 * 1000; + break; + + case 231: + m_PrettyValue = (raw() & 0xFFFF) * 1000 + 273150; + break; + + case 232: + m_PrettyValue = currentValue(); + break; + + case 240: + m_PrettyValue = (raw() & 0xFFFFFFFFU) * 60 * 60 * 1000; + break; + + case 241: + m_PrettyValue = raw() * 65536ULL * 512ULL / 1000000ULL; + break; + + case 242: + m_PrettyValue = raw() * 65536ULL * 512ULL / 1000000ULL; + break; + + default: + m_PrettyValue = raw(); + break; + + } +} + +void SmartAttributeParsedData::verifyAttribute() +{ + if (id() == 3 || id() == 226) + verifyShortTime(); + else if (id() == 5 || id() == 187 || id() == 197 || id() == 198) + verifySectors(); + else if (id() == 9 || id() == 222 || id() == 240) + verifyLongTime(); + else if (id() == 190 || id() == 194 || id() == 231) + verifyTemperature(); +} + +void SmartAttributeParsedData::verifyTemperature() +{ + if (prettyUnit() != SMART_ATTRIBUTE_UNIT_MKELVIN) + return; + + if (prettyValue() < MKELVIN_VALID_MIN || prettyValue() > MKELVIN_VALID_MAX) + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; +} + +void SmartAttributeParsedData::verifyShortTime() +{ + if (prettyUnit() != SMART_ATTRIBUTE_UNIT_MSECONDS) + return; + + if (prettyValue() < MSECOND_VALID_MIN || prettyValue() > MSECOND_VALID_SHORT_MAX) + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; +} + +void SmartAttributeParsedData::verifyLongTime() +{ + if (prettyUnit() != SMART_ATTRIBUTE_UNIT_MSECONDS) + return; + + if (prettyValue() < MSECOND_VALID_MIN || prettyValue() > MSECOND_VALID_LONG_MAX) + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; +} + +void SmartAttributeParsedData::verifySectors() +{ + if (prettyUnit() != SMART_ATTRIBUTE_UNIT_SECTORS) + return; + + quint64 maxSectors = disk()->size() / 512ULL; + + if (prettyValue() == 0xFFFFFFFFULL || prettyValue() == 0xFFFFFFFFFFFFULL || (maxSectors > 0 + && prettyValue() > maxSectors)) + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; + else if ((id() == 5 || id() == 197) && prettyValue() > 0) + m_Warn = true; +} + +bool SmartAttributeParsedData::updateUnit() +{ + if (m_Quirk) { + switch (id()) { + case 3: + if (*m_Quirk & SMART_QUIRK_3_UNUSED) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; + return true; + } + break; + + case 4: + if (*m_Quirk & SMART_QUIRK_4_UNUSED) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; + return true; + } + break; + + case 5: + if (*m_Quirk & SMART_QUIRK_5_UNKNOWN) + return false; + break; + + case 9: + if (*m_Quirk & SMART_QUIRK_9_POWERONMINUTES) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; + return true; + } else if (*m_Quirk & SMART_QUIRK_9_POWERONSECONDS) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; + return true; + } else if (*m_Quirk & SMART_QUIRK_9_POWERONHALFMINUTES) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; + return true; + } else if (*m_Quirk & SMART_QUIRK_9_UNKNOWN) + return false; + break; + + case 190: + if (*m_Quirk & SMART_QUIRK_190_UNKNOWN) + return false; + break; + + case 192: + if (*m_Quirk & SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_NONE; + return true; + } + break; + + case 194: + if (*m_Quirk & SMART_QUIRK_194_10XCELSIUS) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MKELVIN; + return true; + } else if (*m_Quirk & SMART_QUIRK_194_UNKNOWN) + return false; + break; + + case 197: + if (*m_Quirk & SMART_QUIRK_197_UNKNOWN) + return false; + break; + + case 198: + if (*m_Quirk & SMART_QUIRK_198_UNKNOWN) + return false; + break; + + case 200: + if (*m_Quirk & SMART_QUIRK_200_WRITEERRORCOUNT) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_NONE; + return true; + } + break; + + case 201: + if (*m_Quirk & SMART_QUIRK_201_DETECTEDTACOUNT) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_NONE; + return true; + } + break; + + case 225: + if (*m_Quirk & SMART_QUIRK_225_TOTALLBASWRITTEN) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MB; + return true; + } + break; + + case 226: + if (*m_Quirk & SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_SMALL_PERCENT; + return true; + } + break; + + case 227: + if (*m_Quirk & SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_SMALL_PERCENT; + return true; + } + break; + + case 228: + if (*m_Quirk & SMART_QUIRK_228_WORKLOADTIMER) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; + return true; + } + break; + + case 232: + if (*m_Quirk & SMART_QUIRK_232_AVAILABLERESERVEDSPACE) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_PERCENT; + return true; + } + break; + + case 233: + if (*m_Quirk & SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) { + m_PrettyUnit = SMART_ATTRIBUTE_UNIT_PERCENT; + return true; + } + break; + + } + } + + if (tableUnit().contains(id())) { + m_PrettyUnit = tableUnit()[id()]; + return true; + } + + return false; +} + +static const QMap tableUnit() +{ + QMap table; + table.insert(1, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(2, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(3, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS); + table.insert(4, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(5, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_SECTORS); + table.insert(6, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(7, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(8, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(9, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS); + table.insert(10, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(11, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(12, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(13, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(170, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_PERCENT); + table.insert(171, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(172, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(175, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(176, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(177, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(178, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(179, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(180, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(181, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(182, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(183, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(184, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(187, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_SECTORS); + table.insert(188, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(189, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(190, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MKELVIN); + table.insert(191, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(192, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(193, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(194, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MKELVIN); + table.insert(195, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(196, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(197, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_SECTORS); + table.insert(198, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_SECTORS); + table.insert(199, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(200, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(201, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(202, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(203, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(204, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(205, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(206, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(207, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(208, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(209, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(220, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(221, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(222, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS); + table.insert(223, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(224, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(225, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(226, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS); + table.insert(227, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(228, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + table.insert(230, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(231, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MKELVIN); + table.insert(232, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_PERCENT); + table.insert(233, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(234, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_SECTORS); + table.insert(235, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_UNKNOWN); + table.insert(240, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS); + table.insert(241, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MB); + table.insert(242, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MB); + table.insert(250, SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE); + + return table; +} + +static const SmartAttributeParsedData::SmartQuirkDataBase *quirkDatabase() +{ + static const SmartAttributeParsedData::SmartQuirkDataBase quirkDb[] = { + { + "^(FUJITSU MHY2120BH|FUJITSU MHY2250BH)$", + "^0085000B$", + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_9_POWERONMINUTES | + SmartAttributeParsedData::SMART_QUIRK_197_UNKNOWN | + SmartAttributeParsedData::SMART_QUIRK_198_UNKNOWN) + }, + { + "^FUJITSU MHR2040AT$", + NULL, + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_9_POWERONSECONDS | + SmartAttributeParsedData::SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT | + SmartAttributeParsedData::SMART_QUIRK_200_WRITEERRORCOUNT) + }, + { + "^FUJITSU MHS20[6432]0AT( .)?$", + NULL, + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_9_POWERONSECONDS | + SmartAttributeParsedData::SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT | + SmartAttributeParsedData::SMART_QUIRK_200_WRITEERRORCOUNT | + SmartAttributeParsedData::SMART_QUIRK_201_DETECTEDTACOUNT) + }, + { + "^(" + "FUJITSU M1623TAU|" + "FUJITSU MHG2...ATU?.*|" + "FUJITSU MHH2...ATU?.*|" + "FUJITSU MHJ2...ATU?.*|" + "FUJITSU MHK2...ATU?.*|" + "FUJITSU MHL2300AT|" + "FUJITSU MHM2(20|15|10|06)0AT|" + "FUJITSU MHN2...AT|" + "FUJITSU MHR2020AT|" + "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|" + "FUJITSU MHU2...ATU?.*|" + "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|" + "FUJITSU MP[A-G]3...A[HTEV]U?.*" + ")$", + NULL, + SmartAttributeParsedData::SMART_QUIRK_9_POWERONSECONDS + }, + { + "^(" + "SAMSUNG SV4012H|" + "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]" + ")$", + NULL, + SmartAttributeParsedData::SMART_QUIRK_9_POWERONHALFMINUTES + }, + { + "^(" + "SAMSUNG SV0412H|" + "SAMSUNG SV1204H" + ")$", + NULL, + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_9_POWERONHALFMINUTES | + SmartAttributeParsedData::SMART_QUIRK_194_10XCELSIUS) + }, + { + "^SAMSUNG SP40A2H$", + "^RR100-07$", + SmartAttributeParsedData::SMART_QUIRK_9_POWERONHALFMINUTES + }, + { + "^SAMSUNG SP80A4H$", + "^RT100-06$", + SmartAttributeParsedData::SMART_QUIRK_9_POWERONHALFMINUTES + }, + { + "^SAMSUNG SP8004H$", + "^QW100-61$", + SmartAttributeParsedData::SMART_QUIRK_9_POWERONHALFMINUTES + }, + { + "^(" + "Maxtor 2B0(0[468]|1[05]|20)H1|" + "Maxtor 4G(120J6|160J[68])|" + "Maxtor 4D0(20H1|40H2|60H3|80H4)" + ")$", + NULL, + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_9_POWERONMINUTES | + SmartAttributeParsedData::SMART_QUIRK_194_UNKNOWN) + }, + { + "^(" + "Maxtor 2F0[234]0[JL]0|" + "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|" + "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|" + "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|" + "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|" + "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|" + "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|" + "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|" + "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|" + "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|" + "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|" + "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|" + "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|" + "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|" + "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|" + "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|" + "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|" + "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|" + "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|" + "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|" + "Maxtor (98196H8|96147H6)|" + "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|" + "Maxtor 6(E0[234]|K04)0L0|" + "Maxtor 6(B(30|25|20|16|12|10|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0|" + "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|" + "Maxtor 7Y250[PM]0|" + "Maxtor [45]A(25|30|32)0[JN]0|" + "Maxtor 7L(25|30)0[SR]0" + ")$", + NULL, + SmartAttributeParsedData::SMART_QUIRK_9_POWERONMINUTES + }, + { + "^(" + "HITACHI_DK14FA-20B|" + "HITACHI_DK23..-..B?|" + "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|" + "HE[JN]4230[23]0F9AT00|" + "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00" + ")$", + NULL, + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_9_POWERONMINUTES | + SmartAttributeParsedData::SMART_QUIRK_193_LOADUNLOAD) + }, + { + "^HTS541010G9SA00$", + "^MBZOC60P$", + SmartAttributeParsedData::SMART_QUIRK_5_UNKNOWN + }, + { + "^MCCOE64GEMPP$", + "^2.9.0[3-9]$", + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_5_UNKNOWN | + SmartAttributeParsedData::SMART_QUIRK_190_UNKNOWN) + }, + { + "^INTEL SSDSA2(CT|BT|CW|BW)[0-9]{3}G3.*$", + NULL, + (SmartAttributeParsedData::SmartQuirk) (SmartAttributeParsedData::SMART_QUIRK_3_UNUSED | + SmartAttributeParsedData::SMART_QUIRK_4_UNUSED | + SmartAttributeParsedData::SMART_QUIRK_225_TOTALLBASWRITTEN | + SmartAttributeParsedData::SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR | + SmartAttributeParsedData::SMART_QUIRK_227_TIMEWORKLOADHOSTREADS | + SmartAttributeParsedData::SMART_QUIRK_228_WORKLOADTIMER | + SmartAttributeParsedData::SMART_QUIRK_232_AVAILABLERESERVEDSPACE | + SmartAttributeParsedData::SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) + } + }; + + return quirkDb; +} + +static SmartAttributeParsedData::SmartQuirk *getQuirk(QString model, QString firmware) +{ + const SmartAttributeParsedData::SmartQuirkDataBase *db; + + QRegExp modelRegex; + QRegExp firmwareRegex; + + for (db = quirkDatabase(); db->model || db->firmware; db++) { + if (db->model) { + modelRegex.setPattern(QString::fromLocal8Bit(db->model)); + if (!modelRegex.exactMatch(model)) + continue; + } + if (db->firmware) { + firmwareRegex.setPattern(QString::fromLocal8Bit(db->firmware)); + if (!firmwareRegex.exactMatch(firmware)) + continue; + } + return (SmartAttributeParsedData::SmartQuirk *)&db->quirk; + } + + return nullptr; +} diff --git a/src/core/smartattributeparseddata.h b/src/core/smartattributeparseddata.h new file mode 100644 index 0000000..641b5ea --- /dev/null +++ b/src/core/smartattributeparseddata.h @@ -0,0 +1,207 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#if !defined(KPMCORE_SMARTATTRIBUTEPARSEDDATA_H) +#define KPMCORE_SMARTATTRIBUTEPARSEDDATA_H + +#include +#include + +class SmartDiskInformation; + +class SmartAttributeParsedData +{ +public: + enum SmartAttributeUnit { + SMART_ATTRIBUTE_UNIT_UNKNOWN, + SMART_ATTRIBUTE_UNIT_NONE, + SMART_ATTRIBUTE_UNIT_MSECONDS, + SMART_ATTRIBUTE_UNIT_SECTORS, + SMART_ATTRIBUTE_UNIT_MKELVIN, + SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, + SMART_ATTRIBUTE_UNIT_PERCENT, + SMART_ATTRIBUTE_UNIT_MB, + _SMART_ATTRIBUTE_UNIT_MAX + }; + + enum SmartQuirk { + SMART_QUIRK_9_POWERONMINUTES = 0x000001, + SMART_QUIRK_9_POWERONSECONDS = 0x000002, + SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004, + SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008, + SMART_QUIRK_193_LOADUNLOAD = 0x000010, + SMART_QUIRK_194_10XCELSIUS = 0x000020, + SMART_QUIRK_194_UNKNOWN = 0x000040, + SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080, + SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100, + SMART_QUIRK_5_UNKNOWN = 0x000200, + SMART_QUIRK_9_UNKNOWN = 0x000400, + SMART_QUIRK_197_UNKNOWN = 0x000800, + SMART_QUIRK_198_UNKNOWN = 0x001000, + SMART_QUIRK_190_UNKNOWN = 0x002000, + SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000, + SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000, + SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000, + SMART_QUIRK_4_UNUSED = 0x020000, + SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000, + SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000, + SMART_QUIRK_228_WORKLOADTIMER = 0x100000, + SMART_QUIRK_3_UNUSED = 0x200000 + }; + + struct SmartQuirkDataBase { + const char *model; + const char *firmware; + SmartAttributeParsedData::SmartQuirk quirk; + }; + +public: + SmartAttributeParsedData(SmartDiskInformation *disk, QJsonObject jsonAttribute); + + SmartAttributeParsedData(const SmartAttributeParsedData &other); + +public: + quint32 id() const + { + return m_Id; + } + + qint32 currentValue() const + { + return m_CurrentValue; + } + + qint32 worstValue() const + { + return m_WorstValue; + } + + qint32 threshold() const + { + return m_Threshold; + } + + bool prefailure() const + { + return m_Prefailure; + } + + bool online() const + { + return m_Online; + } + + quint64 raw() const + { + return m_Raw; + } + + quint64 prettyValue() const + { + return m_PrettyValue; + } + + SmartAttributeUnit prettyUnit() const + { + return m_PrettyUnit; + } + + bool goodNowValid() const + { + return m_GoodNowValid; + } + + bool goodNow() const + { + return m_GoodNow; + } + + bool goodInThePastValid() const + { + return m_GoodInThePastValid; + } + + bool goodInThePast() const + { + return m_GoodInThePast; + } + + bool thresholdValid() const + { + return m_ThresholdValid; + } + + bool currentValueValid() const + { + return m_CurrentValueValid; + } + + bool worstValueValid() const + { + return m_WorstValueValid; + } + + bool warn() const + { + return m_Warn; + } + + SmartDiskInformation *disk() const + { + return m_Disk; + } + +protected: + void validateValues(); + + bool updateUnit(); + + void makePretty(); + + void verifyAttribute(); + + void verifyTemperature(); + + void verifyShortTime(); + + void verifyLongTime(); + + void verifySectors(); + +private: + quint32 m_Id; + qint32 m_CurrentValue; + qint32 m_WorstValue; + qint32 m_Threshold; + quint64 m_Raw; + quint64 m_PrettyValue; + bool m_CurrentValueValid; + bool m_WorstValueValid; + bool m_ThresholdValid; + bool m_Prefailure; + bool m_Online; + bool m_GoodNow; + bool m_GoodNowValid; + bool m_GoodInThePast; + bool m_GoodInThePastValid; + bool m_Warn; + SmartAttributeUnit m_PrettyUnit; + SmartDiskInformation *m_Disk; + SmartQuirk *m_Quirk; +}; + +#endif // SMARTATTRIBUTEPARSEDDATA_H diff --git a/src/core/smartdiskinformation.cpp b/src/core/smartdiskinformation.cpp new file mode 100644 index 0000000..cf701ef --- /dev/null +++ b/src/core/smartdiskinformation.cpp @@ -0,0 +1,194 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#include "core/smartdiskinformation.h" +#include "core/smartattributeparseddata.h" + +static quint64 u64log2(quint64 n); + +SmartDiskInformation::SmartDiskInformation() : + m_ModelName(QString()), + m_FirmwareVersion(QString()), + m_SerialNumber(QString()), + m_Size(0), + m_Temperature(0), + m_BadSectors(0), + m_PoweredOn(0), + m_PowerCycles(0), + m_SmartStatus(false), + m_BadAttributeNow(false), + m_BadAttributeInThePast(false), + m_SelfTestExecutionStatus(SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER), + m_Overall(SmartDiskInformation::SMART_OVERALL_BAD_STATUS) +{ + +} + +void SmartDiskInformation::updateBadSectors() +{ + SmartAttributeParsedData *reallocatedSectorCt; + reallocatedSectorCt = findAttribute(5); + + SmartAttributeParsedData *currentPendingSector; + currentPendingSector = findAttribute(197); + + if (!reallocatedSectorCt && !currentPendingSector) + m_BadSectors = 0; + else if (reallocatedSectorCt && currentPendingSector) + m_BadSectors = reallocatedSectorCt->prettyValue() + currentPendingSector->prettyValue(); + else if (reallocatedSectorCt) + m_BadSectors = reallocatedSectorCt->prettyValue(); + else + m_BadSectors = currentPendingSector->prettyValue(); +} + +void SmartDiskInformation::updateOverall() +{ + if (!smartStatus()) { + m_Overall = SMART_OVERALL_BAD_STATUS; + return; + } + + quint64 sector_threshold = u64log2(size() / 512) * 1024; + + if (badSectors() >= sector_threshold) { + m_Overall = SMART_OVERALL_BAD_SECTOR_MANY; + return; + } + + validateBadAttributes(); + + if (m_BadAttributeNow) { + m_Overall = SMART_OVERALL_BAD_ATTRIBUTE_NOW; + return; + } + + if (badSectors() > 0) { + m_Overall = SMART_OVERALL_BAD_SECTOR; + return; + } + + if (m_BadAttributeInThePast) { + m_Overall = SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST; + return; + } + + m_Overall = SMART_OVERALL_GOOD; + +} + +bool SmartDiskInformation::updateTemperature() +{ + SmartAttributeParsedData *temperatureCelsius; + SmartAttributeParsedData *temperatureCelsius2; + SmartAttributeParsedData *airflowTemperatureCelsius; + + temperatureCelsius = findAttribute(231); + temperatureCelsius2 = findAttribute(194); + airflowTemperatureCelsius = findAttribute(190); + + if (temperatureCelsius != nullptr + && temperatureCelsius->prettyUnit() == SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MKELVIN) { + m_Temperature = temperatureCelsius->prettyValue(); + return true; + } else if (temperatureCelsius2 != nullptr + && temperatureCelsius2->prettyUnit() == SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MKELVIN) { + m_Temperature = temperatureCelsius2->prettyValue(); + return true; + } else if (airflowTemperatureCelsius != nullptr + && airflowTemperatureCelsius->prettyUnit() == + SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MKELVIN) { + m_Temperature = airflowTemperatureCelsius->prettyValue(); + return true; + } + return false; +} + +bool SmartDiskInformation::updatePowerOn() +{ + SmartAttributeParsedData *powerOnHours; + SmartAttributeParsedData *powerOnSeconds; + + powerOnHours = findAttribute(9); + powerOnSeconds = findAttribute(233); + + if (powerOnHours != nullptr + && powerOnHours->prettyUnit() == SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS) { + m_PoweredOn = powerOnHours->prettyValue(); + return true; + } else if (powerOnSeconds != nullptr + && powerOnSeconds->prettyUnit() == SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_MSECONDS) { + m_PoweredOn = powerOnSeconds->prettyValue(); + return true; + } + return false; +} + +bool SmartDiskInformation::updatePowerCycle() +{ + SmartAttributeParsedData *powerCycleCount; + + powerCycleCount = findAttribute(12); + + if (powerCycleCount != nullptr + && powerCycleCount->prettyUnit() == SmartAttributeParsedData::SMART_ATTRIBUTE_UNIT_NONE) { + m_PowerCycles = powerCycleCount->prettyValue(); + return true; + } + return false; +} + +void SmartDiskInformation::validateBadAttributes() +{ + foreach (SmartAttributeParsedData attribute, m_Attributes) { + if (attribute.prefailure()) { + if (attribute.goodNowValid() && !attribute.goodNow()) + m_BadAttributeNow = true; + if (attribute.goodInThePastValid() && !attribute.goodInThePast()) + m_BadAttributeInThePast = true; + } + } +} + +SmartAttributeParsedData *SmartDiskInformation::findAttribute(quint32 id) +{ + SmartAttributeParsedData *attr = nullptr; + foreach (SmartAttributeParsedData attribute, m_Attributes) { + if (id == attribute.id()) { + attr = new SmartAttributeParsedData(attribute); + break; + } + } + return attr; +} + +static quint64 u64log2(quint64 n) +{ + quint64 r; + + if (n <= 1) + return 0; + + r = 0; + for (;;) { + n = n >> 1; + if (!n) + return r; + r++; + } + return 0; +} diff --git a/src/core/smartdiskinformation.h b/src/core/smartdiskinformation.h new file mode 100644 index 0000000..9c9ba4f --- /dev/null +++ b/src/core/smartdiskinformation.h @@ -0,0 +1,192 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#if !defined(KPMCORE_SMARTDISKINFORMATION_H) +#define SMARTDISKINFORMATION_H + +#include +#include + +class SmartAttributeParsedData; + +class SmartDiskInformation +{ +public: + enum SmartSelfTestExecutionStatus { + SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER = 0, + SMART_SELF_TEST_EXECUTION_STATUS_ABORTED = 1, + SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED = 2, + SMART_SELF_TEST_EXECUTION_STATUS_FATAL = 3, + SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN = 4, + SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL = 5, + SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO = 6, + SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ = 7, + SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING = 8, + SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS = 15, + _SMART_SELF_TEST_EXECUTION_STATUS_MAX + }; + + enum SmartOverall { + SMART_OVERALL_GOOD, + SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST, + SMART_OVERALL_BAD_SECTOR, + SMART_OVERALL_BAD_ATTRIBUTE_NOW, + SMART_OVERALL_BAD_SECTOR_MANY, + SMART_OVERALL_BAD_STATUS, + _SMART_OVERALL_MAX + }; + +public: + SmartDiskInformation(); + +public: + void updateBadSectors(); + + void updateOverall(); + + bool updateTemperature(); + + bool updatePowerOn(); + + bool updatePowerCycle(); + + SmartAttributeParsedData *findAttribute(quint32 id); + +public: + const QString model() const + { + return m_ModelName; + } + + const QString firmware() const + { + return m_FirmwareVersion; + } + + const QString serial() const + { + return m_SerialNumber; + } + + quint64 size() const + { + return m_Size; + } + + bool smartStatus() const + { + return m_SmartStatus; + } + + SmartSelfTestExecutionStatus selfTestExecutionStatus() const + { + return m_SelfTestExecutionStatus; + } + + SmartOverall overall() const + { + return m_Overall; + } + + quint64 temperature() const + { + return m_Temperature; + } + + quint64 badSectors() const + { + return m_BadSectors; + } + + quint64 poweredOn() const + { + return m_PoweredOn; + } + + quint64 powerCycles() const + { + return m_PowerCycles; + } + + QList attributes() const + { + return m_Attributes; + } + +public: + void setModel(QString modelName) + { + m_ModelName = modelName; + } + + void setFirmware(QString firmware) + { + m_FirmwareVersion = firmware; + } + + void setSerial(QString serial) + { + m_SerialNumber = serial; + } + + void setSize(quint64 size) + { + m_Size = size; + } + + void setPowerCycles(quint64 powerCycleCt) + { + m_PowerCycles = powerCycleCt; + } + + void setSmartStatus(bool smartStatus) + { + m_SmartStatus = smartStatus; + } + + void setSelfTestExecutionStatus(SmartSelfTestExecutionStatus status) + { + m_SelfTestExecutionStatus = status; + } + + void addAttribute(SmartAttributeParsedData &attribute) + { + m_Attributes << attribute; + } + +protected: + void validateBadAttributes(); + +private: + QString m_ModelName; + QString m_FirmwareVersion; + QString m_SerialNumber; + quint64 m_Size; + quint64 m_Temperature; + quint64 m_BadSectors; + quint64 m_PoweredOn; + quint64 m_PowerCycles; + bool m_SmartStatus; + bool m_BadAttributeNow; + bool m_BadAttributeInThePast; + SmartSelfTestExecutionStatus m_SelfTestExecutionStatus; + SmartOverall m_Overall; + QList m_Attributes; + +}; + +#endif // SMARTDISKINFORMATION_H diff --git a/src/core/smartparser.cpp b/src/core/smartparser.cpp new file mode 100644 index 0000000..81a8214 --- /dev/null +++ b/src/core/smartparser.cpp @@ -0,0 +1,159 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#include "core/smartparser.h" + +#include "core/smartattributeparseddata.h" +#include "core/smartdiskinformation.h" + +#include +#include +#include +#include +#include +#include + +SmartParser::SmartParser(const QString &device_path) : + m_DevicePath(device_path), + m_DiskInformation(nullptr) +{ + +} + +bool SmartParser::init() +{ + loadSmartOutput(); + + if (m_SmartOutput.isEmpty()) + return false; + + QJsonObject smartJson = m_SmartOutput.object(); + + QString model_name = QString::fromLocal8Bit("model_name"); + QString firmware = QString::fromLocal8Bit("firmware_version"); + QString serial_number = QString::fromLocal8Bit("serial_number"); + QString device = QString::fromLocal8Bit("device"); + QString smart_status = QString::fromLocal8Bit("smart_status"); + QString passed = QString::fromLocal8Bit("passed"); + QString self_test = QString::fromLocal8Bit("self_test"); + QString status = QString::fromLocal8Bit("status"); + QString value = QString::fromLocal8Bit("value"); + QString user_capacity = QString::fromLocal8Bit("user_capacity"); + + if (!smartJson.contains(device)) { + qDebug() << "smart disk open failed for " << devicePath() << ": " << strerror(errno); + return false; + } + + if (!smartJson.contains(smart_status)) { + qDebug() << "getting smart status failed for " << devicePath() << ": " << strerror(errno); + return false; + } + + if (!smartJson.contains(model_name) || !smartJson.contains(firmware) + || !smartJson.contains(serial_number)) { + qDebug() << "getting disk identification data failed for " << devicePath() << ": " << strerror( + errno); + return false; + } + + m_DiskInformation = new SmartDiskInformation(); + + QJsonObject smartStatus = smartJson[smart_status].toObject(); + + m_DiskInformation->setSmartStatus(smartStatus[passed].toBool()); + + m_DiskInformation->setModel(smartJson[model_name].toString()); + m_DiskInformation->setFirmware(smartJson[firmware].toString()); + m_DiskInformation->setSerial(smartJson[serial_number].toString()); + m_DiskInformation->setSize(smartJson[user_capacity].toString().toULongLong()); + + QJsonObject selfTest = smartJson[self_test].toObject(); + QJsonObject selfTestStatus = selfTest[status].toObject(); + + m_DiskInformation->setSelfTestExecutionStatus((SmartDiskInformation::SmartSelfTestExecutionStatus) + selfTestStatus[value].toInt()); + + loadAttributes(); + + m_DiskInformation->updateBadSectors(); + + m_DiskInformation->updateOverall(); + + if (!m_DiskInformation->updateTemperature()) + qDebug() << "getting temp failed for " << devicePath() << ": " << strerror(errno); + + if (!m_DiskInformation->updatePowerOn()) + qDebug() << "getting powered on time failed for " << devicePath() << ": " << strerror(errno); + + if (!m_DiskInformation->updatePowerCycle()) + qDebug() << "getting power cycles failed for " << devicePath() << ": " << strerror(errno); + + return true; +} + +void SmartParser::loadSmartOutput() +{ + if (m_SmartOutput.isEmpty()) { + QStringList args; + args.append(QString::fromLocal8Bit("--all")); + args.append(QString::fromLocal8Bit("-j")); + args.append(devicePath()); + + QProcess smartctl; + smartctl.start(QString::fromLocal8Bit("smartctl"), args); + + bool success = smartctl.waitForFinished(); + + if (!success || smartctl.exitStatus() == QProcess::CrashExit) { + qDebug() << "smartctl initialization failed for " << devicePath() << ": " << strerror(errno); + return; + } + + QByteArray output = smartctl.readAllStandardOutput(); + smartctl.close(); + + m_SmartOutput = QJsonDocument::fromJson(output); + } +} + +void SmartParser::loadAttributes() +{ + loadSmartOutput(); + + if (m_SmartOutput.isEmpty()) + return; + + QJsonObject smartJson = m_SmartOutput.object(); + + QString ata_smart_attributes = QString::fromLocal8Bit("ata_smart_attributes"); + QString table = QString::fromLocal8Bit("table"); + + QJsonObject ataSmartAttributes = smartJson[ata_smart_attributes].toObject(); + + QJsonArray attributeArray = ataSmartAttributes[table].toArray(); + + if (!m_DiskInformation) { + qDebug() << "error loading smart attributes for " << devicePath() << ": " << strerror(errno); + return; + } + + foreach (QJsonValue value, attributeArray) { + SmartAttributeParsedData parsedObject(m_DiskInformation, value.toObject()); + m_DiskInformation->addAttribute(parsedObject); + } +} diff --git a/src/core/smartparser.h b/src/core/smartparser.h new file mode 100644 index 0000000..7a199fa --- /dev/null +++ b/src/core/smartparser.h @@ -0,0 +1,57 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#if !defined(KPMCORE_SMARTPARSER_H) +#define KPMCORE_SMARTPARSER_H + +#include +#include + +class SmartDiskInformation; + +class SmartParser +{ +public: + SmartParser(const QString &device_path); + +public: + bool init(); + +public: + const QString &devicePath() const + { + return m_DevicePath; + } + + SmartDiskInformation *diskInformation() const + { + return m_DiskInformation; + } + +protected: + void loadSmartOutput(); + + void loadAttributes(); + +private: + const QString m_DevicePath; + QJsonDocument m_SmartOutput; + SmartDiskInformation *m_DiskInformation; + +}; + +#endif // SMARTPARSER_H diff --git a/src/core/smartstatus.cpp b/src/core/smartstatus.cpp index 8b1cb91..60c8d6e 100644 --- a/src/core/smartstatus.cpp +++ b/src/core/smartstatus.cpp @@ -18,16 +18,19 @@ #include "core/smartstatus.h" +#include "core/smartparser.h" +#include "core/smartdiskinformation.h" +#include "core/smartattributeparseddata.h" + #include #include #include #include -#include #include -SmartStatus::SmartStatus(const QString& device_path) : +SmartStatus::SmartStatus(const QString &device_path) : m_DevicePath(device_path), m_InitSuccess(false), m_Status(false), @@ -46,148 +49,110 @@ SmartStatus::SmartStatus(const QString& device_path) : void SmartStatus::update() { - SkDisk* skDisk = nullptr; - SkBool skSmartStatus = false; - uint64_t mkelvin = 0; - uint64_t skBadSectors = 0; - uint64_t skPoweredOn = 0; - uint64_t skPowerCycles = 0; + SmartParser parser(devicePath()); - if (sk_disk_open(devicePath().toLocal8Bit().constData(), &skDisk) < 0) { - qDebug() << "smart disk open failed for " << devicePath() << ": " << strerror(errno); + if (!parser.init()) { + qDebug() << "error during smart output parsing for " << devicePath() << ": " << strerror(errno); return; } - if (sk_disk_smart_status(skDisk, &skSmartStatus) < 0) { - qDebug() << "getting smart status failed for " << devicePath() << ": " << strerror(errno); - sk_disk_free(skDisk); + SmartDiskInformation *disk; + + disk = parser.diskInformation(); + + if (!disk) return; + + setStatus(disk->smartStatus()); + + setModelName(disk->model()); + + setFirmware(disk->firmware()); + + setSerial(disk->serial()); + + switch (disk->selfTestExecutionStatus()) { + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_ABORTED: + setSelfTestStatus(Aborted); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED: + setSelfTestStatus(Interrupted); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_FATAL: + setSelfTestStatus(Fatal); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN: + setSelfTestStatus(ErrorUnknown); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL: + setSelfTestStatus(ErrorEletrical); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO: + setSelfTestStatus(ErrorServo); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ: + setSelfTestStatus(ErrorRead); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING: + setSelfTestStatus(ErrorHandling); + break; + + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS: + setSelfTestStatus(InProgress); + break; + + default: + case SmartDiskInformation::SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER: + setSelfTestStatus(Success); + break; + } - setStatus(skSmartStatus); + switch (disk->overall()) { + case SmartDiskInformation::SMART_OVERALL_GOOD: + setOverall(Good); + break; + + case SmartDiskInformation::SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST: + setOverall(BadPast); + break; + + case SmartDiskInformation::SMART_OVERALL_BAD_SECTOR: + setOverall(BadSectors); + break; + + case SmartDiskInformation::SMART_OVERALL_BAD_ATTRIBUTE_NOW: + setOverall(BadNow); + break; + + case SmartDiskInformation::SMART_OVERALL_BAD_SECTOR_MANY: + setOverall(BadSectorsMany); + break; + + default: + case SmartDiskInformation::SMART_OVERALL_BAD_STATUS: + setOverall(Bad); + break; - if (sk_disk_smart_read_data(skDisk) < 0) { - qDebug() << "reading smart data failed for " << devicePath() << ": " << strerror(errno); - sk_disk_free(skDisk); - return; } - const SkIdentifyParsedData* skIdentify; + setTemp(disk->temperature()); - if (sk_disk_identify_parse(skDisk, &skIdentify) < 0) - qDebug() << "getting identify data failed for " << devicePath() << ": " << strerror(errno); - else { - setModelName(QString::fromLocal8Bit(skIdentify->model)); - setFirmware(QString::fromLocal8Bit(skIdentify->firmware)); - setSerial(QString::fromLocal8Bit(skIdentify->serial)); - } + setBadSectors(disk->badSectors()); - const SkSmartParsedData* skParsed; - if (sk_disk_smart_parse(skDisk, &skParsed) < 0) - qDebug() << "parsing disk smart data failed for " << devicePath() << ": " << strerror(errno); - else { - switch (skParsed->self_test_execution_status) { - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED: - setSelfTestStatus(Aborted); - break; + setPoweredOn(disk->poweredOn()); - case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED: - setSelfTestStatus(Interrupted); - break; + setPowerCycles(disk->powerCycles()); - case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL: - setSelfTestStatus(Fatal); - break; + addAttributes(disk->attributes()); - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN: - setSelfTestStatus(ErrorUnknown); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL: - setSelfTestStatus(ErrorEletrical); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO: - setSelfTestStatus(ErrorServo); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ: - setSelfTestStatus(ErrorRead); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING: - setSelfTestStatus(ErrorHandling); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS: - setSelfTestStatus(InProgress); - break; - - default: - case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER: - setSelfTestStatus(Success); - break; - } - } - - SkSmartOverall overall; - - if (sk_disk_smart_get_overall(skDisk, &overall) < 0) - qDebug() << "getting status failed for " << devicePath() << ": " << strerror(errno); - else { - switch (overall) { - case SK_SMART_OVERALL_GOOD: - setOverall(Good); - break; - - case SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST: - setOverall(BadPast); - break; - - case SK_SMART_OVERALL_BAD_SECTOR: - setOverall(BadSectors); - break; - - case SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW: - setOverall(BadNow); - break; - - case SK_SMART_OVERALL_BAD_SECTOR_MANY: - setOverall(BadSectorsMany); - break; - - default: - case SK_SMART_OVERALL_BAD_STATUS: - setOverall(Bad); - break; - } - } - - if (sk_disk_smart_get_temperature(skDisk, &mkelvin) < 0) - qDebug() << "getting temp failed for " << devicePath() << ": " << strerror(errno); - else - setTemp(mkelvin); - - if (sk_disk_smart_get_bad(skDisk, &skBadSectors) < 0) - qDebug() << "getting bad sectors failed for " << devicePath() << ": " << strerror(errno); - else - setBadSectors(skBadSectors); - - if (sk_disk_smart_get_power_on(skDisk, &skPoweredOn) < 0) - qDebug() << "getting powered on time failed for " << devicePath() << ": " << strerror(errno); - else - setPoweredOn(skPoweredOn); - - if (sk_disk_smart_get_power_cycle(skDisk, &skPowerCycles) < 0) - qDebug() << "getting power cycles failed for " << devicePath() << ": " << strerror(errno); - else - setPowerCycles(skPowerCycles); - - m_Attributes.clear(); - - sk_disk_smart_parse_attributes(skDisk, callback, this); - - sk_disk_free(skDisk); setInitSuccess(true); } @@ -195,7 +160,8 @@ QString SmartStatus::tempToString(quint64 mkelvin) { const double celsius = (mkelvin - 273150.0) / 1000.0; const double fahrenheit = 9.0 * celsius / 5.0 + 32; - return xi18nc("@item:intable degrees in Celsius and Fahrenheit", "%1° C / %2° F", QLocale().toString(celsius, 1), QLocale().toString(fahrenheit, 1)); + return xi18nc("@item:intable degrees in Celsius and Fahrenheit", "%1° C / %2° F", + QLocale().toString(celsius, 1), QLocale().toString(fahrenheit, 1)); } QString SmartStatus::selfTestStatusToString(SmartStatus::SelfTestStatus s) @@ -260,11 +226,13 @@ QString SmartStatus::overallAssessmentToString(Overall o) } -void SmartStatus::callback(SkDisk*, const SkSmartAttributeParsedData* a, void* user_data) +void SmartStatus::addAttributes(QList attr) { - SmartStatus* self = reinterpret_cast(user_data); + m_Attributes.clear(); - SmartAttribute sm(a); - self->m_Attributes.append(sm); + foreach (SmartAttributeParsedData at, attr) { + SmartAttribute sm(at); + m_Attributes.append(sm); + } } diff --git a/src/core/smartstatus.h b/src/core/smartstatus.h index c68a0ca..2983582 100644 --- a/src/core/smartstatus.h +++ b/src/core/smartstatus.h @@ -58,92 +58,116 @@ public: typedef QList Attributes; public: - SmartStatus(const QString& device_path); + SmartStatus(const QString &device_path); public: void update(); - const QString& devicePath() const { + const QString &devicePath() const + { return m_DevicePath; } - bool isValid() const { + bool isValid() const + { return m_InitSuccess; } - bool status() const { + bool status() const + { return m_Status; } - const QString& modelName() const { + const QString &modelName() const + { return m_ModelName; } - const QString& serial() const { + const QString &serial() const + { return m_Serial; } - const QString& firmware() const { + const QString &firmware() const + { return m_Firmware; } - quint64 temp() const { + quint64 temp() const + { return m_Temp; } - quint64 badSectors() const { + quint64 badSectors() const + { return m_BadSectors; } - quint64 powerCycles() const { + quint64 powerCycles() const + { return m_PowerCycles; } - quint64 poweredOn() const { + quint64 poweredOn() const + { return m_PoweredOn; } - const Attributes& attributes() const { + const Attributes &attributes() const + { return m_Attributes; } - Overall overall() const { + Overall overall() const + { return m_Overall; } - SelfTestStatus selfTestStatus() const { + SelfTestStatus selfTestStatus() const + { return m_SelfTestStatus; } + void addAttributes(QList attr); + static QString tempToString(quint64 mkelvin); static QString overallAssessmentToString(Overall o); static QString selfTestStatusToString(SmartStatus::SelfTestStatus s); protected: - void setStatus(bool s) { + void setStatus(bool s) + { m_Status = s; } - void setModelName(const QString& name) { + void setModelName(const QString &name) + { m_ModelName = name; } - void setSerial(const QString& s) { + void setSerial(const QString &s) + { m_Serial = s; } - void setFirmware(const QString& f) { + void setFirmware(const QString &f) + { m_Firmware = f; } - void setTemp(quint64 t) { + void setTemp(quint64 t) + { m_Temp = t; } - void setInitSuccess(bool b) { + void setInitSuccess(bool b) + { m_InitSuccess = b; } - void setBadSectors(quint64 s) { + void setBadSectors(quint64 s) + { m_BadSectors = s; } - void setPowerCycles(quint64 p) { + void setPowerCycles(quint64 p) + { m_PowerCycles = p; } - void setPoweredOn(quint64 t) { + void setPoweredOn(quint64 t) + { m_PoweredOn = t; } - void setOverall(Overall o) { + void setOverall(Overall o) + { m_Overall = o; } - void setSelfTestStatus(SelfTestStatus s) { + void setSelfTestStatus(SelfTestStatus s) + { m_SelfTestStatus = s; } - static void callback(SkDisk* skDisk, const SkSmartAttributeParsedData* a, void* user_data); - private: const QString m_DevicePath; bool m_InitSuccess; From d71731141c23dac6474970de61ce32d77a4363b3 Mon Sep 17 00:00:00 2001 From: Caio Carvalho Date: Tue, 9 Jan 2018 01:35:59 -0300 Subject: [PATCH 09/10] - Changed m_Quirk variable in SmartAttributeParsedData to SmartQuirk value instead of reference - Changed smartctl call to use ExternalCommand class instead of QProcess - Changed Q_FOREACH to C++11 ranged based for - Changed quint64 json parsing --- src/core/smartattributeparseddata.cpp | 56 +++++++++++++-------------- src/core/smartattributeparseddata.h | 2 +- src/core/smartdiskinformation.cpp | 4 +- src/core/smartparser.cpp | 24 +++++------- src/core/smartstatus.cpp | 2 +- 5 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/core/smartattributeparseddata.cpp b/src/core/smartattributeparseddata.cpp index 8960c92..3b21ccd 100644 --- a/src/core/smartattributeparseddata.cpp +++ b/src/core/smartattributeparseddata.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #define MKELVIN_VALID_MIN ((qint64) ((-15LL*1000LL) + 273150LL)) #define MKELVIN_VALID_MAX ((qint64) ((100LL*1000LL) + 273150LL)) @@ -30,7 +31,7 @@ #define MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL) static const QMap tableUnit(); -static SmartAttributeParsedData::SmartQuirk *getQuirk(QString model, QString firmware); +static SmartAttributeParsedData::SmartQuirk getQuirk(QString model, QString firmware); SmartAttributeParsedData::SmartAttributeParsedData(SmartDiskInformation *disk, QJsonObject jsonAttribute) : @@ -52,7 +53,7 @@ SmartAttributeParsedData::SmartAttributeParsedData(SmartDiskInformation *disk, m_Warn(false), m_PrettyUnit(SMART_ATTRIBUTE_UNIT_UNKNOWN), m_Disk(disk), - m_Quirk(nullptr) + m_Quirk((SmartAttributeParsedData::SmartQuirk) 0) { if (disk) m_Quirk = getQuirk(disk->model(), disk->firmware()); @@ -63,7 +64,6 @@ SmartAttributeParsedData::SmartAttributeParsedData(SmartDiskInformation *disk, QString worst = QString::fromLocal8Bit("worst"); QString thresh = QString::fromLocal8Bit("thresh"); QString raw = QString::fromLocal8Bit("raw"); - QString str = QString::fromLocal8Bit("string"); QString flags = QString::fromLocal8Bit("flags"); QString prefailure = QString::fromLocal8Bit("prefailure"); QString online = QString::fromLocal8Bit("updated_online"); @@ -75,7 +75,7 @@ SmartAttributeParsedData::SmartAttributeParsedData(SmartDiskInformation *disk, QJsonObject rawObj = jsonAttribute[raw].toObject(); - m_Raw = rawObj[str].toString().toULongLong(); + m_Raw = rawObj[value].toVariant().toULongLong(); QJsonObject flagsObj = jsonAttribute[flags].toObject(); @@ -264,119 +264,119 @@ bool SmartAttributeParsedData::updateUnit() if (m_Quirk) { switch (id()) { case 3: - if (*m_Quirk & SMART_QUIRK_3_UNUSED) { + if (m_Quirk & SMART_QUIRK_3_UNUSED) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; return true; } break; case 4: - if (*m_Quirk & SMART_QUIRK_4_UNUSED) { + if (m_Quirk & SMART_QUIRK_4_UNUSED) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_UNKNOWN; return true; } break; case 5: - if (*m_Quirk & SMART_QUIRK_5_UNKNOWN) + if (m_Quirk & SMART_QUIRK_5_UNKNOWN) return false; break; case 9: - if (*m_Quirk & SMART_QUIRK_9_POWERONMINUTES) { + if (m_Quirk & SMART_QUIRK_9_POWERONMINUTES) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; return true; - } else if (*m_Quirk & SMART_QUIRK_9_POWERONSECONDS) { + } else if (m_Quirk & SMART_QUIRK_9_POWERONSECONDS) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; return true; - } else if (*m_Quirk & SMART_QUIRK_9_POWERONHALFMINUTES) { + } else if (m_Quirk & SMART_QUIRK_9_POWERONHALFMINUTES) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; return true; - } else if (*m_Quirk & SMART_QUIRK_9_UNKNOWN) + } else if (m_Quirk & SMART_QUIRK_9_UNKNOWN) return false; break; case 190: - if (*m_Quirk & SMART_QUIRK_190_UNKNOWN) + if (m_Quirk & SMART_QUIRK_190_UNKNOWN) return false; break; case 192: - if (*m_Quirk & SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) { + if (m_Quirk & SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_NONE; return true; } break; case 194: - if (*m_Quirk & SMART_QUIRK_194_10XCELSIUS) { + if (m_Quirk & SMART_QUIRK_194_10XCELSIUS) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MKELVIN; return true; - } else if (*m_Quirk & SMART_QUIRK_194_UNKNOWN) + } else if (m_Quirk & SMART_QUIRK_194_UNKNOWN) return false; break; case 197: - if (*m_Quirk & SMART_QUIRK_197_UNKNOWN) + if (m_Quirk & SMART_QUIRK_197_UNKNOWN) return false; break; case 198: - if (*m_Quirk & SMART_QUIRK_198_UNKNOWN) + if (m_Quirk & SMART_QUIRK_198_UNKNOWN) return false; break; case 200: - if (*m_Quirk & SMART_QUIRK_200_WRITEERRORCOUNT) { + if (m_Quirk & SMART_QUIRK_200_WRITEERRORCOUNT) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_NONE; return true; } break; case 201: - if (*m_Quirk & SMART_QUIRK_201_DETECTEDTACOUNT) { + if (m_Quirk & SMART_QUIRK_201_DETECTEDTACOUNT) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_NONE; return true; } break; case 225: - if (*m_Quirk & SMART_QUIRK_225_TOTALLBASWRITTEN) { + if (m_Quirk & SMART_QUIRK_225_TOTALLBASWRITTEN) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MB; return true; } break; case 226: - if (*m_Quirk & SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) { + if (m_Quirk & SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_SMALL_PERCENT; return true; } break; case 227: - if (*m_Quirk & SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) { + if (m_Quirk & SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_SMALL_PERCENT; return true; } break; case 228: - if (*m_Quirk & SMART_QUIRK_228_WORKLOADTIMER) { + if (m_Quirk & SMART_QUIRK_228_WORKLOADTIMER) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_MSECONDS; return true; } break; case 232: - if (*m_Quirk & SMART_QUIRK_232_AVAILABLERESERVEDSPACE) { + if (m_Quirk & SMART_QUIRK_232_AVAILABLERESERVEDSPACE) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_PERCENT; return true; } break; case 233: - if (*m_Quirk & SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) { + if (m_Quirk & SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) { m_PrettyUnit = SMART_ATTRIBUTE_UNIT_PERCENT; return true; } @@ -628,7 +628,7 @@ static const SmartAttributeParsedData::SmartQuirkDataBase *quirkDatabase() return quirkDb; } -static SmartAttributeParsedData::SmartQuirk *getQuirk(QString model, QString firmware) +static SmartAttributeParsedData::SmartQuirk getQuirk(QString model, QString firmware) { const SmartAttributeParsedData::SmartQuirkDataBase *db; @@ -646,8 +646,8 @@ static SmartAttributeParsedData::SmartQuirk *getQuirk(QString model, QString fir if (!firmwareRegex.exactMatch(firmware)) continue; } - return (SmartAttributeParsedData::SmartQuirk *)&db->quirk; + return db->quirk; } - return nullptr; + return (SmartAttributeParsedData::SmartQuirk) 0; } diff --git a/src/core/smartattributeparseddata.h b/src/core/smartattributeparseddata.h index 641b5ea..2868fb7 100644 --- a/src/core/smartattributeparseddata.h +++ b/src/core/smartattributeparseddata.h @@ -201,7 +201,7 @@ private: bool m_Warn; SmartAttributeUnit m_PrettyUnit; SmartDiskInformation *m_Disk; - SmartQuirk *m_Quirk; + SmartQuirk m_Quirk; }; #endif // SMARTATTRIBUTEPARSEDDATA_H diff --git a/src/core/smartdiskinformation.cpp b/src/core/smartdiskinformation.cpp index cf701ef..b220279 100644 --- a/src/core/smartdiskinformation.cpp +++ b/src/core/smartdiskinformation.cpp @@ -154,7 +154,7 @@ bool SmartDiskInformation::updatePowerCycle() void SmartDiskInformation::validateBadAttributes() { - foreach (SmartAttributeParsedData attribute, m_Attributes) { + for (const SmartAttributeParsedData &attribute : qAsConst(m_Attributes)) { if (attribute.prefailure()) { if (attribute.goodNowValid() && !attribute.goodNow()) m_BadAttributeNow = true; @@ -167,7 +167,7 @@ void SmartDiskInformation::validateBadAttributes() SmartAttributeParsedData *SmartDiskInformation::findAttribute(quint32 id) { SmartAttributeParsedData *attr = nullptr; - foreach (SmartAttributeParsedData attribute, m_Attributes) { + for (const SmartAttributeParsedData &attribute : qAsConst(m_Attributes)) { if (id == attribute.id()) { attr = new SmartAttributeParsedData(attribute); break; diff --git a/src/core/smartparser.cpp b/src/core/smartparser.cpp index 81a8214..51b9350 100644 --- a/src/core/smartparser.cpp +++ b/src/core/smartparser.cpp @@ -20,11 +20,12 @@ #include "core/smartattributeparseddata.h" #include "core/smartdiskinformation.h" +#include "util/externalcommand.h" + #include #include #include #include -#include #include SmartParser::SmartParser(const QString &device_path) : @@ -80,7 +81,7 @@ bool SmartParser::init() m_DiskInformation->setModel(smartJson[model_name].toString()); m_DiskInformation->setFirmware(smartJson[firmware].toString()); m_DiskInformation->setSerial(smartJson[serial_number].toString()); - m_DiskInformation->setSize(smartJson[user_capacity].toString().toULongLong()); + m_DiskInformation->setSize(smartJson[user_capacity].toVariant().toULongLong()); QJsonObject selfTest = smartJson[self_test].toObject(); QJsonObject selfTestStatus = selfTest[status].toObject(); @@ -114,20 +115,15 @@ void SmartParser::loadSmartOutput() args.append(QString::fromLocal8Bit("-j")); args.append(devicePath()); - QProcess smartctl; - smartctl.start(QString::fromLocal8Bit("smartctl"), args); + ExternalCommand smartctl(QString::fromLocal8Bit("smartctl"), args); - bool success = smartctl.waitForFinished(); + if (smartctl.run() && smartctl.exitCode() == 0) { + QByteArray output = smartctl.rawOutput(); - if (!success || smartctl.exitStatus() == QProcess::CrashExit) { - qDebug() << "smartctl initialization failed for " << devicePath() << ": " << strerror(errno); - return; + m_SmartOutput = QJsonDocument::fromJson(output); } - - QByteArray output = smartctl.readAllStandardOutput(); - smartctl.close(); - - m_SmartOutput = QJsonDocument::fromJson(output); + else + qDebug() << "smartctl initialization failed for " << devicePath() << ": " << strerror(errno); } } @@ -152,7 +148,7 @@ void SmartParser::loadAttributes() return; } - foreach (QJsonValue value, attributeArray) { + for (const QJsonValue &value : qAsConst(attributeArray)) { SmartAttributeParsedData parsedObject(m_DiskInformation, value.toObject()); m_DiskInformation->addAttribute(parsedObject); } diff --git a/src/core/smartstatus.cpp b/src/core/smartstatus.cpp index 60c8d6e..3c8a35a 100644 --- a/src/core/smartstatus.cpp +++ b/src/core/smartstatus.cpp @@ -230,7 +230,7 @@ void SmartStatus::addAttributes(QList attr) { m_Attributes.clear(); - foreach (SmartAttributeParsedData at, attr) { + for (const SmartAttributeParsedData &at : qAsConst(attr)) { SmartAttribute sm(at); m_Attributes.append(sm); } From 44fae61d4f128adf08d524480efc38513cfa9c3e Mon Sep 17 00:00:00 2001 From: Caio Carvalho Date: Tue, 9 Jan 2018 19:34:20 -0300 Subject: [PATCH 10/10] - Removing libatasmart dependency from CMakeLists.txt - Changing getQuirk regular expression evaluation to use QRegularExpression instead of QRegExp --- CMakeLists.txt | 1 - src/core/smartattributeparseddata.cpp | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f863a7..4d58521 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,6 @@ kde_enable_exceptions() find_package(PkgConfig REQUIRED) pkg_check_modules(BLKID REQUIRED blkid>=2.30) -pkg_check_modules(LIBATASMART REQUIRED libatasmart) include_directories(${Qt5Core_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ${BLKID_INCLUDE_DIRS} lib/ src/) diff --git a/src/core/smartattributeparseddata.cpp b/src/core/smartattributeparseddata.cpp index 3b21ccd..f793338 100644 --- a/src/core/smartattributeparseddata.cpp +++ b/src/core/smartattributeparseddata.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #define MKELVIN_VALID_MIN ((qint64) ((-15LL*1000LL) + 273150LL)) @@ -632,18 +632,20 @@ static SmartAttributeParsedData::SmartQuirk getQuirk(QString model, QString firm { const SmartAttributeParsedData::SmartQuirkDataBase *db; - QRegExp modelRegex; - QRegExp firmwareRegex; + QRegularExpression modelRegex; + QRegularExpression firmwareRegex; for (db = quirkDatabase(); db->model || db->firmware; db++) { if (db->model) { modelRegex.setPattern(QString::fromLocal8Bit(db->model)); - if (!modelRegex.exactMatch(model)) + QRegularExpressionMatch match = modelRegex.match(model); + if (!match.hasMatch()) continue; } if (db->firmware) { firmwareRegex.setPattern(QString::fromLocal8Bit(db->firmware)); - if (!firmwareRegex.exactMatch(firmware)) + QRegularExpressionMatch match = firmwareRegex.match(firmware); + if (!match.hasMatch()) continue; } return db->quirk;