blob: 547c65c55b437e0f48a1e547fd9a671b6b41f936 [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 Zongkere1c31ba2009-06-23 17:40:35 -0700313 try:
314 common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
315 script.WriteFirmwareImage("radio", "radio.img")
316 except KeyError:
317 print "warning: no radio image in input target_files; not flashing radio"
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
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700331 if common.BuildAndAddBootableImage(
332 os.path.join(OPTIONS.input_tmp, "RECOVERY"),
333 "system/recovery.img", output_zip):
334 Item.Get("system/recovery.img", dir=False)
Doug Zongkereef39442009-04-02 12:14:19 -0700335
336 FixPermissions(script)
337
338 common.AddBoot(output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700339 script.ShowProgress(0.2, 0)
340
341 script.WriteRawImage("boot", "boot.img")
342 script.ShowProgress(0.2, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700343
Doug Zongker1c390a22009-05-14 19:06:36 -0700344 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700345 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700346
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700347 script.AddToZip(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700348
349
350class File(object):
351 def __init__(self, name, data):
352 self.name = name
353 self.data = data
354 self.size = len(data)
355 self.sha1 = sha.sha(data).hexdigest()
356
357 def WriteToTemp(self):
358 t = tempfile.NamedTemporaryFile()
359 t.write(self.data)
360 t.flush()
361 return t
362
363 def AddToZip(self, z):
Doug Zongker048e7ca2009-06-15 14:31:53 -0700364 common.ZipWriteStr(z, self.name, self.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700365
366
367def LoadSystemFiles(z):
368 """Load all the files from SYSTEM/... in a given target-files
369 ZipFile, and return a dict of {filename: File object}."""
370 out = {}
371 for info in z.infolist():
372 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
373 fn = "system/" + info.filename[7:]
374 data = z.read(info.filename)
375 out[fn] = File(fn, data)
376 return out
377
378
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700379def Difference(tf, sf, diff_program):
380 """Return the patch (as a string of data) needed to turn sf into tf.
381 diff_program is the name of an external program (or list, if
382 additional arguments are desired) to run to generate the diff.
383 """
Doug Zongkereef39442009-04-02 12:14:19 -0700384
385 ttemp = tf.WriteToTemp()
386 stemp = sf.WriteToTemp()
387
388 ext = os.path.splitext(tf.name)[1]
389
390 try:
391 ptemp = tempfile.NamedTemporaryFile()
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700392 if isinstance(diff_program, list):
393 cmd = copy.copy(diff_program)
394 else:
395 cmd = [diff_program]
396 cmd.append(stemp.name)
397 cmd.append(ttemp.name)
398 cmd.append(ptemp.name)
399 p = common.Run(cmd)
Doug Zongkereef39442009-04-02 12:14:19 -0700400 _, err = p.communicate()
Doug Zongker5da317e2009-06-02 13:38:17 -0700401 if err or p.returncode != 0:
402 print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
403 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700404 diff = ptemp.read()
Doug Zongkereef39442009-04-02 12:14:19 -0700405 finally:
Doug Zongker5da317e2009-06-02 13:38:17 -0700406 ptemp.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700407 stemp.close()
408 ttemp.close()
409
410 return diff
411
412
413def GetBuildProp(property, z):
414 """Return the fingerprint of the build of a given target-files
415 ZipFile object."""
416 bp = z.read("SYSTEM/build.prop")
417 if not property:
418 return bp
419 m = re.search(re.escape(property) + r"=(.*)\n", bp)
420 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700421 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700422 return m.group(1).strip()
423
424
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700425def GetRecoveryAPIVersion(zip):
426 """Returns the version of the recovery API. Version 0 is the older
427 amend code (no separate binary)."""
428 try:
429 version = zip.read("META/recovery-api-version.txt")
430 return int(version)
431 except KeyError:
432 try:
433 # version one didn't have the recovery-api-version.txt file, but
434 # it did include an updater binary.
435 zip.getinfo("OTA/bin/updater")
436 return 1
437 except KeyError:
438 return 0
439
Doug Zongkereef39442009-04-02 12:14:19 -0700440def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700441 source_version = GetRecoveryAPIVersion(source_zip)
442
443 if OPTIONS.script_mode == 'amend':
444 script = amend_generator.AmendGenerator()
445 elif OPTIONS.script_mode == 'edify':
446 if source_version == 0:
447 print ("WARNING: generating edify script for a source that "
448 "can't install it.")
449 script = edify_generator.EdifyGenerator(source_version)
450 elif OPTIONS.script_mode == 'auto':
451 if source_version > 0:
452 script = edify_generator.EdifyGenerator(source_version)
453 else:
454 script = amend_generator.AmendGenerator()
455 else:
456 raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
Doug Zongkereef39442009-04-02 12:14:19 -0700457
458 print "Loading target..."
459 target_data = LoadSystemFiles(target_zip)
460 print "Loading source..."
461 source_data = LoadSystemFiles(source_zip)
462
463 verbatim_targets = []
464 patch_list = []
465 largest_source_size = 0
466 for fn in sorted(target_data.keys()):
467 tf = target_data[fn]
468 sf = source_data.get(fn, None)
469
470 if sf is None or fn in OPTIONS.require_verbatim:
471 # This file should be included verbatim
472 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700473 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700474 print "send", fn, "verbatim"
475 tf.AddToZip(output_zip)
476 verbatim_targets.append((fn, tf.size))
477 elif tf.sha1 != sf.sha1:
478 # File is different; consider sending as a patch
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700479 diff_method = "bsdiff"
480 if tf.name.endswith(".gz"):
481 diff_method = "imgdiff"
482 d = Difference(tf, sf, diff_method)
Doug Zongker5da317e2009-06-02 13:38:17 -0700483 if d is not None:
484 print fn, tf.size, len(d), (float(len(d)) / tf.size)
485 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
Doug Zongkereef39442009-04-02 12:14:19 -0700486 # patch is almost as big as the file; don't bother patching
487 tf.AddToZip(output_zip)
488 verbatim_targets.append((fn, tf.size))
489 else:
Doug Zongker048e7ca2009-06-15 14:31:53 -0700490 common.ZipWriteStr(output_zip, "patch/" + fn + ".p", d)
Doug Zongkereef39442009-04-02 12:14:19 -0700491 patch_list.append((fn, tf, sf, tf.size))
492 largest_source_size = max(largest_source_size, sf.size)
493 else:
494 # Target file identical to source.
495 pass
496
497 total_verbatim_size = sum([i[1] for i in verbatim_targets])
498 total_patched_size = sum([i[3] for i in patch_list])
499
500 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
501 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
502
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700503 script.Mount("MTD", "system", "/system")
504 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700505
Doug Zongker5da317e2009-06-02 13:38:17 -0700506 source_boot = File("/tmp/boot.img",
507 common.BuildBootableImage(
508 os.path.join(OPTIONS.source_tmp, "BOOT")))
509 target_boot = File("/tmp/boot.img",
510 common.BuildBootableImage(
511 os.path.join(OPTIONS.target_tmp, "BOOT")))
512 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700513
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700514 source_recovery = File("system/recovery.img",
515 common.BuildBootableImage(
516 os.path.join(OPTIONS.source_tmp, "RECOVERY")))
517 target_recovery = File("system/recovery.img",
518 common.BuildBootableImage(
519 os.path.join(OPTIONS.target_tmp, "RECOVERY")))
520 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700521
522 source_radio = source_zip.read("RADIO/image")
523 target_radio = target_zip.read("RADIO/image")
524 updating_radio = (source_radio != target_radio)
525
526 # The last 0.1 is reserved for creating symlinks, fixing
527 # permissions, and writing the boot image (if necessary).
528 progress_bar_total = 1.0
529 if updating_boot:
530 progress_bar_total -= 0.1
531 if updating_radio:
532 progress_bar_total -= 0.3
533
534 AppendAssertions(script, target_zip)
535
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700536 script.Print("Verifying current system...")
537
Doug Zongkereef39442009-04-02 12:14:19 -0700538 pb_verify = progress_bar_total * 0.3 * \
539 (total_patched_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700540 float(total_patched_size+total_verbatim_size+1))
Doug Zongkereef39442009-04-02 12:14:19 -0700541
542 for i, (fn, tf, sf, size) in enumerate(patch_list):
543 if i % 5 == 0:
544 next_sizes = sum([i[3] for i in patch_list[i:i+5]])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700545 script.ShowProgress(next_sizes * pb_verify / (total_patched_size+1), 1)
546
547 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongkereef39442009-04-02 12:14:19 -0700548
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700549 if updating_recovery:
550 d = Difference(target_recovery, source_recovery, "imgdiff")
551 print "recovery target: %d source: %d diff: %d" % (
552 target_recovery.size, source_recovery.size, len(d))
553
Doug Zongker048e7ca2009-06-15 14:31:53 -0700554 common.ZipWriteStr(output_zip, "patch/recovery.img.p", d)
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700555
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700556 script.PatchCheck("MTD:recovery:%d:%s:%d:%s" %
557 (source_recovery.size, source_recovery.sha1,
558 target_recovery.size, target_recovery.sha1))
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700559
Doug Zongker5da317e2009-06-02 13:38:17 -0700560 if updating_boot:
561 d = Difference(target_boot, source_boot, "imgdiff")
562 print "boot target: %d source: %d diff: %d" % (
563 target_boot.size, source_boot.size, len(d))
564
Doug Zongker048e7ca2009-06-15 14:31:53 -0700565 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700566
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700567 script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
568 (source_boot.size, source_boot.sha1,
569 target_boot.size, target_boot.sha1))
Doug Zongker5da317e2009-06-02 13:38:17 -0700570
571 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700572 script.CacheFreeSpaceCheck(largest_source_size)
573 script.Print("Unpacking patches...")
574 script.UnpackPackageDir("patch", "/tmp/patchtmp")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700575
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700576 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700577
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700578 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700579 script.Print("Erasing user data...")
580 script.FormatPartition("userdata")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700581
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700582 script.Print("Removing unneeded files...")
583 script.DeleteFiles(["/"+i[0] for i in verbatim_targets])
Doug Zongkereef39442009-04-02 12:14:19 -0700584
585 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700586 # Produce the boot image by applying a patch to the current
587 # contents of the boot partition, and write it back to the
588 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700589 script.Print("Patching boot image...")
590 script.ApplyPatch("MTD:boot:%d:%s:%d:%s"
591 % (source_boot.size, source_boot.sha1,
592 target_boot.size, target_boot.sha1),
593 "-",
594 target_boot.size, target_boot.sha1,
595 source_boot.sha1, "/tmp/patchtmp/boot.img.p")
Doug Zongkereef39442009-04-02 12:14:19 -0700596 print "boot image changed; including."
597 else:
598 print "boot image unchanged; skipping."
599
600 if updating_recovery:
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700601 # Produce /system/recovery.img by applying a patch to the current
602 # contents of the recovery partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700603 script.Print("Patching recovery image...")
604 script.ApplyPatch("MTD:recovery:%d:%s:%d:%s"
605 % (source_recovery.size, source_recovery.sha1,
606 target_recovery.size, target_recovery.sha1),
607 "/system/recovery.img",
608 target_recovery.size, target_recovery.sha1,
609 source_recovery.sha1, "/tmp/patchtmp/recovery.img.p")
Doug Zongkereef39442009-04-02 12:14:19 -0700610 print "recovery image changed; including."
611 else:
612 print "recovery image unchanged; skipping."
613
614 if updating_radio:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700615 script.ShowProgress(0.3, 10)
616 script.Print("Writing radio image...")
617 script.WriteFirmwareImage("radio", "radio.img")
Doug Zongker048e7ca2009-06-15 14:31:53 -0700618 common.ZipWriteStr(output_zip, "radio.img", target_radio)
Doug Zongkereef39442009-04-02 12:14:19 -0700619 print "radio image changed; including."
620 else:
621 print "radio image unchanged; skipping."
622
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700623 script.Print("Patching system files...")
Doug Zongkereef39442009-04-02 12:14:19 -0700624 pb_apply = progress_bar_total * 0.7 * \
625 (total_patched_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700626 float(total_patched_size+total_verbatim_size+1))
Doug Zongkereef39442009-04-02 12:14:19 -0700627 for i, (fn, tf, sf, size) in enumerate(patch_list):
628 if i % 5 == 0:
629 next_sizes = sum([i[3] for i in patch_list[i:i+5]])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700630 script.ShowProgress(next_sizes * pb_apply / (total_patched_size+1), 1)
631 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
632 sf.sha1, "/tmp/patchtmp/"+fn+".p")
Doug Zongkereef39442009-04-02 12:14:19 -0700633
634 target_symlinks = CopySystemFiles(target_zip, None)
635
636 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700637 temp_script = script.MakeTemporary()
Doug Zongkereef39442009-04-02 12:14:19 -0700638 FixPermissions(temp_script)
639
640 # Note that this call will mess up the tree of Items, so make sure
641 # we're done with it.
642 source_symlinks = CopySystemFiles(source_zip, None)
643 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
644
645 # Delete all the symlinks in source that aren't in target. This
646 # needs to happen before verbatim files are unpacked, in case a
647 # symlink in the source is replaced by a real file in the target.
648 to_delete = []
649 for dest, link in source_symlinks:
650 if link not in target_symlinks_d:
651 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700652 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700653
654 if verbatim_targets:
655 pb_verbatim = progress_bar_total * \
656 (total_verbatim_size /
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700657 float(total_patched_size+total_verbatim_size+1))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700658 script.ShowProgress(pb_verbatim, 5)
659 script.Print("Unpacking new files...")
660 script.UnpackPackageDir("system", "/system")
661
662 script.Print("Finishing up...")
Doug Zongkereef39442009-04-02 12:14:19 -0700663
664 # Create all the symlinks that don't already exist, or point to
665 # somewhere different than what we want. Delete each symlink before
666 # creating it, since the 'symlink' command won't overwrite.
667 to_create = []
668 for dest, link in target_symlinks:
669 if link in source_symlinks_d:
670 if dest != source_symlinks_d[link]:
671 to_create.append((dest, link))
672 else:
673 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700674 script.DeleteFiles([i[1] for i in to_create])
675 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700676
677 # Now that the symlinks are created, we can set all the
678 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700679 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700680
Doug Zongker1c390a22009-05-14 19:06:36 -0700681 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700682 scirpt.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700683
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700684 script.AddToZip(target_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700685
686
687def main(argv):
688
689 def option_handler(o, a):
690 if o in ("-b", "--board_config"):
691 common.LoadBoardConfig(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700692 elif o in ("-k", "--package_key"):
693 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700694 elif o in ("-i", "--incremental_from"):
695 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700696 elif o in ("-w", "--wipe_user_data"):
697 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700698 elif o in ("-n", "--no_prereq"):
699 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700700 elif o in ("-e", "--extra_script"):
701 OPTIONS.extra_script = a
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700702 elif o in ("-m", "--script_mode"):
703 OPTIONS.script_mode = a
Doug Zongkereef39442009-04-02 12:14:19 -0700704 else:
705 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700706 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700707
708 args = common.ParseOptions(argv, __doc__,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700709 extra_opts="b:k:i:d:wne:m:",
Doug Zongkereef39442009-04-02 12:14:19 -0700710 extra_long_opts=["board_config=",
711 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700712 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700713 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700714 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700715 "extra_script=",
716 "script_mode="],
Doug Zongkereef39442009-04-02 12:14:19 -0700717 extra_option_handler=option_handler)
718
719 if len(args) != 2:
720 common.Usage(__doc__)
721 sys.exit(1)
722
723 if not OPTIONS.max_image_size:
724 print
725 print " WARNING: No board config specified; will not check image"
726 print " sizes against limits. Use -b to make sure the generated"
727 print " images don't exceed partition sizes."
728 print
729
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700730 if OPTIONS.script_mode not in ("amend", "edify", "auto"):
731 raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
732
Doug Zongker1c390a22009-05-14 19:06:36 -0700733 if OPTIONS.extra_script is not None:
734 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
735
Doug Zongkereef39442009-04-02 12:14:19 -0700736 print "unzipping target target-files..."
737 OPTIONS.input_tmp = common.UnzipTemp(args[0])
738 OPTIONS.target_tmp = OPTIONS.input_tmp
739 input_zip = zipfile.ZipFile(args[0], "r")
740 if OPTIONS.package_key:
741 temp_zip_file = tempfile.NamedTemporaryFile()
742 output_zip = zipfile.ZipFile(temp_zip_file, "w",
743 compression=zipfile.ZIP_DEFLATED)
744 else:
745 output_zip = zipfile.ZipFile(args[1], "w",
746 compression=zipfile.ZIP_DEFLATED)
747
748 if OPTIONS.incremental_source is None:
749 WriteFullOTAPackage(input_zip, output_zip)
750 else:
751 print "unzipping source target-files..."
752 OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
753 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
754 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
755
756 output_zip.close()
757 if OPTIONS.package_key:
758 SignOutput(temp_zip_file.name, args[1])
759 temp_zip_file.close()
760
761 common.Cleanup()
762
763 print "done."
764
765
766if __name__ == '__main__':
767 try:
768 main(sys.argv[1:])
769 except common.ExternalError, e:
770 print
771 print " ERROR: %s" % (e,)
772 print
773 sys.exit(1)