Диск, который годами служил верой и правдой, внезапно начал щелкать, выключаться и вести себя в остальном очень странно. Знакомая ситуация?

У меня для вас хорошие новости: даже если вы не делали бэкап, большую часть информации с этого диска скорее всего можно будет спасти. Если вы делали бэкап, то тоже хорошая новость: у вас есть отличный шанс проверить ваши бэкапы на корректность и полноту.

При попытках чтения с этого диска в логах будут примерно такие строки:

sd 8:0:0:0: [sde] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
sd 8:0:0:0: [sde] tag#0 Sense Key : Hardware Error [current]
sd 8:0:0:0: [sde] tag#0 Add. Sense: Internal target failure
sd 8:0:0:0: [sde] tag#0 CDB: Read(10) 28 00 1a e7 4b a0 00 00 08 00
print_req_error: critical target error, dev sde, sector 56420724

Везде в инструкция ниже мы будет работать с диском /dev/sde, который подключен через USB адаптер с поддержкой чтения атрибутов S.M.A.R.T. Надо отметить что не все адаптеры поддерживают такую функциональность, потому при прочих равных лучше подключить диск напрямую к SATA портам, если есть такая возможность.

Диагноз по атрибутам

Подобные симптомы обычно имеют отражение в атрибутах S.M.A.R.T. Большинство атрибутов не представляет для нас никакого практического интереса, но некоторые особенно важны в диагнозе подобных проблем.

Типичные спутники большинства проблем с механическими жесткими дисками - увеличившиеся счётчики Current_Pending_Sector и Offline_Uncorrectable.

Из всего отчёта нас интересуют в первую очередь они:

$ sudo smartctl -A -f brief /dev/sde | grep -e 196 -e 197 -e 198 -e ID
ID# ATTRIBUTE_NAME          FLAGS    VALUE WORST THRESH FAIL RAW_VALUE
196 Reallocated_Event_Count -O--CK   200   200   000    -    0
197 Current_Pending_Sector  -O--CK   200   200   000    -    10
198 Offline_Uncorrectable   ----CK   100   253   000    -    0

Рассмотрим подробно каждый из счетчиков.

Current_Pending_Sector

В счётчике Current_Pending_Sector указано число секторов, которые диск не смог прочитать. Это критическая ошибка в том смысле, что данные уже не восстановить, но не фатальная в том смысле, что для этих секторов у диска ещё есть резерв. При следующей записи в эти секторы прошивка диска сделает логическую замену их другими секторами из резерва, который, как обычно подразумевается, находится в другой физической части диски.

Offline_Uncorrectable

В счётчике Offline_Uncorrectable указано число секторов, которые диск не смог восстановить во время операций, предусмотренных предыдущим пунктом.

Семантика этого счётчика отличается от производителя к производителю, потому оптимистично можно предположить что эти сектора всё же можно будет восстановить аналогично секторам, указаным в предыдущем счётчике. В чуть более худшем случае вы сможете удалить данные с диска, затем спокойно выкинуть его. В совсем плохом случае вы ничего не сможете сделать с диском.

Reallocated_Event_Count

Счётчик Reallocated_Event_Count тоже представляет для нас интерес. В этом счётчике указано число секторов, которые были заменены на резервные. Если в этом счётчике есть какое-то ненулевое значение, а в других счётчиках всё по нулям, то это говорит о том, что с диском уже проводились восстановительные операции, подобные описанным в этой статье.

Для каких-то дисков ненулевая величина в этом счётчике может означать неминуемую кончину диска, для других дисков этот счётчик говорит о нормальной работе диска: он может проработать ещё годы. Не все диски меняют значение этого счётчика, потому нулевое значение нужно сверять с логами самотестирования.

Решаем проблему

Чтобы диск сделал логическую замену сектора, сектор нужно перезаписать нулями. Для чего нужно знать какой именно сектор нужно перезаписывать. В самих атрибутах нет данных о конкретных секторах, вызвавших ошибку. Координаты сектора можно найти в логе самотестирования диска.

Запустим быстрый тест, который быстро покажет проблемный сектор:

$ sudo smartctl -t short /dev/sde | grep ^Test
Testing has begun.
Test will complete after Sat Apr 21 06:25:37 2018

При наличии ошибок тест обычно заканчивается раньше обозначенного времени, но это верно не для всех дисков всех производителей. Для верности подождём и после обозначенного времени смотрим на первую строку в логе тестов:

$ sudo smartctl -l selftest /dev/sde | grep -e '# 1' -e Num
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Completed: read failure       90%      6888         451365794

Проблемный сектор будет в последней колонке отчёта. Номер его указан в физических координатах относительно диска, а чтобы мы могли перезаписать этот сектор нулями, нам нужно знать логические координаты относительно операционной системы.

$ sudo smartctl -i /dev/sde | grep 'Sector Sizes'
Sector Sizes:     512 bytes logical, 4096 bytes physical

Физические секторы на диске имеют размеры в 4 Кб, но логический сектор (LBA) имеет размер 512 байт. Значит нам нужно перезаписать нулями физический сектор размером 4 Кб, используя LBA, посчитанный исходя из размера сектора в 512 байт.

Внимание! Дальнейшие инструкции натурально удаляют данные с диска. При ошибке данные вернуть будет нельзя. Данные будут удалены совсем и навсегда. Это не шутка. Проверяйте каждую команду по несколько раз. Если сомневаетесь, ни шагу дальше.

Перезаписывать нужно именно физический сектор целиком. Если попытаться перезаписать восемь секторов по 512 байт, которые вроде как соответствуют одному сектору по 4 Кб, то диск вернёт ошибку:

$ sudo dd if=/dev/zero of=/dev/sde bs=512 count=8 seek=451365794 conv=fdatasync
dd: error writing '/dev/sde': Input/output error
1+0 records in
0+0 records out
0 bytes copied, 4,26534 s, 0,0 kB/s

Если заметить, что физический сектор в восемь раз больше логического (4096/512 = 8), то проблема высчитывания смещения от начала диска сводится к делению LBA-координат проблемного сектора на восемь:

$ sudo dd if=/dev/zero of=/dev/sde bs=4096 count=1 seek=$((451365794/(4096/512))) conv=fdatasync
1+0 records in
1+0 records out
4096 bytes (4,1 kB, 4,0 KiB) copied, 0,91823 s, 4,5 kB/s

При перезаписи нулями проблемного сектора целиком диск сообщит об успехе, как в этом примере, или об ошибке. Некоторые диски в такой ситуации сообщают об ошибке при записи, ведущей к переназначению сектора, потому при ошибке стоит повторить операцию ещё раз.

Флаг fdatasync в этой команде указывает дождаться физической записи сектора на диск, а если это не удастся, то dd сообщит об ошибке. Это означает, что если команда завершилась с ошибкой и во второй раз, то, или вы ошиблись в расчётах, или диск восставлению не подлежит.

После успешной перезаписи число проблемных секторов уменьшится

$ sudo smartctl -A -f brief /dev/sde | grep -e 197 -e ID
ID# ATTRIBUTE_NAME          FLAGS    VALUE WORST THRESH FAIL RAW_VALUE
197 Current_Pending_Sector  -O--CK   200   200   000    -    9

Уменьшение счётчика Current_Pending_Sector говорит об успехе нашей операции. Если этот или другие счётчики не изменили своих значений, то это, скорее всего, означает что в расчёте координат сектора есть ошибка (был перезаписан нулями не тот сектор).

Повторять до победного

Такую же процедру следует повторить до уменьшения счётчика проблемных секторов до нуля:

  • Запускаем быстрое самотестирование тест диска.
  • Смотрим координаты проблемного сектора из лога.
  • Перезаписываем сектор нулями.
  • Сверяемся с атрибутами.

И так далее.

$ sudo smartctl -t short /dev/sde | grep ^Test
Testing has begun.
Test will complete after Sat Apr 21 07:11:50 2018

$ sudo smartctl -l selftest /dev/sde | grep -e '# 1' -e Num
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Completed: read failure       90%      6889         451407136

$ sudo dd if=/dev/zero of=/dev/sde bs=4096 count=1 seek=$((451407136/(4096/512))) conv=fdatasync
1+0 records in
1+0 records out
4096 bytes (4,1 kB, 4,0 KiB) copied, 0,709324 s, 5,8 kB/s

Не составляет особого труда заскриптовать эту операцию. Эту задачу мы оставляем читателю в качестве упражнения.

Вот и всё

Спустя какое-то время тесты будут проходить без ошибок, а счётчик битых секторов будет показывать ноль.

$ sudo smartctl -l selftest /dev/sde | grep -e '# 1' -e Num
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Completed without error       00%      6890         -

$ sudo smartctl -A -f brief /dev/sde | grep -e 197 -e ID
ID# ATTRIBUTE_NAME          FLAGS    VALUE WORST THRESH FAIL RAW_VALUE
197 Current_Pending_Sector  -O--CK   200   200   000    -    0

После исправления всех ошибок стоит запустить глубокое тестирование диска:

$ sudo smartctl -t long /dev/sde

Эта операция займёт ощутимое время. Если по её итогам найдутся ещё какие-то ошибки, то шаги выше нужно будет повторить.

Если некогда возиться...

Можно обойтись без большей части операций выше если данные на диске не представляют ценности (например, это диск из RAID 1). Для этого запускаем полный тест командой выше, дожидаемся его завершения с ошибкой, затем перезаписываем весь диск целиком нулями:

$ sudo dd if=/dev/zero of=/dev/sde bs=64k oflag=direct status=progress
1236467712 bytes (1,2 GB, 1,2 GiB) copied, 12 s, 103 MB/s

В этой команде можно обратить внимание на два флага: один - для прямо записи на диск, минуя кеш ОС, и последний: для показа процесса копирования. Последняя опция есть в GNU coreutils по крайней мере начиная с версии 8.24, а все версии, выпущенные после 2016 года. Если используется более старая версия dd, то можно или просто подождать, либо использовать трюк с pv.

Затем запускаем глубокое тестирование командой как выше, дожидаемся окончания и убеждаемся что тест завершился без ошибок.

$ sudo smartctl -l selftest /dev/sde | grep -e '# 1' -e Num
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed without error       00%      6910         -
5 of 5 failed self-tests are outdated by newer successful extended offline self-test # 1