blob: b963531ebaf24929c7d30fdd94aeb8b0482c3e6b [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
24 -b (--board_config) <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
27 -k (--package_key) <key>
28 Key to use to sign the package (default is
29 "build/target/product/security/testkey").
30
31 -i (--incremental_from) <file>
32 Generate an incremental OTA using the given target-files zip as
33 the starting build.
34
Doug Zongkerdbfaae52009-04-21 17:12:54 -070035 -w (--wipe_user_data)
36 Generate an OTA package that will wipe the user data partition
37 when installed.
38
Doug Zongker962069c2009-04-23 11:41:58 -070039 -n (--no_prereq)
40 Omit the timestamp prereq check normally included at the top of
41 the build scripts (used for developer OTA packages which
42 legitimately need to go back and forth).
43
Doug Zongker1c390a22009-05-14 19:06:36 -070044 -e (--extra_script) <file>
45 Insert the contents of file at the end of the update script.
46
Hristo Bojinovdafb0422010-08-26 14:35:16 -070047 -a (--aslr_mode) <on|off>
48 Specify whether to turn on ASLR for the package (on by default).
Doug Zongkereef39442009-04-02 12:14:19 -070049"""
50
51import sys
52
53if sys.hexversion < 0x02040000:
54 print >> sys.stderr, "Python 2.4 or newer is required."
55 sys.exit(1)
56
57import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070058import errno
Doug Zongkereef39442009-04-02 12:14:19 -070059import os
60import re
61import sha
62import subprocess
63import tempfile
64import time
65import zipfile
66
67import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070068import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070069
70OPTIONS = common.OPTIONS
71OPTIONS.package_key = "build/target/product/security/testkey"
72OPTIONS.incremental_source = None
73OPTIONS.require_verbatim = set()
74OPTIONS.prohibit_verbatim = set(("system/build.prop",))
75OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070076OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070077OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070078OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070079OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070080OPTIONS.worker_threads = 3
Doug Zongkereef39442009-04-02 12:14:19 -070081
82def MostPopularKey(d, default):
83 """Given a dict, return the key corresponding to the largest
84 value. Returns 'default' if the dict is empty."""
85 x = [(v, k) for (k, v) in d.iteritems()]
86 if not x: return default
87 x.sort()
88 return x[-1][1]
89
90
91def IsSymlink(info):
92 """Return true if the zipfile.ZipInfo object passed in represents a
93 symlink."""
94 return (info.external_attr >> 16) == 0120777
95
Hristo Bojinov96be7202010-08-02 10:26:17 -070096def IsRegular(info):
97 """Return true if the zipfile.ZipInfo object passed in represents a
98 symlink."""
99 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700100
101
102class Item:
103 """Items represent the metadata (user, group, mode) of files and
104 directories in the system image."""
105 ITEMS = {}
106 def __init__(self, name, dir=False):
107 self.name = name
108 self.uid = None
109 self.gid = None
110 self.mode = None
111 self.dir = dir
112
113 if name:
114 self.parent = Item.Get(os.path.dirname(name), dir=True)
115 self.parent.children.append(self)
116 else:
117 self.parent = None
118 if dir:
119 self.children = []
120
121 def Dump(self, indent=0):
122 if self.uid is not None:
123 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
124 else:
125 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
126 if self.dir:
127 print "%s%s" % (" "*indent, self.descendants)
128 print "%s%s" % (" "*indent, self.best_subtree)
129 for i in self.children:
130 i.Dump(indent=indent+1)
131
132 @classmethod
133 def Get(cls, name, dir=False):
134 if name not in cls.ITEMS:
135 cls.ITEMS[name] = Item(name, dir=dir)
136 return cls.ITEMS[name]
137
138 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700139 def GetMetadata(cls, input_zip):
140
141 try:
142 # See if the target_files contains a record of what the uid,
143 # gid, and mode is supposed to be.
144 output = input_zip.read("META/filesystem_config.txt")
145 except KeyError:
146 # Run the external 'fs_config' program to determine the desired
147 # uid, gid, and mode for every Item object. Note this uses the
148 # one in the client now, which might not be the same as the one
149 # used when this target_files was built.
150 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
151 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
152 suffix = { False: "", True: "/" }
153 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
154 for i in cls.ITEMS.itervalues() if i.name])
Doug Zongker3475d362010-03-17 16:39:30 -0700155 output, error = p.communicate(input)
Doug Zongker283e2a12010-03-15 17:52:32 -0700156 assert not error
Doug Zongkereef39442009-04-02 12:14:19 -0700157
158 for line in output.split("\n"):
159 if not line: continue
160 name, uid, gid, mode = line.split()
Doug Zongker283e2a12010-03-15 17:52:32 -0700161 i = cls.ITEMS.get(name, None)
162 if i is not None:
163 i.uid = int(uid)
164 i.gid = int(gid)
165 i.mode = int(mode, 8)
166 if i.dir:
167 i.children.sort(key=lambda i: i.name)
168
169 # set metadata for the files generated by this script.
170 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
171 if i: i.uid, i.gid, i.mode = 0, 0, 0644
172 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
173 if i: i.uid, i.gid, i.mode = 0, 0, 0544
Doug Zongkereef39442009-04-02 12:14:19 -0700174
175 def CountChildMetadata(self):
176 """Count up the (uid, gid, mode) tuples for all children and
177 determine the best strategy for using set_perm_recursive and
178 set_perm to correctly chown/chmod all the files to their desired
179 values. Recursively calls itself for all descendants.
180
181 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
182 all descendants of this node. (dmode or fmode may be None.) Also
183 sets the best_subtree of each directory Item to the (uid, gid,
184 dmode, fmode) tuple that will match the most descendants of that
185 Item.
186 """
187
188 assert self.dir
189 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
190 for i in self.children:
191 if i.dir:
192 for k, v in i.CountChildMetadata().iteritems():
193 d[k] = d.get(k, 0) + v
194 else:
195 k = (i.uid, i.gid, None, i.mode)
196 d[k] = d.get(k, 0) + 1
197
198 # Find the (uid, gid, dmode, fmode) tuple that matches the most
199 # descendants.
200
201 # First, find the (uid, gid) pair that matches the most
202 # descendants.
203 ug = {}
204 for (uid, gid, _, _), count in d.iteritems():
205 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
206 ug = MostPopularKey(ug, (0, 0))
207
208 # Now find the dmode and fmode that match the most descendants
209 # with that (uid, gid), and choose those.
210 best_dmode = (0, 0755)
211 best_fmode = (0, 0644)
212 for k, count in d.iteritems():
213 if k[:2] != ug: continue
214 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
215 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
216 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
217
218 return d
219
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700220 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700221 """Append set_perm/set_perm_recursive commands to 'script' to
222 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700223 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700224
225 self.CountChildMetadata()
226
227 def recurse(item, current):
228 # current is the (uid, gid, dmode, fmode) tuple that the current
229 # item (and all its children) have already been set to. We only
230 # need to issue set_perm/set_perm_recursive commands if we're
231 # supposed to be something different.
232 if item.dir:
233 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700234 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700235 current = item.best_subtree
236
237 if item.uid != current[0] or item.gid != current[1] or \
238 item.mode != current[2]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700239 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700240
241 for i in item.children:
242 recurse(i, current)
243 else:
244 if item.uid != current[0] or item.gid != current[1] or \
245 item.mode != current[3]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700246 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700247
248 recurse(self, (-1, -1, -1, -1))
249
250
251def CopySystemFiles(input_zip, output_zip=None,
252 substitute=None):
253 """Copies files underneath system/ in the input zip to the output
254 zip. Populates the Item class with their metadata, and returns a
Hristo Bojinov96be7202010-08-02 10:26:17 -0700255 list of symlinks as well as a list of files that will be retouched.
256 output_zip may be None, in which case the copy is skipped (but the
257 other side effects still happen). substitute is an optional dict
258 of {output filename: contents} to be output instead of certain input
259 files.
Doug Zongkereef39442009-04-02 12:14:19 -0700260 """
261
262 symlinks = []
Hristo Bojinov96be7202010-08-02 10:26:17 -0700263 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700264
265 for info in input_zip.infolist():
266 if info.filename.startswith("SYSTEM/"):
267 basefilename = info.filename[7:]
268 if IsSymlink(info):
269 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700270 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700271 else:
272 info2 = copy.copy(info)
273 fn = info2.filename = "system/" + basefilename
274 if substitute and fn in substitute and substitute[fn] is None:
275 continue
276 if output_zip is not None:
277 if substitute and fn in substitute:
278 data = substitute[fn]
279 else:
280 data = input_zip.read(info.filename)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700281 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
282 retouch_files.append(("/system/" + basefilename,
283 sha.sha(data).hexdigest()))
Doug Zongkereef39442009-04-02 12:14:19 -0700284 output_zip.writestr(info2, data)
285 if fn.endswith("/"):
286 Item.Get(fn[:-1], dir=True)
287 else:
288 Item.Get(fn, dir=False)
289
290 symlinks.sort()
Hristo Bojinov96be7202010-08-02 10:26:17 -0700291 return (symlinks, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700292
293
Doug Zongkereef39442009-04-02 12:14:19 -0700294def SignOutput(temp_zip_name, output_zip_name):
295 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
296 pw = key_passwords[OPTIONS.package_key]
297
Doug Zongker951495f2009-08-14 12:44:19 -0700298 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
299 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700300
301
Doug Zongkereef39442009-04-02 12:14:19 -0700302def AppendAssertions(script, input_zip):
Doug Zongkereef39442009-04-02 12:14:19 -0700303 device = GetBuildProp("ro.product.device", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700304 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700305
Doug Zongkereef39442009-04-02 12:14:19 -0700306
Doug Zongker486de122010-09-16 14:01:56 -0700307def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700308 """Generate a binary patch that creates the recovery image starting
309 with the boot image. (Most of the space in these images is just the
310 kernel, which is identical for the two, so the resulting patch
311 should be efficient.) Add it to the output zip, along with a shell
312 script that is run from init.rc on first boot to actually do the
313 patching and install the new recovery image.
314
315 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700316 corresponding images. info should be the dictionary returned by
317 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700318
319 Returns an Item for the shell script, which must be made
320 executable.
321 """
322
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700323 d = common.Difference(recovery_img, boot_img)
Doug Zongker761e6422009-09-25 10:45:39 -0700324 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700325 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700326 Item.Get("system/recovery-from-boot.p", dir=False)
327
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700328
329 fstab = OPTIONS.info_dict["fstab"]
330 if fstab:
331 # TODO: this is duplicated from edify_generator.py; fix.
332 PARTITION_TYPES = { "yaffs2": "MTD", "mtd": "MTD",
333 "ext4": "EMMC", "emmc": "EMMC" }
334
335 boot_type = PARTITION_TYPES[fstab["/boot"].fs_type]
336 boot_device = fstab["/boot"].device
337
338 recovery_type = PARTITION_TYPES[fstab["/recovery"].fs_type]
339 recovery_device = fstab["/recovery"].device
340 else:
341 # backwards compatibility for target files w/o recovery.fstab
342 boot_type = recovery_type = OPTIONS.info_dict["partition_type"]
343 boot_device = OPTIONS.info_dict.get("partition_path", "") + "boot"
344 recovery_device = OPTIONS.info_dict.get("partition_path", "") + "recovery"
345
Doug Zongker73ef8252009-07-23 15:12:53 -0700346 # Images with different content will have a different first page, so
347 # we check to see if this recovery has already been installed by
348 # testing just the first 2k.
349 HEADER_SIZE = 2048
350 header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
351 sh = """#!/system/bin/sh
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700352if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(header_size)d:%(header_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700353 log -t recovery "Installing new recovery image"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700354 applypatch %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
Doug Zongker73ef8252009-07-23 15:12:53 -0700355else
356 log -t recovery "Recovery image already installed"
357fi
358""" % { 'boot_size': boot_img.size,
359 'boot_sha1': boot_img.sha1,
360 'header_size': HEADER_SIZE,
361 'header_sha1': header_sha1,
362 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700363 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700364 'boot_type': boot_type,
365 'boot_device': boot_device,
366 'recovery_type': recovery_type,
367 'recovery_device': recovery_device,
Doug Zongker67369982010-07-07 13:53:32 -0700368 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700369 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700370 return Item.Get("system/etc/install-recovery.sh", dir=False)
371
372
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700373def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700374 # TODO: how to determine this? We don't know what version it will
375 # be installed on top of. For now, we expect the API just won't
376 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700377 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700378
Doug Zongker2ea21062010-04-28 16:05:21 -0700379 metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
380 "pre-device": GetBuildProp("ro.product.device", input_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700381 "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700382 }
383
Doug Zongker05d3dea2009-06-22 11:32:31 -0700384 device_specific = common.DeviceSpecificParams(
385 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700386 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700387 output_zip=output_zip,
388 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700389 input_tmp=OPTIONS.input_tmp,
390 metadata=metadata)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700391
Doug Zongker962069c2009-04-23 11:41:58 -0700392 if not OPTIONS.omit_prereq:
393 ts = GetBuildProp("ro.build.date.utc", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700394 script.AssertOlderBuild(ts)
Doug Zongkereef39442009-04-02 12:14:19 -0700395
396 AppendAssertions(script, input_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700397 device_specific.FullOTA_Assertions()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700398
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700399 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700400
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700401 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700402 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700403
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700404 script.FormatPartition("/system")
405 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700406 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700407 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700408
Hristo Bojinov96be7202010-08-02 10:26:17 -0700409 (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700410 script.MakeSymlinks(symlinks)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700411 if OPTIONS.aslr_mode:
412 script.RetouchBinaries(retouch_files)
413 else:
414 script.UndoRetouchBinaries(retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700415
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700416 boot_img = common.File("boot.img", common.BuildBootableImage(
Doug Zongker73ef8252009-07-23 15:12:53 -0700417 os.path.join(OPTIONS.input_tmp, "BOOT")))
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700418 recovery_img = common.File("recovery.img", common.BuildBootableImage(
Doug Zongker73ef8252009-07-23 15:12:53 -0700419 os.path.join(OPTIONS.input_tmp, "RECOVERY")))
Doug Zongker486de122010-09-16 14:01:56 -0700420 MakeRecoveryPatch(output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700421
Doug Zongker283e2a12010-03-15 17:52:32 -0700422 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700423 Item.Get("system").SetPermissions(script)
424
Doug Zongker37974732010-09-16 17:44:38 -0700425 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700426 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700427 script.ShowProgress(0.2, 0)
428
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700429 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700430 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700431
432 script.ShowProgress(0.1, 0)
433 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700434
Doug Zongker1c390a22009-05-14 19:06:36 -0700435 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700436 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700437
Doug Zongker14833602010-02-02 13:12:04 -0800438 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700439 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700440 WriteMetadata(metadata, output_zip)
441
442
443def WriteMetadata(metadata, output_zip):
444 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
445 "".join(["%s=%s\n" % kv
446 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700447
448
Doug Zongkereef39442009-04-02 12:14:19 -0700449
450
451def LoadSystemFiles(z):
452 """Load all the files from SYSTEM/... in a given target-files
453 ZipFile, and return a dict of {filename: File object}."""
454 out = {}
Hristo Bojinov96be7202010-08-02 10:26:17 -0700455 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700456 for info in z.infolist():
457 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700458 basefilename = info.filename[7:]
459 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700460 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700461 out[fn] = common.File(fn, data)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700462 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
463 retouch_files.append(("/system/" + basefilename,
464 out[fn].sha1))
465 return (out, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700466
467
Doug Zongkereef39442009-04-02 12:14:19 -0700468def GetBuildProp(property, z):
469 """Return the fingerprint of the build of a given target-files
470 ZipFile object."""
471 bp = z.read("SYSTEM/build.prop")
472 if not property:
473 return bp
474 m = re.search(re.escape(property) + r"=(.*)\n", bp)
475 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700476 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700477 return m.group(1).strip()
478
479
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700480def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700481 source_version = OPTIONS.source_info_dict["recovery_api_version"]
482 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerb984ae52010-09-16 23:13:11 -0700483 partition_type = OPTIONS.target_info_dict["partition_type"]
484 partition_path = OPTIONS.target_info_dict.get("partition_path", "")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700485
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700486 if source_version == 0:
487 print ("WARNING: generating edify script for a source that "
488 "can't install it.")
Doug Zongkerb984ae52010-09-16 23:13:11 -0700489 script = edify_generator.EdifyGenerator(source_version, OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700490
Doug Zongker2ea21062010-04-28 16:05:21 -0700491 metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700492 "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700493 }
494
Doug Zongker05d3dea2009-06-22 11:32:31 -0700495 device_specific = common.DeviceSpecificParams(
496 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800497 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700498 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800499 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700500 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700501 script=script,
502 metadata=metadata)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700503
Doug Zongkereef39442009-04-02 12:14:19 -0700504 print "Loading target..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700505 (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700506 print "Loading source..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700507 (source_data, source_retouch_files) = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700508
509 verbatim_targets = []
510 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700511 diffs = []
Doug Zongkereef39442009-04-02 12:14:19 -0700512 largest_source_size = 0
513 for fn in sorted(target_data.keys()):
514 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700515 assert fn == tf.name
Doug Zongkereef39442009-04-02 12:14:19 -0700516 sf = source_data.get(fn, None)
517
518 if sf is None or fn in OPTIONS.require_verbatim:
519 # This file should be included verbatim
520 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700521 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700522 print "send", fn, "verbatim"
523 tf.AddToZip(output_zip)
524 verbatim_targets.append((fn, tf.size))
525 elif tf.sha1 != sf.sha1:
526 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700527 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700528 else:
529 # Target file identical to source.
530 pass
531
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700532 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700533
534 for diff in diffs:
535 tf, sf, d = diff.GetPatch()
536 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
537 # patch is almost as big as the file; don't bother patching
538 tf.AddToZip(output_zip)
539 verbatim_targets.append((tf.name, tf.size))
540 else:
541 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
Doug Zongker5a482092010-02-17 16:09:18 -0800542 patch_list.append((tf.name, tf, sf, tf.size, sha.sha(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700543 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700544
545 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
546 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700547 metadata["pre-build"] = source_fp
548 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700549
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700550 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700551 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700552
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700553 source_boot = common.File("/tmp/boot.img",
554 common.BuildBootableImage(
555 os.path.join(OPTIONS.source_tmp, "BOOT")))
556 target_boot = common.File("/tmp/boot.img",
557 common.BuildBootableImage(
558 os.path.join(OPTIONS.target_tmp, "BOOT")))
Doug Zongker5da317e2009-06-02 13:38:17 -0700559 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700560
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700561 source_recovery = common.File("system/recovery.img",
562 common.BuildBootableImage(
563 os.path.join(OPTIONS.source_tmp, "RECOVERY")))
564 target_recovery = common.File("system/recovery.img",
565 common.BuildBootableImage(
566 os.path.join(OPTIONS.target_tmp, "RECOVERY")))
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700567 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700568
Doug Zongker881dd402009-09-20 14:03:55 -0700569 # Here's how we divide up the progress bar:
570 # 0.1 for verifying the start state (PatchCheck calls)
571 # 0.8 for applying patches (ApplyPatch calls)
572 # 0.1 for unpacking verbatim files, symlinking, and doing the
573 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700574
575 AppendAssertions(script, target_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700576 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700577
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700578 script.Print("Verifying current system...")
579
Doug Zongker881dd402009-09-20 14:03:55 -0700580 script.ShowProgress(0.1, 0)
581 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
582 if updating_boot:
583 total_verify_size += source_boot.size
584 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700585
Doug Zongker5a482092010-02-17 16:09:18 -0800586 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700587 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700588 so_far += sf.size
589 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700590
Doug Zongker5da317e2009-06-02 13:38:17 -0700591 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700592 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700593 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700594 print "boot target: %d source: %d diff: %d" % (
595 target_boot.size, source_boot.size, len(d))
596
Doug Zongker048e7ca2009-06-15 14:31:53 -0700597 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700598
Doug Zongker67369982010-07-07 13:53:32 -0700599 script.PatchCheck("%s:%sboot:%d:%s:%d:%s" %
600 (partition_type, partition_path,
601 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700602 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700603 so_far += source_boot.size
604 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700605
606 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700607 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800608
Doug Zongker05d3dea2009-06-22 11:32:31 -0700609 device_specific.IncrementalOTA_VerifyEnd()
610
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700611 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700612
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700613 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700614 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700615 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700616
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700617 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700618 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
619 ["/"+i for i in sorted(source_data)
Doug Zongker3b949f02009-08-24 10:24:32 -0700620 if i not in target_data] +
621 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700622
Doug Zongker881dd402009-09-20 14:03:55 -0700623 script.ShowProgress(0.8, 0)
624 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
625 if updating_boot:
626 total_patch_size += target_boot.size
627 so_far = 0
628
629 script.Print("Patching system files...")
Doug Zongker5a482092010-02-17 16:09:18 -0800630 for fn, tf, sf, size, _ in patch_list:
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800631 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700632 so_far += tf.size
633 script.SetProgress(so_far / total_patch_size)
634
Doug Zongkereef39442009-04-02 12:14:19 -0700635 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700636 # Produce the boot image by applying a patch to the current
637 # contents of the boot partition, and write it back to the
638 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700639 script.Print("Patching boot image...")
Doug Zongker67369982010-07-07 13:53:32 -0700640 script.ApplyPatch("%s:%sboot:%d:%s:%d:%s"
641 % (partition_type, partition_path,
642 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700643 target_boot.size, target_boot.sha1),
644 "-",
645 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800646 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700647 so_far += target_boot.size
648 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700649 print "boot image changed; including."
650 else:
651 print "boot image unchanged; skipping."
652
653 if updating_recovery:
Doug Zongker73ef8252009-07-23 15:12:53 -0700654 # Is it better to generate recovery as a patch from the current
655 # boot image, or from the previous recovery image? For large
656 # updates with significant kernel changes, probably the former.
657 # For small updates where the kernel hasn't changed, almost
658 # certainly the latter. We pick the first option. Future
659 # complicated schemes may let us effectively use both.
660 #
661 # A wacky possibility: as long as there is room in the boot
662 # partition, include the binaries and image files from recovery in
663 # the boot image (though not in the ramdisk) so they can be used
664 # as fodder for constructing the recovery image.
Doug Zongker486de122010-09-16 14:01:56 -0700665 MakeRecoveryPatch(output_zip, target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800666 script.DeleteFiles(["/system/recovery-from-boot.p",
667 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700668 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700669 else:
670 print "recovery image unchanged; skipping."
671
Doug Zongker881dd402009-09-20 14:03:55 -0700672 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700673
Hristo Bojinov96be7202010-08-02 10:26:17 -0700674 (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700675
676 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700677 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700678 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700679 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700680
681 # Note that this call will mess up the tree of Items, so make sure
682 # we're done with it.
Hristo Bojinov96be7202010-08-02 10:26:17 -0700683 (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700684 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
685
686 # Delete all the symlinks in source that aren't in target. This
687 # needs to happen before verbatim files are unpacked, in case a
688 # symlink in the source is replaced by a real file in the target.
689 to_delete = []
690 for dest, link in source_symlinks:
691 if link not in target_symlinks_d:
692 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700693 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700694
695 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700696 script.Print("Unpacking new files...")
697 script.UnpackPackageDir("system", "/system")
698
Doug Zongker42265392010-02-12 10:21:00 -0800699 if updating_recovery:
700 script.Print("Unpacking new recovery...")
701 script.UnpackPackageDir("recovery", "/system")
702
Doug Zongker05d3dea2009-06-22 11:32:31 -0700703 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700704
705 # Create all the symlinks that don't already exist, or point to
706 # somewhere different than what we want. Delete each symlink before
707 # creating it, since the 'symlink' command won't overwrite.
708 to_create = []
709 for dest, link in target_symlinks:
710 if link in source_symlinks_d:
711 if dest != source_symlinks_d[link]:
712 to_create.append((dest, link))
713 else:
714 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700715 script.DeleteFiles([i[1] for i in to_create])
716 script.MakeSymlinks(to_create)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700717 if OPTIONS.aslr_mode:
718 script.RetouchBinaries(target_retouch_files)
719 else:
720 script.UndoRetouchBinaries(target_retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700721
722 # Now that the symlinks are created, we can set all the
723 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700724 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700725
Doug Zongker881dd402009-09-20 14:03:55 -0700726 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700727 device_specific.IncrementalOTA_InstallEnd()
728
Doug Zongker1c390a22009-05-14 19:06:36 -0700729 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700730 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700731
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700732 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700733 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700734
735
736def main(argv):
737
738 def option_handler(o, a):
739 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700740 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700741 elif o in ("-k", "--package_key"):
742 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700743 elif o in ("-i", "--incremental_from"):
744 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700745 elif o in ("-w", "--wipe_user_data"):
746 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700747 elif o in ("-n", "--no_prereq"):
748 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700749 elif o in ("-e", "--extra_script"):
750 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700751 elif o in ("-a", "--aslr_mode"):
752 if a in ("on", "On", "true", "True", "yes", "Yes"):
753 OPTIONS.aslr_mode = True
754 else:
755 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700756 elif o in ("--worker_threads"):
757 OPTIONS.worker_threads = int(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700758 else:
759 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700760 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700761
762 args = common.ParseOptions(argv, __doc__,
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700763 extra_opts="b:k:i:d:wne:a:",
Doug Zongkereef39442009-04-02 12:14:19 -0700764 extra_long_opts=["board_config=",
765 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700766 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700767 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700768 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700769 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700770 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700771 "aslr_mode=",
772 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700773 extra_option_handler=option_handler)
774
775 if len(args) != 2:
776 common.Usage(__doc__)
777 sys.exit(1)
778
Doug Zongker1c390a22009-05-14 19:06:36 -0700779 if OPTIONS.extra_script is not None:
780 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
781
Doug Zongkereef39442009-04-02 12:14:19 -0700782 print "unzipping target target-files..."
783 OPTIONS.input_tmp = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700784
Doug Zongkereef39442009-04-02 12:14:19 -0700785 OPTIONS.target_tmp = OPTIONS.input_tmp
786 input_zip = zipfile.ZipFile(args[0], "r")
Doug Zongker37974732010-09-16 17:44:38 -0700787 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
788 if OPTIONS.verbose:
789 print "--- target info ---"
790 common.DumpInfoDict(OPTIONS.info_dict)
791
792 if OPTIONS.device_specific is None:
793 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
794 if OPTIONS.device_specific is not None:
795 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
796 print "using device-specific extensions in", OPTIONS.device_specific
797
Doug Zongkereef39442009-04-02 12:14:19 -0700798 if OPTIONS.package_key:
799 temp_zip_file = tempfile.NamedTemporaryFile()
800 output_zip = zipfile.ZipFile(temp_zip_file, "w",
801 compression=zipfile.ZIP_DEFLATED)
802 else:
803 output_zip = zipfile.ZipFile(args[1], "w",
804 compression=zipfile.ZIP_DEFLATED)
805
806 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700807 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700808 else:
809 print "unzipping source target-files..."
810 OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
811 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
Doug Zongker37974732010-09-16 17:44:38 -0700812 OPTIONS.target_info_dict = OPTIONS.info_dict
813 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
814 if OPTIONS.verbose:
815 print "--- source info ---"
816 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700817 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700818
819 output_zip.close()
820 if OPTIONS.package_key:
821 SignOutput(temp_zip_file.name, args[1])
822 temp_zip_file.close()
823
824 common.Cleanup()
825
826 print "done."
827
828
829if __name__ == '__main__':
830 try:
831 main(sys.argv[1:])
832 except common.ExternalError, e:
833 print
834 print " ERROR: %s" % (e,)
835 print
836 sys.exit(1)