Kopia struktury dysku twardego

Spis treści

Wszyscy wiedzą, że zabawa z dyskiem zwykle kończy się tragicznie dla zawartych na nim danych. W tym wpisie spróbujemy się przyjrzeć sytuacjom, które nie jednego użytkownika linux'a potrafią przyprawić o zawał serca. Chodzi generalnie o uszkodzenie struktury dysku, oczywiście tylko tej programowej. Na błędy fizyczne nie możemy zbytnio nic poradzić. Natomiast jeśli chodzi o sferę logiczną, to mamy tutaj dość duże pole do popisu i jesteśmy w stanie się zabezpieczyć przed wieloma krytycznymi sytuacjami. Postaramy się tutaj omówić to jak wykonać kopię dysku. Taka kopia będzie miała tylko kilka (ew. kilkanaście) MiB, w skład której wchodzić będzie superblok systemu plików, nagłówki zaszyfrowanych partycji LUKS, struktura LVM i tablica partycji. Uszkodzenie każdego z tych powyższych uniemożliwia nam dostęp do danych zgromadzonych na dysku.

Objawy przy złym przycięciu partycji

Gdy bawimy się naszym dyskiem i zmieniamy w nim układ partycji, to jest niemal pewne, że prędzej czy później popełnimy błąd i podamy złe parametry w fdisk . Jeśli zmniejszymy za bardzo partycję i obetniemy jej trochę systemu plików, to w zależności od posiadanego systemu plików dostaniemy różne informacje z błędami. Poniżej jest kilka takich komunikatów.

W przypadku, gdy zmienialiśmy rozmiar systemu plików NTFS i zrobiliśmy to błędnie, ntfsresize zwróci nam poniższy komunikat:

# ntfsresize -i -f /dev/sdb1
ntfsresize v2013.1.13AR.1 (libntfs-3g)
Failed to read last sector (19531240): Invalid argument
HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,
   or it was not setup correctly (e.g. by not using mdadm --build ...),
   or a wrong device is tried to be mounted,
   or the partition table is corrupt (partition is smaller than NTFS),
   or the NTFS boot sector is corrupt (NTFS size is not valid).
ERROR(22): Opening '/dev/sdb1' as NTFS failed: Invalid argument
The device '/dev/sdb1' doesn't have a valid NTFS.
Maybe you selected the wrong partition? Or the whole disk instead of a
partition (e.g. /dev/hda, not /dev/hda1)? This error might also occur
if the disk was incorrectly repartitioned (see the ntfsresize FAQ).

Gdy próbowaliśmy zaś zmienić rozmiar systemu plików EXT4 i również coś pochrzaniliśmy, to przy sprawdzaniu błędów w fsck , ten zwróci nam poniższe zapytanie:

# fsck.ext4 -fv /dev/sdb1
e2fsck 1.42.9 (28-Dec-2013)
The filesystem size (according to the superblock) is 1280000 blocks
The physical size of the device is 786432 blocks
Either the superblock or the partition table is likely to be corrupt!
Abort? yes

Natomiast jeśli chodzi o problemy przy zmianie rozmiaru systemu plików FAT32, to dosfsck będzie miał poniższy problem:

# dosfsck -v /dev/sdb1
dosfsck 3.0.16 (01 Mar 2013)
dosfsck 3.0.16, 01 Mar 2013, FAT32, LFN
Checking we can access the last sector of the filesystem
Seek to 5242879488:Invalid argument

W przypadku, gdy zmienialiśmy rozmiar partycji LVM i przycięliśmy ją za bardzo w fdisk , to przy skanowaniu voluminów w pvscan dostaniemy taki komunikat:

# pvscan
  /dev/grupa1/volumin5: read failed after 0 of 4096 at 12947750912: Błąd wejścia/wyjścia
  /dev/grupa1/volumin5: read failed after 0 of 4096 at 12947808256: Błąd wejścia/wyjścia
  PV /dev/sdb1   VG grupa1   lvm2 [19,06 GiB / 0    free]
  Total: 1 [19,06 GiB] / in use: 1 [19,06 GiB] / in no VG: 0 [0   ]

Oczywiście nie da rady w takich przypadkach zamontować żadnego przyciętego systemu plików. Jeśli spróbujemy to mount przy próbie zamontowania takiego zasobu wyrzuci poniższy komunikat:

mount: wrong fs type, bad option, bad superblock on /dev/sdb1,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail  or so

Bez obaw. Trzeba po prostu znów odpalić fdisk'a i zrobić partycję nieco większą. Żadnych danych w ten sposób nie stracimy, gdyż operujemy jedynie na tablicy partycji, a ta ma w swojej strukturze parę pozycji, z których tylko 3 są brane pod uwagę, przynajmniej w linux'ie. Są to sektor początkowy partycji, rozmiar w sektorach, oraz typ partycji. Możemy dowolnie się tymi parametrami bawić bez utraty danych, tylko lepiej pamiętać pozycję wyjściową.

Ryzyko utraty danych

Ryzyko utraty danych zawsze istnieje, zwłaszcza gdy obchodzimy się z systemem plików bardzo nieumiejętnie ale wystarczy odrobina rozsądku i kalkulator, aby to ryzyko całkowicie wyeliminować. Poniżej kilka wskazówek na temat tego jak ma wyglądać kopia struktury dysku twardego i jak ją wykonać.

Kopia wpisów tablicy partycji MS-DOS

Zanim się zaczniemy bawić z tablicą partycji, która jest zlokalizowana w MBR (przynajmniej jej część), najlepiej jest wykonać kopię zapasową tego sektora i zachować ją w bezpiecznym miejscu. Jeśli posiadamy partycję rozszerzoną, przydałoby się również zrobić kopię każdego EBR, a ich lokalizacja zależy od faktycznego rozkładu dysków logicznych, przykładowo:

Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux
/dev/sdb6       119330816   128720895     4695040   83  Linux
/dev/sdb7       128722944   156299263    13788160   83  Linux

Są tutaj 3 EBR, a lokalizacja każdego z nich znajduje się zawsze w pierwszym sektorze logicznego dysku. W przypadku pierwszego dysku logicznego jest to parametr "start" partycji rozszerzonej. W przypadku pozostałych dysków sprawa ma się nieco inaczej, bo jest to sektor zaraz za końcem poprzedniej partycji. Między tymi dyskami logicznymi jest trochę wolnego miejsca. W przypadku równania do 1 MiB jest tam 2047 sektorów + 1 sektor na EBR. Jeśli byśmy wykonali kopię tej pozycji co jest w parametrze "start", skopiujemy pierwszy sektor systemu plików, a nie EBR, i przywrócenie tego sektora nic nam nie da.

Kopię MBR za to możemy wykonać bardzo łatwo, bo MBR zawsze znajduje się na tej samej pozycji i jest to pierwszy sektor dysku. Kopię możemy zrobić przy pomocy narzędzia dd , przykładowo:

# dd if=/dev/sdb of=./mbr bs=512 count=1

W razie problemów, by przywrócić podstawową tablicę partycji możemy wgrać 64 bajty do MBR z uprzednio zrobionej kopi zapasowej:

# dd if=./mbr of=/dev/sdb bs=1 count=64 skip=446 seek=446

Kopie EBR robimy w ten sam sposób podając tylko odpowiednie adresy sektorów.

Zamiast jednak się bawić w robienie kopi MBR i EBR, możemy zrobić kopię wpisów w tablicy partycji przy pomocy sfdisk :

# sfdisk -d /dev/sdb > sdb_table
# sfdisk /dev/sda < sdb_table

Tak wygląda przykładowa tablica partycji:

# partition table of /dev/sdb
unit: sectors

/dev/sdb1 : start=     2048, size= 45639680, Id=83
/dev/sdb2 : start= 45641728, size= 34525184, Id=83
/dev/sdb3 : start= 80166912, size= 24057856, Id=83
/dev/sdb4 : start=104224768, size= 52074496, Id= 5
/dev/sdb5 : start=104226816, size= 15101952, Id=83
/dev/sdb6 : start=119330816, size=  9390080, Id=83
/dev/sdb7 : start=128722944, size= 27576320, Id=83

Jak coś nawali, to zawsze możemy ręcznie wpisywać wartości z tej powyższej tabelki do fdisk'a.

Kopia wpisów tablicy partycji GPT

Jeśli ktoś używa tablicy partycji GPT, to nie może kierować się powyższymi wskazówkami co do kopi struktury tablicy partycji, bo ta w przypadku GPT jest inna. Bez problemu możemy zrobić jej kopię i zachować ją jako plik przy pomocy narzędzi gdisk lub sgdisk , przykładowo:

# gdisk /dev/sdb
GPT fdisk (gdisk) version 0.8.8

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): b
Enter backup filename to save: backup_gpt_file
The operation has completed successfully.

Problem ze stworzonym w ten sposób plikiem jest taki, że nie jest on w postaci czytelnej dla człowieka i trzeba przywracać całą kopię partycji, zamiast jedynie pojedynczych wpisów.

Kopia nagłówków zaszyfrowanych partycji LUKS

Jeśli chodzi o zaszyfrowane kontenery LUKS, to trzeba pamiętać, że by się dostać do tego co jest w środku, musimy podać odpowiedni klucz/hasło, a te są przechowywane w nagłówku partycji. Taki nagłówek to zwykle 2 MiB danych. Kopię takiego nagłówka możemy zrobić ręcznie przy pomocy dd albo też posłużyć się w tym celu natywnymi narzędziami LUKS. Jeśli zdecydujemy, aby tak kopia nagłówka LUKS była zrobiona ręcznie, to musimy pierw uzyskać informacje na temat tego ile taki nagłówek faktycznie zajmuje:

# cryptsetup status sdb1
/dev/mapper/sdb1 is active.
  type:    LUKS1
  cipher:  aes-xts-plain64
  keysize: 512 bits
  device:  /dev/sdb1
  offset:  4096 sectors
  size:    78229504 sectors
  mode:    read/write

W tym przypadku jest to 4096 sektorów, z których każdy ma 512 bajtów. Zatem wklepujemy te parametry do dd :

# dd if=/dev/sdb1 of=./backup_header_sdb1 bs=512 count=4096

W ten sposób utworzona kopia nagłówka może zostać przywrócona ręcznie w poniższy sposób:

# dd if=./backup_header_sdb1 of=/dev/sdb1 bs=512 count=4096

Kopię nagłówka LUKS możemy także wykonać przy pomocy luksHeaderBackup , przykładowo:

# cryptsetup luksHeaderBackup /dev/sdb1 --header-backup-file ./backup_header_sdb1

Przywracanie kopi zapasowej nagłówka LUKS odbywa się za sprawą luksHeaderRestore :

# cryptsetup luksHeaderRestore /dev/sdb1 --header-backup-file ./backup_header_sdb1

Kopia struktury LVM

LVM posiada narzędzia umożliwiające dokonanie kopi struktury kontenera i zapisanie jej w czytelnej dla człowieka formie w pliku tekstowym. By zrobić sobie taką kopię, wpisujemy do terminala poniższą komendę:

# vgcfgbackup
  Volume group "grupa1" successfully backed up.

Zostanie w ten sposób utworzony plik /etc/lvm/backup/grupa1 , który może nam posłużyć do odtworzenia całej struktury, w przypadku, gdy ta zostanie w jakiś sposób uszkodzona.

Kopia superbloku systemu plików

Superblok to krytyczny sektor każdego systemu plików. Zawiera on informacje, według których kernel jest w stanie operować na danej partycji. W przypadku, gdy superblok zostaje uszkodzony, kernel nie wie jak zamontować taki system plików, w efekcie czego nie możemy uzyskać dostępu do danych. Systemy plików są w stanie chronić swój superblok kopiując go (kilka razy) i zapisując w innych lokalizacjach w obrębie granic partycji. W taki sposób, jeśli uszkodzeniu ulegnie główny superblok, to możemy przywrócić jego kopię z któregoś z tych zapasowych sektorów.

Lokalizację superbloku jak i jego kopi w systemie plików EXT4 możemy poznać za pomocą dumpe2fs :

# dumpe2fs /dev/sdb1 | grep -i super
...
  Primary superblock at 0, Group descriptors at 1-1
  Backup superblock at 32768, Group descriptors at 32769-32769
  Backup superblock at 98304, Group descriptors at 98305-98305
  Backup superblock at 163840, Group descriptors at 163841-163841
...

W tym przypadku główny superblok jest ulokowany na pozycji 0 . Standardowo jest to pierwszy blok systemu plików. Ilość zapasowych kopi zależy od rozmiaru systemu plików. W tym przypadku mamy do czynienia z partycją 9 GiB. Zatem jest ona niewielka ale i tak mamy kilka kopi zapasowych ulokowanych, min. w bloku 32768 , 98304 , 163840 . Na dobrą sprawę, to nie musimy uczyć się tych pozycji na pamięć. W każdym przypadku są one takie same. Nie musimy także ręcznie dokonywać kopi superbloku, bo system plików robi to za nas.

Uszkodzenie struktury dysku

Może i zmiana rozmiaru LVM, LUKS i normalnych partycji potrafi przebiegać zwykle bez najmniejszych problemów ale w życiu czasem różnie bywa i niekiedy struktura danych zawartych na dysku może zostać rozbita. Zadajmy sobie pytanie, co się stanie jak badsector uderzy w MBR, czyli w miejsce gdzie siedzą wpisy tablicy partycji albo też co się stanie, gdy uszkodzeniu ulegnie nagłówek zaszyfrowanej partycji? Wyżej został opisany, co prawda, sposób dokonania kopi danych i wgrywania ich w przypadku problemów ale to trochę takie bezpłciowe. Poniżej sprawdzimy czy faktyczne uszkodzenie struktury dysku spowoduje utratę rzeczywistych danych.

Uszkodzenie tablicy partycji

W przypadku tablicy partycji MS-DOS może ulec awarii MBR. W MBR poza kodem bootloader'a mamy 4*16=64 bajty na tablicę partycji. Dobrze jest zrobić kopię całego MBR, a potem ewentualnie przywracać jego części. MBR może przechować maksymalnie 4 wpisy odpowiadające 4 partycjom. Nie przechowuje jednak wpisów, które są na partycji rozszerzonej.

Partycję rozszerzoną można traktować jako dysk zagnieżdżony w innym dysku. Posiada swój MBR z tym, że nieco inny. EBR ma taką samą strukturę co MBR ale jest pozbawiony kodu bootloader'a i wykorzystuje dwa z czterech wpisów na partycje. Jeden z nich opisuje partycję (start partycji, bez offsetu, oraz długość partycji), a drugi linkuje do kolejnego EBR (start w miejscu nowego EBR, rozmiar całej partycji począwszy od EBR). W przypadku ostatniej partycji w łańcuchu EBR, ten drugi wpis ma 0 bajtów.

Co się zatem mogłoby stać? Mógłby zostać uszkodzony wpis podstawowej partycji w MBR, w takim przypadku stracilibyśmy jedną partycję. Mógłby zostać też trafiony wpis rozszerzony. W wyniku takiego zdarzenia stracilibyśmy wszystkie partycje w łańcuchu EBR. Jeśli mamy 4 dyski logiczne na rozszerzonej partycji i uszkodzeniu uległby drugi EBR, stracilibyśmy nie tylko 2 dysk logiczny ale również 3 i 4.

Tak wygląda przykładowy dysk, ma 3 partycje podstawowe i 3 rozszerzone:

# fdisk /dev/sdb

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux
/dev/sdb6       119330816   128720895     4695040   83  Linux
/dev/sdb7       128722944   156299263    13788160   83  Linux

Skopiujmy teraz całą tablicę partycji:

# sfdisk -d /dev/sdb > ./sdb_table
Warning: extended partition does not start at a cylinder boundary.
DOS and Linux will interpret the contents differently.

# cat sdb_table
# partition table of /dev/sdb
unit: sectors

/dev/sdb1 : start=     2048, size= 45639680, Id=83
/dev/sdb2 : start= 45641728, size= 34525184, Id=83
/dev/sdb3 : start= 80166912, size= 24057856, Id=83
/dev/sdb4 : start=104224768, size= 52074496, Id= 5
/dev/sdb5 : start=104226816, size= 15101952, Id=83
/dev/sdb6 : start=119330816, size=  9390080, Id=83
/dev/sdb7 : start=128722944, size= 27576320, Id=83

W przypadku, gdy zniknie nam jeden wpis, możemy odtworzyć ręcznie tylko ten, którego brakuje. Czas nabroić trochę. Skoro tablica partycji to 64 bajty po 446 bajtach, to uszkodzimy drugą partycję przez zapisanie 5 bajtów z tych 16 opisujących tą partycję. Zatem 446+16=462 , czyli na 462 zaczyna się drugi wpis, jako że numerowanie zaczyna się od 0, a nie od 1:

# dd if=/dev/zero of=/dev/sdb bs=1 seek=462 count=5
5+0 records in
5+0 records out
5 bytes (5 B) copied, 0.00636016 s, 0.8 kB/s
# partprobe

I patrzymy cośmy uczynili:

# fdisk /dev/sdb

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592    0  Empty
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux
/dev/sdb6       119330816   128720895     4695040   83  Linux
/dev/sdb7       128722944   156299263    13788160   83  Linux

No ładnie druga partycja zniszczona i oznaczona jako "empty". Nie jest to, co prawda, wolna przestrzeń ale dane są pozornie utracone. Wpis partycji ma szereg bajtów opisujących jej strukturę i w tym przypadku nadpisaliśmy tylko typ partycji ale w realiach może się zdarzyć, że nieczytelne będą inne bajty opisujące, np. początek partycji. Jak zatem naprawić szkody, które spowodowaliśmy? W tym przypadku wystarczy na nowo ustawić typ partycji:

# fdisk /dev/sdb

Command (m for help): t
Partition number (1-7): 2
Hex code (type L to list codes): 83
Changed system type of partition 2 to 83 (Linux)

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

A w innych przypadkach? Można oczywiście usunąć cały wpis w tablicy partycji i stworzyć go na nowo.

Command (m for help): d
Partition number (1-7): 2
Warning: partition 2 has empty type
Command (m for help): n
Partition type:
   p   primary (2 primary, 1 extended, 1 free)
   l   logical (numbered from 5)
Select (default p): p
Selected partition 2
First sector (45641728-156299374, default 45641728):
Using default value 45641728
Last sector, +sectors or +size{K,M,G} (45641728-80166911, default 80166911):
Using default value 80166911

Command (m for help): t
Partition number (1-7): 2
Hex code (type L to list codes): 83
Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux
/dev/sdb6       119330816   128720895     4695040   83  Linux
/dev/sdb7       128722944   156299263    13788160   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

Uszkodzenie partycji rozszerzonej

Scenariusz drugi, czyli uszkodzenie partycji rozszerzonej.

# dd if=/dev/zero of=/dev/sdb bs=1 seek=494 count=5
5+0 records in
5+0 records out
5 bytes (5 B) copied, 0.00958093 s, 0.5 kB/s
# partprobe

W tym przypadku partycja rozszerzona będzie pokazywana jako partycja podstawowa i nie da rady zmienić jej typu na 5 (partycja rozszerzona). Spróbujemy przywrócić cały wpis. Jeśli nie wiemy jaki powinien być rozmiar partycji rozszerzonej, możemy nam pomóc kopia zrobiona przez sfdisk :

/dev/sdb4 : start=104224768, size= 52074496, Id= 5

Początkowy sektor to 104224768. Rozmiar zaś to 52074496 sektorów. Pamiętajmy tylko, by od 52074496 odjąć 1 przy wpisywaniu rozmiaru w fdisk :

root:~# fdisk /dev/sdb
Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    0  Empty

Command (m for help): d
Partition number (1-5): 4

Command (m for help): n
Partition type:
   p   primary (3 primary, 0 extended, 1 free)
   e   extended
Select (default e): e
Selected partition 4
First sector (104224768-156299374, default 104224768): 104224768
Last sector, +sectors or +size{K,M,G} (104224768-156299374, default 156299374): +52074495

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

Jak widać stworzenie nowej partycji rozszerzonej nie przywróciło dysków logicznych. Jak je przywrócić? Trzeba ręcznie przepisać wpisy z kopi zrobionej przez sfdisk :

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended

Command (m for help): n
All primary partitions are in use
Adding logical partition 5
First sector (104226816-156299263, default 104226816): 104226816
Last sector, +sectors or +size{K,M,G} (104226816-156299263, default 156299263): +15101951

Command (m for help): n
All primary partitions are in use
Adding logical partition 6
First sector (119330816-156299263, default 119330816): 119330816
Last sector, +sectors or +size{K,M,G} (119330816-156299263, default 156299263): +9390079

Command (m for help): n
All primary partitions are in use
Adding logical partition 7
First sector (128722944-156299263, default 128722944): 128722944
Last sector, +sectors or +size{K,M,G} (128722944-156299263, default 156299263): +27576319

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux
/dev/sdb6       119330816   128720895     4695040   83  Linux
/dev/sdb7       128722944   156299263    13788160   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

Jeszcze tylko sprawdźmy czy damy radę zamontować partycję numer 6:

# mount /dev/sdb6 /mnt

Można również wgrać pierwszy EBR, jego pozycja znajduje się zawsze na początku partycji rozszerzonej, a przy tworzeniu nowej rozszerzonej partycji ten sektor jest nadpisywany. To dlatego nie ma dysków logicznych. Jego kopię w tym przypadku można by sporządzić przy pomocy dd w poniższy sposób:

# dd if=/dev/sdb bs=512 skip=104224768 count=1 of=./ebr

I jeśli stracimy rozszerzony wpis w MBR, tworzymy go na nowo w fdisk'u:

# fdisk /dev/sdb

Command (m for help): d
Partition number (1-7): 4
Command (m for help): n
Partition type:
   p   primary (3 primary, 0 extended, 1 free)
   e   extended
Select (default e): e
Selected partition 4
First sector (104224768-156299374, default 104224768): 104224768
Last sector, +sectors or +size{K,M,G} (104224768-156299374, default 156299374): +52074495

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

I wgrywamy kopię EBR:

# dd if=./ebr bs=512 seek=104224768 count=1 of=/dev/sdb
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000119726 s, 4.3 MB/s
# partprobe

I co widzimy w fdisk'u?

# fdisk /dev/sdb

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux
/dev/sdb6       119330816   128720895     4695040   83  Linux
/dev/sdb7       128722944   156299263    13788160   83  Linux

Logiczne dyski wróciły na swoje miejsce. Sprawdźmy jeszcze czy da radę zamontować partycję numer 6 bez błędów:

# mount /dev/sdb6 /mnt

Ostatnie uszkodzenie będzie polegać na nadpisaniu EBR poprzedzającego partycję sdb6 . Tylko tutaj taka uwaga, nie możemy nadpisywać sektora 119330816 (start sda6), bo tutaj jest uwzględniony offset. EBR tej partycji znajduje się zaraz za końcem partycji poprzedzającej, czyli: 119328767+1=119328768 i to ten sektor należy uszkodzić.

# dd if=/dev/zero bs=512 seek=119328768 of=/dev/sdb count=1
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.00091464 s, 560 kB/s
# partprobe

I sprawdźmy:

# fdisk /dev/sdb
omitting empty partition (6)

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux

Tak jak można było się spodziewać, zniknęły dwie partycje. Ta, której EBR nadpisaliśmy oraz następne w łańcuchu EBR, a że była tylko jedna, to w sumie zniknęły dwie. Oczywiście w tym przypadku sprawa wygląda podobnie, trzeba przywrócić EBR. A co jeśli nie mamy EBR? Można, by skorzystać z kopi sfdisk'a. W sumie to sfdisk rozwiązuje wszystkie problemy. Zatem wchodzimy do fdisk'a i przywracamy dwa zagubione wpisy:

# fdisk /dev/sdb
omitting empty partition (6)

Command (m for help): n
All primary partitions are in use
Adding logical partition 6
First sector (119330816-156299263, default 119330816): 119330816
Last sector, +sectors or +size{K,M,G} (119330816-156299263, default 156299263): +9390079

Command (m for help): n
All primary partitions are in use
Adding logical partition 7
First sector (128722944-156299263, default 128722944): 128722944
Last sector, +sectors or +size{K,M,G} (128722944-156299263, default 156299263): +27576319

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 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
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    45641727    22819840   83  Linux
/dev/sdb2        45641728    80166911    17262592   83  Linux
/dev/sdb3        80166912   104224767    12028928   83  Linux
/dev/sdb4       104224768   156299263    26037248    5  Extended
/dev/sdb5       104226816   119328767     7550976   83  Linux
/dev/sdb6       119330816   128720895     4695040   83  Linux
/dev/sdb7       128722944   156299263    13788160   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

Montujemy i sprawdzamy czy są jakieś błędy:

# mount /dev/sdb6 /mnt

Także kopia pełnej tablicy partycji uchroni nas przed utratą danych wynikającą z jej uszkodzenia.

Uszkodzenie struktury LVM

Przetestujmy zatem czy kopia zapasowa struktury LVM, która jest trzymana w katalogu /etc/lvm/backup/ uchroni nas przed czymś. Popsujmy celowo cały kontener LVM:

# vgremove grupa1
Do you really want to remove volume group "grupa1" containing 3 logical volumes? [y/n]: y
Do you really want to remove active logical volume volumin1? [y/n]: y
  Logical volume "volumin1" successfully removed
Do you really want to remove active logical volume volumin3? [y/n]: y
  Logical volume "volumin3" successfully removed
Do you really want to remove active logical volume volumin5? [y/n]: y
  Logical volume "volumin5" successfully removed
  Volume group "grupa1" successfully removed

# pvremove /dev/sdb1
  Labels on physical volume "/dev/sdb1" successfully wiped

# lsblk /dev/sdb
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sdb      8:16   0  74,5G  0 disk
└─sdb1   8:17   0  19,1G  0 part

No to ładnie namieszaliśmy. Spróbujmy to teraz przywrócić przy pomocy vgcfgrestore :

# vgcfgrestore -v grupa1 -f /etc/lvm/backup/grupa1
  Couldn't find device with uuid xMddCU-5h1X-ZZL4-GFfO-ea4L-PqW8-YCGLte.
  Cannot restore Volume Group grupa1 with 1 PVs marked as missing.
  Restore failed.

# pvcreate --uuid xMddCU-5h1X-ZZL4-GFfO-ea4L-PqW8-YCGLte --restorefile /etc/lvm/backup/grupa1 /dev/sdb1
  Couldn't find device with uuid xMddCU-5h1X-ZZL4-GFfO-ea4L-PqW8-YCGLte.
  Physical volume "/dev/sdb1" successfully created

# vgcfgrestore -vv -f /home/user/grupa1 grupa1
      Setting activation/monitoring to 0
      Setting global/locking_type to 1
      Setting global/wait_for_locks to 1
      File-based locking selected.
      Setting global/locking_dir to /run/lock/lvm
      Setting global/prioritise_write_locks to 1
      Locking /run/lock/lvm/V_grupa1 WB
      Locking /run/lock/lvm/P_orphans WB
      /dev/sdb: size is 156299375 sectors
      /dev/sdb1: size is 39976960 sectors
      /dev/sdb1: size is 39976960 sectors
      /dev/sdb1: lvm2 label detected at sector 1
  Restored volume group grupa1
      Unlocking /run/lock/lvm/P_orphans
      Unlocking /run/lock/lvm/V_grupa1

Sprawdzamy czy coś to dało:

# pvs -v --segments
    Scanning for physical volume names
  PV         VG     Fmt  Attr PSize  PFree Start SSize LV       Start Type   PE Ranges
  /dev/sdb1  grupa1 lvm2 a--  19,06g    0      0  1280 volumin1     0 linear /dev/sdb1:0-1279
  /dev/sdb1  grupa1 lvm2 a--  19,06g    0   1280   512 volumin3     0 linear /dev/sdb1:1280-1791
  /dev/sdb1  grupa1 lvm2 a--  19,06g    0   1792  3087 volumin5     0 linear /dev/sdb1:1792-4878

# lvs -v --segments
    Finding all logical volumes
  LV       VG     Attr      Start SSize  #Str Type   Stripe Chunk
  volumin1 grupa1 -wc------    0   5,00g    1 linear     0     0
  volumin3 grupa1 -wc------    0   2,00g    1 linear     0     0
  volumin5 grupa1 -wc------    0  12,06g    1 linear     0     0

Podmontujmy jeszcze te voluminy:

# vgchange -a y grupa1
  3 logical volume(s) in volume group "grupa1" now active
# mount /dev/mapper/grupa1-volumin1 /media/1
# mount /dev/mapper/grupa1-volumin3 /media/3
# mount /dev/mapper/grupa1-volumin5 /media/5

# lsblk /dev/sdb
NAME                       MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sdb                          8:16   0  74,5G  0 disk
└─sdb1                       8:17   0  19,1G  0 part
  ├─grupa1-volumin1 (dm-1) 254:1    0     5G  0 lvm  /media/1
  ├─grupa1-volumin3 (dm-2) 254:2    0     2G  0 lvm  /media/3
  └─grupa1-volumin5 (dm-3) 254:3    0  12,1G  0 lvm  /media/5

Wszystko działa jak trzeba. Także, jeśli kontener LVM się spieprzy i gdzieś leży kopia metadanych w postaci pliku (na osobnym dysku), to możemy być spokojni. Co ciekawe, wszelkie zmiany struktury kontenera LVM są odnotowywane i zapisywane w /etc/lvm/archive/ . Są tam po prostu wszystkie przeszłe konfiguracje kontenerów LVM. Jeśli manipulujemy rozmiarem kontenera LVM najlepiej zrobić kopię całego foldera /etc/lvm/ i trzymać ją na osobnym dysku.

Uszkodzenie zaszyfrowanego kontenera LUKS

W przypadku zaszyfrowanych partycji LUKS, uszkodzeniu mogą ulec nagłówki uniemożliwiając nam dostęp do zaszyfrowanych danych. Robimy zatem backup nagłówków:

# cryptsetup luksHeaderBackup /dev/sdb1 --header-backup-file ./backup_header_sdb1
# ls -al backup_header_sdb1
-r-------- 1 root root 2.0M Jan 16 16:51 backup_header_sdb1

Nadpiszmy teraz nagłówki przy pomocy 20 sektorów 512 bajtowych:

# dd if=/dev/zero of=/dev/sdb1 bs=512 count=20 seek=100
20+0 records in
20+0 records out
10240 bytes (10 kB) copied, 0.00268263 s, 3.8 MB/s

Spróbujmy otworzyć kontener:

# cryptsetup luksOpen /dev/sdb1 sdb1
Enter passphrase for /dev/sdb1:
No key available with this passphrase.
Enter passphrase for /dev/sdb1:
No key available with this passphrase.
Enter passphrase for /dev/sdb1:
No key available with this passphrase.

Kontener nie przyjmuje hasła, mimo iż jest ono prawidłowe. Jeśli nie otworzymy kontenera, to nie da rady dostać się do danych. Jeśli sprawdzimy nagłówek, zobaczymy, że jakieś hasło tam jest przechowywane:

# cryptsetup luksDump /dev/sdb1
LUKS header information for /dev/sdb1

Version:        1
Cipher name:    aes
Cipher mode:    xts-plain64
Hash spec:      sha512
Payload offset: 4096
MK bits:        512
MK digest:      1e 6b c3 90 8f 4d ae 35 0c 08 38 16 5f 81 97 e2 a5 34 d0 ec
MK salt:        da 47 d9 88 8f ef 97 79 99 96 cb 9d ca cd 4d 56
                73 a1 5f 76 5b 44 a8 76 3d a4 a9 93 35 e1 7c fd
MK iterations:  46875
UUID:           88ddc490-8904-4fad-b072-39686730977b

Key Slot 0: ENABLED
        Iterations:             185180
        Salt:                   01 88 98 93 3d 96 8c 31 5e ea d2 f4 d9 d8 52 9b
                                66 a2 40 19 72 cb 46 c7 7f a4 1c 97 c2 d8 40 45
        Key material offset:    8
        AF stripes:             4000
Key Slot 1: DISABLED
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

Ale jakież to hasło? Tego raczej nie zgadniemy. Przywróćmy zatem kopię nagłówka:

# cryptsetup luksHeaderRestore /dev/sdb1 --header-backup-file ./backup_header_sdb1

WARNING!
========
Device /dev/sdb1 already contains LUKS header. Replacing header will destroy existing keyslots.

Are you sure? (Type uppercase yes): YES

No to jeszcze sprawdźmy czy jesteśmy w stanie zamontować bez problemów ten zaszyfrowany system plików:

# cryptsetup luksOpen /dev/sdb1 sdb1
Enter passphrase for /dev/sdb1:
# mount /dev/mapper/sdb1 /mnt

Także posiadanie kopi zapasowej nagłówków zaszyfrowanych partycji jest iście obowiązkowe.

Uszkodzenie superbloku systemu plików

Sprawdźmy co się stanie, gdy zostanie uszkodzony superblok sytemu plików. Tak wygląda przykładowy dysk w fsidk :

# fdisk -l /dev/sdb
Disk /dev/sdb: 74.5 GiB, 80025280000 bytes, 156299375 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: 0x000642e6

Device     Boot    Start       End   Sectors  Size Id Type
/dev/sdb1  *        2048  19531775  19529728  9.3G 83 Linux
/dev/sdb2       19533822 156297215 136763394 65.2G  5 Extended
/dev/sdb5       19533824  25100287   5566464  2.7G 82 Linux swap / Solaris
/dev/sdb6       25102336 156297215 131194880 62.6G 83 Linux

By uszkodzić superblok partycji sdb1 , musimy zapisać jej pierwszy blok. Niekoniecznie musi to być pełny 4096-bajtowy blok. Zwróćmy uwagę, że zapisujemy urządzenie /dev/sdb1 , a nie /dev/sdb .

# dd if=/dev/urandom bs=4096 count=1 of=/dev/sdb1

W tej chwili, przy próbie zamontowania tego systemu plików via mount , kernel zwróci nam ten poniższy komunikat:

mount.nilfs2: Error while mounting /dev/sdb1 on /mnt: Invalid argument

Przy skanowaniu tego systemu plików w fsck zostanie zwrócona masa błędów, których nie powinniśmy w żaden sposób naprawiać. Dlatego też jeśli już skanujemy taki dysk to zawsze róbmy to z opcją -n , która zapobiegnie wprowadzaniu zmian do tego uszkodzonego systemu plików.

Spróbujmy zatem przywrócić kopię superbloku, która jest przechowywana w jednym z zapasowych bloków, dajmy na to 32768 . By przywrócić superblok, również posługujemy się narzędziem fsck , tylko wskazujemy mu inną lokalizację superbloku, przykładowo:

# fsck -y -f -b 32768 /dev/sdb1

System plików powinien zostać zmodyfikowany, a my powinniśmy być w stanie go bez problemu zamontować. Nie powinniśmy też utracić żadnych danych.

Wnioski

Jak widać w pokazanym przeze mnie doświadczeniu, mając w posiadaniu kalkulator i kopię określonej struktury dysku w zależności od jego konfiguracji, dane przechowywane na takim nośniku są bezpieczne. Zatem nie ma się co stresować przy zmianie poszczególnych parametrów, tylko ważne jest, by była kopia zapasowa, z której to możemy wrócić do pozycji wyjściowej.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.