aboutsummaryrefslogtreecommitdiffstats
path: root/fstab.sh
diff options
context:
space:
mode:
authorLeonard Kugis <leonard@kug.is>2025-09-16 18:56:22 +0000
committerLeonard Kugis <leonard@kug.is>2025-09-16 18:56:22 +0000
commitd25d1ac9e53b3333944ef4e18795cf1354c179f0 (patch)
tree3c56294e1c7ed460a72ded4c7fbca0832897be68 /fstab.sh
downloadmagisk-fstab-d25d1ac9e53b3333944ef4e18795cf1354c179f0.tar.gz
Initial commit
Diffstat (limited to 'fstab.sh')
-rw-r--r--fstab.sh179
1 files changed, 179 insertions, 0 deletions
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
+