blob: 73eccd453f27208bf63ebee0f177f69a5186ea42 [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>
25 Specifies a BoardConfig.mk file containing image max sizes
26 against which the generated image files are checked.
27
28 -k (--package_key) <key>
29 Key to use to sign the package (default is
30 "build/target/product/security/testkey").
31
32 -i (--incremental_from) <file>
33 Generate an incremental OTA using the given target-files zip as
34 the starting build.
35
Doug Zongkerdbfaae52009-04-21 17:12:54 -070036 -w (--wipe_user_data)
37 Generate an OTA package that will wipe the user data partition
38 when installed.
39
Doug Zongker962069c2009-04-23 11:41:58 -070040 -n (--no_prereq)
41 Omit the timestamp prereq check normally included at the top of
42 the build scripts (used for developer OTA packages which
43 legitimately need to go back and forth).
44
Doug Zongker1c390a22009-05-14 19:06:36 -070045 -e (--extra_script) <file>
46 Insert the contents of file at the end of the update script.
47
Doug Zongkerc494d7c2009-06-18 08:43:44 -070048 -m (--script_mode) <mode>
49 Specify 'amend' or 'edify' scripts, or 'auto' to pick
50 automatically (this is the default).
51
Doug Zongkereef39442009-04-02 12:14:19 -070052"""
53
54import sys
55
56if sys.hexversion < 0x02040000:
57 print >> sys.stderr, "Python 2.4 or newer is required."
58 sys.exit(1)
59
60import copy
61import os
62import re
63import sha
64import subprocess
65import tempfile
66import time
67import zipfile
68
69import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070070import amend_generator
71import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070072
73OPTIONS = common.OPTIONS
74OPTIONS.package_key = "build/target/product/security/testkey"
75OPTIONS.incremental_source = None
76OPTIONS.require_verbatim = set()
77OPTIONS.prohibit_verbatim = set(("system/build.prop",))
78OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070079OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070080OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070081OPTIONS.extra_script = None
Doug Zongkerc494d7c2009-06-18 08:43:44 -070082OPTIONS.script_mode = 'auto'
Doug Zongkereef39442009-04-02 12:14:19 -070083
84def MostPopularKey(d, default):
85 """Given a dict, return the key corresponding to the largest
86 value. Returns 'default' if the dict is empty."""
87 x = [(v, k) for (k, v) in d.iteritems()]
88 if not x: return default
89 x.sort()
90 return x[-1][1]
91
92
93def IsSymlink(info):
94 """Return true if the zipfile.ZipInfo object passed in represents a
95 symlink."""
96 return (info.external_attr >> 16) == 0120777
97
98
99
100class Item:
101 """Items represent the metadata (user, group, mode) of files and
102 directories in the system image."""
103 ITEMS = {}
104 def __init__(self, name, dir=False):
105 self.name = name
106 self.uid = None
107 self.gid = None
108 self.mode = None
109 self.dir = dir
110
111 if name:
112 self.parent = Item.Get(os.path.dirname(name), dir=True)
113 self.parent.children.append(self)
114 else:
115 self.parent = None
116 if dir:
117 self.children = []
118
119 def Dump(self, indent=0):
120 if self.uid is not None:
121 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
122 else:
123 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
124 if self.dir:
125 print "%s%s" % (" "*indent, self.descendants)
126 print "%s%s" % (" "*indent, self.best_subtree)
127 for i in self.children:
128 i.Dump(indent=indent+1)
129
130 @classmethod
131 def Get(cls, name, dir=False):
132 if name not in cls.ITEMS:
133 cls.ITEMS[name] = Item(name, dir=dir)
134 return cls.ITEMS[name]
135
136 @classmethod
137 def GetMetadata(cls):
138 """Run the external 'fs_config' program to determine the desired
139 uid, gid, and mode for every Item object."""
140 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
141 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
142 suffix = { False: "", True: "/" }
143 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
144 for i in cls.ITEMS.itervalues() if i.name])
145 output, error = p.communicate(input)
146 assert not error
147
148 for line in output.split("\n"):
149 if not line: continue
150 name, uid, gid, mode = line.split()
151 i = cls.ITEMS[name]
152 i.uid = int(uid)
153 i.gid = int(gid)
154 i.mode = int(mode, 8)
155 if i.dir:
156 i.children.sort(key=lambda i: i.name)
157
158 def CountChildMetadata(self):
159 """Count up the (uid, gid, mode) tuples for all children and
160 determine the best strategy for using set_perm_recursive and
161 set_perm to correctly chown/chmod all the files to their desired
162 values. Recursively calls itself for all descendants.
163
164 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
165 all descendants of this node. (dmode or fmode may be None.) Also
166 sets the best_subtree of each directory Item to the (uid, gid,
167 dmode, fmode) tuple that will match the most descendants of that
168 Item.
169 """
170
171 assert self.dir
172 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
173 for i in self.children:
174 if i.dir:
175 for k, v in i.CountChildMetadata().iteritems():
176 d[k] = d.get(k, 0) + v
177 else:
178 k = (i.uid, i.gid, None, i.mode)
179 d[k] = d.get(k, 0) + 1
180
181 # Find the (uid, gid, dmode, fmode) tuple that matches the most
182 # descendants.
183
184 # First, find the (uid, gid) pair that matches the most
185 # descendants.
186 ug = {}
187 for (uid, gid, _, _), count in d.iteritems():
188 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
189 ug = MostPopularKey(ug, (0, 0))
190
191 # Now find the dmode and fmode that match the most descendants
192 # with that (uid, gid), and choose those.
193 best_dmode = (0, 0755)
194 best_fmode = (0, 0644)
195 for k, count in d.iteritems():
196 if k[:2] != ug: continue
197 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
198 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
199 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
200
201 return d
202
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700203 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700204 """Append set_perm/set_perm_recursive commands to 'script' to
205 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700206 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700207
208 self.CountChildMetadata()
209
210 def recurse(item, current):
211 # current is the (uid, gid, dmode, fmode) tuple that the current
212 # item (and all its children) have already been set to. We only
213 # need to issue set_perm/set_perm_recursive commands if we're
214 # supposed to be something different.
215 if item.dir:
216 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700217 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700218 current = item.best_subtree
219
220 if item.uid != current[0] or item.gid != current[1] or \
221 item.mode != current[2]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700222 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700223
224 for i in item.children:
225 recurse(i, current)
226 else:
227 if item.uid != current[0] or item.gid != current[1] or \
228 item.mode != current[3]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700229 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700230
231 recurse(self, (-1, -1, -1, -1))
232
233
234def CopySystemFiles(input_zip, output_zip=None,
235 substitute=None):
236 """Copies files underneath system/ in the input zip to the output
237 zip. Populates the Item class with their metadata, and returns a
238 list of symlinks. output_zip may be None, in which case the copy is
239 skipped (but the other side effects still happen). substitute is an
240 optional dict of {output filename: contents} to be output instead of
241 certain input files.
242 """
243
244 symlinks = []
245
246 for info in input_zip.infolist():
247 if info.filename.startswith("SYSTEM/"):
248 basefilename = info.filename[7:]
249 if IsSymlink(info):
250 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700251 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700252 else:
253 info2 = copy.copy(info)
254 fn = info2.filename = "system/" + basefilename
255 if substitute and fn in substitute and substitute[fn] is None:
256 continue
257 if output_zip is not None:
258 if substitute and fn in substitute:
259 data = substitute[fn]
260 else:
261 data = input_zip.read(info.filename)
262 output_zip.writestr(info2, data)
263 if fn.endswith("/"):
264 Item.Get(fn[:-1], dir=True)
265 else:
266 Item.Get(fn, dir=False)
267
268 symlinks.sort()
269 return symlinks
270
271
Doug Zongkereef39442009-04-02 12:14:19 -0700272def SignOutput(temp_zip_name, output_zip_name):
273 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
274 pw = key_passwords[OPTIONS.package_key]
275
276 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw)
277
278
Doug Zongkereef39442009-04-02 12:14:19 -0700279def FixPermissions(script):
280 Item.GetMetadata()
281 root = Item.Get("system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700282 root.SetPermissions(script)
Doug Zongkereef39442009-04-02 12:14:19 -0700283
Doug Zongkereef39442009-04-02 12:14:19 -0700284
285def AppendAssertions(script, input_zip):
Doug Zongkereef39442009-04-02 12:14:19 -0700286 device = GetBuildProp("ro.product.device", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700287 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700288
289 info = input_zip.read("OTA/android-info.txt")
290 m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", info)
Doug Zongker9fc74c72009-06-23 16:27:38 -0700291 if m:
292 bootloaders = m.group(1).split("|")
293 script.AssertSomeBootloader(*bootloaders)
Doug Zongkereef39442009-04-02 12:14:19 -0700294
295
296def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700297 if OPTIONS.script_mode in ("amend", "auto"):
298 script = amend_generator.AmendGenerator()
299 else:
300 # TODO: how to determine this? We don't know what version it will
301 # be installed on top of. For now, we expect the API just won't
302 # change very often.
303 script = edify_generator.EdifyGenerator(1)
Doug Zongkereef39442009-04-02 12:14:19 -0700304
Doug Zongker962069c2009-04-23 11:41:58 -0700305 if not OPTIONS.omit_prereq:
306 ts = GetBuildProp("ro.build.date.utc", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700307 script.AssertOlderBuild(ts)
Doug Zongkereef39442009-04-02 12:14:19 -0700308
309 AppendAssertions(script, input_zip)
310
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700311 script.ShowProgress(0.1, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700312
Doug Zongker048e7ca2009-06-15 14:31:53 -0700313 common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700314 script.WriteFirmwareImage("radio", "radio.img")
315 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700316
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700317 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700318 script.FormatPartition("userdata")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700319
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700320 script.FormatPartition("system")
321 script.Mount("MTD", "system", "/system")
322 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700323
324 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700325 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700326
327 common.BuildAndAddBootableImage(os.path.join(OPTIONS.input_tmp, "RECOVERY"),
328 "system/recovery.img", output_zip)
329 Item.Get("system/recovery.img", dir=False)
330
331 FixPermissions(script)
332
333 common.AddBoot(output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700334 script.ShowProgress(0.2, 0)
335
336 script.WriteRawImage("boot", "boot.img")
337 script.ShowProgress(0.2, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700338
Doug Zongker1c390a22009-05-14 19:06:36 -0700339 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700340 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700341
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700342 script.AddToZip(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700343
344
345class File(object):
346 def __init__(self, name, data):
347 self.name = name
348 self.data = data
349 self.size = len(data)
350 self.sha1 = sha.sha(data).hexdigest()
351
352 def WriteToTemp(self):
353 t = tempfile.NamedTemporaryFile()
354 t.write(self.data)
355 t.flush()
356 return t
357
358 def AddToZip(self, z):
Doug Zongker048e7ca2009-06-15 14:31:53 -0700359 common.ZipWriteStr(z, self.name, self.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700360
361
362def LoadSystemFiles(z):
363 """Load all the files from SYSTEM/... in a given target-files
364 ZipFile, and return a dict of {filename: File object}."""
365 out = {}
366 for info in z.infolist():
367 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
368 fn = "system/" + info.filename[7:]
369 data = z.read(info.filename)
370 out[fn] = File(fn, data)
371 return out
372
373
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700374def Difference(tf, sf, diff_program):
375 """Return the patch (as a string of data) needed to turn sf into tf.
376 diff_program is the name of an external program (or list, if
377 additional arguments are desired) to run to generate the diff.
378 """
Doug Zongkereef39442009-04-02 12:14:19 -0700379
380 ttemp = tf.WriteToTemp()
381 stemp = sf.WriteToTemp()
382
383 ext = os.path.splitext(tf.name)[1]
384
385 try:
386 ptemp = tempfile.NamedTemporaryFile()
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700387 if isinstance(diff_program, list):
388 cmd = copy.copy(diff_program)
389 else:
390 cmd = [diff_program]
391 cmd.append(stemp.name)
392 cmd.append(ttemp.name)
393 cmd.append(ptemp.name)
394 p = common.Run(cmd)
Doug Zongkereef39442009-04-02 12:14:19 -0700395 _, err = p.communicate()
Doug Zongker5da317e2009-06-02 13:38:17 -0700396 if err or p.returncode != 0:
397 print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
398 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700399 diff = ptemp.read()
Doug Zongkereef39442009-04-02 12:14:19 -0700400 finally:
Doug Zongker5da317e2009-06-02 13:38:17 -0700401 ptemp.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700402 stemp.close()
403 ttemp.close()
404
405 return diff
406
407
408def GetBuildProp(property, z):
409 """Return the fingerprint of the build of a given target-files
410 ZipFile object."""
411 bp = z.read("SYSTEM/build.prop")
412 if not property:
413 return bp
414 m = re.search(re.escape(property) + r"=(.*)\n", bp)
415 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700416 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700417 return m.group(1).strip()
418
419
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700420def GetRecoveryAPIVersion(zip):
421 """Returns the version of the recovery API. Version 0 is the older
422 amend code (no separate binary)."""
423 try:
424 version = zip.read("META/recovery-api-version.txt")
425 return int(version)
426 except KeyError:
427 try:
428 # version one didn't have the recovery-api-version.txt file, but
429 # it did include an updater binary.
430 zip.getinfo("OTA/bin/updater")
431 return 1
432 except KeyError:
433 return 0
434
Doug Zongkereef39442009-04-02 12:14:19 -0700435def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700436 source_version = GetRecoveryAPIVersion(source_zip)
437
438 if OPTIONS.script_mode == 'amend':
439 script = amend_generator.AmendGenerator()
440 elif OPTIONS.script_mode == 'edify':
441 if source_version == 0:
442 print ("WARNING: generating edify script for a source that "
443 "can't install it.")
444 script = edify_generator.EdifyGenerator(source_version)
445 elif OPTIONS.script_mode == 'auto':
446 if source_version > 0:
447 script = edify_generator.EdifyGenerator(source_version)
448 else:
449 script = amend_generator.AmendGenerator()
450 else:
451 raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
Doug Zongkereef39442009-04-02 12:14:19 -0700452
453 print "Loading target..."
454 target_data = LoadSystemFiles(target_zip)
455 print "Loading source..."
456 source_data = LoadSystemFiles(source_zip)
457
458 verbatim_targets = []
459 patch_list = []
460 largest_source_size = 0
461 for fn in sorted(target_data.keys()):
462 tf = target_data[fn]
463 sf = source_data.get(fn, None)
464
465 if sf is None or fn in OPTIONS.require_verbatim:
466 # This file should be included verbatim
467 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700468 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700469 print "send", fn, "verbatim"
470 tf.AddToZip(output_zip)
471 verbatim_targets.append((fn, tf.size))
472 elif tf.sha1 != sf.sha1:
473 # File is different; consider sending as a patch
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700474 diff_method = "bsdiff"
475 if tf.name.endswith(".gz"):
476 diff_method = "imgdiff"
477 d = Difference(tf, sf, diff_method)
Doug Zongker5da317e2009-06-02 13:38:17 -0700478 if d is not None:
479 print fn, tf.size, len(d), (float(len(d)) / tf.size)
480 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
Doug Zongkereef39442009-04-02 12:14:19 -0700481 # patch is almost as big as the file; don't bother patching
482 tf.AddToZip(output_zip)
483 verbatim_targets.append((fn, tf.size))
484 else:
Doug Zongker048e7ca2009-06-15 14:31:53 -0700485 common.ZipWriteStr(output_zip, "patch/" + fn + ".p", d)
Doug Zongkereef39442009-04-02 12:14:19 -0700486 patch_list.append((fn, tf, sf, tf.size))
487 largest_source_size = max(largest_source_size, sf.size)
488 else:
489 # Target file identical to source.
490 pass
491
492 total_verbatim_size = sum([i[1] for i in verbatim_targets])
493 total_patched_size = sum([i[3] for i in patch_list])
494
495 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
496 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
497
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700498 script.Mount("MTD", "system", "/system")
499 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700500
Doug Zongker5da317e2009-06-02 13:38:17 -0700501 source_boot = File("/tmp/boot.img",
502 common.BuildBootableImage(
503 os.path.join(OPTIONS.source_tmp, "BOOT")))
504 target_boot = File("/tmp/boot.img",
505 common.BuildBootableImage(
506 os.path.join(OPTIONS.target_tmp, "BOOT")))
507 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700508
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700509 source_recovery = File("system/recovery.img",
510 common.BuildBootableImage(
511 os.path.join(OPTIONS.source_tmp, "RECOVERY")))
512 target_recovery = File("system/recovery.img",
513 common.BuildBootableImage(
514 os.path.join(OPTIONS.target_tmp, "RECOVERY")))
515 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700516
517 source_radio = source_zip.read("RADIO/image")
518 target_radio = target_zip.read("RADIO/image")
519 updating_radio = (source_radio != target_radio)
520
521 # The last 0.1 is reserved for creating symlinks, fixing
522 # permissions, and writing the boot image (if necessary).
523 progress_bar_total = 1.0
524 if updating_boot:
525 progress_bar_total -= 0.1
526 if updating_radio:
527 progress_bar_total -= 0.3
528
529 AppendAssertions(script, target_zip)
530
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700531 script.Print("Verifying current system...")
532
Doug Zongkereef39442009-04-02 12:14:19 -0700533 pb_verify = progress_bar_total * 0.3 * \
534 (total_patched_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700535 float(total_patched_size+total_verbatim_size+1))
Doug Zongkereef39442009-04-02 12:14:19 -0700536
537 for i, (fn, tf, sf, size) in enumerate(patch_list):
538 if i % 5 == 0:
539 next_sizes = sum([i[3] for i in patch_list[i:i+5]])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700540 script.ShowProgress(next_sizes * pb_verify / (total_patched_size+1), 1)
541
542 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongkereef39442009-04-02 12:14:19 -0700543
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700544 if updating_recovery:
545 d = Difference(target_recovery, source_recovery, "imgdiff")
546 print "recovery target: %d source: %d diff: %d" % (
547 target_recovery.size, source_recovery.size, len(d))
548
Doug Zongker048e7ca2009-06-15 14:31:53 -0700549 common.ZipWriteStr(output_zip, "patch/recovery.img.p", d)
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700550
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700551 script.PatchCheck("MTD:recovery:%d:%s:%d:%s" %
552 (source_recovery.size, source_recovery.sha1,
553 target_recovery.size, target_recovery.sha1))
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700554
Doug Zongker5da317e2009-06-02 13:38:17 -0700555 if updating_boot:
556 d = Difference(target_boot, source_boot, "imgdiff")
557 print "boot target: %d source: %d diff: %d" % (
558 target_boot.size, source_boot.size, len(d))
559
Doug Zongker048e7ca2009-06-15 14:31:53 -0700560 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700561
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700562 script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
563 (source_boot.size, source_boot.sha1,
564 target_boot.size, target_boot.sha1))
Doug Zongker5da317e2009-06-02 13:38:17 -0700565
566 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700567 script.CacheFreeSpaceCheck(largest_source_size)
568 script.Print("Unpacking patches...")
569 script.UnpackPackageDir("patch", "/tmp/patchtmp")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700570
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700571 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700572
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700573 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700574 script.Print("Erasing user data...")
575 script.FormatPartition("userdata")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700576
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700577 script.Print("Removing unneeded files...")
578 script.DeleteFiles(["/"+i[0] for i in verbatim_targets])
Doug Zongkereef39442009-04-02 12:14:19 -0700579
580 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700581 # Produce the boot image by applying a patch to the current
582 # contents of the boot partition, and write it back to the
583 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700584 script.Print("Patching boot image...")
585 script.ApplyPatch("MTD:boot:%d:%s:%d:%s"
586 % (source_boot.size, source_boot.sha1,
587 target_boot.size, target_boot.sha1),
588 "-",
589 target_boot.size, target_boot.sha1,
590 source_boot.sha1, "/tmp/patchtmp/boot.img.p")
Doug Zongkereef39442009-04-02 12:14:19 -0700591 print "boot image changed; including."
592 else:
593 print "boot image unchanged; skipping."
594
595 if updating_recovery:
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700596 # Produce /system/recovery.img by applying a patch to the current
597 # contents of the recovery partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700598 script.Print("Patching recovery image...")
599 script.ApplyPatch("MTD:recovery:%d:%s:%d:%s"
600 % (source_recovery.size, source_recovery.sha1,
601 target_recovery.size, target_recovery.sha1),
602 "/system/recovery.img",
603 target_recovery.size, target_recovery.sha1,
604 source_recovery.sha1, "/tmp/patchtmp/recovery.img.p")
Doug Zongkereef39442009-04-02 12:14:19 -0700605 print "recovery image changed; including."
606 else:
607 print "recovery image unchanged; skipping."
608
609 if updating_radio:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700610 script.ShowProgress(0.3, 10)
611 script.Print("Writing radio image...")
612 script.WriteFirmwareImage("radio", "radio.img")
Doug Zongker048e7ca2009-06-15 14:31:53 -0700613 common.ZipWriteStr(output_zip, "radio.img", target_radio)
Doug Zongkereef39442009-04-02 12:14:19 -0700614 print "radio image changed; including."
615 else:
616 print "radio image unchanged; skipping."
617
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700618 script.Print("Patching system files...")
Doug Zongkereef39442009-04-02 12:14:19 -0700619 pb_apply = progress_bar_total * 0.7 * \
620 (total_patched_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700621 float(total_patched_size+total_verbatim_size+1))
Doug Zongkereef39442009-04-02 12:14:19 -0700622 for i, (fn, tf, sf, size) in enumerate(patch_list):
623 if i % 5 == 0:
624 next_sizes = sum([i[3] for i in patch_list[i:i+5]])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700625 script.ShowProgress(next_sizes * pb_apply / (total_patched_size+1), 1)
626 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
627 sf.sha1, "/tmp/patchtmp/"+fn+".p")
Doug Zongkereef39442009-04-02 12:14:19 -0700628
629 target_symlinks = CopySystemFiles(target_zip, None)
630
631 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700632 temp_script = script.MakeTemporary()
Doug Zongkereef39442009-04-02 12:14:19 -0700633 FixPermissions(temp_script)
634
635 # Note that this call will mess up the tree of Items, so make sure
636 # we're done with it.
637 source_symlinks = CopySystemFiles(source_zip, None)
638 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
639
640 # Delete all the symlinks in source that aren't in target. This
641 # needs to happen before verbatim files are unpacked, in case a
642 # symlink in the source is replaced by a real file in the target.
643 to_delete = []
644 for dest, link in source_symlinks:
645 if link not in target_symlinks_d:
646 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700647 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700648
649 if verbatim_targets:
650 pb_verbatim = progress_bar_total * \
651 (total_verbatim_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700652 float(total_patched_size+total_verbatim_size+1))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700653 script.ShowProgress(pb_verbatim, 5)
654 script.Print("Unpacking new files...")
655 script.UnpackPackageDir("system", "/system")
656
657 script.Print("Finishing up...")
Doug Zongkereef39442009-04-02 12:14:19 -0700658
659 # Create all the symlinks that don't already exist, or point to
660 # somewhere different than what we want. Delete each symlink before
661 # creating it, since the 'symlink' command won't overwrite.
662 to_create = []
663 for dest, link in target_symlinks:
664 if link in source_symlinks_d:
665 if dest != source_symlinks_d[link]:
666 to_create.append((dest, link))
667 else:
668 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700669 script.DeleteFiles([i[1] for i in to_create])
670 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700671
672 # Now that the symlinks are created, we can set all the
673 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700674 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700675
Doug Zongker1c390a22009-05-14 19:06:36 -0700676 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700677 scirpt.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700678
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700679 script.AddToZip(target_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700680
681
682def main(argv):
683
684 def option_handler(o, a):
685 if o in ("-b", "--board_config"):
686 common.LoadBoardConfig(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700687 elif o in ("-k", "--package_key"):
688 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700689 elif o in ("-i", "--incremental_from"):
690 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700691 elif o in ("-w", "--wipe_user_data"):
692 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700693 elif o in ("-n", "--no_prereq"):
694 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700695 elif o in ("-e", "--extra_script"):
696 OPTIONS.extra_script = a
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700697 elif o in ("-m", "--script_mode"):
698 OPTIONS.script_mode = a
Doug Zongkereef39442009-04-02 12:14:19 -0700699 else:
700 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700701 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700702
703 args = common.ParseOptions(argv, __doc__,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700704 extra_opts="b:k:i:d:wne:m:",
Doug Zongkereef39442009-04-02 12:14:19 -0700705 extra_long_opts=["board_config=",
706 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700707 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700708 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700709 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700710 "extra_script=",
711 "script_mode="],
Doug Zongkereef39442009-04-02 12:14:19 -0700712 extra_option_handler=option_handler)
713
714 if len(args) != 2:
715 common.Usage(__doc__)
716 sys.exit(1)
717
718 if not OPTIONS.max_image_size:
719 print
720 print " WARNING: No board config specified; will not check image"
721 print " sizes against limits. Use -b to make sure the generated"
722 print " images don't exceed partition sizes."
723 print
724
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700725 if OPTIONS.script_mode not in ("amend", "edify", "auto"):
726 raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
727
Doug Zongker1c390a22009-05-14 19:06:36 -0700728 if OPTIONS.extra_script is not None:
729 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
730
Doug Zongkereef39442009-04-02 12:14:19 -0700731 print "unzipping target target-files..."
732 OPTIONS.input_tmp = common.UnzipTemp(args[0])
733 OPTIONS.target_tmp = OPTIONS.input_tmp
734 input_zip = zipfile.ZipFile(args[0], "r")
735 if OPTIONS.package_key:
736 temp_zip_file = tempfile.NamedTemporaryFile()
737 output_zip = zipfile.ZipFile(temp_zip_file, "w",
738 compression=zipfile.ZIP_DEFLATED)
739 else:
740 output_zip = zipfile.ZipFile(args[1], "w",
741 compression=zipfile.ZIP_DEFLATED)
742
743 if OPTIONS.incremental_source is None:
744 WriteFullOTAPackage(input_zip, output_zip)
745 else:
746 print "unzipping source target-files..."
747 OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
748 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
749 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
750
751 output_zip.close()
752 if OPTIONS.package_key:
753 SignOutput(temp_zip_file.name, args[1])
754 temp_zip_file.close()
755
756 common.Cleanup()
757
758 print "done."
759
760
761if __name__ == '__main__':
762 try:
763 main(sys.argv[1:])
764 except common.ExternalError, e:
765 print
766 print " ERROR: %s" % (e,)
767 print
768 sys.exit(1)