#!/bin/bash
#
# generatelayer.sh
#
# Versions:
# - 2010-03-05 Initial release
#
# (c) 2010 by Claudio Nold
# Some important parts are copied from the "larch" Project
# (c) by Michael Towers (http://larch.berlios.de/). He guarantees kindly
# the free use and extension of these parts.
#
# Description:
# This script saves the current session wich is in RAM to disk as a new
# modification layer wich replaces the old one. This script is useless
# if the root directory is directly on a read-write partition.
#
# This live system uses these mounted disks and layers:
# /.livesys/sysdisk = disk wich contains RO squash image of system ("system layer")
# /.livesys/system = mounted squash fs of system ("system layer") -> loop0
# /.livesys/modsdisk = disk wich contains RO squash image of modifications ("mods layer")
# /.livesys/filterlayer = mounted squash fs of filter (=deleted files) -> loop1
# /.livesys/modslayer = mounted squash fs of modifications ("mods layer") -> loop2
# /.livesys/dynamic = mounted RAM fs (tempfs) with RW layer, wich we want to save now
# / = union of: dynamic, modslayer, filterlayer, system
#
# Note: all write accesses go to /.livesys/dynamic and we want to merge
# it with the old modifications and the system layer.
#
# Disclaimer:
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software. Permission is granted to anyone
# to use this software for any purpose, including commercial
# applications.
##########################################
CMDLINE="$(cat /proc/cmdline)"
##########################################
# Some generic excludes (relative!)
##########################################
EXCL="tmp media sys proc var/log var/tmp" # this, we have in system layer already
EXCL="${EXCL} etc/mtab boot/lost+found"
EXCL="${EXCL} root/.xauth* root/.serverauth* root/.xsession*"
EXCL="${EXCL} home/user/.xauth* home/user/.serverauth* home/user/.xsession*"
EXCL="${EXCL} usr/portage/distfiles/*"
##########################################
ESCAPE="\033";
REDF="${ESCAPE}[31m";
GREENF="${ESCAPE}[32m";
BLUEF="${ESCAPE}[34m";
YELLOWF="${ESCAPE}[33m";
RESET="${ESCAPE}[0m";
##########################################
parse_disk() {
if [ "$(echo $1|cut -c -5)" = "UUID=" ]; then
# $1 is a UUID
echo $(findfs $1)
elif [ "$(echo $1|cut -c -6)" = "LABEL=" ]; then
# $1 is a LABEL
echo $(findfs $1)
elif [ "$(echo $1|cut -c -5)" = "/dev/" ]; then
# $1 is a device name
echo $1
else
# $1 is unrecognized.
echo "unknow-disk"
fi
}
newmods ()
{
olaydir=$1
sqsname=$2
echo "Building new 'mods' overlay to ${sqsname}"
# Recreate the desired layout in a new directory, using mount --bind
# ( this is probably over-complicated, but I got annoying warnings about
# conflicting permissions on the mount point when I didn't use a single
# source for squashfs - for some reason the squashfs root was set to
# mode 0777.)
# The other version was:
## root directories which are included in mods.sqf
#includedirs=""
#for d in bin boot etc home lib mnt opt root sbin srv usr var; do
# if [ -d ${olaydir}/${d} ]; then
# includedirs="${includedirs} ${olaydir}/${d}"
# fi
#done
## non-included /var directories
#vardirs="${olaydir}/var/log ${olaydir}/var/tmp ${olaydir}/var/cache/pacman/pkg"
#mksquashfs ${includedirs} "${overlaypath}/mods.tmp" -e ${vardirs}
#res=$?
#+++
mods2=/.livesys/mods2
rm -rf ${mods2}
mkdir -p ${mods2}
# root directories which are included in mods.sqf
includedirs=""
for d in bin etc home lib mnt opt root sbin usr var; do
if [ -d ${olaydir}/${d} ]; then
includedirs="${includedirs} ${d}"
mkdir ${mods2}/${d}
mount -n --bind ${olaydir}/${d} ${mods2}/${d}
fi
done
mksquashfs ${mods2} ${sqsname} -wildcards -e ${EXCL}
res=$?
for d in ${includedirs}; do
umount -n ${mods2}/${d}
done
rm -r ${mods2}
if [ ${res} -ne 0 ]; then
echo "ERROR: Couldn't create ${sqsname}"
return 1
fi
return 0
}
newfilter()
{
olaydir=$1
# 'Copy' existing filter
if [ -d /.livesys/filterlayer ]; then
cd /.livesys/filterlayer
find . -name .wh.\* | { cd "${olaydir}"
while read n; do
b="${n##*/}"
rm -rf "${n%/*}/${b:4}" &>/dev/null
done
}
fi
# Add relevant whiteouts from overlay
cd /.livesys/dynamic
find . -name .wh.\* | { cd "${olaydir}"
while read n; do
b="${n##*/}"
d="${n%/*}"
if [ "${b}" = ".wh..wh..opq" ]; then
rm -rf "${d}"
else
rm -rf "${d}/${b:4}" &>/dev/null
fi
done
}
}
for p in ${CMDLINE};
do
key=${p%%=*}
value=${p#*=}
case $key in
sysdisk)
SYSDISK=$(parse_disk $value)
;;
modsdisk)
MODSDISK=$(parse_disk $value)
;;
modsdir)
MODSDIR=$value
;;
esac
done
MODSLAYER_PREFIX=modslayer
FILTERLAYER_PREFIX=filterlayer
echo -e "${GREENF}Welcome to Modification Layer generation script!${RESET}"
mounted=$(mount|grep /.livesys/system)
if [ "${mounted}" = "" ]; then
echo -e "${REDF}Error: you have to boot the system read-only (Live mode).${RESET}"
echo -e "Nothing done."
exit 1
fi
if [ -z "${MODSDISK}" ]; then
echo -e "${REDF}Error: you have to pass boot parameters for the save location.${RESET}"
echo -e "${REDF}> specifiy for example: modsdisk=/dev/sdb1 modsdir=/live${RESET}"
echo -e "Nothing done."
exit 1
fi
if [ ! -b "${MODSDISK}" ]; then
echo -e "${REDF}Partition ${MODSDISK} used for the save location not found.${RESET}"
echo -e "${REDF}> specifiy for example: modsdisk=/dev/sdb1 modsdir=/live${RESET}"
echo -e "Nothing done."
exit 1
fi
if [ -z "${MODSDIR}" ]; then
echo -e "${REDF}Save directory not set in boot parameters.${RESET}"
echo -e "${REDF}> specifiy for example: modsdisk=/dev/sdb1 modsdir=/live${RESET}"
echo -e "Nothing done."
exit 1
fi
if [ -f /etc/X11/xorg.conf ]; then
echo -e -n "${YELLOWF}Would you like to exclude xorg.conf in the new layer?${RESET} [y/n] default=y "
read ans
if [ "$ans" = "y" -o -z "$ans" ]; then
EXCL="${EXCL} etc/X11/xorg.conf"
fi
fi
PERSRULES=$(ls /etc/udev/rules.d/70-persistent* 2>/dev/null)
if [ ! -z "$PERSRULES" ]; then
echo -e -n "${YELLOWF}Would you like to exclude persistent Udev rules in the new layer?${RESET} [y/n] default=y "
read ans
if [ "$ans" = "y" -o -z "$ans" ]; then
EXCL="${EXCL} etc/udev/rules.d/70-persistent*"
fi
fi
echo "Mounting drive wich will contain the new RO layer: ${MODSDISK}"
mkdir -p /.livesys/savedisk
if [ "${SYSDISK}" = "${MODSDISK}" ]; then
echo "The partition containing the squash image also contains persistent data."
mount -o remount,rw /.livesys/sysdisk
mount -n -o bind /.livesys/sysdisk /.livesys/savedisk
else
# TODO: better check here if it's already mounted...
mount -n -o rw ${MODSDISK} /.livesys/savedisk
fi
# TODO: check if succeed
PINDEX=/.livesys/savedisk${MODSDIR}/useindex
PFILES=$(ls -1 /.livesys/savedisk${MODSDIR}|grep --color=never ${MODSLAYER_PREFIX})
index=0
for f in $PFILES; do
val=$(echo ${f}|tr -dc '[0-9]')
val=$(echo "${val}"|bc)
if [ "$val" -ge "$index" ]; then
index=$(($val+1))
fi
done
INDEXSTR=$(printf "%04d\n" $index)
MODSLAYERNAME=${MODSLAYER_PREFIX}${INDEXSTR}
MODSLAYERFULLNAME=/.livesys/savedisk${MODSDIR}/${MODSLAYERNAME}
FILTERLAYERNAME=${FILTERLAYER_PREFIX}${INDEXSTR}
FILTERLAYERFULLNAME=/.livesys/savedisk${MODSDIR}/${FILTERLAYERNAME}
auplink / flush
#----------------------------------------------------------------------------
# 0) create a tmpfs for aufs xino files; this is only a helper mount point
xinomnt=/.livesys/xinomnt
rm -rf ${xinomnt}
mkdir -p ${xinomnt}
mount -t tmpfs tmpfs ${xinomnt}
#----------------------------------------------------------------------------
# 1) saving/merging dynamic layer to mods layer ("staticlayer")
# TODO: CHECK IF staticlayer EXISTS!
merge_union=/.livesys/merge
rm -rf ${merge_union}
mkdir -p ${merge_union}
if [ -d "/.livesys/modslayer" ]; then
mount -n -t aufs -o ro,xino=${xinomnt}/.aufs.xino,br=/.livesys/dynamic=ro+wh:/.livesys/modslayer=rr none ${merge_union}
res=$?
else
mount -n -t aufs -o ro,xino=${xinomnt}/.aufs.xino,br=/.livesys/dynamic=ro+wh none ${merge_union}
res=$?
fi
# note: whitespaces are not shown (e.g. /root/.wh.romount.sh) here;
# they have to be included in the filter
if [ $res -ne 0 ]; then
echo "** Couldn't mount merge union."
exit 1
fi
newmods ${merge_union} ${MODSLAYERFULLNAME}
res=$?
umount -n ${merge_union}
rm -r ${merge_union}
if [ $res -ne 0 ]; then
echo "** Couldn't create merged mods layer."
exit 1
fi
echo "${INDEXSTR}" > ${PINDEX}
echo "Persistent image ${MODSLAYERNAME} created:"
ls -l /.livesys/savedisk${MODSDIR}
#----------------------------------------------------------------------------
# 2) saving/merging filter layer
# Make a directory for a new filter
filter_new=/.livesys/filter_new
rm -rf ${filter_new}
mkdir -p ${filter_new}
mount -t tmpfs tmpfs ${filter_new}
# Make a directory for a new union
filter_union=/.livesys/filter_union
rm -rf ${filter_union}
mkdir -p ${filter_union}
echo "Mounting filter union"
layers="/.livesys/filter_new=rw:/.livesys/system=rr"
mount -n -t aufs -o br=${layers} none ${filter_union}
if [ $? -ne 0 ]; then
echo "** Couldn't mount filter union."
exit 1
fi
echo "Building new 'filter' overlay to ${FILTERLAYERFULLNAME}"
newfilter ${filter_union}
# Discard filter union
umount -n ${filter_union}
rm -r ${filter_union}
mksquashfs ${filter_new} ${FILTERLAYERFULLNAME}
res=$?
# Discard filter build directory
umount ${filter_new}
rm -r ${filter_new}
if [ $res -ne 0 ]; then
echo "ERROR: Couldn't create ${FILTERLAYERFULLNAME}"
exit 1
fi
#----------------------------------------------------------------------------
umount ${xinomnt}
rm -rf ${xinomnt}
if [ "${SYSDISK}" = "${MODSDISK}" ]; then
mount -o remount,ro /.livesys/sysdisk
fi
umount -f /.livesys/savedisk
echo -e "${GREENF}Done.${RESET}"