Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 <output-dir-name>] 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 <output-dir-name>] [--old_dir <old-dir-name>] payload.bin
```
35 changes: 30 additions & 5 deletions extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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)
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
protobuf<=3.20.0
six>=1.16.0
40 changes: 26 additions & 14 deletions update_payload/applier.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
except ImportError:
pass
import os
import platform
import subprocess
import sys
import tempfile
Expand Down Expand Up @@ -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:
Expand All @@ -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):
Expand Down Expand Up @@ -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)')

Expand Down Expand Up @@ -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:
Expand All @@ -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()

Expand All @@ -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.
Expand Down