Install Debian on USB
From FlimzyWiki
Contents |
Prepare the USB media
Perform these steps from an installed Linux system, and *not* from the chroot prepared above.
Create a single ext2 partition on the USB drive:
foo:~/# fdisk /dev/sda Command (m for help): d Selected partition 1 Command (m for help): n Command action e extended p primary partition (1-4) p Partition number (1-4): 1 First cylinder (1-1015, default 1): Last cylinder or +size or +sizeK (1-1015, default 1015): Using default value 1015 Command (m for help): a Partition number (1-4): 1 Command (m for help): p Disk /dev/sda: 1031 MB, 1031798784 bytes 32 heads, 62 sectors/track, 1015 cylinders Units = cylinders of 1984 * 512 = 1015808 bytes Device Boot Start End Blocks Id System /dev/sda1 * 1 1015 1006849 83 Linux Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table.
In short, create a Linux (type 83) partition that is bootable. Easy enough, right?
If you own a computer with an old BIOS take care that your partition does not exceed 512 MByte (1023 cylinder limit). Otherwise you will face the grub error 18 as you try to boot.
Create a filesystem on the USB media:
foo:~/# mkfs -t ext2 /dev/sda1
Build a Debian chroot
Prepare a work directory, and mount the USB media under the working directory as /boot:
foo:~# mkdir -p /mnt/chroot/boot foo:~# mount /dev/sda1 /mnt/chroot/boot
From Debian Etch
Install Debian:
foo:~# debootstrap --arch i386 etch /mnt/chroot
From Debian Sarge
Install Debian:
foo:~# debootstrap sarge /mnt/chroot
Create /etc/apt/sources.list to point to etch:
deb http://ftp.us.debian.org/debian/ etch main
Then dist-upgrade:
foo:~# chroot /mnt/chroot foo:/# apt-get update foo:/# apt-get dist-upgrade foo:/# exit
Then continue the process as normal.
Configure Locales
Chroot to the new directory, and install the locales we want (to avoid annoying errors while installing future packages), and remove unwanted locales to save space. At minimum, you probably want to generate and keep en_US ISO-8859-1, en_US.ISO-8859-15 ISO-8859-15 and en_US.UTF-8 UTF-8.
foo:~# chroot /mnt/chroot foo:~# apt-get install locales foo:~# dpkg-reconfigure locales foo:~# apt-get install localepurge foo:~# localepurge
Install kernel & modules
You may be asked if you want to abort the install due to the kernel version. Since you are in a chroot, this is not as dangerous as debconf thinks, so do NOT abort!
foo:~# apt-get install linux-image-2.6.18-5-686 unionfs-modules-2.6.18-5-686 squashfs-modules-2.6.18-5-686 foo:~# depmod 2.6.18-5-686
Install essential tools
Install other necessary packages, including my customized version of casper (available at http://aslan.flimzy.com/casper/) and do some basic configuration
foo:~# apt-get install unionfs-tools squashfs-tools grub casper foo:~# user-setup foo:~# cd /tmp foo:~# wget http://aslan.flimzy.com/casper/casper_1.81+nymgy-2_i386.deb foo:~# dpkg -i casper_1.81+nymgy-2_i386.deb
Install optional tools
These are some tools I like to use. You may have other favorite editors or tools you want.
foo:~# apt-get install joe screen ssh less
Remove unwanted packages
Remove some packages you don't need or want:
foo:~# dpkg --purge vim-tiny vim-common tasksel tasksel-data ed nano laptop-detect dmidecode foo:~# dpkg --purge liblzo2-2 libconsole libsigc++-1.2-5c2 libldap-2.3-0 libgdbm3 man-db foo:~# dpkg --purge info manpages dgroff-base libsasl2 cyrus-sasl2-doc
If you don't need DHCP:
foo:~# dpkg --purge dhcp3-client dhcp3-common
I'm a bit more anal about wasted space than you might be, so I like to remove these packages as well:
foo:~# dpkg --purge aptitude libsigc++-2.0-0c2a
Edit/Create config files
/etc/network/interfaces
# Used by ifup(8) and ifdown(8). See the interfaces(5) manpage or # /usr/share/doc/ifupdown/examples for more information. auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp
/etc/hostname
Create this file with an appropriate hostname in it.
/etc/hosts
127.0.0.1 localhost
Add a line for your specific hostname/IP address if you wish.
/etc/fstab
proc /proc proc defaults 0 0 unionfs / unionfs rw 0 0 /dev/sda1 /boot ext2 rw 0 0
NOTE: Even if your USB drive is not /dev/sda1 on the machine you're building this on, it likely will be on the target machine when booted, so you probably want this to read /dev/sda1 anyway.
If you are not using a USB device (perhaps a CF card in an IDE adaptor), however, this device name _could_ very well be different, in which case you'll want to edit accordingly.
Other
Create any other custom configurations necessary, such as updating /etc/apt/sources.list and /etc/resolv.conf.
Create Support Scripts
/etc/initramfs-tools/hooks/casper_custom
Available [1].
#!/bin/sh -e
PREREQS="casper"
# Output pre-requisites
prereqs()
{
echo "$PREREQS"
}
case "$1" in
prereqs)
prereqs
exit 0
;;
esac
PRECLEANUP="02etc_casper_conf 10adduser 13swap 14locales 15autologin "
PRECLEANUP+="18hostname 19keyboard 20xconfig 22gnome_panel_data "
PRECLEANUP+="22screensaver 23etc_modules 23networking 24preseed "
PRECLEANUP+="25configure_init 30accessibility 31disable_update_notifier "
PRECLEANUP+="33disable_binary_drivers 34disable_kwallet "
PRECLEANUP+="35fix_language_selector 12fstab 32disable_hibernation"
script_dir="$DESTDIR/scripts/casper-bottom"
for script in $PRECLEANUP; do
rm $script_dir/$script
done
Then change permissions:
foo:~# chmod +x /etc/initramfs-tools/hooks/casper_custom
/usr/local/bin/moo
Available here.
Remark: in this externally linked script, there's an error on line 84 (one "exit" to many, which prevents updating the live system). The version below on the wiki is correct though.
#!/bin/bash
# Configurable variables
MAXLOOPS=8 # Maximum number of loop devices to allow
MAXPCT=90 # Begin deleting files on /boot when this % is used
CLEANDOC="yes" # Clean /usr/share/doc?
CLEANMAN="yes" # Clean /usr/share/man?
CLEANINF="yes" # Clean /usr/share/info?
# Do not edit anything below here!
let MAXLOOPS=$MAXLOOPS-1
EXCLUDES=/tmp/moo.excludes
INCLUDES=/tmp/moo.includes
EXLIST="^/cow/var/log/ ^/cow/var/lock/ ^/cow/tmp/ ^/cow/var/tmp/ "
EXLIST+="^/cow/sys/ ^/cow/dev/ ^/cow/cow/ ^/cow/proc/ ^/cow/var/run/ "
EXLIST+="^/cow/casper/ /.joe state$ ~$ /.bash_history$ /.lesshst$ "
EXLIST+="^/cow/etc/mtab$ ^/cow/etc/.wh.mtab~ ^/cow/var/cache/apt/archives/ "
EXLIST+="^/cow/var/lib/dhcp./ ^/cow/var/lib/urandom/ "
EXLIST+="^/cow/var/lib/dpkg/.*-old$ ~$"
EXBASE="/var/log/* /var/lock/* /tmp/* /var/tmp/* /sys/* /dev/* *~ "
EXBASE+="/cow/* /proc/* /var/run/* /etc/mtab /boot/* .lesshst .bash_history "
EXBASE+="/var/cache/apt/archives/*.deb /var/lib/dhcp?/* "
EXBASE+="/var/lib/urandom/* /var/lib/dpkg/*-old "
EXBASE+="/casper/* /var/cache/apt/archives/partial/*.deb"
echo "Checking free space on /boot..."
while [ $(df /boot | grep boot | tr -s ' ' | cut -d' ' -f5 | sed -e s/%//) \> $MAXPCT ]; do
in_use=0
zap_file=$(ls -c /boot/casper/*.squashfs | grep -v /base.squashfs | tail
-n1 | sed -e s%/boot/casper/%%)
grep $zap_file /boot/casper/order.lst && in_use=1;
if [ $in_use == 1 ]; then
echo "Unable to zap /boot/casper/$zap_file!"
echo "Please free up some space on /boot then try again"
exit
fi
echo "Zapping $zap_file"
rm /boot/casper/$zap_file
done
zerosize() {
find $* | while read file; do
echo -n "."
rm $file
touch $file
done
}
if [ "$CLEANDOC" == "yes" ]; then
echo -n "Cleaning /usr/share/doc"
zerosize /usr/share/doc -type f -size +1b
zerosize /usr/share/doc -type l
echo ""
fi
if [ "$CLEANMAN" == "yes" ]; then
echo -n "Cleaning /usr/share/man"
zerosize /usr/share/man -type f -size +1b
zerosize /usr/share/man -type l
echo ""
fi
if [ "$CLEANINF" == "yes" ]; then
echo -n "Cleaning /usr/share/info"
zerosize /usr/share/info -type f -size +1b
zerosize /usr/share/info -type l
echo ""
fi
if [ -e /boot/casper/snapshot.ver ]; then
let version=$(cat /boot/casper/snapshot.ver)+1
else
echo "Creating initial base image."
mkdir -p /boot/casper
mkdir -p /cow/
mkdir -p /casper/
mksquashfs / /boot/casper/base.squashfs -e $EXBASE
echo "base.squashfs" > /boot/casper/order.lst
echo "0" > /boot/casper/snapshot.ver
exit
fi
mount -o remount,rw /boot
echo $version > /boot/casper/snapshot.ver
let num_branches=$(/usr/sbin/unionctl / --list | wc -l)-1;
echo "$num_branches branches found..."
if [ $num_branches == $MAXLOOPS ]; then
echo "$MAXLOOPS branches already exist; Creating new base image.";
mksquashfs / /boot/casper/base-$version.squashfs -e $EXBASE
echo base-$version.squashfs > /boot/casper/order.lst
echo "Mounting new image..."
mkdir /casper/base-$version.squashfs
mount -o loop /boot/casper/base-$version.squashfs
/casper/base-$version.squashfs
unionctl / --add --after /cow --mode ro /casper/base-$version.squashfs
echo "You MUST reboot before making another backup!"
exit
fi
if [ $num_branches \> $MAXLOOPS ]; then
echo "$MAXLOOPS branches already exist. Aborting."
exit
fi
echo -n "" > $EXCLUDES
echo -n "" > $INCLUDES
echo "Reading /cow..."
cow_files=$(find /cow -depth);
echo $cow_files | sed -e 's% %\n%g' > /tmp/foo
for x in $EXLIST; do
echo CHECKING: $x >> $EXCLUDES
echo $cow_files | sed -e 's% %\n%g' | grep $x >> $EXCLUDES
cow_files=$(echo $cow_files | sed -e 's% %\n%g' | grep -v $x)
done
for x in $cow_files; do
let files=$files+1;
file[$files]=$x;
done
branches=$(/usr/sbin/unionctl / --list | grep -v /cow | sed -e 's%\s%%g' |
cut -d'(' -f1);
# Create list of excludes
for x in $(seq 1 $files); do
file=$(echo ${file[$x]} | sed -e 's%/cow%%')
zap=0
is_wh_file=0
keep_wh=0
if [ $(echo $file | grep /\.wh\.) ]; then
deleted_file=$(echo $file | sed -e s%\.wh\.%%);
is_wh_file=1
fi
for branch in $branches; do
if [ $zap == 0 ];then
if [ -f "$branch"/"$file" ]; then
/usr/bin/cmp -s /cow"$file" "$branch"/"$file" && zap=1
elif [ -d "$branch"/"$file" ]; then
grep "$file"/ $INCLUDES > /dev/null || zap=1
fi
fi
if [ $is_wh_file == 1 ] && [ $zap == 0 ]; then
if [ -e "$branch"/"$deleted_file" ]; then
keep_wh=1
fi
fi
done
if [ $is_wh_file == 1 ] && [ $keep_wh == 0 ]; then
zap=1;
fi
if [ $zap == 0 ]; then
echo $file >> $INCLUDES
echo -n "+"
else
echo /cow$file >> $EXCLUDES
echo -n "-"
fi
done
echo
echo "Creating /boot/casper/$version.squashfs..."
mksquashfs /cow /boot/casper/$version.squashfs -ef $EXCLUDES
echo $version.squashfs >> /boot/casper/order.lst
mount -o remount,ro /boot
echo "Mounting new image..."
mkdir /casper/$version.squashfs
mount -o loop /boot/casper/$version.squashfs /casper/$version.squashfs
unionctl / --add --after /cow --mode ro /casper/$version.squashfs
rm $EXCLUDES
rm $INCLUDES
Then change permissions:
foo:~# chmod +x /usr/local/bin/moo
Note that by default, 'moo' will truncate all files in /usr/share/doc, /usr/share/man, and /usr/share/info to save space. (I don't mind reading my docs on another machine!) It truncates them rather than deleting them so that dpkg doesn't get confused when files go missing! 0-byte files will compress to practically no space in squashfs, so this shouldn't be much of a waste of space. I found that truncating these files saved roughly 2-5mb of space in the squashfs on a minimalistic Debian install.
If you do not want to delete these files, edit the 'moo' script and change the CLEANMAN, CLEANDOC, and/or CLEANINF variables to "no".
Putting it all together
Install any additional packages you know you'll need, and make any final configurations. Then package the chroot system onto your flash drive:
foo:~# mkdir -p /boot/casper foo:~# update-initramfs -u all foo:~# moo
The moo script will create a few essential directories (/cow, /casper, /boot/casper) and build the base squashfs and place it in /boot/casper.
Install GRUB
You must exit the chroot so that grub-install has proper access to the necessary device files:
foo:~# exit
Then run the following command. Replace '/dev/sda' with the appropriate name of your flash drive on the installation system (for me it's actually '/dev/sdc'):
foo:~# grub-install --recheck --root-directory=/mnt/chroot /dev/sda
Then go back into the chroot, and configure grub.
foo:~# chroot /mnt/chroot foo:~# update-grub
Then manually edit the /boot/grub/menu.lst file. find the line that begins with # kopt= and change it to read:
# kopt=boot=casper ramdisk_size=16384 root=/dev/ram rw ip=frommedia quickreboot livemount=/boot showmounts
Also edit the line that begins with # groot and change it so that it corresponds to the device your flash drive will be on reboot. If you have no SATA drives, above grub-install command would have detected /dev/sda as (hd1,0) and that's what your grub root device should be.
Then update the final configuration, exit the chroot, and unmount the media:
foo:~# update-grub foo:~# exit foo:~# umount /mnt/chroot/boot
Plug the USB stick into a bootable system, and give it a try!
Making changes
At this point, you can update the live system using the 'moo' command as described below. If you want to re-write the main base image, you can do that by remounting your flash drive in /mnt/chroot/boot, and following these steps:
foo:~# mount /dev/sda1 /mnt/chroot/boot foo:~# chroot /mnt/chroot foo:~# rm /boot/casper/*
This removes the previously created image. Then make any changes, and run the 'moo' command again:
foo:~# moo
If you make any changes to the kernel, you will need to re-run the update-initramfs command:
foo:~# update-initramfs -u all
Cleaning up
When you are confident that you no longer need to make changes from your installation system, you can zap the chroot you've created--all of the necessary files are backed up to your flash drive, so they're no longer needed. Be sure your flash drive is no longer mounted on /mnt/chroot/boot before you do this!
foo:~# rm -rf /mnt/chroot
Updating the live system
Any time you make a change to your Debian-on-USB system that needs to be persistent, you will have to run the moo command. The moo command compares the /cow (ramdisk partition) with the underlying squashfs filesystem(s), and writes any changes to a new squashfs, stores it on the USB boot media, and inserts it into the unionfs.
Due to the limitation of the linux kernel for only mounting 8 loop devices, moo will create a new base image after 7 partial images are created. When this happens, moo will inform you that a reboot is desirable. I suggest following this advice, because additional backups cannot be created after this, without unpredicatable results.

