UNIX на одной дискете (по следам PicoBSD)

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

[Михаил Захаров (zakharov@ipb.redline.ru)]

UNIX на одной дискете (по следам PicoBSD)

Михаил Захаров <zakharov@ipb.redline.ru>

Содержание

  1. Введение
  2. Ядро
  3. crunchgen
  4. mfsroot
  5. Загрузочная дискета

1. Введение

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

Программные файлы FreeBSD обладают весьма внушительными размерами (например, во FreeBSD 4.6 файл команды ls занимает чуть менее 300Кб), поэтому основная проблема при подготовке загрузочной дискеты это острая нехватка свободного пространства. По этой причине от простого размещения файлов программ на дискете приходится отказаться, и в таком случае, возможна организация файловой системы в виртуальной памяти компьютера (MFS), где и будет храниться львиная доля всех файлов. Впрочем, это не относится к парадоксальной ситуации с ядром, которое должно располагаться на дискете в корневой директории, несмотря на то, что в обычном состоянии его размеры намного превосходят объем стандартной дискеты. Кроме того, при использовании MFS необходимо учитывать, что эта файловая система считывается из файла образа и отображается на оперативную память и, что естественно, любые изменения в файлах при каждой перезагрузке системы будут уничтожаться. Поэтому, использовать MFS для хранения конфигурационных файлов очень неудобно.

Подобные обстоятельства делают процесс подготовки загрузочной дискеты довольно сложным и сильно отличающимся от аналогичной операции в MS-DOS. Поэтому в двух словах поясню последовательность наших действий:

  1. Для начала, подготовим и сожмем при помощи gzip ядро, с минимальным набором опций, не забыв, однако, включить в него поддержку MFS.
  2. При помощи crunchgen соберем из исходных кодов один программный файл, объединяющий в себе основные программы операционной системы.
  3. Далее создадим mfsroot - образ файловой системы в виртуальной памяти, наполним его к файлами устройств и запишем на него crunch'еный бинарник.
  4. Подготовим файлы конфигурации и основные скрипты загрузки системы. В нашем случае эти файлы придется разделить на две группы: изменяемые и неизменяемые. Файлы первой группы будут размещены на файловой системе дискеты в каталоге /etc вне MFS, и, следовательно, при необходимости, могут быть изменены, файлы второй группы будут записаны на MFS и соответственно, изменения в них будут сохраняться до первой перезагрузки системы.
  5. Подготовим образ дискеты и запишем на него загрузчики ОС boot1, boot2 и loader. В loader.rc укажем, что при загрузке необходимо загрузить ядро kernel и из файла mfsroot организовать файловую систему в виртуальной памяти компьютера, Далее на образ дискеты запишем ядро и сжатый gzip'ом mfsroot, в каталог /etc поместим оставшиеся модифицируемые конфигурационные файлы.
Для наших опытов будем использовать FreeBSD 4.6-Release c полным наборов установленных исходных кодов.

2. Ядро

Нам нужно ядро, которое на дискете занимало бы как можно меньше места. Поэтому выбросим из него все лишнее и оставим, только самое необходимое. Допустим, что наше ядро будет называться SMALL, тогда создаем в каталоге /usr/src/sys/i386/conf файл SMALL:

        machine         i386
        cpu             I586_CPU
        cpu             I686_CPU
        ident           SMALL
        maxusers        10

        options         MATH_EMULATE    # Support for x87 emulation
        options         INET            # InterNETworking
        options         FFS             # Berkeley Fast Filesystem
        options         FFS_ROOT        # FFS usable as root device [keep this!]
        options         MFS             # Memory Filesystem
        options         MD_ROOT         # MD is a potential root device
        options         NFS             # Network Filesystem
        options         CD9660          # ISO 9660 Filesystem
        options         COMPAT_43       # Compatible with BSD 4.3 [KEEP THIS!]

        device          isa
        device          pci

        # Floppy drives
        device          fdc0    at isa? port IO_FD1 irq 6 drq 2
        device          fd0     at fdc0 drive 0

        # ATA and ATAPI devices
        device          ata0    at isa? port IO_WD1 irq 14
        device          ata1    at isa? port IO_WD2 irq 15
        device          ata
        device          atadisk         # ATA disk drives
        device          atapicd         # ATAPI CDROM drives
        options         ATA_STATIC_ID   # Static device numbering

        # atkbdc0 controls both the keyboard and the PS/2 mouse
        device          atkbdc0 at isa? port IO_KBD
        device          atkbd0  at atkbdc? irq 1 flags 0x1

        device          vga0    at isa?

        # syscons is the default console driver, resembling an SCO console
        device          sc0     at isa? flags 0x100

        # Floating point support - do not disable.
        device          npx0    at nexus? port IO_NPX irq 13

        # PCI Ethernet NICs that use the common MII bus controller code.
        # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!
        device          miibus          # MII bus support
        device          xl              # 3Com 3c90x (``Boomerang'', ``Cyclone'')

        # Pseudo devices - the number indicates how many units to allocate.
        pseudo-device   loop            # Network loopback
        pseudo-device   ether           # Ethernet support
        pseudo-device   pty             # Pseudo-ttys (telnet etc)
        pseudo-device   md              # Memory "disks"
Примеры нескольких подобных ядер можно взять у PicoBSD (/usr/src/release/picobsd). В принципе, в ядро можно включить любые опции и драйвера, оптимизированные для каждого конкретного случая, однако необходимо помнить, что поскольку ядро будет загружать MFS-файловую систему, следующие строки обязательны:
        options         MFS             # Memory Filesystem
        options         MD_ROOT         # MD is a potential root device
        pseudo-device   md              # Memory "disks"
теперь компилируем ядро:
        config SMALL; cd ../../compile/SMALL; make depend; make all
Если все прошло удачно, у нас появится ядро, которое по нашим представлениям должно быть эталоном миниатюрности. Для справки, на FreeBSD 4.6 размер такого ядра у меня получился 1705820 байт, что все равно превышает объем 1,44Мб.

Остается "почистить" скомпилированное ядро:

        strip kernel
        strip --remove-section=.note --remove-section=.comment kernel
и наконец сжать его:
        gzip -v -n --best kernel
В результате компрессии, ядро стало занимать чуть более 600 Кбайт. Итак, нам удалось сократить размеры ядра более чем в 2 раза! И у нас остается еще место для корневой файловой системы.

3. Crunchgen

Проблему размещения программных файлов постараемся разрешить при помощи программы /usr/bin/crunchgen, спасибо James da Silva (jds@cs.umd.edu). Эта программа собирает один большой бинарный файл из исходных кодов нескольких программ. Напишем для нее собственный конфигурационный файл crunch.conf:

        buildopts -DNOIPSEC -DRELEASE_CRUNCH -DCRUNCHED_BINARY -DNOSECURE -DNOCRYPT -DNOPAM

        srcdirs /usr/src/bin
        srcdirs /usr/src/sbin/i386
        srcdirs /usr/src/sbin
        srcdirs /usr/src/usr.bin
        srcdirs /usr/src/gnu/usr.bin
        srcdirs /usr/src/usr.sbin
        srcdirs /usr/src/libexec

        progs sh test echo hostname ln login getty stty
        progs inetd telnetd
        progs w kget reboot
        progs init ifconfig df cat
        progs cp rm mknod chmod chown mkdir ls syslogd
        progs sysctl route pwd_mkdb dev_mkdb
        progs mount umount swapon disklabel vnconfig
        progs kill mount_std
        progs pwd telnet
        progs passwd date
        progs mount_cd9660 mount_nfs ping traceroute

        ln mount_cd9660 cd9660
        ln mount_nfs nfs
        ln test [
        ln sh -sh
        ln mount_std procfs
        ln mount_std mount_procfs
        ln chown chgrp

        libs -lncurses -lmytinfo -lipx -lz -lpcap -lalias -lwrap
        libs -ledit -lutil -lmd -lcrypt -lmp -lgmp -lm -lkvm
        libs -lgnuregex -ltermcap -lipsec /usr/src/lib/libtelnet/libtelnet.a
По правде говоря, для наших целей, если произвести незначительные изменения, вполне подойдет один из аналогичных файлов PicoBSD. Далее запускаем crunchgen на выполнение:
        crunchgen crunch.conf
Увидев "Run "make -f crunch.mk" to build crunched binary", выполняем:
        make -f crunch.mk
Единственная проблема, с которой мне пришлось столкнуться при выполнении crunchgen, это ошибки при компиляции telnet. Для их устранения, мне пришлось вручную компилировать telnet, чтобы получить /usr/src/lib/libtelnet/libtelnet.a.

В результате, создается долгожданный программный файл crunch, который при размере чуть более 1Мб выполняет функции более сорока самых необходимых команд FreeBSD.

4. mfsroot

На самом деле если вычесть размеры сжатого ядра, на дискете у нас остается не так много свободного места, всего порядка 600 Кб и следовательно, организовывать корневую файловую систему прямо здесь не имеет никакого смысла, мы просто не сможем там разметить все наши файлы, не говоря уже про объемистый crunch'еный бинарник. Поэтому используем возможность создания файловой в виртуальной памяти компьютера, для этого создадим файл образа MFS, наполним его всеми необходимыми файлами, сожмем его при помощи gzip и наконец, поместим его в корневую директорию дискеты.

Подготовим MFS-образ корневой файловой системы. Пусть он будет располагаться в файле mfsroot:

        dd if=/dev/zero of=mfsroot count=4096 bs=1k     # Создадим mfs-образ. Для минимального набора
                                                        # файлов, 4-х мегабайтов нам вполне хватит
        vnconfig -s labels -c /dev/vn0 mfsroot          # Будем работать с mfsroot как с дисковым устройством
        disklabel -rw vn0 auto                          # Разметим и
        newfs -i 4096 -m 0 -p 0 -o space /dev/vn0c      # организуем на нем файловую систему
        mount /dev/vn0c /mnt                            # и смонтируем ее в каталог /mnt
Теперь осталось воспроизвести на MFS-образе минимальный набор каталогов и заполнить файловую систему необходимыми файлами. Какие именно файлы и директории там будут располагаться зависит от воображения ее автора и целей создаваемой дискеты. В нашем случае, понадобятся каталоги /etc, /dev, /sbin, /usr, /var, /tmp, /flp:
        mkdir /mnt/etc
        mkdir /mnt/sbin
        mkdir /mnt/dev
        mkdir /mnt/usr
        mkdir /mnt/var
        mkdir /mnt/tmp
        mkdir /mnt/flp
Полезно также сделать несколько ссылок:
        cd /mnt
        ln -s sbin ./bin
        ln -s sbin ./stand
        ln -s ../sbin ./usr/bin
        ln -s ../sbin ./usr/sbin
В каталоге /dev надо создать минимальный набор устройств:
        cd /mnt/dev
        mknod console   c 0 0; chmod 600 console
        mknod kmem      c 2 1 root:kmem; chmod 640 kmem
        mknod mem       c 2 0 root:kmem; chmod 640 mem
        mknod null      c 2 2; chmod 666 null
        mknod random    c 2 3; chmod 644 random
        mknod urandom   c 2 4; chmod 644 urandom
        mknod zero      c 2 12; chmod 666 zero
        mknod io        c 2 14; chmod 600 io
        mknod tty       c 1 0; chmod 666 tty
        mknod klog      c 7 0; chmod 600 klog
        mknod stdin     c 22 0; chmod 666 stdin
        mknod stdout    c 22 1; chmod 666 stdout
        mknod stderr    c 22 2; chmod 666 stderr
        mknod pci       c 78 0; chmod 644 pci
        mknod fd0       c 9  0; chmod 600 fd0
        mknod ttyv0     c 12 0; chmod 644 ttyv0
        mknod ttyv1     c 12 1; chmod 644 ttyv1
        mknod ttyv2     c 12 2; chmod 644 ttyv2
        mknod ttyv3     c 12 3; chmod 644 ttyv3
        mknod ttyp0     c 5 0; chmod 666 ttyp0
        mknod ttyp1     c 5 1; chmod 666 ttyp1
        mknod ttyp2     c 5 2; chmod 666 ttyp2
        mknod ttyp3     c 5 3; chmod 666 ttyp3
        mknod ttyp4     c 5 4; chmod 666 ttyp4
        mknod ttyp5     c 5 5; chmod 666 ttyp5
        mknod ttyp6     c 5 6; chmod 666 ttyp6
        mknod ttyp7     c 5 7; chmod 666 ttyp7
        mknod ttyp8     c 5 8; chmod 666 ttyp8
        mknod ttyp9     c 5 9; chmod 666 ttyp9
        mknod vn0       c 43 2; chmod 644 vn0
        mknod vn0a      c 43 0; chmod 644 vn0a
        mknod vn0b      c 43 1; chmod 644 vn0b
        mknod vn0c      c 43 2; chmod 644 vn0c
Не забудем, также, в /sbin на MFS положить файл crunch и сделать на него необходимые жесткие ссылки:
        #!/bin/sh

        сp crunch /mnt/sbin
        for i in `crunchgen -l crunch.conf` ; \
        do \
                ln /mnt/sbin/crunch /mnt/sbin/$i; \
                done
Теперь осталось самое сложное - заполнить каталог etc на образе MFS. Однако своими миниатюризированными конфигурационными файлами нас выручит PicoBSD. Оттуда мы возьмем, слегка модифицировав, несколько жизненно необходимых файлов.
   ttys:
        vga     none                    cons25  off     secure
        ttyv0   "/sbin/getty Pc"        cons25  on      secure
        # Virtual terminals
        ttyv1   "/sbin/getty Pc"        cons25  on      secure
        ttyv2   "/sbin/getty Pc"        cons25  on      secure
        ttyv3   "/sbin/getty Pc"        cons25  on      secure
        # Pseudo terminals
        ttyp0   none    network secure
        ttyp1   none    network secure
        ttyp2   none    network secure
        ttyp3   none    network secure
        ttyp4   none    network secure
        ttyp5   none    network secure
        ttyp6   none    network secure
        ttyp7   none    network secure
        ttyp8   none    network secure
        ttyp9   none    network secure

   gettytab:
        default:\
                :cb:ce:ck:lc:fd#1000:cl:im=\r\n%s/%m (%h) (%t)\r\n\r\n:sp#1200:

        P|Pc|Pc console:\
                :ht:np:sp#115200:

        # Fixed speed entries
        2|std.9600|9600-baud:\
                :np:sp#9600:
        g|std.19200|19200-baud:\
                :np:sp#19200:
        std.38400|38400-baud:\
                :np:sp#38400:
        std.57600|57600-baud:\
                :np:sp#57600:
        std.115200|115200-baud:\
                :np:sp#115200:

        local.9600|CLOCAL tty @ 9600 Bd:\
        :c0#0x0000c300:c1#0x0000cb00:c2#0x0000cb00:\
        :o0#0x00000007:o1#0x00000002:o2#0x00000007:\
        :i0#0x00000704:i1#0x00000000:i2#0x00000704:\
        :l0#0x000005cf:l1#0x00000000:l2#0x000005cf:\
        :sp#9600:

   login.conf:
        # Authentication methods
                auth-defaults:\
                :auth=passwd:

        auth-root-defaults:\
                :auth-login=passwd:\
                :auth-rlogin=passwd:\

        auth-ftp-defaults:\
                :auth=passwd:

        default:\
                :cputime=infinity:\
                :datasize-cur=22M:\
                :stacksize-cur=8M:\
                :memorylocked-cur=10M:\
                :memoryuse-cur=30M:\
                :filesize=infinity:\
                :coredumpsize=0:\
                :maxproc-cur=64:\
                :openfiles-cur=64:\
                :priority=0:\
                :requirehome@:\
                :umask=022:\
                :tc=auth-defaults:

        standard:\
                :copyright=/etc/COPYRIGHT:\
                :welcome=/etc/motd:\
                :setenv=MAIL=/var/mail/$,BLOCKSIZE=K,EDITOR=/usr/bin/ee:\
                :path=~/bin /bin /usr/bin:\
                :nologin=/var/run/nologin:\
                :cputime=1h30m:\
                :datasize=8M:\
                :stacksize=2M:\
                :memorylocked=4M:\
                :memoryuse=8M:\
                :filesize=8M:\
Поскольку образ MFS смонтирован в директорию /mnt, эти файлы должны быть записаны в каталог /mnt/etc.

Для загрузки этих 3-х файлов вполне хватит, однако, работать с такой системой будет практически невозможно. Для более или менее нормального функционирования, системе потребуется большинство конфигурационных файлов, наблюдаемых нами, на любой машине FreeBSD в каталоге /etc. Кроме того, как уже было сказано, изменения в файлах на MFS будут сохраняться только до перезагрузки системы. Решая эту проблему, воспользуемся опытом PicoBSD и разделим все конфигурационные файлы из каталога /etc на две группы. Первые, которые мы не планируем когда-либо изменять, запишем в каталог /etc на MFS, вторые тоже поместим в директорию /etc, но уже на файловой системе дискеты.

Как минимум, на MFS нам пригодятся файлы disktab, protocols, services и termcap, которые в очередной раз можно смело заимствовать у PicoBSD, поэтому, предполагая, что конфигурационные файлы PicoBSD располагаются в /usr/srs/release/picobsd/mfs-tree/etc, выполняем:

        cp /usr/src/release/picobsd/mfs_tree/etc/disktab /mnt/etc
        cp /usr/src/release/picobsd/mfs_tree/etc/protocols /mnt/etc
        cp /usr/src/release/picobsd/mfs_tree/etc/services /mnt/etc
        cp /usr/src/release/picobsd/mfs_tree/etc/termcap /mnt/etc
Так как остальные конфигурационные файлы будут находится за пределами MFS, при загрузки системы их придется копировать на MFS в каталог /etc. Для этого к ttys, gettytab и login.conf в каталог /etc на MFS (в настоящий момент это /mnt/etc), добавим простой rc-файл:
#!/sbin/sh
PATH=/sbin; export PATH
mount -o rdonly /dev/fd0 /flp
cd /etc
cp /flp/etc/* .
umount /flp
. rc.main

exit 0
Этот скрипт должен будет скопировать все файлы из директории /etc на дискете в одноименный каталог на образе MFS и затем вызвать "настоящий" rc-файл (rc.main), однако на этом остановимя немного позже, а сейчас, поскольку MFS-образ готов, выполняем:
        umount /mnt         # размонтируем /mnt и
        vnconfig -u vn0     # отключим связку mfsroot и /dev/vn0c
Завершив эксперименты с mfsroot, заархивируем его:
        gzip --best mfsroot
В результате получается файл mfsroot.gz объемом около 600Кб, при условии что свободное пространство на MFS-образе заполнено нулями, таким образом, операции создания и удаления файлов не оставили за собой мусора в файловой системе. Наилучшие результаты сжатия mfsroot в этом случае достигаются путем записи заранее подготовленных файлов на их точные места на образе MFS.

5. Загрузочная дискета

Настало время подготовить образ загрузочной дискеты. В общем случае, процесс загрузки FreeBSD разделен на 4 этапа, каждому из которых соответствует определенный файл в файловой системе FreeBSD:

  • MBR - /boot/boot0;
  • Stage 1 - /boot/boot1;
  • Stage 2 - /boot/boot2;
  • Stage 3 - /boot/loader.
Разумеется, файлы boot0, boot1 и boot2 являются всего лишь копиями загрузочных областей дисков, которые, по понятным причинам, находятся вне файловой системы FreeBSD.

Поскольку мы планирует загружаться с дискеты, boot0 (MBR) нас не интересует, и в нашем случае, boot-процесс начнется со Stage 1 (/boot/boot1). Во время начальной загрузки BIOS пытается прочитать 1-й сектор нулевой дорожки нулевой стороны диска в дисководе "a:", это и должен быть boot1. Boot1 - весьма простая программа, основная цель которой найти и запустить boot2 (Stage2). В принципе, уже boot2 может загрузить ядро FreeBSD, однако, он, видимо, не может распознать формат заархивированного ядра, по крайней мере, мне так и не удалось заставить его сделать это. Далее, boot2 загружает loader (Stage3), который находится уже в файловой системе в каталоге /boot. В настоящее время loader и является основным средством загрузки ядра. Подробности порядка и особенностях загрузки FreeBSD описаны в handbook.

Таким образом, необходимо чтобы в загрузочных областях дискеты находились boot1 и boot2, а также, на дискете в каталоге /boot файловой системы FreeBSD должны быть записаны файлы loader и loader.rc.

Итак, создадим загрузочный образ дискеты. В командной строке выполняем:

        dd if=/dev/zero of=flpImage count=1440 bs=1k             # Создаем файл образа дискеты - flpImage
        vnconfig -s labels -c /dev/vn0 flpImage                  # Будем работать с ним как с устройством
        disklabel -Brw -b /boot/boot1 -s /boot/boot2 vn0c fd1440 # Помещаем на него boot1 и boot2
        newfs -i 32768 -m 0 -p 0 -o space /dev/vn0с              # Создаем на нем файловую систему
        vnconfig -u vn0                                          # Отключаемся от файла как устройства
Здесь flpImage - файл образа дискеты, который в последствии будет записан на дискету.

Теперь следует подготовить файл loader.rc, который будет выполнен программой loader, в момент, когда загрузка доберется до этапа Stage3. Этот простенький скрипт будет содержать следующие строки:

        echo Loading kernel...       # Выводим на экран сообщение о загрузки ядра
        load /kernel                 # и загружаем его
        echo Loading MFS...          # Выводим сообщение о загрузки MFS-файловой системы
        load -t mfs_root /mfsroot    # И загружаем ее
        autoboot 0                   # Загружаемся сразу, а не ждем обычные 9 секунд.
Далее, полученный образ дискеты остается подмонтировать и записать на него /boot/loader и loader.rc:
        vnconfig vn0 flpImage
        mount /dev/vn0c /mnt
        mkdir /mnt/boot
        cp /boot/loader /mnt/boot
        cp loader.rc /mnt/boot
На образе дискеты создадим каталог /etc, поместив в него несколько конфигурационных файлов:
   hosts:
        127.0.0.1       localhost       localhost.mydomain
        192.168.1.1     1f-bsd          1f-bsd.somewere.net
inet.d в нашем случае может состоять всего из одной строки:
        telnet  stream  tcp     nowait  root    /usr/sbin/telnetd       telnetd
Файлы passwd, master.passwd и group можно подготовить свои, чтобы разрешить доступ только ограниченному кругу людей, или же взять с текущей работающей машины, и, таким образом, перенести на нашу систему всех пользователей.

Урезанные файлы disktab и services, уже по привычке, позаимствуем у PicoBSD, но rc.main, для простоты напишем свой:

        #!/sbin/sh

        # add swap file
        dd if=/dev/zero of=/swapfile count=1024 bs=1k
        vnconfig /dev/vn0b /swapfile
        swapon /dev/vn0b

        # make password database
        pwd_mkdb -p ./master.passwd

        # create device database
        dev_mkdb

        # setup network
        hostname 1f-bsd
        ifconfig lo0 inet 127.0.0.1
        ifconfig xl0 inet 192.168.1.1 netmask 255.255.255.0

        # Start daemons
        syslogd
        inetd

        exit 0
И наконец, поместим в корневом каталоге дискеты сжатые ядро и mfsroot:
        cp kernel.gz /mnt
        cp mfsroot.gz /mnt
Теперь можно смело размонтировать образ дискеты и отключить его от устройства vn0:
        umount /mnt
        vnconfig -u vn0
Почти все готово, единственное, что остается сделать, это вставить дискету в дисковод и выполнить:
        dd if=flpImage of=/dev/fd0
для того, чтобы записать подготовленный образ на дискету. Теперь остается лишь загрузиться с нашей дискеты и проверить, действительно ли она работает.

Оригинал статьи расположен по адресу: http://www.opennet.ru/base/sys/1f_unix.txt.html.

[ опубликовано 26/11/2003 ]

Михаил Захаров (zakharov@ipb.redline.ru) - UNIX на одной дискете (по следам PicoBSD)   Версия для печати