Another lvm2create_initrd for having a root filesystem on LVM2
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.gzThis 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
doneecho "$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
fiecho "$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
Quoted 1 line · Miguel Cabe�a
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"
busybox can make its own symlinks, so you shouldn't build them into the script. This will also work better if someone decides to build busybox with a different set of tools than you built it with.
[linux-lvm] Patch for Miguel Cabeça's lvm2create_initrd script
In reply to Miguel CabeçaHi all,
I recently upgraded the kernel on my box and had an awful time getting
my old initrd image that relies on change_root() to work. I switched to Miguel's new script and it works great, though I had to make a couple of tweaks:
The first one is pretty minor. I have /usr set up as a separate
partition, and debian puts chroot in /usr/sbin. Luckily, chroot is also a busybox function, so I simply added /initrd/sbin and /initrd/bin to the PATH so that it could find a working chroot after pivot_root occurs.
The second one is an ugly hack, and I'm definitely open to better ways
of handling it. After my kernel upgrade (from 2.6.1 to 2.6.4-rc2), the major number of the device mapper devices on my system changed. So, I added a hack to copy the contents of the ramdisk /dev/mapper directory to the /dev/mapper directory on the lvm2 root filesystem, prior to the pivot_root. It works for me, but I'd love to hear about better ways of handling this.
I've attached a patch to this email, or you can get the updated script from:
http://poochiereds.net/svn/lvm2create_initrd
Signature · 1 line
Jeff Layton <[email protected]>
lvm2create_initrd.sh (working copy) renamed from lvm2create_initrd.sh (revision 70) +5 -1
@@ -77,7 +77,7 @@ # include in the path some dirs from the real root filesystem # for chroot, blockdev -PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/lvm-200" +PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/lvm-200:/initrd/bin:/initrd/sbin" PRE="initrd:" echo "$PRE Remounting / read/write"
@@ -141,6 +141,10 @@ return 0 fi +echo "$PRE Copying /dev/mapper devices to $rootvol" +cd /dev +tar cf - ./mapper | ( cd /rootvol/dev; tar xvf - ) + echo "$PRE Umounting /proc" umount /proc
Re: [linux-lvm] Patch for Miguel Cabe?a's lvm2create_initrd script
Quoted 7 lines · Jeff Layton
The second one is an ugly hack, and I'm definitely open to better ways
of handling it. After my kernel upgrade (from 2.6.1 to 2.6.4-rc2), the major number of the device mapper devices on my system changed. So, I added a hack to copy the contents of the ramdisk/dev/mapperdirectory to the/dev/mapperdirectory on the lvm2 root filesystem, prior to thepivot_root. It works for me, but I'd love to hear about better ways of handling this.
Mount tmpfs on /dev/mapper, and then run 'dmsetup mknods' ?
- Joe
Re: [linux-lvm] Patch for Miguel Cabe?a's lvm2create_initrd script
Quoted 9 lines · Joe Thornber
Quoted 7 lines · Jeff Layton
The second one is an ugly hack, and I'm definitely open to better ways
of handling it. After my kernel upgrade (from 2.6.1 to 2.6.4-rc2), the major number of the device mapper devices on my system changed. So, I added a hack to copy the contents of the ramdisk/dev/mapperdirectory to the/dev/mapperdirectory on the lvm2 root filesystem, prior to thepivot_root. It works for me, but I'd love to hear about better ways of handling this.
Quoted 3 lines
Mount tmpfs on
/dev/mapper, and then run 'dmsetup mknods' ?- Joe
Hmm that might work, but what will happen to the devices after you remount the filesystem. Will /dev/mapper always be a tmpfs filesystem after this?
Actually, I'm thinking another way to handle this might be to do a 'chroot /rootvol /sbin/vgmknodes' prior to the pivot_root instead of the tar copy. There might be some way to handle it without the chroot, but I think this will work.
I'll try this method out soon.
-- Jeff