blob: aeb48aebbf0c1622174bf35fabedd255312498b4 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2015 The ChromiumOS Authors
David Pursellf1d16a62015-03-25 13:31:04 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Install/copy the image to the device."""
6
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04007from __future__ import division
David Pursellf1d16a62015-03-25 13:31:04 -07008
Chris McDonald14ac61d2021-07-21 11:49:56 -06009import logging
David Pursellf1d16a62015-03-25 13:31:04 -070010import os
Ralph Nathan9b997232015-05-15 13:13:12 -070011import re
David Pursellf1d16a62015-03-25 13:31:04 -070012import shutil
David Pursellf1d16a62015-03-25 13:31:04 -070013
Amin Hassani153f9162021-02-22 20:48:31 -080014from chromite.cli import device_imager
Achuith Bhandarkaree1336f2020-04-18 11:44:09 +000015from chromite.cli.cros import cros_chrome_sdk
David Pursellf1d16a62015-03-25 13:31:04 -070016from chromite.lib import commandline
17from chromite.lib import cros_build_lib
David Pursellf1d16a62015-03-25 13:31:04 -070018from chromite.lib import dev_server_wrapper as ds_wrapper
Ralph Nathan872ea4d2015-05-05 18:04:56 -070019from chromite.lib import operation
David Pursellf1d16a62015-03-25 13:31:04 -070020from chromite.lib import osutils
Gilad Arnold1c8eda52015-05-04 22:32:38 -070021from chromite.lib import path_util
David Pursellf1d16a62015-03-25 13:31:04 -070022from chromite.lib import remote_access
23
24
Achuith Bhandarkaree1336f2020-04-18 11:44:09 +000025def GetDefaultBoard():
Alex Klein1699fab2022-09-08 08:46:06 -060026 """Look up default board.
Achuith Bhandarkaree1336f2020-04-18 11:44:09 +000027
Alex Klein1699fab2022-09-08 08:46:06 -060028 In a chrome checkout, return $SDK_BOARD. In a chromeos checkout,
29 return the contents of .default_board.
30 """
31 if path_util.DetermineCheckout().type == path_util.CHECKOUT_TYPE_GCLIENT:
32 return os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
33 return cros_build_lib.GetDefaultBoard()
Achuith Bhandarkaree1336f2020-04-18 11:44:09 +000034
35
Ralph Nathan9b997232015-05-15 13:13:12 -070036class UsbImagerOperation(operation.ProgressBarOperation):
Alex Klein1699fab2022-09-08 08:46:06 -060037 """Progress bar for flashing image to operation."""
Ralph Nathan9b997232015-05-15 13:13:12 -070038
Alex Klein1699fab2022-09-08 08:46:06 -060039 def __init__(self, image):
40 super().__init__()
41 self._size = os.path.getsize(image)
42 self._transferred = 0
43 self._bytes = re.compile(r"(\d+) bytes")
Ralph Nathan9b997232015-05-15 13:13:12 -070044
Alex Klein1699fab2022-09-08 08:46:06 -060045 def _GetDDPid(self):
46 """Get the Pid of dd."""
47 try:
48 pids = cros_build_lib.run(
49 ["pgrep", "dd"],
50 capture_output=True,
51 print_cmd=False,
52 encoding="utf-8",
53 ).stdout
54 for pid in pids.splitlines():
55 if osutils.IsChildProcess(int(pid), name="dd"):
56 return int(pid)
57 return -1
58 except cros_build_lib.RunCommandError:
59 # If dd isn't still running, then we assume that it is finished.
60 return -1
Ralph Nathan9b997232015-05-15 13:13:12 -070061
Alex Klein1699fab2022-09-08 08:46:06 -060062 def _PingDD(self, dd_pid):
63 """Send USR1 signal to dd to get status update."""
64 try:
65 cmd = ["kill", "-USR1", str(dd_pid)]
66 cros_build_lib.sudo_run(cmd, print_cmd=False)
67 except cros_build_lib.RunCommandError:
68 # Here we assume that dd finished in the background.
69 return
Ralph Nathan9b997232015-05-15 13:13:12 -070070
Alex Klein1699fab2022-09-08 08:46:06 -060071 def ParseOutput(self, output=None):
72 """Parse the output of dd to update progress bar."""
73 dd_pid = self._GetDDPid()
74 if dd_pid == -1:
75 return
Ralph Nathan9b997232015-05-15 13:13:12 -070076
Alex Klein1699fab2022-09-08 08:46:06 -060077 self._PingDD(dd_pid)
Ralph Nathan9b997232015-05-15 13:13:12 -070078
Alex Klein1699fab2022-09-08 08:46:06 -060079 if output is None:
80 stdout = self._stdout.read()
81 stderr = self._stderr.read()
82 output = stdout + stderr
Ralph Nathan9b997232015-05-15 13:13:12 -070083
Alex Klein1699fab2022-09-08 08:46:06 -060084 match = self._bytes.search(output)
85 if match:
86 self._transferred = int(match.groups()[0])
Ralph Nathan9b997232015-05-15 13:13:12 -070087
Alex Klein1699fab2022-09-08 08:46:06 -060088 self.ProgressBar(self._transferred / self._size)
Ralph Nathan9b997232015-05-15 13:13:12 -070089
90
Mike Frysinger32759e42016-12-21 18:40:16 -050091def _IsFilePathGPTDiskImage(file_path, require_pmbr=False):
Alex Klein1699fab2022-09-08 08:46:06 -060092 """Determines if a file is a valid GPT disk.
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070093
Alex Klein1699fab2022-09-08 08:46:06 -060094 Args:
Alex Klein53cc3bf2022-10-13 08:50:01 -060095 file_path: Path to the file to test.
96 require_pmbr: Whether to require a PMBR in LBA0.
Alex Klein1699fab2022-09-08 08:46:06 -060097 """
98 if os.path.isfile(file_path):
99 with open(file_path, "rb") as image_file:
100 if require_pmbr:
101 # Seek to the end of LBA0 and look for the PMBR boot signature.
102 image_file.seek(0x1FE)
103 if image_file.read(2) != b"\x55\xaa":
104 return False
105 # Current file position is start of LBA1 now.
106 else:
107 # Seek to LBA1 where the GPT starts.
108 image_file.seek(0x200)
Mike Frysinger32759e42016-12-21 18:40:16 -0500109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 # See if there's a GPT here.
111 if image_file.read(8) == b"EFI PART":
112 return True
Mike Frysinger32759e42016-12-21 18:40:16 -0500113
Alex Klein1699fab2022-09-08 08:46:06 -0600114 return False
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700115
116
117def _ChooseImageFromDirectory(dir_path):
Alex Klein1699fab2022-09-08 08:46:06 -0600118 """Lists all image files in |dir_path| and ask user to select one.
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 Args:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600121 dir_path: Path to the directory.
Alex Klein1699fab2022-09-08 08:46:06 -0600122 """
123 images = sorted(
124 [
125 x
126 for x in os.listdir(dir_path)
127 if _IsFilePathGPTDiskImage(os.path.join(dir_path, x))
128 ]
129 )
130 idx = 0
131 if not images:
132 raise ValueError("No image found in %s." % dir_path)
133 elif len(images) > 1:
134 idx = cros_build_lib.GetChoice(
135 "Multiple images found in %s. Please select one to continue:"
136 % ((dir_path,)),
137 images,
138 )
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 return os.path.join(dir_path, images[idx])
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700141
142
David Pursellf1d16a62015-03-25 13:31:04 -0700143class FlashError(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -0600144 """Thrown when there is an unrecoverable error during flash."""
David Pursellf1d16a62015-03-25 13:31:04 -0700145
146
Alex Klein074f94f2023-06-22 10:32:06 -0600147class USBImager:
Alex Klein1699fab2022-09-08 08:46:06 -0600148 """Copy image to the target removable device."""
David Pursellf1d16a62015-03-25 13:31:04 -0700149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 def __init__(self, device, board, image, version, debug=False, yes=False):
151 """Initializes USBImager."""
152 self.device = device
153 self.board = board if board else GetDefaultBoard()
154 self.image = image
155 self.version = version
156 self.debug = debug
157 self.debug_level = logging.DEBUG if debug else logging.INFO
158 self.yes = yes
David Pursellf1d16a62015-03-25 13:31:04 -0700159
Alex Klein1699fab2022-09-08 08:46:06 -0600160 def DeviceNameToPath(self, device_name):
161 return "/dev/%s" % device_name
David Pursellf1d16a62015-03-25 13:31:04 -0700162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 def GetRemovableDeviceDescription(self, device):
164 """Returns a informational description of the removable |device|.
David Pursellf1d16a62015-03-25 13:31:04 -0700165
Alex Klein1699fab2022-09-08 08:46:06 -0600166 Args:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600167 device: the device name (e.g. sdc).
David Pursellf1d16a62015-03-25 13:31:04 -0700168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 Returns:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600170 A string describing |device| (e.g. Patriot Memory 7918 MB).
Alex Klein1699fab2022-09-08 08:46:06 -0600171 """
172 desc = [
173 osutils.GetDeviceInfo(device, keyword="manufacturer"),
174 osutils.GetDeviceInfo(device, keyword="product"),
175 osutils.GetDeviceSize(self.DeviceNameToPath(device)),
176 "(%s)" % self.DeviceNameToPath(device),
177 ]
178 return " ".join([x for x in desc if x])
David Pursellf1d16a62015-03-25 13:31:04 -0700179
Alex Klein1699fab2022-09-08 08:46:06 -0600180 def ListAllRemovableDevices(self):
181 """Returns a list of removable devices.
David Pursellf1d16a62015-03-25 13:31:04 -0700182
Alex Klein1699fab2022-09-08 08:46:06 -0600183 Returns:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600184 A list of device names (e.g. ['sdb', 'sdc']).
Alex Klein1699fab2022-09-08 08:46:06 -0600185 """
186 devices = osutils.ListBlockDevices()
187 removable_devices = []
188 for d in devices:
189 if d.TYPE == "disk" and (d.RM == "1" or d.HOTPLUG == "1"):
190 removable_devices.append(d.NAME)
David Pursellf1d16a62015-03-25 13:31:04 -0700191
Alex Klein1699fab2022-09-08 08:46:06 -0600192 return removable_devices
David Pursellf1d16a62015-03-25 13:31:04 -0700193
Alex Klein1699fab2022-09-08 08:46:06 -0600194 def ChooseRemovableDevice(self, devices):
195 """Lists all removable devices and asks user to select/confirm.
David Pursellf1d16a62015-03-25 13:31:04 -0700196
Alex Klein1699fab2022-09-08 08:46:06 -0600197 Args:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600198 devices: a list of device names (e.g. ['sda', 'sdb']).
David Pursellf1d16a62015-03-25 13:31:04 -0700199
Alex Klein1699fab2022-09-08 08:46:06 -0600200 Returns:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600201 The device name chosen by the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600202 """
203 idx = cros_build_lib.GetChoice(
204 "Removable device(s) found. Please select/confirm to continue:",
205 [self.GetRemovableDeviceDescription(x) for x in devices],
206 )
David Pursellf1d16a62015-03-25 13:31:04 -0700207
Alex Klein1699fab2022-09-08 08:46:06 -0600208 return devices[idx]
David Pursellf1d16a62015-03-25 13:31:04 -0700209
Alex Klein1699fab2022-09-08 08:46:06 -0600210 def CopyImageToDevice(self, image, device):
211 """Copies |image| to the removable |device|.
David Pursellf1d16a62015-03-25 13:31:04 -0700212
Alex Klein1699fab2022-09-08 08:46:06 -0600213 Args:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600214 image: Path to the image to copy.
215 device: Device to copy to.
Alex Klein1699fab2022-09-08 08:46:06 -0600216 """
217 cmd = [
218 "dd",
219 "if=%s" % image,
220 "of=%s" % device,
221 "bs=4M",
222 "iflag=fullblock",
223 "oflag=direct",
224 "conv=fdatasync",
225 ]
226 if logging.getLogger().getEffectiveLevel() <= logging.NOTICE:
227 op = UsbImagerOperation(image)
228 op.Run(
229 cros_build_lib.sudo_run,
230 cmd,
231 debug_level=logging.NOTICE,
232 encoding="utf-8",
233 update_period=0.5,
234 )
235 else:
236 cros_build_lib.sudo_run(
237 cmd,
238 debug_level=logging.NOTICE,
239 print_cmd=logging.getLogger().getEffectiveLevel()
240 < logging.NOTICE,
241 )
David Pursellf1d16a62015-03-25 13:31:04 -0700242
Alex Klein975e86c2023-01-23 16:49:10 -0700243 # dd likely didn't put the backup GPT in the last block. sfdisk fixes
244 # this up for us with a 'write' command, so we have a
245 # standards-conforming GPT. Ignore errors because sfdisk (util-linux <
246 # v2.32) isn't always happy to fix GPT correctness issues.
Alex Klein1699fab2022-09-08 08:46:06 -0600247 cros_build_lib.sudo_run(
248 ["sfdisk", device],
249 input="write\n",
250 check=False,
251 debug_level=self.debug_level,
252 )
Brian Norris6386fde2018-10-29 13:34:28 -0700253
Alex Klein1699fab2022-09-08 08:46:06 -0600254 cros_build_lib.sudo_run(
255 ["partx", "-u", device], debug_level=self.debug_level
256 )
Mike Frysingerfec6e772022-12-27 22:23:28 -0500257 osutils.sync_storage(device, data_only=True, sudo=True)
David Pursellf1d16a62015-03-25 13:31:04 -0700258
Alex Klein1699fab2022-09-08 08:46:06 -0600259 def _GetImagePath(self):
260 """Returns the image path to use."""
261 image_path = None
262 if os.path.isfile(self.image):
263 if not self.yes and not _IsFilePathGPTDiskImage(self.image):
Alex Klein975e86c2023-01-23 16:49:10 -0700264 # TODO(wnwen): Open the tarball and if there is just one file in
265 # it, use that instead. Existing code in upload_symbols.py.
Alex Klein1699fab2022-09-08 08:46:06 -0600266 if cros_build_lib.BooleanPrompt(
Alex Klein975e86c2023-01-23 16:49:10 -0700267 prolog="The given image file is not a valid disk image. "
268 "Perhaps you forgot to untar it.",
Alex Klein1699fab2022-09-08 08:46:06 -0600269 prompt="Terminate the current flash process?",
270 ):
271 raise FlashError("Update terminated by user.")
272 image_path = self.image
273 elif os.path.isdir(self.image):
274 # Ask user which image (*.bin) in the folder to use.
275 image_path = _ChooseImageFromDirectory(self.image)
276 else:
277 # Translate the xbuddy path to get the exact image to use.
278 _, image_path = ds_wrapper.GetImagePathWithXbuddy(
279 self.image, self.board, self.version
280 )
David Pursellf1d16a62015-03-25 13:31:04 -0700281
Alex Klein1699fab2022-09-08 08:46:06 -0600282 logging.info("Using image %s", image_path)
283 return image_path
David Pursellf1d16a62015-03-25 13:31:04 -0700284
Alex Klein1699fab2022-09-08 08:46:06 -0600285 def Run(self):
286 """Image the removable device."""
287 devices = self.ListAllRemovableDevices()
David Pursellf1d16a62015-03-25 13:31:04 -0700288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 if self.device:
290 # If user specified a device path, check if it exists.
291 if not os.path.exists(self.device):
292 raise FlashError("Device path %s does not exist." % self.device)
David Pursellf1d16a62015-03-25 13:31:04 -0700293
Alex Klein1699fab2022-09-08 08:46:06 -0600294 # Then check if it is removable.
295 if self.device not in [self.DeviceNameToPath(x) for x in devices]:
296 msg = "%s is not a removable device." % self.device
297 if not (
298 self.yes
299 or cros_build_lib.BooleanPrompt(default=False, prolog=msg)
300 ):
301 raise FlashError(
302 "You can specify usb:// to choose from a list of "
303 "removable devices."
304 )
305 target = None
306 if self.device:
307 # Get device name from path (e.g. sdc in /dev/sdc).
308 target = self.device.rsplit(os.path.sep, 1)[-1]
309 elif devices:
310 # Ask user to choose from the list.
311 target = self.ChooseRemovableDevice(devices)
312 else:
313 raise FlashError("No removable devices detected.")
David Pursellf1d16a62015-03-25 13:31:04 -0700314
Alex Klein1699fab2022-09-08 08:46:06 -0600315 image_path = self._GetImagePath()
316 device = self.DeviceNameToPath(target)
Jae Hoon Kim7f7bc232022-05-04 23:01:16 +0000317
Alex Klein1699fab2022-09-08 08:46:06 -0600318 device_size_bytes = osutils.GetDeviceSize(device, in_bytes=True)
319 image_size_bytes = os.path.getsize(image_path)
320 if device_size_bytes < image_size_bytes:
321 raise FlashError(
Alex Klein975e86c2023-01-23 16:49:10 -0700322 "Removable device %s has less storage (%d) than the image size "
323 "(%d)." % (device, device_size_bytes, image_size_bytes)
Alex Klein1699fab2022-09-08 08:46:06 -0600324 )
Jae Hoon Kim7f7bc232022-05-04 23:01:16 +0000325
Alex Klein1699fab2022-09-08 08:46:06 -0600326 try:
327 self.CopyImageToDevice(image_path, device)
328 except cros_build_lib.RunCommandError:
329 logging.error(
330 "Failed copying image to device %s",
331 self.DeviceNameToPath(target),
332 )
David Pursellf1d16a62015-03-25 13:31:04 -0700333
334
335class FileImager(USBImager):
Alex Klein1699fab2022-09-08 08:46:06 -0600336 """Copy image to the target path."""
David Pursellf1d16a62015-03-25 13:31:04 -0700337
Alex Klein1699fab2022-09-08 08:46:06 -0600338 def Run(self):
339 """Copy the image to the path specified by self.device."""
340 if not os.path.isdir(os.path.dirname(self.device)):
341 raise FlashError(
342 "Parent of path %s is not a directory." % self.device
343 )
David Pursellf1d16a62015-03-25 13:31:04 -0700344
Alex Klein1699fab2022-09-08 08:46:06 -0600345 image_path = self._GetImagePath()
346 if os.path.isdir(self.device):
347 logging.info(
348 "Copying to %s",
349 os.path.join(self.device, os.path.basename(image_path)),
350 )
351 else:
352 logging.info("Copying to %s", self.device)
353 try:
354 shutil.copy(image_path, self.device)
355 except IOError:
356 logging.error(
357 "Failed to copy image %s to %s", image_path, self.device
358 )
David Pursellf1d16a62015-03-25 13:31:04 -0700359
360
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000361# TODO(b/190631159, b/196056723): Change default of no_minios_update to |False|.
Alex Klein1699fab2022-09-08 08:46:06 -0600362def Flash(
363 device,
364 image,
365 board=None,
366 version=None,
367 no_rootfs_update=False,
368 no_stateful_update=False,
369 no_minios_update=True,
370 clobber_stateful=False,
371 reboot=True,
372 ssh_private_key=None,
373 ping=True,
374 disable_rootfs_verification=False,
375 clear_cache=False,
376 yes=False,
377 force=False,
378 debug=False,
379 clear_tpm_owner=False,
380 delta=False,
Jae Hoon Kim04a08062023-03-15 06:06:03 +0000381 reboot_timeout=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600382):
383 """Flashes a device, USB drive, or file with an image.
David Pursellf1d16a62015-03-25 13:31:04 -0700384
Alex Klein1699fab2022-09-08 08:46:06 -0600385 This provides functionality common to `cros flash` and `brillo flash`
386 so that they can parse the commandline separately but still use the
387 same underlying functionality.
David Pursellf1d16a62015-03-25 13:31:04 -0700388
Alex Klein1699fab2022-09-08 08:46:06 -0600389 Args:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600390 device: commandline.Device object; None to use the default device.
391 image: Path (string) to the update image. Can be a local or xbuddy path;
392 non-existent local paths are converted to xbuddy.
393 board: Board to use; None to automatically detect.
394 no_rootfs_update: Don't update rootfs partition; SSH |device| scheme
395 only.
396 no_stateful_update: Don't update stateful partition; SSH |device| scheme
397 only.
398 no_minios_update: Don't update miniOS partition; SSH |device| scheme
399 only.
400 clobber_stateful: Clobber stateful partition; SSH |device| scheme only.
401 clear_tpm_owner: Clear the TPM owner on reboot; SSH |device| scheme
402 only.
403 reboot: Reboot device after update; SSH |device| scheme only.
404 ssh_private_key: Path to an SSH private key file; None to use test keys.
405 ping: Ping the device before attempting update; SSH |device| scheme
406 only.
407 disable_rootfs_verification: Remove rootfs verification after update;
408 SSH |device| scheme only.
409 clear_cache: Clear the devserver static directory.
410 yes: Assume "yes" for any prompt.
411 force: Ignore confidence checks and prompts. Overrides |yes| if True.
412 debug: Print additional debugging messages.
413 version: Default version.
414 delta: Whether to use delta compression when transferring image bytes.
Jae Hoon Kim04a08062023-03-15 06:06:03 +0000415 reboot_timeout: The timeout for reboot.
David Pursellf1d16a62015-03-25 13:31:04 -0700416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 Raises:
Alex Klein53cc3bf2022-10-13 08:50:01 -0600418 FlashError: An unrecoverable error occurred.
419 ValueError: Invalid parameter combination.
Alex Klein1699fab2022-09-08 08:46:06 -0600420 """
421 if force:
422 yes = True
David Pursellf1d16a62015-03-25 13:31:04 -0700423
Alex Klein1699fab2022-09-08 08:46:06 -0600424 if clear_cache:
425 ds_wrapper.DevServerWrapper.WipeStaticDirectory()
426 ds_wrapper.DevServerWrapper.CreateStaticDirectory()
David Pursellf1d16a62015-03-25 13:31:04 -0700427
Alex Klein975e86c2023-01-23 16:49:10 -0700428 # The user may not have specified a source image, use version as the
429 # default.
Alex Klein1699fab2022-09-08 08:46:06 -0600430 image = image or version
431 if not device or device.scheme == commandline.DEVICE_SCHEME_SSH:
432 if device:
433 hostname, port = device.hostname, device.port
434 else:
435 hostname, port = None, None
Amin Hassani153f9162021-02-22 20:48:31 -0800436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 with remote_access.ChromiumOSDeviceHandler(
438 hostname, port=port, private_key=ssh_private_key, ping=ping
439 ) as device_p:
440 device_imager.DeviceImager(
441 device_p,
442 image,
443 board=board,
444 version=version,
445 no_rootfs_update=no_rootfs_update,
446 no_stateful_update=no_stateful_update,
447 no_minios_update=no_minios_update,
448 no_reboot=not reboot,
449 disable_verification=disable_rootfs_verification,
450 clobber_stateful=clobber_stateful,
451 clear_tpm_owner=clear_tpm_owner,
452 delta=delta,
Jae Hoon Kim04a08062023-03-15 06:06:03 +0000453 reboot_timeout=reboot_timeout,
Alex Klein1699fab2022-09-08 08:46:06 -0600454 ).Run()
455 elif device.scheme == commandline.DEVICE_SCHEME_USB:
456 path = osutils.ExpandPath(device.path) if device.path else ""
457 logging.info("Preparing to image the removable device %s", path)
458 imager = USBImager(path, board, image, version, debug=debug, yes=yes)
459 imager.Run()
460 elif device.scheme == commandline.DEVICE_SCHEME_FILE:
461 logging.info("Preparing to copy image to %s", device.path)
462 imager = FileImager(
463 device.path, board, image, version, debug=debug, yes=yes
464 )
465 imager.Run()