Min Soe Han

LegalBusinessElse


backTech:


Mount/Unmount mountpoint that has user,noauto options in /etc/fstab

noauto

DATE 20240817

Mounting data partition to a mountpoint outside directory structure in /etc/fstab is a bit complicated. By default, the partitions are auto mounted to their mountpoints respectively at boot. But the problem is that, the owner of the mountpoint would be root and being without write permission for normal user because mount command was executed by root. On the other hand, the line in /etc/fstab that has user,noauto options is not auto mounted at boot. It can be mounted later as normal user by just running the following.

~$ mount ~/Extra-Data-Partition

But if the above mount command has any option, it would not work asking superuser permission.

user@hostname ~]$ mount --mkdir --onlyonce ~/Extra-Data-Partition
mount: /home/user/Extra-Data-Partition: must be superuser to use mount.
        dmesg(1) may have more information after failed mount system call.
[user@hostname ~]$

In case of many extra data partitions being supposed to be mounted to mountpoints outside directory structure, it would be hard to manage if you care about if the mountpoint is already mounted or not, or the mountpoint directory exists or not. For that matter, I wrote a dash script.

The Script

Here’s the dash shell script designed to automate mount, unmount and toggle.

#!/bin/dash

# check if run as root and exit
if [ $(($(id -u))) -eq 0 ]; then
    exit 0
fi
# check if it is already running and exit
SCRIPTNAME="$(basename -- $0)"
if pidof -x "$SCRIPTNAME" -o $$ >/dev/null 2>&1; then
    echo "the script is already running. clean exit."
    exit 0
fi

NOAUTOMP=$(awk '/^UUID.*noauto/ {print $2}' /etc/fstab)
if echo "$NOAUTOMP" | grep '\s' >/dev/null 2>&1; then
    notify-send -t 3600 "SPACE" "Mountpoint names include space. check them first. clean exit."
    exit 0
fi

lsblkline_func () {
    local LSBLKLN=$(lsblk -r | column -t | sed 's/\s/_/g')
    local MAXLINE=$(echo "$LSBLKLN" | wc -L)
    for i in $LSBLKLN; do
        local ADDLN=$(($MAXLINE-$(echo -n "$i" | wc -c)))
        echo ""$i"$(for i in $(seq $ADDLN); do echo -n "_"; done)"
    done
}

mountnoauto_func () {
    for i in $NOAUTOMP; do
        if lsblk -r | grep "$i" >/dev/null 2>&1; then
            notify-send -t 2700 "Already Mounted" ""$i" is already mounted."
            continue
        fi
        mkdir -p "$i"
        mount "$i"
    done
    notify-send -t 3600 "Mounted All" "$(lsblkline_func)"
}
umountnoauto_func () {
    for i in $NOAUTOMP; do
        if lsblk -r | grep "$i" >/dev/null 2>&1; then
            umount "$i"
            continue
        fi
        notify-send -t 2700 "NOT Mounted" ""$i" is NOT mounted."
    done
    notify-send -t 3600 "Unmounted All" "$(lsblkline_func)"
}
togglenoauto_func () {
    local MOUN=0
    local UMOUN=0
    for i in $NOAUTOMP; do
        if lsblk -r | grep "$i" >/dev/null 2>&1; then
            local MOUN=1
        else
            local UMOUN=1
        fi
    done
    if [ $((MOUN)) -eq 1 ] && [ $((UMOUN)) -eq 1 ]; then
        notify-send -t 2700 "Can NOT Toggle" "Some are mounted, some are NOT. clean exit."
        exit 0
    fi
    if [ $((MOUN)) -eq 1 ] && [ $((UMOUN)) -eq 0 ]; then
        for i in $NOAUTOMP; do
            umount "$i"
        done
        exit 0
    fi
    if [ $((MOUN)) -eq 0 ] && [ $((UMOUN)) -eq 1 ]; then
        for i in $NOAUTOMP; do
            mkdir -p "$i"
            mount "$i"
        done
        exit 0
    fi
}
interactive_func () {
    # check if it is running in terminal or exit
    case "$(ps -o stat= -p $$)" in
        *+*) echo "continue..." ;;
        *) notify-send -t 2700 "clean exit" "Please run it in terminal."; exit 0 ;;
    esac
    [ -f /tmp/noautomountpoints.msh ] && rm /tmp/noautomountpoints.msh
    while true; do
        local NUM=1
        for i in $NOAUTOMP; do
            if lsblk -r | grep "$i" >/dev/null 2>&1; then
                echo ""$NUM". mounted    "$i""
            else
                echo ""$NUM". unmounted  "$i""
            fi
            local NUM=$((NUM+1))
        done > /tmp/noautomountpoints.msh
        while true; do
            echo; lsblk
            echo; cat /tmp/noautomountpoints.msh
            echo; read -p "Type Number or [e|exit|q|quit] for Exit: " SNUM
            case "$SNUM" in
                e|exit|q|quit) echo "clean exit."; exit 0 ;;
                [0-9]|[0-9][0-9]|[0-9][0-9][0-9]) echo "continue..." ;;
                *) echo "Invalid selection. Repeated. Type number only."; continue ;;
            esac
            if ! grep "^$SNUM" /tmp/noautomountpoints.msh >/dev/null 2>&1; then
                echo "Invalid selection. Repeated. Type number only."
                unset SNUM
                continue
            fi
            break
        done
        local SELINE=$(grep "^$SNUM" /tmp/noautomountpoints.msh)
        local MSTATE=$(echo "$SELINE" | awk '{print $2}')
        local MPOINT=$(echo "$SELINE" | awk '{print $3}')
        case "$MSTATE" in
            mounted) umount "$MPOINT" ;;
            unmounted) mkdir -p "$MPOINT"; mount "$MPOINT" ;;
            *) echo "Oop! something went wrong. clean exit."; exit 0 ;;
        esac
        echo; lsblk
        echo; read -p "Exit or Continue? [e/c]; " CEYN
        case "$CEYN" in
            e|exit) break ;;
            c|con|continue) continue ;;
            *) echo "Invalid selection. clean exit."; exit 0 ;;
        esac
    done
    [ -f /tmp/noautomountpoints.msh ] && rm /tmp/noautomountpoints.msh
}
helpusage_func () {
    echo "
    Usage:
        noauto m|mount
        noauto u|umount
        noauto t|toggle
        noauto c|check
        noauto
    "
}

case $1 in
    m|mount) mountnoauto_func ;;
    u|umount) umountnoauto_func ;;
    t|toggle) togglenoauto_func ;;
    c|check) lsblkline_func ;;
    "") interactive_func ;;
    h|help) helpusage_func ;;
    *) echo "Invalid usage"; helpusage_func; exit 0 ;;
esac