diff --git a/README.md b/README.md index d57aa81..4cb4bcb 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,20 @@ -## System requirement +## System requirements - Python3, pip -- google protobuf for python `pip3 install protobuf` +- google protobuf for python, six +``` +pip3 install -r requirements.txt +``` ### Full OTA - -- LD_LIBRARY_PATH=./lib64/ ./extract.py --output_dir output/ payload.bin -- This will start to extract the images within the payload.bin file to the output folder you are in. +``` +./extract.py [--skip_hash] [--output dir ] payload.bin +``` +- This will start to extract the images within the payload.bin file to a folder named "output" by default (a different folder name can be specified). ### Incremental OTA -- Copy original images (from full OTA or dumped from devices) to old folder (with part name without file extension, ex: boot, system) -- LD_LIBRARY_PATH=./lib64/ ./extract.py --output_dir output/ --old_dir old/ payload.bin +- Copy original images (from full OTA or dumped from devices) to old_dir folder +``` +./extract.py [--skip_hash] [--ignore_block_size] [--output dir ] [--old_dir ] payload.bin +``` diff --git a/extract.py b/extract.py index b7a7b55..e156f32 100755 --- a/extract.py +++ b/extract.py @@ -2,11 +2,21 @@ import argparse import errno +import glob import os +import platform import update_payload from update_payload import applier +if platform.machine == 'x86_64': + os.environ['LD_LIBRARY_PATH'] = './lib64/' +if platform.machine == 'x86': + os.environ['LD_LIBRARY_PATH'] = './lib/' +elif platform.machine == 'aarch64': + os.environ['LD_LIBRARY_PATH'] = '/system/lib64:/system/lib' +elif platform.machine == 'arm': + os.environ['LD_LIBRARY_PATH'] = '/system/lib' def list_content(payload_file_name): with open(payload_file_name, 'rb') as payload_file: @@ -18,18 +28,21 @@ def list_content(payload_file_name): part.new_partition_info.size)) -def extract(payload_file_name, output_dir="output", old_dir="old", partition_names=None): +def extract(payload_file_name, output_dir="output", old_dir="old", partition_names=None, skip_hash=None, ignore_block_size=None): try: os.makedirs(output_dir) except OSError as e: if e.errno != errno.EEXIST: raise + for i in glob.glob(old_dir + '/*.img'): + os.rename(i, i[:-4]) + with open(payload_file_name, 'rb') as payload_file: payload = update_payload.Payload(payload_file) payload.Init() - helper = applier.PayloadApplier(payload) + helper = applier.PayloadApplier(payload, ignore_block_size) for part in payload.manifest.partitions: if partition_names and part.partition_name not in partition_names: continue @@ -40,12 +53,20 @@ def extract(payload_file_name, output_dir="output", old_dir="old", partition_nam helper._ApplyToPartition( part.operations, part.partition_name, 'install_operations', output_file, - part.new_partition_info, old_file, part.old_partition_info) + part.new_partition_info, old_file, + part.old_partition_info, skip_hash) else: helper._ApplyToPartition( part.operations, part.partition_name, 'install_operations', output_file, - part.new_partition_info) + part.new_partition_info, + skip_hash=skip_hash) + + for i in glob.glob(old_dir + '/*'): + os.rename(i, i + '.img') + + for i in glob.glob(output_dir + '/*'): + os.rename(i, i + '.img') if __name__ == '__main__': parser = argparse.ArgumentParser() @@ -59,9 +80,13 @@ def extract(payload_file_name, output_dir="output", old_dir="old", partition_nam help="Name of the partitions to extract") parser.add_argument("--list_partitions", action="store_true", help="List the partitions included in the payload.bin") + parser.add_argument("--skip_hash", action="store_true", + help="Skip the hash check for individual img files") + parser.add_argument("--ignore_block_size", action="store_true", + help="Ignore block size") args = parser.parse_args() if args.list_partitions: list_content(args.payload) else: - extract(args.payload, args.output_dir, args.old_dir, args.partitions) + extract(args.payload, args.output_dir, args.old_dir, args.partitions, args.skip_hash, args.ignore_block_size) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c950008 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +protobuf<=3.20.0 +six>=1.16.0 diff --git a/update_payload/applier.py b/update_payload/applier.py index c05095a..2b205b3 100644 --- a/update_payload/applier.py +++ b/update_payload/applier.py @@ -45,6 +45,7 @@ except ImportError: pass import os +import platform import subprocess import sys import tempfile @@ -206,8 +207,8 @@ class PayloadApplier(object): applying an update payload. """ - def __init__(self, payload, bsdiff_in_place=True, bspatch_path="./bspatch", - puffpatch_path="./puffin", truncate_to_expected_size=True): + def __init__(self, payload, ignore_block_size, arch='x86_64', bspatch_path='bspatch', puffpatch_path='puffin', + bsdiff_in_place=True, truncate_to_expected_size=True): """Initialize the applier. Args: @@ -221,11 +222,18 @@ def __init__(self, payload, bsdiff_in_place=True, bspatch_path="./bspatch", """ assert payload.is_init, 'uninitialized update payload' self.payload = payload + self.ignore_block_size = ignore_block_size self.block_size = payload.manifest.block_size self.minor_version = payload.manifest.minor_version + self.arch = platform.machine() + if self.arch == 'x86_64' or self.arch == 'x86': + self.bspatch_path = "./bspatch" or bspatch_path + self.puffpatch_path = "./puffin" or puffpatch_path + else: + self.bspatch_path = "/data/local/bin/bspatch" or bspatch_path + self.puffpatch_path = "/data/local/bin/puffin" or puffpatch_path + self.bsdiff_in_place = bsdiff_in_place - self.bspatch_path = bspatch_path or 'bspatch' - self.puffpatch_path = puffpatch_path or 'puffin' self.truncate_to_expected_size = truncate_to_expected_size def _ApplyReplaceOperation(self, op, op_name, out_data, part_file, part_size): @@ -271,7 +279,7 @@ def _ApplyReplaceOperation(self, op, op_name, out_data, part_file, part_size): part_size)) # Make sure that we have enough data to write. - if data_end >= data_length + block_size: + if not self.ignore_block_size and (data_end >= data_length + block_size): raise PayloadError( '%s: more dst blocks than data (even with padding)') @@ -517,7 +525,8 @@ def _ApplyOperations(self, operations, base_name, old_part_file, def _ApplyToPartition(self, operations, part_name, base_name, new_part_file_name, new_part_info, - old_part_file_name=None, old_part_info=None): + old_part_file_name=None, old_part_info=None, + skip_hash=None): """Applies an update to a partition. Args: @@ -528,16 +537,18 @@ def _ApplyToPartition(self, operations, part_name, base_name, new_part_info: size and expected hash of dest partition old_part_file_name: file name of source partition (optional) old_part_info: size and expected hash of source partition (optional) + skip_hash: command line arg to skip hash checks Raises: PayloadError if anything goes wrong with the update. """ # Do we have a source partition? if old_part_file_name: - # Verify the source partition. - with open(old_part_file_name, 'rb') as old_part_file: - _VerifySha256(old_part_file, old_part_info.hash, - 'old ' + part_name, length=old_part_info.size) + # Verify the source partition if skip_hash arg was not given. + if not skip_hash: + with open(old_part_file_name, 'rb') as old_part_file: + _VerifySha256(old_part_file, old_part_info.hash, + 'old ' + part_name, length=old_part_info.size) new_part_file_mode = 'r+b' open(new_part_file_name, 'w').close() @@ -563,10 +574,11 @@ def _ApplyToPartition(self, operations, part_name, base_name, new_part_file.seek(new_part_info.size) new_part_file.truncate() - # Verify the resulting partition. - with open(new_part_file_name, 'rb') as new_part_file: - _VerifySha256(new_part_file, new_part_info.hash, - 'new ' + part_name, length=new_part_info.size) + # Verify the resulting partition if skip_hash arg was not given. + if not skip_hash: + with open(new_part_file_name, 'rb') as new_part_file: + _VerifySha256(new_part_file, new_part_info.hash, + 'new ' + part_name, length=new_part_info.size) def Run(self, new_parts, old_parts=None): """Applier entry point, invoking all update operations.