#!/bin/sh

# set -e

export PATH=/root/usr/bin:/root/usr/sbin:/root/bin:/root/sbin:/usr/bin:/usr/sbin:/bin:/sbin

mountpoint=/cdrom

mkdir -p $mountpoint

#overlay_method=unionfs
#if [ "${DPKG_ARCH}" = "ia64" ] || [ "${DPKG_ARCH}" = "hppa" ] || [ "${DPKG_ARCH}" = "sparc" ]; then
#    overlay_method=devmapper
#fi

USERNAME=casper
USERFULLNAME="Live session user"
HOST=live

[ -f /etc/casper.conf ] && . /etc/casper.conf

export USERNAME USERFULLNAME HOST

is_casper_path() {
    path=$1
    if [ -d "$path/casper" ]; then
        if [ "$(echo $path/casper/*.cloop)" != "$path/casper/*.cloop" ] || 
            [ "$(echo $path/casper/*.squashfs)" != "$path/casper/*.squashfs" ]; then
            return 0
        fi
    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}
}

get_backing_device() {
	case "$1" in
            *.cloop)
                echo $(setup_loop "$1" "cloop" "/sys/block/cloop*")
			;;
            *.squashfs)
                echo $(setup_loop "$1" "loop" "/sys/block/loop*")
                ;;
            *)
                panic "Unrecognized casper filesystem: $1"
                ;;
	esac
}

match_files_in_dir() {
    # Does any files match pattern $1 ?

    local pattern="$1"
    if [ "$(echo $pattern)" != "$pattern" ]; then
        return 0
    fi
    return 1
}

mount_images_in_directory() {
    directory="$1"
    rootmnt="$2"
    if match_files_in_dir "$directory/casper/*.cloop"; then
        # Let's hope there's just one matching *.cloop... FIXME
        setup_devmapper $(get_backing_device "$directory/casper/*.cloop") "$rootmnt"
    elif match_files_in_dir "$directory/casper/*.squashfs"; then
        setup_unionfs "$directory/casper" "$rootmnt"
    else 
        :
    fi
}

sys2dev() {
    sysdev=${1#/sys}
    echo "/dev/$(udevinfo -q name -p ${sysdev} 2>/dev/null|| echo ${sysdev##*/})"
}

setup_loop() {
    local fspath=$1
    local module=$2
    local pattern=$3

    modprobe -Qb "$module"
    udevsettle
 
    for loopdev in $pattern; do
        if [ "$(cat $loopdev/size)" -eq 0 ]; then
            dev=$(sys2dev "${loopdev}")
            losetup "$dev" "$fspath"
            echo "$dev"
            return 0
        fi
    done
    panic "No loop devices available"
}

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
}

setup_devmapper() {
    backdev="$1"
    rootmnt="$2"

    modprobe -Qb dm-mod
    COW_DEVICE=/dev/ram1
    COW_NAME="casper-cow"

    BACKING_FILE_SIZE=$(blockdev --getsize "$backdev")
    MAX_COW_SIZE=$(blockdev --getsize "$COW_DEVICE")
    CHUNK_SIZE=8 # sectors

    if [ -z "$COW_SIZE" -o "$COW_SIZE" -gt "$MAX_COW_SIZE" ]; then
        COW_SIZE=$MAX_COW_SIZE
    fi

    echo "0 $COW_SIZE linear $COW_DEVICE 0" | dmsetup create $COW_NAME

    echo "0 $BACKING_FILE_SIZE snapshot $backdev /dev/mapper/$COW_NAME p $CHUNK_SIZE" | \
        dmsetup create casper-snapshot
    if [ "$(get_fstype $backdev)" = "unknown" ]; then
        panic "Unknown file system type on $backdev"
    fi
    mount -t $(get_fstype "$backdev") /dev/mapper/casper-snapshot $rootmnt || panic "Can not mount /dev/mapper/casper/snapshot on $rootmnt"

    mkdir -p "$rootmnt/rofs"
    echo "0 $BACKING_FILE_SIZE linear $backdev 0" | dmsetup create casper-backing
    mount -t $(get_fstype "$backdev") /dev/mapper/casper-backing "$rootmnt/rofs"
}

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
}

find_cow_device() {
    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)" = "casper-rw" ]; then
                echo "$devname"
                return
            elif [ "$(get_fstype ${devname})" = "vfat" ]; then
                mkdir -p /cow-backing
                if where_is_mounted ${devname} > /dev/null; then
                    mount -o remount,rw ${devname} $(where_is_mounted ${devname}) || panic "Remounting failed"
                    mount -o bind $(where_is_mounted ${devname}) /cow-backing || panic "Cannot bind-mount"
                else
                    mount -t $(get_fstype "${devname}") -o rw "${devname}" /cow-backing || panic "Cannot mount $devname on /cow-backing"
                fi

                if [ -e "/cow-backing/casper-rw" ]; then
                    echo $(setup_loop "/cow-backing/casper-rw" "loop" "/sys/block/loop*")
                    return 0
                else
                    umount /cow-backing
                fi
            fi
            
        done
    done
    return 1    
}

setup_unionfs() {
    image_directory="$1"
    rootmnt="$2"
    modprobe -Qb unionfs

    # run-init can't deal with images in a subdir, but we're going to
    # move all of these away before it runs anyway.  No, we're not,
    # put them in / since move-mounting them into / breaks mono and
    # some other apps.

    croot="/"

    # Let's just mount the read-only file systems first
    mkdir -p "${croot}"
    for image in "${image_directory}"/*.squashfs; do
        imagename=$(basename "${image}")
        backdev=$(get_backing_device "$image")
        fstype=$(get_fstype "${backdev}")
        if [ "${fstype}" = "unknown" ]; then
            panic "Unknown file system type on ${backdev} (${image})"
        fi
        mkdir -p "${croot}/${imagename}"
        mount -t "${fstype}" -o ro "${backdev}" "${croot}/${imagename}" || panic "Can not mount $backdev ($image) on ${croot}/${imagename}"
    done

    rofsstring=""
    for dir in $(mount -t squashfs | cut -d\  -f 3); do
        rofsstring="$dir=ro:$rofsstring"
    done
    rofsstring=${rofsstring%:}

    mkdir -p /cow
    
    if grep -q persistent /proc/cmdline; then
        i=0
        # We love udev and the kernel!
        while [ "$i" -lt 300 ]; do
            cowdevice=$(find_cow_device) 
            if [ -b "$cowdevice" ]; then
                mount -t $(get_fstype "$cowdevice") -o rw "$cowdevice" /cow || panic "Can not mount $cowdevice on /cow"
                break
            fi
            sleep 0.1
            i=$(( $i + 1 ))
        done
    else
        mount -t tmpfs tmpfs /cow
    fi
    
    mount -t unionfs -o dirs=/cow=rw:$rofsstring unionfs "$rootmnt"

    if grep -q showmounts /proc/cmdline; then
        for d in $(mount -t squashfs | cut -d\  -f 3); do
            mkdir -p "${rootmnt}/casper/${d##*/}"
            mount -o move "${d}" "${rootmnt}/casper/${d##*/}"
        done

        mkdir -p "$rootmnt/cow"
        mount -o bind /cow "$rootmnt/cow"
    fi

}

is_usb_device() {
    sysfs_path="${1#/sys}"
    if /lib/udev/path_id "${sysfs_path}" | grep -E -q "ID_PATH=(usb|pci-[^-]*-usb)"; then
        return 0
    fi
    return 1
}

find_livefs() {
	mounted=
        for sysblock in $(echo /sys/block/* | tr ' ' '\n' | grep -v loop | grep -v ram); do
            devname=$(sys2dev "${sysblock}")
            fstype=$(get_fstype "${devname}")
            if /lib/udev/cdrom_id ${devname} > /dev/null; then
                mount -t ${fstype} -o ro "$devname" $mountpoint || continue
                if is_casper_path $mountpoint; then
                    echo $mountpoint
                    return
                else
                    umount $mountpoint
                fi
            elif is_usb_device "$sysblock"; then
                for dev in $(subdevices "${sysblock}"); do
                    devname=$(sys2dev "${dev}")
                    fstype=$(get_fstype "${devname}")
                    case ${fstype} in
                        vfat|iso9660|udf)
                            mount -t ${fstype} -o ro "${devname}" $mountpoint || continue
                            if is_casper_path $mountpoint; then
                                echo $mountpoint
                                return
                            else
                                umount $mountpoint
                            fi
                            ;;
                    esac
                done
	    elif [ "${fstype}" = "squashfs" ]; then

                # This is an ugly hack situation, the block device has
                # a squashfs image directly on it.  It's hopefully
                # casper, so take it and run with it.

                ln -s "${devname}" "${devname}.${fstype}"
                echo "${devname}.${fstype}"
                return
            fi
        done
}

pulsate() {
    if [ -x /sbin/usplash_write ]; then
        /sbin/usplash_write "PULSATE"
    fi
}

set_usplash_timeout() {
    if [ -x /sbin/usplash_write ]; then
        /sbin/usplash_write "TIMEOUT 120"
    fi
}

mountroot() {
    exec 6>&1
    exec 7>&2
    exec > casper.log
    exec 2>&1
    
    set_usplash_timeout
    [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-premount"
    pulsate
    run_scripts /scripts/casper-premount
    [ "$quiet" != "y" ] && log_end_msg

    # Needed here too because some things (*cough* udev *cough*)
    # changes the timeout

    set_usplash_timeout

    for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13; do
        livefs_root=$(find_livefs)
        if [ "${livefs_root}" ]; then
            break
        fi
        sleep 1
    done
    if [ "$?" -gt 0 ]; then
        panic "Unable to find a medium containing a live file system"
    fi
    
    mount_images_in_directory "$livefs_root" "$rootmnt"
    log_end_msg

    maybe_break casper-bottom
    [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-bottom"

    pulsate
    run_scripts /scripts/casper-bottom
    [ "$quiet" != "y" ] && log_end_msg

    exec 1>&6 6>&-
    exec 2>&7 7>&-
    cp casper.log "${rootmnt}/var/log/"
}
