xref: /aosp_15_r20/external/toolchain-utils/make_root_writable.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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