adacam/scripts/build/build-v5-patched-updater.sh
Kayos ed7ae5ba57 docs+scripts: project status, build scripts, patched usb-updater v5
- PROJECT_STATUS.md: full project log (hardware, partitions, artifacts, lessons learned, next steps)
- scripts/build/build-artifact-from-existing.py: rebuild artifact from existing data tar with new header
- scripts/build/build-v5-patched-updater.sh: patch system.img usb-updater + build artifact
- recovery/usb-updater-v5-patched: patched usb-updater with SSH recovery prepended

adacam-ssh-fix-v5.mender: 403MB, SHA256 acfbd16db9620f23785f8b103ffaeff6aed780f383273a61a23c8002f2bf0980
Status: PENDING TEST on replacement Bee (192.168.0.10)
2026-03-16 09:58:45 -07:00

157 lines
4.9 KiB
Bash

#!/bin/bash
set -e
WORKSPACE=/mnt/cache/appdata/openclaw/config/workspace
FIRMWARE_DIR=$WORKSPACE/projects/adacam/firmware/extracted/mender/data_extracted
ORIG_SYSTEM=$FIRMWARE_DIR/system.img
ORIG_SYSHASH=$FIRMWARE_DIR/syshash.img
ORIG_BOOT=$FIRMWARE_DIR/boot.img
WORK_DIR=/tmp/bee_v5_work
OUT_DIR=$WORKSPACE/projects/adacam/recovery
OUT=$OUT_DIR/adacam-ssh-fix-v5.mender
mkdir -p $WORK_DIR
# Copy system.img to work dir for modification
echo "Copying system.img..."
cp $ORIG_SYSTEM $WORK_DIR/system.img
# Mount the image
echo "Mounting system.img..."
mkdir -p $WORK_DIR/mnt
LOOP=$(losetup --find --show $WORK_DIR/system.img)
mount -t ext4 $LOOP $WORK_DIR/mnt
# Read the existing usb-updater
echo "Patching usb-updater..."
cat $WORK_DIR/mnt/usr/bin/usb-updater | head -2
# Build patched version
SSH_FIX='#!/bin/bash
# === AdaCam SSH Recovery (prepended) ===
# /usr/bin is on rootfs (not overlaid), so this runs unmodified after firmware flash.
# Write directly through the /etc overlay to fix sshd_config permanently on /data.
mkdir -p /home/root/.ssh
cat > /etc/ssh/sshd_config << '"'"'SSHEOF'"'"'
PermitRootLogin yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication yes
PermitEmptyPasswords yes
ChallengeResponseAuthentication no
UsePAM no
X11Forwarding yes
Compression no
ClientAliveInterval 15
ClientAliveCountMax 4
Subsystem sftp /usr/libexec/sftp-server
ListenAddress 0.0.0.0
SSHEOF
cat > /home/root/.ssh/authorized_keys << '"'"'KEYS'"'"'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK87jxvlXvo60pxwdtyJsXeFsb4KsAiFx4FnyXz81kh7 cobb@adacam
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOQxwJU91TCxds34P18D3xRbu7rxlrgTUoml/H8nxeDK kayos@openclaw
KEYS
chmod 700 /home/root/.ssh
chmod 600 /home/root/.ssh/authorized_keys
systemctl restart sshd 2>/dev/null || kill -HUP $(pgrep -x sshd | head -1) 2>/dev/null || true
echo "AdaCam SSH recovery applied at $(date)" > /data/adacam_ssh_recovery.log 2>/dev/null || true
# === End AdaCam SSH Recovery ===
'
# Get everything after the first line (#!/bin/bash) of original
ORIG_BODY=$(tail -n +2 $WORK_DIR/mnt/usr/bin/usb-updater)
# Write patched file
echo "$SSH_FIX" > $WORK_DIR/usb-updater-patched
echo "$ORIG_BODY" >> $WORK_DIR/usb-updater-patched
chmod 755 $WORK_DIR/usb-updater-patched
cp $WORK_DIR/usb-updater-patched $WORK_DIR/mnt/usr/bin/usb-updater
echo "Patched usb-updater head:"
head -5 $WORK_DIR/mnt/usr/bin/usb-updater
# Unmount
echo "Unmounting..."
umount $WORK_DIR/mnt
losetup -d $LOOP
# Compute new syshash
echo "Computing new syshash..."
SALT=3f3e0633b0a2cbf5066cd50af1a178ff8f50f660382f6c7508f58566cec64142
veritysetup format \
--hash=sha384 \
--salt=$SALT \
--data-block-size=4096 \
--hash-block-size=4096 \
$WORK_DIR/system.img $WORK_DIR/syshash.img 2>&1 | tail -5
echo "New syshash computed."
# Build mender artifact
echo "Building mender artifact..."
python3 - << PYEOF
import tarfile, hashlib, io, os
SYSTEM = '$WORK_DIR/system.img'
SYSHASH = '$WORK_DIR/syshash.img'
BOOT = '$ORIG_BOOT'
OUT = '$OUT'
def sha256b(b):
return hashlib.sha256(b).hexdigest()
def sha256f(p):
h = hashlib.sha256()
with open(p,'rb') as f:
while True:
c = f.read(1024*1024)
if not c: break
h.update(c)
return h.hexdigest()
version_data = b'format: mender\nversion: 3\n'
print('Building data tar...')
data_buf = io.BytesIO()
with tarfile.open(fileobj=data_buf, mode='w:gz') as dt:
for name, path in [('system.img', SYSTEM), ('syshash.img', SYSHASH), ('boot.img', BOOT)]:
print(f' adding {name} ({os.path.getsize(path)//1024//1024}MB)...')
dt.add(path, arcname=name)
data_bytes = data_buf.getvalue()
print(f'data tar: {len(data_bytes)//1024//1024}MB')
print('Hashing...')
sys_hash = sha256f(SYSTEM)
syshash_hash = sha256f(SYSHASH)
boot_hash = sha256f(BOOT)
print('Building header...')
hdr_buf = io.BytesIO()
with tarfile.open(fileobj=hdr_buf, mode='w:gz') as hdr:
for name, data in [
('header-info', b'{"payloads":[{"type":"dm-verity-update"}],"artifact_provides":{"artifact_name":"adacam-ssh-fix-v5"},"artifact_depends":{"device_type":["keembay"]}}'),
('headers/0000/type-info', b'{"type":"dm-verity-update"}'),
]:
ti = tarfile.TarInfo(name=name); ti.size=len(data)
hdr.addfile(ti, io.BytesIO(data))
hdr_bytes = hdr_buf.getvalue()
manifest = '\n'.join([
f'{sys_hash} data/0000/system.img',
f'{syshash_hash} data/0000/syshash.img',
f'{boot_hash} data/0000/boot.img',
f'{sha256b(hdr_bytes)} header.tar.gz',
f'{sha256b(version_data)} version',
]).encode()+b'\n'
print('Building artifact...')
with tarfile.open(OUT, 'w:') as art:
for name, data in [('version',version_data),('manifest',manifest),('header.tar.gz',hdr_bytes)]:
ti=tarfile.TarInfo(name=name); ti.size=len(data)
art.addfile(ti, io.BytesIO(data))
ti=tarfile.TarInfo(name='data/0000.tar.gz'); ti.size=len(data_bytes)
art.addfile(ti, io.BytesIO(data_bytes))
print(f'DONE: {OUT} ({os.path.getsize(OUT)//1024//1024}MB)')
PYEOF
echo "All done."