diff options
-rw-r--r-- | .gitignore | 100 | ||||
-rw-r--r-- | META-INF/com/google/android/update-binary | 33 | ||||
-rw-r--r-- | META-INF/com/google/android/updater-script | 1 | ||||
-rw-r--r-- | README.md | 23 | ||||
-rw-r--r-- | config/fstab.conf | 4 | ||||
-rw-r--r-- | config/main.conf | 5 | ||||
-rw-r--r-- | customize.sh | 4 | ||||
-rw-r--r-- | fstab.sh | 179 | ||||
-rw-r--r-- | module.prop | 6 | ||||
-rw-r--r-- | service.sh | 53 |
10 files changed, 408 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b983ed5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,100 @@ +release +# Created by https://www.toptal.com/developers/gitignore/api/linux,windows,macos,vim +# Edit at https://www.toptal.com/developers/gitignore?templates=linux,windows,macos,vim + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/linux,windows,macos,vim + diff --git a/META-INF/com/google/android/update-binary b/META-INF/com/google/android/update-binary new file mode 100644 index 0000000..28b48e5 --- /dev/null +++ b/META-INF/com/google/android/update-binary @@ -0,0 +1,33 @@ +#!/sbin/sh + +################# +# Initialization +################# + +umask 022 + +# echo before loading util_functions +ui_print() { echo "$1"; } + +require_new_magisk() { + ui_print "*******************************" + ui_print " Please install Magisk v20.4+! " + ui_print "*******************************" + exit 1 +} + +######################### +# Load util_functions.sh +######################### + +OUTFD=$2 +ZIPFILE=$3 + +mount /data 2>/dev/null + +[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk +. /data/adb/magisk/util_functions.sh +[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk + +install_module +exit 0 diff --git a/META-INF/com/google/android/updater-script b/META-INF/com/google/android/updater-script new file mode 100644 index 0000000..11d5c96 --- /dev/null +++ b/META-INF/com/google/android/updater-script @@ -0,0 +1 @@ +#MAGISK diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e82703 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# magisk-fstab + +A magisk module to load custom fstab configs on startup. + +## Requirements + +- Magisk v20.4+ +- Kernel support for filesystems to be mounted + +## Installation + +Install the release zip in Magisk Manager. + +## Usage + +1. Adjust the main config in `${MODDIR}/config/main.conf` to your liking. `$MODDIR` is usually `/data/adb/modules/magisk-fstab`. +2. Place your `fstab` entries in `${MODDIR}/config/fstab.conf`. +3. Upon every restart, this module will try to perform the actions based on the `fstab` config. + +## References + +- [Magisk documentation](https://topjohnwu.github.io/Magisk/) +- [fstab manual](https://man7.org/linux/man-pages/man5/fstab.5.html) diff --git a/config/fstab.conf b/config/fstab.conf new file mode 100644 index 0000000..903906d --- /dev/null +++ b/config/fstab.conf @@ -0,0 +1,4 @@ +# Static information about the filesystems. +# See fstab(5) for details. + +# <file system> <dir> <type> <options> <dump> <pass> diff --git a/config/main.conf b/config/main.conf new file mode 100644 index 0000000..2acc32f --- /dev/null +++ b/config/main.conf @@ -0,0 +1,5 @@ +# magisk-fstab configuration file + +# Options to be injected into `su -c` calls. +# Typically, `--mount-master` is required for mounts in master namespace. +su_options="--mount-master" diff --git a/customize.sh b/customize.sh new file mode 100644 index 0000000..7e61433 --- /dev/null +++ b/customize.sh @@ -0,0 +1,4 @@ +set_perm $MODPATH/service.sh 0 0 0755
+set_perm $MODPATH/fstab.sh 0 0 0755
+set_perm $MODPATH/config/main.conf 0 0 0755
+set_perm $MODPATH/config/fstab.conf 0 0 0755
diff --git a/fstab.sh b/fstab.sh new file mode 100644 index 0000000..e00dc16 --- /dev/null +++ b/fstab.sh @@ -0,0 +1,179 @@ +#!/system/bin/sh +# fstab.sh +# Reads an fstab-style config and performs mounts/swapon without touching /etc/fstab. +# - Messages go to console AND to $LOG_FILE. +# - Privileged commands (mkdir, mount, swapon) run via: su --mount-master -c "…" +# +# Usage: ./fstab.sh path/to/fstab.conf +# Override log file by exporting LOG_FILE before running. + +FSTAB_FILE="$1" + +if [ -z "$FSTAB_FILE" ]; then + echo "Usage: $0 path/to/fstab.conf" + exit 2 +fi + +if [ ! -r "$FSTAB_FILE" ]; then + echo "File '$FSTAB_FILE' not readable." + exit 2 +fi + +# Ensure 'su' exists +if ! command -v su >/dev/null 2>&1; then + echo "ERROR: 'su' not found. Cannot perform privileged operations." + exit 2 +fi + +error_count=0 +line_no=0 + +# --- logging helpers (both console and file) --- +log() { echo "$*" | tee -a "$LOG_FILE"; } +warn() { echo "WARN: $*" | tee -a "$LOG_FILE"; } +err() { echo "ERROR: $*" | tee -a "$LOG_FILE"; error_count=`expr $error_count + 1`; } + +# --- safe single-quoting for building a command string for su -c --- +shell_quote() { + # Output a single-quoted representation of $1, handling internal single quotes + # 'abc'd' -> 'abc'"'"'d' + printf "'%s'" "$(printf "%s" "$1" | sed "s/'/'\"'\"'/g")" +} + +# --- run a command as root via su --mount-master -c "..." preserving args safely --- +# Usage: run_root CMD ARG1 ARG2 ... +run_root() { + cmd="$1"; shift + # Build a single string: CMD 'ARG1' 'ARG2' ... + cmdline=$(shell_quote "$cmd") + for a in "$@"; do + cmdline="$cmdline $(shell_quote "$a")" + done + log "RUN (as root): $cmd $*" + # Execute + su $su_options -c "$cmdline" + rc=$? + [ $rc -ne 0 ] && err "Command failed (exit $rc): $cmd $*" + return $rc +} + +# --- check if a mountpoint is already mounted (no root needed) --- +is_mounted() { + mp="$1" + if command -v mountpoint >/dev/null 2>&1; then + mountpoint -q "$mp" + return $? + fi + grep -qs " $mp " /proc/mounts + return $? +} + +# --- check if swap device is already active (no root needed) --- +is_swap_active() { + spec="$1" + if command -v swapon >/dev/null 2>&1; then + # Prefer swapon --show if available + run_root swapon --show=NAME --noheadings 2>/dev/null | grep -Fxq "$spec" && return 0 + basename_spec=`basename "$spec"` + run_root swapon --show=NAME --noheadings 2>/dev/null | awk -F/ '{print $NF}' | grep -Fxq "$basename_spec" && return 0 + return 1 + fi + grep -qs "^$spec[[:space:]]" /proc/swaps + return $? +} + +# --- process each fstab line --- +while IFS= read -r rawline || [ -n "$rawline" ]; do + line_no=`expr $line_no + 1` + line=`echo "$rawline" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'` + + case "$line" in + ""|\#*) continue ;; + esac + + # spec file vfstype mntops dump pass (default 0 for last two if missing) + set -- `echo "$line" | awk '{print $1, $2, $3, $4, ($5? $5:0), ($6? $6:0)}'` + fs_spec=$1 + fs_file=$2 + fs_vfstype=$3 + fs_mntops=$4 + fs_freq=$5 + fs_pass=$6 + + if [ -z "$fs_spec" ] || [ -z "$fs_file" ] || [ -z "$fs_vfstype" ]; then + warn "Line $line_no: Invalid entry, skipped." + continue + fi + + echo "$fs_mntops" | grep -qw noauto && { + log "Line $line_no: 'noauto' found, skipping $fs_spec -> $fs_file." + continue + } + + # --- swap handling --- + if [ "$fs_vfstype" = "swap" ]; then + if is_swap_active "$fs_spec"; then + log "Line $line_no: Swap '$fs_spec' already active — skipped." + continue + fi + if command -v swapon >/dev/null 2>&1; then + log "Line $line_no: Activating swap: $fs_spec" + if [ "$fs_mntops" = "-" ]; then + run_root swapon "$fs_spec" || true + else + run_root swapon -o "$fs_mntops" "$fs_spec" || true + fi + else + err "Line $line_no: swapon not available." + fi + continue + fi + + # --- bind / rbind mounts --- + echo "$fs_mntops" | grep -q bind && { + if is_mounted "$fs_file"; then + log "Line $line_no: $fs_file already mounted — skipped." + continue + fi + if [ ! -e "$fs_file" ]; then + log "Line $line_no: Creating mountpoint $fs_file" + run_root mkdir -p "$fs_file" || { err "Line $line_no: mkdir failed for $fs_file"; continue; } + fi + if echo "$fs_mntops" | grep -qw rbind; then + log "Line $line_no: rbind mount: $fs_spec -> $fs_file" + run_root mount --rbind "$fs_spec" "$fs_file" || true + else + log "Line $line_no: bind mount: $fs_spec -> $fs_file" + run_root mount --bind "$fs_spec" "$fs_file" || true + fi + continue + } + + # --- regular filesystem mounts --- + if is_mounted "$fs_file"; then + log "Line $line_no: $fs_file already mounted — skipped." + continue + fi + + if [ ! -e "$fs_file" ]; then + log "Line $line_no: Creating mountpoint $fs_file" + run_root mkdir -p "$fs_file" || { err "Line $line_no: mkdir failed"; continue; } + fi + + if [ "$fs_mntops" = "-" ]; then + log "Line $line_no: Mounting $fs_spec -> $fs_file (type=$fs_vfstype)" + run_root mount -t "$fs_vfstype" "$fs_spec" "$fs_file" || true + else + log "Line $line_no: Mounting $fs_spec -> $fs_file (type=$fs_vfstype, opts=$fs_mntops)" + run_root mount -t "$fs_vfstype" -o "$fs_mntops" "$fs_spec" "$fs_file" || true + fi +done < "$FSTAB_FILE" + +if [ $error_count -gt 0 ]; then + echo "Done: $error_count errors occurred." | tee -a "$LOG_FILE" + exit 1 +else + echo "Done: all entries processed successfully." | tee -a "$LOG_FILE" + exit 0 +fi + diff --git a/module.prop b/module.prop new file mode 100644 index 0000000..068ea02 --- /dev/null +++ b/module.prop @@ -0,0 +1,6 @@ +id=magisk-fstab +name=FSTAB loader +version=v1.0.0 +versionCode=1 +author=lionheart1810 +description=Loads custom fstab file and performs corresponding actions diff --git a/service.sh b/service.sh new file mode 100644 index 0000000..37f50dd --- /dev/null +++ b/service.sh @@ -0,0 +1,53 @@ +#!/system/bin/sh +# Do NOT assume where your module will be located. +# ALWAYS use $MODDIR if you need to know where this script and module is placed. +# This will make sure your module will still work if Magisk changes its mount point in the future +MODDIR=${0%/*} + +export LOG_FILE="${MODDIR}/magisk-fstab.log" +LOG_MAX_LINES=1000 + +BOOTWAIT_MAX_COUNT=20 +BOOTWAIT_COUNT_INTERVAL=15s + +CONF_MAIN="${MODDIR}/config/main.conf" +CONF_FSTAB="${MODDIR}/config/fstab.conf" + +# Check if configs are readable + +if [ ! -r "$CONF_MAIN" ]; then + echo "File '$CONF_MAIN' not readable." + exit 2 +fi + +if [ ! -r "$CONF_FSTAB" ]; then + echo "File '$CONF_FSTAB' not readable." + exit 2 +fi + +# Read main config +. "${CONF_MAIN}" >> "${LOG_FILE}" 2>&1 + +# wait for system boot to complete +bootwait_count=0 +until [[ $(getprop sys.boot_completed) || ${bootwait_count} -ge ${BOOTWAIT_MAX_COUNT} ]]; do + sleep ${BOOTWAIT_COUNT_INTERVAL} + bootwait_count=$((bootwait_count+1)) +done +if [ ${bootwait_count} -ge ${BOOTWAIT_MAX_COUNT} ]; then + exit 1 +fi + +# prevent log file from growing too large +tail -n "${LOG_MAX_LINES}" "${LOG_FILE}" > "${LOGFILE}.tmp" +mv "${LOGFILE}.tmp" "${LOG_FILE}" + +echo "=== $(date) ===" >> "${LOG_FILE}" 2>&1 + +if [ -f "${CONF}" ] + "${MODDIR}/fstab.sh" "${CONF_FSTAB}" & +else + echo "${CONF} not found." >> "${LOG_FILE}" 2>&1 +fi + +wait |