#!/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