Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
82 lines
4.2 KiB
Python
82 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: MPL-2.0
|
|
"""Compare raw boot backups to the installed-kernel diagnostic, without device writes."""
|
|
import argparse
|
|
import hashlib
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from boot_backup import verify
|
|
from tundra import ROOT, checked_source, digest, now, save
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--backup', required=True, type=Path)
|
|
args = parser.parse_args()
|
|
backup = args.backup.resolve()
|
|
verify(backup)
|
|
spec = importlib.util.spec_from_file_location('collector', ROOT/'tools/collect-device-ssh.py')
|
|
collector = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(collector)
|
|
ssh = collector.ssh_base(ROOT/'logs/device/ssh-control', '10.15.19.82', 'droidian')
|
|
code = """import json,subprocess
|
|
def prop(key): return subprocess.check_output(['getprop',key],text=True).strip()
|
|
print(json.dumps({k:prop(k) for k in ('ro.product.device','ro.boot.slot_suffix','ro.boot.serialno')}))
|
|
"""
|
|
device = json.loads(subprocess.check_output(ssh+[shlex.join(['python3','-c',code])], timeout=20))
|
|
if device['ro.product.device'] != 'sargo' or device['ro.boot.slot_suffix'] != '_a' or not device['ro.boot.serialno']:
|
|
raise ValueError('Expected the inventoried Sargo running slot A')
|
|
build = json.loads((ROOT/'out/sargo-raw-diagnostic/build.json').read_text())
|
|
if build.get('rawBackupManifestSha256') != digest(backup/'manifest.json'):
|
|
raise ValueError('Diagnostic was not built from this raw backup')
|
|
slots = {}
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
for slot in ('a','b'):
|
|
out = Path(temp)/slot
|
|
result = subprocess.check_output([sys.executable,
|
|
str(checked_source('aosp-mkbootimg')/'unpack_bootimg.py'),
|
|
'--boot_img',str(backup/f'boot_{slot}.img'),'--out',str(out),
|
|
'--format=mkbootimg'], text=True)
|
|
args = shlex.split(result)
|
|
fields = dict(zip(args[::2],args[1::2]))
|
|
slots[slot] = {'kernelSha256':digest(out/'kernel'),
|
|
'ramdiskSha256':digest(out/'ramdisk'),
|
|
'header':{k:v for k,v in fields.items() if k not in ('--kernel','--ramdisk','--dtb','--cmdline')},
|
|
'matchesDiagnosticKernel':digest(out/'kernel') == build['kernelSha256']}
|
|
if slot == 'a':
|
|
for key,value in slots[slot]['header'].items():
|
|
if build['header'].get(key) != value:
|
|
raise ValueError('Slot A header differs from the diagnostic: '+key)
|
|
if build['header']['--cmdline'] != fields['--cmdline']+' rdinit=/init tundra.usb=1':
|
|
raise ValueError('Diagnostic command line differs from the inventoried boot')
|
|
if not slots['a']['matchesDiagnosticKernel']:
|
|
raise ValueError('Slot A kernel differs from the diagnostic')
|
|
image = ROOT/'out/sargo-raw-diagnostic'/build['file']
|
|
if digest(image) != build['imageSha256']:
|
|
raise ValueError('Diagnostic image changed')
|
|
report = {'recordedAt':now(),'device':'sargo','activeSlot':'a',
|
|
'serialSha256':hashlib.sha256(device['ro.boot.serialno'].encode()).hexdigest(),
|
|
'backupDirectory':str(backup),'backupManifestSha256':digest(backup/'manifest.json'),
|
|
'slots':slots,'diagnosticImage':str(image.relative_to(ROOT)),
|
|
'diagnosticImageSha256':digest(image),'diagnosticBuildSha256':digest(image.parent/'build.json'),
|
|
'rawBackupVerified':True,'restorationTested':False,'hardwareBootTested':False,
|
|
'flashReady':False}
|
|
path = ROOT/'logs/device/boot-backup-qualification.json'
|
|
save(path,report)
|
|
validation = json.loads((ROOT/'VALIDATION.json').read_text())
|
|
validation['deviceInventory']['rawPartitionsBackedUp'] = True
|
|
validation['bootBackup'] = {'report':str(path.relative_to(ROOT)),
|
|
'reportSha256':digest(path),'verified':True,'restorationTested':False}
|
|
save(ROOT/'VALIDATION.json',validation)
|
|
print('Six partitions verified; slot A kernel and header match the diagnostic.')
|
|
print('Slot B has different contents and is not a qualified fallback.')
|
|
print(path)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|