| #!/usr/bin/env python |
| # |
| # Copyright 2018 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Helper tool for 'oem at-write-persistent-digest' fastboot command. |
| |
| This tool generates and stages the correct input data format, based on the |
| user-provided inputs, for the 'oem at-write-persistent-digest' fastboot command |
| for Android Things devices before running the command itself. |
| |
| The input format is defined elsewhere to be the following: |
| |
| - Name length: 4 bytes (little-endian) |
| - Name: 'name length' bytes |
| - Digest length: 4 bytes (little-endian) |
| - Digest: 'digest length' bytes |
| |
| Digest length can be zero, indicating that any existing digest with the given |
| name should be deleted. This corresponds to the '--clear_digest' option for this |
| tool. |
| |
| Digest names must be prefixed with 'avb.persistent_digest.', and if the |
| user-provided name does not include that prefix it is added automatically. |
| """ |
| |
| import sys |
| |
| ver = sys.version_info |
| if (ver[0] < 2) or (ver[0] == 2 and ver[1] < 7) or (ver[0] == 3 and ver[1] < 2): |
| print('This script requires Python 2.7+ or 3.2+') |
| sys.exit(1) |
| |
| import argparse |
| import os |
| import shutil |
| import struct |
| import subprocess |
| import tempfile |
| |
| HELP_DESCRIPTION = """Helper script for 'fastboot oem |
| at-write-persistent-digest' that generates and stages the required input data |
| format.""" |
| |
| AVB_PERSISTENT_DIGEST_PREFIX = 'avb.persistent_digest.' |
| |
| |
| def WritePersistentDigest(name, |
| digest=None, |
| clear_digest=False, |
| serial=None, |
| verbose=False): |
| if not name.startswith(AVB_PERSISTENT_DIGEST_PREFIX): |
| print("Automatically adding '{}' prefix to persistent value name".format( |
| AVB_PERSISTENT_DIGEST_PREFIX)) |
| name = AVB_PERSISTENT_DIGEST_PREFIX + name |
| |
| tempdir = tempfile.mkdtemp() |
| try: |
| digest_data = os.path.join(tempdir, 'digest_data') |
| |
| with open(digest_data, 'wb') as out: |
| out.write(struct.pack('<I', len(name))) |
| out.write(name) |
| if clear_digest: |
| out.write(struct.pack('<I', 0)) |
| else: |
| digest_bytes = bytearray.fromhex(digest) |
| out.write(struct.pack('<I', len(digest_bytes))) |
| out.write(digest_bytes) |
| |
| def fastboot_cmd(args): |
| args = ['fastboot'] + (['-s', serial] if serial else []) + args |
| if verbose: |
| print('$ ' + ' '.join(args)) |
| |
| try: |
| out = subprocess.check_output( |
| args, stderr=subprocess.STDOUT).decode('utf-8') |
| except subprocess.CalledProcessError as e: |
| print(e.output.decode('utf-8')) |
| print("Command '{}' returned non-zero exit status {}".format( |
| ' '.join(e.cmd), e.returncode)) |
| sys.exit(1) |
| |
| if verbose: |
| print(out) |
| |
| fastboot_cmd(['stage', digest_data]) |
| fastboot_cmd(['oem', 'at-write-persistent-digest']) |
| |
| print("Persistent value '{}' {}".format( |
| name, 'cleared' if clear_digest else 'written')) |
| |
| finally: |
| shutil.rmtree(tempdir) |
| |
| |
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser(description=HELP_DESCRIPTION) |
| |
| # Optional arguments |
| parser.add_argument( |
| '-v', |
| '--verbose', |
| action='store_true', |
| help='verbose; prints fastboot commands and their output') |
| parser.add_argument( |
| '-s', |
| '--serial', |
| help= |
| "specify device to unlock, either by serial or any other valid value for fastboot's -s arg" |
| ) |
| |
| # Required arguments |
| parser.add_argument( |
| '--name', |
| required=True, |
| help= |
| "persistent digest name to write, 'avb.persistent_digest.' prefix will be automatically added if not already present" |
| ) |
| group = parser.add_mutually_exclusive_group(required=True) |
| group.add_argument( |
| '--clear_digest', |
| action='store_true', |
| help= |
| 'clear any existing persistent digest value, rather than writing a new value' |
| ) |
| group.add_argument( |
| '--digest', |
| help='persistent digest value to write, as a hex encoded string') |
| |
| # Print help if no args given |
| args = parser.parse_args(args=None if sys.argv[1:] else ['-h']) |
| |
| WritePersistentDigest( |
| name=args.name, |
| clear_digest=args.clear_digest, |
| digest=args.digest, |
| serial=args.serial, |
| verbose=args.verbose) |