[linux-lvm] Another lvm2create_initrd for having a root filesystem on LVM2

Miguel Cabeça cabeca at ist.utl.pt
Thu Feb 26 19:06:02 UTC 2004


Hello,

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/

-------------- next part --------------
#!/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



More information about the linux-lvm mailing list