1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# -*- coding: utf-8 -*- 3*760c253cSXin Li# 4*760c253cSXin Li# Copyright 2021 The ChromiumOS Authors 5*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 6*760c253cSXin Li# found in the LICENSE file. 7*760c253cSXin Li 8*760c253cSXin Li"""Script to make / directory on chromebook writable. 9*760c253cSXin Li 10*760c253cSXin LiThis script updates a remote chromebook to make the / directory writable." 11*760c253cSXin Li""" 12*760c253cSXin Li 13*760c253cSXin Li 14*760c253cSXin Li__author__ = "[email protected] (Caroline Tice)" 15*760c253cSXin Li 16*760c253cSXin Liimport argparse 17*760c253cSXin Liimport os 18*760c253cSXin Liimport sys 19*760c253cSXin Liimport time 20*760c253cSXin Li 21*760c253cSXin Lifrom cros_utils import command_executer 22*760c253cSXin Lifrom cros_utils import locks 23*760c253cSXin Lifrom cros_utils import logger 24*760c253cSXin Lifrom cros_utils import machines 25*760c253cSXin Lifrom cros_utils import misc 26*760c253cSXin Li 27*760c253cSXin Li 28*760c253cSXin Lilock_file = "/tmp/image_chromeos_lock/image_chromeos_lock" 29*760c253cSXin Li 30*760c253cSXin Li 31*760c253cSXin Lidef Usage(parser, message): 32*760c253cSXin Li print("ERROR: %s" % message) 33*760c253cSXin Li parser.print_help() 34*760c253cSXin Li sys.exit(0) 35*760c253cSXin Li 36*760c253cSXin Li 37*760c253cSXin Lidef RebootChromebook(chromeos_root, remote, cmd_executer): 38*760c253cSXin Li cmd = "sudo reboot" 39*760c253cSXin Li cmd_executer.CrosRunCommand( 40*760c253cSXin Li cmd, chromeos_root=chromeos_root, machine=remote 41*760c253cSXin Li ) 42*760c253cSXin Li time.sleep(10) 43*760c253cSXin Li success = False 44*760c253cSXin Li for _ in range(1, 10): 45*760c253cSXin Li if machines.MachineIsPingable(remote): 46*760c253cSXin Li success = True 47*760c253cSXin Li break 48*760c253cSXin Li time.sleep(1) 49*760c253cSXin Li return success 50*760c253cSXin Li 51*760c253cSXin Li 52*760c253cSXin Lidef ParseOutput(output): 53*760c253cSXin Li # See comment in FindPartitionNum. 54*760c253cSXin Li lines = output.split("\n") 55*760c253cSXin Li num_str = "-1" 56*760c253cSXin Li for line in lines: 57*760c253cSXin Li l = line.strip() 58*760c253cSXin Li words = l.split() 59*760c253cSXin Li if ( 60*760c253cSXin Li len(words) > 2 61*760c253cSXin Li and words[0] == "sudo" 62*760c253cSXin Li and words[1] == "/usr/share/vboot/bin/make_dev_ssd.sh" 63*760c253cSXin Li and words[-2] == "--partitions" 64*760c253cSXin Li ): 65*760c253cSXin Li num_str = words[-1] 66*760c253cSXin Li break 67*760c253cSXin Li num = int(num_str) 68*760c253cSXin Li 69*760c253cSXin Li return num 70*760c253cSXin Li 71*760c253cSXin Li 72*760c253cSXin Lidef FindPartitionNum(chromeos_root, remote, logs, cmd_executer): 73*760c253cSXin Li partition_cmd = ( 74*760c253cSXin Li "/usr/share/vboot/bin/make_dev_ssd.sh " "--remove_rootfs_verification" 75*760c253cSXin Li ) 76*760c253cSXin Li _, output, _ = cmd_executer.CrosRunCommandWOutput( 77*760c253cSXin Li partition_cmd, 78*760c253cSXin Li chromeos_root=chromeos_root, 79*760c253cSXin Li machine=remote, 80*760c253cSXin Li terminated_timeout=10, 81*760c253cSXin Li ) 82*760c253cSXin Li 83*760c253cSXin Li # The command above, with no --partitions flag, should return output 84*760c253cSXin Li # in the following form: 85*760c253cSXin Li 86*760c253cSXin Li # make_dev_ssd.sh: INFO: checking system firmware... 87*760c253cSXin Li # 88*760c253cSXin Li # ERROR: YOU ARE TRYING TO MODIFY THE LIVE SYSTEM IMAGE /dev/mmcblk0. 89*760c253cSXin Li # 90*760c253cSXin Li # The system may become unusable after that change, especially when you have 91*760c253cSXin Li # some auto updates in progress. To make it safer, we suggest you to only 92*760c253cSXin Li # change the partition you have booted with. To do that, re-execute this 93*760c253cSXin Li # command as: 94*760c253cSXin Li # 95*760c253cSXin Li # sudo /usr/share/vboot/bin/make_dev_ssd.sh --partitions 4 96*760c253cSXin Li # 97*760c253cSXin Li # If you are sure to modify other partition, please invoke the command again 98*760c253cSXin Li # and explicitly assign only one target partition for each time 99*760c253cSXin Li # (--partitions N ) 100*760c253cSXin Li # 101*760c253cSXin Li # make_dev_ssd.sh: ERROR: IMAGE /dev/mmcblk0 IS NOT MODIFIED. 102*760c253cSXin Li 103*760c253cSXin Li # We pass this output to the ParseOutput function where it finds the 'sudo' 104*760c253cSXin Li # line with the partition number and returns the partition number. 105*760c253cSXin Li 106*760c253cSXin Li num = ParseOutput(output) 107*760c253cSXin Li 108*760c253cSXin Li if num == -1: 109*760c253cSXin Li logs.LogOutput('Failed to find partition number in "%s"' % output) 110*760c253cSXin Li return num 111*760c253cSXin Li 112*760c253cSXin Li 113*760c253cSXin Lidef TryRemoveRootfsFromPartition( 114*760c253cSXin Li chromeos_root, remote, cmd_executer, partition_num 115*760c253cSXin Li): 116*760c253cSXin Li partition_cmd = ( 117*760c253cSXin Li "/usr/share/vboot/bin/make_dev_ssd.sh " 118*760c253cSXin Li "--remove_rootfs_verification --partition %d" % partition_num 119*760c253cSXin Li ) 120*760c253cSXin Li ret = cmd_executer.CrosRunCommand( 121*760c253cSXin Li partition_cmd, 122*760c253cSXin Li chromeos_root=chromeos_root, 123*760c253cSXin Li machine=remote, 124*760c253cSXin Li terminated_timeout=10, 125*760c253cSXin Li ) 126*760c253cSXin Li return ret 127*760c253cSXin Li 128*760c253cSXin Li 129*760c253cSXin Lidef TryRemountPartitionAsRW(chromeos_root, remote, cmd_executer): 130*760c253cSXin Li command = "sudo mount -o remount,rw /" 131*760c253cSXin Li ret = cmd_executer.CrosRunCommand( 132*760c253cSXin Li command, 133*760c253cSXin Li chromeos_root=chromeos_root, 134*760c253cSXin Li machine=remote, 135*760c253cSXin Li terminated_timeout=10, 136*760c253cSXin Li ) 137*760c253cSXin Li return ret 138*760c253cSXin Li 139*760c253cSXin Li 140*760c253cSXin Lidef Main(argv): 141*760c253cSXin Li parser = argparse.ArgumentParser() 142*760c253cSXin Li parser.add_argument( 143*760c253cSXin Li "-c", 144*760c253cSXin Li "--chromeos_root", 145*760c253cSXin Li dest="chromeos_root", 146*760c253cSXin Li help="Target directory for ChromeOS installation.", 147*760c253cSXin Li ) 148*760c253cSXin Li parser.add_argument("-r", "--remote", dest="remote", help="Target device.") 149*760c253cSXin Li parser.add_argument( 150*760c253cSXin Li "-n", 151*760c253cSXin Li "--no_lock", 152*760c253cSXin Li dest="no_lock", 153*760c253cSXin Li default=False, 154*760c253cSXin Li action="store_true", 155*760c253cSXin Li help="Do not attempt to lock remote before imaging. " 156*760c253cSXin Li "This option should only be used in cases where the " 157*760c253cSXin Li "exclusive lock has already been acquired (e.g. in " 158*760c253cSXin Li "a script that calls this one).", 159*760c253cSXin Li ) 160*760c253cSXin Li 161*760c253cSXin Li options = parser.parse_args(argv[1:]) 162*760c253cSXin Li 163*760c253cSXin Li # Common initializations 164*760c253cSXin Li log_level = "average" 165*760c253cSXin Li cmd_executer = command_executer.GetCommandExecuter(log_level=log_level) 166*760c253cSXin Li l = logger.GetLogger() 167*760c253cSXin Li 168*760c253cSXin Li if options.chromeos_root is None: 169*760c253cSXin Li Usage(parser, "--chromeos_root must be set") 170*760c253cSXin Li 171*760c253cSXin Li if options.remote is None: 172*760c253cSXin Li Usage(parser, "--remote must be set") 173*760c253cSXin Li 174*760c253cSXin Li options.chromeos_root = os.path.expanduser(options.chromeos_root) 175*760c253cSXin Li 176*760c253cSXin Li try: 177*760c253cSXin Li should_unlock = False 178*760c253cSXin Li if not options.no_lock: 179*760c253cSXin Li try: 180*760c253cSXin Li _ = locks.AcquireLock( 181*760c253cSXin Li list(options.remote.split()), options.chromeos_root 182*760c253cSXin Li ) 183*760c253cSXin Li should_unlock = True 184*760c253cSXin Li except Exception as e: 185*760c253cSXin Li raise RuntimeError("Error acquiring machine: %s" % str(e)) 186*760c253cSXin Li 187*760c253cSXin Li # Workaround for crosbug.com/35684. 188*760c253cSXin Li os.chmod(misc.GetChromeOSKeyFile(options.chromeos_root), 0o600) 189*760c253cSXin Li 190*760c253cSXin Li if log_level == "average": 191*760c253cSXin Li cmd_executer.SetLogLevel("verbose") 192*760c253cSXin Li 193*760c253cSXin Li if not machines.MachineIsPingable(options.remote): 194*760c253cSXin Li raise RuntimeError( 195*760c253cSXin Li "Machine %s does not appear to be up." % options.remote 196*760c253cSXin Li ) 197*760c253cSXin Li 198*760c253cSXin Li ret = TryRemountPartitionAsRW( 199*760c253cSXin Li options.chromeos_root, options.remote, cmd_executer 200*760c253cSXin Li ) 201*760c253cSXin Li 202*760c253cSXin Li if ret != 0: 203*760c253cSXin Li l.LogOutput( 204*760c253cSXin Li "Initial mount command failed. Looking for root partition" 205*760c253cSXin Li " number." 206*760c253cSXin Li ) 207*760c253cSXin Li part_num = FindPartitionNum( 208*760c253cSXin Li options.chromeos_root, options.remote, l, cmd_executer 209*760c253cSXin Li ) 210*760c253cSXin Li if part_num != -1: 211*760c253cSXin Li l.LogOutput( 212*760c253cSXin Li "Attempting to remove rootfs verification on partition %d" 213*760c253cSXin Li % part_num 214*760c253cSXin Li ) 215*760c253cSXin Li ret = TryRemoveRootfsFromPartition( 216*760c253cSXin Li options.chromeos_root, 217*760c253cSXin Li options.remote, 218*760c253cSXin Li cmd_executer, 219*760c253cSXin Li part_num, 220*760c253cSXin Li ) 221*760c253cSXin Li if ret == 0: 222*760c253cSXin Li l.LogOutput( 223*760c253cSXin Li "Succeeded in removing roofs verification from" 224*760c253cSXin Li " partition %d. Rebooting..." % part_num 225*760c253cSXin Li ) 226*760c253cSXin Li if not RebootChromebook( 227*760c253cSXin Li options.chromeos_root, options.remote, cmd_executer 228*760c253cSXin Li ): 229*760c253cSXin Li raise RuntimeError("Chromebook failed to reboot.") 230*760c253cSXin Li l.LogOutput( 231*760c253cSXin Li "Reboot succeeded. Attempting to remount partition." 232*760c253cSXin Li ) 233*760c253cSXin Li ret = TryRemountPartitionAsRW( 234*760c253cSXin Li options.chromeos_root, options.remote, cmd_executer 235*760c253cSXin Li ) 236*760c253cSXin Li if ret == 0: 237*760c253cSXin Li l.LogOutput("Re-mounted / as writable.") 238*760c253cSXin Li else: 239*760c253cSXin Li l.LogOutput("Re-mount failed. / is not writable.") 240*760c253cSXin Li else: 241*760c253cSXin Li l.LogOutput( 242*760c253cSXin Li "Failed to remove rootfs verification from partition" 243*760c253cSXin Li " %d." % part_num 244*760c253cSXin Li ) 245*760c253cSXin Li else: 246*760c253cSXin Li l.LogOutput("Re-mounted / as writable.") 247*760c253cSXin Li 248*760c253cSXin Li l.LogOutput("Exiting.") 249*760c253cSXin Li 250*760c253cSXin Li finally: 251*760c253cSXin Li if should_unlock: 252*760c253cSXin Li locks.ReleaseLock( 253*760c253cSXin Li list(options.remote.split()), options.chromeos_root 254*760c253cSXin Li ) 255*760c253cSXin Li 256*760c253cSXin Li return ret 257*760c253cSXin Li 258*760c253cSXin Li 259*760c253cSXin Liif __name__ == "__main__": 260*760c253cSXin Li retval = Main(sys.argv) 261*760c253cSXin Li sys.exit(retval) 262