Merge branch 'debian'
[live-boot-grml.git] / scripts / live-helpers
index eac2175..3109356 100644 (file)
-## live-initramfs helper functions, used by live-initramfs on boot and by live-snapshot
+# live-boot helper functions, used by live-boot on boot and by live-snapshot
 
-if [ "${BUILD_SYSTEM}" = "Ubuntu" ]; then
-    MP_QUIET="-Q"
-elif [ "${BUILD_SYSTEM}" = "Debian" ]; then
-    MP_QUIET="-q"
-else
-    MP_QUIET=""
+if [ ! -x "/bin/fstype" ]
+then
+       # klibc not in path -> not in initramfs
+       export PATH="${PATH}:/usr/lib/klibc/bin"
 fi
 
-if [ ! -x "/bin/fstype" ]; then
-    # klibc not in path -> not in initramfs
-    export PATH="${PATH}:/usr/lib/klibc/bin"
+# handle upgrade path from old udev (using udevinfo) to
+# recent versions of udev (using udevadm info)
+if [ -x /sbin/udevadm ]
+then
+       udevinfo='/sbin/udevadm info'
+else
+       udevinfo='udevinfo'
 fi
 
-sys2dev() {
-    sysdev=${1#/sys}
-    echo "/dev/$(udevinfo -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
-}
-
-subdevices() {
-    sysblock=$1
-    r=""
-    for dev in "${sysblock}" "${sysblock}"/*; do
-        if [ -e "${dev}/dev" ]; then
-            r="${r} ${dev}"
-        fi
-    done
-    echo ${r}
-}
-
-get_fstype() {
-    local FSTYPE
-    local FSSIZE
-    eval $(fstype < $1)
-    if [ "$FSTYPE" != "unknown" ]; then
-        echo $FSTYPE
-        return 0
-    fi
-    /lib/udev/vol_id -t $1 2>/dev/null
+sys2dev ()
+{
+       sysdev=${1#/sys}
+       echo "/dev/$($udevinfo -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
 }
 
-where_is_mounted() {
-    device=$1
-    if grep -q "^$device " /proc/mounts; then
-        grep "^$device " /proc/mounts | read d mountpoint rest
-        echo $mountpoint
-        return 0
-    fi
-    return 1
+subdevices ()
+{
+       sysblock=${1}
+       r=""
+
+       for dev in "${sysblock}"/* "${sysblock}"
+       do
+               if [ -e "${dev}/dev" ]
+               then
+                       r="${r} ${dev}"
+               fi
+       done
+
+       echo ${r}
 }
 
-lastline() {
-    while read lines ; do
-        line=${lines}
-    done
-    echo "${line}"
+storage_devices()
+{
+       black_listed_devices="${1}"
+       white_listed_devices="${2}"
+
+       for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "loop|ram|fd")
+       do
+               fulldevname=$(sys2dev "${sysblock}")
+
+               if echo "${black_listed_devices}" | grep -qe "\<${fulldevname}\>" || \
+                       [ -n "${white_listed_devices}" ] && \
+                       echo "${white_listed_devices}" | grep -qve "\<${fulldevname}\>"
+               then
+                       # skip this device entirely
+                       continue
+               fi
+
+               for dev in $(subdevices "${sysblock}")
+               do
+                       devname=$(sys2dev "${dev}")
+
+                       if echo "${black_listed_devices}" | grep -qe "\<${devname}\>"
+                       then
+                               # skip this subdevice
+                               continue
+                       else
+                               echo "${devname}"
+                       fi
+               done
+       done
+}
+
+is_supported_fs ()
+{
+       fstype="${1}"
+
+       # Validate input first
+       if [ -z "${fstype}" ]
+       then
+               return 1
+       fi
+
+       # Try to look if it is already supported by the kernel
+       if grep -q ${fstype} /proc/filesystems
+       then
+               return 0
+       else
+               # Then try to add support for it the gentle way using the initramfs capabilities
+               modprobe ${fstype}
+               if grep -q ${fstype} /proc/filesystems
+               then
+                       return 0
+               # Then try the hard way if /root is already reachable
+               else
+                       kmodule="/root/lib/modules/`uname -r`/${fstype}/${fstype}.ko"
+                       if [ -e "${kmodule}" ]
+                       then
+                               insmod "${kmodule}"
+                               if grep -q ${fstype} /proc/filesystems
+                               then
+                                       return 0
+                               fi
+                       fi
+               fi
+       fi
+
+       return 1
+}
+
+get_fstype ()
+{
+       /sbin/blkid -s TYPE -o value $1 2>/dev/null
+}
+
+where_is_mounted ()
+{
+       device=${1}
+
+       if grep -q "^${device} " /proc/mounts
+       then
+               # return the first found
+               grep -m1 "^${device} " /proc/mounts | cut -f2 -d ' '
+       fi
+}
+
+lastline ()
+{
+       while read lines
+       do
+               line=${lines}
+       done
+
+       echo "${line}"
 }
 
 base_path ()
 {
-    testpath="${1}"
-    mounts="$(awk '{print $2}' /proc/mounts)"
-    testpath="$(busybox realpath ${testpath})"
+       testpath="${1}"
+       mounts="$(awk '{print $2}' /proc/mounts)"
+       testpath="$(busybox realpath ${testpath})"
 
-    while true ; do
-        if echo "${mounts}" | grep -qs "^${testpath}" ; then
-            set -- `echo "${mounts}" | grep "^${testpath}" | lastline`
-            echo ${1}
-            break
-        else
-            testpath=`dirname $testpath`
-        fi
-    done
+       while true
+       do
+               if echo "${mounts}" | grep -qs "^${testpath}"
+               then
+                       set -- $(echo "${mounts}" | grep "^${testpath}" | lastline)
+                       echo ${1}
+                       break
+               else
+                       testpath=$(dirname $testpath)
+               fi
+       done
 }
 
 fs_size ()
 {
-    # Returns used/free fs kbytes + 5% more
-    # You could pass a block device as $1 or the mount point as $2
-
-    dev="${1}"
-    mountp="${2}"
-    used="${3}"
-
-    if [ -z "${mountp}" ]; then
-        mountp=$(where_is_mounted "${dev}")
-        if [ "$?" -gt 0 ]; then
-            mountp="/mnt/tmp_fs_size"
-            mkdir -p "${mountp}"
-            mount -t $(get_fstype "${dev}") -o ro "${dev}" "${mountp}"
-            doumount=1
-        fi
-    fi
+       # Returns used/free fs kbytes + 5% more
+       # You could pass a block device as ${1} or the mount point as ${2}
 
-    if [ "${used}" = "used" ]; then
-        size=$(du -ks ${mountp} | cut -f1)
-        size=$(expr ${size} + ${size} / 20 ) # FIXME: 5% more to be sure
-    else
-        # free space
-        size="$(df -k | grep -s ${mountp} | awk '{print $4}')"
-    fi
+       dev="${1}"
+       mountp="${2}"
+       used="${3}"
 
-    if [ -n "${doumount}" ]; then
-        umount "${mountp}"
-        rmdir "${mountp}"
-    fi
-    echo "${size}"
-}
-
-load_keymap()
-{
-       # Load custom keymap
-       if [ -x /bin/loadkeys -a -r /etc/boottime.kmap.gz ]; then
-               loadkeys /etc/boottime.kmap.gz
-       fi
-}
-
-setup_loop() {
-    local fspath=$1
-    local module=$2
-    local pattern=$3
-    local offset=$4
-    local encryption=$5
-
-    modprobe ${MP_QUIET} -b "$module"
-    udevsettle
-
-    for loopdev in $pattern; do
-        if [ "$(cat $loopdev/size)" -eq 0 ]; then
-            dev=$(sys2dev "${loopdev}")
-            options=''
-            if [ 0 -lt "${offset}" ]; then
-                options="${options} -o ${offset}"
-            fi
-            if [ -z "${encryption}" ]; then
-                losetup ${options} "${dev}" "${fspath}"
-            else
-                # Loop AES encryption
-                while true; do
-                                            load_keymap
-                    echo -n "Enter passphrase for ${fspath}: " >&6
-                    read -s passphrase
-                    echo "${passphrase}" > /tmp/passphrase
-                    exec 9</tmp/passphrase
-                    /sbin/losetup ${options} -e "${encryption}" -p 9 "${dev}" "${fspath}"
-                    error=$?
-                    exec 9<&-
-                    rm -f /tmp/passphrase
-                    if [ 0 -eq ${error} ]; then
-                        unset error
-                        break
-                    fi
-                    echo -n "Something went wrong... Retry? [YES/no] " >&6
-                    read answer
-                    if [ 'no' = "${answer}" ]; then
-                        unset answer
-                        break
-                    fi
-                done
-            fi
-            echo "$dev"
-            return 0
-        fi
-    done
-    panic "No loop devices available"
+       if [ -z "${mountp}" ]
+       then
+               mountp="$(where_is_mounted ${dev})"
+
+               if [ -z "${mountp}" ]
+               then
+                       mountp="/mnt/tmp_fs_size"
+
+                       mkdir -p "${mountp}"
+                       mount -t $(get_fstype "${dev}") -o ro "${dev}" "${mountp}" || log_warning_msg "cannot mount -t $(get_fstype ${dev}) -o ro ${dev} ${mountp}"
+
+                       doumount=1
+               fi
+       fi
+
+       if [ "${used}" = "used" ]
+       then
+               size=$(du -ks ${mountp} | cut -f1)
+               size=$(expr ${size} + ${size} / 20 ) # FIXME: 5% more to be sure
+       else
+               # free space
+               size="$(df -k | grep -s ${mountp} | awk '{print $4}')"
+       fi
+
+       if [ -n "${doumount}" ]
+       then
+               umount "${mountp}" || log_warning_msg "cannot umount ${mountp}"
+               rmdir "${mountp}"
+       fi
+
+       echo "${size}"
+}
+
+load_keymap ()
+{
+       # Load custom keymap
+       if [ -x /bin/loadkeys -a -r /etc/boottime.kmap.gz ]
+       then
+               loadkeys /etc/boottime.kmap.gz
+       fi
+}
+
+setup_loop ()
+{
+       local fspath=${1}
+       local module=${2}
+       local pattern=${3}
+       local offset=${4}
+       local encryption=${5}
+       local readonly=${6}
+
+       # the output of setup_loop is evaluated in other functions,
+       # modprobe leaks kernel options like "libata.dma=0"
+       # as "options libata dma=0" on stdout, causing serious
+       # problems therefor, so instead always avoid output to stdout
+       modprobe -q -b "${module}" 1>/dev/null
+
+       udevadm settle
+
+       for loopdev in ${pattern}
+       do
+               if [ "$(cat ${loopdev}/size)" -eq 0 ]
+               then
+                       dev=$(sys2dev "${loopdev}")
+                       options=''
+
+                       if [ -n "${readonly}" ]
+                       then
+                               if losetup --help 2>&1 | grep -q -- "-r\b"
+                               then
+                                       options="${options} -r"
+                               fi
+                       fi
+
+                       if [ -n "${offset}" ] && [ 0 -lt "${offset}" ]
+                       then
+                               options="${options} -o ${offset}"
+                       fi
+
+                       if [ -z "${encryption}" ]
+                       then
+                               losetup ${options} "${dev}" "${fspath}"
+                       else
+                               # Loop AES encryption
+                               while true
+                               do
+                                       load_keymap
+
+                                       echo -n "Enter passphrase for root filesystem: " >&6
+                                       read -s passphrase
+                                       echo "${passphrase}" > /tmp/passphrase
+                                       unset passphrase
+                                       exec 9</tmp/passphrase
+                                       /sbin/losetup ${options} -e "${encryption}" -p 9 "${dev}" "${fspath}"
+                                       error=${?}
+                                       exec 9<&-
+                                       rm -f /tmp/passphrase
+
+                                       if [ 0 -eq ${error} ]
+                                       then
+                                               unset error
+                                               break
+                                       fi
+
+                                       echo
+                                       echo -n "There was an error decrypting the root filesystem ... Retry? [Y/n] " >&6
+                                       read answer
+
+                                       if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
+                                       then
+                                               unset answer
+                                               break
+                                       fi
+                               done
+                       fi
+
+                       echo "${dev}"
+                       return 0
+               fi
+       done
+
+       panic "No loop devices available"
 }
 
 try_mount ()
 {
-    dev="${1}"
-    mountp="${2}"
-    opts="${3}"
+       dev="${1}"
+       mountp="${2}"
+       opts="${3}"
+       fstype="${4}"
+
+       old_mountp="$(where_is_mounted ${dev})"
+
+       if [ -n "${old_mountp}" ]
+       then
+               if [ "${opts}" != "ro" ]
+               then
+                       mount -o remount,"${opts}" "${dev}" "${old_mountp}" || panic "Remounting ${dev} ${opts} on ${old_mountp} failed"
+               fi
+
+               mount -o bind "${old_mountp}" "${mountp}" || panic "Cannot bind-mount ${old_mountp} on ${mountp}"
+       else
+               if [ -z "${fstype}" ]
+               then
+                       fstype=$(get_fstype "${dev}")
+               fi
+               mount -t "${fstype}" -o "${opts}" "${dev}" "${mountp}" || \
+               ( echo "SKIPPING: Cannot mount ${dev} on ${mountp}, fstype=${fstype}, options=${opts}" > live-boot.log && return 0 )
+       fi
+}
+
+open_luks_device ()
+{
+       dev="${1}"
+       name="$(basename ${dev})"
+       opts="--key-file=-"
+       if [ -n "${PERSISTENT_READONLY}" ]
+       then
+               opts="${opts} --readonly"
+       fi
+
+       load_keymap
+
+       while true
+       do
+               /lib/cryptsetup/askpass "Enter passphrase for ${dev}: " | \
+                       /sbin/cryptsetup -T 1 luksOpen ${dev} ${name} ${opts}
+
+               if [ 0 -eq ${?} ]
+               then
+                       luks_device="/dev/mapper/${name}"
+                       echo ${luks_device}
+                       return 0
+               fi
+
+               echo >&6
+               echo -n "There was an error decrypting ${dev} ... Retry? [Y/n] " >&6
+               read answer
+
+               if [ "$(echo "${answer}" | cut -b1 | tr A-Z a-z)" = "n" ]
+               then
+                       return 2
+               fi
+       done
+}
+
+find_persistent_media ()
+{
+       # Scans devices for overlays and snapshots, and returns a whitespace
+       # separated list of how to use them. Only overlays with a partition
+       # label or file name in ${overlays} are returned, and ditto for
+       # snapshots with labels in ${snapshots}.
+       #
+       # When scanning a LUKS device, the user will be asked to enter the
+       # passphrase; on failure to enter it, or if no persistent partitions
+       # or files were found, the LUKS device is closed.
+       #
+       # For a snapshot file the return value is ${label}=${snapdata}", where
+       # ${snapdata} is the parameter used for try_snap().
+       #
+       # For all other cases (overlay/snapshot partition and overlay file) the
+       # return value is "${label}=${device}", where ${device} a device that
+       # can mount the content. In the case of an overlay file, the device
+       # containing the file will remain mounted as a side-effect.
+       #
+       # No devices in ${black_listed_devices} will be scanned, and if
+       # ${white_list_devices} is non-empty, only devices in it will be
+       # scanned.
+
+       overlays="${1}"
+       snapshots="${2}"
+       black_listed_devices="${3}"
+       white_listed_devices="${4}"
+
+       for dev in $(storage_devices "${black_listed_devices}" "${white_listed_devices}")
+       do
+               luks_device=""
 
-    if where_is_mounted ${dev} > /dev/null; then
-        mount -o remount,"${opts}" ${dev} $(where_is_mounted ${dev}) || panic "Remounting failed"
-        mount -o bind $(where_is_mounted ${dev}) ${mountp} || panic "Cannot bind-mount"
+               # Checking for a luks device
+               if echo ${PERSISTENT_ENCRYPTION} | grep -qe "\<luks\>" && \
+                  /sbin/cryptsetup isLuks ${dev}
+               then
+                       if luks_device=$(open_luks_device "${dev}")
+                       then
+                               dev="${luks_device}"
+                       else
+                               # skip $dev since we failed/chose not to open it
+                               continue
+                       fi
+               elif echo ${PERSISTENT_ENCRYPTION} | grep -qve "\<none\>"
+               then
+                       # skip $dev since we don't allow unencrypted storage
+                       continue
+               fi
+
+               if echo ${PERSISTENT_STORAGE} | grep -qe "\<filesystem\>"
+               then
+                       for label in ${overlays} ${snapshots}
+                       do
+                               if [ "$(/sbin/blkid -s LABEL -o value $dev 2>/dev/null)" = "${label}" ]
+                               then
+                                       overlays=$(echo ${overlays} | sed -e "s|\<${label}\>||")
+                                       snapshots=$(echo ${snapshots} | sed -e "s|\<${label}\>||")
+                                       echo "${label}=${dev}"
+                                       # skip to the next device
+                                       continue 2
+                               fi
+                       done
+               fi
+
+               if echo ${PERSISTENT_STORAGE} | grep -qe "\<file\>"
+               then
+                       devfstype="$(get_fstype ${dev})"
+                       overlay_on_dev=""
+                       snapshot_on_dev=""
+                       backing="/$(basename ${dev})-backing"
+                       mkdir -p "${backing}"
+                       if is_supported_fs ${devfstype} && try_mount "${dev}" "${backing}" "rw" "${devfstype}"
+                       then
+                               for label in ${overlays}
+                               do
+                                       path=${backing}/${PERSISTENT_PATH}${label}
+                                       if [ -f "${path}" ]
+                                       then
+                                               overlays=$(echo ${overlays} | sed -e "s|\<${label}\>||")
+                                               overlay_on_dev="yes"
+                                               echo "${label}=$(setup_loop "${path}" "loop" "/sys/block/loop*")"
+                                       fi
+                               done
+
+                               for label in ${snapshots}
+                               do
+                                       for ext in squashfs cpio.gz ext2 ext3 ext4 jffs2
+                                       do
+                                               path="${PERSISTENT_PATH}${label}.${ext}"
+                                               if [ -f "${backing}/${path}" ]
+                                               then
+                                                       snapshots=$(echo ${snapshots} | sed -e "s|\<${label}\>||")
+                                                       snapshot_on_dev="yes"
+                                                       echo "${label}=${dev}:${backing}:${path}"
+                                               fi
+                                       done
+                               done
+                       fi
+                       if [ -z "${overlay_on_dev}" ]
+                       then
+                               umount ${backing} > /dev/null 2>&1 || true
+                               if [ -z "${snapshot_on_dev}" ] && [ -n "${luks_device}" ] && /sbin/cryptsetup status "${luks_device}" 1> /dev/null
+                               then
+                                       /sbin/cryptsetup luksClose "${luks_device}"
+                               fi
+                       fi
+               fi
+       done
+}
+
+get_mac ()
+{
+       mac=""
+
+       for adaptor in /sys/class/net/*
+       do
+               status="$(cat ${adaptor}/iflink)"
+
+               if [ "${status}" -eq 2 ]
+               then
+                       mac="$(cat ${adaptor}/address)"
+                       mac="$(echo ${mac} | sed 's/:/-/g' | tr '[a-z]' '[A-Z]')"
+               fi
+       done
+
+       echo ${mac}
+}
+
+is_luks()
+{
+    devname="${1}"
+    if [ -x /sbin/cryptsetup ]
+    then
+       /sbin/cryptsetup isLuks "${devname}" 2>/dev/null || ret=${?}
+       return ${ret}
     else
-        mount -t $(get_fstype "${dev}") -o "${opts}" "${dev}" "${mountp}" || panic "Cannot mount ${dev} on ${mountp}"
+       return 1
     fi
+
+}
+
+removable_dev ()
+{
+       output_format="${1}"
+       want_usb="${2}"
+       ret=
+
+       for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
+       do
+               dev_ok=
+               if [ "$(cat ${sysblock}/removable)" = "1" ]
+               then
+                       if [ -z "${want_usb}" ]
+                       then
+                               dev_ok="yes"
+                       else
+                               if readlink ${sysblock} | grep -q usb
+                               then
+                                       dev_ok="yes"
+                               fi
+                       fi
+               fi
+
+               if [ "${dev_ok}" = "yes" ]
+               then
+                       case "${output_format}" in
+                               sys)
+                                       ret="${ret} ${sysblock}"
+                                       ;;
+                               *)
+                                       devname=$(sys2dev "${sysblock}")
+                                       ret="${ret} ${devname}"
+                                       ;;
+                       esac
+               fi
+       done
+
+       echo "${ret}"
 }
 
-find_cow_device() {
-    pers_label="${1}"
-    cow_backing="/${pers_label}-backing"
-    for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop); do
-        for dev in $(subdevices "${sysblock}"); do
-            devname=$(sys2dev "${dev}")
-            if [ "$(/lib/udev/vol_id -l $devname 2>/dev/null)" = "${pers_label}" ]; then
-                echo "$devname"
-                return
-            elif [ "$(get_fstype ${devname})" = "vfat" ]; then # FIXME: all supported block devices should be scanned
-                mkdir -p "${cow_backing}"
-                try_mount "${devname}" "${cow_backing}" "rw"
-                if [ -e "${cow_backing}/${pers_label}" ]; then
-                    echo $(setup_loop "${cow_backing}/${pers_label}" "loop" "/sys/block/loop*")
-                    return 0
-                else
-                    umount ${cow_backing}
-                fi
-            fi
-        done
-    done
-}
-
-find_files()
-# return the first of $filenames found on vfat and ext2 devices
-# FIXME: merge with above function
-{
-    filenames="${1}"
-    snap_backing="/snap-backing"
-    for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop); do
-        for dev in $(subdevices "${sysblock}"); do
-            devname=$(sys2dev "${dev}")
-            devfstype="$(get_fstype ${devname})"
-            if [ "${devfstype}" = "vfat" ] ||  [ "${devfstype}" = "ext2" ] ; then # FIXME: all supported block devices should be scanned
-                mkdir -p "${snap_backing}"
-                try_mount "${devname}" "${snap_backing}" "ro"
-                for filename in ${filenames}; do
-                    if [ -e "${snap_backing}/${filename}" ]; then
-                        echo "${devname} ${snap_backing} ${filename}"
-                        return 0
-                    fi
-                done
-                umount ${snap_backing}
-            fi
-        done
-    done
+removable_usb_dev ()
+{
+       output_format="${1}"
+
+       removable_dev "${output_format}" "want_usb"
 }
 
+non_removable_dev ()
+{
+       output_format="${1}"
+       ret=
+
+       for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -vE "/(loop|ram|dm-|fd)")
+       do
+               if [ "$(cat ${sysblock}/removable)" = "0" ]
+               then
+                       case "${output_format}" in
+                               sys)
+                                       ret="${ret} ${sysblock}"
+                                       ;;
+                               *)
+                                       devname=$(sys2dev "${sysblock}")
+                                       ret="${ret} ${devname}"
+                                       ;;
+                       esac
+               fi
+       done
 
+       echo "${ret}"
+}