mkuserimg_mke2fs.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2018 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. import argparse
  17. import logging
  18. import os
  19. import subprocess
  20. import sys
  21. def RunCommand(cmd, env):
  22. """Runs the given command.
  23. Args:
  24. cmd: the command represented as a list of strings.
  25. env: a dictionary of additional environment variables.
  26. Returns:
  27. A tuple of the output and the exit code.
  28. """
  29. env_copy = os.environ.copy()
  30. env_copy.update(env)
  31. logging.info("Env: %s", env)
  32. logging.info("Running: " + " ".join(cmd))
  33. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
  34. env=env_copy)
  35. output, _ = p.communicate()
  36. return output, p.returncode
  37. def ParseArguments(argv):
  38. """Parses the input arguments to the program."""
  39. parser = argparse.ArgumentParser(
  40. description=__doc__,
  41. formatter_class=argparse.RawDescriptionHelpFormatter)
  42. parser.add_argument("src_dir", help="The source directory for user image.")
  43. parser.add_argument("output_file", help="The path of the output image file.")
  44. parser.add_argument("ext_variant", choices=["ext2", "ext4"],
  45. help="Variant of the extended filesystem.")
  46. parser.add_argument("mount_point", help="The mount point for user image.")
  47. parser.add_argument("fs_size", help="Size of the file system.")
  48. parser.add_argument("file_contexts", nargs='?',
  49. help="The selinux file context.")
  50. parser.add_argument("--android_sparse", "-s", action="store_true",
  51. help="Outputs an android sparse image (mke2fs).")
  52. parser.add_argument("--journal_size", "-j",
  53. help="Journal size (mke2fs).")
  54. parser.add_argument("--timestamp", "-T",
  55. help="Fake timetamp for the output image.")
  56. parser.add_argument("--fs_config", "-C",
  57. help="Path to the fs config file (e2fsdroid).")
  58. parser.add_argument("--product_out", "-D",
  59. help="Path to the directory with device specific fs"
  60. " config files (e2fsdroid).")
  61. parser.add_argument("--block_list_file", "-B",
  62. help="Path to the block list file (e2fsdroid).")
  63. parser.add_argument("--base_alloc_file_in", "-d",
  64. help="Path to the input base fs file (e2fsdroid).")
  65. parser.add_argument("--base_alloc_file_out", "-A",
  66. help="Path to the output base fs file (e2fsdroid).")
  67. parser.add_argument("--label", "-L",
  68. help="The mount point (mke2fs).")
  69. parser.add_argument("--inodes", "-i",
  70. help="The extfs inodes count (mke2fs).")
  71. parser.add_argument("--inode_size", "-I",
  72. help="The extfs inode size (mke2fs).")
  73. parser.add_argument("--reserved_percent", "-M",
  74. help="The reserved blocks percentage (mke2fs).")
  75. parser.add_argument("--flash_erase_block_size", "-e",
  76. help="The flash erase block size (mke2fs).")
  77. parser.add_argument("--flash_logical_block_size", "-o",
  78. help="The flash logical block size (mke2fs).")
  79. parser.add_argument("--mke2fs_uuid", "-U",
  80. help="The mke2fs uuid (mke2fs) .")
  81. parser.add_argument("--mke2fs_hash_seed", "-S",
  82. help="The mke2fs hash seed (mke2fs).")
  83. parser.add_argument("--share_dup_blocks", "-c", action="store_true",
  84. help="ext4 share dup blocks (e2fsdroid).")
  85. args, remainder = parser.parse_known_args(argv)
  86. # The current argparse doesn't handle intermixed arguments well. Checks
  87. # manually whether the file_contexts exists as the last argument.
  88. # TODO(xunchang) use parse_intermixed_args() when we switch to python 3.7.
  89. if len(remainder) == 1 and remainder[0] == argv[-1]:
  90. args.file_contexts = remainder[0]
  91. elif remainder:
  92. parser.print_usage()
  93. sys.exit(1)
  94. return args
  95. def ConstructE2fsCommands(args):
  96. """Builds the mke2fs & e2fsdroid command based on the input arguments.
  97. Args:
  98. args: The result of ArgumentParser after parsing the command line arguments.
  99. Returns:
  100. A tuple of two lists that serve as the command for mke2fs and e2fsdroid.
  101. """
  102. BLOCKSIZE = 4096
  103. e2fsdroid_opts = []
  104. mke2fs_extended_opts = []
  105. mke2fs_opts = []
  106. if args.android_sparse:
  107. mke2fs_extended_opts.append("android_sparse")
  108. else:
  109. e2fsdroid_opts.append("-e")
  110. if args.timestamp:
  111. e2fsdroid_opts += ["-T", args.timestamp]
  112. if args.fs_config:
  113. e2fsdroid_opts += ["-C", args.fs_config]
  114. if args.product_out:
  115. e2fsdroid_opts += ["-p", args.product_out]
  116. if args.block_list_file:
  117. e2fsdroid_opts += ["-B", args.block_list_file]
  118. if args.base_alloc_file_in:
  119. e2fsdroid_opts += ["-d", args.base_alloc_file_in]
  120. if args.base_alloc_file_out:
  121. e2fsdroid_opts += ["-D", args.base_alloc_file_out]
  122. if args.share_dup_blocks:
  123. e2fsdroid_opts.append("-s")
  124. if args.file_contexts:
  125. e2fsdroid_opts += ["-S", args.file_contexts]
  126. if args.flash_erase_block_size:
  127. mke2fs_extended_opts.append("stripe_width={}".format(
  128. int(args.flash_erase_block_size) / BLOCKSIZE))
  129. if args.flash_logical_block_size:
  130. # stride should be the max of 8kb and the logical block size
  131. stride = max(int(args.flash_logical_block_size), 8192)
  132. mke2fs_extended_opts.append("stride={}".format(stride / BLOCKSIZE))
  133. if args.mke2fs_hash_seed:
  134. mke2fs_extended_opts.append("hash_seed=" + args.mke2fs_hash_seed)
  135. if args.journal_size:
  136. if args.journal_size == "0":
  137. mke2fs_opts += ["-O", "^has_journal"]
  138. else:
  139. mke2fs_opts += ["-J", "size=" + args.journal_size]
  140. if args.label:
  141. mke2fs_opts += ["-L", args.label]
  142. if args.inodes:
  143. mke2fs_opts += ["-N", args.inodes]
  144. if args.inode_size:
  145. mke2fs_opts += ["-I", args.inode_size]
  146. if args.mount_point:
  147. mke2fs_opts += ["-M", args.mount_point]
  148. if args.reserved_percent:
  149. mke2fs_opts += ["-m", args.reserved_percent]
  150. if args.mke2fs_uuid:
  151. mke2fs_opts += ["-U", args.mke2fs_uuid]
  152. if mke2fs_extended_opts:
  153. mke2fs_opts += ["-E", ','.join(mke2fs_extended_opts)]
  154. # Round down the filesystem length to be a multiple of the block size
  155. blocks = int(args.fs_size) / BLOCKSIZE
  156. mke2fs_cmd = (["mke2fs"] + mke2fs_opts +
  157. ["-t", args.ext_variant, "-b", str(BLOCKSIZE), args.output_file,
  158. str(blocks)])
  159. e2fsdroid_cmd = (["e2fsdroid"] + e2fsdroid_opts +
  160. ["-f", args.src_dir, "-a", args.mount_point,
  161. args.output_file])
  162. return mke2fs_cmd, e2fsdroid_cmd
  163. def main(argv):
  164. logging_format = '%(asctime)s %(filename)s %(levelname)s: %(message)s'
  165. logging.basicConfig(level=logging.INFO, format=logging_format,
  166. datefmt='%H:%M:%S')
  167. args = ParseArguments(argv)
  168. if not os.path.isdir(args.src_dir):
  169. logging.error("Can not find directory %s", args.src_dir)
  170. sys.exit(2)
  171. if not args.mount_point:
  172. logging.error("Mount point is required")
  173. sys.exit(2)
  174. if args.mount_point[0] != '/':
  175. args.mount_point = '/' + args.mount_point
  176. if not args.fs_size:
  177. logging.error("Size of the filesystem is required")
  178. sys.exit(2)
  179. mke2fs_cmd, e2fsdroid_cmd = ConstructE2fsCommands(args)
  180. # truncate output file since mke2fs will keep verity section in existing file
  181. with open(args.output_file, 'w') as output:
  182. output.truncate()
  183. # run mke2fs
  184. mke2fs_env = {"MKE2FS_CONFIG" : "./system/extras/ext4_utils/mke2fs.conf"}
  185. if args.timestamp:
  186. mke2fs_env["E2FSPROGS_FAKE_TIME"] = args.timestamp
  187. output, ret = RunCommand(mke2fs_cmd, mke2fs_env)
  188. print(output)
  189. if ret != 0:
  190. logging.error("Failed to run mke2fs: " + output)
  191. sys.exit(4)
  192. # run e2fsdroid
  193. e2fsdroid_env = {}
  194. if args.timestamp:
  195. e2fsdroid_env["E2FSPROGS_FAKE_TIME"] = args.timestamp
  196. output, ret = RunCommand(e2fsdroid_cmd, e2fsdroid_env)
  197. # The build script is parsing the raw output of e2fsdroid; keep the pattern
  198. # unchanged for now.
  199. print(output)
  200. if ret != 0:
  201. logging.error("Failed to run e2fsdroid_cmd: " + output)
  202. os.remove(args.output_file)
  203. sys.exit(4)
  204. if __name__ == '__main__':
  205. main(sys.argv[1:])