├── META-INF └── com │ └── google │ └── android │ ├── updater-script │ └── update-binary ├── customize.sh ├── module.prop ├── cifs-sample.conf ├── service.sh ├── mount-from-config.sh └── README.md /META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /customize.sh: -------------------------------------------------------------------------------- 1 | set_perm $MODPATH/service.sh 0 0 0755 2 | set_perm $MODPATH/mount-from-config.sh 0 0 0755 -------------------------------------------------------------------------------- /module.prop: -------------------------------------------------------------------------------- 1 | id=multi-mount 2 | name=Multi-Mount 3 | version=v1.0.0 4 | versionCode=1 5 | author=clifforama 6 | description=Mounts one or more filesystems (e.g. CIFS/SMB, NFS, etc.) 7 | -------------------------------------------------------------------------------- /cifs-sample.conf: -------------------------------------------------------------------------------- 1 | mount_options="-t cifs -o vers=2.0,username=user,password=pass" 2 | mount_source="//host/share" 3 | mount_target="/mnt/cifs-share" 4 | mount_max_retries=20 5 | mount_retry_interval=15s 6 | -------------------------------------------------------------------------------- /META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | ################# 4 | # Initialization 5 | ################# 6 | 7 | umask 022 8 | 9 | # echo before loading util_functions 10 | ui_print() { echo "$1"; } 11 | 12 | require_new_magisk() { 13 | ui_print "*******************************" 14 | ui_print " Please install Magisk v20.4+! " 15 | ui_print "*******************************" 16 | exit 1 17 | } 18 | 19 | ######################### 20 | # Load util_functions.sh 21 | ######################### 22 | 23 | OUTFD=$2 24 | ZIPFILE=$3 25 | 26 | mount /data 2>/dev/null 27 | 28 | [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk 29 | . /data/adb/magisk/util_functions.sh 30 | [ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk 31 | 32 | install_module 33 | exit 0 34 | -------------------------------------------------------------------------------- /service.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | # Do NOT assume where your module will be located. 3 | # ALWAYS use $MODDIR if you need to know where this script and module is placed. 4 | # This will make sure your module will still work if Magisk changes its mount point in the future 5 | MODDIR=${0%/*} 6 | 7 | export LOG_FILE="${MODDIR}/multi-mount.log" 8 | LOG_MAX_LINES=1000 9 | 10 | BOOTWAIT_MAX_COUNT=20 11 | BOOTWAIT_COUNT_INTERVAL=15s 12 | 13 | CONF_DIR="/sdcard/.multi-mount" 14 | CONF_FILESPEC="${CONF_DIR}/*.conf" 15 | 16 | # wait for system boot to complete 17 | bootwait_count=0 18 | until [[ $(getprop sys.boot_completed) || ${bootwait_count} -ge ${BOOTWAIT_MAX_COUNT} ]]; do 19 | sleep ${BOOTWAIT_COUNT_INTERVAL} 20 | bootwait_count=$((bootwait_count+1)) 21 | done 22 | if [ ${bootwait_count} -ge ${BOOTWAIT_MAX_COUNT} ]; then 23 | exit 1 24 | fi 25 | 26 | # prevent log file from growing too large 27 | tail -n "${LOG_MAX_LINES}" "${LOG_FILE}" > "${LOGFILE}.tmp" 28 | mv "${LOGFILE}.tmp" "${LOG_FILE}" 29 | 30 | echo "=== $(date) ===" >> "${LOG_FILE}" 2>&1 31 | 32 | # process config files in parallel 33 | config_file_count=0 34 | for f in ${CONF_FILESPEC}; do 35 | config_file_count=$((config_file_count+1)) 36 | "${MODDIR}/mount-from-config.sh" "${f}" & 37 | done 38 | if [ "${config_file_count}" -eq 0 ]; then 39 | echo "No config files found." >> "${LOG_FILE}" 2>&1 40 | fi 41 | wait 42 | -------------------------------------------------------------------------------- /mount-from-config.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | CONFIG_FILE="$1" 3 | CONFIG_PARAMS="mount_options mount_source mount_target mount_max_retries mount_retry_interval" 4 | 5 | # source user-specified config file 6 | if [ -r "${CONFIG_FILE}" ]; then 7 | . "${CONFIG_FILE}" >> "${LOG_FILE}" 2>&1 8 | missing_count=0 9 | for p in ${CONFIG_PARAMS}; do 10 | if [[ -z $(eval "echo \"\$${p}\"") ]]; then 11 | echo "Config file \"${CONFIG_FILE}\" is missing param \"${p}\"." >> "${LOG_FILE}" 2>&1 12 | missing_count=$((missing_count+1)) 13 | fi 14 | done 15 | if [ ${missing_count} -gt 0 ]; then 16 | exit 17 | fi 18 | else 19 | echo "Unable to read config file \"${CONFIG_FILE}\"." >> "${LOG_FILE}" 2>&1 20 | exit 21 | fi 22 | 23 | # attempt to mount 24 | mkdir -p "${mount_target}" >> "${LOG_FILE}" 2>&1 25 | retries=0 26 | while : ; do 27 | su -c "mount ${mount_options} \"${mount_source}\" \"${mount_target}\"" >> "${LOG_FILE}" 2>&1 28 | if grep -q "${mount_target}" /proc/mounts; then 29 | echo "Successfully mounted source \"${mount_source}\" at \"${mount_target}\"." >> "${LOG_FILE}" 2>&1 30 | break 31 | elif [ ${retries} -lt "${mount_max_retries}" ]; then 32 | sleep "${mount_retry_interval}" 33 | retries=$((retries+1)) 34 | else 35 | echo "Failed to mount source \"${mount_source}\" at \"${mount_target}\" after $((retries+1)) attempts (${retries} retries)." >> "${LOG_FILE}" 2>&1 36 | exit 37 | fi 38 | done 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi-Mount 2 | 3 | ## Description 4 | 5 | This Magisk module mounts one or more filesystems (e.g. CIFS/SMB, NFS, etc.). 6 | 7 | 8 | 9 | ## Requirements 10 | 11 | - Magisk v20.4+ 12 | 13 | - Kernel support for filesystem(s) to be mounted (hint: `grep cifs /proc/filesystems`) 14 | 15 | 16 | 17 | ## Instructions 18 | 19 | 1. Create config file(s) with filename(s) ending in `.conf` and place them in `/sdcard/.multi-mount` (i.e. the 20 | `.multi-mount` directory resides in the same directory that typically contains DCIM, Documents, Download, etc.). Each 21 | config file corresponds to a distinct mount configuration and must adhere to the following example: 22 | 23 | mount_options="-t cifs -o vers=2.0,username=user,password=pass" 24 | mount_source="//host/share" 25 | mount_target="/mnt/cifs-share" 26 | mount_max_retries=20 27 | mount_retry_interval=15s 28 | 29 | 2. Install module via Magisk Manager, then reboot. 30 | 31 | 32 | 33 | ## Notes 34 | 35 | - Every distinct mount requires its own config file. So, to mount multiple CIFS shares, create multiple config files 36 | (e.g. `cifs-1.conf`, `cifs-2.conf`, etc.). Filenames are arbitrary, but must end in `.conf`. 37 | 38 | - Config files are read at boot. Reboot for changes to take effect. 39 | 40 | - In order to mount a network share, the network must be reachable after boot within the timeframe defined by 41 | `mount_max_retries` and `mount_retry_interval`. 42 | 43 | - If mounting a CIFS share fails, try specifying a different CIFS version. 44 | 45 | - While any filesystem supported by the kernel should work, Multi-Mount has only been tested with CIFS on [KonstaKANG's 46 | LineageOS 18.1 Android TV (Android 11) for Raspberry Pi 4](https://konstakang.com/devices/rpi4/LineageOS18-ATV/), 47 | release 11.10. 48 | --------------------------------------------------------------------------------