weidner -> computer -> linux -> cd-rom-router
Stand: 2001-01-20
(2000-11-14)

Linux CD-ROM Router

InitRD

Der CD-ROM-Router startet mit einer geschickt angelegten initialen RAM-Disk. Dieses Kapitel erläutert, wie diese gemacht wird.

cdmount

bin/mount können wir nicht nehmen, da dieses dynamisch mit libc gelinkt ist und diese zu groß für eine InitRD ist. Wir behelfen uns mit einem kleinen C-Programm, daß ich dem

Artikel von YAMAMORI Takenori entnommen habe.

Listing von cdmount.c:
#include <sys/mount.h>

main(int argc, char ** argv)
{
  int data = 0;
  return mount(argv[1],argv[2],argv[3],MS_RDONLY|MS_MGC_VAL,&data);
}

cdmount.c muß mit den folgenden Optionen übersetzt werden. Wichtig ist, daß es statisch gelinkt wird und die Symboltabellen abgetrennt werden.

gcc -O2 -static -s -o /tmp/cdmount cdmount.c

Zum Test kann es wie folgt aufgerufen werden:

# /tmp/cdmount /dev/cdrom /mnt/cdrom iso9660 # cat /proc/mounts

Wenn eine CD-ROM normal eingehängt wird, ist es in Ordnung.

cdmount in Assembler (2001-01-20)

Obwohl cdmount nur ein sehr kleines Programm ist, so ist das erzeugte Binärprogramm auf meinem Rechner doch 219828 Byte groß geworden. Über die Ursache will ich mich an dieser Stelle nicht auslassen, aber nach einer Alternative habe ich mich schon umgesehen. Dann fand ich Robin Miyagis wunderbares

LINUX ASSEMBLER TUTORIAL mit dessen Hilfe ich die folgende Alternative basteln konnte, die genau das gleiche macht wie das C-Programm, aber nur ein 412 Byte großes Binary erzeugt.

        ## cdmount.s ###################################################

        ## Mathias Weidner  ############################################
        ## http://weidner.in-bad-schmiedeberg.de/ ######################

        ## This file contains minimal program to mount a CD.
	##
	## It takes three arguments: device, mount point, filesystem
	##
	## example:
	## cdmount /dev/cdrom /mnt/cdrom iso9660

        ## Compile Instructions:
        ## -------------------------------------------------------------
        ## as -o cdmount.o cdmount.s
        ## ld -O0 -o cdmount cdmount.o
########################################################################
        .section .data

	.align 4

mountdata:
        .long 0

########################################################################
        .section .text

        .globl _start

        .align 4
_start:
        movl %esp, %ebp         # store %esp in %ebp
_mount:
        addl $8, %esp           # %esp ---> second parameter on stack
        movl (%esp), %ebx       # move next parameter into %ebx
        addl $4, %esp           # %esp ---> third parameter on stack
        movl (%esp), %ecx       # move next parameter into %ecx
        addl $4, %esp           # %esp ---> fourth parameter on stack
        movl (%esp), %edx       # move next parameter into %edx
	movl $0xc0ed0001, %esi	# MS_MGC_VAL|MS_RDONLY ---> %esi
	leal mountdata, %edi    # pointer to 0
	movl $21, %eax          # system call _mount
	int $0x80
_exit:
	movl %eax, %ebx         # give back return value
        xorl %eax, %eax         # %eax = 0
        incl %eax               # %eax = 1, system call _exit ()
        int $0x80               # execute _exit () system call

        ## end of cdmount.s ############################################

initrd.img

Zum Erzeugen einer initialen RAM-Disk für den Systemstart gibt es bei RedHat das Programm sbin/mkinitrd. Die damit erzeugte InitRD muß noch etwas angepaßt werden.

mkinitrd kopiert die sash auf die InitRD. Diese Shell ist zwar für interaktive Bootdisketten recht komfortabel durch die vielen eingebauten Kommandos, doch leider fehlen Ihr die meisten Bourne-Shell Programmiermöglichkeiten, so daß wir diese entfernen und stattdessen die ash (statisch gelinkte Version) verwenden.

Konkret wird die InitRD wie folgt angepaßt:

---
# /sbin/mkinitrd -v -f /boot/initrd-cd-rom.img 2.2.16-3
# zcat /boot/initrd-cd-rom.img > /tmp/initrd-cd-rom
---
# mkdir /mnt/tmp
# mount -o loop /tmp/initrd-cd-rom /mnt/tmp
# cd /mnt/tmp/bin
# rm sash
# cp -p /bin/ash.static sh
# cd ..
# mkdir -p mnt/{cdrom,floppy,dev}
# mv bin dev lib mnt/cdrom
# mv etc mnt/floppy
# ln -s mnt/cdrom/* .
# ln -s mnt/floppy/* .
# ln -s mnt/cdrom/usr mnt/cdrom/sbin mnt/cdrom/var mnt/cdrom/root .
# touch fastboot
# for i in 0 1 2 3 4 5 6 7 8 9; do mknod mnt/dev/tty$i c 4 $i; done
# mkdir proc
# mkdir tmp
# chmod 1777 tmp
# cp -a /dev/hd[abcd]* /dev/scd0 /dev/sda* mnt/cdrom/dev
# cp -p /tmp/cdmount .
# vi linuxrc               # siehe unten über linuxrc
# cd /
# umount /mnt/tmp
# gzip -9 < /tmp/initrd-cd-rom > /boot/initrd-cd-rom.img

linuxrc

Wir fügen ein paar Befehle in linuxrc wie folgt ein. Dort stehen bereits ein paar Zeilen, die durch mkinitrd geschrieben wurden, aber diese können wir stehen lassen.

Listing von linuxrc:
#! /bin/sh

echo "Loading aic7xxx module
insmod /lib/aic7xxx.o

/cdmount /dev/hdc /mnt/cdrom iso9660 \
 || /cdmount /dev/hdd /mnt/cdrom iso9660 \
 || /cdmount /dev/hdb /mnt/cdrom iso9660 \
 || /cdmount /dev/scd0 /mnt/cdrom iso9660 \
 || (
  echo 'cannot mount CD-ROM, invoke /bin/sh'
  /bin/sh
)

mount /dev/fd0 /mnt/floppy \
 || mount -o loop -t ext2 /mnt/cdrom/boot.img /mnt/floppy

mount -t proc none /proc
echo 0x100 > /proc/sys/kernel/real-root-dev
umount /proc

Layout der InitRD

Nun müßte der Verzeichnisbaum in der InitRD wie folgt aussehen:

/etc -> mnt/floppy/etc
/sbin -> mnt/cdrom/sbin
/bin -> mnt/cdrom/bin
/usr -> mnt/cdrom/usr
/lib -> mnt/cdrom/lib
/var -> mnt/cdrom/var
/dev -> mnt/cdrom/dev
/root -> mnt/cdrom/root
/
/tmp/var/lock/
        /run/
/proc/
/
/linuxrc
/cdmount
/fastboot
/
/mnt/cdrom/bin/sh
    /     /   /insmod
    /     /
    /     /lib/aic7xxx.o
    /     /
    /     /dev/console
    /         /null
    /         /hdc
    /         ...
    /floppy/etc

Die Verzeichnisse, die von mkinitrd angelegt wurden, sind unter &/;mnt/cdrom verschoben und durch symbolische Links ersetzt. Unter &/;mnt/cdrom sind die minimalen Dateien, die benötigt werden, bevor die CD-ROM eingehängt wird. Die Konfigurationsdaten von &/;etc werden von der Bootdiskette genommen, die unter &/;mnt/floppy eingehängt wird. Damit kann die Konfiguration auf einer Diskette geändert werden, ohne daß lilo jedesmal aufgerufen werden muß.

In linuxrc wird der cdmount Befehl verwendet um die CD-ROM einzuhängen. Dabei werden nacheinander /dev/hdc,hdd,hdb,scd0 probiert, bis die CD-ROM montiert werden kann. Sobald die CD-ROM eingehängt ist, stehen alle Kommandos von der CD-ROM zur Verfügung. Wenn keine CD-ROM montiert werden kann, wird eine Shell gestartet und die Nachricht ausgegeben. Damit kann zu Testzwecken eine Festplatte eingehängt werden bevor eine richtige CD-ROM gebrannt wird.

Nachdem die CD-ROM montiert wurde, wird unter &/;mnt/floppy versucht eine Diskette zu montieren. Schlägt dieses fehl, wird die Datei boot.img aus der Wurzel der CD-ROM genommen. Damit stehen die Konfigurationsdateien zur Verfügung.

Schließlich wird das &/;proc Dateisystem eingehängt und 0x100 (das ist die Gerätenummer der InitRD) in real-root-dev geschrieben um festzulegen, daß die InitRD selbst als Rootdateisystem fungiert.

Außerdem gibt es eine Datei fastboot in der InitRD, um die Überprüfung der Dateisysteme laut fstab zu unterbinden.

Wenn alle Änderungen an initrd-cd-rom eingetragen sind, wird diese ausgehängt, mit gzip komprimiert und als &/;boot/initrd-cd-rom.img abgelegt.

Boot Test für initrd-cd-rom.img

Nun sollte die InitRD für die CD-ROM fertig sein. Diese können wir mit LILO und der Festplatte testen. Dazu fügen wir die folgenden Zeilen an &/;etc/lilo.conf an:

image = /boot/vmlinuz-2.2.16-3
	label  = cd-rom
	initrd = /boot/initrd-cd-rom.img
	root   = 0x101      # das darf nicht 0x100 sein, der
	                    # eigentliche Wert ist egal

Dann installieren wir LILO.

# /sbin/lilo
Added linux *
Added cd-rom

Nun haben wir einen Boot Test mit der Festplatte. Wir legen noch keine CD-ROM ein. Nach dem Neustart geben wir cd-rom beim Prompt von LILO ein. Nachdem vmlinuz und initrd-cd-rom geladen sind landen wir in einer Shell und können die Festplatte wie folgt einhängen:

# /cdmount /dev/hda1 /mnt/cdrom ext2
           =========
	       |
	       +---- durch die korrekte Partition ersetzen.

Indem wir die Festplatte read-only einhängen, überprüfen wir, ob sie als CD-ROM geeignet ist. Nach dem Aufruf von cdmount sollten die üblichen UNIX-Befehle wie z.B. ls verfügbar sein.

Wenn wir die Shell verlassen, wird linuxrc beendet und sbin/init erlangt die Kontrolle über den Rechner.