add_img_to_target_files.py revision 0effed4b9484392994a1169f23a7b282cfad91d7
1#!/usr/bin/env python
2#
3# Copyright (C) 2014 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 that does not contain images (ie, does
19not have an IMAGES/ top-level subdirectory), produce the images and
20add them to the zipfile.
21
22Usage:  add_img_to_target_files [flag] target_files
23
24  -a  (--add_missing)
25      Build and add missing images to "IMAGES/". If this option is
26      not specified, this script will simply exit when "IMAGES/"
27      directory exists in the target file.
28
29  -r  (--rebuild_recovery)
30      Rebuild the recovery patch and write it to the system image. Only
31      meaningful when system image needs to be rebuilt.
32
33  --replace_verity_private_key
34      Replace the private key used for verity signing. (same as the option
35      in sign_target_files_apks)
36
37  --replace_verity_public_key
38       Replace the certificate (public key) used for verity verification. (same
39       as the option in sign_target_files_apks)
40
41  --is_signing
42      Skip building & adding the images for "userdata" and "cache" if we
43      are signing the target files.
44"""
45
46from __future__ import print_function
47
48import sys
49
50if sys.hexversion < 0x02070000:
51  print("Python 2.7 or newer is required.", file=sys.stderr)
52  sys.exit(1)
53
54import datetime
55import errno
56import os
57import shlex
58import shutil
59import subprocess
60import tempfile
61import zipfile
62
63import build_image
64import common
65import rangelib
66import sparse_img
67
68OPTIONS = common.OPTIONS
69
70OPTIONS.add_missing = False
71OPTIONS.rebuild_recovery = False
72OPTIONS.replace_updated_files_list = []
73OPTIONS.replace_verity_public_key = False
74OPTIONS.replace_verity_private_key = False
75OPTIONS.is_signing = False
76
77
78class OutputFile(object):
79  def __init__(self, output_zip, input_dir, prefix, name):
80    self._output_zip = output_zip
81    self.input_name = os.path.join(input_dir, prefix, name)
82
83    if self._output_zip:
84      self._zip_name = os.path.join(prefix, name)
85
86      root, suffix = os.path.splitext(name)
87      self.name = common.MakeTempFile(prefix=root + '-', suffix=suffix)
88    else:
89      self.name = self.input_name
90
91  def Write(self):
92    if self._output_zip:
93      common.ZipWrite(self._output_zip, self.name, self._zip_name)
94
95
96def GetCareMap(which, imgname):
97  """Generate care_map of system (or vendor) partition"""
98
99  assert which in ("system", "vendor")
100
101  simg = sparse_img.SparseImage(imgname)
102  care_map_list = [which]
103
104  care_map_ranges = simg.care_map
105  key = which + "_adjusted_partition_size"
106  adjusted_blocks = OPTIONS.info_dict.get(key)
107  if adjusted_blocks:
108    assert adjusted_blocks > 0, "blocks should be positive for " + which
109    care_map_ranges = care_map_ranges.intersect(rangelib.RangeSet(
110        "0-%d" % (adjusted_blocks,)))
111
112  care_map_list.append(care_map_ranges.to_string_raw())
113  return care_map_list
114
115
116def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
117  """Turn the contents of SYSTEM into a system image and store it in
118  output_zip. Returns the name of the system image file."""
119
120  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.img")
121  if os.path.exists(img.input_name):
122    print("system.img already exists in %s, no need to rebuild..." % (prefix,))
123    return img.input_name
124
125  def output_sink(fn, data):
126    ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
127    ofile.write(data)
128    ofile.close()
129
130    arc_name = "SYSTEM/" + fn
131    if arc_name in output_zip.namelist():
132      OPTIONS.replace_updated_files_list.append(arc_name)
133    else:
134      common.ZipWrite(output_zip, ofile.name, arc_name)
135
136  if OPTIONS.rebuild_recovery:
137    print("Building new recovery patch")
138    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
139                             boot_img, info_dict=OPTIONS.info_dict)
140
141  block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.map")
142  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
143              block_list=block_list)
144
145  return img.name
146
147
148def AddSystemOther(output_zip, prefix="IMAGES/"):
149  """Turn the contents of SYSTEM_OTHER into a system_other image
150  and store it in output_zip."""
151
152  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system_other.img")
153  if os.path.exists(img.input_name):
154    print("system_other.img already exists in %s, no need to rebuild..." % (
155        prefix,))
156    return
157
158  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
159
160
161def AddVendor(output_zip, prefix="IMAGES/"):
162  """Turn the contents of VENDOR into a vendor image and store in it
163  output_zip."""
164
165  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.img")
166  if os.path.exists(img.input_name):
167    print("vendor.img already exists in %s, no need to rebuild..." % (prefix,))
168    return img.input_name
169
170  block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.map")
171  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
172              block_list=block_list)
173  return img.name
174
175
176def AddDtbo(output_zip, prefix="IMAGES/"):
177  """Adds the DTBO image.
178
179  Uses the image under prefix if it already exists. Otherwise looks for the
180  image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
181  """
182
183  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "dtbo.img")
184  if os.path.exists(img.input_name):
185    print("dtbo.img already exists in %s, no need to rebuild..." % (prefix,))
186    return img.input_name
187
188  dtbo_prebuilt_path = os.path.join(
189      OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
190  assert os.path.exists(dtbo_prebuilt_path)
191  shutil.copy(dtbo_prebuilt_path, img.name)
192
193  # AVB-sign the image as needed.
194  if OPTIONS.info_dict.get("avb_enable") == "true":
195    avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
196    part_size = OPTIONS.info_dict["dtbo_size"]
197    # The AVB hash footer will be replaced if already present.
198    cmd = [avbtool, "add_hash_footer", "--image", img.name,
199           "--partition_size", str(part_size), "--partition_name", "dtbo"]
200    common.AppendAVBSigningArgs(cmd, "dtbo")
201    args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
202    if args and args.strip():
203      cmd.extend(shlex.split(args))
204    p = common.Run(cmd, stdout=subprocess.PIPE)
205    p.communicate()
206    assert p.returncode == 0, \
207        "avbtool add_hash_footer of %s failed" % (img.name,)
208
209  img.Write()
210  return img.name
211
212
213def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
214  print("creating " + what + ".img...")
215
216  # The name of the directory it is making an image out of matters to
217  # mkyaffs2image.  It wants "system" but we have a directory named
218  # "SYSTEM", so create a symlink.
219  temp_dir = tempfile.mkdtemp()
220  OPTIONS.tempfiles.append(temp_dir)
221  try:
222    os.symlink(os.path.join(input_dir, what.upper()),
223               os.path.join(temp_dir, what))
224  except OSError as e:
225    # bogus error on my mac version?
226    #   File "./build/tools/releasetools/img_from_target_files"
227    #     os.path.join(OPTIONS.input_tmp, "system"))
228    # OSError: [Errno 17] File exists
229    if e.errno == errno.EEXIST:
230      pass
231
232  image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
233  fstab = info_dict["fstab"]
234  mount_point = "/" + what
235  if fstab and mount_point in fstab:
236    image_props["fs_type"] = fstab[mount_point].fs_type
237
238  # Use a fixed timestamp (01/01/2009) when packaging the image.
239  # Bug: 24377993
240  epoch = datetime.datetime.fromtimestamp(0)
241  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
242  image_props["timestamp"] = int(timestamp)
243
244  if what == "system":
245    fs_config_prefix = ""
246  else:
247    fs_config_prefix = what + "_"
248
249  fs_config = os.path.join(
250      input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
251  if not os.path.exists(fs_config):
252    fs_config = None
253
254  # Override values loaded from info_dict.
255  if fs_config:
256    image_props["fs_config"] = fs_config
257  if block_list:
258    image_props["block_list"] = block_list.name
259
260  succ = build_image.BuildImage(os.path.join(temp_dir, what),
261                                image_props, output_file.name)
262  assert succ, "build " + what + ".img image failed"
263
264  output_file.Write()
265  if block_list:
266    block_list.Write()
267
268  # Set the 'adjusted_partition_size' that excludes the verity blocks of the
269  # given image. When avb is enabled, this size is the max image size returned
270  # by the avb tool.
271  is_verity_partition = "verity_block_device" in image_props
272  verity_supported = (image_props.get("verity") == "true" or
273                      image_props.get("avb_enable") == "true")
274  is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
275  if verity_supported and (is_verity_partition or is_avb_enable):
276    adjusted_blocks_value = image_props.get("partition_size")
277    if adjusted_blocks_value:
278      adjusted_blocks_key = what + "_adjusted_partition_size"
279      info_dict[adjusted_blocks_key] = int(adjusted_blocks_value)/4096 - 1
280
281
282def AddUserdata(output_zip, prefix="IMAGES/"):
283  """Create a userdata image and store it in output_zip.
284
285  In most case we just create and store an empty userdata.img;
286  But the invoker can also request to create userdata.img with real
287  data from the target files, by setting "userdata_img_with_data=true"
288  in OPTIONS.info_dict.
289  """
290
291  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "userdata.img")
292  if os.path.exists(img.input_name):
293    print("userdata.img already exists in %s, no need to rebuild..." % (
294        prefix,))
295    return
296
297  # Skip userdata.img if no size.
298  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
299  if not image_props.get("partition_size"):
300    return
301
302  print("creating userdata.img...")
303
304  # Use a fixed timestamp (01/01/2009) when packaging the image.
305  # Bug: 24377993
306  epoch = datetime.datetime.fromtimestamp(0)
307  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
308  image_props["timestamp"] = int(timestamp)
309
310  # The name of the directory it is making an image out of matters to
311  # mkyaffs2image.  So we create a temp dir, and within it we create an
312  # empty dir named "data", or a symlink to the DATA dir,
313  # and build the image from that.
314  temp_dir = tempfile.mkdtemp()
315  OPTIONS.tempfiles.append(temp_dir)
316  user_dir = os.path.join(temp_dir, "data")
317  empty = (OPTIONS.info_dict.get("userdata_img_with_data") != "true")
318  if empty:
319    # Create an empty dir.
320    os.mkdir(user_dir)
321  else:
322    # Symlink to the DATA dir.
323    os.symlink(os.path.join(OPTIONS.input_tmp, "DATA"),
324               user_dir)
325
326  fstab = OPTIONS.info_dict["fstab"]
327  if fstab:
328    image_props["fs_type"] = fstab["/data"].fs_type
329  succ = build_image.BuildImage(user_dir, image_props, img.name)
330  assert succ, "build userdata.img image failed"
331
332  common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
333  img.Write()
334
335
336def AppendVBMetaArgsForPartition(cmd, partition, img_path, public_key_dir):
337  if not img_path:
338    return
339
340  # Check if chain partition is used.
341  key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
342  if key_path:
343    # extract public key in AVB format to be included in vbmeta.img
344    avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
345    public_key_path = os.path.join(public_key_dir, "%s.avbpubkey" % partition)
346    p = common.Run([avbtool, "extract_public_key", "--key", key_path,
347                    "--output", public_key_path],
348                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
349    p.communicate()
350    assert p.returncode == 0, \
351        "avbtool extract_public_key fail for partition: %r" % partition
352
353    rollback_index_location = OPTIONS.info_dict[
354        "avb_" + partition + "_rollback_index_location"]
355    cmd.extend(["--chain_partition", "%s:%s:%s" % (
356        partition, rollback_index_location, public_key_path)])
357  else:
358    cmd.extend(["--include_descriptors_from_image", img_path])
359
360
361def AddVBMeta(output_zip, boot_img_path, system_img_path, vendor_img_path,
362              dtbo_img_path, prefix="IMAGES/"):
363  """Create a VBMeta image and store it in output_zip."""
364  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vbmeta.img")
365  avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
366  cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
367  common.AppendAVBSigningArgs(cmd, "vbmeta")
368
369  public_key_dir = tempfile.mkdtemp(prefix="avbpubkey-")
370  OPTIONS.tempfiles.append(public_key_dir)
371
372  AppendVBMetaArgsForPartition(cmd, "boot", boot_img_path, public_key_dir)
373  AppendVBMetaArgsForPartition(cmd, "system", system_img_path, public_key_dir)
374  AppendVBMetaArgsForPartition(cmd, "vendor", vendor_img_path, public_key_dir)
375  AppendVBMetaArgsForPartition(cmd, "dtbo", dtbo_img_path, public_key_dir)
376
377  args = OPTIONS.info_dict.get("avb_vbmeta_args")
378  if args and args.strip():
379    cmd.extend(shlex.split(args))
380
381  p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
382  p.communicate()
383  assert p.returncode == 0, "avbtool make_vbmeta_image failed"
384  img.Write()
385
386
387def AddPartitionTable(output_zip, prefix="IMAGES/"):
388  """Create a partition table image and store it in output_zip."""
389
390  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.img")
391  bpt = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.bpt")
392
393  # use BPTTOOL from environ, or "bpttool" if empty or not set.
394  bpttool = os.getenv("BPTTOOL") or "bpttool"
395  cmd = [bpttool, "make_table", "--output_json", bpt.name,
396         "--output_gpt", img.name]
397  input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
398  input_files = input_files_str.split(" ")
399  for i in input_files:
400    cmd.extend(["--input", i])
401  disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
402  if disk_size:
403    cmd.extend(["--disk_size", disk_size])
404  args = OPTIONS.info_dict.get("board_bpt_make_table_args")
405  if args:
406    cmd.extend(shlex.split(args))
407
408  p = common.Run(cmd, stdout=subprocess.PIPE)
409  p.communicate()
410  assert p.returncode == 0, "bpttool make_table failed"
411
412  img.Write()
413  bpt.Write()
414
415
416def AddCache(output_zip, prefix="IMAGES/"):
417  """Create an empty cache image and store it in output_zip."""
418
419  img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "cache.img")
420  if os.path.exists(img.input_name):
421    print("cache.img already exists in %s, no need to rebuild..." % (prefix,))
422    return
423
424  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
425  # The build system has to explicitly request for cache.img.
426  if "fs_type" not in image_props:
427    return
428
429  print("creating cache.img...")
430
431  # Use a fixed timestamp (01/01/2009) when packaging the image.
432  # Bug: 24377993
433  epoch = datetime.datetime.fromtimestamp(0)
434  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
435  image_props["timestamp"] = int(timestamp)
436
437  # The name of the directory it is making an image out of matters to
438  # mkyaffs2image.  So we create a temp dir, and within it we create an
439  # empty dir named "cache", and build the image from that.
440  temp_dir = tempfile.mkdtemp()
441  OPTIONS.tempfiles.append(temp_dir)
442  user_dir = os.path.join(temp_dir, "cache")
443  os.mkdir(user_dir)
444
445  fstab = OPTIONS.info_dict["fstab"]
446  if fstab:
447    image_props["fs_type"] = fstab["/cache"].fs_type
448  succ = build_image.BuildImage(user_dir, image_props, img.name)
449  assert succ, "build cache.img image failed"
450
451  common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
452  img.Write()
453
454
455def ReplaceUpdatedFiles(zip_filename, files_list):
456  """Update all the zip entries listed in the files_list.
457
458  For now the list includes META/care_map.txt, and the related files under
459  SYSTEM/ after rebuilding recovery.
460  """
461
462  cmd = ["zip", "-d", zip_filename] + files_list
463  p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
464  p.communicate()
465
466  output_zip = zipfile.ZipFile(zip_filename, "a",
467                               compression=zipfile.ZIP_DEFLATED,
468                               allowZip64=True)
469  for item in files_list:
470    file_path = os.path.join(OPTIONS.input_tmp, item)
471    assert os.path.exists(file_path)
472    common.ZipWrite(output_zip, file_path, arcname=item)
473  common.ZipClose(output_zip)
474
475
476def AddImagesToTargetFiles(filename):
477  if os.path.isdir(filename):
478    OPTIONS.input_tmp = os.path.abspath(filename)
479    input_zip = None
480  else:
481    OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)
482
483  if not OPTIONS.add_missing:
484    if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
485      print("target_files appears to already contain images.")
486      sys.exit(1)
487
488  has_vendor = os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR"))
489  has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
490                                                "SYSTEM_OTHER"))
491
492  if input_zip:
493    OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)
494
495    common.ZipClose(input_zip)
496    output_zip = zipfile.ZipFile(filename, "a",
497                                 compression=zipfile.ZIP_DEFLATED,
498                                 allowZip64=True)
499  else:
500    OPTIONS.info_dict = common.LoadInfoDict(filename, filename)
501    output_zip = None
502    images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
503    if not os.path.isdir(images_dir):
504      os.makedirs(images_dir)
505    images_dir = None
506
507  has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
508
509  def banner(s):
510    print("\n\n++++ " + s + " ++++\n\n")
511
512  prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
513  boot_image = None
514  if os.path.exists(prebuilt_path):
515    banner("boot")
516    print("boot.img already exists in IMAGES/, no need to rebuild...")
517    if OPTIONS.rebuild_recovery:
518      boot_image = common.GetBootableImage(
519          "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
520  else:
521    banner("boot")
522    boot_image = common.GetBootableImage(
523        "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
524    if boot_image:
525      if output_zip:
526        boot_image.AddToZip(output_zip)
527      else:
528        boot_image.WriteToDir(OPTIONS.input_tmp)
529
530  recovery_image = None
531  if has_recovery:
532    banner("recovery")
533    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
534    if os.path.exists(prebuilt_path):
535      print("recovery.img already exists in IMAGES/, no need to rebuild...")
536      if OPTIONS.rebuild_recovery:
537        recovery_image = common.GetBootableImage(
538            "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
539            "RECOVERY")
540    else:
541      recovery_image = common.GetBootableImage(
542          "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
543      if recovery_image:
544        if output_zip:
545          recovery_image.AddToZip(output_zip)
546        else:
547          recovery_image.WriteToDir(OPTIONS.input_tmp)
548
549      banner("recovery (two-step image)")
550      # The special recovery.img for two-step package use.
551      recovery_two_step_image = common.GetBootableImage(
552          "IMAGES/recovery-two-step.img", "recovery-two-step.img",
553          OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
554      if recovery_two_step_image:
555        if output_zip:
556          recovery_two_step_image.AddToZip(output_zip)
557        else:
558          recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
559
560  banner("system")
561  system_img_path = AddSystem(
562      output_zip, recovery_img=recovery_image, boot_img=boot_image)
563  vendor_img_path = None
564  if has_vendor:
565    banner("vendor")
566    vendor_img_path = AddVendor(output_zip)
567  if has_system_other:
568    banner("system_other")
569    AddSystemOther(output_zip)
570  if not OPTIONS.is_signing:
571    banner("userdata")
572    AddUserdata(output_zip)
573    banner("cache")
574    AddCache(output_zip)
575
576  if OPTIONS.info_dict.get("board_bpt_enable") == "true":
577    banner("partition-table")
578    AddPartitionTable(output_zip)
579
580  dtbo_img_path = None
581  if OPTIONS.info_dict.get("has_dtbo") == "true":
582    banner("dtbo")
583    dtbo_img_path = AddDtbo(output_zip)
584
585  if OPTIONS.info_dict.get("avb_enable") == "true":
586    banner("vbmeta")
587    boot_contents = boot_image.WriteToTemp()
588    AddVBMeta(output_zip, boot_contents.name, system_img_path,
589              vendor_img_path, dtbo_img_path)
590
591  # For devices using A/B update, copy over images from RADIO/ and/or
592  # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
593  # images ready under IMAGES/. All images should have '.img' as extension.
594  banner("radio")
595  ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt")
596  if os.path.exists(ab_partitions):
597    with open(ab_partitions, 'r') as f:
598      lines = f.readlines()
599    # For devices using A/B update, generate care_map for system and vendor
600    # partitions (if present), then write this file to target_files package.
601    care_map_list = []
602    for line in lines:
603      if line.strip() == "system" and (
604          "system_verity_block_device" in OPTIONS.info_dict or
605          OPTIONS.info_dict.get("system_avb_hashtree_enable") == "true"):
606        assert os.path.exists(system_img_path)
607        care_map_list += GetCareMap("system", system_img_path)
608      if line.strip() == "vendor" and (
609          "vendor_verity_block_device" in OPTIONS.info_dict or
610          OPTIONS.info_dict.get("vendor_avb_hashtree_enable") == "true"):
611        assert os.path.exists(vendor_img_path)
612        care_map_list += GetCareMap("vendor", vendor_img_path)
613
614      img_name = line.strip() + ".img"
615      prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
616      if os.path.exists(prebuilt_path):
617        print("%s already exists, no need to overwrite..." % (img_name,))
618        continue
619
620      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
621      img_vendor_dir = os.path.join(
622        OPTIONS.input_tmp, "VENDOR_IMAGES")
623      if os.path.exists(img_radio_path):
624        if output_zip:
625          common.ZipWrite(output_zip, img_radio_path,
626                          os.path.join("IMAGES", img_name))
627        else:
628          shutil.copy(img_radio_path, prebuilt_path)
629      else:
630        for root, _, files in os.walk(img_vendor_dir):
631          if img_name in files:
632            if output_zip:
633              common.ZipWrite(output_zip, os.path.join(root, img_name),
634                os.path.join("IMAGES", img_name))
635            else:
636              shutil.copy(os.path.join(root, img_name), prebuilt_path)
637            break
638
639      if output_zip:
640        # Zip spec says: All slashes MUST be forward slashes.
641        img_path = 'IMAGES/' + img_name
642        assert img_path in output_zip.namelist(), "cannot find " + img_name
643      else:
644        img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
645        assert os.path.exists(img_path), "cannot find " + img_name
646
647    if care_map_list:
648      care_map_path = "META/care_map.txt"
649      if output_zip and care_map_path not in output_zip.namelist():
650        common.ZipWriteStr(output_zip, care_map_path, '\n'.join(care_map_list))
651      else:
652        with open(os.path.join(OPTIONS.input_tmp, care_map_path), 'w') as fp:
653          fp.write('\n'.join(care_map_list))
654        if output_zip:
655          OPTIONS.replace_updated_files_list.append(care_map_path)
656
657  if output_zip:
658    common.ZipClose(output_zip)
659    if OPTIONS.replace_updated_files_list:
660      ReplaceUpdatedFiles(output_zip.filename,
661                          OPTIONS.replace_updated_files_list)
662
663
664def main(argv):
665  def option_handler(o, a):
666    if o in ("-a", "--add_missing"):
667      OPTIONS.add_missing = True
668    elif o in ("-r", "--rebuild_recovery",):
669      OPTIONS.rebuild_recovery = True
670    elif o == "--replace_verity_private_key":
671      OPTIONS.replace_verity_private_key = (True, a)
672    elif o == "--replace_verity_public_key":
673      OPTIONS.replace_verity_public_key = (True, a)
674    elif o == "--is_signing":
675      OPTIONS.is_signing = True
676    else:
677      return False
678    return True
679
680  args = common.ParseOptions(
681      argv, __doc__, extra_opts="ar",
682      extra_long_opts=["add_missing", "rebuild_recovery",
683                       "replace_verity_public_key=",
684                       "replace_verity_private_key=",
685                       "is_signing"],
686      extra_option_handler=option_handler)
687
688
689  if len(args) != 1:
690    common.Usage(__doc__)
691    sys.exit(1)
692
693  AddImagesToTargetFiles(args[0])
694  print("done.")
695
696if __name__ == '__main__':
697  try:
698    common.CloseInheritedPipes()
699    main(sys.argv[1:])
700  except common.ExternalError as e:
701    print("\n   ERROR: %s\n" % (e,))
702    sys.exit(1)
703  finally:
704    common.Cleanup()
705