#!/bin/bash
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# Copyright 2019 Alessandro "Locutus73" Miele
# You can download the latest version of this script from:
# https://github.com/MiSTer-devel/CIFS_MiSTer
# Version 2.2.0 - 2026-04-19 - Default unmount now targets cifs_mount.sh managed mounts only.
# - Added --all to preserve the old global "umount -a -t cifs" behavior.
# - Falls back to lazy unmount for busy targets.
# - Warns when the caller's current directory was unmounted.
# Version 2.0 - 2019-02-05 - Renamed from mount_cifs.sh and umount_cifs.sh to cifs_mount.sh and cifs_umount.sh for having them sequentially listed in alphabetical order.
# Version 1.0 - 2019.01.05 - First commit
BASE_PATH="/media/fat"
LOCAL_DIR="cifs"
IFS="|"
SINGLE_CIFS_CONNECTION="true"
SPECIAL_DIRECTORIES="config|linux|System Volume Information"
LAZY_UNMOUNT_ON_BUSY="true"
trim_ini_field() {
local FIELD="$1"
FIELD="${FIELD#"${FIELD%%[!$' \t']*}"}"
FIELD="${FIELD%"${FIELD##*[!$' \t']}"}"
printf '%s' "$FIELD"
}
load_cifs_ini() {
local INI_FILE="$1"
local LINE KEY VALUE ALLOWED_KEY KEY_ALLOWED
shift
while IFS= read -r LINE || [ "$LINE" != "" ]
do
LINE=${LINE%$'\r'}
case "$LINE" in
*=*) ;;
*) continue ;;
esac
KEY=$(trim_ini_field "${LINE%%=*}")
case "$KEY" in
''|\#*) continue ;;
esac
KEY_ALLOWED="false"
for ALLOWED_KEY in "$@"
do
if [ "$KEY" == "$ALLOWED_KEY" ]
then
KEY_ALLOWED="true"
break
fi
done
[ "$KEY_ALLOWED" == "true" ] || continue
VALUE=$(trim_ini_field "${LINE#*=}")
case "$VALUE" in
\"*) VALUE=${VALUE#\"}; VALUE=${VALUE%%\"*} ;;
\'*) VALUE=${VALUE#\'}; VALUE=${VALUE%%\'*} ;;
esac
printf -v "$KEY" '%s' "$VALUE"
done < "$INI_FILE"
}
resolve_path() {
if command -v realpath >/dev/null 2>&1
then
realpath "$1" 2>/dev/null && return 0
fi
if command -v readlink >/dev/null 2>&1
then
readlink -f "$1" 2>/dev/null && return 0
fi
echo "$1"
}
ORIGINAL_SCRIPT_PATH="$0"
if [ "$ORIGINAL_SCRIPT_PATH" == "bash" ]
then
ORIGINAL_SCRIPT_PATH=$(ps | grep "^ *$PPID " | grep -o "[^ ]*$")
fi
SCRIPT_REALPATH=$(resolve_path "$ORIGINAL_SCRIPT_PATH")
case "$SCRIPT_REALPATH" in
*/*) SCRIPT_DIR=${SCRIPT_REALPATH%/*} ;;
*) SCRIPT_DIR="." ;;
esac
CIFS_UMOUNT_INI_KEYS=(
BASE_PATH
LOCAL_DIR
SINGLE_CIFS_CONNECTION
SPECIAL_DIRECTORIES
LAZY_UNMOUNT_ON_BUSY
)
MOUNT_INI_PATH="${SCRIPT_DIR}/cifs_mount.ini"
FALLBACK_INI="/media/fat/Scripts/cifs_mount.ini"
MOUNT_SCRIPT_NAME="cifs_mount"
TEMP_MOUNT="/tmp/${MOUNT_SCRIPT_NAME}"
if [ "${1:-}" == "--all" ]
then
if umount -a -t cifs
then
echo "Done!"
exit 0
fi
echo "CIFS shares not unmounted."
exit 1
elif [ "${1:-}" != "" ]
then
echo "Usage: ${ORIGINAL_SCRIPT_PATH##*/} [--all]"
exit 2
fi
if [ -f "$MOUNT_INI_PATH" ]
then
load_cifs_ini "$MOUNT_INI_PATH" "${CIFS_UMOUNT_INI_KEYS[@]}"
elif [ "$MOUNT_INI_PATH" != "$FALLBACK_INI" ] && [ -f "$FALLBACK_INI" ]
then
load_cifs_ini "$FALLBACK_INI" "${CIFS_UMOUNT_INI_KEYS[@]}"
fi
IFS="|"
UNMOUNT_TARGETS=()
UNMOUNT_FAILURES=0
INITIAL_PWD=$(pwd -P 2>/dev/null || pwd 2>/dev/null)
CURRENT_DIRECTORY_UNMOUNTED="false"
add_target() {
local TARGET="$1"
local EXISTING
[ "$TARGET" != "" ] || return 0
for EXISTING in "${UNMOUNT_TARGETS[@]}"
do
[ "$EXISTING" == "$TARGET" ] && return 0
done
UNMOUNT_TARGETS+=("$TARGET")
}
target_contains_initial_pwd() {
local TARGET="$1"
[ "$INITIAL_PWD" != "" ] || return 1
case "$INITIAL_PWD" in
"$TARGET"|"$TARGET"/*) return 0 ;;
esac
return 1
}
is_special_directory() {
local DIRECTORY="$1"
local SPECIAL_DIRECTORY
local OLD_IFS="$IFS"
IFS="|"
for SPECIAL_DIRECTORY in $SPECIAL_DIRECTORIES
do
if [ "$DIRECTORY" == "$SPECIAL_DIRECTORY" ]
then
IFS="$OLD_IFS"
return 0
fi
done
IFS="$OLD_IFS"
return 1
}
get_mount_info() {
local PATH_TO_CHECK="$1"
local LINE
local SOURCE
local REST
local TARGET
local TYPE_REST
MOUNT_SOURCE_FOR_TARGET=""
MOUNT_TYPE_FOR_TARGET=""
while IFS= read -r LINE
do
SOURCE=${LINE%% on *}
REST=${LINE#* on }
[ "$REST" != "$LINE" ] || continue
TARGET=${REST%% type *}
[ "$TARGET" == "$PATH_TO_CHECK" ] || continue
TYPE_REST=${REST#* type }
[ "$TYPE_REST" != "$REST" ] || return 1
MOUNT_SOURCE_FOR_TARGET="$SOURCE"
MOUNT_TYPE_FOR_TARGET=${TYPE_REST%% *}
return 0
done </dev/null 2>&1
then
UNMOUNTED="true"
elif [ "$LAZY_UNMOUNT_ON_BUSY" == "true" ] && umount -l "$TARGET" >/dev/null 2>&1
then
UNMOUNTED="true"
else
echo "${TARGET##*/} not unmounted"
UNMOUNT_FAILURES=$((UNMOUNT_FAILURES + 1))
return 1
fi
done
if [ "$UNMOUNTED" == "true" ]
then
echo "${TARGET##*/} unmounted"
target_contains_initial_pwd "$TARGET" && CURRENT_DIRECTORY_UNMOUNTED="true"
fi
return 0
}
if [ "$LOCAL_DIR" == "*" ]
then
add_wildcard_targets_from_mounts
else
add_configured_targets
fi
for ((INDEX=${#UNMOUNT_TARGETS[@]} - 1; INDEX >= 0; INDEX--))
do
unmount_path "${UNMOUNT_TARGETS[$INDEX]}"
done
unmount_temp_mounts
if [ "$UNMOUNT_FAILURES" -gt 0 ]
then
echo "Done with ${UNMOUNT_FAILURES} failure(s)."
exit 1
fi
echo "Done!"
[ "$CURRENT_DIRECTORY_UNMOUNTED" == "true" ] && echo "Current shell directory was unmounted; run: cd /media/fat/Scripts"
exit 0