This HowTo describes how to create a system booting from a Compact Flash (CF) card, but operating (almost only) in the RAM. Because of the limited program-/erase-cycles of a CF card, it is important to reduce these cycles to a minimum. Though, your system should be able to save some configuration-changes in /etc. That's why I decided to put /etc on an extra r/w-partition on the cf-card. Everything else in / is in RAM an lost when the system reboots (!).
The flexibility of my approach ist the gentoo-chroot behind the scenes. the system is built from a chroot which gives you full control over all packages with portage, easy updating and customization possibilities.
This HowTo also implies that you have an IDE-CF-controller which is accessable via /hdX. Of course, if your card-reader has a different controller, replace /hdX with your card-reader device.
Here's a brief overview about the system-architecture:
1) the CF-card has two partitions: hdc1 (for /boot and root.tgz) and hdc2 (for /etc)
2) the system boots with grub and uses the initrd from /boot
3) in the "pre-boot-enviroment" (initrd), hdc1 (ro) and hdc2 (rw) are mounted to /cf
4) then the root.tgz tarball is extracted to /new and hdc2 mounted to /new/etc (still in the initrd)
5) pivot_root makes /new to our new root enviroment
6) control is passed to init booting the system
Requirements
You need to have enough space on your system. Depending on what packages you need to include in your CF-system, this will vary. I'd say in average a 4GB free space is fair enough.
You must be familiar with installing Gentoo using a stage1/2 tarball because that's what we're going to use in this mini-HowTo. If you're in doubt, consult the Gentoo Handbook (http://www.gentoo.org/doc/en/handbook/).
Setting up the build environment
Our build environment is a directory we will 'chroot' into and install gentoo. Its contents will make the CF-system later on. I will use the name 'source' for it, and will create it under a directory in my home called 'base'.
Code: Select all
cd ~
mkdir -p base/source
Code: Select all
cd base/source
tar xvjpf ~/stage1-x86-2005.0.tar.bz2
# or
tar xvjpf ~/stage2-x86-2005.0.tar.bz2
mkdir oldroot
Code: Select all
cd base/source/usr/
tar xvjf ~/portage-whatever.tar.bz2
Your source directory is now ready to start installating gentoo.
Installing gentoo
Setup necessary mount points before you 'chroot' to your build directory and start installation. On my system I keep 'distfiles' under the default '/usr/portage' directory. I'm going to use it instead of downloading distfiles again during the installation.
Code: Select all
cd base/source
mount -o bind /proc proc
mkdir usr/portage/distfiles
mount -o bind /usr/portage/distfiles usr/portage/distfiles
Code: Select all
######################################
# /etc/make.conf
######################################
USE="alsa -arts -avi -bitmap-fonts -cdr -cups -dvd -emacs -esd -foomaticdb -gif -gtk -gtk2 -jpeg -gnome -kde -nls -motif -mp3 -mpeg -oggvorbis -opengl -oss perl -pdflib -png -ppds -qt -quicktime samba -spell -truetype -truetype-fonts -type1-fonts -X -xmms"
#from http://radagast.bglug.ca/epia/epia_howto/index.html
# notice the -Os optimization for small binaries!
CFLAGS="-march=i586 -m3dnow -mmmx -Os -pipe -fomit-frame-pointer"
CHOST="i586-pc-linux-gnu"
CXXFLAGS="${CFLAGS}"
GENTOO_MIRRORS="http://ftp.uni-erlangen.de/pub/mirrors/gentoo http://ftp.tu-clausthal.de/pub/linux/gentoo http://gentoo.oregonstate.edu http://www.ibiblio.org/pub/Linux/distributions/gentoo"
MAKEOPTS="-j2"
Code: Select all
cd base/source
chroot . /bin/bash --login
env-update && source /etc/profile
cd /usr/portage; scripts/bootstrap.sh (stage1 only: bootstrap system)
emerge --newuse system (stage1, stage2 only: install base system)
ln -sf /usr/share/zoneinfo/<path to time zone file> /etc/localtime
emerge syslog-ng vixie-cron hotplug udev
rc-update add syslog-ng default
rc-update add vixie-cron default
rc-update add hotplug default (add also hostname, domainname, net.eth?, etc.)
emerge foo bar baz ... (custom packages)
Code: Select all
# <fs> <mountpoint> <type> <opts> <dump/pass>
none / tmpfs defaults 0 0
/dev/hdc2 /etc ext3 noatime 0 0
none /proc proc defaults 0 0
none /dev/shm tmpfs defaults 0 0
Merge your desired kernel now. I decided to use the 'hardened-dev-sources' since my system will be on the internet and should be armed against evil intruders like 'eve' or 'trudy' :)
When configuring your kernel, make sure the following is compiled in
1. initrd support, set size to 8MB (8192kb)
2. ext2/ext3 filesystem support (which we use for our initrd image and cf-card)
3. tmpfs filesystem support
Code: Select all
emerge hardened-dev-sources
cd /usr/src/linux
make menuconfig
make -j? && make modules_install && cp arch/i386/boot/bzImage /boot (? = cpu-count+1)
Code: Select all
echo 'sys-boot/grub ~x86 >> /etc/portage/package.keywords'
emerge grub
Code: Select all
default 0
timeout 5
splashimage=(hd0,0)/boot/grub/splash.xpm.gz
title globalways gentoo linux
root (hd0,0)
kernel /boot/bzImage root=/dev/ram0 rw init=/linuxrc
initrd /boot/initrd
This is where most of the action occures during booting. It's a very simple task once you get the hang of it. First create the image, I'm using an 8MB initrd but feel free to expand/reduce that if you need more/less, just remember to set the option in your kernel configuration for the maximum ramdisk size properly.
Code: Select all
touch /boot/initrd
dd if=/dev/zero of=/boot/initrd bs=1024k count=8
losetup /dev/loop0 /boot/initrd
mke2fs /dev/loop0
mkdir /mnt/initrd
mount /dev/loop0 /mnt/initrd
Code: Select all
cd /mnt/initrd
mkdir etc dev lib bin proc new cf
touch linuxrc
chmod +x linuxrc
touch etc/mtab
touch etc/fstab
Now you need to copy necessary files into bin and lib. For bin, copy the following:
Code: Select all
/bin/sh
/bin/cat
/bin/mount
/bin/umount
/bin/mkdir
/bin/chroot
/bin/tar
/sbin/pivot_root
/usr/bin/beep
Code: Select all
cd /mnt/initrd/bin
ls | xargs ldd | grep '/' | cut -d '(' -f 1 | sort -n | uniq
cp /lib/libc.so.6 /mnt/initrd/lib/
cp /lib/ld-linux.so.2 /mnt/initrd/lib/
... (do this for all libraries shown)
...and there it is. JeffW makes our lives easier with this sweet line:
Code: Select all
carter initrd # find /bin -type f | xargs ldd | grep '/lib/' | awk '{print $3}' | sort -n | uniq | xargs -i, cp , /mnt/initrd,
carter initrd # find
.
./cf
./bin
./dev
./etc
./lib
./lib/libm.so.6
./lib/libacl.so.1
./lib/libresolv.so.2
./lib/libcom_err.so.2
./lib/libncurses.so.5
./lib/libe2p.so.2
./lib/libattr.so.1
./lib/libproc.so.3.1.15
./lib/libcrypt.so.1
./lib/ld-linux.so.2
./lib/libdl.so.2
./lib/libuuid.so.1
./lib/libpthread.so.0
./lib/libpam_misc.so.0
./lib/libpam.so.0
./lib/libc.so.6
./lib/librt.so.1
./new
./usr
./usr/lib
./usr/lib/libcrack.so.2
./usr/lib/liblockfile.so.1
./proc
carter initrd # Code: Select all
mknod /mnt/initrd/dev/console c 5 1
mknod /mnt/initrd/dev/null c 1 3
mknod /mnt/initrd/dev/hda b 3 0
mknod /mnt/initrd/dev/hdb b 3 64
mknod /mnt/initrd/dev/hdc b 22 0
mknod /mnt/initrd/dev/hdc1 b 22 1
mknod /mnt/initrd/dev/hdc2 b 22 2
mknod /mnt/initrd/dev/hdd b 22 64
mknod /mnt/initrd/dev/tty c 4 0
mknod /mnt/initrd/dev/loop0 b 7 0
Finally, we need to create our linuxrc script. The script will do the follwoing:
1. Save whatever is passed to the kernel to pass it later on to /sbin/init
2. Mount hdc1 (fist partitiong on the CF-card) /cf and extract it to /new. This will become our root filesystem, which is read-only
3. Mount hdc2 to /cf/etc and extract the etc-tarball to /new/etc
4. Finally, pivot the root filesystem on newroot and start the real init process
Code: Select all
#!/bin/sh
#
# hdc1 = read-only for /boot and root.tar
# hdc2 = read/write for /etc
#
# we need to access the binaries in the initrd
export PATH=/bin
# get kernel CMDLINE
mount -t proc none /proc
CMDLINE=`cat /proc/cmdline`
umount /proc
# mount compact flash device
mount -t ext3 -o ro /dev/hdc1 /cf &> /dev/null
if [ $? != 0 ]; then
echo "[ !! ] couldn't mount root file-system !"
exec /bin/sh
else
echo "[ ok ] root file-system successfully mounted (ro) !"
fi
# create the tmpfs for / and untar the system into it
mount -t tmpfs -o size=256m none /new &> /dev/null
cd /new
tar zxpf /cf/root.tgz &> /dev/null
if [ $? != 0 ]; then
echo "[ !! ] couldn't untar root file-system !"
/bin/beep -f 1500 -r 100000 -l 200 -d 1000
else
echo "[ ok ] root file-system successfully untared !"
/bin/beep -f 1000 -r 3 -l 200 -d 500
fi
# check for an existing /etc (possibly from the root-tarball)
mkdir /new/etc
if [ $? != 0 ]; then
echo "[ !! ] /etc already exists, cleaning ...!"
rm -rf /new/etc/*
else
echo "[ ok ] /etc created !"
fi
# mount writeable /etc in new root-enviroment
mount -t ext3 /dev/hdc2 /new/etc &> /dev/null
if [ $? != 0 ]; then
echo "[ !! ] couldn't mount /etc !"
else
echo "[ ok ] /etc mounted (rw) !"
/bin/beep -f 1000 -l 200 -d 500
fi
# pivot root and start real init
cd /new
pivot_root . oldroot
exec chroot . /bin/sh <<- EOF >dev/console 2>&1
umount /oldroot/cf
umount /oldroot
exec /sbin/init ${CMDLINE}
EOF
Our source directory is ready. Exit 'chroot' and unmount proc and distfiles (and maybe some customized binds).
Building the CF-system
Building the CF-system invloves the follwoing steps:
1. Clean up unnecessary directories in source (like /tmp and /var/tmp)
2. Create the target directory that will contain the files on the CF-card
3. call `geninc` to generate our included files (includes.lst) in our root-tarball
4. create the file-system-tarballs for each CF-partition
To help automate these steps, create a simple 'build' script inside 'base'
Code: Select all
cd base
touch build
chmod +x build
Code: Select all
#!/bin/bash
INCLUDES='../includes.lst'
# prepare the source-tree
rm -rf source/var/tmp/*
rm -rf source/var/run/*
rm -rf source/var/lock/*
rm -rf source/tmp/*
rm -f source/etc/mtab
touch source/etc/mtab
# copy /boot and /etc
rm -rf target
mkdir target
cp -a source/boot target/
cp -a source/etc target/
# generate the file list
echo "[ >> ] generating include-list..."
./geninc
# create the rootFS
cd source/
echo "[ >> ] building root.tgz..."
tar czpf ../target/root.tgz -T $INCLUDES #&> /dev/null
cd ../target
echo "[ >> ] building exportable tarballs..."
tar cpf ../last-boot.tar boot root.tgz && echo "[ ok ] /boot & root.tgz built!"
cd etc
tar cpf ../../last-etc.tar * && echo "[ ok ] /etc built!"
Code: Select all
#!/bin/bash
SOURCETEE='./source'
INC='includes.lst'
############################
# populating root
#
cat << EOF > $INC
bin
dev
home
lib
mnt
oldroot
opt
proc
root
sbin
sys
tmp
EOF
############################
# populating var
#
cat << EOF > $INC
var/cache
var/empty
var/lib
var/lock
var/log
var/mail
var/run
var/spool
var/state
var/tmp
EOF
############################
# populating usr/lib (aka the big guy)
#
# basic libraries
find ${SOURCETREE}/usr/lib -maxdepth 1 -iname '*.so*' >> $INC
# special stuff
find ${SOURCETREE}/usr/lib -iname 'libstdc++.so*' >> $INC
cat << EOF >> $INC
usr/lib/groff
usr/lib/gcc-config
usr/lib/gentoolkit
usr/lib/perl5
usr/lib/pkgconfig
usr/lib/portage
usr/lib/pppd
usr/lib/quagga
usr/lib/tc
EOF
############################
# rest of /usr (what is really necessary?)
cat << EOF >> $INC
usr/bin
usr/i586-pc-linux-gnu
usr/libexec
usr/local
usr/man
usr/sbin
usr/share/consolefonts
usr/share/keymaps
usr/share/misc/file/magic
usr/share/nmap
usr/share/zoneinfo/Europe/Berlin
usr/tmp
EOF
# possible candidates ?!
# -------------------
# usr/lib/hotplug
# usr/lib/binutils
# usr/lib/gconv
# usr/lib/glib
# usr/lib/grub
# usr/share/cracklib
# usr/share/gettext
After executing the build-script, you will have to new files in your current directory:
- last-boot.tar
- last-etc.tar
here is a sample script for deploying the system on a CF-card:
Code: Select all
#!/bin/bash
#
# this script deploys the latest tarball of the cf-system
# it should be started from the machine with the cf-card!
#
BOOT='/base/last-boot.tar'
ETC='/base/last-etc.tar'
BOOT_MOUNT='/mnt/base/boot'
ETC_MOUNT='/mnt/base/etc'
# mounting the devices
echo "[ >> ] creating and mounting the partitions..."
mke2fs /dev/hdc1
tune2fs -j -i 0 -c 0 /dev/hdc1
mke2fs /dev/hdc2
tune2fs -j -i 0 -c 0 /dev/hdc2
mount /dev/hdc1 ${BOOT_MOUNT}
mount /dev/hdc2 ${ETC_MOUNT}
# extracting the tarballs
echo "[ >> ] extracting the tarballs..."
tar xpf ${BOOT} -C ${BOOT_MOUNT}
tar xpf ${ETC} -C ${ETC_MOUNT}
# install grub on hdc1
grub-install --root-directory=${BOOT_MOUNT} /dev/hdc
# unmounting the devices
echo "[ >> ] unmounting the devices..."
umount ${BOOT_MOUNT}
umount ${ETC_MOUNT}
# say something ;)
if [ $? != 0 ]; then
echo "[ !! ] an error occured while deploying the system !"
else
echo "[ ok ] system deployed !"
fi
I use a simple script to chroot to my build source each time I need to sync portage, merge new packages, customize configuration files. I called it 'work'.
Code: Select all
cd base
touch work
chmod +x work
Code: Select all
#!/bin/bash
mount -o bind /proc source/proc
mount -o bind /sys source/sys
#mount -o bind /dev source/dev
#mount -o bind /dev/pts source/dev/pts
mount -o bind /usr/portage/distfiles source/usr/portage/distfiles
chroot source/ /bin/bash --login
umount source/proc
umount source/sys
#umount source/dev/pts
#umount source/dev
umount source/usr/portage/distfiles
Troubleshooting
- If booting the CF fails at some point during linuxrc script execution, you can always change your grub line to 'init=/bin/sh' instead of linuxrc. This will give you a nice little shell inside the initrd image at boot time. From there you can type the commands in the linuxrc script manually and try to find out with one is failing and why.
Actually you shouldn't thank me, but rather veezi who allowed me to copy the scaffold for this HowTo from is own LiveCD-HowTo.
I only copied all his work and modified some essential parts, because I love his chroot-concept. Comments are expressly desired!
Please help me to make this HowTo more robust, i just collected my thoughts during the development of the system, so some steps might miss or seem unclear. your feedback will improve this HowTo and will make it easy reproducable for everyone !!


