forked from openkylin/platform_build
full support for OTA of vendor partitions
Make vendor partition a first-class member of the OTA system (for target_files that contain a VENDOR/ subdirectory). Build vendor images in a way that is compatible with block-based OTA. Support updating the vendor partition in both full and incremental, block and file OTAs. In most cases this is handled by refactoring the existing code to handle the system partition to handle either, and then calling it twice. Currently we don't support incremental OTAs from a target-files without a VENDOR subdirectory to one with one, or vice versa. To add or remove a vendor partition a full OTA will need to be done. Bug: 15544685 Change-Id: I9cb9a1267060bd9683a9bea19b43a26b5a43800d
This commit is contained in:
parent
4b445e8998
commit
c8b4e849f1
|
@ -1335,8 +1335,10 @@ endif
|
||||||
$(hide) ./build/tools/releasetools/make_recovery_patch $(zip_root) $(zip_root)
|
$(hide) ./build/tools/releasetools/make_recovery_patch $(zip_root) $(zip_root)
|
||||||
@# Zip everything up, preserving symlinks
|
@# Zip everything up, preserving symlinks
|
||||||
$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
|
$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
|
||||||
@# Run fs_config on all the system, boot ramdisk, and recovery ramdisk files in the zip, and save the output
|
@# Run fs_config on all the system, vendor, boot ramdisk,
|
||||||
|
@# and recovery ramdisk files in the zip, and save the output
|
||||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt
|
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt
|
||||||
|
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="VENDOR/" } /^VENDOR\// {print "vendor/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/vendor_filesystem_config.txt
|
||||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt
|
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt
|
||||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt
|
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt
|
||||||
$(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt)
|
$(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt)
|
||||||
|
|
|
@ -1008,14 +1008,14 @@ def XZ(path):
|
||||||
p.communicate()
|
p.communicate()
|
||||||
assert p.returncode == 0, "Couldn't compress patch"
|
assert p.returncode == 0, "Couldn't compress patch"
|
||||||
|
|
||||||
def MakeSystemPatch(source_file, target_file):
|
def MakePartitionPatch(source_file, target_file, partition):
|
||||||
with tempfile.NamedTemporaryFile() as output_file:
|
with tempfile.NamedTemporaryFile() as output_file:
|
||||||
XDelta3(source_file.name, target_file.name, output_file.name)
|
XDelta3(source_file.name, target_file.name, output_file.name)
|
||||||
XZ(output_file.name)
|
XZ(output_file.name)
|
||||||
with open(output_file.name + ".xz") as patch_file:
|
with open(output_file.name + ".xz") as patch_file:
|
||||||
patch_data = patch_file.read()
|
patch_data = patch_file.read()
|
||||||
os.unlink(patch_file.name)
|
os.unlink(patch_file.name)
|
||||||
return File("system.muimg.p", patch_data)
|
return File(partition + ".muimg.p", patch_data)
|
||||||
|
|
||||||
def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
|
def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
|
||||||
info_dict=None):
|
info_dict=None):
|
||||||
|
|
|
@ -203,11 +203,10 @@ class EdifyGenerator(object):
|
||||||
p.device, p.length, p.mount_point))
|
p.device, p.length, p.mount_point))
|
||||||
|
|
||||||
def WipeBlockDevice(self, partition):
|
def WipeBlockDevice(self, partition):
|
||||||
if partition != "/system":
|
if partition not in ("/system", "/vendor"):
|
||||||
raise ValueError(("WipeBlockDevice currently only works "
|
raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,))
|
||||||
"on /system, not %s\n") % (partition,))
|
|
||||||
fstab = self.info.get("fstab", None)
|
fstab = self.info.get("fstab", None)
|
||||||
size = self.info.get("system_size", None)
|
size = self.info.get(partition.lstrip("/") + "_size", None)
|
||||||
device = fstab[partition].device
|
device = fstab[partition].device
|
||||||
|
|
||||||
self.script.append('wipe_block_device("%s", %s);' % (device, size))
|
self.script.append('wipe_block_device("%s", %s);' % (device, size))
|
||||||
|
|
|
@ -59,9 +59,21 @@ def AddSystem(output_zip, sparse=True):
|
||||||
data = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict, sparse=sparse)
|
data = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict, sparse=sparse)
|
||||||
common.ZipWriteStr(output_zip, "system.img", data)
|
common.ZipWriteStr(output_zip, "system.img", data)
|
||||||
|
|
||||||
|
|
||||||
def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
|
def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
|
||||||
print "creating system.img..."
|
return CreateImage(input_dir, info_dict, "system",
|
||||||
|
sparse=sparse, map_file=map_file)
|
||||||
|
|
||||||
|
def AddVendor(output_zip, sparse=True):
|
||||||
|
data = BuildVendor(OPTIONS.input_tmp, OPTIONS.info_dict, sparse=sparse)
|
||||||
|
common.ZipWriteStr(output_zip, "vendor.img", data)
|
||||||
|
|
||||||
|
def BuildVendor(input_dir, info_dict, sparse=True, map_file=None):
|
||||||
|
return CreateImage(input_dir, info_dict, "vendor",
|
||||||
|
sparse=sparse, map_file=map_file)
|
||||||
|
|
||||||
|
|
||||||
|
def CreateImage(input_dir, info_dict, what, sparse=True, map_file=None):
|
||||||
|
print "creating " + what + ".img..."
|
||||||
|
|
||||||
img = tempfile.NamedTemporaryFile()
|
img = tempfile.NamedTemporaryFile()
|
||||||
|
|
||||||
|
@ -69,8 +81,8 @@ def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
|
||||||
# mkyaffs2image. It wants "system" but we have a directory named
|
# mkyaffs2image. It wants "system" but we have a directory named
|
||||||
# "SYSTEM", so create a symlink.
|
# "SYSTEM", so create a symlink.
|
||||||
try:
|
try:
|
||||||
os.symlink(os.path.join(input_dir, "SYSTEM"),
|
os.symlink(os.path.join(input_dir, what.upper()),
|
||||||
os.path.join(input_dir, "system"))
|
os.path.join(input_dir, what))
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
# bogus error on my mac version?
|
# bogus error on my mac version?
|
||||||
# File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem
|
# File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem
|
||||||
|
@ -79,22 +91,28 @@ def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
|
||||||
if (e.errno == errno.EEXIST):
|
if (e.errno == errno.EEXIST):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
image_props = build_image.ImagePropFromGlobalDict(info_dict, "system")
|
image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
|
||||||
fstab = info_dict["fstab"]
|
fstab = info_dict["fstab"]
|
||||||
if fstab:
|
if fstab:
|
||||||
image_props["fs_type" ] = fstab["/system"].fs_type
|
image_props["fs_type" ] = fstab["/" + what].fs_type
|
||||||
|
|
||||||
fs_config = os.path.join(input_dir, "META/filesystem_config.txt")
|
if what == "system":
|
||||||
|
fs_config_prefix = ""
|
||||||
|
else:
|
||||||
|
fs_config_prefix = what + "_"
|
||||||
|
|
||||||
|
fs_config = os.path.join(
|
||||||
|
input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
|
||||||
if not os.path.exists(fs_config): fs_config = None
|
if not os.path.exists(fs_config): fs_config = None
|
||||||
|
|
||||||
fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts")
|
fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts")
|
||||||
if not os.path.exists(fc_config): fc_config = None
|
if not os.path.exists(fc_config): fc_config = None
|
||||||
|
|
||||||
succ = build_image.BuildImage(os.path.join(input_dir, "system"),
|
succ = build_image.BuildImage(os.path.join(input_dir, what),
|
||||||
image_props, img.name,
|
image_props, img.name,
|
||||||
fs_config=fs_config,
|
fs_config=fs_config,
|
||||||
fc_config=fc_config)
|
fc_config=fc_config)
|
||||||
assert succ, "build system.img image failed"
|
assert succ, "build " + what + ".img image failed"
|
||||||
|
|
||||||
mapdata = None
|
mapdata = None
|
||||||
|
|
||||||
|
@ -104,7 +122,7 @@ def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
|
||||||
else:
|
else:
|
||||||
success, name = build_image.UnsparseImage(img.name, replace=False)
|
success, name = build_image.UnsparseImage(img.name, replace=False)
|
||||||
if not success:
|
if not success:
|
||||||
assert False, "unsparsing system.img failed"
|
assert False, "unsparsing " + what + ".img failed"
|
||||||
|
|
||||||
if map_file:
|
if map_file:
|
||||||
mmap = tempfile.NamedTemporaryFile()
|
mmap = tempfile.NamedTemporaryFile()
|
||||||
|
@ -131,45 +149,6 @@ def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
|
||||||
return mapdata, data
|
return mapdata, data
|
||||||
|
|
||||||
|
|
||||||
def AddVendor(output_zip):
|
|
||||||
"""Turn the contents of VENDOR into vendor.img and store it in
|
|
||||||
output_zip."""
|
|
||||||
|
|
||||||
image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
|
|
||||||
"vendor")
|
|
||||||
# The build system has to explicitly request for vendor.img.
|
|
||||||
if "fs_type" not in image_props:
|
|
||||||
return
|
|
||||||
|
|
||||||
print "creating vendor.img..."
|
|
||||||
|
|
||||||
img = tempfile.NamedTemporaryFile()
|
|
||||||
|
|
||||||
# The name of the directory it is making an image out of matters to
|
|
||||||
# mkyaffs2image. It wants "vendor" but we have a directory named
|
|
||||||
# "VENDOR", so create a symlink or an empty directory if VENDOR does not
|
|
||||||
# exist.
|
|
||||||
if not os.path.exists(os.path.join(OPTIONS.input_tmp, "vendor")):
|
|
||||||
if os.path.exists(os.path.join(OPTIONS.input_tmp, "VENDOR")):
|
|
||||||
os.symlink(os.path.join(OPTIONS.input_tmp, "VENDOR"),
|
|
||||||
os.path.join(OPTIONS.input_tmp, "vendor"))
|
|
||||||
else:
|
|
||||||
os.mkdir(os.path.join(OPTIONS.input_tmp, "vendor"))
|
|
||||||
|
|
||||||
img = tempfile.NamedTemporaryFile()
|
|
||||||
|
|
||||||
fstab = OPTIONS.info_dict["fstab"]
|
|
||||||
if fstab:
|
|
||||||
image_props["fs_type" ] = fstab["/vendor"].fs_type
|
|
||||||
succ = build_image.BuildImage(os.path.join(OPTIONS.input_tmp, "vendor"),
|
|
||||||
image_props, img.name)
|
|
||||||
assert succ, "build vendor.img image failed"
|
|
||||||
|
|
||||||
common.CheckSize(img.name, "vendor.img", OPTIONS.info_dict)
|
|
||||||
output_zip.write(img.name, "vendor.img")
|
|
||||||
img.close()
|
|
||||||
|
|
||||||
|
|
||||||
def AddUserdata(output_zip):
|
def AddUserdata(output_zip):
|
||||||
"""Create an empty userdata image and store it in output_zip."""
|
"""Create an empty userdata image and store it in output_zip."""
|
||||||
|
|
||||||
|
@ -287,10 +266,21 @@ def main(argv):
|
||||||
if recovery_image:
|
if recovery_image:
|
||||||
recovery_image.AddToZip(output_zip)
|
recovery_image.AddToZip(output_zip)
|
||||||
|
|
||||||
|
def banner(s):
|
||||||
|
print "\n\n++++ " + s + " ++++\n\n"
|
||||||
|
|
||||||
if not bootable_only:
|
if not bootable_only:
|
||||||
|
banner("AddSystem")
|
||||||
AddSystem(output_zip)
|
AddSystem(output_zip)
|
||||||
AddVendor(output_zip)
|
try:
|
||||||
|
input_zip.getinfo("VENDOR/")
|
||||||
|
banner("AddVendor")
|
||||||
|
AddVendor(output_zip)
|
||||||
|
except KeyError:
|
||||||
|
pass # no vendor partition for this device
|
||||||
|
banner("AddUserdata")
|
||||||
AddUserdata(output_zip)
|
AddUserdata(output_zip)
|
||||||
|
banner("AddCache")
|
||||||
AddCache(output_zip)
|
AddCache(output_zip)
|
||||||
CopyInfo(output_zip)
|
CopyInfo(output_zip)
|
||||||
|
|
||||||
|
|
|
@ -159,50 +159,21 @@ def ClosestFileMatch(src, tgtfiles, existing):
|
||||||
return result
|
return result
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class Item:
|
class ItemSet:
|
||||||
"""Items represent the metadata (user, group, mode) of files and
|
def __init__(self, partition, fs_config):
|
||||||
directories in the system image."""
|
self.partition = partition
|
||||||
ITEMS = {}
|
self.fs_config = fs_config
|
||||||
def __init__(self, name, dir=False):
|
self.ITEMS = {}
|
||||||
self.name = name
|
|
||||||
self.uid = None
|
|
||||||
self.gid = None
|
|
||||||
self.mode = None
|
|
||||||
self.selabel = None
|
|
||||||
self.capabilities = None
|
|
||||||
self.dir = dir
|
|
||||||
|
|
||||||
if name:
|
def Get(self, name, dir=False):
|
||||||
self.parent = Item.Get(os.path.dirname(name), dir=True)
|
if name not in self.ITEMS:
|
||||||
self.parent.children.append(self)
|
self.ITEMS[name] = Item(self, name, dir=dir)
|
||||||
else:
|
return self.ITEMS[name]
|
||||||
self.parent = None
|
|
||||||
if dir:
|
|
||||||
self.children = []
|
|
||||||
|
|
||||||
def Dump(self, indent=0):
|
|
||||||
if self.uid is not None:
|
|
||||||
print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
|
|
||||||
else:
|
|
||||||
print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
|
|
||||||
if self.dir:
|
|
||||||
print "%s%s" % (" "*indent, self.descendants)
|
|
||||||
print "%s%s" % (" "*indent, self.best_subtree)
|
|
||||||
for i in self.children:
|
|
||||||
i.Dump(indent=indent+1)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def Get(cls, name, dir=False):
|
|
||||||
if name not in cls.ITEMS:
|
|
||||||
cls.ITEMS[name] = Item(name, dir=dir)
|
|
||||||
return cls.ITEMS[name]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def GetMetadata(cls, input_zip):
|
|
||||||
|
|
||||||
|
def GetMetadata(self, input_zip):
|
||||||
# The target_files contains a record of what the uid,
|
# The target_files contains a record of what the uid,
|
||||||
# gid, and mode are supposed to be.
|
# gid, and mode are supposed to be.
|
||||||
output = input_zip.read("META/filesystem_config.txt")
|
output = input_zip.read(self.fs_config)
|
||||||
|
|
||||||
for line in output.split("\n"):
|
for line in output.split("\n"):
|
||||||
if not line: continue
|
if not line: continue
|
||||||
|
@ -220,7 +191,7 @@ class Item:
|
||||||
if key == "capabilities":
|
if key == "capabilities":
|
||||||
capabilities = value
|
capabilities = value
|
||||||
|
|
||||||
i = cls.ITEMS.get(name, None)
|
i = self.ITEMS.get(name, None)
|
||||||
if i is not None:
|
if i is not None:
|
||||||
i.uid = int(uid)
|
i.uid = int(uid)
|
||||||
i.gid = int(gid)
|
i.gid = int(gid)
|
||||||
|
@ -231,11 +202,44 @@ class Item:
|
||||||
i.children.sort(key=lambda i: i.name)
|
i.children.sort(key=lambda i: i.name)
|
||||||
|
|
||||||
# set metadata for the files generated by this script.
|
# set metadata for the files generated by this script.
|
||||||
i = cls.ITEMS.get("system/recovery-from-boot.p", None)
|
i = self.ITEMS.get("system/recovery-from-boot.p", None)
|
||||||
if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
|
if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
|
||||||
i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
|
i = self.ITEMS.get("system/etc/install-recovery.sh", None)
|
||||||
if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
|
if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
|
||||||
|
|
||||||
|
|
||||||
|
class Item:
|
||||||
|
"""Items represent the metadata (user, group, mode) of files and
|
||||||
|
directories in the system image."""
|
||||||
|
def __init__(self, itemset, name, dir=False):
|
||||||
|
self.itemset = itemset
|
||||||
|
self.name = name
|
||||||
|
self.uid = None
|
||||||
|
self.gid = None
|
||||||
|
self.mode = None
|
||||||
|
self.selabel = None
|
||||||
|
self.capabilities = None
|
||||||
|
self.dir = dir
|
||||||
|
|
||||||
|
if name:
|
||||||
|
self.parent = itemset.Get(os.path.dirname(name), dir=True)
|
||||||
|
self.parent.children.append(self)
|
||||||
|
else:
|
||||||
|
self.parent = None
|
||||||
|
if dir:
|
||||||
|
self.children = []
|
||||||
|
|
||||||
|
def Dump(self, indent=0):
|
||||||
|
if self.uid is not None:
|
||||||
|
print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
|
||||||
|
else:
|
||||||
|
print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
|
||||||
|
if self.dir:
|
||||||
|
print "%s%s" % (" "*indent, self.descendants)
|
||||||
|
print "%s%s" % (" "*indent, self.best_subtree)
|
||||||
|
for i in self.children:
|
||||||
|
i.Dump(indent=indent+1)
|
||||||
|
|
||||||
def CountChildMetadata(self):
|
def CountChildMetadata(self):
|
||||||
"""Count up the (uid, gid, mode, selabel, capabilities) tuples for
|
"""Count up the (uid, gid, mode, selabel, capabilities) tuples for
|
||||||
all children and determine the best strategy for using set_perm_recursive and
|
all children and determine the best strategy for using set_perm_recursive and
|
||||||
|
@ -320,9 +324,8 @@ class Item:
|
||||||
recurse(self, (-1, -1, -1, -1, None, None))
|
recurse(self, (-1, -1, -1, -1, None, None))
|
||||||
|
|
||||||
|
|
||||||
def CopySystemFiles(input_zip, output_zip=None,
|
def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
|
||||||
substitute=None):
|
"""Copies files for the partition in the input zip to the output
|
||||||
"""Copies files underneath system/ in the input zip to the output
|
|
||||||
zip. Populates the Item class with their metadata, and returns a
|
zip. Populates the Item class with their metadata, and returns a
|
||||||
list of symlinks. output_zip may be None, in which case the copy is
|
list of symlinks. output_zip may be None, in which case the copy is
|
||||||
skipped (but the other side effects still happen). substitute is an
|
skipped (but the other side effects still happen). substitute is an
|
||||||
|
@ -332,15 +335,17 @@ def CopySystemFiles(input_zip, output_zip=None,
|
||||||
|
|
||||||
symlinks = []
|
symlinks = []
|
||||||
|
|
||||||
|
partition = itemset.partition
|
||||||
|
|
||||||
for info in input_zip.infolist():
|
for info in input_zip.infolist():
|
||||||
if info.filename.startswith("SYSTEM/"):
|
if info.filename.startswith(partition.upper() + "/"):
|
||||||
basefilename = info.filename[7:]
|
basefilename = info.filename[7:]
|
||||||
if IsSymlink(info):
|
if IsSymlink(info):
|
||||||
symlinks.append((input_zip.read(info.filename),
|
symlinks.append((input_zip.read(info.filename),
|
||||||
"/system/" + basefilename))
|
"/" + partition + "/" + basefilename))
|
||||||
else:
|
else:
|
||||||
info2 = copy.copy(info)
|
info2 = copy.copy(info)
|
||||||
fn = info2.filename = "system/" + basefilename
|
fn = info2.filename = partition + "/" + basefilename
|
||||||
if substitute and fn in substitute and substitute[fn] is None:
|
if substitute and fn in substitute and substitute[fn] is None:
|
||||||
continue
|
continue
|
||||||
if output_zip is not None:
|
if output_zip is not None:
|
||||||
|
@ -350,9 +355,9 @@ def CopySystemFiles(input_zip, output_zip=None,
|
||||||
data = input_zip.read(info.filename)
|
data = input_zip.read(info.filename)
|
||||||
output_zip.writestr(info2, data)
|
output_zip.writestr(info2, data)
|
||||||
if fn.endswith("/"):
|
if fn.endswith("/"):
|
||||||
Item.Get(fn[:-1], dir=True)
|
itemset.Get(fn[:-1], dir=True)
|
||||||
else:
|
else:
|
||||||
Item.Get(fn, dir=False)
|
itemset.Get(fn, dir=False)
|
||||||
|
|
||||||
symlinks.sort()
|
symlinks.sort()
|
||||||
return symlinks
|
return symlinks
|
||||||
|
@ -387,6 +392,13 @@ def HasRecoveryPatch(target_files_zip):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def HasVendorPartition(target_files_zip):
|
||||||
|
try:
|
||||||
|
target_files_zip.getinfo("VENDOR/")
|
||||||
|
return True
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
def GetOemProperty(name, oem_props, oem_dict, info_dict):
|
def GetOemProperty(name, oem_props, oem_dict, info_dict):
|
||||||
if oem_props is not None and name in oem_props:
|
if oem_props is not None and name in oem_props:
|
||||||
return oem_dict[name]
|
return oem_dict[name]
|
||||||
|
@ -489,10 +501,13 @@ else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
|
||||||
|
|
||||||
if OPTIONS.wipe_user_data:
|
if OPTIONS.wipe_user_data:
|
||||||
system_progress -= 0.1
|
system_progress -= 0.1
|
||||||
|
if HasVendorPartition(input_zip):
|
||||||
|
system_progress -= 0.1
|
||||||
|
|
||||||
if "selinux_fc" in OPTIONS.info_dict:
|
if "selinux_fc" in OPTIONS.info_dict:
|
||||||
WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
|
WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
|
||||||
|
|
||||||
|
system_items = ItemSet("system", "META/filesystem_config.txt")
|
||||||
script.ShowProgress(system_progress, 0)
|
script.ShowProgress(system_progress, 0)
|
||||||
if block_based:
|
if block_based:
|
||||||
mapdata, data = img_from_target_files.BuildSystem(
|
mapdata, data = img_from_target_files.BuildSystem(
|
||||||
|
@ -510,7 +525,7 @@ else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
|
||||||
script.UnpackPackageDir("recovery", "/system")
|
script.UnpackPackageDir("recovery", "/system")
|
||||||
script.UnpackPackageDir("system", "/system")
|
script.UnpackPackageDir("system", "/system")
|
||||||
|
|
||||||
symlinks = CopySystemFiles(input_zip, output_zip)
|
symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
|
||||||
script.MakeSymlinks(symlinks)
|
script.MakeSymlinks(symlinks)
|
||||||
|
|
||||||
boot_img = common.GetBootableImage("boot.img", "boot.img",
|
boot_img = common.GetBootableImage("boot.img", "boot.img",
|
||||||
|
@ -519,13 +534,37 @@ else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
|
||||||
if not block_based:
|
if not block_based:
|
||||||
def output_sink(fn, data):
|
def output_sink(fn, data):
|
||||||
common.ZipWriteStr(output_zip, "recovery/" + fn, data)
|
common.ZipWriteStr(output_zip, "recovery/" + fn, data)
|
||||||
Item.Get("system/" + fn, dir=False)
|
system_items.Get("system/" + fn, dir=False)
|
||||||
|
|
||||||
common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
|
common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
|
||||||
recovery_img, boot_img)
|
recovery_img, boot_img)
|
||||||
|
|
||||||
Item.GetMetadata(input_zip)
|
system_items.GetMetadata(input_zip)
|
||||||
Item.Get("system").SetPermissions(script)
|
system_items.Get("system").SetPermissions(script)
|
||||||
|
|
||||||
|
if HasVendorPartition(input_zip):
|
||||||
|
vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
|
||||||
|
script.ShowProgress(0.1, 0)
|
||||||
|
|
||||||
|
if block_based:
|
||||||
|
mapdata, data = img_from_target_files.BuildVendor(
|
||||||
|
OPTIONS.input_tmp, OPTIONS.info_dict,
|
||||||
|
sparse=False, map_file=True)
|
||||||
|
|
||||||
|
common.ZipWriteStr(output_zip, "vendor.map", mapdata)
|
||||||
|
common.ZipWriteStr(output_zip, "vendor.muimg", data)
|
||||||
|
script.WipeBlockDevice("/vendor")
|
||||||
|
script.WriteRawImage("/vendor", "vendor.muimg", mapfn="vendor.map")
|
||||||
|
else:
|
||||||
|
script.FormatPartition("/vendor")
|
||||||
|
script.Mount("/vendor")
|
||||||
|
script.UnpackPackageDir("vendor", "/vendor")
|
||||||
|
|
||||||
|
symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
|
||||||
|
script.MakeSymlinks(symlinks)
|
||||||
|
|
||||||
|
vendor_items.GetMetadata(input_zip)
|
||||||
|
vendor_items.Get("vendor").SetPermissions(script)
|
||||||
|
|
||||||
common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
|
common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
|
||||||
common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
|
common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
|
||||||
|
@ -544,7 +583,7 @@ else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
|
||||||
if OPTIONS.wipe_user_data:
|
if OPTIONS.wipe_user_data:
|
||||||
script.ShowProgress(0.1, 10)
|
script.ShowProgress(0.1, 10)
|
||||||
script.FormatPartition("/data")
|
script.FormatPartition("/data")
|
||||||
|
|
||||||
if OPTIONS.two_step:
|
if OPTIONS.two_step:
|
||||||
script.AppendExtra("""
|
script.AppendExtra("""
|
||||||
set_stage("%(bcb_dev)s", "");
|
set_stage("%(bcb_dev)s", "");
|
||||||
|
@ -571,14 +610,15 @@ def WriteMetadata(metadata, output_zip):
|
||||||
"".join(["%s=%s\n" % kv
|
"".join(["%s=%s\n" % kv
|
||||||
for kv in sorted(metadata.iteritems())]))
|
for kv in sorted(metadata.iteritems())]))
|
||||||
|
|
||||||
def LoadSystemFiles(z):
|
def LoadPartitionFiles(z, partition):
|
||||||
"""Load all the files from SYSTEM/... in a given target-files
|
"""Load all the files from the given partition in a given target-files
|
||||||
ZipFile, and return a dict of {filename: File object}."""
|
ZipFile, and return a dict of {filename: File object}."""
|
||||||
out = {}
|
out = {}
|
||||||
|
prefix = partition.upper() + "/"
|
||||||
for info in z.infolist():
|
for info in z.infolist():
|
||||||
if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
|
if info.filename.startswith(prefix) and not IsSymlink(info):
|
||||||
basefilename = info.filename[7:]
|
basefilename = info.filename[7:]
|
||||||
fn = "system/" + basefilename
|
fn = partition + "/" + basefilename
|
||||||
data = z.read(info.filename)
|
data = z.read(info.filename)
|
||||||
out[fn] = common.File(fn, data)
|
out[fn] = common.File(fn, data)
|
||||||
return out
|
return out
|
||||||
|
@ -602,6 +642,45 @@ def AddToKnownPaths(filename, known_paths):
|
||||||
known_paths.add(path)
|
known_paths.add(path)
|
||||||
dirs.pop()
|
dirs.pop()
|
||||||
|
|
||||||
|
class BlockDifference:
|
||||||
|
def __init__(self, partition, builder, output_zip):
|
||||||
|
with tempfile.NamedTemporaryFile() as src_file:
|
||||||
|
with tempfile.NamedTemporaryFile() as tgt_file:
|
||||||
|
print "building source " + partition + " image..."
|
||||||
|
src_file = tempfile.NamedTemporaryFile()
|
||||||
|
src_mapdata, src_data = builder(OPTIONS.source_tmp,
|
||||||
|
OPTIONS.source_info_dict,
|
||||||
|
sparse=False, map_file=True)
|
||||||
|
|
||||||
|
self.src_sha1 = sha1(src_data).hexdigest()
|
||||||
|
print "source " + partition + " sha1:", self.src_sha1
|
||||||
|
src_file.write(src_data)
|
||||||
|
|
||||||
|
print "building target " + partition + " image..."
|
||||||
|
tgt_file = tempfile.NamedTemporaryFile()
|
||||||
|
tgt_mapdata, tgt_data = builder(OPTIONS.target_tmp,
|
||||||
|
OPTIONS.target_info_dict,
|
||||||
|
sparse=False, map_file=True)
|
||||||
|
self.tgt_sha1 = sha1(tgt_data).hexdigest()
|
||||||
|
print "target " + partition + " sha1:", self.tgt_sha1
|
||||||
|
tgt_len = len(tgt_data)
|
||||||
|
tgt_file.write(tgt_data)
|
||||||
|
|
||||||
|
system_type, self.device = common.GetTypeAndDevice("/" + partition,
|
||||||
|
OPTIONS.info_dict)
|
||||||
|
self.patch = common.MakePartitionPatch(src_file, tgt_file, partition)
|
||||||
|
|
||||||
|
TestBlockPatch(src_data, src_mapdata, self.patch.data,
|
||||||
|
tgt_mapdata, self.tgt_sha1)
|
||||||
|
src_data = None
|
||||||
|
tgt_data = None
|
||||||
|
|
||||||
|
self.patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
|
||||||
|
self.src_mapfilename = self.patch.name + ".src.map"
|
||||||
|
common.ZipWriteStr(output_zip, self.src_mapfilename, src_mapdata)
|
||||||
|
self.tgt_mapfilename = self.patch.name + ".tgt.map"
|
||||||
|
common.ZipWriteStr(output_zip, self.tgt_mapfilename, tgt_mapdata)
|
||||||
|
|
||||||
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
source_version = OPTIONS.source_info_dict["recovery_api_version"]
|
source_version = OPTIONS.source_info_dict["recovery_api_version"]
|
||||||
target_version = OPTIONS.target_info_dict["recovery_api_version"]
|
target_version = OPTIONS.target_info_dict["recovery_api_version"]
|
||||||
|
@ -648,40 +727,13 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
"/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
|
"/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
|
||||||
updating_recovery = (source_recovery.data != target_recovery.data)
|
updating_recovery = (source_recovery.data != target_recovery.data)
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile() as src_file:
|
system_diff = BlockDifference("system", img_from_target_files.BuildSystem,
|
||||||
with tempfile.NamedTemporaryFile() as tgt_file:
|
output_zip)
|
||||||
print "building source system image..."
|
if HasVendorPartition(target_zip):
|
||||||
src_file = tempfile.NamedTemporaryFile()
|
if not HasVendorPartition(source_zip):
|
||||||
src_mapdata, src_data = img_from_target_files.BuildSystem(
|
raise RuntimeError("can't generate incremental that adds /vendor")
|
||||||
OPTIONS.source_tmp, OPTIONS.source_info_dict,
|
vendor_diff = BlockDifference("vendor", img_from_target_files.BuildVendor,
|
||||||
sparse=False, map_file=True)
|
output_zip)
|
||||||
|
|
||||||
src_sys_sha1 = sha1(src_data).hexdigest()
|
|
||||||
print "source system sha1:", src_sys_sha1
|
|
||||||
src_file.write(src_data)
|
|
||||||
|
|
||||||
print "building target system image..."
|
|
||||||
tgt_file = tempfile.NamedTemporaryFile()
|
|
||||||
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)
|
|
||||||
tgt_file.write(tgt_data)
|
|
||||||
|
|
||||||
system_type, system_device = common.GetTypeAndDevice("/system", OPTIONS.info_dict)
|
|
||||||
system_patch = common.MakeSystemPatch(src_file, tgt_file)
|
|
||||||
|
|
||||||
TestBlockPatch(src_data, src_mapdata, system_patch.data, tgt_mapdata, tgt_sys_sha1)
|
|
||||||
src_data = None
|
|
||||||
tgt_data = None
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
|
oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
|
||||||
oem_dict = None
|
oem_dict = None
|
||||||
|
@ -774,12 +826,23 @@ else
|
||||||
|
|
||||||
device_specific.IncrementalOTA_InstallBegin()
|
device_specific.IncrementalOTA_InstallBegin()
|
||||||
|
|
||||||
|
if HasVendorPartition(target_zip):
|
||||||
|
script.Print("Patching vendor image...")
|
||||||
|
script.ShowProgress(0.1, 0)
|
||||||
|
script.Syspatch(vendor_diff.device,
|
||||||
|
vendor_diff.tgt_mapfilename, vendor_diff.tgt_sha1,
|
||||||
|
vendor_diff.src_mapfilename, vendor_diff.src_sha1,
|
||||||
|
vendor_diff.patch.name)
|
||||||
|
sys_progress = 0.8
|
||||||
|
else:
|
||||||
|
sys_progress = 0.9
|
||||||
|
|
||||||
script.Print("Patching system image...")
|
script.Print("Patching system image...")
|
||||||
script.ShowProgress(0.9, 0)
|
script.ShowProgress(sys_progress, 0)
|
||||||
script.Syspatch(system_device,
|
script.Syspatch(system_diff.device,
|
||||||
tgt_mapfilename, tgt_sys_sha1,
|
system_diff.tgt_mapfilename, system_diff.tgt_sha1,
|
||||||
src_mapfilename, src_sys_sha1,
|
system_diff.src_mapfilename, system_diff.src_sha1,
|
||||||
system_patch.name)
|
system_diff.patch.name)
|
||||||
|
|
||||||
if OPTIONS.two_step:
|
if OPTIONS.two_step:
|
||||||
common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
|
common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
|
||||||
|
@ -881,6 +944,127 @@ def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1):
|
||||||
print "test of system image patch succeeded"
|
print "test of system image patch succeeded"
|
||||||
|
|
||||||
|
|
||||||
|
class FileDifference:
|
||||||
|
def __init__(self, partition, source_zip, target_zip, output_zip):
|
||||||
|
print "Loading target..."
|
||||||
|
self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
|
||||||
|
print "Loading source..."
|
||||||
|
self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
|
||||||
|
|
||||||
|
self.verbatim_targets = verbatim_targets = []
|
||||||
|
self.patch_list = patch_list = []
|
||||||
|
diffs = []
|
||||||
|
self.renames = renames = {}
|
||||||
|
known_paths = set()
|
||||||
|
largest_source_size = 0
|
||||||
|
|
||||||
|
matching_file_cache = {}
|
||||||
|
for fn, sf in source_data.items():
|
||||||
|
assert fn == sf.name
|
||||||
|
matching_file_cache["path:" + fn] = sf
|
||||||
|
if fn in target_data.keys():
|
||||||
|
AddToKnownPaths(fn, known_paths)
|
||||||
|
# Only allow eligibility for filename/sha matching
|
||||||
|
# if there isn't a perfect path match.
|
||||||
|
if target_data.get(sf.name) is None:
|
||||||
|
matching_file_cache["file:" + fn.split("/")[-1]] = sf
|
||||||
|
matching_file_cache["sha:" + sf.sha1] = sf
|
||||||
|
|
||||||
|
for fn in sorted(target_data.keys()):
|
||||||
|
tf = target_data[fn]
|
||||||
|
assert fn == tf.name
|
||||||
|
sf = ClosestFileMatch(tf, matching_file_cache, renames)
|
||||||
|
if sf is not None and sf.name != tf.name:
|
||||||
|
print "File has moved from " + sf.name + " to " + tf.name
|
||||||
|
renames[sf.name] = tf
|
||||||
|
|
||||||
|
if sf is None or fn in OPTIONS.require_verbatim:
|
||||||
|
# This file should be included verbatim
|
||||||
|
if fn in OPTIONS.prohibit_verbatim:
|
||||||
|
raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
|
||||||
|
print "send", fn, "verbatim"
|
||||||
|
tf.AddToZip(output_zip)
|
||||||
|
verbatim_targets.append((fn, tf.size))
|
||||||
|
if fn in target_data.keys():
|
||||||
|
AddToKnownPaths(fn, known_paths)
|
||||||
|
elif tf.sha1 != sf.sha1:
|
||||||
|
# File is different; consider sending as a patch
|
||||||
|
diffs.append(common.Difference(tf, sf))
|
||||||
|
else:
|
||||||
|
# Target file data identical to source (may still be renamed)
|
||||||
|
pass
|
||||||
|
|
||||||
|
common.ComputeDifferences(diffs)
|
||||||
|
|
||||||
|
for diff in diffs:
|
||||||
|
tf, sf, d = diff.GetPatch()
|
||||||
|
path = "/".join(tf.name.split("/")[:-1])
|
||||||
|
if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
|
||||||
|
path not in known_paths:
|
||||||
|
# patch is almost as big as the file; don't bother patching
|
||||||
|
# or a patch + rename cannot take place due to the target
|
||||||
|
# directory not existing
|
||||||
|
tf.AddToZip(output_zip)
|
||||||
|
verbatim_targets.append((tf.name, tf.size))
|
||||||
|
if sf.name in renames:
|
||||||
|
del renames[sf.name]
|
||||||
|
AddToKnownPaths(tf.name, known_paths)
|
||||||
|
else:
|
||||||
|
common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
|
||||||
|
patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
|
||||||
|
largest_source_size = max(largest_source_size, sf.size)
|
||||||
|
|
||||||
|
self.largest_source_size = largest_source_size
|
||||||
|
|
||||||
|
def EmitVerification(self, script):
|
||||||
|
so_far = 0
|
||||||
|
for tf, sf, size, patch_sha in self.patch_list:
|
||||||
|
if tf.name != sf.name:
|
||||||
|
script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
|
||||||
|
script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
|
||||||
|
so_far += sf.size
|
||||||
|
return so_far
|
||||||
|
|
||||||
|
def RemoveUnneededFiles(self, script, extras=()):
|
||||||
|
script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
|
||||||
|
["/"+i for i in sorted(self.source_data)
|
||||||
|
if i not in self.target_data and
|
||||||
|
i not in self.renames] +
|
||||||
|
list(extras))
|
||||||
|
|
||||||
|
def TotalPatchSize(self):
|
||||||
|
return sum(i[1].size for i in self.patch_list)
|
||||||
|
|
||||||
|
def EmitPatches(self, script, total_patch_size, so_far):
|
||||||
|
self.deferred_patch_list = deferred_patch_list = []
|
||||||
|
for item in self.patch_list:
|
||||||
|
tf, sf, size, _ = item
|
||||||
|
if tf.name == "system/build.prop":
|
||||||
|
deferred_patch_list.append(item)
|
||||||
|
continue
|
||||||
|
if (sf.name != tf.name):
|
||||||
|
script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
|
||||||
|
script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
|
||||||
|
so_far += tf.size
|
||||||
|
script.SetProgress(so_far / total_patch_size)
|
||||||
|
return so_far
|
||||||
|
|
||||||
|
def EmitDeferredPatches(self, script):
|
||||||
|
for item in self.deferred_patch_list:
|
||||||
|
tf, sf, size, _ = item
|
||||||
|
script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
|
||||||
|
script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
|
||||||
|
|
||||||
|
def EmitRenames(self, script):
|
||||||
|
if len(self.renames) > 0:
|
||||||
|
script.Print("Renaming files...")
|
||||||
|
for src, tgt in self.renames.iteritems():
|
||||||
|
print "Renaming " + src + " to " + tgt.name
|
||||||
|
script.RenameFile(src, tgt.name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
target_has_recovery_patch = HasRecoveryPatch(target_zip)
|
target_has_recovery_patch = HasRecoveryPatch(target_zip)
|
||||||
source_has_recovery_patch = HasRecoveryPatch(source_zip)
|
source_has_recovery_patch = HasRecoveryPatch(source_zip)
|
||||||
|
@ -923,75 +1107,13 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
info_dict=OPTIONS.info_dict)
|
info_dict=OPTIONS.info_dict)
|
||||||
|
|
||||||
print "Loading target..."
|
system_diff = FileDifference("system", source_zip, target_zip, output_zip)
|
||||||
target_data = LoadSystemFiles(target_zip)
|
|
||||||
print "Loading source..."
|
|
||||||
source_data = LoadSystemFiles(source_zip)
|
|
||||||
|
|
||||||
verbatim_targets = []
|
|
||||||
patch_list = []
|
|
||||||
diffs = []
|
|
||||||
renames = {}
|
|
||||||
known_paths = set()
|
|
||||||
largest_source_size = 0
|
|
||||||
|
|
||||||
matching_file_cache = {}
|
|
||||||
for fn, sf in source_data.items():
|
|
||||||
assert fn == sf.name
|
|
||||||
matching_file_cache["path:" + fn] = sf
|
|
||||||
if fn in target_data.keys():
|
|
||||||
AddToKnownPaths(fn, known_paths)
|
|
||||||
# Only allow eligibility for filename/sha matching
|
|
||||||
# if there isn't a perfect path match.
|
|
||||||
if target_data.get(sf.name) is None:
|
|
||||||
matching_file_cache["file:" + fn.split("/")[-1]] = sf
|
|
||||||
matching_file_cache["sha:" + sf.sha1] = sf
|
|
||||||
|
|
||||||
for fn in sorted(target_data.keys()):
|
|
||||||
tf = target_data[fn]
|
|
||||||
assert fn == tf.name
|
|
||||||
sf = ClosestFileMatch(tf, matching_file_cache, renames)
|
|
||||||
if sf is not None and sf.name != tf.name:
|
|
||||||
print "File has moved from " + sf.name + " to " + tf.name
|
|
||||||
renames[sf.name] = tf
|
|
||||||
|
|
||||||
if sf is None or fn in OPTIONS.require_verbatim:
|
|
||||||
# This file should be included verbatim
|
|
||||||
if fn in OPTIONS.prohibit_verbatim:
|
|
||||||
raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
|
|
||||||
print "send", fn, "verbatim"
|
|
||||||
tf.AddToZip(output_zip)
|
|
||||||
verbatim_targets.append((fn, tf.size))
|
|
||||||
if fn in target_data.keys():
|
|
||||||
AddToKnownPaths(fn, known_paths)
|
|
||||||
elif tf.sha1 != sf.sha1:
|
|
||||||
# File is different; consider sending as a patch
|
|
||||||
diffs.append(common.Difference(tf, sf))
|
|
||||||
else:
|
|
||||||
# Target file data identical to source (may still be renamed)
|
|
||||||
pass
|
|
||||||
|
|
||||||
common.ComputeDifferences(diffs)
|
|
||||||
|
|
||||||
for diff in diffs:
|
|
||||||
tf, sf, d = diff.GetPatch()
|
|
||||||
path = "/".join(tf.name.split("/")[:-1])
|
|
||||||
if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
|
|
||||||
path not in known_paths:
|
|
||||||
# patch is almost as big as the file; don't bother patching
|
|
||||||
# or a patch + rename cannot take place due to the target
|
|
||||||
# directory not existing
|
|
||||||
tf.AddToZip(output_zip)
|
|
||||||
verbatim_targets.append((tf.name, tf.size))
|
|
||||||
if sf.name in renames:
|
|
||||||
del renames[sf.name]
|
|
||||||
AddToKnownPaths(tf.name, known_paths)
|
|
||||||
else:
|
|
||||||
common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
|
|
||||||
patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
|
|
||||||
largest_source_size = max(largest_source_size, sf.size)
|
|
||||||
|
|
||||||
script.Mount("/system")
|
script.Mount("/system")
|
||||||
|
if HasVendorPartition(target_zip):
|
||||||
|
vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
|
||||||
|
script.Mount("/vendor")
|
||||||
|
else:
|
||||||
|
vendor_diff = None
|
||||||
|
|
||||||
target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
|
target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
|
||||||
source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
|
source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
|
||||||
|
@ -1075,13 +1197,9 @@ else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
|
||||||
device_specific.IncrementalOTA_VerifyBegin()
|
device_specific.IncrementalOTA_VerifyBegin()
|
||||||
|
|
||||||
script.ShowProgress(0.1, 0)
|
script.ShowProgress(0.1, 0)
|
||||||
so_far = 0
|
so_far = system_diff.EmitVerification(script)
|
||||||
|
if vendor_diff:
|
||||||
for tf, sf, size, patch_sha in patch_list:
|
so_far += vendor_diff.EmitVerification(script)
|
||||||
if tf.name != sf.name:
|
|
||||||
script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
|
|
||||||
script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
|
|
||||||
so_far += sf.size
|
|
||||||
|
|
||||||
if updating_boot:
|
if updating_boot:
|
||||||
d = common.Difference(target_boot, source_boot)
|
d = common.Difference(target_boot, source_boot)
|
||||||
|
@ -1099,8 +1217,12 @@ else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
|
||||||
target_boot.size, target_boot.sha1))
|
target_boot.size, target_boot.sha1))
|
||||||
so_far += source_boot.size
|
so_far += source_boot.size
|
||||||
|
|
||||||
if patch_list or updating_recovery or updating_boot:
|
size = []
|
||||||
script.CacheFreeSpaceCheck(largest_source_size)
|
if system_diff.patch_list: size.append(system_diff.largest_source_size)
|
||||||
|
if vendor_diff:
|
||||||
|
if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
|
||||||
|
if size or updating_recovery or updating_boot:
|
||||||
|
script.CacheFreeSpaceCheck(max(size))
|
||||||
|
|
||||||
device_specific.IncrementalOTA_VerifyEnd()
|
device_specific.IncrementalOTA_VerifyEnd()
|
||||||
|
|
||||||
|
@ -1122,30 +1244,22 @@ else
|
||||||
print "writing full boot image (forced by two-step mode)"
|
print "writing full boot image (forced by two-step mode)"
|
||||||
|
|
||||||
script.Print("Removing unneeded files...")
|
script.Print("Removing unneeded files...")
|
||||||
script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
|
system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
|
||||||
["/"+i for i in sorted(source_data)
|
if vendor_diff:
|
||||||
if i not in target_data and
|
vendor_diff.RemoveUnneededFiles(script)
|
||||||
i not in renames] +
|
|
||||||
["/system/recovery.img"])
|
|
||||||
|
|
||||||
script.ShowProgress(0.8, 0)
|
script.ShowProgress(0.8, 0)
|
||||||
total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
|
total_patch_size = 1.0 + system_diff.TotalPatchSize()
|
||||||
|
if vendor_diff:
|
||||||
|
total_patch_size += vendor_diff.TotalPatchSize()
|
||||||
if updating_boot:
|
if updating_boot:
|
||||||
total_patch_size += target_boot.size
|
total_patch_size += target_boot.size
|
||||||
so_far = 0
|
|
||||||
|
|
||||||
script.Print("Patching system files...")
|
script.Print("Patching system files...")
|
||||||
deferred_patch_list = []
|
so_far = system_diff.EmitPatches(script, total_patch_size, 0)
|
||||||
for item in patch_list:
|
if vendor_diff:
|
||||||
tf, sf, size, _ = item
|
script.Print("Patching vendor files...")
|
||||||
if tf.name == "system/build.prop":
|
so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
|
||||||
deferred_patch_list.append(item)
|
|
||||||
continue
|
|
||||||
if (sf.name != tf.name):
|
|
||||||
script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
|
|
||||||
script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
|
|
||||||
so_far += tf.size
|
|
||||||
script.SetProgress(so_far / total_patch_size)
|
|
||||||
|
|
||||||
if not OPTIONS.two_step:
|
if not OPTIONS.two_step:
|
||||||
if updating_boot:
|
if updating_boot:
|
||||||
|
@ -1166,6 +1280,10 @@ else
|
||||||
else:
|
else:
|
||||||
print "boot image unchanged; skipping."
|
print "boot image unchanged; skipping."
|
||||||
|
|
||||||
|
system_items = ItemSet("system", "META/filesystem_config.txt")
|
||||||
|
if vendor_diff:
|
||||||
|
vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
|
||||||
|
|
||||||
if updating_recovery:
|
if updating_recovery:
|
||||||
# Recovery is generated as a patch using both the boot image
|
# Recovery is generated as a patch using both the boot image
|
||||||
# (which contains the same linux kernel as recovery) and the file
|
# (which contains the same linux kernel as recovery) and the file
|
||||||
|
@ -1179,7 +1297,7 @@ else
|
||||||
if not target_has_recovery_patch:
|
if not target_has_recovery_patch:
|
||||||
def output_sink(fn, data):
|
def output_sink(fn, data):
|
||||||
common.ZipWriteStr(output_zip, "recovery/" + fn, data)
|
common.ZipWriteStr(output_zip, "recovery/" + fn, data)
|
||||||
Item.Get("system/" + fn, dir=False)
|
system_items.Get("system/" + fn, dir=False)
|
||||||
|
|
||||||
common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
|
common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
|
||||||
target_recovery, target_boot)
|
target_recovery, target_boot)
|
||||||
|
@ -1191,16 +1309,24 @@ else
|
||||||
|
|
||||||
script.ShowProgress(0.1, 10)
|
script.ShowProgress(0.1, 10)
|
||||||
|
|
||||||
target_symlinks = CopySystemFiles(target_zip, None)
|
target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
|
||||||
|
if vendor_diff:
|
||||||
|
target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
|
||||||
|
|
||||||
|
temp_script = script.MakeTemporary()
|
||||||
|
system_items.GetMetadata(target_zip)
|
||||||
|
system_items.Get("system").SetPermissions(temp_script)
|
||||||
|
if vendor_diff:
|
||||||
|
vendor_items.GetMetadata(target_zip)
|
||||||
|
vendor_items.Get("vendor").SetPermissions(temp_script)
|
||||||
|
|
||||||
|
# Note that this call will mess up the trees of Items, so make sure
|
||||||
|
# we're done with them.
|
||||||
|
source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
|
||||||
|
if vendor_diff:
|
||||||
|
source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
|
||||||
|
|
||||||
target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
|
target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
|
||||||
temp_script = script.MakeTemporary()
|
|
||||||
Item.GetMetadata(target_zip)
|
|
||||||
Item.Get("system").SetPermissions(temp_script)
|
|
||||||
|
|
||||||
# Note that this call will mess up the tree of Items, so make sure
|
|
||||||
# we're done with it.
|
|
||||||
source_symlinks = CopySystemFiles(source_zip, None)
|
|
||||||
source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
|
source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
|
||||||
|
|
||||||
# Delete all the symlinks in source that aren't in target. This
|
# Delete all the symlinks in source that aren't in target. This
|
||||||
|
@ -1212,20 +1338,20 @@ else
|
||||||
to_delete.append(link)
|
to_delete.append(link)
|
||||||
script.DeleteFiles(to_delete)
|
script.DeleteFiles(to_delete)
|
||||||
|
|
||||||
if verbatim_targets:
|
if system_diff.verbatim_targets:
|
||||||
script.Print("Unpacking new files...")
|
script.Print("Unpacking new system files...")
|
||||||
script.UnpackPackageDir("system", "/system")
|
script.UnpackPackageDir("system", "/system")
|
||||||
|
if vendor_diff and vendor_diff.verbatim_targets:
|
||||||
|
script.Print("Unpacking new vendor files...")
|
||||||
|
script.UnpackPackageDir("vendor", "/vendor")
|
||||||
|
|
||||||
if updating_recovery and not target_has_recovery_patch:
|
if updating_recovery and not target_has_recovery_patch:
|
||||||
script.Print("Unpacking new recovery...")
|
script.Print("Unpacking new recovery...")
|
||||||
script.UnpackPackageDir("recovery", "/system")
|
script.UnpackPackageDir("recovery", "/system")
|
||||||
|
|
||||||
if len(renames) > 0:
|
system_diff.EmitRenames(script)
|
||||||
script.Print("Renaming files...")
|
if vendor_diff:
|
||||||
|
vendor_diff.EmitRenames(script)
|
||||||
for src in renames:
|
|
||||||
print "Renaming " + src + " to " + renames[src].name
|
|
||||||
script.RenameFile(src, renames[src].name)
|
|
||||||
|
|
||||||
script.Print("Symlinks and permissions...")
|
script.Print("Symlinks and permissions...")
|
||||||
|
|
||||||
|
@ -1256,10 +1382,7 @@ else
|
||||||
# device can still come up, it appears to be the old build and will
|
# device can still come up, it appears to be the old build and will
|
||||||
# get set the OTA package again to retry.
|
# get set the OTA package again to retry.
|
||||||
script.Print("Patching remaining system files...")
|
script.Print("Patching remaining system files...")
|
||||||
for item in deferred_patch_list:
|
system_diff.EmitDeferredPatches(script)
|
||||||
tf, sf, size, _ = item
|
|
||||||
script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
|
|
||||||
script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
|
|
||||||
|
|
||||||
if OPTIONS.wipe_user_data:
|
if OPTIONS.wipe_user_data:
|
||||||
script.Print("Erasing user data...")
|
script.Print("Erasing user data...")
|
||||||
|
|
Loading…
Reference in New Issue