After some time reading docs and other scripts I decided to write my very own lvm2create_initrd.
Inspiration to write this script came from various sources 1. The original LVM lvmcreate_initrd [1] This is the script I started with
2. Kernel Documentation/initrd.txt [2]
This is where I learned the two mechanisms to change the root filesystem: change_root and pivot_root
3. EVMS INSTALL.initrd & linuxrc [3] Some ideas like greping the /proc/cmdline for lvm2root parameter
4. Jeffrey Layton's lvm2create_initrd: [4] Some ideas like lvm2rescue and more
5. Christophe Saout's initrd & linuxrc: [5]
Great linuxrc. However it still uses the deprecated change_root mechanism and devfs
This script tries to be the combination of the best features of all sources and implements a pure pivot_root mechanism to change the root filesystem.
To use the initrd generated by this script you have to pass the kernel some parameters:
root=/dev/ram0 (mandatory) This is needed for the kernel to treat the initrd ramdisk as root and execute /sbin/init with PID 1 as opposed to starting /linuxrc with pid !=1. This is necessary for the final part of the script.
lvm2root=/dev/VG/LV
(mandatory) lvm2root is the name of your LV root volume, for example /dev/system/root
lvm2rescue
(optional) you can pass this option to have access to a shell inside initrd
If you use lilo try adding/modifying an entry similar to this one in lilo.conf:
image=/boot/vmlinuz-lvm2-2.6.3-4 label="ramdisk_LVM" initrd=/boot/initrd-lvm2-2.6.3-4.gz append="root=/dev/ram0 lvm2root=/dev/system/root <other parameters>"
If using grub try adding/modifying an entry similar to this one in menu.lst
title ramdisk LVM
kernel /boot/vmlinuz-lvm2-2.6.3-4 root=/dev/ram0 lvm2root=/dev/system/root <other parameters>
initrd /boot/initrd-lvm2-2.6.3-4.gz
This script has only been tested on Debian sid (unstable) with kernel 2.6 with everything required to boot
the root filesystem built-in (not as modules). Ex: SCSI or IDE, RAID, device mapper
It does not support modules nor devfs as devfs is deprecated in the 2.6 kernel series
It needs lvm2 tools, busybox, pivot_root, MAKEDEV
Comments are welcome
Have fun
Miguel Cabeça
CIIST - IST - Lisboa - Portugal
[1] ftp://ftp.sistina.com/pub/LVM/1.0/ [2] http://www.kernel.org/ [3] http://evms.sourceforge.net/ [4] http://poochiereds.net/svn/lvm2create_initrd/ [5] http://www.saout.de/misc/
#!/bin/bash # # lvm2create_initrd v0.1 26/02/2004 # Miguel Cabeca # cabeca (at) ist (dot) utl (dot) pt # # Inspiration to write this script came from various sources # # Original LVM lvmcreate_initrd: ftp://ftp.sistina.com/pub/LVM/1.0/ # Kernel initrd.txt: http://www.kernel.org/ # EVMS INSTALL.initrd & linuxrc: http://evms.sourceforge.net/ # Jeffrey Layton's lvm2create_initrd: http://poochiereds.net/svn/lvm2create_initrd/ # Christophe Saout's initrd & linuxrc: http://www.saout.de/misc/ # # This script was only tested with kernel 2.6 with everything required to boot # the root filesystem built-in (not as modules). Ex: SCSI or IDE, RAID, device mapper # It does not support devfs as it is deprecated in the 2.6 kernel series # # It needs lvm2 tools, busybox, pivot_root, MAKEDEV # # It has been tested on Debian sid (unstable) only # TMPMNT=/tmp/mnt.$$ DEVRAM=/tmp/initrd.$$ BINFILES="/lib/lvm-200/lvm /bin/bash /bin/busybox /sbin/pivot_root" ETCFILES="/etc/lvm/lvm.conf" BUSYBOXSYMLINKS="ar cat chgrp chmod chown chroot clear cp cut df dirname dmesg echo expr false find free grep gunzip gzip halt head hostname id ifconfig kill killall klogd linuxrc ln loadkmap logger losetup ls md5sum mkdir mknod mkswap more mount mv ping poweroff ps pwd reboot reset rm rmdir route sed sleep sort swapoff swapon sync syslogd tail tar test touch tr true tty umount uname uniq uptime wc wget which whoami zcat" BASICDEVICES="std consoleonly fd" # This is were you tell the script wich block devices it will create on the initrd with MAKEDEV(8) # see MAKEDEV man page for details (man MAKEDEV) # Personally I only use LVM on top of md devices. Use 'hd{a..l}' for IDE 'sd{a..h}' for SCSI and 'md' for raid # Also make sure you have an /etc/lvm/lvm.conf properly set up with filters # BLOCKDEVICES="hda hdc sda sdb sdc md" BLOCKDEVICES="md" # Uncomment this if you want to disable automatic size determination #INITRDSIZE=4096 PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH usage () { echo "Create an initial ramdisk image for LVM2 root filesystem" echo "$cmd: [-h] [-v] [-V] [kernel version]" echo " -h|--help print this usage message" echo " -v|--verbose verbose progress messages" echo " -c|--lvmconf path to lvm.conf (/etc/lvm/lvm.conf)" echo " -V|--version print script version and exit" } verbose () { [ "$VERBOSE" ] && echo "`echo $cmd | tr '[a-z0-9/_]' ' '` -- $1" || true } cleanup () { [ "`mount | grep $DEVRAM`" ] && verbose "unmounting $DEVRAM" && umount $DEVRAM [ -f $DEVRAM ] && verbose "removing $DEVRAM" && rm $DEVRAM [ -d $TMPMNT ] && verbose "removing $TMPMNT" && rmdir $TMPMNT verbose "exit with code $1" exit $1 } trap " verbose 'Caught interrupt' echo 'Bye bye...' cleanup 1 " 1 2 3 15 create_init () { cat << 'INIT' > $TMPMNT/sbin/init #!/bin/bash # include in the path some dirs from the real root filesystem # for chroot, blockdev PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/lvm-200" PRE="initrd:" echo "$PRE Remounting / read/write" mount -t ext2 -o remount,rw /dev/ram0 / # We need /proc for device mapper echo "$PRE Mounting /proc" mount -t proc none /proc # Create the /dev/mapper/control device for the ioctl # interface using the major and minor numbers that have been allocated # dynamically. echo -n "$PRE Finding device mapper major and minor numbers " MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices) MINOR=$(sed -n 's/^ *\([0-9]\+\) \+device-mapper$/\1/p' /proc/misc) if test -n "$MAJOR" -a -n "$MINOR" ; then mkdir -p -m 755 /dev/mapper mknod -m 600 /dev/mapper/control c $MAJOR $MINOR fi echo "($MAJOR,$MINOR)" # Device-Mapper dynamically allocates all device numbers. This means it is possible # that the root volume specified to LILO or Grub may have a different number when the # initrd runs than when the system was last running. In order to make sure the # correct volume is mounted as root, the linuxrc script must determine what the # desired root volume name is. # # Get the LVM2 root volume name from the kernel command line. In order for # this to work correctly, "lvm2root=/dev/Volume_Group_Name/Root_Volume_Name" needs to be passed # to the kernel command line (where Root_Volume_Name is replaced by your actual # root volume's name. for arg in `cat /proc/cmdline`; do echo $arg | grep '^lvm2root=' > /dev/null if [ $? -eq 0 ]; then rootvol=${arg#lvm2root=} break fi done echo "$PRE Activating LVM2 volumes" lvm vgchange --ignorelockingfailure -P -a y # run a shell if we're passed lvm2rescue on commandline grep lvm2rescue /proc/cmdline 1>/dev/null 2>&1 if [ $? -eq 0 ]; then /bin/echo /bin/echo "*** Entering LVM2 rescue shell. Exit shell to continue booting. ***" /bin/echo /bin/bash fi echo "$PRE Mounting root filesystem $rootvol" mkdir /rootvol if ! mount -t auto $rootvol /rootvol; then echo "\t*FAILED*"; return 0 fi echo "$PRE Umounting /proc" umount /proc echo "$PRE Changing roots" cd /rootvol mkdir -p initrd if ! pivot_root . initrd ; then echo "\t*FAILED*" fi echo "$PRE Proceeding with boot..." exec chroot . /bin/sh -c 'umount /initrd; blockdev --flushbufs /dev/ram0 ; exec /sbin/init' < dev/console > dev/console 2>&1 INIT chmod 555 $TMPMNT/sbin/init } # # Main # cmd=`basename $0` DEVFILES="" LIBFILES="" INITRDFILES="" VERSION=`uname -r` while [ $# -gt 0 ]; do case $1 in -h|--help) usage; exit 0;; -V|--version) exit 0;; -v|--verbose) VERBOSE="y";; -c|--lvmconf) LVMCONF=$2; shift;; [2-9].[0-9]*.[0-9]*) VERSION=$1;; *) echo "$cmd -- invalid option '$1'"; usage; exit 0;; esac shift done INITRD=${INITRD:-"/boot/initrd-lvm2-$VERSION.gz"} echo "$cmd -- make LVM initial ram disk $INITRD" echo "" for a in $BINFILES; do if [ ! -r $a ] ; then echo "$cmd -- ERROR: you need $a" exit 1; fi; done # Figure out which shared libraries we actually need in our initrd echo "$cmd -- finding required shared libraries" verbose "BINFILES: `echo $BINFILES`" LIBFILES=`ldd $BINFILES 2>/dev/null | awk '{if (/=>/) { print $3 }}' | sort -u` if [ $? -ne 0 ]; then echo "$cmd -- ERROR figuring out needed shared libraries" exit 1 fi verbose "Shared libraries needed: `echo $LIBFILES`" # Calculate the the size of the ramdisk image. # Don't forget that inodes take up space too, as does the filesystem metadata. echo "$cmd -- calculating initrd filesystem parameters" if [ -z "$INITRDSIZE" ]; then echo "$cmd -- calculating loopback file size" verbose "finding size" INITRDSIZE="`du -Lck $BINFILES $LIBFILES $ETCFILES | tail -1 | cut -f 1`" verbose "minimum: $INITRDSIZE kB for files + inodes + filesystem metadata" INITRDSIZE=`expr $INITRDSIZE + 512` # enough for ext2 fs + a bit fi INITRDFILES="`echo $BINFILES $LIBFILES $ETCFILES`" echo "$cmd -- making loopback file ($INITRDSIZE kB)" verbose "using $DEVRAM as a temporary loopback file" dd if=/dev/zero of=$DEVRAM count=$INITRDSIZE bs=1024 > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "$cmd -- ERROR creating loopback file" cleanup 1 fi echo "$cmd -- making ram disk filesystem" verbose "mke2fs -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE" [ "$VERBOSE" ] && OPT_Q="" || OPT_Q="-q" mke2fs $OPT_Q -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE if [ $? -ne 0 ]; then echo "$cmd -- ERROR making ram disk filesystem" echo "$cmd -- ERROR you need to use mke2fs >= 1.14 or increase INITRDSIZE" cleanup 1 fi verbose "creating mountpoint $TMPMNT" mkdir $TMPMNT if [ $? -ne 0 ]; then echo "$cmd -- ERROR making $TMPMNT" cleanup 1 fi echo "$cmd -- mounting ram disk filesystem" verbose "mount -o loop $DEVRAM $TMPMNT" mount -oloop $DEVRAM $TMPMNT if [ $? -ne 0 ]; then echo "$cmd -- ERROR mounting $DEVRAM on $TMPMNT" cleanup 1 fi verbose "creating basic set of directories in $TMPMNT" (cd $TMPMNT; mkdir bin dev etc lib proc sbin var) if [ $? -ne 0 ]; then echo "$cmd -- ERROR creating directories in $TMPMNT" cleanup 1 fi # Add some /dev files echo "$cmd -- adding required /dev files" verbose "BASICDEVICES: `echo $BASICDEVICES`" verbose "BLOCKDEVICES: `echo $BLOCKDEVICES`" [ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="" (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES) if [ $? -ne 0 ]; then echo "$cmd -- ERROR adding /dev files" exit 1 fi # copy necessary files to ram disk echo "$cmd -- copying initrd files to ram disk" [ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="--quiet" verbose "find \$INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT" find $INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT if [ $? -ne 0 ]; then echo "$cmd -- ERROR cpio to ram disk" cleanup 1 fi echo "$cmd -- creating symlinks to busybox" [ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="" for link in $BUSYBOXSYMLINKS ;do ln -s $OPT_Q busybox $TMPMNT/bin/$link done echo "$cmd -- creating new /sbin/init" create_init if [ $? -ne 0 ]; then echo "$cmd -- ERROR creating init" cleanup exit 1 fi verbose "removing $TMPMNT/lost+found" rmdir $TMPMNT/lost+found echo "$cmd -- ummounting ram disk" umount $DEVRAM if [ $? -ne 0 ]; then echo "$cmd -- ERROR umounting $DEVRAM" cleanup 1 fi echo "$cmd -- creating compressed initrd $INITRD" verbose "dd if=$DEVRAM bs=1k count=$INITRDSIZE | gzip -9" dd if=$DEVRAM bs=1k count=$INITRDSIZE 2>/dev/null | gzip -9 > $INITRD if [ $? -ne 0 ]; then echo "$cmd -- ERROR creating $INITRD" cleanup 1 fi cat << FINALTXT -------------------------------------------------------- Your initrd is ready in $INITRD Don't forget to set root=/dev/ram0 in kernel parameters Don't forget to set lvm2root=/dev/VG/LV in kernel parameters, where LV is your root volume If you use lilo try adding/modifying an entry similar to this one in lilo.conf: image=/boot/vmlinuz-lvm2-2.6.3-4 label="ramdisk_LVM" initrd=/boot/initrd-lvm2-2.6.3-4.gz append="root=/dev/ram0 lvm2root=/dev/system/root <other parameters>" If using grub try adding/modifying an entry similar to this one in menu.lst title ramdisk LVM kernel /boot/vmlinuz-lvm2-2.6.3-4 root=/dev/ram0 lvm2root=/dev/system/root <other parameters> initrd /boot/initrd-lvm2-2.6.3-4.gz You can also pass lvm2rescue to the kernel to get a shell -------------------------------------------------------- FINALTXT cleanup 0