xref: /aosp_15_r20/system/extras/ext4_utils/mkuserimg_mke2fs.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*288bf522SAndroid Build Coastguard Worker#
3*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project
4*288bf522SAndroid Build Coastguard Worker#
5*288bf522SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*288bf522SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*288bf522SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*288bf522SAndroid Build Coastguard Worker#
9*288bf522SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*288bf522SAndroid Build Coastguard Worker#
11*288bf522SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*288bf522SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*288bf522SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*288bf522SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*288bf522SAndroid Build Coastguard Worker# limitations under the License.
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Workerimport argparse
18*288bf522SAndroid Build Coastguard Workerimport logging
19*288bf522SAndroid Build Coastguard Workerimport os
20*288bf522SAndroid Build Coastguard Workerimport pkgutil
21*288bf522SAndroid Build Coastguard Workerimport subprocess
22*288bf522SAndroid Build Coastguard Workerimport sys
23*288bf522SAndroid Build Coastguard Workerimport tempfile
24*288bf522SAndroid Build Coastguard Worker
25*288bf522SAndroid Build Coastguard Worker
26*288bf522SAndroid Build Coastguard Workerdef RunCommand(cmd, env):
27*288bf522SAndroid Build Coastguard Worker  """Runs the given command.
28*288bf522SAndroid Build Coastguard Worker
29*288bf522SAndroid Build Coastguard Worker  Args:
30*288bf522SAndroid Build Coastguard Worker    cmd: the command represented as a list of strings.
31*288bf522SAndroid Build Coastguard Worker    env: a dictionary of additional environment variables.
32*288bf522SAndroid Build Coastguard Worker  Returns:
33*288bf522SAndroid Build Coastguard Worker    A tuple of the output and the exit code.
34*288bf522SAndroid Build Coastguard Worker  """
35*288bf522SAndroid Build Coastguard Worker  env_copy = os.environ.copy()
36*288bf522SAndroid Build Coastguard Worker  env_copy.update(env)
37*288bf522SAndroid Build Coastguard Worker
38*288bf522SAndroid Build Coastguard Worker  cmd[0] = FindProgram(cmd[0])
39*288bf522SAndroid Build Coastguard Worker
40*288bf522SAndroid Build Coastguard Worker  logging.info("Env: %s", env)
41*288bf522SAndroid Build Coastguard Worker  logging.info("Running: " + " ".join(cmd))
42*288bf522SAndroid Build Coastguard Worker
43*288bf522SAndroid Build Coastguard Worker  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
44*288bf522SAndroid Build Coastguard Worker                       env=env_copy, text=True)
45*288bf522SAndroid Build Coastguard Worker  output, _ = p.communicate()
46*288bf522SAndroid Build Coastguard Worker
47*288bf522SAndroid Build Coastguard Worker  return output, p.returncode
48*288bf522SAndroid Build Coastguard Worker
49*288bf522SAndroid Build Coastguard Workerdef FindProgram(prog_name):
50*288bf522SAndroid Build Coastguard Worker  """Finds the path to prog_name.
51*288bf522SAndroid Build Coastguard Worker
52*288bf522SAndroid Build Coastguard Worker  Args:
53*288bf522SAndroid Build Coastguard Worker    prog_name: the program name to find.
54*288bf522SAndroid Build Coastguard Worker  Returns:
55*288bf522SAndroid Build Coastguard Worker    path to the progName if found. The program is searched in the same directory
56*288bf522SAndroid Build Coastguard Worker    where this script is located at. If not found, progName is returned.
57*288bf522SAndroid Build Coastguard Worker  """
58*288bf522SAndroid Build Coastguard Worker  exec_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
59*288bf522SAndroid Build Coastguard Worker  prog_path = os.path.join(exec_dir, prog_name)
60*288bf522SAndroid Build Coastguard Worker  if os.path.exists(prog_path):
61*288bf522SAndroid Build Coastguard Worker    return prog_path
62*288bf522SAndroid Build Coastguard Worker  else:
63*288bf522SAndroid Build Coastguard Worker    return prog_name
64*288bf522SAndroid Build Coastguard Worker
65*288bf522SAndroid Build Coastguard Workerdef ParseArguments(argv):
66*288bf522SAndroid Build Coastguard Worker  """Parses the input arguments to the program."""
67*288bf522SAndroid Build Coastguard Worker
68*288bf522SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
69*288bf522SAndroid Build Coastguard Worker      description=__doc__,
70*288bf522SAndroid Build Coastguard Worker      formatter_class=argparse.RawDescriptionHelpFormatter)
71*288bf522SAndroid Build Coastguard Worker
72*288bf522SAndroid Build Coastguard Worker  parser.add_argument("src_dir", help="The source directory for user image.")
73*288bf522SAndroid Build Coastguard Worker  parser.add_argument("output_file", help="The path of the output image file.")
74*288bf522SAndroid Build Coastguard Worker  parser.add_argument("ext_variant", choices=["ext2", "ext4"],
75*288bf522SAndroid Build Coastguard Worker                      help="Variant of the extended filesystem.")
76*288bf522SAndroid Build Coastguard Worker  parser.add_argument("mount_point", help="The mount point for user image.")
77*288bf522SAndroid Build Coastguard Worker  parser.add_argument("fs_size", help="Size of the file system.")
78*288bf522SAndroid Build Coastguard Worker  parser.add_argument("file_contexts", nargs='?',
79*288bf522SAndroid Build Coastguard Worker                      help="The selinux file context.")
80*288bf522SAndroid Build Coastguard Worker
81*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--android_sparse", "-s", action="store_true",
82*288bf522SAndroid Build Coastguard Worker                      help="Outputs an android sparse image (mke2fs).")
83*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--journal_size", "-j",
84*288bf522SAndroid Build Coastguard Worker                      help="Journal size (mke2fs).")
85*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--timestamp", "-T",
86*288bf522SAndroid Build Coastguard Worker                      help="Fake timetamp for the output image.")
87*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--fs_config", "-C",
88*288bf522SAndroid Build Coastguard Worker                      help="Path to the fs config file (e2fsdroid).")
89*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--product_out", "-D",
90*288bf522SAndroid Build Coastguard Worker                      help="Path to the directory with device specific fs"
91*288bf522SAndroid Build Coastguard Worker                           " config files (e2fsdroid).")
92*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--block_list_file", "-B",
93*288bf522SAndroid Build Coastguard Worker                      help="Path to the block list file (e2fsdroid).")
94*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--base_alloc_file_in", "-d",
95*288bf522SAndroid Build Coastguard Worker                      help="Path to the input base fs file (e2fsdroid).")
96*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--base_alloc_file_out", "-A",
97*288bf522SAndroid Build Coastguard Worker                      help="Path to the output base fs file (e2fsdroid).")
98*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--label", "-L",
99*288bf522SAndroid Build Coastguard Worker                      help="The mount point (mke2fs).")
100*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--inodes", "-i",
101*288bf522SAndroid Build Coastguard Worker                      help="The extfs inodes count (mke2fs).")
102*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--inode_size", "-I",
103*288bf522SAndroid Build Coastguard Worker                      help="The extfs inode size (mke2fs).")
104*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--reserved_percent", "-M",
105*288bf522SAndroid Build Coastguard Worker                      help="The reserved blocks percentage (mke2fs).")
106*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--flash_erase_block_size", "-e",
107*288bf522SAndroid Build Coastguard Worker                      help="The flash erase block size (mke2fs).")
108*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--flash_logical_block_size", "-o",
109*288bf522SAndroid Build Coastguard Worker                      help="The flash logical block size (mke2fs).")
110*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--mke2fs_uuid", "-U",
111*288bf522SAndroid Build Coastguard Worker                      help="The mke2fs uuid (mke2fs) .")
112*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--mke2fs_hash_seed", "-S",
113*288bf522SAndroid Build Coastguard Worker                      help="The mke2fs hash seed (mke2fs).")
114*288bf522SAndroid Build Coastguard Worker  parser.add_argument("--share_dup_blocks", "-c", action="store_true",
115*288bf522SAndroid Build Coastguard Worker                      help="ext4 share dup blocks (e2fsdroid).")
116*288bf522SAndroid Build Coastguard Worker
117*288bf522SAndroid Build Coastguard Worker  args, remainder = parser.parse_known_args(argv)
118*288bf522SAndroid Build Coastguard Worker  # The current argparse doesn't handle intermixed arguments well. Checks
119*288bf522SAndroid Build Coastguard Worker  # manually whether the file_contexts exists as the last argument.
120*288bf522SAndroid Build Coastguard Worker  # TODO(xunchang) use parse_intermixed_args() when we switch to python 3.7.
121*288bf522SAndroid Build Coastguard Worker  if len(remainder) == 1 and remainder[0] == argv[-1]:
122*288bf522SAndroid Build Coastguard Worker    args.file_contexts = remainder[0]
123*288bf522SAndroid Build Coastguard Worker  elif remainder:
124*288bf522SAndroid Build Coastguard Worker    parser.print_usage()
125*288bf522SAndroid Build Coastguard Worker    sys.exit(1)
126*288bf522SAndroid Build Coastguard Worker
127*288bf522SAndroid Build Coastguard Worker  return args
128*288bf522SAndroid Build Coastguard Worker
129*288bf522SAndroid Build Coastguard Worker
130*288bf522SAndroid Build Coastguard Workerdef ConstructE2fsCommands(args):
131*288bf522SAndroid Build Coastguard Worker  """Builds the mke2fs & e2fsdroid command based on the input arguments.
132*288bf522SAndroid Build Coastguard Worker
133*288bf522SAndroid Build Coastguard Worker  Args:
134*288bf522SAndroid Build Coastguard Worker    args: The result of ArgumentParser after parsing the command line arguments.
135*288bf522SAndroid Build Coastguard Worker  Returns:
136*288bf522SAndroid Build Coastguard Worker    A tuple of two lists that serve as the command for mke2fs and e2fsdroid.
137*288bf522SAndroid Build Coastguard Worker  """
138*288bf522SAndroid Build Coastguard Worker
139*288bf522SAndroid Build Coastguard Worker  BLOCKSIZE = 4096
140*288bf522SAndroid Build Coastguard Worker
141*288bf522SAndroid Build Coastguard Worker  e2fsdroid_opts = []
142*288bf522SAndroid Build Coastguard Worker  mke2fs_extended_opts = []
143*288bf522SAndroid Build Coastguard Worker  mke2fs_opts = []
144*288bf522SAndroid Build Coastguard Worker
145*288bf522SAndroid Build Coastguard Worker  if args.android_sparse:
146*288bf522SAndroid Build Coastguard Worker    mke2fs_extended_opts.append("android_sparse")
147*288bf522SAndroid Build Coastguard Worker  else:
148*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts.append("-e")
149*288bf522SAndroid Build Coastguard Worker  if args.timestamp:
150*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts += ["-T", args.timestamp]
151*288bf522SAndroid Build Coastguard Worker  if args.fs_config:
152*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts += ["-C", args.fs_config]
153*288bf522SAndroid Build Coastguard Worker  if args.product_out:
154*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts += ["-p", args.product_out]
155*288bf522SAndroid Build Coastguard Worker  if args.block_list_file:
156*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts += ["-B", args.block_list_file]
157*288bf522SAndroid Build Coastguard Worker  if args.base_alloc_file_in:
158*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts += ["-d", args.base_alloc_file_in]
159*288bf522SAndroid Build Coastguard Worker  if args.base_alloc_file_out:
160*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts += ["-D", args.base_alloc_file_out]
161*288bf522SAndroid Build Coastguard Worker  if args.share_dup_blocks:
162*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts.append("-s")
163*288bf522SAndroid Build Coastguard Worker  if args.file_contexts:
164*288bf522SAndroid Build Coastguard Worker    e2fsdroid_opts += ["-S", args.file_contexts]
165*288bf522SAndroid Build Coastguard Worker
166*288bf522SAndroid Build Coastguard Worker  if args.flash_erase_block_size:
167*288bf522SAndroid Build Coastguard Worker    mke2fs_extended_opts.append("stripe_width={}".format(
168*288bf522SAndroid Build Coastguard Worker        int(args.flash_erase_block_size) // BLOCKSIZE))
169*288bf522SAndroid Build Coastguard Worker  if args.flash_logical_block_size:
170*288bf522SAndroid Build Coastguard Worker    # stride should be the max of 8kb and the logical block size
171*288bf522SAndroid Build Coastguard Worker    stride = max(int(args.flash_logical_block_size), 8192)
172*288bf522SAndroid Build Coastguard Worker    mke2fs_extended_opts.append("stride={}".format(stride // BLOCKSIZE))
173*288bf522SAndroid Build Coastguard Worker  if args.mke2fs_hash_seed:
174*288bf522SAndroid Build Coastguard Worker    mke2fs_extended_opts.append("hash_seed=" + args.mke2fs_hash_seed)
175*288bf522SAndroid Build Coastguard Worker
176*288bf522SAndroid Build Coastguard Worker  if args.journal_size:
177*288bf522SAndroid Build Coastguard Worker    if args.journal_size == "0":
178*288bf522SAndroid Build Coastguard Worker      mke2fs_opts += ["-O", "^has_journal"]
179*288bf522SAndroid Build Coastguard Worker    else:
180*288bf522SAndroid Build Coastguard Worker      mke2fs_opts += ["-J", "size=" + args.journal_size]
181*288bf522SAndroid Build Coastguard Worker  if args.label:
182*288bf522SAndroid Build Coastguard Worker    mke2fs_opts += ["-L", args.label]
183*288bf522SAndroid Build Coastguard Worker  if args.inodes:
184*288bf522SAndroid Build Coastguard Worker    mke2fs_opts += ["-N", args.inodes]
185*288bf522SAndroid Build Coastguard Worker  if args.inode_size:
186*288bf522SAndroid Build Coastguard Worker    mke2fs_opts += ["-I", args.inode_size]
187*288bf522SAndroid Build Coastguard Worker  if args.mount_point:
188*288bf522SAndroid Build Coastguard Worker    mke2fs_opts += ["-M", args.mount_point]
189*288bf522SAndroid Build Coastguard Worker  if args.reserved_percent:
190*288bf522SAndroid Build Coastguard Worker    mke2fs_opts += ["-m", args.reserved_percent]
191*288bf522SAndroid Build Coastguard Worker  if args.mke2fs_uuid:
192*288bf522SAndroid Build Coastguard Worker    mke2fs_opts += ["-U", args.mke2fs_uuid]
193*288bf522SAndroid Build Coastguard Worker  if mke2fs_extended_opts:
194*288bf522SAndroid Build Coastguard Worker    mke2fs_opts += ["-E", ','.join(mke2fs_extended_opts)]
195*288bf522SAndroid Build Coastguard Worker
196*288bf522SAndroid Build Coastguard Worker  # Round down the filesystem length to be a multiple of the block size
197*288bf522SAndroid Build Coastguard Worker  blocks = int(args.fs_size) // BLOCKSIZE
198*288bf522SAndroid Build Coastguard Worker  mke2fs_cmd = (["mke2fs"] + mke2fs_opts +
199*288bf522SAndroid Build Coastguard Worker                ["-t", args.ext_variant, "-b", str(BLOCKSIZE), args.output_file,
200*288bf522SAndroid Build Coastguard Worker                 str(blocks)])
201*288bf522SAndroid Build Coastguard Worker
202*288bf522SAndroid Build Coastguard Worker  e2fsdroid_cmd = (["e2fsdroid"] + e2fsdroid_opts +
203*288bf522SAndroid Build Coastguard Worker                   ["-f", args.src_dir, "-a", args.mount_point,
204*288bf522SAndroid Build Coastguard Worker                    args.output_file])
205*288bf522SAndroid Build Coastguard Worker
206*288bf522SAndroid Build Coastguard Worker  return mke2fs_cmd, e2fsdroid_cmd
207*288bf522SAndroid Build Coastguard Worker
208*288bf522SAndroid Build Coastguard Worker
209*288bf522SAndroid Build Coastguard Workerdef main(argv):
210*288bf522SAndroid Build Coastguard Worker  logging_format = '%(asctime)s %(filename)s %(levelname)s: %(message)s'
211*288bf522SAndroid Build Coastguard Worker  logging.basicConfig(level=logging.INFO, format=logging_format,
212*288bf522SAndroid Build Coastguard Worker                      datefmt='%H:%M:%S')
213*288bf522SAndroid Build Coastguard Worker
214*288bf522SAndroid Build Coastguard Worker  args = ParseArguments(argv)
215*288bf522SAndroid Build Coastguard Worker  if not os.path.isdir(args.src_dir):
216*288bf522SAndroid Build Coastguard Worker    logging.error("Can not find directory %s", args.src_dir)
217*288bf522SAndroid Build Coastguard Worker    sys.exit(2)
218*288bf522SAndroid Build Coastguard Worker  if not args.mount_point:
219*288bf522SAndroid Build Coastguard Worker    logging.error("Mount point is required")
220*288bf522SAndroid Build Coastguard Worker    sys.exit(2)
221*288bf522SAndroid Build Coastguard Worker  if args.mount_point[0] != '/':
222*288bf522SAndroid Build Coastguard Worker    args.mount_point = '/' + args.mount_point
223*288bf522SAndroid Build Coastguard Worker  if not args.fs_size:
224*288bf522SAndroid Build Coastguard Worker    logging.error("Size of the filesystem is required")
225*288bf522SAndroid Build Coastguard Worker    sys.exit(2)
226*288bf522SAndroid Build Coastguard Worker
227*288bf522SAndroid Build Coastguard Worker  mke2fs_cmd, e2fsdroid_cmd = ConstructE2fsCommands(args)
228*288bf522SAndroid Build Coastguard Worker
229*288bf522SAndroid Build Coastguard Worker  # truncate output file since mke2fs will keep verity section in existing file
230*288bf522SAndroid Build Coastguard Worker  with open(args.output_file, 'w') as output:
231*288bf522SAndroid Build Coastguard Worker    output.truncate()
232*288bf522SAndroid Build Coastguard Worker
233*288bf522SAndroid Build Coastguard Worker  # run mke2fs
234*288bf522SAndroid Build Coastguard Worker  with tempfile.NamedTemporaryFile() as conf_file:
235*288bf522SAndroid Build Coastguard Worker    conf_data = pkgutil.get_data('mkuserimg_mke2fs', 'mke2fs.conf')
236*288bf522SAndroid Build Coastguard Worker    conf_file.write(conf_data)
237*288bf522SAndroid Build Coastguard Worker    conf_file.flush()
238*288bf522SAndroid Build Coastguard Worker    mke2fs_env = {"MKE2FS_CONFIG" : conf_file.name}
239*288bf522SAndroid Build Coastguard Worker
240*288bf522SAndroid Build Coastguard Worker    if args.timestamp:
241*288bf522SAndroid Build Coastguard Worker      mke2fs_env["E2FSPROGS_FAKE_TIME"] = args.timestamp
242*288bf522SAndroid Build Coastguard Worker
243*288bf522SAndroid Build Coastguard Worker    output, ret = RunCommand(mke2fs_cmd, mke2fs_env)
244*288bf522SAndroid Build Coastguard Worker    print(output)
245*288bf522SAndroid Build Coastguard Worker    if ret != 0:
246*288bf522SAndroid Build Coastguard Worker      logging.error("Failed to run mke2fs: " + output)
247*288bf522SAndroid Build Coastguard Worker      sys.exit(4)
248*288bf522SAndroid Build Coastguard Worker
249*288bf522SAndroid Build Coastguard Worker  # run e2fsdroid
250*288bf522SAndroid Build Coastguard Worker  e2fsdroid_env = {}
251*288bf522SAndroid Build Coastguard Worker  if args.timestamp:
252*288bf522SAndroid Build Coastguard Worker    e2fsdroid_env["E2FSPROGS_FAKE_TIME"] = args.timestamp
253*288bf522SAndroid Build Coastguard Worker
254*288bf522SAndroid Build Coastguard Worker  output, ret = RunCommand(e2fsdroid_cmd, e2fsdroid_env)
255*288bf522SAndroid Build Coastguard Worker  # The build script is parsing the raw output of e2fsdroid; keep the pattern
256*288bf522SAndroid Build Coastguard Worker  # unchanged for now.
257*288bf522SAndroid Build Coastguard Worker  print(output)
258*288bf522SAndroid Build Coastguard Worker  if ret != 0:
259*288bf522SAndroid Build Coastguard Worker    logging.error("Failed to run e2fsdroid_cmd: " + output)
260*288bf522SAndroid Build Coastguard Worker    os.remove(args.output_file)
261*288bf522SAndroid Build Coastguard Worker    sys.exit(4)
262*288bf522SAndroid Build Coastguard Worker
263*288bf522SAndroid Build Coastguard Worker
264*288bf522SAndroid Build Coastguard Workerif __name__ == '__main__':
265*288bf522SAndroid Build Coastguard Worker  main(sys.argv[1:])
266