" title="Написать письмо">Написать письмо

Статистика

Пользователи : 1
Статьи : 1954
Просмотры материалов : 7085995
 
Получить S.M.A.R.T. накопителя (30.08.2019). Печать E-mail
2019 - Август
30.08.2019 22:06
Save & Share
В полезных исходниках лежит модуль для BCB6, позволяющий получать параметры S.M.A.R.T. как оценку текущего и будущего состояния накопителя HDD или SSD. Параметры описаны в Википедии. В статье - исходный код для предварительной оценки.


/*
Модуль тестировался на дисках:
- Seagate: ST380215A, ST500DM002, ST3120213A, ST3320413AS, ST3320418AS, ST3250318AS, ST340014A;
- Western Digital: WD5000AAKX, WD5000AADS;
- Hitachi: HDT725032VLAT80;
- Samsung: SP1203N, SV4012H.
*/

//Для работы со S.M.A.R.T. Необходимо подключение и одноименного файла .LIB.
#include "WinNT.h"
#include "WinDEF.h"
#include "WinIOCtl.h"

#include "WinInet.h" //Для работы с сетевыми подключениями.

//Индексы атрибута S.M.A.R.T. Hitachi искажает S.M.A.R.T.: смещение Value с 3 на RAW (+2), смещение WORST с 4 на 9.
#define INDEX_ATTRIBUTE_INDEX       0 //Идентификатор атрибута.
#define INDEX_ATTRIBUTE_STATUSFLAGS 1 //Тип атрибута.
                                      //Второго индекса, реально, нет.
#define INDEX_ATTRIBUTE_VALUE       3 //Текущее значение атрибута.
#define INDEX_ATTRIBUTE_WORST       4 //Наихудшее зафиксированное значение. Пороговое значение - лежит в byteThreshold[1]. Для Hitachi - смещение по Threshold неизвестно.
#define INDEX_ATTRIBUTE_RAW         5 //Абсолютное значение.

//Типы атрибута S.M.A.R.T.
#define PRE_FAILURE_WARRANTY        0x1 //Критический (жизненно важный).
#define ON_LINE_COLLECTION          0x2 //Коллекция реального времени.
#define PERFORMANCE_ATTRIBUTE       0x4 //Отражает производительность диска.
#define ERROR_RATE_ATTRIBUTE        0x8 //Отражает частоту появления ошибок.
#define EVENT_COUNT_ATTRIBUTE       0x10 //Счетчик событий.
#define SELF_PRESERVING_ATTRIBUTE   0x20 //Самосохраняющийся.

//Ошибки драйвера накопителя.
#define SMART_NO_ERROR          0 //No error
#define SMART_IDE_ERROR         1 //Error from IDE controller
#define SMART_INVALID_FLAG      2 //Invalid command flag
#define SMART_INVALID_COMMAND   3 //Invalid command byte
#define SMART_INVALID_BUFFER    4 //Bad buffer (null, invalid addr..)
#define SMART_INVALID_DRIVE     5 //Drive number not valid
#define SMART_INVALID_IOCTL     6 //Invalid IOCTL
#define SMART_ERROR_NO_MEM      7 //Could not lock user's buffer
#define SMART_INVALID_REGISTER  8 //Some IDE Register not valid
#define SMART_NOT_SUPPORTED     9 //Invalid cmd flag set
#define SMART_NO_IDE_DEVICE     10 //Cmd issued to device not present although drive number is valid.

//Для получения информации о производителе накопителя. Возможно получение и типа.
#define IOCTL_STORAGE_QUERY_PROPERTY   CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)

DWORD g_dwBytes_Returned = 0; //Заглушка для выполнения функций.
char g_cErrorText_Title[2] = "32";
int g_iErrorText_Icons = 32;

bool bSystem_Test(void)
{
    int iDisk_Number = 0; //Номер накопителя.
    bool bNormal = true; //Флаг нормы теста. Лучше сразу сообщить обо всех ошибках, чтобы 100 раз не разбирать системник.

    //Сбор данных.
    AnsiString asDate = DateToStr(Date()); //Критический. Проверка работоспособности батареи BIOS как фактора записи корректной даты съема телеметрии, в т.ч. для печати протоколов.
    float fHDA_Temperature = fGet_SMART_Parameter(iDisk_Number, 194); //Температура железа накопителя.
    float fMechanical_Shock = fGet_SMART_Parameter(iDisk_Number, 191); //Счетчик механических ударов и внешних нагрузок.
    float fDisk_Shift = fGet_SMART_Parameter(iDisk_Number, 220); //Критический. Показатель смещения блока дисков относительно шпинделя.
    float fWrite_Error_Rate = fGet_SMART_Parameter(iDisk_Number, 200); //Количество сбойных секторов, вместо которых используется резервная область диска (программами проверки поверхности НЕ показываются.
    bool bLAN_Online = InternetGetConnectedState(&g_dwBytes_Returned, 0); //Флаг "хоть одно сетевое подключение подключено", т.е. передача данных возможна.

    //Параметры, отсутствующие в S.M.A.R.T. моего HDD WDC WD5000AAKX.
    float fAirflow_Temperature = fGet_SMART_Parameter(iDisk_Number, 190);
    float fHDD_Temperature = fGet_SMART_Parameter(iDisk_Number, 231);

    float fReallocated_Sectors_Count = fGet_SMART_Parameter(iDisk_Number, 5);

    if (asDate.SubString(asDate.Length()-3, 4).ToInt() < 2019)
    {
        Application->MessageBox("Дата установлена неправильно. Возможно, разряжена батарея BIOS на системной плате (как правило, CR2032). Программа завершает работу.", g_cErrorText_Title, g_iErrorText_Icons);
        bNormal = false;
    }

    if (fHDA_Temperature > 40)
    {
        Application->MessageBox(("Температура накопителя HDD №" + IntToStr(iDisk_Number) + " выше 40 градусов ("+ FloatToStr(fHDA_Temperature) + "). Почистите пылевые фильтры и вентиляторы ЭВМ, протрите накопитель от пыли и установите температуру помещения в соответствии с ТУ.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        //bNormal = false;
    }

    if (fReallocated_Sectors_Count > 200)
    {
        Application->MessageBox(("Параметр Reallocated Sectors Count накопителя HDD №" + IntToStr(iDisk_Number) + " выше 200 ("+ FloatToStr(fReallocated_Sectors_Count) + "). Рекомендуется неспешный поиск нового носителя под замену.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        //bNormal = false;
    }

    if (fMechanical_Shock > 0)
    {
        Application->MessageBox(("Параметр Mechanical Shock накопителя HDD №" + IntToStr(iDisk_Number) + " выше 0 ("+ FloatToStr(fMechanical_Shock) + "). Проверьте КПА на отсутствие вибраций, не допускайте ударов накопителя и КПА при эксплуатации и перевозке.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        //bNormal = false;
    }

    if (fDisk_Shift > 0)
    {
        Application->MessageBox(("Параметр Disk Shift накопителя HDD №" + IntToStr(iDisk_Number) + " выше 0 ("+ FloatToStr(fDisk_Shift) + "). Это означает, что произошло смещение блока дисков относительно шпинделя. Немедленно обесточьте КПА и приобретите новый носитель с целью переноса информации. С данной ошибкой диск быстро становится неработоспособным.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        bNormal = false;
    }

    if (bLAN_Online)
    {
        Application->MessageBox("Обнаружено сетевое подключение с состоянием \"подключено\". С активным сетевым подключением работа с КПА невозможна из-за вероятности ложноотрицательных результатов.", g_cErrorText_Title, g_iErrorText_Icons);
        bNormal = false;
    }

    return bNormal;
}

float fGet_SMART_Parameter(int iDisk_Number, int iParameter_Number)
{
    //Возвращение текущего значения параметра; без порогового значения, т.к. на разных носителях они разные.
    //В случае отсутствия параметра в S.M.A.R.T. данная функция вернет -2147483648, а не -2147483647 (что за глюки в разрядах у Builder - не знаю).
    float fResult = -2147483647;

    bool bData_Getted = false; //Флаг успешного получения информации S.M.A.R.T.

    //Для получения производителя диска.
    char cQuery[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0};
    char cResult[512] = {0}; //Буфер для получения данных от DeviceIoControl.
    AnsiString asManufacturer_Disk = ""; //Строка с названием диска и его производителя.

    bool bWestern_Digital = false; //WDC содержит текущую температуру в RAW, а не в VALUE, - но нет флага отличия WDC от других дисков. В итоге температура на WDC отличается на +2.5 градуса, что критично.
    bool bHitachi = false; //Hitachi имеет другую структуру S.M.A.R.T. по температуре и расширенный ее съем.
    bool bSeagate = false; //Для кучи.
    bool bSamsung = false; //Для кучи.

    //Открытие физического диска.
    AnsiString asDrive_Path = "\\\\.\\PhysicalDrive" + IntToStr(iDisk_Number);
    HANDLE hDrive_Physical = CreateFile(asDrive_Path.c_str(),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
    if (hDrive_Physical == INVALID_HANDLE_VALUE)
    {
        Application->MessageBox(("Не удалось открыть накопитель HDD №" + IntToStr(iDisk_Number) + " для получения его состояния. Проверьте наличие диска в системе и не занят ли он какой-либо программой (например, Victoria).").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        return -1;
    }

    //Определение производителя диска.
    bData_Getted = DeviceIoControl(hDrive_Physical, IOCTL_STORAGE_QUERY_PROPERTY, &cQuery, sizeof(cQuery), &cResult, sizeof(cResult), &g_dwBytes_Returned, NULL);

    if (!bData_Getted) Application->MessageBox(("Не удалось получить производителя накопителя №" + IntToStr(iDisk_Number) + ". Показания температуры в S.M.A.R.T. могут быть искажены.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
    else
    {
        for (unsigned int iTemp=0; iTemp<g_dwBytes_Returned; iTemp++)
        {
            if (cResult[iTemp] != 0) asManufacturer_Disk += cResult[iTemp]; //Исключение терминаторов.
        }

        if (asManufacturer_Disk.Pos("ST") != 0) bSeagate = true; //Лучше первым: вероятность наличия "ST" в других фирмах сохраняется.
        if (asManufacturer_Disk.Pos("WDC") != 0) bWestern_Digital = true;
        if (asManufacturer_Disk.Pos("HDT") != 0) bHitachi = true;
        if (asManufacturer_Disk.Pos("SP")!=0 || asManufacturer_Disk.Pos("SV")!=0) bSamsung = true;

        //ShowMessage(asManufacturer_Disk);
    }

    //Получение информации из S.M.A.R.T.
    //Структура, посылаемая для получения данных в DeviceIoControl.
    const int iParams_To_DeviceIoControl_Size_const = sizeof(SENDCMDINPARAMS) - 1; //Количество байт данных при выполнении DeviceIoControl.
    SENDCMDINPARAMS Params_To_DeviceIoControl = {0}; //Структура, посылаемая для получения данных в DeviceIoControl.

    Params_To_DeviceIoControl.cBufferSize = READ_ATTRIBUTE_BUFFER_SIZE; //Размер буфера для получения атрибутов.
    Params_To_DeviceIoControl.irDriveRegs.bFeaturesReg = READ_ATTRIBUTES; //Запрос на чтение атрибутов.
    Params_To_DeviceIoControl.irDriveRegs.bSectorCountReg = 1; //Регистр количества секторов.
    Params_To_DeviceIoControl.irDriveRegs.bSectorNumberReg = 1; //Регистр номера сектора.
    Params_To_DeviceIoControl.irDriveRegs.bCylLowReg = SMART_CYL_LOW; //Младший разряд указателя на № цилиндра.
    Params_To_DeviceIoControl.irDriveRegs.bCylHighReg = SMART_CYL_HI; //Старший разряд указателя на № цилиндра.
    Params_To_DeviceIoControl.irDriveRegs.bDriveHeadReg = 0xA0|((static_cast<BYTE>(iDisk_Number)&1)<<4); //Регистр диска/головки IDE.
    Params_To_DeviceIoControl.irDriveRegs.bCommandReg = SMART_CMD; //Команда IDE.

    const int iParams_From_DeviceIoControl_Size_const = sizeof(SENDCMDOUTPARAMS)-1 + READ_ATTRIBUTE_BUFFER_SIZE;  //Количество байт данных при выполнении DeviceIoControl.
    SENDCMDOUTPARAMS Params_From_DeviceIoControl[iParams_From_DeviceIoControl_Size_const] = {0}; //Структура, принимающая данные из DeviceIoControl (данные).

    bData_Getted = DeviceIoControl(hDrive_Physical,
                                   SMART_RCV_DRIVE_DATA,
                                   &Params_To_DeviceIoControl,
                                   iParams_To_DeviceIoControl_Size_const,
                                   &Params_From_DeviceIoControl,
                                   iParams_From_DeviceIoControl_Size_const,
                                   &g_dwBytes_Returned,
                                   NULL
                                  );

    if (!bData_Getted)
    {
        Application->MessageBox(("Не удалось получить текущие значения параметров S.M.A.R.T. накопителя HDD №" + IntToStr(iDisk_Number) + " для получения его состояния.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        CloseHandle(hDrive_Physical);
        return -1;
    }

    //Получение ошибок драйвера или интерфейса.
    if (Params_From_DeviceIoControl->DriverStatus.bDriverError != 0)
    {
        Application->MessageBox(("Обнаружена ошибка драйвера накопителя HDD №" + IntToStr(iDisk_Number) + ". Данные S.M.A.R.T. могут быть искажены.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        CloseHandle(hDrive_Physical);
        return -1;
    }

    if (Params_From_DeviceIoControl->DriverStatus.bIDEError != 0)
    {
        Application->MessageBox(("Обнаружена ошибка интерфейса IDE накопителя HDD №" + IntToStr(iDisk_Number) + ". Данные S.M.A.R.T. могут быть искажены.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        CloseHandle(hDrive_Physical);
        return -1;
    }

    //Данные для получения пороговых значений параметров (подготовка).
    Params_To_DeviceIoControl.cBufferSize = READ_THRESHOLD_BUFFER_SIZE;
    Params_To_DeviceIoControl.irDriveRegs.bFeaturesReg = READ_THRESHOLDS;

    SENDCMDOUTPARAMS Thresholds_From_DeviceIoControl[iParams_From_DeviceIoControl_Size_const] = {0}; //Структура, принимающая данные из DeviceIoControl (пороговые значения).

    g_dwBytes_Returned = 0;
    bData_Getted = DeviceIoControl(hDrive_Physical,
                                   SMART_RCV_DRIVE_DATA,
                                   &Params_To_DeviceIoControl,
                                   iParams_To_DeviceIoControl_Size_const,
                                   &Thresholds_From_DeviceIoControl,
                                   iParams_From_DeviceIoControl_Size_const,
                                   &g_dwBytes_Returned,
                                   NULL
                                  );
    if (!bData_Getted)
    {
        Application->MessageBox(("Не удалось получить пороговые значения параметров S.M.A.R.T. накопителя HDD №" + IntToStr(iDisk_Number) + " для получения его состояния.").c_str(), g_cErrorText_Title, g_iErrorText_Icons);
        CloseHandle(hDrive_Physical);
        return -1;
    }

    CloseHandle(hDrive_Physical);

    //Работа с полученной информацией.
    BYTE *byteAttributes = static_cast<BYTE*>(Params_From_DeviceIoControl->bBuffer); //Обычно 0x0A.
    BYTE *byteThresholds = static_cast<BYTE*>(Thresholds_From_DeviceIoControl->bBuffer); //Обычно 0x01.

    for (int i=0; i<54; ++i) //Кол-во параметров S.M.A.R.T. - 54.
    {
        BYTE *byteAttribute = &byteAttributes[2+i*12];
        BYTE *byteThreshold = &byteThresholds[2+i*12];
        unsigned __int64 uiData = 0; //Флаг определения накопителя Hitachi, у которой смещен S.M.A.R.T. И Western Digital, у которых температура записывается не в Value, а в RAW.

        if (!byteAttribute[INDEX_ATTRIBUTE_INDEX]) continue; //Пропуск пустых атрибутов.

        CopyMemory(static_cast<void*>(&uiData),static_cast<void*>(&byteAttribute[INDEX_ATTRIBUTE_RAW]),sizeof(&byteAttribute[INDEX_ATTRIBUTE_RAW])); //Определение uiData для флага Hitachi.
        if (byteAttribute[INDEX_ATTRIBUTE_INDEX] == iParameter_Number)
        {
            if (iParameter_Number==194 || iParameter_Number==190 || iParameter_Number==231) //Танцы с бубном с температурой. 194-й - самый приближенный к физической реальности: температура железа.
            {
                if (bHitachi) //Преобразование для Hitachi. uiData показывает на Seagate и WDC просто температуру. Для Hitachi предполагается показ 3 температур как сумма минимальной, текущей и максимальной.
                {
                    fResult = byteAttribute[INDEX_ATTRIBUTE_RAW];
                    //if (iParameter_Number==194) byteAttribute[INDEX_ATTRIBUTE_RAW+2] - минимальная температура, byteAttribute[INDEX_ATTRIBUTE_RAW+4] - максимальная.
                }
                else if (bWestern_Digital) fResult = byteAttribute[INDEX_ATTRIBUTE_RAW];
                else fResult = byteAttribute[INDEX_ATTRIBUTE_VALUE];

                if (fResult > 80) fResult = (fResult-32) * 5/9; //Жесткие диски могут возвращать температуру и в кельвинах, и в цельсиях.
            }
            else fResult = byteAttribute[INDEX_ATTRIBUTE_VALUE]; //Обычный параметр с одним значением.
            //byteAttribute[INDEX_ATTRIBUTE_WORST] - максимальное зафиксированное значение, byteThreshold[1] - пороговое. Пороговому - нет доверия.

            break;
        }
    }

    return fResult;
}
Обновлено ( 30.08.2019 22:25 )
 
 

Последние новости


©2008-2024. All Rights Reserved. Разработчик - " title="Сергей Белов">Сергей Белов. Материалы сайта предоставляются по принципу "как есть". Автор не несет никакой ответственности и не гарантирует отсутствие неправильных сведений и ошибок. Вся ответственность за использование материалов лежит полностью на читателях. Размещение материалов данного сайта на иных сайтах запрещено без указания активной ссылки на данный сайт-первоисточник (ГК РФ: ст.1259 п.1 + ст.1274 п.1-3).

Много статей не имеет срока устаревания. Есть смысл смотреть и 2011, и даже 2008 год. Политика сайта: написать статью, а потом обновлять ее много лет.
Открыта карта ВТБ для донатов на дорогостоящие эксперименты: 5368 2902 0040 0838.

Рекламодателям! Перестаньте спамить мне на почту с предложениями о размещении рекламы на этом сайте. Я никогда спамером/рекламщиком не был и не буду!
Top.Mail.Ru