#!/system/bin/sh
# fbind, a versatile mounting utility for Android
# Copyright (C) 2017-2021, VR25 @ xda-developers
# License: GPLv3+


appdata() {
  # appdata com.example.someapp
  mkfs_ext2() {
    local i=
    for i in /*/*bin/mkfs.ext2 /sbin/mkfs.ext2 mkfs.ext2; do
      if [ -x $i ] || which $i >/dev/null; then
        eval $i "$@" && break || echo "(i) Trying alternative: $i..."
      fi
    done
  }
  #dd if=/dev/zero of="$extsd/.fbind/appdata/${1}.img" bs=1024 count=
}


apply_config() {

  local i=

  [ -f $config ] || touch $config
  grep -iq '^permissive' $config && setenforce 0

  # set default extsd if not defined in config so loop/part can use it
  grep -q '^extsd_path ' $config || default_extsd

# storage paths fallbacks
  for i in $prefix /mnt/runtime/write /mnt/user/0; do
    is_mounted $i/emulated && prefix=$i && break || prefix=
  done
  [ -n "$prefix" ] && intsd=$prefix/emulated/0 || intsd=/data/media/0
  ! is_mounted $extsd && extsd=$intsd || mount -o remount,mask=0 $extsd 2>/dev/null
  obb=$intsd/Android/obb
  extobb=$extsd/Android/obb

  grep -E '^extsd_path |^intsd_path |^part |^loop ' $config > $tmpf
  . $tmpf
  rm $tmpf
}


bind_mount() {
  if ! is_mounted "$2"; then
    if $interactiveMode; then
      echo
      echo "<$1> <$2>"
    else
      wait_storage "$@" || return 1
    fi
    mkdir -p "$1" "$2"
    if which bindfs >/dev/null; then
      su -Mc bindfs -o nosuid,nodev,noexec,noatime,nonempty -u 0 -g 9997 -p a-rwx,ug+rw,ugo+X \
        --create-with-perms=a-rwx,ug+rw,ugo+X --xattr-none --chown-ignore \
        --chgrp-ignore --chmod-ignore \""$1"\" \""$2"\" || {
          echo "(i) Trying \"mount -o bind\"..."
          mount -o bind \""$1"\" \""$2"\"
        }
    else
      mount -o bind \""$1"\" \""$2"\"
    fi
    if is_mounted "$2"; then
      [ -z "$3" ] && rm -rf "$1/Android" 2>/dev/null
    else
      rmdir "$2" 2>/dev/null
    fi
  fi
}


bind_mount_wrapper() {
  $interactiveMode && echo "Bind-mounting..."

  # $extobb <--> $obb
  obb() { bind_mount $extobb $obb; }

  # $extobb/$1 <--> $obb/$1
  obbf() { bind_mount $extobb/$1 $obb/$1; }

  # $extsd/<path> <--> $intsd/<same path>
  target() { bind_mount "$extsd/$1" "$intsd/$1" $2; }

  # $extsd/<path> <--> $intsd/<path>
  from_to() { bind_mount "$extsd/$2" "$intsd/$1" $3; }

  # $extsd/$1 <--> $intsd
  int_extf() {
    if [ -z "$1" ]; then
      bind_mount $extsd/.fbind $intsd
    else
      bind_mount "$extsd/$1" $intsd
    fi
    target Android
    target data
    obb
  }

  if [ -n "$1" ]; then
    grep -E '^int_extf|^bind_mount |^obb|^from_to |^target ' $config | grep -E "$1" >$tmpf
  else
    grep -E '^int_extf|^bind_mount |^obb|^from_to |^target ' $config >$tmpf
  fi
  . $tmpf
  rm $tmpf
  if $interactiveMode; then
    echo
    echo "- Done"
  fi
}


# fallback sdcard path
default_extsd() {
  local df=
  local dir=
  local size=0
  local newSize=0
  $interactiveMode || wait_until_true grep -q /mnt/media_rw/ /proc/mounts
  if grep -q /mnt/media_rw/ /proc/mounts; then
    for dir in /mnt/media_rw/* /mnt/media_rw/.*; do
      if is_mounted $dir; then
        df="$(df $dir | tail -n1)"
        case "$df" in
          /*) newSize=$(echo "$df" | awk '{print $2}');;
          *) newSize=$(echo "$df" | awk '{print $1}');;
        esac
        if [ $newSize -gt $size ]; then
          size=$newSize
          extsd=$dir
        fi
      fi
    done
  fi
}


edit() {
  local f=$1
  shift
  if [ -n "$1" ]; then
    "$@" $f
  else
    vim $f 2>/dev/null || vi $f 2>/dev/null || nano $f
  fi
}


exit_wizard() {
  local ans=no
  echo
  if $modified && ! $alreadyMoved; then
    printf "(?) Move data & bind folders now (y/N)? "
    read ans
    if echo $ans | grep -iq y; then
      fbind -Mm
    fi
  fi
  rm $tmpf 2>/dev/null
  exit 0
}


# alternate extsd path
extsd_path() {
  extsd="$1"
  extobb="$extsd/Android/obb"
  wait_until_true is_mounted "$1"
}


exxit() {
  local exitCode=$?
  echo
  rm $dataDir/.FUSE.tmp 2>/dev/null
  exit $exitCode
}


fbind() {
  /system/bin/sh "$0" "$@"
}


force_fuse() {
  if [ -f $modDir/system.prop ]; then
    mv $modDir/system.prop $modDir/FUSE.prop
    rm $dataDir/.FUSE* 2>/dev/null
    echo "(i) Force FUSE: no"
  else
    mv $modDir/FUSE.prop $modDir/system.prop
    touch $dataDir/.FUSE
    echo "(i) Force FUSE: yes"
  fi
  echo "- Change takes effect after rebooting."
}


# set alternate intsd path
intsd_path() {
  intsd="$1"
  obb="$intsd/Android/obb"
}


is_mounted() { mountpoint -q "$1" 2>/dev/null; }


# mount loop device (loop [-o mount_opts] <.img file> <mount point>)
loop() {
  _losetup() {
    local i=
    for i in /*/*bin/losetup /sbin/losetup losetup; do
      if [ -x $i ] || which $i >/dev/null; then
        eval $i "$@" && break || echo "(i) Trying alternative: $i..."
      fi
    done
  }
  if echo "$1 $2" | grep -q '^\-o '; then # using extra mount options
    local extraOpts="$1 $2"
    shift 2
  fi
  local fPath=${1%--L*}
  local luksPass="${1#*--L,}"
  [ "$luksPass" = "$1" ] && luksPass=
  local pName=$(echo ${1##*/} | sed 's/--L.*//')
  [ -z "$2" ] && return 1

  if ! is_mounted "$2"; then
    wait_storage "$@"
    [ $? -ne 0 ] && return 1
    wait_until_true test -e $fPath || return 1
    local pPath=$(_losetup -fs "$fPath")
    [ -e "$pPath" ] || return 1
    if echo "$1" | grep -q '\-\-L'; then
      if $interactiveMode || [ -n "$luksPass" ]; then
        # open LUKS volume
        if [ -n "$luksPass" ]; then
          printf "$luksPass" | cryptsetup luksOpen $pPath $pName
        else
          cryptsetup luksOpen $pPath $pName
        fi
        pPath="/dev/mapper/$pName"
      fi
    fi
    [ -e "$pPath" ] && mount_dev "$pPath" "$2" "$extraOpts"
  fi
}


mount() {
  su -Mc /system/bin/mount -o rw,noatime "$@"
}


mount_dev() {

  local extraOpts="$3"
  local context="u:object_r:sdcardfs:s0"

  grep -iq '/storage/emulated fuse' /proc/mounts && context="u:object_r:fuse:s0"
  extraOpts="$extraOpts -o nosuid,nodev,noexec,context=$context"
  mkdir -p "$2"

  # detecting file system
  fsType=$(blkid "$1" | sed 's/.*TYPE=\"\(.*\)\".*/\1/')

  # user permissions of FAT
  #echo "$fsType" | grep -q "fat$" && extraOpts="$extraOpts -o uid=0,gid=9997,umask=0007"
  echo "$fsType" | grep -q "fat$" && extraOpts="$extraOpts -o uid=0,gid=9997,fmask=0117,dmask=0006"
  test -n "$fsType" && extraOpts="-t $fsType $extraOpts"

  # checking ext partition
  echo "$fsType" | grep -q "^ext[2-4]" && e2fsck -fy "$1"

  # mounting:
  if echo "$2" | grep -qE "^$intsd"; then
    local tPath="/mnt/${2##*/}.fbind"
    mkdir -p $tPath
    mount $extraOpts "$1" "$tPath"
    bind_mount "$tPath" "$2"
  else
    mount $extraOpts "$1" "$2"
  fi

  # user permissions for ext[2-4]
  echo "$fsType" | grep -q "^ext[2-4]" && chown 0:9997 "$2" && chmod 0775 "$2"

  is_mounted "$2" || rmdir "$2" 2>/dev/null
}


# move data
mv_data() {
  if ! is_mounted "$1" && [ -n "$(ls -Ad "$1" 2>/dev/null)" ]; then
    echo
    echo "$1 --> $2"
    #rm -rf "$2" 2>/dev/null
    mv "$2" "$2".old 2>/dev/null
    if cp -R "$@"; then
      rm -rf "$1" 2>/dev/null
      return 0
    else
      echo "(!) Copy failed"
      echo "- Source data left intact"
      return 1
    fi
  fi
}


mv_data_wrapper() {
  echo "Moving..."
  obb() { mv_data $obb $extobb; }
  bind_mount() { mv_data "$2" "$1"; }
  obbf() { mv_data $obb/$1 $extobb/$1; }
  target() { mv_data "$intsd/$1" "$extsd/$1"; }
  from_to() { mv_data "$intsd/$1" "$extsd/$2"; }

  # $intsd --> $extsd/$1
  int_extf() {
    if [ -n "$1" ]; then
      mv_data $intsd "$extsd/$1"
    else
      mv_data $intsd $extsd/.fbind
    fi
  }

  if [ -n "$1" ]; then
    grep -E '^int_extf|^bind_mount |^obb|^from_to |^target ' \
      $config 2>/dev/null | grep -E "$1" > $tmpf 2>/dev/null
  else
    grep -E '^int_extf|^bind_mount |^obb|^from_to |^target ' \
      $config > $tmpf 2>/dev/null
  fi

  . $tmpf
  rm $tmpf
  echo
  echo "- Done"
}


obb_to_external() {
  local option=
  echo
  printf "a) all\ns)specific\n\n#? "
  read -n 1 option
  case $option in
    all)
      if ! grep -Eq '^obb$|^obb ' $config; then
        echo obb >> $config
        modified=true
        echo
        echo Done
        printf "(?) Move data & bind folders now (y/N)? "
        read ans
        if echo $ans | grep -iq y; then
          fbind -M
          fbind -m
          alreadyMoved=true
        fi
      fi
    ;;
    *)
      to_external obbf com.madgames.greatgame
    ;;
  esac
}


# partition mounter
part() {
  if echo "$1 $2" | grep -q '^\-o '; then # using extra mount options
    local extraOpts="$1 $2"
    shift 2
  fi
  local pPath=${1%--L*}
  local luksPass="${1#*--L,}"
  [ "$luksPass" = "$1" ] && luksPass=
  local pName=$(echo ${1##*/} | sed 's/--L.*//')
  [ -z "$2" ] && return 1

  if ! is_mounted "$2"; then
    wait_storage "$@" && wait_until_true test -e $pPath || return 1
    if echo "$1" | grep -q '\-\-L'; then
      if $interactiveMode || [ -n "$luksPass" ]; then
        # open LUKS volume
        if [ -n "$luksPass" ]; then
          printf "$luksPass" | cryptsetup luksOpen $pPath $pName
        else
          cryptsetup luksOpen $pPath $pName
        fi
        pPath="/dev/mapper/$pName"
      fi
    fi
    if [ -e "$pPath" ]; then
      [ -n "$3" ] && $3 $pPath
      mount_dev "$pPath" "$2" "$extraOpts"
    fi
  fi
}


print_header() {
  echo "fbind $(sed -n 's/^version=//p' $modDir/module.prop)
Copyright (C) 2017-2021, VR25 @ xda-developers
License: GPLv3+"
}


# remove stubborn files/folders
remove_wrapper() {
  remove() {
    if [ -e "$intsd/$1" ] || [ -e "$extsd/$1" ]; then
      $interactiveMode && echo "- $1"
      rm -rf "$intsd/$1" "$extsd/$1" 2>/dev/null
    fi
  }
  [ -n "$1" ] && echo "remove \"$1\"" >$tmpf
  grep '^remove ' $config >>$tmpf
  . $tmpf
  rm $tmpf
}


test_prefixes() {

  local a=
  local i=

  set -- $(/system/bin/mount | grep '/emulated type' | awk '{print $3}' \
    | sed 's/\/emulated.*//' | grep -Ev '/mnt/androidwritable/0|/mnt/runtime/default')

  _exxit() {
    { unmount $intsd/test-dir-i
    rm -rf $extsd/test-dir-e; } >/dev/null 2>&1
    exxit
  }

  trap _exxit EXIT

  for i in $@; do
    echo "(i) Testing $i"
    unmount $intsd/test-dir-i >/dev/null 2>&1
    intsd=$i/emulated/0
    bind_mount $extsd/test-dir-e $intsd/test-dir-i >/dev/null 2>&1 || continue
    sleep 2
    is_mounted /storage/emulated/0/test-dir-i || continue
    echo "(?) Can you write to /storage/emulated/0/test-dir-i/ WITHOUT root? (y/N)"
    read a
    case ".$a" in
      .[yY]*) :;;
      *) continue;;
    esac
    sed -i '/^prefix=/d' $config
    sed -i "1iprefix=$i" $config
    echo "(i) Added to config: prefix=$i"
    break
  done
}


to_external() {

  local ans=no
  local line=
  local target=

  reset
  echo
  echo "(i) Type targets, one at a time (e.g., $([ -n "$2" ] \
    && echo "$2" || echo "DCIM/Camera") <enter>). Press <enter> again when done."
  echo

  while read target; do
    if [ -n "$target" ]; then
      if [ -n "$line" ]; then
        if [ -n "$1" ]; then
          cat $config | tr -d \" | tr -d \' | grep "$1 $target" >/dev/null || line="$(printf "$line\n$1 \"$target\"")"
        else
          cat $config | tr -d \" | tr -d \' | grep "from_to $target" >/dev/null || line="$(printf "$line\nfrom_to \"$target\" \".fbind/$target\"")"
        fi
      else
        if [ -n "$1" ]; then
          cat $config | tr -d \" | tr -d \' | grep "$1 $target" >/dev/null || line="$1 \"$target\""
        else
          cat $config | tr -d \" | tr -d \' | grep "from_to $target" >/dev/null || line="from_to \"$target\" \".fbind/$target\""
        fi
      fi
    else
      echo "$line" > $tmpf
      break
    fi
  done

  if grep -q .. $tmpf; then
    echo "Generated config:"
    cat $tmpf | tee -a $config
    modified=true
    rm $tmpf
    echo
    printf "(?) Move data & bind-mount now (y/N)? "
    read ans
    if echo $ans | grep -iq y; then
      fbind -Mm
      alreadyMoved=true
    fi
  fi
}


toggle_auto_mount() {
  printf "(i) Auto-mount "
  if grep -q noAutoMount $config; then
    sed -i /noAutoMount/d $config
    echo enabled
  else
    echo noAutoMount >> $config
    echo disabled
  fi
}


troubleshooting() {
  echo
  sed -En "/## NOTES/,/---/p" $dataDir/README.md \
    | grep -Ev '^---|^$|## ' | sed 's/^- /\n- /' | less
}


unmount() {
  local line=
  local pattern="$(echo "$1" | sed "s|$prefix||")"
  /system/bin/mount | grep "$pattern type " >/dev/null 2>&1 && {
    echo "<...${pattern#/}>"
    /system/bin/mount | grep "$pattern type " | sed -E -e 's|^.* on /|/|' -e "s|($pattern) type .*|\1|" | \
      while IFS= read line; do
        umount -f "$line" 2>/dev/null
      done
    # reverse order
    /system/bin/mount | grep "$pattern type " | tac | sed -E -e 's|^.* on /|/|' -e "s|($pattern) type .*|\1|" | \
      while IFS= read line; do
        umount -f "$line" 2>/dev/null
      done
    rmdir "$1" 2>/dev/null
  }
}


unmount_wrapper() {

  obb() { unmount $obb; }
  bind_mount() { unmount "$2"; }
  obbf() { unmount $obb/$1; }
  target() { unmount "$intsd/$1"; }
  from_to() { unmount "$intsd/$1"; }

  int_extf() {
    unmount $obb
    unmount $intsd/Android
    unmount $intsd/data
    unmount $intsd
  }

  if [ -n "$1" ]; then
    loop() { unmount "$2"; }

    part() {
      echo "$1 $2" | grep -q '^\-o ' && shift 2
      unmount "$2"
      echo "$1" | grep -q '\-\-L' \
        && cryptsetup luksClose $(echo ${1##*/} | sed 's/--L.*//')
    }

    grep -E '^int_extf|^bind_mount |^obb|^from_to |^target |^loop |^part ' $config \
      | grep -E "$1" 2>/dev/null > $tmpf

  else
    grep -E '^int_extf|^bind_mount |^obb|^from_to |^target ' $config > $tmpf
  fi

  echo "Unmounting..."
  echo
  . $tmpf
  rm $tmpf
  echo
  echo "- Done"
}


usage() {
  cd $TMPDIR
  cat <<EOF | less
$([ -n "$1" ] && echo "All commands" || print_header)

Usage:
  fbind (wizard)
  fbind [option...] [argument...]

-a|--auto-mount
Toggle auto-mount on boot (default: enabled).

-b|--bind-mount <target> <mount point>
Bind-mount folders not listed in config.txt.
SDcarsFS read and write runtime paths are handled automatically.
Missing directories are created accordingly.
e.g., fbind -b /data/someFolder /data/mountHere

-c|--config [editor] [option...]
Open config.txt w/ [editor] [option...] (default: vim|vi|nano).
e.g., fbind -c nano -l

-C|--cryptsetup [option...] [argument...]
Run cryptsetup [option...] [argument...].

-f|--fuse
Toggle FUSE usage for emulated storage (default: off).

-h|--help
List all commands.

-l|--log [editor] [option...]
Open service.log w/ [editor] [option...] (default: more|vim|vi|nano).
e.g., fbind -l

-m|--mount [egrep regex]
Bind-mount matched or all (no arg).
e.g., fbind -m "Whats|Downl|part"

-M|--move [ext. regex]
Move matched or all (no args) to external storage.
Only unmounted folders are affected.
e.g., fbind -M "Download|obb"

-Mm [egrep regex]
Same as "fbind -M [arg] && fbind -m [arg]"
e.g., fbind -Mm

-r|--readme
Open README.md w/ [editor] [option...] (default: more|vim|vi|nano).

-R|--remove [target]
Remove stubborn/unwanted file/folder from \$intsd and \$extsd.
By default, all "remove" lines from config are included.
e.g., fbind -R Android/data/com.facebook.orca

-t|--test-prefixes
Test internal storage prefixes (views) and automatically update the config.

-u|--unmount [mount point or egrep regex]
Unmount matched or all (no arg).
This works for regular bind-mounts, SDcardFS bind-mounts, regular partitions, loop devices and LUKS/LUKS2 encrypted volumes.
Unmounting "all at once" (no arg) does not affect partitions nor loop devices.
These must be unmounted with a regex argument.
For unmounting folders bound with the --bind-mount option, the mount points must be supplied, since those are not in config.txt.
e.g., fbind -u "loop|part|Downl"

-um|--remount [egrep regex]
Remount matched or all (no arg).
e.g., fbind -um "Download|obb"

-v|--version
Print version code (integer)
EOF
}


wait_storage() {
  if echo "$@" | grep -Eq "/data/media/|/storage/emulated/|$prefix/"; then
    wait_until_true || return 1
  fi
  if echo "$@" | grep -q /mnt/media_rw/; then
    wait_until_true grep -q /mnt/media_rw/ /proc/mounts || return 1
  fi
}


wait_until_true() {
  local _=
  ([ -n "$1" ] || set -- grep -Eq "' /storage/emulated (fuse|sdcardfs) '" /proc/mounts
  for _ in $(seq 100); do
    eval "$@" && break || {
      $interactiveMode && return 1
      sleep 3
    }
    set +x
  done)
}


wizard() {

  local target=
  [ -z "$modified" ] && modified=false
  [ -z "$alreadyMoved" ] && alreadyMoved=false
  [ -n "$1" ] && echo || reset
  echo
  [ -n "$1" ] && echo "Main Menu" || print_header
  echo

  printf "a) All commands
b) Boot logs
c) Terminal logs
d) Documentation
m) Media to external
n) Non-media to external
o) OBB to external
t) Troubleshooting
e) Exit wizard
\n#? "

  read -n 1 target

  case $target in
    a) printf "\n$(usage noIntro)\n" | less;;
    b) edit $dataDir/logs/service.log less;;
    c) edit $dataDir/logs/terminal.log less;;
    d) edit $dataDir/README.md less;;
    m) to_external;;
    n) to_external target "Android/data <enter>, TitaniumBackup";;
    o) obb_to_external;;
    t) troubleshooting;;
    e) exit_wizard;;
  esac

  wizard noReset
}


# Prepare environment

echo

if touch /dev/.root_test 2>/dev/null; then
  rm /dev/.root_test
else
  echo "(!) Must run as root (su)"
  exit 1
fi

modDir=/data/adb/modules/vr25.fbind
[ -f $modDir/update ] && {
  modDir=${modDir%/*}_update/vr25.fbind
  exec=$modDir/system/bin/fbind
  [ $0 = $exec ] || exec $exec "$@"
}

busyboxDir=/dev/.vr25/busybox

if [ ! -x $busyboxDir/ls ]; then
  mkdir -p $busyboxDir
  /data/adb/magisk/busybox --install -s $busyboxDir/
fi

case $PATH in
  /data/adb/vr25/bin:*) :;;
  *) export PATH=/data/adb/vr25/bin:$busyboxDir:$modDir/bin:$PATH;;
esac

service=false
TMPDIR=/dev/.vr25/fbind
tmpf=$TMPDIR/tmpf
interactiveMode=true
dataDir=/data/adb/vr25/fbind-data
config=$dataDir/config.txt
: ${prefix:=$(sed -n 's/^prefix=//p' $config 2>/dev/null)}
: ${prefix:=/mnt/runtime/write}
intsd=$prefix/emulated/0
obb=$intsd/Android/obb

case "$1" in
  -s|--service) service=true;;
esac

mkdir -p $dataDir/logs $TMPDIR
trap exxit EXIT

if ! $service; then
  log=$dataDir/logs/terminal.log
  [ -z "${LINENO-}" ] || export PS4='$LINENO: '
  date > $log
  echo "versionCode=$(sed -n s/versionCode=//p $modDir/module.prop)" >> $log
  set -x 2>> $log
fi

# FUSE-related bootloop fix
if [ -f $modDir/system.prop ]; then
  if [ -f $dataDir/.FUSE.tmp ]; then
    force_fuse > /dev/null
    am start -a android.intent.action.REBOOT < /dev/null > /dev/null 2>&1 \
      || reboot 2>/dev/null \
        || /system/bin/reboot
  else
    touch $dataDir/.FUSE.tmp
  fi
else
  rm $dataDir/.FUSE 2>/dev/null
fi

$service || apply_config # & handle LUKS/2


case $1 in

  -a|--auto-mount) toggle_auto_mount;;
  -b|--bind-mount) bind_mount "$2" "$3" > /dev/null;;
  -c|--config) shift; edit $config "$@";;
  -C|--cryptsetup) shift; cryptsetup "$@";;
  -f|--fuse) force_fuse;;
  -h|--help) usage;;
  -l|--log) shift; edit $dataDir/logs/service.log "${@:-more}";;
  -m|--mount) bind_mount_wrapper "$2";;
  -M|--move) mv_data_wrapper "$2";;
  -Mm) fbind -M "$2"; fbind -m "$2";;
  -r|--readme) shift; edit $dataDir/README.md "${@:-more}";;
  -R|--remove) shift; remove_wrapper "$@";;

  -s|--service)

    # wait until data is decrypted
    (set +x
    for _ in $(seq 100); do
      [ -d /storage/emulated/0/Documents ] && break || sleep 3
    done)

    wait_until_true

    while :; do
      interactiveMode=false
      log=$dataDir/logs/service.log
      [ -z "${LINENO-}" ] || export PS4='$LINENO: '
      exec > $log 2>&1
      date
      echo "versionCode=$(sed -n s/versionCode=//p $modDir/module.prop)"
      set -x
      grep -iq noAutoMount $config && exit 0
      apply_config # and mount partitions & loop devices
      grep -Eq '^int_extf|^bind_mount |^obb.*|^from_to |^target ' $config && bind_mount_wrapper
      grep -q '^remove ' $config && remove_wrapper
      break # wip
    done
  ;;

  -t|--test-prefixes) test_prefixes;;
  -u|--unmount) unmount_wrapper "$2";;
  -um|--remount) fbind -u "$2"; fbind -m "$2";;
  -v|--version) sed -n 's/versionCode=//p' $modDir/module.prop;;
  *) wizard;;
esac

exit 0
