Всему рано или поздно приходит конец, как и SD карточкам. И по закону подлости это случается тогда, когда этого МЕНЬШЕ всего ожидаешь.
Итак, дача. Вдали от шума большого города старый китайский телефон нес службу вместо модема раздавая интернет поверх OpenVPN соединения. Сотовые операторы либо просят много за статический прямой IP адрес, либо не имеют вообще такой услуги, так что я пользуюсь OpenVPN, чтобы узнать что у меня творится на даче. Ну там, пара камер, которые следят вот за этими ребятами:
На самом телефоне крутится android, внутри которого живет Debian, а внутри него OpenVPN. И все это живет на SD карте, которая изволила накрыться медным тазиком. И в какой-то момент я понял, что свежего бэкапа у меня под рукой не было. Вернее не было бэкапа именно на конфиг OpenVPN с которым я игрался в течение долгих месяцев, подбирая оптимальные параметры для работы поверх 4g сети. Так что эта заметка будет как раз о восстановлении данных с SD карты, просыпавшей бэдами.
Как умирают SD карты
SD карточки могут дохнуть самыми различными и странными способами. Вот мой список, быть может не самый полный, но зато все выдернуто из собственной практики:
- Переходят в перманентный read-only режим. Самый безобидный способ умереть, не правда ли?
- Часть блоков перестают читаться (мой случай), до тех пор пока в них что-то не запишешь, чтобы переназначились <- Мой случай
- Некоторые карты тупо перестают определяться (их не видят ни кард-ридеры, ни mmc-хосты на одноплатниках) и вообще не подают признаки жизни. (Например, если под эпоксидкой обломился провод). Иногда с них можно все еще вытащить данные в SPI режиме, иногда нет
Итак, вот хитрый план.
Часть блоков карты не читаются, после монтирования на телефоне там нет добрых 3/4 файлов. Прогонять e2fsck на больной карте без бэкапа я не рискнул, потому действовать будем так:
- Снимаем образ с карты при помощи dd/ddrescue
- Примонтируем копию образа через loop-mount и прогоняем e2fsck
- Если не поможет, придумаем что-то еще 😉
- …
- PROFIT
В конце концов нужен тоько конфиг OpenVPN и пара скриптов, так что шансы были неплохие.
Почему не следует использовать кардридер
Кардридеры это весьма сложные устройства, которые превращают SDIO шину (а может и еще несколько интерфейсов, типа Memory Stick) в USB стореджи. И внутри них происходит много всего. Они чаще всего построены на базе микроконтроллеров со своей прошивкой, которая как-то обрабатывает ошибки, буферизирует запросы, и делает еще много чего разного. А мы об их внутренностях не знаем практически ничего. Когда я воткнул эту карту в кардридер — тот просто «отваливался» как только обнаруживал «плохой» блок. Другой же кардридер «повисал» на несколько минут каждый раз, когда натыкался на плохой блок.
Урок усвоен: Кард-ридеры в большинстве своем плохо работают с плохими карточками, так как никто из разработчиков не запаривался особенно с обработкой ошибок и тестировании на дохлых и полудохлых картах.
До кучи, в большинстве ноутбуков кардридер подключен внутри по usb, потому принципиально от них ничем не отличается.
В любом случае, чтобы снять образ с карты необходимо было избавиться от этой аппаратной прослойки. Быстро и решительно. Распберри или любого другой одноплатник с SD слотом для этого отлично подойдет, если конечно может загрузиться откуда-то кроме этого слота. SD у них, как правило, подключен через подсистему ядра mmc_host, в которой все хорошо с обработкой ошибок.
Но как назло одноплатника в тот вечер под рукой не было. Но был телефон на android с правами root, и фиговое соединение с интернетом 😉
ddrescue спешит на помощь
ARM кросс-тулчейн у меня под рукой был, но не было Android NDK. Последний было крайне долго качать да и не нужно. Потому я взял архив ddrescue, скомпилировал как статический бинарник (У Android отличная от большинства дистрибутивов линукса libc, потому с динамической линковкой ничего бы не взлетело ) и закинул на устройство.
Вот краткое описание процедуры:
wget http://mirror.tochlab.net/pub/gnu/ddrescue/ddrescue-1.22.tar.lz tar vxpf ddrescue-1.22.tar.lz cd ddrescue-1.22 nano configure #Or whatever your editor of choice is. |
Теперь открываем скрипт configure из архива ddrescueлюбимым редактором и правим в самом начале первые строки. Должно получиться что-то вроде такого.
CXX=arm-rcm-linux-gnueabihf-g++ CPPFLAGS= CXXFLAGS='-Wall -W -O2 -static' |
Меняем arm-rcm-linux-gnueabihf на префикс своего кросс-компилятора, который, очевидно, должен быть в PATH.
После запускаем:
./configure make adb push ddrescue /sdcard adb shell |
Теперь на телефоне запускаем:
su busybox mount -o remount,rw /system cp /sdcard/ddrescue /system/bin/ chmod 755 /system/bin/ddrescue ddrescue |
Если видим краткую справку, то ура, ddrescue заработал. Можно приступать к активным действиям по спасению данных. Главное убедиться, что внутренней памяти телефона хватит на то, чтобы сохранить образ SD.
Втыкаем «больную» SD карточку, запускаем ddrescue. По умолчанию оный почистит все нечитаемые блоки на карте. Если этого не хочется делать, то надо указать один из этих флажков:
-n, --no-scrape skip the scraping phase -N, --no-trim skip the trimming phase
Хотя у меня вполне заработало и с дефолтными параметрами, вышло как-то так:
root@OUKITEL:/ # ddrescue /dev/block/mmcblk1 /sdcard/recovered.img /sdcard/recovered.map GNU ddrescue 1.22 ipos: 2253 MB, non-trimmed: 0 B, current rate: 0 B/s opos: 2253 MB, non-scraped: 0 B, average rate: 2967 kB/s non-tried: 0 B, bad-sector: 1556 kB, error rate: 512 B/s rescued: 3962 MB, bad areas: 380, run time: 22m 14s pct rescued: 99.96%, read errors: 3320, remaining time: 0s time since last successful read: 1s Finished root@OUKITEL:/ #
Из вышеуказанного видно, что 0.04% бэдов из 4GB данных уже хватило, чтобы свести с ума все найденные в хозяйстве кардридеры.
После спасения данных и перекачки из на комп, я подключил образ через losetup и запустил наконец-то e2fsck.
0 ✓ necromant @ sylwer ~ $ sudo losetup NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE /dev/loop0 0 0 1 0 /var/lib/docker/devicemapper/devicemapper/data /dev/loop1 0 0 1 0 /var/lib/docker/devicemapper/devicemapper/metadata /dev/loop2 0 0 0 0 /home/necromant/arvale.img 0 ✓ necromant @ sylwer ~ $ sudo e2fsck /dev/loop2p2 e2fsck 1.42.12 (29-Aug-2014) arvale: recovering journal arvale: clean, 15687/235712 files, 149756/941824 blocks 0 ✓ necromant @ sylwer ~ $ |
WTF??? Оный был уверен, что файловая система чиста. Пришлось добавить флажок -f вместе с -y и -v за компанию. После этого e2fsck раскочегарился, отыскал кучу ошибок и даже исправил их.
0 ✓ necromant @ sylwer ~ $ sudo e2fsck --y -f -v /dev/loop2p2 |
Полный лог здесь не привожу, уж больно жирный.
От /usr/bin осталось мало чего, что-то потерлось и из /usr. Но /etc/ со всеми конфигами был жив, и OpenVPN скрипты тоже оказались живы, так что вытащив все это добро с карточки и закинув поверх имевшейся резервной копии все вновь встало на свои места. Успех.