Мысль дня

Ты никогда не решишь проблему, если будешь думать так же, как те, кто её создал.

© Альберт Эйнштейн

Восстановление данных и лечение HDD с помощью dd во FreeBSD

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

Для начала нужно немедленно отмонтировать все разделы диска во избежание потери данных. Впрочем, у вас наверняка есть их резервная копия. Ведь есть, правда? ;) Помните, до тех пор пока данные не хранятся в трёх разных местах, их вообще не существует! Нравоучения закончились, теперь можно приступать к диагностике и лечению. Перво-наперво посмотрим SMART подозрительного диска на предмет подтверждения своих подозрений. А лучше сразу запустить само-диагностику HDD, выполнив команду smartctl -t long /dev/ad6, а потом уже смотреть информацию смарта. Для этого воспользуемся утилитой smartctl. Она выдаёт много интересного, но вот самое интересное:

  1. gva# smartctl -A /dev/ad6
  2. ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE       UPDATED   WHEN_FAILED RAW_VALUE
  3.   5 Reallocated_Sector_Ct   0x0033   100   100   036    Pre-fail   Always        -       0
  4. 197 Current_Pending_Sector  0x0012   100   096   000    Old_age    Always        -       169
  5. 198 Offline_Uncorrectable   0x0010   100   096   000    Old_age    Offline       -       169

Как видим, на диске пока не появилось бэд-блоков, однако появились подозрительные блоки в количестве 169 штук, которые были выявлены в ходе самодиагностики винта. Это блоки диска, при чтении которых произошёл какой-то сбой, о котором и в самом смарте написано и можно почитать в логах. Вот отрывок /var/log/messages:

  1. Jan 14 22:03:17 gva kernel: (ada1:ahcich1:0:0:0): READ_DMA48. ACB: 25 00 2f d1 c1 40 9f 00 00 00 20 00
  2. Jan 14 22:03:17 gva kernel: (ada1:ahcich1:0:0:0): CAM status: ATA Status Error
  3. Jan 14 22:03:17 gva kernel: (ada1:ahcich1:0:0:0): ATA status: 51 (DRDY SERV ERR), error: 40 (UNC )
  4. Jan 14 22:03:17 gva kernel: (ada1:ahcich1:0:0:0): RES: 51 40 38 d1 c1 00 9f 00 00 00 00
  5. Jan 14 22:03:17 gva kernel: (ada1:ahcich1:0:0:0): Retrying command
  6. Jan 14 22:03:21 gva kernel: (ada1:ahcich1:0:0:0): READ_DMA48. ACB: 25 00 2f d1 c1 40 9f 00 00 00 20 00
  7. Jan 14 22:03:21 gva kernel: (ada1:ahcich1:0:0:0): CAM status: ATA Status Error
  8. Jan 14 22:03:21 gva kernel: (ada1:ahcich1:0:0:0): ATA status: 51 (DRDY SERV ERR), error: 40 (UNC )
  9. Jan 14 22:03:21 gva kernel: (ada1:ahcich1:0:0:0): RES: 51 40 38 d1 c1 00 9f 00 00 00 00
  10. Jan 14 22:03:21 gva kernel: (ada1:ahcich1:0:0:0): Retrying command
  11. Jan 14 22:03:25 gva kernel: (ada1:ahcich1:0:0:0): READ_DMA48. ACB: 25 00 2f d1 c1 40 9f 00 00 00 20 00
  12. Jan 14 22:03:25 gva kernel: (ada1:ahcich1:0:0:0): CAM status: ATA Status Error
  13. Jan 14 22:03:25 gva kernel: (ada1:ahcich1:0:0:0): ATA status: 51 (DRDY SERV ERR), error: 40 (UNC )
  14. Jan 14 22:03:25 gva kernel: (ada1:ahcich1:0:0:0): RES: 51 40 38 d1 c1 00 9f 00 00 00 00
  15. Jan 14 22:03:25 gva kernel: (ada1:ahcich1:0:0:0): Retrying command
  16. Jan 14 22:03:29 gva kernel: (ada1:ahcich1:0:0:0): READ_DMA48. ACB: 25 00 2f d1 c1 40 9f 00 00 00 20 00
  17. Jan 14 22:03:29 gva kernel: (ada1:ahcich1:0:0:0): CAM status: ATA Status Error
  18. Jan 14 22:03:29 gva kernel: (ada1:ahcich1:0:0:0): ATA status: 51 (DRDY SERV ERR), error: 40 (UNC )
  19. Jan 14 22:03:29 gva kernel: (ada1:ahcich1:0:0:0): RES: 51 40 38 d1 c1 00 9f 00 00 00 00
  20. Jan 14 22:03:29 gva kernel: (ada1:ahcich1:0:0:0): Retrying command
  21. Jan 14 22:03:33 gva kernel: (ada1:ahcich1:0:0:0): READ_DMA48. ACB: 25 00 2f d1 c1 40 9f 00 00 00 20 00
  22. Jan 14 22:03:33 gva kernel: (ada1:ahcich1:0:0:0): CAM status: ATA Status Error
  23. Jan 14 22:03:33 gva kernel: (ada1:ahcich1:0:0:0): ATA status: 51 (DRDY SERV ERR), error: 40 (UNC )
  24. Jan 14 22:03:33 gva kernel: (ada1:ahcich1:0:0:0): RES: 51 40 38 d1 c1 00 9f 00 00 00 00
  25. Jan 14 22:03:33 gva kernel: (ada1:ahcich1:0:0:0): Error 5, Retries exhausted
  26. Jan 14 22:03:33 gva kernel: g_vfs_done():ada1s1a[READ(offset=1372302983168, length=16384)]error = 5

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

  1. gva# dd if=/dev/ad6 of=/dev/null bs=65536 conv=noerror
  2. dd: /dev/ad6: Input/output error
  3. 20939144+0 records in
  4. 20939144+0 records out
  5. 1372267741184 bytes transferred in 13768.709737 secs (99665674 bytes/sec)
  6. dd: /dev/ad6: Input/output error
  7. dd: /dev/ad6: Input/output error
  8. 20939630+0 records in
  9. 20939630+0 records out
  10. 1372301295616 bytes transferred in 14014.402646 secs (97920784 bytes/sec)
  11. dd: /dev/ad6: Input/output error
  12. 22892776+1 records in
  13. 22892776+1 records out
  14. 1500300992512 bytes transferred in 15608.577596 secs (96120289 bytes/sec)

Как видно из листинга, с помощью утилиты dd мы спровоцировали не читаемые блоки заявить о себе. Параметр conv=noerror говорит программе, что следует продолжать работу не смотря на встреченную ошибку. Хотя размер блоков на диске 512 байт, для ускорения работы программы был задан размер блока 65536 байт (bs=65536). Можно задать и другой размер, который покажет наибольшую скорость в вашем случае. Обычно оптимальный размер блока подбирается экспериментальным путём. Таким образом мы выявим не сам сбойный 512-байтовый блок, а группу блоков, среди которых и находится сбойный. При необходимости можно уточнить поиск с меньшим размером блоков, благо, где искать мы уже знаем, так что много времени это не займёт. Но пока такой необходимости нет. Зато есть необходимость выявить номер 512-байтового блока, а точнее его LBA-адрес — именно он понадобится в дальнейшем. Будем вычислять его по формуле:


бн = Бн * Бр / бр - 1
бн = 20939144 * 65536 / 512 - 1 = 2680210431


где

  • бн — номер искомого 512-байтового блока диска (2680210431),
  • Бн — номер 65536-байтового блока, который выдала dd (20939144),
  • Бр — размер блока, который выдала dd (65536),
  • бр — размер блока, номер которого мы ищем (512).
  • - 1 — смещение количества блоков, относительно нумерации LBA.

Таким образом мы вычисляем номера физических блоков для всех сбойных блоков, номера которых нам указал dd. Я не знаю номера это именно не прочитанных блоков или последних прочитанных, поэтому, на всякий случай, к вычисленным номерам добавлю ещё набор этих же номеров, увеличенных на единицу. Кроме того самый последний номер блока в выдаче dd указывает не на сбойный блок, а на последний, поэтому его мы в расчёт не берём.

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

  1. gva# bsdlabel /dev/ad6s1
  2. # /dev/ad6s1:
  3. 8 partitions:
  4. #          size     offset    fstype   [fsize bsize bps/cpg]
  5.   a: 2930277089         16    unused        0     0
  6.   c: 2930277105          0    unused        0     0     # "raw" part, don't edit

Чтобы понять, в каком разделе находится блок, нужно в колонке offset найти строку с меньшим наиболее близким к номеру блока числом. К примеру, если блок имеет номер 2680210431, значит наиболее близкое к нему число в колонке offset — 16, что означает, что блок находится в разделе a. И это не удивительно, поскольку в примере всего один раздел. :) Полный путь к разделу — /dev/ad6s1a, его мы и монтируем. Сделаем это в режиме чтения во избежание всяческих неожиданностей:

  1. gva# mount -o ro -t ufs /dev/ad6s1a /mnt

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

  1. gva# fsdb -r /dev/ad6s1a
  2. ** /dev/ad6s1a (NO WRITE)
  3. Examining file system `/dev/ad6s1a'
  4. Last Mounted on /usr/home/disk
  5. current inode: directory
  6. I=2 MODE=40755 SIZE=512
  7.         BTIME=Jan 8 00:52:13 2011 [0 nsec]
  8.         MTIME=Jul 16 23:39:29 2013 [0 nsec]
  9.         CTIME=Jul 16 23:39:29 2013 [0 nsec]
  10.         ATIME=Jan 12 03:13:30 2014 [0 nsec]
  11. OWNER=gva GRP=gva LINKCNT=10 FLAGS=0 BLKCNT=4 GEN=3e80ec28
  12. fsdb (inum: 2)> findblk 2680210432
  13. 2680210432: data block of inode 165523470
  14. fsdb (inum: 2)> findblk 2680210560
  15. 2680210560: data block of inode 165523470
  16. fsdb (inum: 2)> findblk 2680272640
  17. 2680211968: data block of inode 165523470
  18. fsdb (inum: 2)> findblk 2680272768
  19. 2680212096: data block of inode 165523470
  20. fsdb (inum: 2)> q

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

Теперь, когда известен инод повреждённого файла, можно найти путь к файлу с помощью программы find:

  1. gva# find /mnt -inum 165523470
  2. /mnt/private/xxx/porno/video.file

Файл найден и теперь приступим к его восстановлению. В принципе, файл уже повреждён и до первоначального вида он восстановлен не будет. Но хоть какие-то его ошмётки всё-же восстановить удастся. Для этого снова воспользуемся утилитой dd.

  1. gva# dd if=/mnt/private/xxx/porno/video.file of=/usr/home/gva/video.file bs=512 conv=noerror,sync
  2. dd: /mnt/private/xxx/porno/video.file: Input/output error
  3. 4938784+0 records in
  4. 4938784+0 records out
  5. 2528657408 bytes transferred in 255.684458 secs (9889758 bytes/sec)
  6. dd: /mnt/private/xxx/porno/video.file: Input/output error
  7. dd: /mnt/private/xxx/porno/video.file: Input/output error
  8. 4938785+0 records in
  9. 4938785+0 records out
  10. 2528657920 bytes transferred in 316.122989 secs (7998969 bytes/sec)
  11. dd: /mnt/private/xxx/porno/video.file: Input/output error
  12. 70658472+0 records in
  13. 70658472+0 records out
  14. 36177137664 bytes transferred in 15758.371732 secs (2295741 bytes/sec)

Как понятно из листинга, мы скопировали повреждённый файл в другое место, игнорируя ошибки чтения (noerror) и заполняя непрочитанные места нулями (sync). Так же был выбран самый маленький размер блока в 512 байт (bs=512) для более точного выявления сбойных секторов и более полной копии.

Если вдруг при копировании не возникло ошибок, значит был неправильно рассчитан сбойный физический блок. Требуется более подробный поиск. Его снова осуществим утилитой dd, но теперь укажем ей конкретные блоки для чтения. Для этого воспользуемся такой формулой:


бк = Бр / бр
бк = 65536 / 512 = 128


где

  • бк — количество 512-байтовых блоков, в которых следует уточнить поиск (128),
  • Бр — размер блока, который выдала dd при грубом поиске (65536),
  • бр — размер блока, количество которых мы ищем (512).

Теперь можно уточнить поиск вот такой командой:

  1. gva# dd if=/dev/ad6 of=/dev/null bs=65536 conv=noerror count=128

Опция count подскажет dd сколько блоков прочитать. В результате мы получим уже точные номера физических блоков, в которых определим иноды, по которым определим имена файлов, которые скопируем в безопасное место.

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

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

  1. gva# umount /mnt
  2. gva# fsck -y -f -t ufs /dev/ad6s1a
  3. gva# mount -t ufs /dev/ad6s1a /mnt

Так-как затирать не читаемые блоки по номерам лениво, а файл с ними всего один, будем затирать повреждённый файл. dd сама разберётся что к чему:

  1. gva# dd if=/dev/zero of=/mnt/private/xxx/porno/video.file bs=512
  2. /mnt: write failed, filesystem is full
  3. dd: /mnt/private/xxx/porno/video.file: No space left on device
  4. 644384545+0 records in
  5. 644384544+0 records out
  6. 329924886528 bytes transferred in 5154.050639 secs (64012737 bytes/sec)

Судя по листингам, восстанавливаемый файл при копировании имел размер 36177137664 байт, а после заполнения нулями распух в десять раз — до 329924886528 байт… Это вовсе не потому, что я забыл указать опцию count. Дело в том, что при записи в файл совсем не обязательно данные должны писаться в те же блоки, где была предыдущая версия файла. Так что, плохие блоки могли бы и не быть затёртыми. Поэтому нужно перезаписать нулями и свободное место в профилактических целях. Благо у меня его не много. Если свободного места много, следует всё же заморочиться с затиранием конкретных блоков по номерам, быстрее будет. Устройство /dev/zero — файл бесконечного размера и его копирование в другой файл приведёт к тому, что целевой файл будет расти бесконечно, пока не исчерпается свободное место на диске. Это и произошло. Профилактические работы прошли успешно. :)

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

  1. gva# dd if=/mnt/private/xxx/porno/video.file of=/dev/null bs=65536 conv=noerror
  2. 5034254+1 records in
  3. 5034254+1 records out
  4. 329924886528 bytes transferred in 3758.004039 secs (87792584 bytes/sec)

Как видим, ошибок больше не возникает. Это означает, что лечение помогло. Заглянем в SMART.

  1. gva# smartctl -A /dev/ad6
  2. ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE       UPDATED   WHEN_FAILED RAW_VALUE
  3.   5 Reallocated_Sector_Ct   0x0033   100   100   036    Pre-fail   Always        -       0
  4. 197 Current_Pending_Sector  0x0012   100   096   000    Old_age    Always        -       0
  5. 198 Offline_Uncorrectable   0x0010   100   096   000    Old_age    Offline       -       0

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

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

Примечания

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

Чтобы не выполнять предыдущую рекомендацию, напишите скрипт, который всё это автоматизирует. ;)

Если вас что-то смутило в процессе восстановления, не поленитесь перепроверить диск ещё раз.

Название восстанавливаемого файла в статье изменено с целью поисковой оптимизации. :)

Комментарии

Архив

  1. 2022
    1. август
      1. Сайт восстановил работу
  2. 2021
    1. январь
      1. Kubntu Linux Opera: не работает видео
      2. Не обновляется FreeBSD со старой версии, ошибка «Cowardly»
  3. 2015
    1. сентябрь
      1. Ворона позирует
      2. Как сделать новость из ничего
    2. август
      1. Моё первое селфи
      2. Спам от Майкрософта
    3. июнь
      1. 1000
      2. Сельдерей
    4. май
      1. Сирень зацвела
      2. Ночное солнце
    5. апрель
      1. Ить-ить-ить-ить
    6. январь
      1. Разрыв VPN-соединения при нагрузке
  4. 2014
    1. декабрь
      1. Суровый холостяцкий смайл
    2. ноябрь
      1. Перечень IP–адресов в IPFW
      2. Длина строки правила IPFW во FreeBSD
    3. октябрь
      1. Свет в конце тоннеля
      2. Огоньки
    4. сентябрь
      1. Попал под раздачу
      2. Собираю чемодан
      3. Чьи–то уши торчат над холмом
      4. Увеличение свободного места на телефоне LG P500 Optimus One
      5. Места нет, но место есть
      6. Пластиковая Африка
    5. август
      1. Кошка в ванной
      2. В PHP функция возвращает NULL вместо данных
      3. В Оперу вернулись закладки!
      4. Убираем рамки вокруг экрана
      5. Загадочный дом
    6. июль
      1. Божьи коровы и прочие твари
      2. Человек собаке друг
    7. июнь
      1. Котоворот
      2. Древесная живность
      3. Шампунь
    8. апрель
      1. Еда
      2. Смайл
      3. Одуванчики
      4. Шашлычная история
      5. Кровавая Луна
      6. Старый пень
      7. Бриллиант
    9. март
      1. Лисапет наносит ответный удар
      2. Весна пришла или Лисапет 2
      3. Лисапет
      4. Запах весны
    10. январь
      1. Восстановление данных и лечение HDD с помощью dd во FreeBSD
      2. Новогодняя ёлка 2014
  5. 2013
    1. декабрь
      1. Ледяные короны
      2. Луна
    2. ноябрь
      1. Тарас Шевченко - І виріс я на чужині
      2. Увеличение раздела диска во FreeBSD
      3. Одна особенность работы freebsd-update
    3. октябрь
      1. Обзор телефона LG P500 Optimus One
      2. Призрачная трава
    4. сентябрь
      1. Подарок
      2. Мокрая, как мышь
      3. Пластиковое нашествие
      4. Использование переменных в шаблоне REGEXP
    5. август
      1. Файл конфигурации MPlayer
      2. Микширование аудиоканалов в MPlayer
      3. Спамеры тоже люди
      4. Распятый мексиканец
      5. Подсолнуховое море
      6. Подложили свинью
      7. Глобальное обновление
    6. апрель
      1. Завтрак
    7. март
      1. Пересылка всей почты другому пользователю
    8. январь
      1. Задолбали ссылки
      2. Xerox Phaser 6000 против Linux x86_64
  6. 2012
    1. декабрь
      1. Изогнутый айфон
    2. октябрь
      1. Собака бывает кусачей
    3. июнь
      1. Поддержка Monkey's Audio в Linux
      2. Поддержка чересстрочного VC–1 в 64–битном линуксе
      3. Моя кошка ловит мушку
      4. Диапазон IP–адресов в IPFW
    4. май
      1. Задушила меня жаба
    5. февраль
      1. Как закрыть и открыть порты во FreeBSD
    6. январь
      1. Обмен данными между скриптами shell и PHP
  7. 2011
    1. декабрь
      1. Пасхальное яйцо в Muon
    2. ноябрь
      1. Windows XP и NFS
      2. От природы не уйдёшь
    3. октябрь
      1. Swappiness
      2. Бодания с Ubuntu 11.10
      3. Подпишись, не будь упырём!
      4. Установка Ubuntu на RAID 0
      5. Кодинг на сон грядущий
    4. сентябрь
      1. Аську! Больше асек!
      2. Разбитое сердце
      3. Привет из параллельной вселенной
      4. Карточный домик
    5. август
      1. Обзор ноутбука ASUS 1215B
    6. июль
      1. Работает–ли компьютер без видеокарты?
    7. июнь
      1. Частушка на злободневную тему
    8. май
      1. Как–то в прошлой жизни…
    9. март
      1. 8 марта
      2. Планшеты
      3. Цветы
  8. 2010
    1. декабрь
      1. Типа динамическая маршрутизация
    2. ноябрь
      1. Закрытые порты или бодания с FreeBSD
      2. Мой новый старый комп
      3. Глюк с кодировкой в MySQL
      4. Ёжик в тумане
    3. август
      1. Кукурузное счастье
    4. май
      1. День победы 2010
      2. Не пора–ли менять браузер?
      3. Оптимизация дисковой системы в Linux
  9. 2009
    1. ноябрь
      1. Перенос системы на другой жёсткий диск
    2. август
      1. Выбор HTML–редактора под линукс
      2. Сходим в оперу?
      3. Линукс
      4. 4:00
    3. июль
      1. Семантичеcкие URL
      2. Браузеры под раздачей
      3. Графика
      4. Достало!
      5. Где–то там море
      6. С чего всё началось
      7. Начало

Поддержка проекта

Поделиться

Комментировать