handle don't care regions in the system image
The system partitions has regions that we shouldn't write and can't depend on the contents of. Adds a new script to generate a map of these regions (using the sparse image as input), and include the map in the package zip so it can be used when writing or patching the system partition. Also fixes a bug where the wrong SELinux file contexts are used when generating incrementals. Change-Id: Iaca5b967a3b7d1df843c7c21becc19b3f1633dad
This commit is contained in:
@@ -1135,7 +1135,8 @@ DISTTOOLS := $(HOST_OUT_EXECUTABLES)/minigzip \
|
||||
$(HOST_OUT_EXECUTABLES)/mkuserimg.sh \
|
||||
$(HOST_OUT_EXECUTABLES)/make_ext4fs \
|
||||
$(HOST_OUT_EXECUTABLES)/simg2img \
|
||||
$(HOST_OUT_EXECUTABLES)/e2fsck
|
||||
$(HOST_OUT_EXECUTABLES)/e2fsck \
|
||||
$(HOST_OUT_EXECUTABLES)/xdelta3
|
||||
|
||||
OTATOOLS := $(DISTTOOLS) \
|
||||
$(HOST_OUT_EXECUTABLES)/aapt
|
||||
|
@@ -27,6 +27,8 @@ import sys
|
||||
import commands
|
||||
import shutil
|
||||
|
||||
import simg_map
|
||||
|
||||
def RunCommand(cmd):
|
||||
""" Echo and run the given command
|
||||
|
||||
@@ -146,6 +148,13 @@ def UnsparseImage(sparse_image_path, replace=True):
|
||||
return False, None
|
||||
return True, unsparse_image_path
|
||||
|
||||
def MappedUnsparseImage(sparse_image_path, unsparse_image_path,
|
||||
map_path, mapped_unsparse_image_path):
|
||||
if simg_map.ComputeMap(sparse_image_path, unsparse_image_path,
|
||||
map_path, mapped_unsparse_image_path):
|
||||
return False
|
||||
return True
|
||||
|
||||
def MakeVerityEnabledImage(out_file, prop_dict):
|
||||
"""Creates an image that is verifiable using dm-verity.
|
||||
|
||||
|
@@ -1015,7 +1015,7 @@ def MakeSystemPatch(source_file, target_file):
|
||||
with open(output_file.name + ".xz") as patch_file:
|
||||
patch_data = patch_file.read()
|
||||
os.unlink(patch_file.name)
|
||||
return File("system.img.p", patch_data)
|
||||
return File("system.muimg.p", patch_data)
|
||||
|
||||
def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
|
||||
info_dict=None):
|
||||
|
@@ -190,6 +190,16 @@ class EdifyGenerator(object):
|
||||
(p.fs_type, common.PARTITION_TYPES[p.fs_type],
|
||||
p.device, p.length, p.mount_point))
|
||||
|
||||
def WipeBlockDevice(self, partition):
|
||||
if partition != "/system":
|
||||
raise ValueError(("WipeBlockDevice currently only works "
|
||||
"on /system, not %s\n") % (partition,))
|
||||
fstab = self.info.get("fstab", None)
|
||||
size = self.info.get("system_size", None)
|
||||
device = fstab[partition].device
|
||||
|
||||
self.script.append('wipe_block_device("%s", %s);' % (device, size))
|
||||
|
||||
def DeleteFiles(self, file_list):
|
||||
"""Delete all files in file_list."""
|
||||
if not file_list: return
|
||||
@@ -224,7 +234,7 @@ class EdifyGenerator(object):
|
||||
cmd = "".join(cmd)
|
||||
self.script.append(self._WordWrap(cmd))
|
||||
|
||||
def WriteRawImage(self, mount_point, fn):
|
||||
def WriteRawImage(self, mount_point, fn, mapfn=None):
|
||||
"""Write the given package file into the partition for the given
|
||||
mount point."""
|
||||
|
||||
@@ -238,6 +248,11 @@ class EdifyGenerator(object):
|
||||
'write_raw_image(package_extract_file("%(fn)s"), "%(device)s");'
|
||||
% args)
|
||||
elif partition_type == "EMMC":
|
||||
if mapfn:
|
||||
args["map"] = mapfn
|
||||
self.script.append(
|
||||
'package_extract_file("%(fn)s", "%(device)s", "%(map)s");' % args)
|
||||
else:
|
||||
self.script.append(
|
||||
'package_extract_file("%(fn)s", "%(device)s");' % args)
|
||||
else:
|
||||
@@ -309,7 +324,9 @@ class EdifyGenerator(object):
|
||||
common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary",
|
||||
data, perms=0755)
|
||||
|
||||
def Syspatch(self, filename, size, target_sha, source_sha, patchfile):
|
||||
def Syspatch(self, filename, target_mapfile, target_sha,
|
||||
source_mapfile, source_sha, patchfile):
|
||||
"""Applies a compressed binary patch to a block device."""
|
||||
call = 'syspatch("%s", "%s", "%s", "%s", "%s");'
|
||||
self.script.append(call % (filename, size, target_sha, source_sha, patchfile))
|
||||
call = 'syspatch("%s", "%s", "%s", "%s", "%s", "%s");'
|
||||
self.script.append(call % (filename, target_mapfile, target_sha,
|
||||
source_mapfile, source_sha, patchfile))
|
||||
|
@@ -60,7 +60,7 @@ def AddSystem(output_zip, sparse=True):
|
||||
common.ZipWriteStr(output_zip, "system.img", data)
|
||||
|
||||
|
||||
def BuildSystem(input_dir, info_dict, sparse=True):
|
||||
def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
|
||||
print "creating system.img..."
|
||||
|
||||
img = tempfile.NamedTemporaryFile()
|
||||
@@ -87,6 +87,8 @@ def BuildSystem(input_dir, info_dict, sparse=True):
|
||||
image_props, img.name)
|
||||
assert succ, "build system.img image failed"
|
||||
|
||||
mapdata = None
|
||||
|
||||
if sparse:
|
||||
img.seek(os.SEEK_SET, 0)
|
||||
data = img.read()
|
||||
@@ -95,13 +97,30 @@ def BuildSystem(input_dir, info_dict, sparse=True):
|
||||
success, name = build_image.UnsparseImage(img.name, replace=False)
|
||||
if not success:
|
||||
assert False, "unsparsing system.img failed"
|
||||
|
||||
if map_file:
|
||||
mmap = tempfile.NamedTemporaryFile()
|
||||
mimg = tempfile.NamedTemporaryFile(delete=False)
|
||||
success = build_image.MappedUnsparseImage(
|
||||
img.name, name, mmap.name, mimg.name)
|
||||
if not success:
|
||||
assert False, "creating sparse map failed"
|
||||
os.unlink(name)
|
||||
name = mimg.name
|
||||
|
||||
with open(mmap.name) as f:
|
||||
mapdata = f.read()
|
||||
|
||||
try:
|
||||
with open(name) as f:
|
||||
data = f.read()
|
||||
finally:
|
||||
os.unlink(name)
|
||||
|
||||
if mapdata is None:
|
||||
return data
|
||||
else:
|
||||
return mapdata, data
|
||||
|
||||
|
||||
def AddVendor(output_zip):
|
||||
|
@@ -461,8 +461,14 @@ else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
|
||||
|
||||
script.ShowProgress(system_progress, 30)
|
||||
if block_based:
|
||||
img_from_target_files.AddSystem(output_zip, sparse=False)
|
||||
script.WriteRawImage("/system", "system.img")
|
||||
mapdata, data = img_from_target_files.BuildSystem(
|
||||
OPTIONS.input_tmp, OPTIONS.info_dict,
|
||||
sparse=False, map_file=True)
|
||||
|
||||
common.ZipWriteStr(output_zip, "system.map", mapdata)
|
||||
common.ZipWriteStr(output_zip, "system.muimg", data)
|
||||
script.WipeBlockDevice("/system")
|
||||
script.WriteRawImage("/system", "system.muimg", mapfn="system.map")
|
||||
else:
|
||||
script.FormatPartition("/system")
|
||||
script.Mount("/system")
|
||||
@@ -608,8 +614,10 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||
with tempfile.NamedTemporaryFile() as tgt_file:
|
||||
print "building source system image..."
|
||||
src_file = tempfile.NamedTemporaryFile()
|
||||
src_data = img_from_target_files.BuildSystem(
|
||||
OPTIONS.source_tmp, OPTIONS.source_info_dict, sparse=False)
|
||||
src_mapdata, src_data = img_from_target_files.BuildSystem(
|
||||
OPTIONS.source_tmp, OPTIONS.source_info_dict,
|
||||
sparse=False, map_file=True)
|
||||
|
||||
src_sys_sha1 = sha1(src_data).hexdigest()
|
||||
print "source system sha1:", src_sys_sha1
|
||||
src_file.write(src_data)
|
||||
@@ -617,8 +625,9 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||
|
||||
print "building target system image..."
|
||||
tgt_file = tempfile.NamedTemporaryFile()
|
||||
tgt_data = img_from_target_files.BuildSystem(
|
||||
OPTIONS.target_tmp, OPTIONS.target_info_dict, sparse=False)
|
||||
tgt_mapdata, tgt_data = img_from_target_files.BuildSystem(
|
||||
OPTIONS.target_tmp, OPTIONS.target_info_dict,
|
||||
sparse=False, map_file=True)
|
||||
tgt_sys_sha1 = sha1(tgt_data).hexdigest()
|
||||
print "target system sha1:", tgt_sys_sha1
|
||||
tgt_sys_len = len(tgt_data)
|
||||
@@ -628,6 +637,10 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||
system_type, system_device = common.GetTypeAndDevice("/system", OPTIONS.info_dict)
|
||||
system_patch = common.MakeSystemPatch(src_file, tgt_file)
|
||||
system_patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
|
||||
src_mapfilename = system_patch.name + ".src.map"
|
||||
common.ZipWriteStr(output_zip, src_mapfilename, src_mapdata)
|
||||
tgt_mapfilename = system_patch.name + ".tgt.map"
|
||||
common.ZipWriteStr(output_zip, tgt_mapfilename, tgt_mapdata)
|
||||
|
||||
AppendAssertions(script, OPTIONS.target_info_dict)
|
||||
device_specific.IncrementalOTA_Assertions()
|
||||
@@ -713,9 +726,8 @@ else
|
||||
|
||||
script.Print("Patching system image...")
|
||||
script.Syspatch(system_device,
|
||||
OPTIONS.info_dict["system_size"],
|
||||
tgt_sys_sha1,
|
||||
src_sys_sha1,
|
||||
tgt_mapfilename, tgt_sys_sha1,
|
||||
src_mapfilename, src_sys_sha1,
|
||||
system_patch.name)
|
||||
|
||||
if OPTIONS.two_step:
|
||||
@@ -1249,6 +1261,9 @@ def main(argv):
|
||||
OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
|
||||
OPTIONS.target_info_dict = OPTIONS.info_dict
|
||||
OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
|
||||
if "selinux_fc" in OPTIONS.source_info_dict:
|
||||
OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
|
||||
"file_contexts")
|
||||
if OPTIONS.package_key is None:
|
||||
OPTIONS.package_key = OPTIONS.source_info_dict.get(
|
||||
"default_system_dev_certificate",
|
||||
|
148
tools/releasetools/simg_map.py
Normal file
148
tools/releasetools/simg_map.py
Normal file
@@ -0,0 +1,148 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# Copyright (C) 2012 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.
|
||||
|
||||
from __future__ import print_function
|
||||
import getopt, posixpath, signal, struct, sys
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 4:
|
||||
print("No sparse_image_file specified")
|
||||
usage(me)
|
||||
|
||||
sparse_fn = sys.argv[1]
|
||||
unsparse_fn = sys.argv[2]
|
||||
map_file = sys.argv[3]
|
||||
mapped_unsparse_fn = sys.argv[4]
|
||||
|
||||
return ComputeMap(sparse_fn, unsparse_fn, map_file, mapped_unsparse_fn)
|
||||
|
||||
|
||||
def ComputeMap(sparse_fn, unsparse_fn, map_file, mapped_unsparse_fn):
|
||||
care_map = []
|
||||
|
||||
with open(sparse_fn, "rb") as FH:
|
||||
header_bin = FH.read(28)
|
||||
header = struct.unpack("<I4H4I", header_bin)
|
||||
|
||||
magic = header[0]
|
||||
major_version = header[1]
|
||||
minor_version = header[2]
|
||||
file_hdr_sz = header[3]
|
||||
chunk_hdr_sz = header[4]
|
||||
blk_sz = header[5]
|
||||
total_blks = header[6]
|
||||
total_chunks = header[7]
|
||||
image_checksum = header[8]
|
||||
|
||||
if magic != 0xED26FF3A:
|
||||
print("%s: %s: Magic should be 0xED26FF3A but is 0x%08X"
|
||||
% (me, path, magic))
|
||||
return 1
|
||||
if major_version != 1 or minor_version != 0:
|
||||
print("%s: %s: I only know about version 1.0, but this is version %u.%u"
|
||||
% (me, path, major_version, minor_version))
|
||||
return 1
|
||||
if file_hdr_sz != 28:
|
||||
print("%s: %s: The file header size was expected to be 28, but is %u."
|
||||
% (me, path, file_hdr_sz))
|
||||
return 1
|
||||
if chunk_hdr_sz != 12:
|
||||
print("%s: %s: The chunk header size was expected to be 12, but is %u."
|
||||
% (me, path, chunk_hdr_sz))
|
||||
return 1
|
||||
|
||||
print("%s: Total of %u %u-byte output blocks in %u input chunks."
|
||||
% (sparse_fn, total_blks, blk_sz, total_chunks))
|
||||
|
||||
offset = 0
|
||||
for i in range(total_chunks):
|
||||
header_bin = FH.read(12)
|
||||
header = struct.unpack("<2H2I", header_bin)
|
||||
chunk_type = header[0]
|
||||
reserved1 = header[1]
|
||||
chunk_sz = header[2]
|
||||
total_sz = header[3]
|
||||
data_sz = total_sz - 12
|
||||
|
||||
if chunk_type == 0xCAC1:
|
||||
if data_sz != (chunk_sz * blk_sz):
|
||||
print("Raw chunk input size (%u) does not match output size (%u)"
|
||||
% (data_sz, chunk_sz * blk_sz))
|
||||
return 1
|
||||
else:
|
||||
care_map.append((1, chunk_sz))
|
||||
FH.seek(data_sz, 1)
|
||||
|
||||
elif chunk_type == 0xCAC2:
|
||||
print("Fill chunks are not supported")
|
||||
return 1
|
||||
|
||||
elif chunk_type == 0xCAC3:
|
||||
if data_sz != 0:
|
||||
print("Don't care chunk input size is non-zero (%u)" % (data_sz))
|
||||
return 1
|
||||
else:
|
||||
care_map.append((0, chunk_sz))
|
||||
|
||||
elif chunk_type == 0xCAC4:
|
||||
print("CRC32 chunks are not supported")
|
||||
|
||||
else:
|
||||
print("Unknown chunk type 0x%04X not supported" % (chunk_type,))
|
||||
return 1
|
||||
|
||||
offset += chunk_sz
|
||||
|
||||
if total_blks != offset:
|
||||
print("The header said we should have %u output blocks, but we saw %u"
|
||||
% (total_blks, offset))
|
||||
|
||||
junk_len = len(FH.read())
|
||||
if junk_len:
|
||||
print("There were %u bytes of extra data at the end of the file."
|
||||
% (junk_len))
|
||||
return 1
|
||||
|
||||
last_kind = None
|
||||
new_care_map = []
|
||||
for kind, size in care_map:
|
||||
if kind != last_kind:
|
||||
new_care_map.append((kind, size))
|
||||
last_kind = kind
|
||||
else:
|
||||
new_care_map[-1] = (kind, new_care_map[-1][1] + size)
|
||||
|
||||
if new_care_map[0][0] == 0:
|
||||
new_care_map.insert(0, (1, 0))
|
||||
if len(new_care_map) % 2:
|
||||
new_care_map.append((0, 0))
|
||||
|
||||
with open(map_file, "w") as fmap:
|
||||
fmap.write("%d\n%d\n" % (blk_sz, len(new_care_map)))
|
||||
for _, sz in new_care_map:
|
||||
fmap.write("%d\n" % sz)
|
||||
|
||||
with open(unsparse_fn, "rb") as fin:
|
||||
with open(mapped_unsparse_fn, "wb") as fout:
|
||||
for k, sz in care_map:
|
||||
data = fin.read(sz * blk_sz)
|
||||
if k:
|
||||
fout.write(data)
|
||||
else:
|
||||
assert data == "\x00" * len(data)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Reference in New Issue
Block a user