#!/bin/sh
# Provides tools to mount and umount unionfs specific to SWIR system.

# import run environment
. /etc/run.env

# This executable
this_e=$( basename $0 )

# Set global variables (e.g. outside of functions).
flash_mntpt_g=${UFS_ROOT}
root_etc_g=/etc
root_data_g=/data
overlayfs_sig_g=${OVERLAYFS_SIGNATURE}
ufs_type=${UFS_T}
etc_is_writable_g=${SWI_FALSE}

# I've found cases where /etc/localtime is pointing to /etc/TZ,
# and we need to fix it, if possible.
# This will only work if /etc is writeable.
localtime_fixup()
{
    local ret=$SWI_OK
    local localtime=""
    local timezone_files_root=/usr/share/zoneinfo
    local sl_localtime=/etc/localtime
    local real_localtime=$timezone_files_root/localtime
    local timezone=""
    local timezone_file=/etc/timezone
    local default_tz="Universal"

    if [ "x${etc_is_writable_g}" != "x${SWI_TRUE}" ] ; then
        return $SWI_OK
    fi

    # If timezone mount point does not exist, make one.
    if [ ! -f $real_localtime ] ; then
        swi_log "Creating [$real_localtime] mount point."
        touch $real_localtime
    fi

    # Make sure that timezone string exists. If it does not,
    # setup default.
    if [ ! -f $timezone_file ] ; then
        timezone="$default_tz"
        swi_log "Using default timezone [$timezone]."
        echo "$timezone" >$timezone_file
    else
        timezone=$( cat $timezone_file )
        swi_log "Using timezone [$timezone]."
    fi

    # Make sure that timezone exists.
    if [ ! -f $timezone_files_root/$timezone ] ; then
        swi_log "[$timezone] does not exist, using [$default_tz]."
        timezone="$default_tz"
        echo "$timezone" >$timezone_file
    fi

    # /etc/localtime must point to proper timezone file.
    localtime=$( readlink -f $sl_localtime )
    if [ "x$localtime" != "x$real_localtime" ] ; then
        # None of this should fail, it's RW file system.
        rm -f $sl_localtime
        ln -s $real_localtime $sl_localtime
    fi

    # We always have to do this, because overlay is mounted on /etc
    # after timezone initial mount bind (take a look at
    # mount_early:mount_early_set_timezone() )
    umount $real_localtime &>/dev/null
    mount --bind $timezone_files_root/$timezone $real_localtime

    return $SWI_OK
}

# Make sure that /etc/timezone is writable even if /etc is not.
set_timezone_writable()
{
    local ret=${SWI_OK}

    # If /etc is not writable, make sure that /etc/timezone is.
    if [ "x${etc_is_writable_g}" = "x${SWI_TRUE}" ] ; then
        return ${SWI_OK}
    fi

    # This is only going to work if /etc/timezone exists
    if [ ! -f /etc/timezone ] ; then
        # Timezone file is not available, and there is nothing
        # we should do here. Note that this is not an error.
        return ${SWI_OK}
    fi

    # Save timezone content.
    cat /etc/timezone > /tmp/timezone

    # Now, make sure it appears to be writable.
    mount --bind /tmp/timezone /etc/timezone
    if [ $? -ne 0 ] ; then
        swi_log "Cannot mount /tmp/timezone"
        ret=${SWI_ERR}
    fi

    return ${ret}
}

mount_unionfs_dir()
{
    local root_g=$1
    local mount_opts=""
    local ret=1
    local extra_mnt_opts=""
    local kvers_chk="4.14"

    mkdir -p ${flash_mntpt_g}${root_g}

    # Kernel 3.14
    # Prior to overlayfs, we've had aufs. Even these are very similar,
    # operations like file/dir move/delete are handled differently.
    # In order to be backwards compatible, we need to make sure that
    # prior changes are preserved. For now, we are not going to be
    # very smart and try to transition from aufs to overlayfs. If
    # RW unionfs was aufs, leave it that way.
    if [[ "${ufs_type}" == "overlayfs" ]]; then
        if [[ ! -f ${flash_mntpt_g}/${overlayfs_sig_g} ]]; then
            # Flip it back, not ready to transition yet.
            ufs_type=aufs
        fi
    fi

    if [[ "${ufs_type}" == "aufs" ]]; then
        mount_opts="dirs=${flash_mntpt_g}${root_g}=rw:${root_g}=ro"
    elif [[ "${ufs_type}" == "overlayfs" ]]; then
        # Overlayfs v21 have a bit different mount options. And it is called overlayfs
        # instead of overlay. This file system is in kernel 3.14 (e.g. mdm9x15/WP85).
       mount_opts="lowerdir=${root_g},upperdir=${flash_mntpt_g}${root_g}"
       touch ${flash_mntpt_g}/${overlayfs_sig_g}
    elif [[ "${ufs_type}" == "overlay" ]]; then
        # Overlay behavior in kernel 4.14 has changed, and we need to adapt
        uname -r | grep "^${kvers_chk}" >/dev/null
        ret=$?
        if [ ${ret} = 0 ] ; then
            # Kernel is 4.14, add extra overlay file system options
            extra_mnt_opts=",override_creds=off"
        fi

        # Make sure working directory is empty
        rm -rf ${flash_mntpt_g}${root_g}_wk
        mkdir -p ${flash_mntpt_g}${root_g}_wk
        mount_opts="lowerdir=${root_g},workdir=${flash_mntpt_g}${root_g}_wk,upperdir=${flash_mntpt_g}${root_g}${extra_mnt_opts}"
    else
        return ${SWI_ERR}
    fi

    mount -t ${ufs_type} -o ${mount_opts} ${ufs_type} ${root_g}
    if [ $? -ne 0 ] ; then
        swi_log "Unable to mount unionfs file system (${flash_mntpt_g}${root_g}=rw, ${root_g}=ro, union=${root_g})."
        return ${SWI_ERR}
    fi

    return ${SWI_OK}
}

# Bind mount nagger related files.
bind_mount_nagger_files()
{
    local ret=${SWI_OK}

    mkdir -p ${FLASH_MOUNTPOINT_RFS}/etc/default

    # Bind mount /etc/shadow
    if [ ! -f ${FLASH_MOUNTPOINT_RFS}/etc/shadow ] ; then
        cp -af /etc/shadow ${FLASH_MOUNTPOINT_RFS}/etc/.
    fi

    mount --bind ${FLASH_MOUNTPOINT_RFS}/etc/shadow /etc/shadow
    if [ $? -ne 0 ] ; then
        swi_log "Cannot bind mount /etc/shadow ."
        return ${SWI_ERR}
    fi

    # Bind mount /etc/default/dropbear
    if [ ! -f ${FLASH_MOUNTPOINT_RFS}/etc/default/dropbear ] ; then
        cp -af /etc/default/dropbear ${FLASH_MOUNTPOINT_RFS}/etc/default/.
    fi

    mount --bind ${FLASH_MOUNTPOINT_RFS}/etc/default/dropbear /etc/default/dropbear
    if [ $? -ne 0 ] ; then
        swi_log "Cannot bind mount /etc/default/dropbear ."
        return ${SWI_ERR}
    fi

    # Bind mount /etc/securetty
    if [ ! -f ${FLASH_MOUNTPOINT_RFS}/etc/securetty ] ; then
        cp -af /etc/securetty ${FLASH_MOUNTPOINT_RFS}/etc/.
    fi

    mount --bind ${FLASH_MOUNTPOINT_RFS}/etc/securetty /etc/securetty
    if [ $? -ne 0 ] ; then
        swi_log "Cannot bind mount /etc/securetty ."
        return ${SWI_ERR}
    fi

    return ${ret}
}

#
# Fixup few things once unionfs is mounted.
#
mount_fixup()
{
    local ret=${SWI_OK}

    # Bind mount nagger related files, if required.
    if is_bindmount_nagger_files; then
        if ! bind_mount_nagger_files; then
            ret=${SWI_ERR}
        fi
    fi

    return ${ret}
}


# Start union fs
mount_unionfs_start()
{
    local ret=${SWI_OK}

    is_fudge_ro_rootfs_allowed
    if [ $? -ne ${SWI_TRUE} ] ; then
        swi_log "Not allowed to fudge rootfs."
        return ${SWI_ERR}
    fi

    is_etc_overlay_disabled
    if [ $? -ne ${SWI_TRUE} ] ; then
        # Fixup ECM entry in mdev.conf:
        # If there is a RW version of mdev.conf, update it with ECM entries
        rw_mdev_conf=${flash_mntpt_g}${root_etc_g}/mdev.conf
        if [ -w ${rw_mdev_conf} ]; then
            rw_mdev_conf_updated=`grep bringup_ecm ${rw_mdev_conf}`
            if [ "x${rw_mdev_conf_updated}" = "x" ]; then
                echo "\$DEVPATH=.*/net/e[ec]m[0-9] 0:0 0660 ! @/etc/mdev/bringup_ecm.sh" >> ${rw_mdev_conf}
                echo "\$DEVPATH=.*/net/usb[0-9] 0:0 0660 ! @/etc/mdev/bringup_ecm.sh" >> ${rw_mdev_conf}
            fi
            # If RW and RO files are now identical, remove the RW one
            mdev_conf_diff=`diff ${rw_mdev_conf} ${root_etc_g}/mdev.conf`
            if [ "x${mdev_conf_diff}" = "x" ]; then
                rm -f ${rw_mdev_conf}
            fi
        fi
        # Try to make /etc writable.
        mount_unionfs_dir ${root_etc_g}
        if [ $? -ne ${SWI_OK} ] ; then
            return ${SWI_ERR}
        fi
        etc_is_writable_g=${SWI_TRUE}
    fi

    # Try to make /data writable
    mount_unionfs_dir ${root_data_g}
    if [ $? -ne ${SWI_OK} ] ; then
        umount -l ${root_etc_g}
        return ${SWI_ERR}
    fi


    return ${ret}
}

unset_timezone_writable()
{
    # Do not care about the result
    umount -l /etc/timezone &>/dev/null

    return ${SWI_OK}
}

# Stop union fs.
mount_unionfs_stop()
{
    ret=${SWI_OK}

    is_fudge_ro_rootfs_allowed
    if [ $? -ne ${SWI_TRUE} ] ; then
        swi_log "Nothing to do (fudging of rootfs is not allowed)."
        return ${ret}
    fi

    # Unmount unionfs (don't care about the result).
    umount -l ${root_etc_g} &>/dev/null
    umount -l ${root_data_g} &>/dev/null

    return ${ret}
}


#
# Execution starts here.
#
case "$1" in
    start)
        mount_unionfs_start
        set_timezone_writable
        localtime_fixup
        mount_fixup
    ;;

    stop)
        unset_timezone_writable
        mount_unionfs_stop
    ;;

    *)
        echo "Usage: ${this_e} {start | stop}"
        exit 1
    ;;
esac

exit 0

