testsAndMisc/linux_configuration/scripts/mtk_root/00-preflight.sh
Krzysztof kuhy Rudnicki 1b43ba093c
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
mtk_root: build the rooting toolkit before the phone lands
Prep for a Ulefone Armor X12 Pro (MediaTek MT6765) that has not arrived,
validated read-only against the Pixel 6a that has.

Partition discovery never hardcodes boot vs init_boot. It reads
/dev/block/by-name/, derives the slot scheme from observed _a suffixes, and
picks the ramdisk carrier by preference order init_boot then boot over what is
actually present. The AOSP launch-version rule explains why that ordering is
right; the code consults the filesystem, not the API level. One path therefore
serves both a Pixel (no init_boot, falls to boot_a) and a 13-launch MediaTek
device (init_boot_a taken). Patching the wrong one yields a phone that will
not boot.

Classification refuses rather than guesses: ULEFONE needs a positive MediaTek
signal AND a vendor match, with no "not a Pixel therefore Ulefone" fallback.
Carrier lock is always reported UNDETERMINED, because sys.oem_unlock_allowed,
ro.oem_unlock_supported and settings global oem_unlock_allowed read identically
whether a device is carrier-locked or merely has the toggle off; asserting a
negative would wrongly rule out a path.

No flashing script, deliberately. Automating irreversible partition writes
defeats the confirmation steps that catch a wrong partition or missing slot
suffix.

Three bugs found by running the code rather than only linting it:
- SIGPIPE: `adb version | head -1` under pipefail exited 141 and killed
  00-preflight.sh mid-run
- 20-dump-stock.sh exited silently when no recon had run, because find on a
  nonexistent cache dir fails under set -e
- the read-only static scan had a hole: stripping all quoted strings to avoid
  false positives on printf text also hid `adb shell "reboot bootloader"`.
  Now only printf/echo/cat arguments are stripped, and a negative self-test
  asserts the scan fails on injected commands

Also narrowed a world-writable udev catch-all (idVendor=="*", MODE="0666")
that granted every local process read/write access to every USB device;
commented with a backup, adb connectivity re-verified afterwards.

Validated: 58/58 offline fixture tests; 00-preflight.sh HOST READY (12/12);
10-recon.sh read-only against the Pixel reports PIXEL, boot_a, A/B, no
init_boot, path C-or-D indeterminate, with the device unchanged afterwards.
Untested and labelled as such in READY.md: the init_boot branch on real
hardware, mtkclient over BROM, and the whole flash/verify sequence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ReVsw7qovYkkGbqcU9tn7
2026-08-04 20:38:44 +02:00

223 lines
6.6 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# 00-preflight.sh - Verify the host toolkit is intact. No device required.
#
# Run this first on the day the phone arrives, to confirm nothing rotted while
# it was in transit. Every check reports independently; the script exits
# non-zero if any FAIL, so it is usable as a gate.
#
# This checks the HOST only. It cannot confirm that udev actually grants
# access to a MediaTek device in BROM mode - that needs the hardware.
# ============================================================================
set -euo pipefail
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
readonly SCRIPT_DIR
readonly SCRIPT_NAME="${0##*/}"
# shellcheck source=../lib/mtk_common.sh
source "$SCRIPT_DIR/../lib/mtk_common.sh"
readonly UDEV_RULES_FILE="/etc/udev/rules.d/60-mtk-root.rules"
PASS=0
FAIL=0
WARN=0
pass() {
PASS=$((PASS + 1))
printf ' \033[0;32m[PASS]\033[0m %-34s %s\n' "$1" "${2:-}"
}
fail() {
FAIL=$((FAIL + 1))
printf ' \033[0;31m[FAIL]\033[0m %-34s %s\n' "$1" "${2:-}"
}
warn() {
WARN=$((WARN + 1))
printf ' \033[0;33m[WARN]\033[0m %-34s %s\n' "$1" "${2:-}"
}
usage() {
cat <<EOF
Usage: $SCRIPT_NAME [--help]
Verifies host tooling for the mtk_root toolkit. No device needed.
Exits non-zero if any check fails.
EOF
exit 0
}
check_tools() {
mtk_heading "Tools"
local -a required=(adb fastboot python3 git sha256sum)
local tool="" version=""
for tool in "${required[@]}"; do
if ! command -v "$tool" >/dev/null 2>&1; then
fail "$tool" "not installed - run install.sh"
continue
fi
# Invoke it, don't just locate it: a binary on PATH that cannot run is a
# failure mode `command -v` reports as success.
#
# Each capture is wrapped in `|| true` and reads the whole stream before
# slicing. Piping straight into `head -1` closes the pipe early, and under
# `set -o pipefail` the resulting SIGPIPE kills this script outright.
version=""
case "$tool" in
adb) version="$(adb version 2>/dev/null || true)" ;;
fastboot) version="$(fastboot --version 2>/dev/null || true)" ;;
python3) version="$(python3 -V 2>&1 || true)" ;;
git) version="$(git --version 2>/dev/null || true)" ;;
*) version="$("$tool" --version 2>/dev/null || true)" ;;
esac
version="${version%%$'\n'*}"
if [[ -z $version ]]; then
fail "$tool" "present but did not respond to a version query"
else
pass "$tool" "$version"
fi
done
}
check_groups() {
mtk_heading "Group membership"
local -a wanted=(plugdev adbusers)
local group="" active_groups=""
active_groups=" $(id -nG) "
for group in "${wanted[@]}"; do
if ! getent group "$group" >/dev/null 2>&1; then
warn "$group" "group does not exist on this system"
continue
fi
if [[ $active_groups == *" $group "* ]]; then
pass "$group" "active in this session"
elif id -nG "$USER" 2>/dev/null | grep -qw "$group"; then
# Membership that the current login session predates. The rule is real
# but will not take effect until re-login, and USB access will fail in
# a way that looks like a udev problem.
warn "$group" "member, but not in this session - log out and back in"
else
fail "$group" "not a member - run install.sh"
fi
done
}
check_udev() {
mtk_heading "udev rules"
if [[ -f $UDEV_RULES_FILE ]]; then
if command -v udevadm >/dev/null 2>&1 && udevadm verify --help >/dev/null 2>&1; then
# A real syntax verdict. `udevadm control --reload` exits 0 regardless,
# so it cannot be used to validate anything.
if udevadm verify "$UDEV_RULES_FILE" >/dev/null 2>&1; then
pass "60-mtk-root.rules" "present, udevadm verify clean"
else
fail "60-mtk-root.rules" "present but udevadm verify REJECTED it"
fi
else
warn "60-mtk-root.rules" "present; udevadm verify unavailable, syntax unchecked"
fi
else
fail "60-mtk-root.rules" "missing - run install.sh"
fi
# A wildcard-vendor world-writable rule is a genuine finding, not a nit: it
# applies to every USB device attached to this machine, not just phones.
local catchall=""
catchall="$(mtk_udev_catchall_files)"
if [[ -n $catchall ]]; then
fail "no world-writable catch-all" "found in: $(tr '\n' ' ' <<<"$catchall")"
printf ' A rule matching idVendor=="*" with MODE="0666" grants every\n'
printf ' local process read/write access to EVERY USB device, including\n'
printf ' storage and security keys. Narrow it: install.sh --fix-udev\n'
else
pass "no world-writable catch-all" "no wildcard 0666 rule found"
fi
}
check_venv() {
mtk_heading "mtkclient"
local venv_status=""
if venv_status="$(mtk_check_venv)"; then
pass "venv importable" "$venv_status"
else
fail "venv importable" "$venv_status"
printf ' The venv existing is not enough - it must be able to import\n'
printf ' usb/serial. A Python upgrade orphans a venv while leaving it\n'
printf ' looking complete. Rebuild: install.sh\n'
fi
local clone_dir=""
clone_dir="$(mtk_mtkclient_dir)"
if [[ -d "$clone_dir/.git" ]]; then
local rev=""
rev="$(git -C "$clone_dir" rev-parse --short HEAD 2>/dev/null || echo unknown)"
pass "mtkclient checkout" "rev ${rev}"
else
fail "mtkclient checkout" "not a git clone at ${clone_dir} - run install.sh"
fi
}
check_cache() {
mtk_heading "Workspace"
local cache_dir=""
cache_dir="$(mtk_cache_dir)"
if [[ ! -d $cache_dir ]]; then
fail "cache dir" "missing: ${cache_dir} - run install.sh"
return
fi
if [[ -w $cache_dir ]]; then
pass "cache dir writable" "$cache_dir"
else
fail "cache dir writable" "not writable: ${cache_dir}"
fi
}
main() {
printf '\033[1m%s - host readiness for the mtk_root toolkit\033[0m\n' "$SCRIPT_NAME"
check_tools
check_groups
check_udev
check_venv
check_cache
mtk_heading "Result"
printf ' %d passed, %d failed, %d warnings\n\n' "$PASS" "$FAIL" "$WARN"
if [[ $FAIL -gt 0 ]]; then
printf '\033[0;31mNOT READY\033[0m - resolve the failures above (usually: ./install.sh)\n'
return 1
fi
if [[ $WARN -gt 0 ]]; then
printf '\033[0;33mREADY, with warnings\033[0m - read them before trusting a USB connection.\n'
return 0
fi
printf '\033[0;32mHOST READY\033[0m - udev access to a real MediaTek device remains unverified\n'
printf 'until hardware is attached; that cannot be checked without the phone.\n'
return 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
-h | --help) usage ;;
*)
mtk_error "Unknown option: $1"
exit 1
;;
esac
done
main