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