• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright (C) 2011 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"""
18Build image output_image_file from input_directory, properties_file, and target_out_dir
19
20Usage:  build_image input_directory properties_file output_image_file target_out_dir
21
22"""
23import os
24import os.path
25import re
26import subprocess
27import sys
28import common
29import shlex
30import shutil
31import sparse_img
32import tempfile
33
34OPTIONS = common.OPTIONS
35
36FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"
37BLOCK_SIZE = 4096
38
39def RunCommand(cmd):
40  """Echo and run the given command.
41
42  Args:
43    cmd: the command represented as a list of strings.
44  Returns:
45    A tuple of the output and the exit code.
46  """
47  print "Running: ", " ".join(cmd)
48  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
49  output, _ = p.communicate()
50  print "%s" % (output.rstrip(),)
51  return (output, p.returncode)
52
53def GetVerityFECSize(partition_size):
54  cmd = ["fec", "-s", str(partition_size)]
55  output, exit_code = RunCommand(cmd)
56  if exit_code != 0:
57    return False, 0
58  return True, int(output)
59
60def GetVerityTreeSize(partition_size):
61  cmd = ["build_verity_tree", "-s", str(partition_size)]
62  output, exit_code = RunCommand(cmd)
63  if exit_code != 0:
64    return False, 0
65  return True, int(output)
66
67def GetVerityMetadataSize(partition_size):
68  cmd = ["system/extras/verity/build_verity_metadata.py", "size",
69         str(partition_size)]
70  output, exit_code = RunCommand(cmd)
71  if exit_code != 0:
72    return False, 0
73  return True, int(output)
74
75def GetVeritySize(partition_size, fec_supported):
76  success, verity_tree_size = GetVerityTreeSize(partition_size)
77  if not success:
78    return 0
79  success, verity_metadata_size = GetVerityMetadataSize(partition_size)
80  if not success:
81    return 0
82  verity_size = verity_tree_size + verity_metadata_size
83  if fec_supported:
84    success, fec_size = GetVerityFECSize(partition_size + verity_size)
85    if not success:
86      return 0
87    return verity_size + fec_size
88  return verity_size
89
90def GetSimgSize(image_file):
91  simg = sparse_img.SparseImage(image_file, build_map=False)
92  return simg.blocksize * simg.total_blocks
93
94def ZeroPadSimg(image_file, pad_size):
95  blocks = pad_size // BLOCK_SIZE
96  print("Padding %d blocks (%d bytes)" % (blocks, pad_size))
97  simg = sparse_img.SparseImage(image_file, mode="r+b", build_map=False)
98  simg.AppendFillChunk(0, blocks)
99
100def AVBCalcMaxImageSize(avbtool, partition_size, additional_args):
101  """Calculates max image size for a given partition size.
102
103  Args:
104    avbtool: String with path to avbtool.
105    partition_size: The size of the partition in question.
106    additional_args: Additional arguments to pass to 'avbtool
107      add_hashtree_image'.
108  Returns:
109    The maximum image size or 0 if an error occurred.
110  """
111  cmdline = "%s add_hashtree_footer " % avbtool
112  cmdline += "--partition_size %d " % partition_size
113  cmdline += "--calc_max_image_size "
114  cmdline += additional_args
115  (output, exit_code) = RunCommand(shlex.split(cmdline))
116  if exit_code != 0:
117    return 0
118  else:
119    return int(output)
120
121def AVBAddHashtree(image_path, avbtool, partition_size, partition_name,
122                   signing_args, additional_args):
123  """Adds dm-verity hashtree and AVB metadata to an image.
124
125  Args:
126    image_path: Path to image to modify.
127    avbtool: String with path to avbtool.
128    partition_size: The size of the partition in question.
129    partition_name: The name of the partition - will be embedded in metadata.
130    signing_args: Arguments for signing the image.
131    additional_args: Additional arguments to pass to 'avbtool
132      add_hashtree_image'.
133  Returns:
134    True if the operation succeeded.
135  """
136  cmdline = "%s add_hashtree_footer " % avbtool
137  cmdline += "--partition_size %d " % partition_size
138  cmdline += "--partition_name %s " % partition_name
139  cmdline += "--image %s " % image_path
140  cmdline += signing_args + " "
141  cmdline += additional_args
142  (_, exit_code) = RunCommand(shlex.split(cmdline))
143  return exit_code == 0
144
145def AdjustPartitionSizeForVerity(partition_size, fec_supported):
146  """Modifies the provided partition size to account for the verity metadata.
147
148  This information is used to size the created image appropriately.
149  Args:
150    partition_size: the size of the partition to be verified.
151  Returns:
152    A tuple of the size of the partition adjusted for verity metadata, and
153    the size of verity metadata.
154  """
155  key = "%d %d" % (partition_size, fec_supported)
156  if key in AdjustPartitionSizeForVerity.results:
157    return AdjustPartitionSizeForVerity.results[key]
158
159  hi = partition_size
160  if hi % BLOCK_SIZE != 0:
161    hi = (hi // BLOCK_SIZE) * BLOCK_SIZE
162
163  # verity tree and fec sizes depend on the partition size, which
164  # means this estimate is always going to be unnecessarily small
165  verity_size = GetVeritySize(hi, fec_supported)
166  lo = partition_size - verity_size
167  result = lo
168
169  # do a binary search for the optimal size
170  while lo < hi:
171    i = ((lo + hi) // (2 * BLOCK_SIZE)) * BLOCK_SIZE
172    v = GetVeritySize(i, fec_supported)
173    if i + v <= partition_size:
174      if result < i:
175        result = i
176        verity_size = v
177      lo = i + BLOCK_SIZE
178    else:
179      hi = i
180
181  AdjustPartitionSizeForVerity.results[key] = (result, verity_size)
182  return (result, verity_size)
183
184AdjustPartitionSizeForVerity.results = {}
185
186def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path,
187                   padding_size):
188  cmd = ["fec", "-e", "-p", str(padding_size), sparse_image_path,
189         verity_path, verity_fec_path]
190  output, exit_code = RunCommand(cmd)
191  if exit_code != 0:
192    print "Could not build FEC data! Error: %s" % output
193    return False
194  return True
195
196def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
197  cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path,
198         verity_image_path]
199  output, exit_code = RunCommand(cmd)
200  if exit_code != 0:
201    print "Could not build verity tree! Error: %s" % output
202    return False
203  root, salt = output.split()
204  prop_dict["verity_root_hash"] = root
205  prop_dict["verity_salt"] = salt
206  return True
207
208def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
209                        block_device, signer_path, key, signer_args):
210  cmd = ["system/extras/verity/build_verity_metadata.py", "build",
211         str(image_size), verity_metadata_path, root_hash, salt, block_device,
212         signer_path, key]
213  if signer_args:
214    cmd.append("--signer_args=\"%s\"" % (' '.join(signer_args),))
215  output, exit_code = RunCommand(cmd)
216  if exit_code != 0:
217    print "Could not build verity metadata! Error: %s" % output
218    return False
219  return True
220
221def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
222  """Appends the unsparse image to the given sparse image.
223
224  Args:
225    sparse_image_path: the path to the (sparse) image
226    unsparse_image_path: the path to the (unsparse) image
227  Returns:
228    True on success, False on failure.
229  """
230  cmd = ["append2simg", sparse_image_path, unsparse_image_path]
231  output, exit_code = RunCommand(cmd)
232  if exit_code != 0:
233    print "%s: %s" % (error_message, output)
234    return False
235  return True
236
237def Append(target, file_to_append, error_message):
238  print "appending %s to %s" % (file_to_append, target)
239  with open(target, "a") as out_file:
240    with open(file_to_append, "r") as input_file:
241      for line in input_file:
242        out_file.write(line)
243  return True
244
245def BuildVerifiedImage(data_image_path, verity_image_path,
246                       verity_metadata_path, verity_fec_path,
247                       padding_size, fec_supported):
248  if not Append(verity_image_path, verity_metadata_path,
249                "Could not append verity metadata!"):
250    return False
251
252  if fec_supported:
253    # build FEC for the entire partition, including metadata
254    if not BuildVerityFEC(data_image_path, verity_image_path,
255                          verity_fec_path, padding_size):
256      return False
257
258    if not Append(verity_image_path, verity_fec_path, "Could not append FEC!"):
259      return False
260
261  if not Append2Simg(data_image_path, verity_image_path,
262                     "Could not append verity data!"):
263    return False
264  return True
265
266def UnsparseImage(sparse_image_path, replace=True):
267  img_dir = os.path.dirname(sparse_image_path)
268  unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path)
269  unsparse_image_path = os.path.join(img_dir, unsparse_image_path)
270  if os.path.exists(unsparse_image_path):
271    if replace:
272      os.unlink(unsparse_image_path)
273    else:
274      return True, unsparse_image_path
275  inflate_command = ["simg2img", sparse_image_path, unsparse_image_path]
276  (_, exit_code) = RunCommand(inflate_command)
277  if exit_code != 0:
278    os.remove(unsparse_image_path)
279    return False, None
280  return True, unsparse_image_path
281
282def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
283  """Creates an image that is verifiable using dm-verity.
284
285  Args:
286    out_file: the location to write the verifiable image at
287    prop_dict: a dictionary of properties required for image creation and
288               verification
289  Returns:
290    True on success, False otherwise.
291  """
292  # get properties
293  image_size = int(prop_dict["partition_size"])
294  block_dev = prop_dict["verity_block_device"]
295  signer_key = prop_dict["verity_key"] + ".pk8"
296  if OPTIONS.verity_signer_path is not None:
297    signer_path = OPTIONS.verity_signer_path
298  else:
299    signer_path = prop_dict["verity_signer_cmd"]
300  signer_args = OPTIONS.verity_signer_args
301
302  # make a tempdir
303  tempdir_name = tempfile.mkdtemp(suffix="_verity_images")
304
305  # get partial image paths
306  verity_image_path = os.path.join(tempdir_name, "verity.img")
307  verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img")
308  verity_fec_path = os.path.join(tempdir_name, "verity_fec.img")
309
310  # build the verity tree and get the root hash and salt
311  if not BuildVerityTree(out_file, verity_image_path, prop_dict):
312    shutil.rmtree(tempdir_name, ignore_errors=True)
313    return False
314
315  # build the metadata blocks
316  root_hash = prop_dict["verity_root_hash"]
317  salt = prop_dict["verity_salt"]
318  if not BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
319                             block_dev, signer_path, signer_key, signer_args):
320    shutil.rmtree(tempdir_name, ignore_errors=True)
321    return False
322
323  # build the full verified image
324  target_size = int(prop_dict["original_partition_size"])
325  verity_size = int(prop_dict["verity_size"])
326
327  padding_size = target_size - image_size - verity_size
328  assert padding_size >= 0
329
330  if not BuildVerifiedImage(out_file,
331                            verity_image_path,
332                            verity_metadata_path,
333                            verity_fec_path,
334                            padding_size,
335                            fec_supported):
336    shutil.rmtree(tempdir_name, ignore_errors=True)
337    return False
338
339  shutil.rmtree(tempdir_name, ignore_errors=True)
340  return True
341
342def ConvertBlockMapToBaseFs(block_map_file):
343  fd, base_fs_file = tempfile.mkstemp(prefix="script_gen_",
344                                      suffix=".base_fs")
345  os.close(fd)
346
347  convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file]
348  (_, exit_code) = RunCommand(convert_command)
349  if exit_code != 0:
350    os.remove(base_fs_file)
351    return None
352  return base_fs_file
353
354def BuildImage(in_dir, prop_dict, out_file, target_out=None):
355  """Build an image to out_file from in_dir with property prop_dict.
356
357  Args:
358    in_dir: path of input directory.
359    prop_dict: property dictionary.
360    out_file: path of the output image file.
361    target_out: path of the product out directory to read device specific FS config files.
362
363  Returns:
364    True iff the image is built successfully.
365  """
366  # system_root_image=true: build a system.img that combines the contents of
367  # /system and the ramdisk, and can be mounted at the root of the file system.
368  origin_in = in_dir
369  fs_config = prop_dict.get("fs_config")
370  base_fs_file = None
371  if (prop_dict.get("system_root_image") == "true"
372      and prop_dict["mount_point"] == "system"):
373    in_dir = tempfile.mkdtemp()
374    # Change the mount point to "/"
375    prop_dict["mount_point"] = "/"
376    if fs_config:
377      # We need to merge the fs_config files of system and ramdisk.
378      fd, merged_fs_config = tempfile.mkstemp(prefix="root_fs_config",
379                                              suffix=".txt")
380      os.close(fd)
381      with open(merged_fs_config, "w") as fw:
382        if "ramdisk_fs_config" in prop_dict:
383          with open(prop_dict["ramdisk_fs_config"]) as fr:
384            fw.writelines(fr.readlines())
385        with open(fs_config) as fr:
386          fw.writelines(fr.readlines())
387      fs_config = merged_fs_config
388
389  build_command = []
390  fs_type = prop_dict.get("fs_type", "")
391  run_fsck = False
392
393  fs_spans_partition = True
394  if fs_type.startswith("squash"):
395    fs_spans_partition = False
396
397  is_verity_partition = "verity_block_device" in prop_dict
398  verity_supported = prop_dict.get("verity") == "true"
399  verity_fec_supported = prop_dict.get("verity_fec") == "true"
400
401  # Adjust the partition size to make room for the hashes if this is to be
402  # verified.
403  if verity_supported and is_verity_partition:
404    partition_size = int(prop_dict.get("partition_size"))
405    (adjusted_size, verity_size) = AdjustPartitionSizeForVerity(partition_size,
406                                                                verity_fec_supported)
407    if not adjusted_size:
408      return False
409    prop_dict["partition_size"] = str(adjusted_size)
410    prop_dict["original_partition_size"] = str(partition_size)
411    prop_dict["verity_size"] = str(verity_size)
412
413  # Adjust partition size for AVB.
414  if prop_dict.get("avb_enable") == "true":
415    avbtool = prop_dict.get("avb_avbtool")
416    partition_size = int(prop_dict.get("partition_size"))
417    additional_args = prop_dict["avb_add_hashtree_footer_args"]
418    max_image_size = AVBCalcMaxImageSize(avbtool, partition_size,
419                                         additional_args)
420    if max_image_size == 0:
421      return False
422    prop_dict["partition_size"] = str(max_image_size)
423    prop_dict["original_partition_size"] = str(partition_size)
424
425  if fs_type.startswith("ext"):
426    build_command = [prop_dict["ext_mkuserimg"]]
427    if "extfs_sparse_flag" in prop_dict:
428      build_command.append(prop_dict["extfs_sparse_flag"])
429      run_fsck = True
430    build_command.extend([in_dir, out_file, fs_type,
431                          prop_dict["mount_point"]])
432    build_command.append(prop_dict["partition_size"])
433    if "journal_size" in prop_dict:
434      build_command.extend(["-j", prop_dict["journal_size"]])
435    if "timestamp" in prop_dict:
436      build_command.extend(["-T", str(prop_dict["timestamp"])])
437    if fs_config:
438      build_command.extend(["-C", fs_config])
439    if target_out:
440      build_command.extend(["-D", target_out])
441    if "block_list" in prop_dict:
442      build_command.extend(["-B", prop_dict["block_list"]])
443    if "base_fs_file" in prop_dict:
444      base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])
445      if base_fs_file is None:
446        return False
447      build_command.extend(["-d", base_fs_file])
448    build_command.extend(["-L", prop_dict["mount_point"]])
449    if "extfs_inode_count" in prop_dict:
450      build_command.extend(["-i", prop_dict["extfs_inode_count"]])
451    if "flash_erase_block_size" in prop_dict:
452      build_command.extend(["-e", prop_dict["flash_erase_block_size"]])
453    if "flash_logical_block_size" in prop_dict:
454      build_command.extend(["-o", prop_dict["flash_logical_block_size"]])
455    if "selinux_fc" in prop_dict:
456      build_command.append(prop_dict["selinux_fc"])
457  elif fs_type.startswith("squash"):
458    build_command = ["mksquashfsimage.sh"]
459    build_command.extend([in_dir, out_file])
460    if "squashfs_sparse_flag" in prop_dict:
461      build_command.extend([prop_dict["squashfs_sparse_flag"]])
462    build_command.extend(["-m", prop_dict["mount_point"]])
463    if target_out:
464      build_command.extend(["-d", target_out])
465    if fs_config:
466      build_command.extend(["-C", fs_config])
467    if "selinux_fc" in prop_dict:
468      build_command.extend(["-c", prop_dict["selinux_fc"]])
469    if "block_list" in prop_dict:
470      build_command.extend(["-B", prop_dict["block_list"]])
471    if "squashfs_compressor" in prop_dict:
472      build_command.extend(["-z", prop_dict["squashfs_compressor"]])
473    if "squashfs_compressor_opt" in prop_dict:
474      build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]])
475    if "squashfs_block_size" in prop_dict:
476      build_command.extend(["-b", prop_dict["squashfs_block_size"]])
477    if "squashfs_disable_4k_align" in prop_dict and prop_dict.get("squashfs_disable_4k_align") == "true":
478      build_command.extend(["-a"])
479  elif fs_type.startswith("f2fs"):
480    build_command = ["mkf2fsuserimg.sh"]
481    build_command.extend([out_file, prop_dict["partition_size"]])
482  else:
483    print("Error: unknown filesystem type '%s'" % (fs_type))
484    return False
485
486  if in_dir != origin_in:
487    # Construct a staging directory of the root file system.
488    ramdisk_dir = prop_dict.get("ramdisk_dir")
489    if ramdisk_dir:
490      shutil.rmtree(in_dir)
491      shutil.copytree(ramdisk_dir, in_dir, symlinks=True)
492    staging_system = os.path.join(in_dir, "system")
493    shutil.rmtree(staging_system, ignore_errors=True)
494    shutil.copytree(origin_in, staging_system, symlinks=True)
495
496  has_reserved_blocks = prop_dict.get("has_ext4_reserved_blocks") == "true"
497  ext4fs_output = None
498
499  try:
500    if fs_type.startswith("ext4"):
501      (ext4fs_output, exit_code) = RunCommand(build_command)
502    else:
503      (_, exit_code) = RunCommand(build_command)
504  finally:
505    if in_dir != origin_in:
506      # Clean up temporary directories and files.
507      shutil.rmtree(in_dir, ignore_errors=True)
508      if fs_config:
509        os.remove(fs_config)
510    if base_fs_file is not None:
511      os.remove(base_fs_file)
512  if exit_code != 0:
513    return False
514
515  # Bug: 21522719, 22023465
516  # There are some reserved blocks on ext4 FS (lesser of 4096 blocks and 2%).
517  # We need to deduct those blocks from the available space, since they are
518  # not writable even with root privilege. It only affects devices using
519  # file-based OTA and a kernel version of 3.10 or greater (currently just
520  # sprout).
521  # Separately, check if there's enough headroom space available. This is useful for
522  # devices with low disk space that have system image variation between builds.
523  if (has_reserved_blocks or "partition_headroom" in prop_dict) and fs_type.startswith("ext4"):
524    assert ext4fs_output is not None
525    ext4fs_stats = re.compile(
526        r'Created filesystem with .* (?P<used_blocks>[0-9]+)/'
527        r'(?P<total_blocks>[0-9]+) blocks')
528    m = ext4fs_stats.match(ext4fs_output.strip().split('\n')[-1])
529    used_blocks = int(m.groupdict().get('used_blocks'))
530    total_blocks = int(m.groupdict().get('total_blocks'))
531    reserved_blocks = 0
532    headroom_blocks = 0
533    adjusted_blocks = total_blocks
534    if has_reserved_blocks:
535      reserved_blocks = min(4096, int(total_blocks * 0.02))
536      adjusted_blocks -= reserved_blocks
537    if "partition_headroom" in prop_dict:
538      headroom_blocks = int(prop_dict.get('partition_headroom')) / BLOCK_SIZE
539      adjusted_blocks -= headroom_blocks
540    if used_blocks > adjusted_blocks:
541      mount_point = prop_dict.get("mount_point")
542      print("Error: Not enough room on %s (total: %d blocks, used: %d blocks, "
543            "reserved: %d blocks, headroom: %d blocks, available: %d blocks)" % (
544                mount_point, total_blocks, used_blocks, reserved_blocks,
545                headroom_blocks, adjusted_blocks))
546      return False
547
548  if not fs_spans_partition:
549    mount_point = prop_dict.get("mount_point")
550    partition_size = int(prop_dict.get("partition_size"))
551    image_size = GetSimgSize(out_file)
552    if image_size > partition_size:
553      print("Error: %s image size of %d is larger than partition size of "
554            "%d" % (mount_point, image_size, partition_size))
555      return False
556    if verity_supported and is_verity_partition:
557      ZeroPadSimg(out_file, partition_size - image_size)
558
559  # create the verified image if this is to be verified
560  if verity_supported and is_verity_partition:
561    if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict):
562      return False
563
564  # Add AVB hashtree and metadata.
565  if "avb_enable" in prop_dict:
566    avbtool = prop_dict.get("avb_avbtool")
567    original_partition_size = int(prop_dict.get("original_partition_size"))
568    partition_name = prop_dict["partition_name"]
569    signing_args = prop_dict["avb_signing_args"]
570    additional_args = prop_dict["avb_add_hashtree_footer_args"]
571    if not AVBAddHashtree(out_file, avbtool, original_partition_size,
572                          partition_name, signing_args, additional_args):
573      return False
574
575  if run_fsck and prop_dict.get("skip_fsck") != "true":
576    success, unsparse_image = UnsparseImage(out_file, replace=False)
577    if not success:
578      return False
579
580    # Run e2fsck on the inflated image file
581    e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
582    (_, exit_code) = RunCommand(e2fsck_command)
583
584    os.remove(unsparse_image)
585
586  return exit_code == 0
587
588
589def ImagePropFromGlobalDict(glob_dict, mount_point):
590  """Build an image property dictionary from the global dictionary.
591
592  Args:
593    glob_dict: the global dictionary from the build system.
594    mount_point: such as "system", "data" etc.
595  """
596  d = {}
597
598  if "build.prop" in glob_dict:
599    bp = glob_dict["build.prop"]
600    if "ro.build.date.utc" in bp:
601      d["timestamp"] = bp["ro.build.date.utc"]
602
603  def copy_prop(src_p, dest_p):
604    if src_p in glob_dict:
605      d[dest_p] = str(glob_dict[src_p])
606
607  common_props = (
608      "extfs_sparse_flag",
609      "squashfs_sparse_flag",
610      "selinux_fc",
611      "skip_fsck",
612      "ext_mkuserimg",
613      "verity",
614      "verity_key",
615      "verity_signer_cmd",
616      "verity_fec",
617      "avb_signing_args",
618      "avb_avbtool"
619      )
620  for p in common_props:
621    copy_prop(p, p)
622
623  d["mount_point"] = mount_point
624  if mount_point == "system":
625    copy_prop("fs_type", "fs_type")
626    # Copy the generic system fs type first, override with specific one if
627    # available.
628    copy_prop("system_fs_type", "fs_type")
629    copy_prop("system_headroom", "partition_headroom")
630    copy_prop("system_size", "partition_size")
631    copy_prop("system_journal_size", "journal_size")
632    copy_prop("system_verity_block_device", "verity_block_device")
633    copy_prop("system_root_image", "system_root_image")
634    copy_prop("ramdisk_dir", "ramdisk_dir")
635    copy_prop("ramdisk_fs_config", "ramdisk_fs_config")
636    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks")
637    copy_prop("system_squashfs_compressor", "squashfs_compressor")
638    copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
639    copy_prop("system_squashfs_block_size", "squashfs_block_size")
640    copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")
641    copy_prop("system_base_fs_file", "base_fs_file")
642    copy_prop("system_avb_enable", "avb_enable")
643    copy_prop("system_avb_add_hashtree_footer_args",
644              "avb_add_hashtree_footer_args")
645    copy_prop("system_extfs_inode_count", "extfs_inode_count")
646  elif mount_point == "system_other":
647    # We inherit the selinux policies of /system since we contain some of its files.
648    d["mount_point"] = "system"
649    copy_prop("fs_type", "fs_type")
650    copy_prop("system_fs_type", "fs_type")
651    copy_prop("system_size", "partition_size")
652    copy_prop("system_journal_size", "journal_size")
653    copy_prop("system_verity_block_device", "verity_block_device")
654    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks")
655    copy_prop("system_squashfs_compressor", "squashfs_compressor")
656    copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
657    copy_prop("system_squashfs_block_size", "squashfs_block_size")
658    copy_prop("system_base_fs_file", "base_fs_file")
659    copy_prop("system_avb_enable", "avb_enable")
660    copy_prop("system_avb_add_hashtree_footer_args",
661              "avb_add_hashtree_footer_args")
662    copy_prop("system_extfs_inode_count", "extfs_inode_count")
663  elif mount_point == "data":
664    # Copy the generic fs type first, override with specific one if available.
665    copy_prop("fs_type", "fs_type")
666    copy_prop("userdata_fs_type", "fs_type")
667    copy_prop("userdata_size", "partition_size")
668    copy_prop("flash_logical_block_size","flash_logical_block_size")
669    copy_prop("flash_erase_block_size", "flash_erase_block_size")
670  elif mount_point == "cache":
671    copy_prop("cache_fs_type", "fs_type")
672    copy_prop("cache_size", "partition_size")
673  elif mount_point == "vendor":
674    copy_prop("vendor_fs_type", "fs_type")
675    copy_prop("vendor_size", "partition_size")
676    copy_prop("vendor_journal_size", "journal_size")
677    copy_prop("vendor_verity_block_device", "verity_block_device")
678    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks")
679    copy_prop("vendor_squashfs_compressor", "squashfs_compressor")
680    copy_prop("vendor_squashfs_compressor_opt", "squashfs_compressor_opt")
681    copy_prop("vendor_squashfs_block_size", "squashfs_block_size")
682    copy_prop("vendor_squashfs_disable_4k_align", "squashfs_disable_4k_align")
683    copy_prop("vendor_base_fs_file", "base_fs_file")
684    copy_prop("vendor_avb_enable", "avb_enable")
685    copy_prop("vendor_avb_add_hashtree_footer_args",
686              "avb_add_hashtree_footer_args")
687    copy_prop("vendor_extfs_inode_count", "extfs_inode_count")
688  elif mount_point == "oem":
689    copy_prop("fs_type", "fs_type")
690    copy_prop("oem_size", "partition_size")
691    copy_prop("oem_journal_size", "journal_size")
692    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks")
693    copy_prop("oem_extfs_inode_count", "extfs_inode_count")
694  d["partition_name"] = mount_point
695  return d
696
697
698def LoadGlobalDict(filename):
699  """Load "name=value" pairs from filename"""
700  d = {}
701  f = open(filename)
702  for line in f:
703    line = line.strip()
704    if not line or line.startswith("#"):
705      continue
706    k, v = line.split("=", 1)
707    d[k] = v
708  f.close()
709  return d
710
711
712def main(argv):
713  if len(argv) != 4:
714    print __doc__
715    sys.exit(1)
716
717  in_dir = argv[0]
718  glob_dict_file = argv[1]
719  out_file = argv[2]
720  target_out = argv[3]
721
722  glob_dict = LoadGlobalDict(glob_dict_file)
723  if "mount_point" in glob_dict:
724    # The caller knows the mount point and provides a dictionay needed by
725    # BuildImage().
726    image_properties = glob_dict
727  else:
728    image_filename = os.path.basename(out_file)
729    mount_point = ""
730    if image_filename == "system.img":
731      mount_point = "system"
732    elif image_filename == "system_other.img":
733      mount_point = "system_other"
734    elif image_filename == "userdata.img":
735      mount_point = "data"
736    elif image_filename == "cache.img":
737      mount_point = "cache"
738    elif image_filename == "vendor.img":
739      mount_point = "vendor"
740    elif image_filename == "oem.img":
741      mount_point = "oem"
742    else:
743      print >> sys.stderr, "error: unknown image file name ", image_filename
744      exit(1)
745
746    image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
747
748  if not BuildImage(in_dir, image_properties, out_file, target_out):
749    print >> sys.stderr, "error: failed to build %s from %s" % (out_file,
750                                                                in_dir)
751    exit(1)
752
753
754if __name__ == '__main__':
755  main(sys.argv[1:])
756