blob: 11e66957191e543dbf80dbb51a31716bdb84ac8a [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 Zongker171f1cd2009-06-15 22:36:37 -0700291 if m:
292 bootloaders = m.group(1).split("|")
Doug Zongkerac4920a2009-06-18 13:42:20 -0700293 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 Zongker171f1cd2009-06-15 22:36:37 -0700313 try:
314 common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
Doug Zongkerac4920a2009-06-18 13:42:20 -0700315 script.WriteFirmwareImage("radio", "radio.img")
Doug Zongker171f1cd2009-06-15 22:36:37 -0700316 except KeyError:
317 pass
318
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700319 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700320
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700321 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700322 script.FormatPartition("userdata")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700323
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700324 script.FormatPartition("system")
325 script.Mount("MTD", "system", "/system")
326 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700327
328 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700329 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700330
331 common.BuildAndAddBootableImage(os.path.join(OPTIONS.input_tmp, "RECOVERY"),
332 "system/recovery.img", output_zip)
333 Item.Get("system/recovery.img", dir=False)
334
335 FixPermissions(script)
336
337 common.AddBoot(output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700338 script.ShowProgress(0.2, 0)
339
340 script.WriteRawImage("boot", "boot.img")
341 script.ShowProgress(0.2, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700342
Doug Zongker1c390a22009-05-14 19:06:36 -0700343 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700344 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700345
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700346 script.AddToZip(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700347
348
349class File(object):
350 def __init__(self, name, data):
351 self.name = name
352 self.data = data
353 self.size = len(data)
354 self.sha1 = sha.sha(data).hexdigest()
355
356 def WriteToTemp(self):
357 t = tempfile.NamedTemporaryFile()
358 t.write(self.data)
359 t.flush()
360 return t
361
362 def AddToZip(self, z):
Doug Zongker048e7ca2009-06-15 14:31:53 -0700363 common.ZipWriteStr(z, self.name, self.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700364
365
366def LoadSystemFiles(z):
367 """Load all the files from SYSTEM/... in a given target-files
368 ZipFile, and return a dict of {filename: File object}."""
369 out = {}
370 for info in z.infolist():
371 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
372 fn = "system/" + info.filename[7:]
373 data = z.read(info.filename)
374 out[fn] = File(fn, data)
375 return out
376
377
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700378def Difference(tf, sf, diff_program):
379 """Return the patch (as a string of data) needed to turn sf into tf.
380 diff_program is the name of an external program (or list, if
381 additional arguments are desired) to run to generate the diff.
382 """
Doug Zongkereef39442009-04-02 12:14:19 -0700383
384 ttemp = tf.WriteToTemp()
385 stemp = sf.WriteToTemp()
386
387 ext = os.path.splitext(tf.name)[1]
388
389 try:
390 ptemp = tempfile.NamedTemporaryFile()
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700391 if isinstance(diff_program, list):
392 cmd = copy.copy(diff_program)
393 else:
394 cmd = [diff_program]
395 cmd.append(stemp.name)
396 cmd.append(ttemp.name)
397 cmd.append(ptemp.name)
398 p = common.Run(cmd)
Doug Zongkereef39442009-04-02 12:14:19 -0700399 _, err = p.communicate()
Doug Zongker5da317e2009-06-02 13:38:17 -0700400 if err or p.returncode != 0:
401 print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
402 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700403 diff = ptemp.read()
Doug Zongkereef39442009-04-02 12:14:19 -0700404 finally:
Doug Zongker5da317e2009-06-02 13:38:17 -0700405 ptemp.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700406 stemp.close()
407 ttemp.close()
408
409 return diff
410
411
412def GetBuildProp(property, z):
413 """Return the fingerprint of the build of a given target-files
414 ZipFile object."""
415 bp = z.read("SYSTEM/build.prop")
416 if not property:
417 return bp
418 m = re.search(re.escape(property) + r"=(.*)\n", bp)
419 if not m:
420 raise ExternalException("couldn't find %s in build.prop" % (property,))
421 return m.group(1).strip()
422
423
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700424def GetRecoveryAPIVersion(zip):
425 """Returns the version of the recovery API. Version 0 is the older
426 amend code (no separate binary)."""
427 try:
428 version = zip.read("META/recovery-api-version.txt")
429 return int(version)
430 except KeyError:
431 try:
432 # version one didn't have the recovery-api-version.txt file, but
433 # it did include an updater binary.
434 zip.getinfo("OTA/bin/updater")
435 return 1
436 except KeyError:
437 return 0
438
Doug Zongkereef39442009-04-02 12:14:19 -0700439def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700440 source_version = GetRecoveryAPIVersion(source_zip)
441
442 if OPTIONS.script_mode == 'amend':
443 script = amend_generator.AmendGenerator()
444 elif OPTIONS.script_mode == 'edify':
445 if source_version == 0:
446 print ("WARNING: generating edify script for a source that "
447 "can't install it.")
448 script = edify_generator.EdifyGenerator(source_version)
449 elif OPTIONS.script_mode == 'auto':
450 if source_version > 0:
451 script = edify_generator.EdifyGenerator(source_version)
452 else:
453 script = amend_generator.AmendGenerator()
454 else:
455 raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
Doug Zongkereef39442009-04-02 12:14:19 -0700456
457 print "Loading target..."
458 target_data = LoadSystemFiles(target_zip)
459 print "Loading source..."
460 source_data = LoadSystemFiles(source_zip)
461
462 verbatim_targets = []
463 patch_list = []
464 largest_source_size = 0
465 for fn in sorted(target_data.keys()):
466 tf = target_data[fn]
467 sf = source_data.get(fn, None)
468
469 if sf is None or fn in OPTIONS.require_verbatim:
470 # This file should be included verbatim
471 if fn in OPTIONS.prohibit_verbatim:
472 raise ExternalError("\"%s\" must be sent verbatim" % (fn,))
473 print "send", fn, "verbatim"
474 tf.AddToZip(output_zip)
475 verbatim_targets.append((fn, tf.size))
476 elif tf.sha1 != sf.sha1:
477 # File is different; consider sending as a patch
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700478 diff_method = "bsdiff"
479 if tf.name.endswith(".gz"):
480 diff_method = "imgdiff"
481 d = Difference(tf, sf, diff_method)
Doug Zongker5da317e2009-06-02 13:38:17 -0700482 if d is not None:
483 print fn, tf.size, len(d), (float(len(d)) / tf.size)
484 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
Doug Zongkereef39442009-04-02 12:14:19 -0700485 # patch is almost as big as the file; don't bother patching
486 tf.AddToZip(output_zip)
487 verbatim_targets.append((fn, tf.size))
488 else:
Doug Zongker048e7ca2009-06-15 14:31:53 -0700489 common.ZipWriteStr(output_zip, "patch/" + fn + ".p", d)
Doug Zongkereef39442009-04-02 12:14:19 -0700490 patch_list.append((fn, tf, sf, tf.size))
491 largest_source_size = max(largest_source_size, sf.size)
492 else:
493 # Target file identical to source.
494 pass
495
496 total_verbatim_size = sum([i[1] for i in verbatim_targets])
497 total_patched_size = sum([i[3] for i in patch_list])
498
499 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
500 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
501
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700502 script.Mount("MTD", "system", "/system")
503 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700504
Doug Zongker5da317e2009-06-02 13:38:17 -0700505 source_boot = File("/tmp/boot.img",
506 common.BuildBootableImage(
507 os.path.join(OPTIONS.source_tmp, "BOOT")))
508 target_boot = File("/tmp/boot.img",
509 common.BuildBootableImage(
510 os.path.join(OPTIONS.target_tmp, "BOOT")))
511 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700512
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700513 source_recovery = File("system/recovery.img",
514 common.BuildBootableImage(
515 os.path.join(OPTIONS.source_tmp, "RECOVERY")))
516 target_recovery = File("system/recovery.img",
517 common.BuildBootableImage(
518 os.path.join(OPTIONS.target_tmp, "RECOVERY")))
519 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700520
521 source_radio = source_zip.read("RADIO/image")
522 target_radio = target_zip.read("RADIO/image")
523 updating_radio = (source_radio != target_radio)
524
525 # The last 0.1 is reserved for creating symlinks, fixing
526 # permissions, and writing the boot image (if necessary).
527 progress_bar_total = 1.0
528 if updating_boot:
529 progress_bar_total -= 0.1
530 if updating_radio:
531 progress_bar_total -= 0.3
532
533 AppendAssertions(script, target_zip)
534
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700535 script.Print("Verifying current system...")
536
Doug Zongkereef39442009-04-02 12:14:19 -0700537 pb_verify = progress_bar_total * 0.3 * \
538 (total_patched_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700539 float(total_patched_size+total_verbatim_size+1))
Doug Zongkereef39442009-04-02 12:14:19 -0700540
541 for i, (fn, tf, sf, size) in enumerate(patch_list):
542 if i % 5 == 0:
543 next_sizes = sum([i[3] for i in patch_list[i:i+5]])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700544 script.ShowProgress(next_sizes * pb_verify / (total_patched_size+1), 1)
545
546 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongkereef39442009-04-02 12:14:19 -0700547
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700548 if updating_recovery:
549 d = Difference(target_recovery, source_recovery, "imgdiff")
550 print "recovery target: %d source: %d diff: %d" % (
551 target_recovery.size, source_recovery.size, len(d))
552
Doug Zongker048e7ca2009-06-15 14:31:53 -0700553 common.ZipWriteStr(output_zip, "patch/recovery.img.p", d)
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700554
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700555 script.PatchCheck("MTD:recovery:%d:%s:%d:%s" %
556 (source_recovery.size, source_recovery.sha1,
557 target_recovery.size, target_recovery.sha1))
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700558
Doug Zongker5da317e2009-06-02 13:38:17 -0700559 if updating_boot:
560 d = Difference(target_boot, source_boot, "imgdiff")
561 print "boot target: %d source: %d diff: %d" % (
562 target_boot.size, source_boot.size, len(d))
563
Doug Zongker048e7ca2009-06-15 14:31:53 -0700564 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700565
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700566 script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
567 (source_boot.size, source_boot.sha1,
568 target_boot.size, target_boot.sha1))
Doug Zongker5da317e2009-06-02 13:38:17 -0700569
570 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700571 script.CacheFreeSpaceCheck(largest_source_size)
572 script.Print("Unpacking patches...")
573 script.UnpackPackageDir("patch", "/tmp/patchtmp")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700574
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700575 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700576
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700577 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700578 script.Print("Erasing user data...")
579 script.FormatPartition("userdata")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700580
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700581 script.Print("Removing unneeded files...")
582 script.DeleteFiles(["/"+i[0] for i in verbatim_targets])
Doug Zongkereef39442009-04-02 12:14:19 -0700583
584 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700585 # Produce the boot image by applying a patch to the current
586 # contents of the boot partition, and write it back to the
587 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700588 script.Print("Patching boot image...")
589 script.ApplyPatch("MTD:boot:%d:%s:%d:%s"
590 % (source_boot.size, source_boot.sha1,
591 target_boot.size, target_boot.sha1),
592 "-",
593 target_boot.size, target_boot.sha1,
594 source_boot.sha1, "/tmp/patchtmp/boot.img.p")
Doug Zongkereef39442009-04-02 12:14:19 -0700595 print "boot image changed; including."
596 else:
597 print "boot image unchanged; skipping."
598
599 if updating_recovery:
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700600 # Produce /system/recovery.img by applying a patch to the current
601 # contents of the recovery partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700602 script.Print("Patching recovery image...")
603 script.ApplyPatch("MTD:recovery:%d:%s:%d:%s"
604 % (source_recovery.size, source_recovery.sha1,
605 target_recovery.size, target_recovery.sha1),
606 "/system/recovery.img",
607 target_recovery.size, target_recovery.sha1,
608 source_recovery.sha1, "/tmp/patchtmp/recovery.img.p")
Doug Zongkereef39442009-04-02 12:14:19 -0700609 print "recovery image changed; including."
610 else:
611 print "recovery image unchanged; skipping."
612
613 if updating_radio:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700614 script.ShowProgress(0.3, 10)
615 script.Print("Writing radio image...")
616 script.WriteFirmwareImage("radio", "radio.img")
Doug Zongker048e7ca2009-06-15 14:31:53 -0700617 common.ZipWriteStr(output_zip, "radio.img", target_radio)
Doug Zongkereef39442009-04-02 12:14:19 -0700618 print "radio image changed; including."
619 else:
620 print "radio image unchanged; skipping."
621
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700622 script.Print("Patching system files...")
Doug Zongkereef39442009-04-02 12:14:19 -0700623 pb_apply = progress_bar_total * 0.7 * \
624 (total_patched_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700625 float(total_patched_size+total_verbatim_size+1))
Doug Zongkereef39442009-04-02 12:14:19 -0700626 for i, (fn, tf, sf, size) in enumerate(patch_list):
627 if i % 5 == 0:
628 next_sizes = sum([i[3] for i in patch_list[i:i+5]])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700629 script.ShowProgress(next_sizes * pb_apply / (total_patched_size+1), 1)
630 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
631 sf.sha1, "/tmp/patchtmp/"+fn+".p")
Doug Zongkereef39442009-04-02 12:14:19 -0700632
633 target_symlinks = CopySystemFiles(target_zip, None)
634
635 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700636 temp_script = script.MakeTemporary()
Doug Zongkereef39442009-04-02 12:14:19 -0700637 FixPermissions(temp_script)
638
639 # Note that this call will mess up the tree of Items, so make sure
640 # we're done with it.
641 source_symlinks = CopySystemFiles(source_zip, None)
642 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
643
644 # Delete all the symlinks in source that aren't in target. This
645 # needs to happen before verbatim files are unpacked, in case a
646 # symlink in the source is replaced by a real file in the target.
647 to_delete = []
648 for dest, link in source_symlinks:
649 if link not in target_symlinks_d:
650 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700651 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700652
653 if verbatim_targets:
654 pb_verbatim = progress_bar_total * \
655 (total_verbatim_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700656 float(total_patched_size+total_verbatim_size+1))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700657 script.ShowProgress(pb_verbatim, 5)
658 script.Print("Unpacking new files...")
659 script.UnpackPackageDir("system", "/system")
660
661 script.Print("Finishing up...")
Doug Zongkereef39442009-04-02 12:14:19 -0700662
663 # Create all the symlinks that don't already exist, or point to
664 # somewhere different than what we want. Delete each symlink before
665 # creating it, since the 'symlink' command won't overwrite.
666 to_create = []
667 for dest, link in target_symlinks:
668 if link in source_symlinks_d:
669 if dest != source_symlinks_d[link]:
670 to_create.append((dest, link))
671 else:
672 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700673 script.DeleteFiles([i[1] for i in to_create])
674 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700675
676 # Now that the symlinks are created, we can set all the
677 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700678 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700679
Doug Zongker1c390a22009-05-14 19:06:36 -0700680 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700681 scirpt.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700682
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700683 script.AddToZip(target_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700684
685
686def main(argv):
687
688 def option_handler(o, a):
689 if o in ("-b", "--board_config"):
690 common.LoadBoardConfig(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700691 elif o in ("-k", "--package_key"):
692 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700693 elif o in ("-i", "--incremental_from"):
694 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700695 elif o in ("-w", "--wipe_user_data"):
696 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700697 elif o in ("-n", "--no_prereq"):
698 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700699 elif o in ("-e", "--extra_script"):
700 OPTIONS.extra_script = a
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700701 elif o in ("-m", "--script_mode"):
702 OPTIONS.script_mode = a
Doug Zongkereef39442009-04-02 12:14:19 -0700703 else:
704 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700705 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700706
707 args = common.ParseOptions(argv, __doc__,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700708 extra_opts="b:k:i:d:wne:m:",
Doug Zongkereef39442009-04-02 12:14:19 -0700709 extra_long_opts=["board_config=",
710 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700711 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700712 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700713 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700714 "extra_script=",
715 "script_mode="],
Doug Zongkereef39442009-04-02 12:14:19 -0700716 extra_option_handler=option_handler)
717
718 if len(args) != 2:
719 common.Usage(__doc__)
720 sys.exit(1)
721
722 if not OPTIONS.max_image_size:
723 print
724 print " WARNING: No board config specified; will not check image"
725 print " sizes against limits. Use -b to make sure the generated"
726 print " images don't exceed partition sizes."
727 print
728
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700729 if OPTIONS.script_mode not in ("amend", "edify", "auto"):
730 raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
731
Doug Zongker1c390a22009-05-14 19:06:36 -0700732 if OPTIONS.extra_script is not None:
733 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
734
Doug Zongkereef39442009-04-02 12:14:19 -0700735 print "unzipping target target-files..."
736 OPTIONS.input_tmp = common.UnzipTemp(args[0])
737 OPTIONS.target_tmp = OPTIONS.input_tmp
738 input_zip = zipfile.ZipFile(args[0], "r")
739 if OPTIONS.package_key:
740 temp_zip_file = tempfile.NamedTemporaryFile()
741 output_zip = zipfile.ZipFile(temp_zip_file, "w",
742 compression=zipfile.ZIP_DEFLATED)
743 else:
744 output_zip = zipfile.ZipFile(args[1], "w",
745 compression=zipfile.ZIP_DEFLATED)
746
747 if OPTIONS.incremental_source is None:
748 WriteFullOTAPackage(input_zip, output_zip)
749 else:
750 print "unzipping source target-files..."
751 OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
752 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
753 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
754
755 output_zip.close()
756 if OPTIONS.package_key:
757 SignOutput(temp_zip_file.name, args[1])
758 temp_zip_file.close()
759
760 common.Cleanup()
761
762 print "done."
763
764
765if __name__ == '__main__':
766 try:
767 main(sys.argv[1:])
768 except common.ExternalError, e:
769 print
770 print " ERROR: %s" % (e,)
771 print
772 sys.exit(1)