blob: dbd0f12f64c60b36e90ba6ffe896d0fe07d41fd0 [file] [log] [blame]
Mike Frysingerfcdd9422022-12-28 10:59:06 -05001# Copyright 2022 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Internal helper for attaching & detaching disk images.
6
7We have a long history of the kernel flaking when working with loopback devices,
8so this helper takes care of setting up & tearing it down reliably.
9"""
10
Mike Frysingerfcdd9422022-12-28 10:59:06 -050011from pathlib import Path
12import sys
13from typing import List, Optional
14
15from chromite.lib import commandline
16from chromite.lib import constants
17from chromite.lib import cros_build_lib
18from chromite.lib import image_lib
19from chromite.lib import osutils
20from chromite.utils import pformat
21
22
23def get_parser() -> commandline.ArgumentParser:
24 """Return a command line parser."""
25 parser = commandline.ArgumentParser(description=__doc__)
26
27 subparsers = parser.add_subparsers(dest="subcommand")
28 subparsers.required = True
29
30 subparser = subparsers.add_parser(
31 "attach", help="Attach a disk image to a loopback device."
32 )
33 subparser.add_argument(
34 "path",
35 type=Path,
36 help="Disk image to attach.",
37 )
38
39 subparser = subparsers.add_parser(
40 "detach", help="Detach a loopback device."
41 )
42 subparser.add_argument(
43 "path",
44 type=Path,
45 help="Loopback device to free.",
46 )
47
48 return parser
49
50
51def main(argv: Optional[List[str]] = None) -> Optional[int]:
52 parser = get_parser()
53 opts = parser.parse_args(argv)
54
55 if not osutils.IsRootUser():
56 result = cros_build_lib.sudo_run(
Mike Frysinger5429f302023-03-27 15:48:52 -040057 [constants.CHROMITE_SCRIPTS_DIR / "cros_losetup"] + argv,
Mike Frysingerfcdd9422022-12-28 10:59:06 -050058 check=False,
59 )
60 return result.returncode
61
62 if opts.subcommand == "attach":
63 loop_path = image_lib.LoopbackPartitions.attach_image(opts.path)
64 print(
65 pformat.json(
66 {
67 "path": loop_path,
68 },
69 compact=not sys.stdin.isatty(),
70 ),
71 end="",
72 )
73 elif opts.subcommand == "detach":
74 if not image_lib.LoopbackPartitions.detach_loopback(opts.path):
75 return 1
76 else:
77 raise RuntimeError(f"Unknown command: {opts.subcommand}")
78
79 return 0