diff options
Diffstat (limited to 'tools/releasetools/common.py')
-rw-r--r-- | tools/releasetools/common.py | 137 |
1 files changed, 114 insertions, 23 deletions
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 592ed19..6f921e0 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -32,10 +32,7 @@ import zipfile import blockimgdiff import rangelib -try: - from hashlib import sha1 as sha1 -except ImportError: - from sha import sha as sha1 +from hashlib import sha1 as sha1 class Options(object): @@ -52,6 +49,11 @@ class Options(object): self.java_args = "-Xmx2048m" # JVM Args self.public_key_suffix = ".x509.pem" self.private_key_suffix = ".pk8" + # use otatools built boot_signer by default + self.boot_signer_path = "boot_signer" + self.boot_signer_args = [] + self.verity_signer_path = None + self.verity_signer_args = [] self.verbose = False self.tempfiles = [] self.device_specific = None @@ -200,12 +202,13 @@ def LoadDictionaryFromLines(lines): def LoadRecoveryFSTab(read_helper, fstab_version): class Partition(object): - def __init__(self, mount_point, fs_type, device, length, device2): + def __init__(self, mount_point, fs_type, device, length, device2, context): self.mount_point = mount_point self.fs_type = fs_type self.device = device self.length = length self.device2 = device2 + self.context = context try: data = read_helper("RECOVERY/RAMDISK/etc/recovery.fstab") @@ -254,6 +257,7 @@ def LoadRecoveryFSTab(read_helper, fstab_version): line = line.strip() if not line or line.startswith("#"): continue + # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags> pieces = line.split() if len(pieces) != 5: raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,)) @@ -273,9 +277,17 @@ def LoadRecoveryFSTab(read_helper, fstab_version): # Ignore all unknown options in the unified fstab continue + mount_flags = pieces[3] + # Honor the SELinux context if present. + context = None + for i in mount_flags.split(","): + if i.startswith("context="): + context = i + mount_point = pieces[1] d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2], - device=pieces[0], length=length, device2=None) + device=pieces[0], length=length, + device2=None, context=context) else: raise ValueError("Unknown fstab_version: \"%d\"" % (fstab_version,)) @@ -360,10 +372,14 @@ def BuildBootableImage(sourcedir, fs_config_file, info_dict=None): assert p.returncode == 0, "mkbootimg of %s image failed" % ( os.path.basename(sourcedir),) - if info_dict.get("verity_key", None): + if (info_dict.get("boot_signer", None) == "true" and + info_dict.get("verity_key", None)): path = "/" + os.path.basename(sourcedir).lower() - cmd = ["boot_signer", path, img.name, info_dict["verity_key"] + ".pk8", - info_dict["verity_key"] + ".x509.pem", img.name] + cmd = [OPTIONS.boot_signer_path] + cmd.extend(OPTIONS.boot_signer_args) + cmd.extend([path, img.name, + info_dict["verity_key"] + ".pk8", + info_dict["verity_key"] + ".x509.pem", img.name]) p = Run(cmd, stdout=subprocess.PIPE) p.communicate() assert p.returncode == 0, "boot_signer of %s image failed" % path @@ -380,6 +396,10 @@ def BuildBootableImage(sourcedir, fs_config_file, info_dict=None): p.communicate() assert p.returncode == 0, "vboot_signer of %s image failed" % path + # Clean up the temp files. + img_unsigned.close() + img_keyblock.close() + img.seek(os.SEEK_SET, 0) data = img.read() @@ -540,7 +560,7 @@ def SignFile(input_name, output_name, key, password, align=None, raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,)) if align: - p = Run(["zipalign", "-f", str(align), sign_name, output_name]) + p = Run(["zipalign", "-f", "-p", str(align), sign_name, output_name]) p.communicate() if p.returncode != 0: raise ExternalError("zipalign failed: return code %s" % (p.returncode,)) @@ -652,7 +672,9 @@ def ParseOptions(argv, argv, "hvp:s:x:" + extra_opts, ["help", "verbose", "path=", "signapk_path=", "extra_signapk_args=", "java_path=", "java_args=", "public_key_suffix=", - "private_key_suffix=", "device_specific=", "extra="] + + "private_key_suffix=", "boot_signer_path=", "boot_signer_args=", + "verity_signer_path=", "verity_signer_args=", "device_specific=", + "extra="] + list(extra_long_opts)) except getopt.GetoptError as err: Usage(docstring) @@ -679,6 +701,14 @@ def ParseOptions(argv, OPTIONS.public_key_suffix = a elif o in ("--private_key_suffix",): OPTIONS.private_key_suffix = a + elif o in ("--boot_signer_path",): + OPTIONS.boot_signer_path = a + elif o in ("--boot_signer_args",): + OPTIONS.boot_signer_args = shlex.split(a) + elif o in ("--verity_signer_path",): + OPTIONS.verity_signer_path = a + elif o in ("--verity_signer_args",): + OPTIONS.verity_signer_args = shlex.split(a) elif o in ("-s", "--device_specific"): OPTIONS.device_specific = a elif o in ("-x", "--extra"): @@ -854,16 +884,55 @@ def ZipWrite(zip_file, filename, arcname=None, perms=0o644, zipfile.ZIP64_LIMIT = saved_zip64_limit -def ZipWriteStr(zip_file, filename, data, perms=0o644, compression=None): - # use a fixed timestamp so the output is repeatable. - zinfo = zipfile.ZipInfo(filename=filename, - date_time=(2009, 1, 1, 0, 0, 0)) - if compression is None: +def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None, + compress_type=None): + """Wrap zipfile.writestr() function to work around the zip64 limit. + + Even with the ZIP64_LIMIT workaround, it won't allow writing a string + longer than 2GiB. It gives 'OverflowError: size does not fit in an int' + when calling crc32(bytes). + + But it still works fine to write a shorter string into a large zip file. + We should use ZipWrite() whenever possible, and only use ZipWriteStr() + when we know the string won't be too long. + """ + + saved_zip64_limit = zipfile.ZIP64_LIMIT + zipfile.ZIP64_LIMIT = (1 << 32) - 1 + + if not isinstance(zinfo_or_arcname, zipfile.ZipInfo): + zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname) zinfo.compress_type = zip_file.compression + if perms is None: + perms = 0o644 else: - zinfo.compress_type = compression - zinfo.external_attr = perms << 16 + zinfo = zinfo_or_arcname + + # If compress_type is given, it overrides the value in zinfo. + if compress_type is not None: + zinfo.compress_type = compress_type + + # If perms is given, it has a priority. + if perms is not None: + zinfo.external_attr = perms << 16 + + # Use a fixed timestamp so the output is repeatable. + zinfo.date_time = (2009, 1, 1, 0, 0, 0) + zip_file.writestr(zinfo, data) + zipfile.ZIP64_LIMIT = saved_zip64_limit + + +def ZipClose(zip_file): + # http://b/18015246 + # zipfile also refers to ZIP64_LIMIT during close() when it writes out the + # central directory. + saved_zip64_limit = zipfile.ZIP64_LIMIT + zipfile.ZIP64_LIMIT = (1 << 32) - 1 + + zip_file.close() + + zipfile.ZIP64_LIMIT = saved_zip64_limit class DeviceSpecificParams(object): @@ -969,7 +1038,7 @@ class File(object): return t def AddToZip(self, z, compression=None): - ZipWriteStr(z, self.name, self.data, compression=compression) + ZipWriteStr(z, self.name, self.data, compress_type=compression) DIFF_PROGRAM_BY_EXT = { ".gz" : "imgdiff", @@ -1106,6 +1175,9 @@ class BlockDifference(object): self.partition = partition self.check_first_block = check_first_block + # Due to http://b/20939131, check_first_block is disabled temporarily. + assert not self.check_first_block + if version is None: version = 1 if OPTIONS.info_dict: @@ -1133,24 +1205,25 @@ class BlockDifference(object): if progress: script.ShowProgress(progress, 0) self._WriteUpdate(script, output_zip) + self._WritePostInstallVerifyScript(script) def WriteVerifyScript(self, script): partition = self.partition if not self.src: script.Print("Image %s will be patched unconditionally." % (partition,)) else: + ranges = self.src.care_map.subtract(self.src.clobbered_blocks) + ranges_str = ranges.to_string_raw() if self.version >= 3: script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || ' 'block_image_verify("%s", ' 'package_extract_file("%s.transfer.list"), ' '"%s.new.dat", "%s.patch.dat")) then') % ( - self.device, self.src.care_map.to_string_raw(), - self.src.TotalSha1(), + self.device, ranges_str, self.src.TotalSha1(), self.device, partition, partition, partition)) else: script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % ( - self.device, self.src.care_map.to_string_raw(), - self.src.TotalSha1())) + self.device, ranges_str, self.src.TotalSha1())) script.Print('Verified %s image...' % (partition,)) script.AppendExtra('else') @@ -1171,6 +1244,21 @@ class BlockDifference(object): script.AppendExtra(('abort("%s partition has unexpected contents");\n' 'endif;') % (partition,)) + def _WritePostInstallVerifyScript(self, script): + partition = self.partition + script.Print('Verifying the updated %s image...' % (partition,)) + # Unlike pre-install verification, clobbered_blocks should not be ignored. + ranges = self.tgt.care_map + ranges_str = ranges.to_string_raw() + script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % ( + self.device, ranges_str, + self.tgt.TotalSha1(include_clobbered_blocks=True))) + script.Print('Verified the updated %s image.' % (partition,)) + script.AppendExtra( + 'else\n' + ' abort("%s partition has unexpected contents after OTA update");\n' + 'endif;' % (partition,)) + def _WriteUpdate(self, script, output_zip): ZipWrite(output_zip, '{}.transfer.list'.format(self.path), @@ -1198,6 +1286,9 @@ class BlockDifference(object): return ctx.hexdigest() + # TODO(tbao): Due to http://b/20939131, block 0 may be changed without + # remounting R/W. Will change the checking to a finer-grained way to + # mask off those bits. def _CheckFirstBlock(self, script): r = rangelib.RangeSet((0, 1)) srchash = self._HashBlocks(self.src, r) |