Восстановление RAID 10 массива

Поломался однажды RAID10 массив. Как обычно, все произошло внезапно: однажды перезапустили сервер, и получили «(что-то там) … not found». С поврежденным (degraded) массивом сервер стартовать отказался. Массив работал на полусофтовом-полухардварном псевдоконтроллере материнской платы Intel. Физически все жесткие диски оказались здоровыми, хотя для нижеописанного это не критично.

Далее следует описание восстановления информации с массива из серии «с помощью швейцарского перочинного ножа», т.е. без применения изощрённых средств восстановления. Если конкретно, понадобится компьютер с любым *nix и интерпретатором python3. Можно, конечно, воспользоваться программами вроде R-Studio, однако, по-моему это слишком простая задача для них.

Итак, делаем следующее. Открываем в бинарном редакторе все 3 диска и выбираем из них пару striped дисков, т.е. на которых чередующимися блоками составляют весь массив (как RAID 0). Делаем образы обоих жестких дисков в файлы с помощью замечательной утилиты dd по типу

dd if=/dev/sdX of=/tmp/vY.hdd bs=32M

В итоге получаем два больших файла v0.hdd, v1.hdd. С самими HDD далее не работаем.

Снова открываем с помощью бинарного редактора какой-нибудь из образов и определяем размер stripe-элемента (chunk size). Сделать это не сложно даже «невооружённым взглядом»: ищется несколько этих кусков с чёткими границами. Легче всего это сделать на файле с вразумительным содержимом, или даже на любом — если свободное место (почти) заполнено нулями. В нашем случае размер stripe оказался 64K. При этом на одном из образов в начальном секторе сразу обнаружился MBR, а во втором нечто совсем не похожее на MBR. Так стал понятен порядок stripe от разных дисков.

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

#!/usr/bin/python3
"""RAID0 recovery utility."""

dev0 = '/tmp/v1.hdd'
dev1 = '/tmp/v0.hdd'

DEV_OUT = '/tmp/out.hdd'

# stripe 64k
STRIPE = 64 * 1024
	
MAX_COUNT = 500 * 2**29 // STRIPE MAX COUNT

if __name__ == '__main__':
	f0 = open(dev0, 'rb')
	f1 = open(dev1, 'rb')

	f_out = open(DEV_OUT, 'wb')

	offset = 0
	while offset < MAX_COUNT:
		d0 = f0.read(STRIPE)
		d1 = f1.read(STRIPE)

		if d0 and d1:
			f_out.write(d0)
			f_out.write(d1)

			offset += 1
		else:
			print("One of input files are finished. Read: {0} blocks.".format(offset))
			break

	f0.close()
	f1.close()
	f_out.close()

Запускаем скрипт, оставляем его на ~10 минут и на выходе получаем полный образ диска. Разделы из него успешно монтируются самостоятельно или могут быть (всем образом) записаны на физический HDD. Так, совсем несложно и без дорогого софта, можно восстановить запоротый RAID10-массив.