Pull to refresh

Сказ о том, как поднимался упавший fake RAID 0

Reading time 8 min
Views 6.1K
… или, точнее говоря, — как я восстанавливал данные с положеного мною nVidia RAID 0.

Завязка

Собственно, прелюдия. Живёт у меня дома комп, контроллер IDE у него реализован в nVidia MCP65. У данного контроллера есть опция, превращающая его в RAID-контроллер с поддержкой RAID 0 и RAID 1. В моём случае — RAID 0 из двух Samsung'ов по 250 Гб каждый. Вобщем, ничего сверхестественного, набортным RAID'ом удивить сложно было уже 5 лет назад, не то что в наше время. Если кого-то заинтересовала приставка «fake» — так принято называть полуаппаратные реализации RAID. Разберём на пальцах.

В таких ОС как Windows 2003 или, скажем, Debian Lenny GNU Linux возможно реализовать программный RAID-массив средствами ОС. В первом случае это будет конвертация дисков в так называемые динамические диски и манипуляция с имеющимися на них разделами, во втором — например, использование LVM-утилит (вобщем-то, также «как бы» динамические диски). Отсутсвие необходимости в дополнительных контроллерах, использование мощностей процессора и оперативной памяти для функционирования и т. д.

С другого краю села — аппаратные реализации, скажем, Intel SRCSATAWB: до 16 SATA-накопителей, уровень RAID вплоть до 60, затраты на контроллер — около 15000 руб… По сравнению с первым случаем — снятие нагрузки с процессора и оперативки, аппаратная реализация, затраты на приобретение девайса.

Понятия «аппаратный RAID» и «программный RAID» — контрарны, т. е. противоположны друг-другу, однако позволяют существовать промежуточным вариантам, общее имя которым — «полуаппаратный RAID», или же «fake RAID». Как бы часть работы такого RAID'а делегируется драйверу в ОС. По факту — делегируется чуть ли не вся работа: fake RAID'у остаётся только прикидываться ветошью (RAID'ом), говоря операционке, скажем, что в составе ПК не 2 диска, а 1, зато в 2 раза больше, да требовать дрова для работы, да заниматься базовым мониторингом дисков, коварно выкидывая из состава RAID-массива диски (впрочем, это входит и в обязанность аппаратного RAID); остальным делом — работой по реализации функционала RAID 0, RAID 1, RAID 5 (нужное подчеркнуть) занимается драйвер RAID-контроллера. Кстати, это ИМХО и есть наиглавнейший опозновательный признак fake RAID — если такое устройство не способно определиться и заработать в любой системе с использованием стандартного драйвера IDE, представившись ему единым накопителем, но функционирует исключительно с использованием своего личного драйвера — fake, «подделка», рога и копыта…

Э-э-э… Так вот, прелюдия. =) Заимев в своё распоряжение с моего родного техотдела оставленный клиентом «нерабочий» диск WD на 400 Гб, я принял в который раз решение поглядеть на какой-нибудь свежий Linux-дистрибутив. Выбор на сей раз пал на Ubuntu 9.04 x86. В уже настроенной системе я сунулся попытаться получить доступ к моему fake RAID'у. Название утилиты, которой пользовался в прошлый раз год назад я не вспомнил (впрочем, позднее таки вспомнил — dmraid), но за пару минут нагуглил другую софтину — mdadm. В убунтятнике софтина нашлась, установил, стал разбираться, наткнулся где-то в гугле на то, что дескать «неплохо бы восстановить пространство, зарезервированое накопителем для HPA». Сказано — сделано (с помощью Victoria). Как оказалось — зря. =)

Вот за что люблю RAID — так это за определённость. Уж коль выпал диск из массива RAID 0, так максимум что можно попытаться сделать — проверить, есть ли контакт винта с питанием и контроллером / материнской платой. Так и у меня: диски подключены, однако в RAID-BIOS'е в составе массива только один из них. Варианты действий — удалить массив.

В оконцовке завязки я имел: удалённый массив RAID 0, отключеный RAID-BIOS, два исправных винта по 250 гигов с имеющимися на них данными (т. е. размазанными по обеим винтам), загрузочный винт на 400 Гб с установленной на него Ubuntu, а так же желание восстановить нажитое «непосильным трудом».

Экшн

Принципиально в восстановлении данных с RAID 0 ничего сложного нет, если причиной деградации массива является не поломка одного из накопителей, а лишь расформирование массива вручную (например, при содействии человеческого фактора, как в моём случае). Все данные на месте, пожалуй что за исключением самой информации о массиве (которая, впрочем, в цифровом виде для восстановления данных с массива уже не нужна); главное — вспомнить, как организуется RAID 0, что такое MBR и Partition Table, собрать данные для восстановления структуры массива и слить с дисков данные.

Алгоритм восстановления данных в моём случае оказался следующим:
1. Узнать, какой из 2-х дисков является первым в массиве, а который — вторым.
2. Узнать размер страйпа в массиве.
3. Воспользоваться ПО, которое примет эти данные в качестве исходных, а на выходе даст восстановленные данные (или даст более-менее простой вариант доступа к ним).
4. Получить доступ к восстановленным данным.
Причём всё это сделать желательно в среде Linux — for fun. =)

Разберём пункты по порядку.

1.
Данные в массиве RAID 0 из двух накопителей поочерёдно записываются на каждый из дисков равными на всём протяжении массива блоками (страйпами).

К примеру:
первые X Кб — на диск 1 по LBA-смещению Y (для простоты можем взять, что Y = 0 — это будет 0 сектор диска; я сливал конкретный раздел диска, и Y получился равным номеру начального сектора этого раздела),
следующие X Кб — на диск 2 по тому же смещению Y,
следующие X Кб — на диск 1 по смещению Y + 1 * (X / 512),
следующие X Кб — на диск 2 по смещению Y + 1 * (X / 512),
следующие X Кб — на диск 1 по смещению Y + 2 * (X / 512),
следующие X Кб — на диск 2 по смещению Y + 2 * (X / 512),
следующие X Кб — на диск 1 по смещению Y + 3 * (X / 512),
следующие X Кб — на диск 2 по смещению Y + 3 * (X / 512),


Определить, какой из дисков является первым можно поглядев на нулевой сектор обоих дисков в 16-ричном редакторе: в этом случае в нулевом секторе первого диска в массиве будет находится MBR массива со всеми её MBR-очьими признаками: сигнатура «55AA» по смещению 510 (1FEh) байт нулевого сектора, текст а-ля «Invalid partition table»; тогда как в нулевом секторе второго диска ничего из этого почти наверняка не будет.

sudo dd if=/dev/sdb of=/home/f/mbr01 count=1
sudo dd if=/dev/sdc of=/home/f/mbr02 count=1
ghex2 /home/f/mbr01
ghex2 /home/f/mbr02


В моём случае первым диском массива оказался /dev/sdb, вторым — /dev/sdc. Ниже этот порядок устройств будет использоваться.

2.
Я смутно помнил, что размер страйпа моего массива был равен 64 Кб. Тем не менее, нужна была проверка.

Проверить это не слишком сложно, однако более хлопотно, нежели п. 1. Для анализа потребовались дампы, аналогичные сделаным в п. 1, только более крупного объёма — по 32 Мб (65536 секторов).

sudo dd if=/dev/sdb of=/home/f/dump01 count=65536
sudo dd if=/dev/sdc of=/home/f/dump02 count=65536
ghex2 /home/f/dump01
ghex2 /home/f/dump02


Есть смысл начать с максимального размера страйпа — как правило это 64 Кб. Переведя число 65536 в 16-ричную систему счисления (в 16-ричном редакторе смещение отображается естественно в ней) получаем 10000h. Перемещаясь по смещениям, кратным этому числу, мы убеждаемся в следующем: данные, непосредственно выше этих смещений, выглядят визуально сильно иначе, чем данные, оказывающиеся непосредственно ниже этих смещений.
image
Разумеется, на данных смещениях могут попадаться (особенно в начале дампа) и такие картины, когда всё забито нулями, либо же наоборот, энтропия выше и ниже смещения данных настолько высока, что сделать вывод о том, является ли данное смещение началом нового страйпа невозможно.
image
В hex-редакторе следует найти ту область данных по смещению, кратному размеру страйпа (10000h), в которой можно будет сориентироваться и сделать вывод.

Получив подтверждение того, что на смещениях, кратных 10000h мы наблюдаем резкие переходы, как будто бы часть данных пропущена (а при данном смещении так оно и будет практически наверняка), следующим шагом мы выдвигаем предположение, что размер страйпа у нас вообще-то мог бы быть и в 2 раза меньше — 32 Кб или 8000h. Повторяем выше приведённую операцию при данном размере смещения. Если находим несколько смещений, у которых наблюдается тот же эффект — смена паттерна данных на границе смещения, — делаем вывод, что размер страйпа таки действительно может быть 32 Кб… Уменьшаем предполагаемый размер страйпа в 2 раза — и повторяем… Останавливаемся же мы в том случае, когда мы натыкаемся хотя бы на одно подтверждение того факта, что при текущем предполагаемом размере страйпа хотя бы по одному смещению мы нашли данные, для которых с большой долей вероятности часть под смещением является продолжением части над смещением, другими словами — мы видим явно непрерывную последовательность данных. Особо хорошо это заметно в том случае, когда мы наткнулись на содержимое текстового файла. В этом случае мы отказываемся от текущего размера страйпа и возвращаемся к варианту предыдущего размера страйпа, большего в 2 раза, и особо тщательно проверяем этот вариант: текстовый документ или явно последовательная информация (разумеется, последовательности из нулей это не касается, так же как и хаотичного мусора) на границе смещений обнаружиться не должна, иначе следует увеличить размер страйпа ещё в 2 раза.

В моём случае размер страйпа был подтверждён — 64 Кб, или 128 секторов. Ниже это число будет использоваться.

3.
Поиск в гугле готового решения ничего не дал ни по софту для Windows, ни по софту для Linux: может плохо искал, может был слишком пьян (что, впрочем, то же самое). Соответственно я решил, что самым подходящим способом будет скомпоновать единый файл-образ диска (массива), а уж там как-нибудь разберусь. Носитель для записи файл-образа я, сотрудник техотдела, одолжил на складе фирмы, где имею честь работать, у товарища tozx — привет тебе, дружище! =)

Пришлось вспоминать свой небогатый навык скриптинга на Perl'е:

#! /usr/bin/perl

use strict;
use warnings;

my $innnum = 0; # переменная-счётчик, номер сектора для считывания с дисков из ex-массива
my $outnum = 0; # переменная-счётчик, номер сектора для записи в файл-образ
my $multipler = 128; # размер страйпа в секторах; сколько секторов читаем за раз
my $lbasize = 488395008; # размер физического диска из ex-массива в секторах, округлённый по значению размера страйпа (кратный 128 в меньшую сторону)
my $timert1 = 0; # необязательный вспомогательный счётчик
my $timert2 = 0; # ещё один необязательный счётчик

while ($lbasize - $innnum >= $multipler) # пока не прочитали весь диск
{
system ("dd if=/dev/sdb of=/media/recovery/image count=$multipler skip=$innnum seek=$outnum 2> /dev/null");
$outnum += $multipler;
system ("dd if=/dev/sdc of=/media/recovery/image count=$multipler skip=$innnum seek=$outnum 2> /dev/null");
$outnum += $multipler;
$innnum += $multipler;
$timert1++;
$timert2++;
if ($timert2 == 256) { # выводим время от времени прогресс нашей операции
$timert2 = 0;
print ("$innnum OF $lbasize\n");
}
if ($timert1 == 16192) { # останавливаем время от времени операцию, давая дискам остыть
$timert1 = 0;
print ("So hot! Sleeping for 33 sec...\n");
sleep (33);
}
}
print ("DONE.\n");


Размер одного диска в секторах можно узнать с помощью Victoria, либо посмотрев на наклейку на диске (LBA).

4.
Привожу рецепт подмонтирования образа HDD, которым я воспользовался, с небольшими изменениями:

sudo losetup /dev/loop0 /media/recovery/image
sudo fdisk -lu /dev/loop0


Получаем на терминал список найденых в образе разделов. В моём случае:

Диск /dev/loop0: 500.1 ГБ, 500116488192 байт
255 heads, 63 sectors/track, 60802 cylinders, всего 976790016 секторов
Units = секторы of 1 * 512 = 512 bytes
Disk identifier: 0xdf39af97

Устр-во Загр Начало Конец Блоки Id Система
/dev/loop0p1 * 2048 83891429 41944691 7 HPFS/NTFS
/dev/loop0p2 83892224 488395055 202251416 7 HPFS/NTFS


Для нужного раздела (в моём случае меня интересовал второй раздел) берём число из столбца «Начало», умножаем его на размер сектора (512), получая 42952818688, и вбиваем команду:

sudo losetup -o42952818688 /dev/loop1 /dev/loop0


Далее остаётся подмонтировать устройство /dev/loop1 как раздел с соответствующей файловой системой (не забыв заранее создать каталог для монтирования):

sudo mkdir /media/yuppi
sudo mount -t ntfs -o ro /dev/loop1 /media/yuppi


Послесловие


«На всё про всё» потрачено четыре вечера. Само копирование 500 Гб (по 250 Гб с диска) заняло почти сутки — на быстродействии сказывается то, что скрипт читает по 64 Кб с диска, для каждого чтения требуется вызывать системную команду. Скорость считывания составляла ~15 Мб/сек. Для ускорения операции перловый скрипт можно улучшить, добавив 2 массива-буфера размером в несколько мегабайт каждый (по одному на каждый физический диск), в который считывать данные с дисков, а уже после считывания разбирать данные с этих буферов в файл-образ. Но было лень тратить время — у меня реализация этой возможности в перле заняла бы больше времени, нежели медленное копирование тем способом, который я описал.

Следует ли упоминать, что ничего не пропало… =)

Успехов.
Tags:
Hubs:
+57
Comments 42
Comments Comments 42

Articles