|
|
@ -76,6 +76,48 @@ def unsparsify(f): |
|
|
|
raise Exception("Unhandled sparse chunk type") |
|
|
|
raise Exception("Unhandled sparse chunk type") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def flash_partition(cloudlog, spinner, target_slot, partition): |
|
|
|
|
|
|
|
cloudlog.info(f"Downloading and writing {partition['name']}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
downloader = StreamingDecompressor(partition['url']) |
|
|
|
|
|
|
|
with open(f"/dev/disk/by-partlabel/{partition['name']}{target_slot}", 'wb') as out: |
|
|
|
|
|
|
|
partition_size = partition['size'] |
|
|
|
|
|
|
|
# Clear hash before flashing |
|
|
|
|
|
|
|
out.seek(partition_size) |
|
|
|
|
|
|
|
out.write(b"\x00" * 64) |
|
|
|
|
|
|
|
out.seek(0) |
|
|
|
|
|
|
|
os.sync() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Flash partition |
|
|
|
|
|
|
|
if partition['sparse']: |
|
|
|
|
|
|
|
raw_hash = hashlib.sha256() |
|
|
|
|
|
|
|
for chunk in unsparsify(downloader): |
|
|
|
|
|
|
|
raw_hash.update(chunk) |
|
|
|
|
|
|
|
out.write(chunk) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if spinner is not None: |
|
|
|
|
|
|
|
spinner.update_progress(out.tell(), partition_size) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if raw_hash.hexdigest().lower() != partition['hash_raw'].lower(): |
|
|
|
|
|
|
|
raise Exception(f"Unsparse hash mismatch '{raw_hash.hexdigest().lower()}'") |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
while not downloader.eof: |
|
|
|
|
|
|
|
out.write(downloader.read(1024 * 1024)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if spinner is not None: |
|
|
|
|
|
|
|
spinner.update_progress(out.tell(), partition_size) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if downloader.sha256.hexdigest().lower() != partition['hash'].lower(): |
|
|
|
|
|
|
|
raise Exception("Uncompressed hash mismatch") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if out.tell() != partition['size']: |
|
|
|
|
|
|
|
raise Exception("Uncompressed size mismatch") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Write hash after successfull flash |
|
|
|
|
|
|
|
os.sync() |
|
|
|
|
|
|
|
out.write(partition['hash_raw'].lower().encode()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def flash_agnos_update(manifest_path, cloudlog, spinner=None): |
|
|
|
def flash_agnos_update(manifest_path, cloudlog, spinner=None): |
|
|
|
update = json.load(open(manifest_path)) |
|
|
|
update = json.load(open(manifest_path)) |
|
|
|
|
|
|
|
|
|
|
@ -89,45 +131,22 @@ def flash_agnos_update(manifest_path, cloudlog, spinner=None): |
|
|
|
os.system(f"abctl --set_unbootable {target_slot_number}") |
|
|
|
os.system(f"abctl --set_unbootable {target_slot_number}") |
|
|
|
|
|
|
|
|
|
|
|
for partition in update: |
|
|
|
for partition in update: |
|
|
|
cloudlog.info(f"Downloading and writing {partition['name']}") |
|
|
|
success = False |
|
|
|
|
|
|
|
|
|
|
|
downloader = StreamingDecompressor(partition['url']) |
|
|
|
for retries in range(10): |
|
|
|
with open(f"/dev/disk/by-partlabel/{partition['name']}{target_slot}", 'wb') as out: |
|
|
|
try: |
|
|
|
partition_size = partition['size'] |
|
|
|
flash_partition(cloudlog, spinner, target_slot, partition) |
|
|
|
# Clear hash before flashing |
|
|
|
success = True |
|
|
|
out.seek(partition_size) |
|
|
|
break |
|
|
|
out.write(b"\x00" * 64) |
|
|
|
|
|
|
|
out.seek(0) |
|
|
|
except requests.exceptions.RequestException: |
|
|
|
os.sync() |
|
|
|
spinner.update("Waiting for internet...") |
|
|
|
|
|
|
|
cloudlog.info(f"Failed to download {partition['name']}, retrying ({retries})") |
|
|
|
# Flash partition |
|
|
|
time.sleep(10) |
|
|
|
if partition['sparse']: |
|
|
|
|
|
|
|
raw_hash = hashlib.sha256() |
|
|
|
if not success: |
|
|
|
for chunk in unsparsify(downloader): |
|
|
|
cloudlog.info(f"Failed to flash {partition['name']}, aborting") |
|
|
|
raw_hash.update(chunk) |
|
|
|
raise Exception("Maximum retries exceeded") |
|
|
|
out.write(chunk) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if spinner is not None: |
|
|
|
|
|
|
|
spinner.update_progress(out.tell(), partition_size) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if raw_hash.hexdigest().lower() != partition['hash_raw'].lower(): |
|
|
|
|
|
|
|
raise Exception(f"Unsparse hash mismatch '{raw_hash.hexdigest().lower()}'") |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
while not downloader.eof: |
|
|
|
|
|
|
|
out.write(downloader.read(1024 * 1024)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if spinner is not None: |
|
|
|
|
|
|
|
spinner.update_progress(out.tell(), partition_size) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if downloader.sha256.hexdigest().lower() != partition['hash'].lower(): |
|
|
|
|
|
|
|
raise Exception("Uncompressed hash mismatch") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if out.tell() != partition['size']: |
|
|
|
|
|
|
|
raise Exception("Uncompressed size mismatch") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Write hash after successfull flash |
|
|
|
|
|
|
|
os.sync() |
|
|
|
|
|
|
|
out.write(partition['hash_raw'].lower().encode()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cloudlog.info(f"AGNOS ready on slot {target_slot}") |
|
|
|
cloudlog.info(f"AGNOS ready on slot {target_slot}") |
|
|
|
|
|
|
|
|
|
|
|