1 /*
2     SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com>
3     SPDX-FileCopyrightText: 2018-2020 Andrius Štikonas <andrius@stikonas.eu>
4 
5     SPDX-License-Identifier: GPL-3.0-or-later
6 */
7 
8 #include "core/smartdiskinformation.h"
9 #include "core/smartattributeparseddata.h"
10 
11 #include <memory>
12 #include <utility>
13 
14 static quint64 u64log2(quint64 n);
15 
16 /** Creates a new SmartDiskInformationObject */
SmartDiskInformation()17 SmartDiskInformation::SmartDiskInformation() :
18     m_ModelName(QString()),
19     m_FirmwareVersion(QString()),
20     m_SerialNumber(QString()),
21     m_Sectors(0),
22     m_Temperature(0),
23     m_BadSectors(0),
24     m_PoweredOn(0),
25     m_PowerCycles(0),
26     m_SmartStatus(false),
27     m_BadAttributeNow(false),
28     m_BadAttributeInThePast(false),
29     m_SelfTestExecutionStatus(SmartStatus::SelfTestStatus::Success),
30     m_Overall(SmartStatus::Overall::Bad)
31 {
32 }
33 
34 /** Update the number of bad sectors based on reallocated sector count and current pending sector attributes data */
updateBadSectors()35 void SmartDiskInformation::updateBadSectors()
36 {
37     std::unique_ptr<SmartAttributeParsedData> reallocatedSectorCt(findAttribute(5));
38     std::unique_ptr<SmartAttributeParsedData> currentPendingSector(findAttribute(197));
39 
40     if (!reallocatedSectorCt && !currentPendingSector)
41         m_BadSectors = 0;
42     else if (reallocatedSectorCt && currentPendingSector)
43         m_BadSectors = reallocatedSectorCt->prettyValue() + currentPendingSector->prettyValue();
44     else if (reallocatedSectorCt)
45         m_BadSectors = reallocatedSectorCt->prettyValue();
46     else
47         m_BadSectors = currentPendingSector->prettyValue();
48 }
49 
50 /** Update SMART overall data based on the quantity of bad sectors and the status of SMART attributes */
updateOverall()51 void SmartDiskInformation::updateOverall()
52 {
53     if (!smartStatus()) {
54         m_Overall = SmartStatus::Overall::Bad;
55         return;
56     }
57 
58     quint64 sector_threshold = u64log2(sectors()) * 1024;
59 
60     if (badSectors() >= sector_threshold) {
61         m_Overall = SmartStatus::Overall::BadSectorsMany;
62         return;
63     }
64 
65     validateBadAttributes();
66 
67     if (m_BadAttributeNow) {
68         m_Overall = SmartStatus::Overall::BadNow;
69         return;
70     }
71 
72     if (badSectors() > 0) {
73         m_Overall = SmartStatus::Overall::BadSectors;
74         return;
75     }
76 
77     if (m_BadAttributeInThePast) {
78         m_Overall = SmartStatus::Overall::BadPast;
79         return;
80     }
81 
82     m_Overall = SmartStatus::Overall::Good;
83 }
84 
85 /** Update the temperature value based on SMART attributes
86     @return a boolean representing the status of the operation
87 */
updateTemperature()88 bool SmartDiskInformation::updateTemperature()
89 {
90     std::unique_ptr<SmartAttributeParsedData> temperatureCelsius(findAttribute(231));
91     std::unique_ptr<SmartAttributeParsedData> temperatureCelsius2(findAttribute(194));
92     std::unique_ptr<SmartAttributeParsedData> airflowTemperatureCelsius(findAttribute(190));
93 
94     if (temperatureCelsius != nullptr
95             && temperatureCelsius->prettyUnit() == SmartAttributeUnit::Milikelvin) {
96         m_Temperature = temperatureCelsius->prettyValue();
97         return true;
98     } else if (temperatureCelsius2 != nullptr
99                && temperatureCelsius2->prettyUnit() == SmartAttributeUnit::Milikelvin) {
100         m_Temperature = temperatureCelsius2->prettyValue();
101         return true;
102     } else if (airflowTemperatureCelsius != nullptr
103                && airflowTemperatureCelsius->prettyUnit() ==
104                SmartAttributeUnit::Milikelvin) {
105         m_Temperature = airflowTemperatureCelsius->prettyValue();
106         return true;
107     }
108     return false;
109 }
110 
111 /** Update the powered on value based on SMART attributes
112     @return a boolean representing the status of the operation
113 */
updatePowerOn()114 bool SmartDiskInformation::updatePowerOn()
115 {
116     std::unique_ptr<SmartAttributeParsedData> powerOnHours(findAttribute(9));
117     std::unique_ptr<SmartAttributeParsedData> powerOnSeconds(findAttribute(233));
118 
119     if (powerOnHours != nullptr
120             && powerOnHours->prettyUnit() == SmartAttributeUnit::Miliseconds) {
121         m_PoweredOn = powerOnHours->prettyValue();
122         return true;
123     } else if (powerOnSeconds != nullptr
124                && powerOnSeconds->prettyUnit() == SmartAttributeUnit::Miliseconds) {
125         m_PoweredOn = powerOnSeconds->prettyValue();
126         return true;
127     }
128     return false;
129 }
130 
131 /** Update the power cycles value based on SMART attributes
132     @return a boolean representing the status of the operation
133 */
updatePowerCycle()134 bool SmartDiskInformation::updatePowerCycle()
135 {
136     std::unique_ptr<SmartAttributeParsedData> powerCycleCount(findAttribute(12));
137 
138     if (powerCycleCount != nullptr
139             && powerCycleCount->prettyUnit() == SmartAttributeUnit::None) {
140         m_PowerCycles = powerCycleCount->prettyValue();
141         return true;
142     }
143     return false;
144 }
145 
146 /** Validate disk attributes status */
validateBadAttributes()147 void SmartDiskInformation::validateBadAttributes()
148 {
149     for (const SmartAttributeParsedData &attribute : std::as_const(m_Attributes)) {
150         if (attribute.prefailure()) {
151             if (attribute.goodNowValid() && !attribute.goodNow())
152                 m_BadAttributeNow = true;
153             if (attribute.goodInThePastValid() && !attribute.goodInThePast())
154                 m_BadAttributeInThePast = true;
155         }
156     }
157 }
158 
159 /** Search for a attribute based on its id number
160     @return a reference to the attribute
161 */
findAttribute(quint32 id)162 SmartAttributeParsedData *SmartDiskInformation::findAttribute(quint32 id)
163 {
164     SmartAttributeParsedData *attr = nullptr;
165     for (const SmartAttributeParsedData &attribute : std::as_const(m_Attributes)) {
166         if (id == attribute.id()) {
167             attr = new SmartAttributeParsedData(attribute);
168             break;
169         }
170     }
171     return attr;
172 }
173 
u64log2(quint64 n)174 static quint64 u64log2(quint64 n)
175 {
176     quint64 r;
177 
178     if (n <= 1)
179         return 0;
180 
181     r = 0;
182     for (;;) {
183         n = n >> 1;
184         if (!n)
185             return r;
186         r++;
187     }
188     return 0;
189 }
190