#!/bin/sh -u # fp6-vendor-blobs-extract - copy proprietary blobs out of the stock Android # partitions instead of distributing them. pmOS installs flash only # boot+userdata, so every installed FP6 still carries the stock vendor/dsp # partitions: the device duplicates files it already contains, for its own # operation - nothing is distributed by us or anyone else. Design, legal # frame and on-phone verification: fp6 repo journal/blobs/. # # Manifest fragments: /usr/share/fp6-vendor-blobs/manifest.d/*.manifest, # processed in sorted order; '#' comments and blank lines ignored: # # file # rebind # # file: mount the first available listed partition READ-ONLY (ext4 also # gets -o noload - never a byte written to the stock partitions, not # even a journal replay), copy to , verify the # sha256. A missing source or a hash mismatch tries the next listed # partition; no verified copy on any of them fails the run loudly - an # unverified blob is never installed and a missing one never silently # skipped. Dests that already exist are left alone (no hashing: a # deliberately replaced file stays). # rebind: if this fragment's run extracted at least one file, unbind and # re-probe on so the consuming driver picks the file up # in the same boot. Unconditional on purpose: a still-bound consumer may # have already failed a deferred firmware request that is never retried # (aw88261 binds on i2c probe but requests the ACF only at card init), # so only a fresh probe with the file present is a known-good state. # All of a fragment's devices are unbound first, then re-probed, so a # shared sound card re-forms once instead of bouncing per device. # # Partitions resolve via /dev/mapper (the pmOS initramfs maps the dynamic # partitions in super on every boot), then /dev/disk/by-partlabel (raw # partitions like dsp_a), then one make-dynpart-mappings fallback run; # mappings that run creates are removed again at the end. # # --if-device: exit 0 quietly when no stock super partition is visible # (apk post-install scripts run inside build/CI chroots too; on images # built there the first-boot service does the real extraction). MANIFEST_DIR=/usr/share/fp6-vendor-blobs/manifest.d SUPER=/dev/disk/by-partlabel/super MNT= MNT_PART= CREATED= TRIED_MAPPING= log() { echo "fp6-vendor-blobs: $*"; } unmount_cur() { if [ -n "$MNT" ]; then umount "$MNT" 2>/dev/null rmdir "$MNT" 2>/dev/null MNT= MNT_PART= fi } cleanup() { unmount_cur for name in $CREATED; do dmsetup remove "$name" 2>/dev/null || true done CREATED= } fail() { echo "fp6-vendor-blobs: ERROR: $*" >&2 cleanup exit 1 } # resolve a partition name to a block device part_dev() { [ -b "/dev/mapper/$1" ] && { echo "/dev/mapper/$1"; return 0; } [ -b "/dev/disk/by-partlabel/$1" ] && { echo "/dev/disk/by-partlabel/$1"; return 0; } if [ -z "$TRIED_MAPPING" ] && [ -b "$SUPER" ]; then TRIED_MAPPING=1 before=$(dmsetup ls 2>/dev/null | awk '{print $1}') make-dynpart-mappings "$SUPER" 0 2>/dev/null after=$(dmsetup ls 2>/dev/null | awk '{print $1}') for name in $after; do case " $before " in *" $name "*) ;; *) CREATED="$CREATED $name" ;; esac done [ -n "$CREATED" ] && log "mapped dynamic partitions:$CREATED" [ -b "/dev/mapper/$1" ] && { echo "/dev/mapper/$1"; return 0; } fi return 1 } mount_part() { [ "$MNT_PART" = "$1" ] && return 0 unmount_cur dev=$(part_dev "$1") || return 1 dir=$(mktemp -d /run/fp6-vendor-blobs.XXXXXX) || fail "mktemp failed" # noload succeeds on any ext4 and correctly fails on non-ext4, where # plain ro cannot write anyway (erofs); a dirty ext4 is never replayed if ! mount -o ro,noload "$dev" "$dir" 2>/dev/null && \ ! mount -o ro "$dev" "$dir" 2>/dev/null; then rmdir "$dir" 2>/dev/null return 1 fi MNT=$dir MNT_PART=$1 } extract() { # parts=$1 src=$2 dest=$3 want=$4 for part in $(echo "$parts" | tr ',' ' '); do mount_part "$part" || { log "$part: not mountable, trying next"; continue; } [ -f "$MNT/$src" ] || { log "$part: no $src, trying next"; continue; } tmp="$dest.fp6-extract.$$" mkdir -p "${dest%/*}" || fail "cannot create ${dest%/*}" cp "$MNT/$src" "$tmp" || { rm -f "$tmp"; fail "copying $part:$src failed"; } got=$(sha256sum "$tmp" | awk '{print $1}') if [ "$got" != "$want" ]; then rm -f "$tmp" log "$part:$src sha256 $got != expected, trying next" continue fi chmod 644 "$tmp" && mv "$tmp" "$dest" || { rm -f "$tmp"; fail "installing $dest failed"; } log "extracted $part:$src -> $dest" return 0 done fail "no listed partition ($parts) yields $src with sha256 $want - $dest NOT installed" } rebind_all() { # for rb in $1; do bus=${rb%%/*} dev=${rb#*/} [ -e "/sys/bus/$bus/devices/$dev/driver" ] || continue if echo "$dev" > "/sys/bus/$bus/devices/$dev/driver/unbind" 2>/dev/null; then log "unbound $bus $dev" fi done for rb in $1; do bus=${rb%%/*} dev=${rb#*/} if [ ! -e "/sys/bus/$bus/devices/$dev" ]; then log "rebind: no $dev on bus $bus (yet) - its driver will probe on its own" elif echo "$dev" > "/sys/bus/$bus/drivers_probe" 2>/dev/null; then log "re-probed $bus $dev" else log "rebind: drivers_probe of $dev failed (driver not loaded yet?)" fi done } if [ "${1:-}" = --if-device ] && [ ! -b "$SUPER" ]; then log "no stock super partition visible (build chroot?), nothing to do" exit 0 fi [ -d "$MANIFEST_DIR" ] || exit 0 # fast path for every boot after the first: all dests already present missing= for f in "$MANIFEST_DIR"/*.manifest; do [ -e "$f" ] || continue while read -r kind _ _ dest _; do [ "$kind" = file ] && [ ! -e "$dest" ] && missing=1 done < "$f" done [ -z "$missing" ] && exit 0 for f in "$MANIFEST_DIR"/*.manifest; do [ -e "$f" ] || continue extracted= rebinds= while read -r kind a b c d; do case "$kind" in ''|'#'*) ;; file) [ -n "$d" ] || fail "$f: malformed file line" [ -e "$c" ] && continue extract "$a" "$b" "$c" "$d"