| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2021 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Script to make / directory on chromebook writable. |
| |
| This script updates a remote chromebook to make the / directory writable." |
| """ |
| |
| from __future__ import print_function |
| |
| __author__ = '[email protected] (Caroline Tice)' |
| |
| import argparse |
| import os |
| import sys |
| import time |
| |
| from cros_utils import command_executer |
| from cros_utils import locks |
| from cros_utils import logger |
| from cros_utils import machines |
| from cros_utils import misc |
| |
| lock_file = '/tmp/image_chromeos_lock/image_chromeos_lock' |
| |
| |
| def Usage(parser, message): |
| print('ERROR: %s' % message) |
| parser.print_help() |
| sys.exit(0) |
| |
| |
| def RebootChromebook(chromeos_root, remote, cmd_executer): |
| cmd = 'sudo reboot' |
| cmd_executer.CrosRunCommand(cmd, chromeos_root=chromeos_root, machine=remote) |
| time.sleep(10) |
| success = False |
| for _ in range(1, 10): |
| if machines.MachineIsPingable(remote): |
| success = True |
| break |
| time.sleep(1) |
| return success |
| |
| |
| def ParseOutput(output): |
| # See comment in FindPartitionNum. |
| lines = output.split('\n') |
| num_str = '-1' |
| for line in lines: |
| l = line.strip() |
| words = l.split() |
| if (len(words) > 2 and words[0] == 'sudo' and |
| words[1] == '/usr/share/vboot/bin/make_dev_ssd.sh' and |
| words[-2] == '--partitions'): |
| num_str = words[-1] |
| break |
| num = int(num_str) |
| |
| return num |
| |
| |
| def FindPartitionNum(chromeos_root, remote, logs, cmd_executer): |
| partition_cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh ' |
| '--remove_rootfs_verification') |
| _, output, _ = cmd_executer.CrosRunCommandWOutput( |
| partition_cmd, |
| chromeos_root=chromeos_root, |
| machine=remote, |
| terminated_timeout=10) |
| |
| # The command above, with no --partitions flag, should return output |
| # in the following form: |
| |
| # make_dev_ssd.sh: INFO: checking system firmware... |
| # |
| # ERROR: YOU ARE TRYING TO MODIFY THE LIVE SYSTEM IMAGE /dev/mmcblk0. |
| # |
| # The system may become unusable after that change, especially when you have |
| # some auto updates in progress. To make it safer, we suggest you to only |
| # change the partition you have booted with. To do that, re-execute this |
| # command as: |
| # |
| # sudo /usr/share/vboot/bin/make_dev_ssd.sh --partitions 4 |
| # |
| # If you are sure to modify other partition, please invoke the command again |
| # and explicitly assign only one target partition for each time |
| # (--partitions N ) |
| # |
| # make_dev_ssd.sh: ERROR: IMAGE /dev/mmcblk0 IS NOT MODIFIED. |
| |
| # We pass this output to the ParseOutput function where it finds the 'sudo' |
| # line with the partition number and returns the partition number. |
| |
| num = ParseOutput(output) |
| |
| if num == -1: |
| logs.LogOutput('Failed to find partition number in "%s"' % output) |
| return num |
| |
| |
| def TryRemoveRootfsFromPartition(chromeos_root, remote, cmd_executer, |
| partition_num): |
| partition_cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh ' |
| '--remove_rootfs_verification --partition %d' % |
| partition_num) |
| ret = cmd_executer.CrosRunCommand( |
| partition_cmd, |
| chromeos_root=chromeos_root, |
| machine=remote, |
| terminated_timeout=10) |
| return ret |
| |
| |
| def TryRemountPartitionAsRW(chromeos_root, remote, cmd_executer): |
| command = 'sudo mount -o remount,rw /' |
| ret = cmd_executer.CrosRunCommand( |
| command, |
| chromeos_root=chromeos_root, |
| machine=remote, |
| terminated_timeout=10) |
| return ret |
| |
| |
| def Main(argv): |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| '-c', |
| '--chromeos_root', |
| dest='chromeos_root', |
| help='Target directory for ChromeOS installation.') |
| parser.add_argument('-r', '--remote', dest='remote', help='Target device.') |
| parser.add_argument( |
| '-n', |
| '--no_lock', |
| dest='no_lock', |
| default=False, |
| action='store_true', |
| help='Do not attempt to lock remote before imaging. ' |
| 'This option should only be used in cases where the ' |
| 'exclusive lock has already been acquired (e.g. in ' |
| 'a script that calls this one).') |
| |
| options = parser.parse_args(argv[1:]) |
| |
| # Common initializations |
| log_level = 'average' |
| cmd_executer = command_executer.GetCommandExecuter(log_level=log_level) |
| l = logger.GetLogger() |
| |
| if options.chromeos_root is None: |
| Usage(parser, '--chromeos_root must be set') |
| |
| if options.remote is None: |
| Usage(parser, '--remote must be set') |
| |
| options.chromeos_root = os.path.expanduser(options.chromeos_root) |
| |
| try: |
| should_unlock = False |
| if not options.no_lock: |
| try: |
| _ = locks.AcquireLock( |
| list(options.remote.split()), options.chromeos_root) |
| should_unlock = True |
| except Exception as e: |
| raise RuntimeError('Error acquiring machine: %s' % str(e)) |
| |
| # Workaround for crosbug.com/35684. |
| os.chmod(misc.GetChromeOSKeyFile(options.chromeos_root), 0o600) |
| |
| if log_level == 'average': |
| cmd_executer.SetLogLevel('verbose') |
| |
| if not machines.MachineIsPingable(options.remote): |
| raise RuntimeError('Machine %s does not appear to be up.' % |
| options.remote) |
| |
| ret = TryRemountPartitionAsRW(options.chromeos_root, options.remote, |
| cmd_executer) |
| |
| if ret != 0: |
| l.LogOutput('Initial mount command failed. Looking for root partition' |
| ' number.') |
| part_num = FindPartitionNum(options.chromeos_root, options.remote, l, |
| cmd_executer) |
| if part_num != -1: |
| l.LogOutput('Attempting to remove rootfs verification on partition %d' % |
| part_num) |
| ret = TryRemoveRootfsFromPartition(options.chromeos_root, |
| options.remote, cmd_executer, |
| part_num) |
| if ret == 0: |
| l.LogOutput('Succeeded in removing roofs verification from' |
| ' partition %d. Rebooting...' % part_num) |
| if not RebootChromebook(options.chromeos_root, options.remote, |
| cmd_executer): |
| raise RuntimeError('Chromebook failed to reboot.') |
| l.LogOutput('Reboot succeeded. Attempting to remount partition.') |
| ret = TryRemountPartitionAsRW(options.chromeos_root, options.remote, |
| cmd_executer) |
| if ret == 0: |
| l.LogOutput('Re-mounted / as writable.') |
| else: |
| l.LogOutput('Re-mount failed. / is not writable.') |
| else: |
| l.LogOutput('Failed to remove rootfs verification from partition' |
| ' %d.' % part_num) |
| else: |
| l.LogOutput('Re-mounted / as writable.') |
| |
| l.LogOutput('Exiting.') |
| |
| finally: |
| if should_unlock: |
| locks.ReleaseLock(list(options.remote.split()), options.chromeos_root) |
| |
| return ret |
| |
| |
| if __name__ == '__main__': |
| retval = Main(sys.argv) |
| sys.exit(retval) |