blob: 9e9ccf5e95373819d72d108fd5ca7870437ceecd [file] [log] [blame]
Xiaochu Liudeed0232018-06-26 10:25:34 -07001# -*- coding: utf-8 -*-
2# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Xiaochu Liudeed0232018-06-26 10:25:34 -07005"""Script to generate a DLC (Downloadable Content) artifact."""
6
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04007from __future__ import division
Xiaochu Liudeed0232018-06-26 10:25:34 -07008from __future__ import print_function
9
10import hashlib
11import json
12import math
13import os
Amin Hassanibc1a4792019-10-24 14:39:57 -070014import re
Amin Hassani11a88cf2019-01-29 15:31:24 -080015import shutil
Xiaochu Liudeed0232018-06-26 10:25:34 -070016
17from chromite.lib import commandline
18from chromite.lib import cros_build_lib
Amin Hassanib97a5ee2019-01-23 14:44:43 -080019from chromite.lib import cros_logging as logging
Xiaochu Liudeed0232018-06-26 10:25:34 -070020from chromite.lib import osutils
21
Amin Hassani8f1cc0f2019-03-06 15:34:53 -080022from chromite.scripts import cros_set_lsb_release
Xiaochu Liudeed0232018-06-26 10:25:34 -070023
Amin Hassani2af75a92019-01-22 21:07:45 -080024DLC_META_DIR = 'opt/google/dlc/'
Andrew67b5fa72020-02-05 14:14:48 -080025DLC_TMP_META_DIR = 'meta'
26DLC_BUILD_DIR = 'build/rootfs/dlc/'
Amin Hassanid5742d32019-01-22 21:13:34 -080027LSB_RELEASE = 'etc/lsb-release'
Jae Hoon Kim5f411e42020-01-09 13:30:56 -080028DLC_IMAGE = 'dlc.img'
29IMAGELOADER_JSON = 'imageloader.json'
Andrew67b5fa72020-02-05 14:14:48 -080030EBUILD_PARAMETERS = 'ebuild_parameters.json'
Amin Hassanid5742d32019-01-22 21:13:34 -080031
Amin Hassani11a88cf2019-01-29 15:31:24 -080032# This file has major and minor version numbers that the update_engine client
33# supports. These values are needed for generating a delta/full payload.
34UPDATE_ENGINE_CONF = 'etc/update_engine.conf'
35
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -080036_EXTRA_RESOURCES = (UPDATE_ENGINE_CONF,)
Amin Hassani11a88cf2019-01-29 15:31:24 -080037
Amin Hassanid5742d32019-01-22 21:13:34 -080038DLC_ID_KEY = 'DLC_ID'
Amin Hassanib5a48042019-03-18 14:30:51 -070039DLC_PACKAGE_KEY = 'DLC_PACKAGE'
Amin Hassanid5742d32019-01-22 21:13:34 -080040DLC_NAME_KEY = 'DLC_NAME'
Amin Hassani8f1cc0f2019-03-06 15:34:53 -080041DLC_APPID_KEY = 'DLC_RELEASE_APPID'
Amin Hassani2af75a92019-01-22 21:07:45 -080042
Amin Hassani22a25eb2019-01-11 14:25:02 -080043_SQUASHFS_TYPE = 'squashfs'
44_EXT4_TYPE = 'ext4'
45
Amin Hassani160e12e2020-04-13 14:29:36 -070046_USED_BY_USER = 'user'
47_USED_BY_SYSTEM = 'system'
48
Amin Hassanibc1a4792019-10-24 14:39:57 -070049MAX_ID_NAME = 40
Amin Hassanid5742d32019-01-22 21:13:34 -080050
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -080051
Xiaochu Liudeed0232018-06-26 10:25:34 -070052def HashFile(file_path):
53 """Calculate the sha256 hash of a file.
54
55 Args:
56 file_path: (str) path to the file.
57
58 Returns:
59 [str]: The sha256 hash of the file.
60 """
61 sha256 = hashlib.sha256()
62 with open(file_path, 'rb') as f:
63 for b in iter(lambda: f.read(2048), b''):
64 sha256.update(b)
65 return sha256.hexdigest()
66
67
Jae Hoon Kim5f411e42020-01-09 13:30:56 -080068def GetValueInJsonFile(json_path, key, default_value=None):
69 """Reads file containing JSON and returns value or default_value for key.
70
71 Args:
72 json_path: (str) File containing JSON.
73 key: (str) The desired key to lookup.
74 default_value: (default:None) The default value returned in case of missing
75 key.
76 """
77 with open(json_path) as fd:
78 return json.load(fd).get(key, default_value)
79
80
Andrew67b5fa72020-02-05 14:14:48 -080081class EbuildParams(object):
82 """Object to store and retrieve DLC ebuild parameters.
83
84 Attributes:
85 dlc_id: (str) DLC ID.
86 dlc_package: (str) DLC package.
87 fs_type: (str) file system type.
88 pre_allocated_blocks: (int) number of blocks pre-allocated on device.
89 version: (str) DLC version.
90 name: (str) DLC name.
Jae Hoon Kim6ef63172020-04-06 12:39:04 -070091 description: (str) DLC description.
Andrew67b5fa72020-02-05 14:14:48 -080092 preload: (bool) allow for preloading DLC.
Amin Hassani160e12e2020-04-13 14:29:36 -070093 used_by: (str) The user of this DLC, e.g. "system" or "user"
Andrew67b5fa72020-02-05 14:14:48 -080094 """
95
96 def __init__(self, dlc_id, dlc_package, fs_type, pre_allocated_blocks,
Amin Hassani160e12e2020-04-13 14:29:36 -070097 version, name, description, preload, used_by):
Andrew67b5fa72020-02-05 14:14:48 -080098 self.dlc_id = dlc_id
99 self.dlc_package = dlc_package
100 self.fs_type = fs_type
101 self.pre_allocated_blocks = pre_allocated_blocks
102 self.version = version
103 self.name = name
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700104 self.description = description
Andrew67b5fa72020-02-05 14:14:48 -0800105 self.preload = preload
Amin Hassani160e12e2020-04-13 14:29:36 -0700106 self.used_by = used_by
Andrew67b5fa72020-02-05 14:14:48 -0800107
108 def StoreDlcParameters(self, install_root_dir, sudo):
109 """Store DLC parameters defined in the ebuild.
110
111 Store DLC parameters defined in the ebuild in a temporary file so they can
112 be retrieved in the build_image phase.
113
114 Args:
115 install_root_dir: (str) The path to the root installation directory.
116 sudo: (bool) Use sudo to write the file.
117 """
118 ebuild_params_path = EbuildParams.GetParamsPath(install_root_dir,
119 self.dlc_id,
120 self.dlc_package)
121 osutils.WriteFile(ebuild_params_path,
122 json.dumps(self.__dict__),
123 makedirs=True, sudo=sudo)
124
125 @staticmethod
126 def GetParamsPath(install_root_dir, dlc_id, dlc_package):
127 """Get the path to the file storing the ebuild parameters.
128
129 Args:
130 install_root_dir: (str) The path to the root installation directory.
131 dlc_id: (str) DLC ID.
132 dlc_package: (str) DLC package.
133
134 Returns:
135 [str]: Path to |EBUILD_PARAMETERS|.
136 """
137 return os.path.join(install_root_dir, DLC_BUILD_DIR, dlc_id, dlc_package,
138 EBUILD_PARAMETERS)
139
140 @classmethod
Jae Hoon Kim264b8d22020-04-06 11:49:19 -0700141 def LoadEbuildParams(cls, sysroot, dlc_id, dlc_package):
Andrew67b5fa72020-02-05 14:14:48 -0800142 """Read the stored ebuild parameters file and return a class instance.
143
144 Args:
145 dlc_id: (str) DLC ID.
146 dlc_package: (str) DLC package.
147 sysroot: (str) The path to the build root directory.
148
149 Returns:
150 [bool] : True if |ebuild_params_path| exists, False otherwise.
151 """
152 path = cls.GetParamsPath(sysroot, dlc_id, dlc_package)
153 if not os.path.exists(path):
154 return None
155
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700156 with open(path, 'rb') as fp:
Andrew67b5fa72020-02-05 14:14:48 -0800157 return cls(**json.load(fp))
158
159 def __str__(self):
160 return str(self.__dict__)
161
162
Amin Hassani174eb7e2019-01-18 11:11:24 -0800163class DlcGenerator(object):
Xiaochu Liudeed0232018-06-26 10:25:34 -0700164 """Object to generate DLC artifacts."""
165 # Block size for the DLC image.
166 # We use 4K for various reasons:
167 # 1. it's what imageloader (linux kernel) supports.
168 # 2. it's what verity supports.
169 _BLOCK_SIZE = 4096
170 # Blocks in the initial sparse image.
171 _BLOCKS = 500000
172 # Version of manifest file.
173 _MANIFEST_VERSION = 1
174
Amin Hassanicc7ffce2019-01-11 14:57:52 -0800175 # The DLC root path inside the DLC module.
176 _DLC_ROOT_DIR = 'root'
177
Andrew67b5fa72020-02-05 14:14:48 -0800178 def __init__(self, ebuild_params, sysroot, install_root_dir, src_dir=None):
Xiaochu Liudeed0232018-06-26 10:25:34 -0700179 """Object initializer.
180
181 Args:
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800182 sysroot: (str) The path to the build root directory.
Amin Hassani2af75a92019-01-22 21:07:45 -0800183 install_root_dir: (str) The path to the root installation directory.
Andrew67b5fa72020-02-05 14:14:48 -0800184 ebuild_params: (EbuildParams) Ebuild variables.
185 src_dir: (str) Optional path to the DLC source root directory. When None,
186 the default directory in |DLC_BUILD_DIR| is used.
Xiaochu Liudeed0232018-06-26 10:25:34 -0700187 """
Andrew67b5fa72020-02-05 14:14:48 -0800188 # Use a temporary directory to avoid having to use sudo every time we write
189 # into the build directory.
190 self.temp_root = osutils.TempDir(prefix='dlc', sudo_rm=True)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700191 self.src_dir = src_dir
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800192 self.sysroot = sysroot
Amin Hassani2af75a92019-01-22 21:07:45 -0800193 self.install_root_dir = install_root_dir
Andrew67b5fa72020-02-05 14:14:48 -0800194 self.ebuild_params = ebuild_params
195 # If the client is not overriding the src_dir, use the default one.
196 if not self.src_dir:
197 self.src_dir = os.path.join(self.sysroot, DLC_BUILD_DIR,
198 self.ebuild_params.dlc_id,
199 self.ebuild_params.dlc_package,
200 self._DLC_ROOT_DIR)
Amin Hassani2af75a92019-01-22 21:07:45 -0800201
Andrew67b5fa72020-02-05 14:14:48 -0800202 self.image_dir = os.path.join(self.temp_root.tempdir,
203 DLC_BUILD_DIR,
204 self.ebuild_params.dlc_id,
205 self.ebuild_params.dlc_package)
206
207 self.meta_dir = os.path.join(self.image_dir, DLC_TMP_META_DIR)
Amin Hassani2af75a92019-01-22 21:07:45 -0800208
Xiaochu Liudeed0232018-06-26 10:25:34 -0700209 # Create path for all final artifacts.
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800210 self.dest_image = os.path.join(self.image_dir, DLC_IMAGE)
Amin Hassani2af75a92019-01-22 21:07:45 -0800211 self.dest_table = os.path.join(self.meta_dir, 'table')
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800212 self.dest_imageloader_json = os.path.join(self.meta_dir, IMAGELOADER_JSON)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700213
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700214 # Log out the member variable values initially set.
215 logging.debug('Initial internal values of DlcGenerator: %s',
Andrew67b5fa72020-02-05 14:14:48 -0800216 repr({k:str(i) for k, i in self.__dict__.items()}))
217
218 def CopyTempContentsToBuildDir(self):
219 """Copy the temp files to the build directory using sudo."""
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700220 src = self.temp_root.tempdir.rstrip('/') + '/.'
221 dst = self.install_root_dir
222 logging.info(
223 'Copy files from temporary directory (%s) to build directory (%s).',
224 src, dst)
225 cros_build_lib.sudo_run(['cp', '-dR', src, dst])
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700226
Xiaochu Liudeed0232018-06-26 10:25:34 -0700227 def SquashOwnerships(self, path):
228 """Squash the owernships & permissions for files.
229
230 Args:
231 path: (str) path that contains all files to be processed.
232 """
Mike Frysinger45602c72019-09-22 02:15:11 -0400233 cros_build_lib.sudo_run(['chown', '-R', '0:0', path])
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800234 cros_build_lib.sudo_run([
235 'find', path, '-exec', 'touch', '-h', '-t', '197001010000.00', '{}', '+'
236 ])
Xiaochu Liudeed0232018-06-26 10:25:34 -0700237
238 def CreateExt4Image(self):
239 """Create an ext4 image."""
240 with osutils.TempDir(prefix='dlc_') as temp_dir:
241 mount_point = os.path.join(temp_dir, 'mount_point')
Andrew67b5fa72020-02-05 14:14:48 -0800242 # Create the directory where the image is located if it doesn't exist.
243 osutils.SafeMakedirs(os.path.split(self.dest_image)[0])
Xiaochu Liudeed0232018-06-26 10:25:34 -0700244 # Create a raw image file.
245 with open(self.dest_image, 'w') as f:
246 f.truncate(self._BLOCKS * self._BLOCK_SIZE)
247 # Create an ext4 file system on the raw image.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800248 cros_build_lib.run([
249 '/sbin/mkfs.ext4', '-b',
250 str(self._BLOCK_SIZE), '-O', '^has_journal', self.dest_image
251 ],
252 capture_output=True)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700253 # Create the mount_point directory.
254 osutils.SafeMakedirs(mount_point)
255 # Mount the ext4 image.
256 osutils.MountDir(self.dest_image, mount_point, mount_opts=('loop', 'rw'))
Amin Hassanicc7ffce2019-01-11 14:57:52 -0800257
Xiaochu Liudeed0232018-06-26 10:25:34 -0700258 try:
Amin Hassani11a88cf2019-01-29 15:31:24 -0800259 self.SetupDlcImageFiles(mount_point)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700260 finally:
261 # Unmount the ext4 image.
262 osutils.UmountDir(mount_point)
263 # Shrink to minimum size.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800264 cros_build_lib.run(['/sbin/e2fsck', '-y', '-f', self.dest_image],
265 capture_output=True)
266 cros_build_lib.run(['/sbin/resize2fs', '-M', self.dest_image],
267 capture_output=True)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700268
269 def CreateSquashfsImage(self):
270 """Create a squashfs image."""
271 with osutils.TempDir(prefix='dlc_') as temp_dir:
Amin Hassani22a25eb2019-01-11 14:25:02 -0800272 squashfs_root = os.path.join(temp_dir, 'squashfs-root')
Amin Hassani11a88cf2019-01-29 15:31:24 -0800273 self.SetupDlcImageFiles(squashfs_root)
Amin Hassani22a25eb2019-01-11 14:25:02 -0800274
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800275 cros_build_lib.run([
276 'mksquashfs', squashfs_root, self.dest_image, '-4k-align', '-noappend'
277 ],
278 capture_output=True)
Amin Hassani22a25eb2019-01-11 14:25:02 -0800279
280 # We changed the ownership and permissions of the squashfs_root
281 # directory. Now we need to remove it manually.
282 osutils.RmDir(squashfs_root, sudo=True)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700283
Amin Hassani11a88cf2019-01-29 15:31:24 -0800284 def SetupDlcImageFiles(self, dlc_dir):
285 """Prepares the directory dlc_dir with all the files a DLC needs.
286
287 Args:
288 dlc_dir: (str) The path to where to setup files inside the DLC.
289 """
290 dlc_root_dir = os.path.join(dlc_dir, self._DLC_ROOT_DIR)
291 osutils.SafeMakedirs(dlc_root_dir)
Jae Hoon Kimd14646b2019-08-21 14:49:26 -0700292 osutils.CopyDirContents(self.src_dir, dlc_root_dir, symlinks=True)
Amin Hassani11a88cf2019-01-29 15:31:24 -0800293 self.PrepareLsbRelease(dlc_dir)
294 self.CollectExtraResources(dlc_dir)
295 self.SquashOwnerships(dlc_dir)
296
Amin Hassanid5742d32019-01-22 21:13:34 -0800297 def PrepareLsbRelease(self, dlc_dir):
298 """Prepare the file /etc/lsb-release in the DLC module.
299
300 This file is used dropping some identification parameters for the DLC.
301
302 Args:
303 dlc_dir: (str) The path to root directory of the DLC. e.g. mounted point
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800304 when we are creating the image.
Amin Hassanid5742d32019-01-22 21:13:34 -0800305 """
Amin Hassani8f1cc0f2019-03-06 15:34:53 -0800306 # Reading the platform APPID and creating the DLC APPID.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800307 platform_lsb_release = osutils.ReadFile(
308 os.path.join(self.sysroot, LSB_RELEASE))
Amin Hassani8f1cc0f2019-03-06 15:34:53 -0800309 app_id = None
310 for line in platform_lsb_release.split('\n'):
311 if line.startswith(cros_set_lsb_release.LSB_KEY_APPID_RELEASE):
312 app_id = line.split('=')[1]
313 if app_id is None:
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800314 raise Exception(
315 '%s does not have a valid key %s' %
316 (platform_lsb_release, cros_set_lsb_release.LSB_KEY_APPID_RELEASE))
Amin Hassanid5742d32019-01-22 21:13:34 -0800317
Mike Frysinger1c834ea2019-10-14 04:29:41 -0400318 fields = (
Andrew67b5fa72020-02-05 14:14:48 -0800319 (DLC_ID_KEY, self.ebuild_params.dlc_id),
320 (DLC_PACKAGE_KEY, self.ebuild_params.dlc_package),
321 (DLC_NAME_KEY, self.ebuild_params.name),
Amin Hassani8f1cc0f2019-03-06 15:34:53 -0800322 # The DLC appid is generated by concatenating the platform appid with
323 # the DLC ID using an underscore. This pattern should never be changed
324 # once set otherwise it can break a lot of things!
Andrew67b5fa72020-02-05 14:14:48 -0800325 (DLC_APPID_KEY, '%s_%s' % (app_id, self.ebuild_params.dlc_id)),
Mike Frysinger1c834ea2019-10-14 04:29:41 -0400326 )
Amin Hassani8f1cc0f2019-03-06 15:34:53 -0800327
328 lsb_release = os.path.join(dlc_dir, LSB_RELEASE)
329 osutils.SafeMakedirs(os.path.dirname(lsb_release))
Mike Frysinger1c834ea2019-10-14 04:29:41 -0400330 content = ''.join('%s=%s\n' % (k, v) for k, v in fields)
Amin Hassanid5742d32019-01-22 21:13:34 -0800331 osutils.WriteFile(lsb_release, content)
332
Amin Hassani11a88cf2019-01-29 15:31:24 -0800333 def CollectExtraResources(self, dlc_dir):
334 """Collect the extra resources needed by the DLC module.
335
336 Look at the documentation around _EXTRA_RESOURCES.
337
338 Args:
339 dlc_dir: (str) The path to root directory of the DLC. e.g. mounted point
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800340 when we are creating the image.
Amin Hassani11a88cf2019-01-29 15:31:24 -0800341 """
342 for r in _EXTRA_RESOURCES:
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800343 source_path = os.path.join(self.sysroot, r)
Amin Hassani11a88cf2019-01-29 15:31:24 -0800344 target_path = os.path.join(dlc_dir, r)
345 osutils.SafeMakedirs(os.path.dirname(target_path))
346 shutil.copyfile(source_path, target_path)
347
Xiaochu Liudeed0232018-06-26 10:25:34 -0700348 def CreateImage(self):
349 """Create the image and copy the DLC files to it."""
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700350 logging.info('Creating the DLC image.')
Andrew67b5fa72020-02-05 14:14:48 -0800351 if self.ebuild_params.fs_type == _EXT4_TYPE:
Xiaochu Liudeed0232018-06-26 10:25:34 -0700352 self.CreateExt4Image()
Andrew67b5fa72020-02-05 14:14:48 -0800353 elif self.ebuild_params.fs_type == _SQUASHFS_TYPE:
Xiaochu Liudeed0232018-06-26 10:25:34 -0700354 self.CreateSquashfsImage()
355 else:
Andrew67b5fa72020-02-05 14:14:48 -0800356 raise ValueError('Wrong fs type: %s used:' % self.ebuild_params.fs_type)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700357
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700358 def VerifyImageSize(self):
Xiaochu Liu36b30592019-08-06 09:39:54 -0700359 """Verify the image can fit to the reserved file."""
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700360 logging.info('Verifying the DLC image size.')
361 image_bytes = os.path.getsize(self.dest_image)
Andrew67b5fa72020-02-05 14:14:48 -0800362 preallocated_bytes = (self.ebuild_params.pre_allocated_blocks *
363 self._BLOCK_SIZE)
Xiaochu Liu36b30592019-08-06 09:39:54 -0700364 # Verifies the actual size of the DLC image is NOT smaller than the
365 # preallocated space.
366 if preallocated_bytes < image_bytes:
367 raise ValueError(
368 'The DLC_PREALLOC_BLOCKS (%s) value set in DLC ebuild resulted in a '
369 'max size of DLC_PREALLOC_BLOCKS * 4K (%s) bytes the DLC image is '
370 'allowed to occupy. The value is smaller than the actual image size '
371 '(%s) required. Increase DLC_PREALLOC_BLOCKS in your ebuild to at '
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800372 'least %d.' %
Andrew67b5fa72020-02-05 14:14:48 -0800373 (self.ebuild_params.pre_allocated_blocks, preallocated_bytes,
374 image_bytes, self.GetOptimalImageBlockSize(image_bytes)))
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700375
376 def GetOptimalImageBlockSize(self, image_bytes):
377 """Given the image bytes, get the least amount of blocks required."""
378 return int(math.ceil(image_bytes / self._BLOCK_SIZE))
Xiaochu Liu36b30592019-08-06 09:39:54 -0700379
Xiaochu Liudeed0232018-06-26 10:25:34 -0700380 def GetImageloaderJsonContent(self, image_hash, table_hash, blocks):
381 """Return the content of imageloader.json file.
382
383 Args:
384 image_hash: (str) sha256 hash of the DLC image.
385 table_hash: (str) sha256 hash of the DLC table file.
386 blocks: (int) number of blocks in the DLC image.
387
388 Returns:
389 [str]: content of imageloader.json file.
390 """
391 return {
Andrew67b5fa72020-02-05 14:14:48 -0800392 'fs-type': self.ebuild_params.fs_type,
393 'id': self.ebuild_params.dlc_id,
394 'package': self.ebuild_params.dlc_package,
Xiaochu Liudeed0232018-06-26 10:25:34 -0700395 'image-sha256-hash': image_hash,
396 'image-type': 'dlc',
397 'is-removable': True,
398 'manifest-version': self._MANIFEST_VERSION,
Andrew67b5fa72020-02-05 14:14:48 -0800399 'name': self.ebuild_params.name,
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700400 'description': self.ebuild_params.description,
Andrew67b5fa72020-02-05 14:14:48 -0800401 'pre-allocated-size':
402 str(self.ebuild_params.pre_allocated_blocks * self._BLOCK_SIZE),
Jae Hoon Kimbd8ae6e2020-02-03 18:54:29 -0800403 'size': str(blocks * self._BLOCK_SIZE),
Xiaochu Liudeed0232018-06-26 10:25:34 -0700404 'table-sha256-hash': table_hash,
Andrew67b5fa72020-02-05 14:14:48 -0800405 'version': self.ebuild_params.version,
406 'preload-allowed': self.ebuild_params.preload,
Amin Hassani160e12e2020-04-13 14:29:36 -0700407 'used-by': self.ebuild_params.used_by,
Xiaochu Liudeed0232018-06-26 10:25:34 -0700408 }
409
410 def GenerateVerity(self):
411 """Generate verity parameters and hashes for the image."""
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700412 logging.info('Generating DLC image verity.')
Xiaochu Liudeed0232018-06-26 10:25:34 -0700413 with osutils.TempDir(prefix='dlc_') as temp_dir:
414 hash_tree = os.path.join(temp_dir, 'hash_tree')
415 # Get blocks in the image.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800416 blocks = math.ceil(os.path.getsize(self.dest_image) / self._BLOCK_SIZE)
417 result = cros_build_lib.run([
418 'verity', 'mode=create', 'alg=sha256', 'payload=' + self.dest_image,
419 'payload_blocks=' + str(blocks), 'hashtree=' + hash_tree,
420 'salt=random'
421 ],
422 capture_output=True)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700423 table = result.output
424
425 # Append the merkle tree to the image.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800426 osutils.WriteFile(
427 self.dest_image, osutils.ReadFile(hash_tree, mode='rb'), mode='a+b')
Xiaochu Liudeed0232018-06-26 10:25:34 -0700428
429 # Write verity parameter to table file.
Mike Frysinger1c834ea2019-10-14 04:29:41 -0400430 osutils.WriteFile(self.dest_table, table, mode='wb')
Xiaochu Liudeed0232018-06-26 10:25:34 -0700431
432 # Compute image hash.
433 image_hash = HashFile(self.dest_image)
434 table_hash = HashFile(self.dest_table)
435 # Write image hash to imageloader.json file.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800436 blocks = math.ceil(os.path.getsize(self.dest_image) / self._BLOCK_SIZE)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700437 imageloader_json_content = self.GetImageloaderJsonContent(
438 image_hash, table_hash, int(blocks))
439 with open(self.dest_imageloader_json, 'w') as f:
440 json.dump(imageloader_json_content, f)
441
442 def GenerateDLC(self):
443 """Generate a DLC artifact."""
Andrew67b5fa72020-02-05 14:14:48 -0800444 # Create directories.
445 osutils.SafeMakedirs(self.image_dir)
446 osutils.SafeMakedirs(self.meta_dir)
447
448 # Create the image into |self.temp_root| and copy the DLC files to it.
Xiaochu Liudeed0232018-06-26 10:25:34 -0700449 self.CreateImage()
Andrew67b5fa72020-02-05 14:14:48 -0800450 # Verify the image created is within pre-allocated size.
Jae Hoon Kime5b88622019-08-23 11:11:33 -0700451 self.VerifyImageSize()
Andrew67b5fa72020-02-05 14:14:48 -0800452 # Generate hash tree and other metadata and save them under
453 # |self.temp_root|.
Xiaochu Liudeed0232018-06-26 10:25:34 -0700454 self.GenerateVerity()
Andrew67b5fa72020-02-05 14:14:48 -0800455 # Copy the files from |self.temp_root| into the build directory.
456 self.CopyTempContentsToBuildDir()
457
458 # Now that the image was successfully generated, delete |ebuild_params_path|
459 # to indicate that the image in the build directory is in sync with the
460 # files installed during the build_package phase.
461 ebuild_params_path = EbuildParams.GetParamsPath(
462 self.sysroot, self.ebuild_params.dlc_id, self.ebuild_params.dlc_package)
463 osutils.SafeUnlink(ebuild_params_path, sudo=True)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700464
465
Andrew67b5fa72020-02-05 14:14:48 -0800466def IsDlcPreloadingAllowed(dlc_id, dlc_build_dir):
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800467 """Validates that DLC and it's packages all were built with DLC_PRELOAD=true.
468
469 Args:
470 dlc_id: (str) DLC ID.
Andrew67b5fa72020-02-05 14:14:48 -0800471 dlc_build_dir: (str) the root path where DLC build files reside.
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800472 """
473
Andrew67b5fa72020-02-05 14:14:48 -0800474 dlc_id_meta_dir = os.path.join(dlc_build_dir, dlc_id)
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800475 if not os.path.exists(dlc_id_meta_dir):
Andrew67b5fa72020-02-05 14:14:48 -0800476 logging.error('DLC build directory (%s) does not exist for preloading '
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800477 'check, will not preload', dlc_id_meta_dir)
478 return False
479
480 packages = os.listdir(dlc_id_meta_dir)
481 if not packages:
Andrew67b5fa72020-02-05 14:14:48 -0800482 logging.error('DLC ID build directory (%s) does not have any '
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800483 'packages, will not preload.', dlc_id_meta_dir)
484 return False
485
Andrew67b5fa72020-02-05 14:14:48 -0800486 for package in packages:
487 image_loader_json = os.path.join(dlc_id_meta_dir, package, DLC_TMP_META_DIR,
488 IMAGELOADER_JSON)
489 if not os.path.exists(image_loader_json):
490 logging.error('DLC metadata file (%s) does not exist, will not preload.',
491 image_loader_json)
492 return False
493 if not GetValueInJsonFile(json_path=image_loader_json,
494 key='preload-allowed', default_value=False):
495 return False
496 # All packages support preload.
497 return True
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800498
499
Andrew67b5fa72020-02-05 14:14:48 -0800500def InstallDlcImages(sysroot, dlc_id=None, install_root_dir=None, preload=False,
501 rootfs=None, src_dir=None):
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800502 """Copies all DLC image files into the images directory.
503
504 Copies the DLC image files in the given build directory into the given DLC
505 image directory. If the DLC build directory does not exist, or there is no DLC
506 for that board, this function does nothing.
507
508 Args:
509 sysroot: Path to directory containing DLC images, e.g /build/<board>.
Andrew67b5fa72020-02-05 14:14:48 -0800510 dlc_id: (str) DLC ID. If None, all the DLCs will be installed.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800511 install_root_dir: Path to DLC output directory, e.g.
Andrew67b5fa72020-02-05 14:14:48 -0800512 src/build/images/<board>/<version>. If None, the image will be generated
513 but will not be copied to a destination.
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800514 preload: When true, only copies DLC(s) if built with DLC_PRELOAD=true.
Andrew67b5fa72020-02-05 14:14:48 -0800515 rootfs: (str) Path to the platform rootfs.
516 src_dir: (str) Path to the DLC source root directory.
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800517 """
Andrew67b5fa72020-02-05 14:14:48 -0800518 dlc_build_dir = os.path.join(sysroot, DLC_BUILD_DIR)
519 if not os.path.exists(dlc_build_dir):
520 logging.info('DLC build directory (%s) does not exist, ignoring.',
521 dlc_build_dir)
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800522 return
523
Andrew67b5fa72020-02-05 14:14:48 -0800524 if dlc_id is not None:
525 if not os.path.exists(os.path.join(dlc_build_dir, dlc_id)):
526 raise Exception(
527 'DLC "%s" does not exist in the build directory %s.' %
528 (dlc_id, dlc_build_dir))
529 dlc_ids = [dlc_id]
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800530 else:
Andrew67b5fa72020-02-05 14:14:48 -0800531 # Process all DLCs.
532 dlc_ids = os.listdir(dlc_build_dir)
533 if not dlc_ids:
534 logging.info('There are no DLC(s) to copy to output, ignoring.')
535 return
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800536
Andrew67b5fa72020-02-05 14:14:48 -0800537 logging.info('Detected the following DLCs: %s', ', '.join(dlc_ids))
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800538
Andrew67b5fa72020-02-05 14:14:48 -0800539 for d_id in dlc_ids:
540 dlc_id_path = os.path.join(dlc_build_dir, d_id)
541 dlc_packages = [direct for direct in os.listdir(dlc_id_path)
542 if os.path.isdir(os.path.join(dlc_id_path, direct))]
543 for d_package in dlc_packages:
544 logging.info('Building image: DLC %s', d_id)
Jae Hoon Kim264b8d22020-04-06 11:49:19 -0700545 params = EbuildParams.LoadEbuildParams(sysroot=sysroot, dlc_id=d_id,
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700546 dlc_package=d_package)
Andrew67b5fa72020-02-05 14:14:48 -0800547 # Because portage sandboxes every ebuild package during build_packages
548 # phase, we cannot delete the old image during that phase, but we can use
549 # the existence of the file |EBUILD_PARAMETERS| to know if the image
550 # has to be generated or not.
551 if not params:
552 logging.info('The ebuild parameters file (%s) for DLC (%s) does not '
553 'exist. This means that the image was already '
554 'generated and there is no need to create it again.',
555 EbuildParams.GetParamsPath(sysroot, d_id, d_package), d_id)
556 else:
557 dlc_generator = DlcGenerator(
558 src_dir=src_dir,
559 sysroot=sysroot,
560 install_root_dir=sysroot,
561 ebuild_params=params)
562 dlc_generator.GenerateDLC()
563
564 # Copy the dlc images to install_root_dir.
565 if install_root_dir:
566 if preload and not IsDlcPreloadingAllowed(d_id, dlc_build_dir):
567 logging.info('Skipping installation of DLC %s because the preload '
568 'flag is set and the DLC does not support preloading.',
569 d_id)
570 else:
Jae Hoon Kimb7da6fe2020-04-02 16:17:48 -0700571 osutils.SafeMakedirsNonRoot(install_root_dir)
Andrew67b5fa72020-02-05 14:14:48 -0800572 install_dlc_dir = os.path.join(install_root_dir, d_id, d_package)
Jae Hoon Kimb7da6fe2020-04-02 16:17:48 -0700573 osutils.SafeMakedirsNonRoot(install_dlc_dir)
574 source_dlc_dir = os.path.join(dlc_build_dir, d_id, d_package)
Andrew67b5fa72020-02-05 14:14:48 -0800575 for filepath in (os.path.join(source_dlc_dir, fname) for fname in
576 os.listdir(source_dlc_dir) if
577 fname.endswith('.img')):
578 logging.info('Copying DLC(%s) image from %s to %s: ', d_id,
579 filepath, install_dlc_dir)
Jae Hoon Kimb7da6fe2020-04-02 16:17:48 -0700580 shutil.copy(filepath, install_dlc_dir)
Andrew67b5fa72020-02-05 14:14:48 -0800581 logging.info('Done copying DLC to %s.', install_dlc_dir)
582 else:
583 logging.info('install_root_dir value was not provided. Copying dlc'
584 ' image skipped.')
585
586 # Create metadata directory in rootfs.
587 if rootfs:
588 meta_rootfs = os.path.join(rootfs, DLC_META_DIR, d_id, d_package)
589 osutils.SafeMakedirs(meta_rootfs, sudo=True)
590 # Copy the metadata files to rootfs.
591 meta_dir_src = os.path.join(dlc_build_dir, d_id, d_package,
592 DLC_TMP_META_DIR)
593 logging.info('Copying DLC(%s) metadata from %s to %s: ', d_id,
594 meta_dir_src, meta_rootfs)
595 # Use sudo_run since osutils.CopyDirContents doesn't support sudo.
596 cros_build_lib.sudo_run(['cp', '-dR',
597 meta_dir_src.rstrip('/') + '/.',
598 meta_rootfs], print_cmd=False, stderr=True)
599
600 else:
601 logging.info('rootfs value was not provided. Copying metadata skipped.')
602
603 logging.info('Done installing DLCs.')
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800604
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800605
Xiaochu Liudeed0232018-06-26 10:25:34 -0700606def GetParser():
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800607 """Creates an argument parser and returns it."""
Xiaochu Liudeed0232018-06-26 10:25:34 -0700608 parser = commandline.ArgumentParser(description=__doc__)
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800609 # This script is used both for building an individual DLC or copying all final
610 # DLCs images to their final destination nearby chromiumsos_test_image.bin,
611 # etc. These two arguments are required in both cases.
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800612 parser.add_argument(
613 '--sysroot',
614 type='path',
615 metavar='DIR',
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800616 help="The root path to the board's build root, e.g. "
617 '/build/eve')
Andrew67b5fa72020-02-05 14:14:48 -0800618 # TODO(andrewlassalle): Remove src-dir in the future(2021?) if nobody uses it.
619 parser.add_argument(
620 '--src-dir',
621 type='path',
622 metavar='SRC_DIR_PATH',
623 help='Override the default Root directory path that contains all DLC '
624 'files to be packed.')
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800625 parser.add_argument(
626 '--install-root-dir',
627 type='path',
628 metavar='DIR',
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800629 help='If building a specific DLC, it is the root path to'
630 ' install DLC images (%s) and metadata (%s). Otherwise it'
631 ' is the target directory where the Chrome OS images gets'
632 ' dropped in build_image, e.g. '
Andrew67b5fa72020-02-05 14:14:48 -0800633 'src/build/images/<board>/latest.' % (DLC_BUILD_DIR, DLC_META_DIR))
Amin Hassani22a25eb2019-01-11 14:25:02 -0800634
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800635 one_dlc = parser.add_argument_group('Arguments required for building only '
636 'one DLC')
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800637 one_dlc.add_argument(
Andrew67b5fa72020-02-05 14:14:48 -0800638 '--rootfs',
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800639 type='path',
Andrew67b5fa72020-02-05 14:14:48 -0800640 metavar='ROOT_FS_PATH',
641 help='Path to the platform rootfs.')
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800642 one_dlc.add_argument(
643 '--pre-allocated-blocks',
644 type=int,
645 metavar='PREALLOCATEDBLOCKS',
646 help='Number of blocks (block size is 4k) that need to'
647 'be pre-allocated on device.')
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800648 one_dlc.add_argument('--version', metavar='VERSION', help='DLC Version.')
649 one_dlc.add_argument('--id', metavar='ID', help='DLC ID (unique per DLC).')
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800650 one_dlc.add_argument(
651 '--package',
652 metavar='PACKAGE',
653 help='The package ID that is unique within a DLC, One'
654 ' DLC cannot have duplicate package IDs.')
655 one_dlc.add_argument(
656 '--name', metavar='NAME', help='A human-readable name for the DLC.')
657 one_dlc.add_argument(
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700658 '--description',
659 help='The description for the DLC.')
660 one_dlc.add_argument(
Jae Hoon Kimfaca4b02020-01-09 13:49:03 -0800661 '--fs-type',
662 metavar='FS_TYPE',
663 default=_SQUASHFS_TYPE,
664 choices=(_SQUASHFS_TYPE, _EXT4_TYPE),
665 help='File system type of the image.')
666 one_dlc.add_argument(
667 '--preload',
668 default=False,
669 action='store_true',
670 help='Allow preloading of DLC.')
Andrew67b5fa72020-02-05 14:14:48 -0800671 one_dlc.add_argument(
Amin Hassani160e12e2020-04-13 14:29:36 -0700672 '--used-by', default=_USED_BY_SYSTEM,
673 choices=(_USED_BY_USER, _USED_BY_SYSTEM),
674 help='Defines how this DLC will be used so dlcservice can take proper '
675 'actions based on the type of usage. For example, if "user" is passed, '
676 'dlcservice does ref counting when DLC is installed/uninstalled. For '
677 '"system", there will be no such provisions.')
678 one_dlc.add_argument(
Andrew67b5fa72020-02-05 14:14:48 -0800679 '--build-package',
680 default=False,
681 action='store_true',
682 help='Flag to indicate if the script is executed during the '
683 'build_packages phase.')
Xiaochu Liudeed0232018-06-26 10:25:34 -0700684 return parser
685
686
Andrew67b5fa72020-02-05 14:14:48 -0800687def ValidateDlcIdentifier(parser, name):
Amin Hassanibc1a4792019-10-24 14:39:57 -0700688 """Validates the DLC identifiers like ID and package names.
689
690 The name specifications are:
691 - No underscore.
692 - First character should be only alphanumeric.
693 - Other characters can be alphanumeric and '-' (dash).
Jae Hoon Kimdb1cab82020-01-21 19:27:28 -0800694 - Maximum length of 40 (MAX_ID_NAME) characters.
Amin Hassanibc1a4792019-10-24 14:39:57 -0700695
696 For more info see:
697 https://chromium.googlesource.com/chromiumos/platform2/+/master/dlcservice/docs/developer.md#create-a-dlc-module
698
699 Args:
Andrew67b5fa72020-02-05 14:14:48 -0800700 parser: Arguments parser.
Jae Hoon Kimdb1cab82020-01-21 19:27:28 -0800701 name: The value of the string to be validated.
Amin Hassanibc1a4792019-10-24 14:39:57 -0700702 """
Jae Hoon Kimdb1cab82020-01-21 19:27:28 -0800703 errors = []
704 if not name:
705 errors.append('Must not be empty.')
706 if not name[0].isalnum():
707 errors.append('Must start with alphanumeric character.')
708 if not re.match(r'^[a-zA-Z0-9][a-zA-Z0-9-]*$', name):
709 errors.append('Must only use alphanumeric and - (dash).')
710 if len(name) > MAX_ID_NAME:
711 errors.append('Must be within %d characters.' % MAX_ID_NAME)
712
713 if errors:
714 msg = '%s is invalid:\n%s' % (name, '\n'.join(errors))
Andrew67b5fa72020-02-05 14:14:48 -0800715 parser.error(msg)
Amin Hassanibc1a4792019-10-24 14:39:57 -0700716
717
Andrew67b5fa72020-02-05 14:14:48 -0800718def ValidateArguments(parser, opts, req_flags, invalid_flags):
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800719 """Validates the correctness of the passed arguments.
720
721 Args:
Andrew67b5fa72020-02-05 14:14:48 -0800722 parser: Arguments parser.
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800723 opts: Parsed arguments.
Andrew67b5fa72020-02-05 14:14:48 -0800724 req_flags: all the required flags.
725 invalid_flags: all the flags that are not allowed.
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800726 """
727 # Make sure if the intention is to build one DLC, all the required arguments
Andrew67b5fa72020-02-05 14:14:48 -0800728 # are passed and none of the invalid ones are passed. This will ensure the
729 # script is called twice per DLC.
730 if opts.id:
731 if not all(vars(opts)[x] is not None for x in req_flags):
732 parser.error('If the intention is to build only one DLC, all the flags'
733 '%s required for it should be passed.' % req_flags)
734 if any(vars(opts)[x] is not None for x in invalid_flags):
735 parser.error('If the intention is to build only one DLC, all the flags'
736 '%s should be passed in the build_packages phase, not in '
737 'the build_image phase.' % invalid_flags)
738
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800739 if opts.fs_type == _EXT4_TYPE:
Andrew67b5fa72020-02-05 14:14:48 -0800740 parser.error('ext4 unsupported for DLC, see https://crbug.com/890060')
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800741
Amin Hassanibc1a4792019-10-24 14:39:57 -0700742 if opts.id:
Andrew67b5fa72020-02-05 14:14:48 -0800743 ValidateDlcIdentifier(parser, opts.id)
Amin Hassanibc1a4792019-10-24 14:39:57 -0700744 if opts.package:
Andrew67b5fa72020-02-05 14:14:48 -0800745 ValidateDlcIdentifier(parser, opts.package)
Amin Hassanibc1a4792019-10-24 14:39:57 -0700746
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800747
Xiaochu Liudeed0232018-06-26 10:25:34 -0700748def main(argv):
Andrew67b5fa72020-02-05 14:14:48 -0800749 parser = GetParser()
750 opts = parser.parse_args(argv)
Xiaochu Liudeed0232018-06-26 10:25:34 -0700751 opts.Freeze()
Andrew67b5fa72020-02-05 14:14:48 -0800752 per_dlc_req_args = ['id']
753 per_dlc_invalid_args = []
754 if opts.build_package:
755 per_dlc_req_args += ['pre_allocated_blocks', 'version', 'name',
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700756 'description', 'package', 'install_root_dir']
Andrew67b5fa72020-02-05 14:14:48 -0800757 per_dlc_invalid_args += ['src_dir', 'sysroot']
Amin Hassanib97a5ee2019-01-23 14:44:43 -0800758 else:
Andrew67b5fa72020-02-05 14:14:48 -0800759 per_dlc_req_args += ['sysroot']
760 per_dlc_invalid_args += ['name', 'pre_allocated_blocks', 'version',
761 'package']
762
763 ValidateArguments(parser, opts, per_dlc_req_args, per_dlc_invalid_args)
764
765 if opts.build_package:
766 logging.info('Building package: DLC %s', opts.id)
Jae Hoon Kim6ef63172020-04-06 12:39:04 -0700767 params = EbuildParams(
768 dlc_id=opts.id,
769 dlc_package=opts.package,
770 fs_type=opts.fs_type,
771 name=opts.name,
772 description=opts.description,
773 pre_allocated_blocks=opts.pre_allocated_blocks,
774 version=opts.version,
Amin Hassani160e12e2020-04-13 14:29:36 -0700775 preload=opts.preload,
776 used_by=opts.used_by)
Andrew67b5fa72020-02-05 14:14:48 -0800777 params.StoreDlcParameters(install_root_dir=opts.install_root_dir, sudo=True)
778
779 else:
780 InstallDlcImages(
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800781 sysroot=opts.sysroot,
Andrew67b5fa72020-02-05 14:14:48 -0800782 dlc_id=opts.id,
Jae Hoon Kim5f411e42020-01-09 13:30:56 -0800783 install_root_dir=opts.install_root_dir,
Andrew67b5fa72020-02-05 14:14:48 -0800784 preload=opts.preload,
785 src_dir=opts.src_dir,
786 rootfs=opts.rootfs)