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