niedziela, 14 października 2007

Migracja działającego systemu Linux na dysk USB

W dzisiejszym artykule chciałbym poruszyć sposób rozwiązania zagadnienia, jakim jest przeniesienie działającego systemu linux na inny dysk (a dodatkowo podłączony przez USB).

Od lat kilku jestem zadowolonym użytkownikiem linuxa Mandrivy, obecnie w wersji 2007.1. Jako że używam jej na laptopie (Acer Travelmate 4001WLMi), do sprawnego działania wszystkich jego podzespołów, hibernacji itp. konieczne były różne drobne modyfikacje i usprawnienia. Jak wiadomo, linux jest jak wino – im starszy tym lepszy :-) Z czasem więc doprowadziłem system do perfekcji, i mógłbym go tak zostawić, gdyby na horyzoncie nie pojawiła się pokusa w postaci nowej Mandrivy 2008 :-) Ponieważ jednak moja 2007.1 działa idealnie, zanim zacznę kombinować z instalacją nowej postanowiłem przenieść istniejący system na inny dysk twardy (podłączany przez USB) tak abym (w przypadku problemów) mógł się z niego zabootować i pracować pod stabilnym starym systemem.

Używając Googli można w miarę szybko znaleźć przydatne porady, pomimo to żaden opis nie był na tyle kompletny by poruszyć problemy które napotkałem i niniejszym chciałbym tę lukę zapełnić.

Najbardziej przydatnym źródłem informacji było dla mnie [HOW-TO] Przeniesienie linuksa na nowy dysk i początkowe kroki pokrywają się z tam omówionymi. Przejdźmy zatem do meritum sprawy.

  1. Przygotowania


    Ponieważ nasz 'nowy' system będzie uruchamiany wprost z dysku USB, konieczne jest aby posiadał odpowiednie sterowniki. Osobiście nie chciało mi sie kombinować z umieszczaniem niezbędnych modułów w obrazie initrd, zdecydowałem więc wkompilować w kernel następujące moduły: SCSI support (moduł SCSI, konieczny dla obsługi urzadzeń USB), Support for Host-side USB (moduł USB), USB Mass Storage support (moduł USB_STORAGE), EHCI HCD (USB 2.0) support (moduł USB_EHCI_HCD), OHCI HCD support (USB_OHCI_HCD) oraz UHCI HCD (most Intel and VIA) support (USB_UHCI_HCD). Być może nie wszystkie były potrzebne, nie sprawdzałem. Jądro którego używałem to 2.6.21.1.
    Największym problemem (po wykonaniu wszystkich poniższych punktów) był kernel panic przy próbie zabootowania z nowego dysku. Komunikaty wyglądały mniej więcej tak:
    mount:error 6 mounting ext3
    switchroot:mount failed:22
    ERROR opening /dev/console!!!!:20

    Powodem tych błędów było opóźnienie w inicjalizacji urządzenia USB. Jest to o tyle zabawne, że przecież sam kernel został już z tego dysku wczytany, a tu nagle nie może go obsłużyć :). Kombinowałem z parametrem jądra rootdelay, niestety bez efektów. Ostatecznie rozwiązaniem okazało się dodanie do pliku linuxrc (znajdującego się w ramdysku initrd) polecenia sleep 10, co pozwoliło na poprawne zainicjalizowanie sterownika USB i wykrycie urządzenia /dev/sda. Ostatecznie plik ten wyglądał tak:
    #!/bin/nash

    echo "Loading jbd.ko module"
    insmod /lib/jbd.ko
    echo "Loading ext3.ko module"
    insmod /lib/ext3.ko
    echo going to sleep...
    sleep 10

    echo Mounting /proc filesystem
    mount -t proc /proc /proc
    echo Mounting sysfs
    mount -t sysfs none /sys
    echo Creating device files
    mountdev size=32M,mode=0755
    echo -n /sbin/hotplug > /proc/sys/kernel/hotplug
    mkdir /dev/.udevdb
    mkdevices /dev
    echo Creating root device
    mkrootdev /dev/root
    echo 1 > /sys/power/suspend2/do_resume
    echo Mounting root filesystem /dev/root with flags noatime
    mount -o noatime --ro -t ext3 /dev/root /sysroot
    echo Switching to new root
    switchroot --movedev /sysroot
    echo Initrd finished

    ponieważ w mandrivie skrypt ten jest tworzony przez inny skrypt o nazwie mkinitrd, cały problem z modyfikacją linuxrc ograniczył się do dodania w odpowiednim miejscu w mkinitrd linijek:
    echo "echo going to sleep..." >> $RCFILE
    echo "sleep 10" >> $RCFILE

    a następnie wydanie polecenia make install w katalogu źródeł używanego aktualnie kernela (u mnie /usr/src/linux) co pośrednio spowodowało wygenerowanie nowego pliku linuxrc oraz obrazu initrd.

  2. Partycjonowanie


    Na początek na nowym docelowym dysku musimy założyć odpowiednie partycje. W moim wypadku były to: partycja typu ext3 ( /dev/sda1, na system) oraz partycja typu swap(/dev/sda2). Sam proces tworzenia partycji pominę, jako że jest on opisany co najmniej milion razy w internecie. Ja akurat użyłem programu qtparted.
    Podsumowując, w efekcie mamy nastepującą sytuację:

    stary dysk (z zainstalowaną mdv 2007.1): /dev/hda2 (na /dev/hda1 jest windows) oraz /dev/hda3 - swap
    nowy dysk (podłączany przez USB): /dev/sda1 oraz /dev/sda2 (swap)

  3. Montujemy docelową partycję /dev/sda1. W moim przypadku była ona podmontowana jako /media/disk.

  4. Kopiujemy całą zawartość źródłowej partycji (która jest zamontowana jako /):
    cp -ax / /media/disk

    flaga 'a' powoduje zachowanie w kopiach struktury i atrybutów oryginalnych plików (o ile możliwe), zaś 'x' omija katalogi znajdujące się na innym systemie plików (przy czym same katalogi, stanowiące punkty montowania, są kopiowane).
    Operacja kopiowania może potrwać całkiem długo (u mnie do przekopiowania było 22GB danych). Jako sposób śledzenia postępu bardzo spodobało mi się uruchomienie na innej konsoli polecenia watch df.

  5. Modyfikujemy zawartość pliku /media/disk/etc/fstab tak, by odpowiadał nowym partycjom. W moim przypadku:
    stary fstab:
    /dev/hda2 / ext3 noatime 1 1
    /dev/hdc /media/cdrom auto umask=0,users,iocharset=utf8,noauto,ro,exec 0 0
    /dev/hda1 /mnt/windows vfat umask=0,iocharset=utf8 0 0
    none /proc proc defaults 0 0
    /dev/hda3 swap swap defaults 0 0

    nowy fstab:
    /dev/sda1 / ext3 noatime 1 1
    /dev/hdc /media/cdrom auto umask=0,users,iocharset=utf8,noauto,ro,exec 0 0
    # /dev/hda1 /mnt/windows vfat umask=0,iocharset=utf8 0 0
    none /proc proc defaults 0 0
    /dev/sda2 swap swap defaults 0 0

    Jak widać wykomentowałem linijkę związaną z partycją windowsową, chwilowo dostęp do niej nie jest mi potrzebny – w przyszłości można odkomentować.

  6. Ponieważ w 207.1 używałem lilo, modyfikujemy plik /media/disk/etc/lilo.conf:
    # WARNING: do not forget to run lilo after modifying this file

    default="2.6.21.1"
    boot=/dev/hda
    map=/boot/map
    install=menu
    menu-scheme=wb:bw:wb:bw
    compact
    prompt
    nowarn
    timeout=100
    message=/boot/message
    other=/dev/hda1
    label="windows"
    table=/dev/hda
    image=/boot/vmlinuz-2.6.21.1
    label="2.6.21.1"
    root=/dev/hda2
    initrd=/boot/initrd-2.6.21.1.img
    append="resume2=swap:/dev/hda3 splash=silent"
    vga=791

    # WARNING: do not forget to run lilo after modifying this file

    default="2.6.21.1"
    disk=/dev/sda bios=0x80
    boot=/dev/sda
    map=/boot/map
    install=menu
    menu-scheme=wb:bw:wb:bw
    compact
    prompt
    nowarn
    timeout=100
    message=/boot/message
    image=/boot/vmlinuz-2.6.21.1
    label="2.6.21.1"
    root=/dev/sda1
    initrd=/boot/initrd-2.6.21.1.img
    append=" resume2=swap:/dev/sda2 splash=silent"
    vga=791

    jak widać oprócz oczywistej zmiany używanych dysków/partycji (we wpisach bios= i root=), dodałem również disk=/dev/sda bios=0x80. Wpis ten okazał się konieczny do poprawnego działania – bez niego, pomimo dodania sleep 10 do linuxrc, urządzenie /dev/sda nie było wykrywane w czasie bootowania.
    Ppoczątkowo w parametrach jądra (append=) dodawałem również opcję rootdelay=30. Niestety (pomimo że dokumentacja twierdzi iż parametr ten jest obsługiwany przez tą wersję jądra) nie zaobserwowałem ŻADNEGO efektu w działaniu (a teoretycznie powinno to być lepszym rozwiązaniem niż dodawanie sleep...), zdecydowałem więc o porzuceniu tego wpisu.

  7. wydajemy komendę:
    lilo -C /media/disk/etc/lilo.conf


  8. Warto zauważyć, że katalog /media/disk/dev jest pusty. W moim przypadku miałem problemy z dostępem do urządzenia konsoli. Metodą prób i błedów okazało się, że rozwiązaniem problemu jest skopiowanie plików /dev/console i /dev/null do wspomnianego wyżej katalogu.

Teraz wystarczy zrestartować komputer, ustawić w biosie bootowanie z urządzenia USB i (miejmy nadzieję) cieszyć się z sukcesu. Najwięcej czasu straciłem właśnie na rozwiązanie problemu 'kernel panic' co ostatecznie sprowadziło się do dodania sleep w pliku linuxrc.
Cały wpis!

O co tu chodzi?

Witajcie,

Ten blog powstał jako spin-off bloga Tcl Mentor. Ile bowiem można pisać o Tclu :) Są w życiu inne ciekawe tematy, i chciałbym aby tutaj one trafiały. Zapraszam chętnych do współpracy, każdy wartościowy artykuł zostanie opublikowany!

Nie mogę obiecać, że będę regularnie zamieszczał tutaj nowe posty. Reguła jest bowiem tylko jedna - wszystko co wyda mi się warte zachowania/opublikowania, trafi tutaj. Jakość, a nie ilość!

Pozdrawiam wszystkich i zachęcam do kontaktu.

TCLfriend@gmail.com