OSDN Git Service

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