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