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

Михаил Захаров [email protected]

Содержание

  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 ([email protected]). Эта программа собирает один большой бинарный файл из исходных кодов нескольких программ. Напишем для нее собственный конфигурационный файл 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:

Разумеется, файлы 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 

для того, чтобы записать подготовленный образ на дискету. Теперь, остается лишь загрузиться с нашей дискеты и проверить, действительно ли она работает.