Hlavní navigace

Záchrana dat ze starého RAID 0 vytvořeného pomocí fakeRAID

Zkusíme si záchranu dat ze dvou 18 let starých IDE disků, u kterých už si ani nepamatujeme, jak vlastně byly zapojeny. Pro získání dat použijeme vlastní skripty v bashi a zjistíme, který z nich je nejrychlejší.
Jan Fikar
Doba čtení: 8 minut

Sdílet

Nedávno jsem našel v šuplíku dva IDE disky IBM 41 GB Deskstar 60GXP 7200 RMP z počítače s Windows NT 4.0, které tam ležely asi od roku 2002. Chtěl jsem se podívat, co na nich je a tak jsem pomocí nástroje ddrescue přes IDE – USB3 redukci vytvořil jejich obraz na počítači s Linuxem.


ddrescue /dev/sdb disk1.img disk1.log

Kupodivu kopírování proběhlo úspěšně, jen s asi 10 kB nečitelnými. Na jednom z disků (disk1) se dokonce objevila tabulka oddílů, na druhém tabulka oddílů nebyla.

Disk disk1.img: 38.35 GiB, 41174138880 bytes, 80418240 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x6e3defed

Device        Boot Start       End   Sectors  Size Id Type
disk1.img1 *       63 160810649 160810587 76.7G  7 HPFS/NTFS/exFAT

Připojit oddíl se nedaří a všímavý čtenář si jistě povšimne nesouladu velikosti oddílu a disku. Hned mě napadlo, že data budou rozdělena na obou discích, ale jak? To už jsem si bohužel přesně nevybavoval.

NT 4.0

Windows NT 4.0 měly možnost skládat fyzické disky do „stripe sets“, kdy se střídají data z jednoho disku s daty z druhého disku (tedy RAID 0), a také do „linear volumes“, kdy jsou data z druhého disku za daty z prvního disku. Našel jsem historický program z roku 1994 vol od českého autora Martina Hinnera. Ten obsahoval modul pro jádro 2.3.18, který pak dokáže „stripe sets“ a „linear volumes“ připojit.

Spíše pro zábavu jsem zkusil modul přeložit, ale samozřejmě to nešlo. Nakoukl jsem tedy do zdrojového kódu a kontaktoval Martina, protože se mi zdálo, že stačí prostě disky v případě „linear volumes“ spojit za sebe a v případě „stripe sets“ jednoduše střídavě číst 32kB bloky z prvního a druhého disku. Martin odpověděl, že si ani už nepamatuje, že by něco takového programoval, ale v principu mi dal za pravdu.

Spojit soubory za sebe cat disk1.img disk2.img > disk0.img výsledek nepřineslo. Stejně se mi zdálo, že tam bylo něco jako RAID 0. Vyrobil jsem tedy skript v bashi, který bude střídavě zapisovat data z jednoho a druhého souboru.

IDE rozhraní

Přitom koukám, že oba IDE disky byly nastaveny jumpery (na obrázku bílé) na MASTER. Kdo nepamatuje, tak rozhraní IDE mělo možnost pověsit na jeden plochý vodič dva disky, jeden byl nastaven jako MASTER a druhý SLAVE. Případně byla možnost magického CABLE SELECT.

To mě přivedlo na myšlenku, kolik jsem vlastně měl v počítači IDE rozhraní, protože většinou bývala jen dvě. Vzpomněl jsem si, že MB byla Asus A7V133 a v manuálu se dozvídám, že měla čtyři IDE rozhraní, z toho dvě Promise IDE ATA-100/RAID 0/1 tzv. fakeRAID.

Navíc na téměř na konci obou disků (v oblasti za oddílem) jsem našel řetězec Promise Technology, IncTo už se mi trochu rozsvítilo a uvědomil jsem si, že jsem patrně používal tento Promise RAID 0. Což pro data v podstatě znamená to stejné, střídavě se berou z jednoho disku a druhého po blocích o velikosti „stripe size“, kterou ale neznám. Řadič zřejmě umožňuje velikosti bloků od 8 kB do 64 kB.

Skripty v bashi

Kouknu tedy na začátek druhého disku a tam se již 32 B od začátku objevuje řetězec FILE*. To v NTFS znamená obsah MFT tabulky, tedy tabulky, kde jsou záznamy o souborech. Kouknu se na začátek prvního disku a hledám také řetězec FILE*. Nacházím jej zhruba 53 kB od začátku. Teoreticky tedy velikost stripe bude více než 53 kB, začátek MFT mám na prvním disku a na druhém pokračování. Dobře, sázím tedy na velikost stripe 64 kB a jdu napsat skript.

#!/bin/bash
#Varianta 1
stripe=64k rm disk.img #echo 41174138880/64/1024 | bc -l for skip in $(seq 0..628268) do dd if=disk1.img bs=${stripe} skip=${skip} count=1 >> disk.img 2>/dev/null sync sync dd if=disk2.img bs=${stripe} skip=${skip} count=1 >> disk.img 2>/dev/null sync sync done
 

Magické číslo 628268 získám podělením velikosti jednoho disku velikostí stripe a zaokrouhlením nahoru, aby se i závěrečný necelý stripe pro jistotu nakopíroval. Skript je ale celkem pomalý. Hlavně kvůli synchronizaci, bez ní by se ale mohla data zapsat ve špatném pořádku. Zkusím tedy rychlejší verzi.

#!/bin/bash
#Varianta 2
stripe=64k rm disk0.img #echo 41174138880/64/1024 | bc -l for skip in $(seq 0..628268) do dd if=disk1.img bs=${stripe} skip=${skip} count=1 of=disk0.img oflag=noatime,append iflag=noatime conv=fdatasync,notrunc 2>/dev/null dd if=disk2.img bs=${stripe} skip=${skip} count=1 of=disk0.img oflag=noatime,append iflag=noatime conv=fdatasync,notrunc 2>/dev/null done

Toto je o trochu rychlejší, ale raději stále používám  fdatasync. Zrychlení také přineslo přenesení všech souborů na SSD. Naštěstí jsou velikosti starých disků malé ve srovnání s novými SSD. Hlavně nesmíte splést skip a seek jako já.

Ještě rychlejší je vytvořit cílový soubor dopředu a pak do něj zapisovat na správné místo právě pomocí seek. V tomto případě se obejdeme bez sync, a to je rychlejší.

#!/bin/bash
#Varianta 3
stripe=64k
length=628268
#echo 41174138880/64/1024 | bc -l
dd if=/dev/zero of=disk0.img bs=${stripe} count=${length} oflag=noatime conv=fdatasync 2>/dev/null
seek=0
for skip in $(seq 0 $length)
do
        dd if=disk1.img bs=${stripe} skip=${skip} seek=${seek} count=1 of=disk0.img oflag=noatime iflag=noatime 2>/dev/null
        seek=$(($seek +1))
        dd if=disk2.img bs=${stripe} skip=${skip} seek=${seek} count=1 of=disk0.img oflag=noatime iflag=noatime 2>/dev/null
        seek=$(($seek +1))
done

Také je možnost rozdělit smyčku na dvě a zapisovat nejprve z jednoho obrazu a až poté na správná místa z druhého obrazu. Kupodivu toto je pomalejší než varianta tři.

#!/bin/bash
#Varianta 4
stripe=64k
#echo 41174138880/64/1024 | bc -l
length=628268
dd if=/dev/zero of=disk0.img bs=${stripe} count=${length} oflag=noatime conv=fdatasync 2>/dev/null
seek=0
for skip in $(seq 0 $length)
do
        dd if=disk1.img bs=${stripe} skip=${skip} seek=${seek} count=1 of=disk0.img oflag=noatime iflag=noatime 2>/dev/null
        seek=$(($seek +2))
done
seek=1
for skip in $(seq 0 $length)
do
        dd if=disk2.img bs=${stripe} skip=${skip} seek=${seek} count=1 of=disk0.img oflag=noatime iflag=noatime 2>/dev/null
        seek=$(($seek +2))
done

Protože i na SSD může celá operace trvat několik hodin (obzvláště pro malé velikosti stripe, třeba 8 kB), podíváme se, jestli nepomůže použít místo bashe rychlejší dash. Jelikož jsem nepoužil bashismus, tak jen nahradím na prvním řádku bash za dash.

Rychlost skriptů na SSD
varianta 64 kB bash 64 kB dash 8 kB bash 8 kB dash
1 1,24 MB/s 1,29 MB/s 0,15 MB/s 0,16 MB/s
2 16,3 MB/s 18,5 MB/s 3,22 MB/s 4,19 MB/s
3 25,6 MB/s 31,6 MB/s 5,08 MB/s 6,77 MB/s
4 20,3 MB/s 24,9 MB/s 4,21 MB/s 6,10 MB/s

Nejlépe tedy vychází varianta tři s dashem. Jak teď zjistím, že jsem data poskládal dobře, či zda je potřeba použít jinou velikost stripe?

Testdisk

Samozřejmě lze disk zkusit namountovat pomocí ntfs-3g. Nejprve je výhodné použít partx, který automaticky vybere oddíly z disku a nemusím ručně určovat offset.

sudo partx -a disk0.img
ntfs-3g /dev/loop0p1 /mnt/tmp

Buď se hned podaří, nebo dostanu chybu, například o neshodě MFT s její zálohou.

$MFTMirr does not match $MFT (record 0).
Failed to mount '/dev/loop0p1': Input/output error
NTFS is either inconsistent, or there is a hardware fault, or it's a
SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows
then reboot into Windows twice. The usage of the /f parameter is very
important! If the device is a SoftRAID/FakeRAID then first activate
it and mount a different device under the /dev/mapper/ directory, (e.g.
/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation
for more details.

Pokud /dev/loop0 odmountujete a už jej nebudete potřebovat, je dobré jej zase od souboru odpojit partx -d /dev/loop0. Druhou možností kontroly správnosti souborového systému NTFS je pustit na obraz disku testdisk.

testdisk disk0.img

Dáte rozdělení disku Intel a Advanced Filesystem Utils. Zde je možnost Boot a kouknete, jestli NTFS boot sektor je v pořádku a jestli je i jeho záloha na konci disku identická. To už téměř znamená, že máte vyhráno. Dále je možné opravit MFT (Repair MFT). Když vám testdisk řekne, že jsou obě kopie MFT identické, je to zase dobré znamení. Také je možné se podívat na obsah souborů pomocí List, případně ještě obnovit nedávno smazané soubory pod Undelete (obsah těchto souborů však může být přepsán novějšími daty).


Testdisk, chybná záloha boot sektoru


Testdisk, záloha boot sektoru v pořádku

Takže data z fakeRAIDu jsem úspěšně zachránil. Nezdá se, že by Promise udržoval nějaké informace o poli na nepoužívaném konci disku. Konce obou disků jsou identické. Spíš si myslím, že parametry budou zapsány pouze v CMOS BIOSu. Zajímavou otázkou je, jestli by si Promise poradil s prohozením pořadí disků, tedy jestli se také dívá, na kterém disku je MBR.

tip_Ansible

Mdraid

Obecně je lepší ukládat data na SW RAID, kde je větší jistota, že svá data dostanete zpět. Jak je na tom mdraid v Linuxu? Mdraid uloží na disk tzv. superblok, ve kterém je zaznamenáno, jak pole vypadá, jaké má členy i velikost stripe. Tento superblock může být ve verzi 0.9, 1.0, 1.1 a 1.2. Přitom ve verzi 0.9 a 1.0 je superblok na konci, tedy například k disku z RAID 1 se můžete chovat jako k normálnímu disku, nebo RAID 0 rekonstruovat mým skriptem (i když je vždy lepší použít mdadm, ale třeba jste nechtěně superblok zničili pomocí mdadm --zero-superblock). Naopak verze 1.1 a 1.2 mají superblok na začátku a 4 kB od začátku disku, s těmi by bylo potřeba skript upravit.

Umístení superbloku různé verze
verze umístění
0.9 konec oddílu
1.0 konec oddílu
1.1 začátek oddílu
1.2 4 kB od začátku oddílu

Navíc superblok 0.9 má možnost být automaticky sestaven pomocí samotného jádra bez initrd (potřeba bylo nastavit id oddílu na 0×FD raid autodetect arrays). Ale zase je omezen ve velikosti (2 TB pro jádro starší 3.1 a 4 TB pro novější). V současnosti se stejně k sestavení mdraid používá initrd a výchozím superblokem je tedy 1.2.