blob: 96168aadb4c297c7a51eac4c0bdfbee311c2db7c [file] [log] [blame]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001#
2# Copyright (c) 2013, Intel Corporation.
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: GPL-2.0-only
Brad Bishop6e60e8b2018-02-01 10:27:11 -05005#
6# DESCRIPTION
7# This implements the 'direct' imager plugin class for 'wic'
8#
9# AUTHORS
10# Tom Zanussi <tom.zanussi (at] linux.intel.com>
11#
12
13import logging
14import os
Brad Bishopd7bf8c12018-02-25 22:55:05 -050015import random
Brad Bishop6e60e8b2018-02-01 10:27:11 -050016import shutil
17import tempfile
18import uuid
19
20from time import strftime
21
Brad Bishopd7bf8c12018-02-25 22:55:05 -050022from oe.path import copyhardlinktree
23
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024from wic import WicError
25from wic.filemap import sparse_copy
26from wic.ksparser import KickStart, KickStartError
27from wic.pluginbase import PluginMgr, ImagerPlugin
Brad Bishopd7bf8c12018-02-25 22:55:05 -050028from wic.misc import get_bitbake_var, exec_cmd, exec_native_cmd
Brad Bishop6e60e8b2018-02-01 10:27:11 -050029
30logger = logging.getLogger('wic')
31
32class DirectPlugin(ImagerPlugin):
33 """
34 Install a system into a file containing a partitioned disk image.
35
36 An image file is formatted with a partition table, each partition
37 created from a rootfs or other OpenEmbedded build artifact and dd'ed
38 into the virtual disk. The disk image can subsequently be dd'ed onto
39 media and used on actual hardware.
40 """
41 name = 'direct'
42
43 def __init__(self, wks_file, rootfs_dir, bootimg_dir, kernel_dir,
44 native_sysroot, oe_builddir, options):
45 try:
46 self.ks = KickStart(wks_file)
47 except KickStartError as err:
48 raise WicError(str(err))
49
50 # parse possible 'rootfs=name' items
51 self.rootfs_dir = dict(rdir.split('=') for rdir in rootfs_dir.split(' '))
52 self.bootimg_dir = bootimg_dir
53 self.kernel_dir = kernel_dir
54 self.native_sysroot = native_sysroot
55 self.oe_builddir = oe_builddir
56
Andrew Geisslerd1e89492021-02-12 15:35:20 -060057 self.debug = options.debug
Brad Bishop6e60e8b2018-02-01 10:27:11 -050058 self.outdir = options.outdir
59 self.compressor = options.compressor
60 self.bmap = options.bmap
Brad Bishopd7bf8c12018-02-25 22:55:05 -050061 self.no_fstab_update = options.no_fstab_update
Andrew Geisslerd1e89492021-02-12 15:35:20 -060062 self.updated_fstab_path = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -050063
64 self.name = "%s-%s" % (os.path.splitext(os.path.basename(wks_file))[0],
65 strftime("%Y%m%d%H%M"))
Andrew Geisslerd1e89492021-02-12 15:35:20 -060066 self.workdir = self.setup_workdir(options.workdir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050067 self._image = None
68 self.ptable_format = self.ks.bootloader.ptable
69 self.parts = self.ks.partitions
70
71 # as a convenience, set source to the boot partition source
72 # instead of forcing it to be set via bootloader --source
73 for part in self.parts:
74 if not self.ks.bootloader.source and part.mountpoint == "/boot":
75 self.ks.bootloader.source = part.source
76 break
77
78 image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
79 self._image = PartitionedImage(image_path, self.ptable_format,
80 self.parts, self.native_sysroot)
81
Andrew Geisslerd1e89492021-02-12 15:35:20 -060082 def setup_workdir(self, workdir):
83 if workdir:
84 if os.path.exists(workdir):
85 raise WicError("Internal workdir '%s' specified in wic arguments already exists!" % (workdir))
86
87 os.makedirs(workdir)
88 return workdir
89 else:
90 return tempfile.mkdtemp(dir=self.outdir, prefix='tmp.wic.')
91
Brad Bishop6e60e8b2018-02-01 10:27:11 -050092 def do_create(self):
93 """
94 Plugin entry point.
95 """
96 try:
97 self.create()
98 self.assemble()
99 self.finalize()
100 self.print_info()
101 finally:
102 self.cleanup()
103
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600104 def update_fstab(self, image_rootfs):
105 """Assume partition order same as in wks"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500106 if not image_rootfs:
107 return
108
109 fstab_path = image_rootfs + "/etc/fstab"
110 if not os.path.isfile(fstab_path):
111 return
112
113 with open(fstab_path) as fstab:
114 fstab_lines = fstab.readlines()
115
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500116 updated = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600117 for part in self.parts:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500118 if not part.realnum or not part.mountpoint \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500119 or part.mountpoint == "/":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500120 continue
121
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500122 if part.use_uuid:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400123 if part.fsuuid:
124 # FAT UUID is different from others
125 if len(part.fsuuid) == 10:
126 device_name = "UUID=%s-%s" % \
127 (part.fsuuid[2:6], part.fsuuid[6:])
128 else:
129 device_name = "UUID=%s" % part.fsuuid
130 else:
131 device_name = "PARTUUID=%s" % part.uuid
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800132 elif part.use_label:
133 device_name = "LABEL=%s" % part.label
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500134 else:
135 # mmc device partitions are named mmcblk0p1, mmcblk0p2..
136 prefix = 'p' if part.disk.startswith('mmcblk') else ''
137 device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500138
139 opts = part.fsopts if part.fsopts else "defaults"
140 line = "\t".join([device_name, part.mountpoint, part.fstype,
141 opts, "0", "0"]) + "\n"
142
143 fstab_lines.append(line)
144 updated = True
145
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600146 if updated:
147 self.updated_fstab_path = os.path.join(self.workdir, "fstab")
148 with open(self.updated_fstab_path, "w") as f:
149 f.writelines(fstab_lines)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500150
151 def _full_path(self, path, name, extention):
152 """ Construct full file path to a file we generate. """
153 return os.path.join(path, "%s-%s.%s" % (self.name, name, extention))
154
155 #
156 # Actual implemention
157 #
158 def create(self):
159 """
160 For 'wic', we already have our build artifacts - we just create
161 filesystems from the artifacts directly and combine them into
162 a partitioned image.
163 """
Brad Bishop96ff1982019-08-19 13:50:42 -0400164 if not self.no_fstab_update:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600165 self.update_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500166
167 for part in self.parts:
168 # get rootfs size from bitbake variable if it's not set in .ks file
169 if not part.size:
170 # and if rootfs name is specified for the partition
171 image_name = self.rootfs_dir.get(part.rootfs_dir)
172 if image_name and os.path.sep not in image_name:
173 # Bitbake variable ROOTFS_SIZE is calculated in
174 # Image._get_rootfs_size method from meta/lib/oe/image.py
175 # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
176 # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
177 rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name)
178 if rsize_bb:
179 part.size = int(round(float(rsize_bb)))
180
181 self._image.prepare(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500182 self._image.layout_partitions()
183 self._image.create()
184
185 def assemble(self):
186 """
187 Assemble partitions into disk image
188 """
189 self._image.assemble()
190
191 def finalize(self):
192 """
193 Finalize the disk image.
194
195 For example, prepare the image to be bootable by e.g.
196 creating and installing a bootloader configuration.
197 """
198 source_plugin = self.ks.bootloader.source
199 disk_name = self.parts[0].disk
200 if source_plugin:
201 plugin = PluginMgr.get_plugins('source')[source_plugin]
202 plugin.do_install_disk(self._image, disk_name, self, self.workdir,
203 self.oe_builddir, self.bootimg_dir,
204 self.kernel_dir, self.native_sysroot)
205
206 full_path = self._image.path
207 # Generate .bmap
208 if self.bmap:
209 logger.debug("Generating bmap file for %s", disk_name)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500210 python = os.path.join(self.native_sysroot, 'usr/bin/python3-native/python3')
211 bmaptool = os.path.join(self.native_sysroot, 'usr/bin/bmaptool')
212 exec_native_cmd("%s %s create %s -o %s.bmap" % \
213 (python, bmaptool, full_path, full_path), self.native_sysroot)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500214 # Compress the image
215 if self.compressor:
216 logger.debug("Compressing disk %s with %s", disk_name, self.compressor)
217 exec_cmd("%s %s" % (self.compressor, full_path))
218
219 def print_info(self):
220 """
221 Print the image(s) and artifacts used, for the user.
222 """
223 msg = "The new image(s) can be found here:\n"
224
225 extension = "direct" + {"gzip": ".gz",
226 "bzip2": ".bz2",
227 "xz": ".xz",
228 None: ""}.get(self.compressor)
229 full_path = self._full_path(self.outdir, self.parts[0].disk, extension)
230 msg += ' %s\n\n' % full_path
231
232 msg += 'The following build artifacts were used to create the image(s):\n'
233 for part in self.parts:
234 if part.rootfs_dir is None:
235 continue
236 if part.mountpoint == '/':
237 suffix = ':'
238 else:
239 suffix = '["%s"]:' % (part.mountpoint or part.label)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400240 rootdir = part.rootfs_dir
Brad Bishop316dfdd2018-06-25 12:45:53 -0400241 msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), rootdir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500242
243 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
244 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
245 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
246
247 logger.info(msg)
248
249 @property
250 def rootdev(self):
251 """
252 Get root device name to use as a 'root' parameter
253 in kernel command line.
254
255 Assume partition order same as in wks
256 """
257 for part in self.parts:
258 if part.mountpoint == "/":
259 if part.uuid:
260 return "PARTUUID=%s" % part.uuid
261 else:
262 suffix = 'p' if part.disk.startswith('mmcblk') else ''
263 return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum)
264
265 def cleanup(self):
266 if self._image:
267 self._image.cleanup()
268
269 # Move results to the output dir
270 if not os.path.exists(self.outdir):
271 os.makedirs(self.outdir)
272
273 for fname in os.listdir(self.workdir):
274 path = os.path.join(self.workdir, fname)
275 if os.path.isfile(path):
276 shutil.move(path, os.path.join(self.outdir, fname))
277
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600278 # remove work directory when it is not in debugging mode
279 if not self.debug:
280 shutil.rmtree(self.workdir, ignore_errors=True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500281
282# Overhead of the MBR partitioning scheme (just one sector)
283MBR_OVERHEAD = 1
284
285# Overhead of the GPT partitioning scheme
286GPT_OVERHEAD = 34
287
288# Size of a sector in bytes
289SECTOR_SIZE = 512
290
291class PartitionedImage():
292 """
293 Partitioned image in a file.
294 """
295
296 def __init__(self, path, ptable_format, partitions, native_sysroot=None):
297 self.path = path # Path to the image file
298 self.numpart = 0 # Number of allocated partitions
299 self.realpart = 0 # Number of partitions in the partition table
Brad Bishop08902b02019-08-20 09:16:51 -0400300 self.primary_part_num = 0 # Number of primary partitions (msdos)
301 self.extendedpart = 0 # Create extended partition before this logical partition (msdos)
302 self.extended_size_sec = 0 # Size of exteded partition (msdos)
303 self.logical_part_cnt = 0 # Number of total logical paritions (msdos)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500304 self.offset = 0 # Offset of next partition (in sectors)
305 self.min_size = 0 # Minimum required disk size to fit
306 # all partitions (in bytes)
307 self.ptable_format = ptable_format # Partition table format
308 # Disk system identifier
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500309 self.identifier = random.SystemRandom().randint(1, 0xffffffff)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500310
311 self.partitions = partitions
312 self.partimages = []
313 # Size of a sector used in calculations
314 self.sector_size = SECTOR_SIZE
315 self.native_sysroot = native_sysroot
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400316 num_real_partitions = len([p for p in self.partitions if not p.no_table])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500317
318 # calculate the real partition number, accounting for partitions not
319 # in the partition table and logical partitions
320 realnum = 0
321 for part in self.partitions:
322 if part.no_table:
323 part.realnum = 0
324 else:
325 realnum += 1
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400326 if self.ptable_format == 'msdos' and realnum > 3 and num_real_partitions > 4:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500327 part.realnum = realnum + 1
328 continue
329 part.realnum = realnum
330
Brad Bishop316dfdd2018-06-25 12:45:53 -0400331 # generate parition and filesystem UUIDs
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500332 for part in self.partitions:
333 if not part.uuid and part.use_uuid:
334 if self.ptable_format == 'gpt':
335 part.uuid = str(uuid.uuid4())
336 else: # msdos partition table
337 part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400338 if not part.fsuuid:
339 if part.fstype == 'vfat' or part.fstype == 'msdos':
340 part.fsuuid = '0x' + str(uuid.uuid4())[:8].upper()
341 else:
342 part.fsuuid = str(uuid.uuid4())
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600343 else:
344 #make sure the fsuuid for vfat/msdos align with format 0xYYYYYYYY
345 if part.fstype == 'vfat' or part.fstype == 'msdos':
346 if part.fsuuid.upper().startswith("0X"):
347 part.fsuuid = '0x' + part.fsuuid.upper()[2:].rjust(8,"0")
348 else:
349 part.fsuuid = '0x' + part.fsuuid.upper().rjust(8,"0")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350
351 def prepare(self, imager):
352 """Prepare an image. Call prepare method of all image partitions."""
353 for part in self.partitions:
354 # need to create the filesystems in order to get their
355 # sizes before we can add them and do the layout.
356 part.prepare(imager, imager.workdir, imager.oe_builddir,
357 imager.rootfs_dir, imager.bootimg_dir,
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600358 imager.kernel_dir, imager.native_sysroot,
359 imager.updated_fstab_path)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500360
361 # Converting kB to sectors for parted
362 part.size_sec = part.disk_size * 1024 // self.sector_size
363
364 def layout_partitions(self):
365 """ Layout the partitions, meaning calculate the position of every
366 partition on the disk. The 'ptable_format' parameter defines the
367 partition table format and may be "msdos". """
368
369 logger.debug("Assigning %s partitions to disks", self.ptable_format)
370
371 # The number of primary and logical partitions. Extended partition and
372 # partitions not listed in the table are not included.
373 num_real_partitions = len([p for p in self.partitions if not p.no_table])
374
375 # Go through partitions in the order they are added in .ks file
376 for num in range(len(self.partitions)):
377 part = self.partitions[num]
378
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500379 if self.ptable_format == 'msdos' and part.part_name:
380 raise WicError("setting custom partition name is not " \
381 "implemented for msdos partitions")
382
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500383 if self.ptable_format == 'msdos' and part.part_type:
384 # The --part-type can also be implemented for MBR partitions,
385 # in which case it would map to the 1-byte "partition type"
386 # filed at offset 3 of the partition entry.
387 raise WicError("setting custom partition type is not " \
388 "implemented for msdos partitions")
389
390 # Get the disk where the partition is located
391 self.numpart += 1
392 if not part.no_table:
393 self.realpart += 1
394
395 if self.numpart == 1:
396 if self.ptable_format == "msdos":
397 overhead = MBR_OVERHEAD
398 elif self.ptable_format == "gpt":
399 overhead = GPT_OVERHEAD
400
401 # Skip one sector required for the partitioning scheme overhead
402 self.offset += overhead
403
Brad Bishop08902b02019-08-20 09:16:51 -0400404 if self.ptable_format == "msdos":
405 if self.primary_part_num > 3 or \
406 (self.extendedpart == 0 and self.primary_part_num >= 3 and num_real_partitions > 4):
407 part.type = 'logical'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500408 # Reserve a sector for EBR for every logical partition
409 # before alignment is performed.
Brad Bishop08902b02019-08-20 09:16:51 -0400410 if part.type == 'logical':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500411 self.offset += 2
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500412
Brad Bishop08902b02019-08-20 09:16:51 -0400413 align_sectors = 0
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500414 if part.align:
415 # If not first partition and we do have alignment set we need
416 # to align the partition.
417 # FIXME: This leaves a empty spaces to the disk. To fill the
418 # gaps we could enlargea the previous partition?
419
420 # Calc how much the alignment is off.
421 align_sectors = self.offset % (part.align * 1024 // self.sector_size)
422
423 if align_sectors:
424 # If partition is not aligned as required, we need
425 # to move forward to the next alignment point
426 align_sectors = (part.align * 1024 // self.sector_size) - align_sectors
427
428 logger.debug("Realignment for %s%s with %s sectors, original"
429 " offset %s, target alignment is %sK.",
430 part.disk, self.numpart, align_sectors,
431 self.offset, part.align)
432
433 # increase the offset so we actually start the partition on right alignment
434 self.offset += align_sectors
435
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500436 if part.offset is not None:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500437 offset = part.offset // self.sector_size
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500438
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500439 if offset * self.sector_size != part.offset:
440 raise WicError("Could not place %s%s at offset %d with sector size %d" % (part.disk, self.numpart, part.offset, self.sector_size))
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500441
442 delta = offset - self.offset
443 if delta < 0:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500444 raise WicError("Could not place %s%s at offset %d: next free sector is %d (delta: %d)" % (part.disk, self.numpart, part.offset, self.offset, delta))
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500445
446 logger.debug("Skipping %d sectors to place %s%s at offset %dK",
447 delta, part.disk, self.numpart, part.offset)
448
449 self.offset = offset
450
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500451 part.start = self.offset
452 self.offset += part.size_sec
453
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500454 if not part.no_table:
455 part.num = self.realpart
456 else:
457 part.num = 0
458
Brad Bishop08902b02019-08-20 09:16:51 -0400459 if self.ptable_format == "msdos" and not part.no_table:
460 if part.type == 'logical':
461 self.logical_part_cnt += 1
462 part.num = self.logical_part_cnt + 4
463 if self.extendedpart == 0:
464 # Create extended partition as a primary partition
465 self.primary_part_num += 1
466 self.extendedpart = part.num
467 else:
468 self.extended_size_sec += align_sectors
Andrew Geissler82c905d2020-04-13 13:39:40 -0500469 self.extended_size_sec += part.size_sec + 2
Brad Bishop08902b02019-08-20 09:16:51 -0400470 else:
471 self.primary_part_num += 1
472 part.num = self.primary_part_num
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500473
474 logger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
475 "sectors (%d bytes).", part.mountpoint, part.disk,
476 part.num, part.start, self.offset - 1, part.size_sec,
477 part.size_sec * self.sector_size)
478
479 # Once all the partitions have been layed out, we can calculate the
480 # minumim disk size
481 self.min_size = self.offset
482 if self.ptable_format == "gpt":
483 self.min_size += GPT_OVERHEAD
484
485 self.min_size *= self.sector_size
486
487 def _create_partition(self, device, parttype, fstype, start, size):
488 """ Create a partition on an image described by the 'device' object. """
489
490 # Start is included to the size so we need to substract one from the end.
491 end = start + size - 1
492 logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors",
493 parttype, start, end, size)
494
495 cmd = "parted -s %s unit s mkpart %s" % (device, parttype)
496 if fstype:
497 cmd += " %s" % fstype
498 cmd += " %d %d" % (start, end)
499
500 return exec_native_cmd(cmd, self.native_sysroot)
501
502 def create(self):
503 logger.debug("Creating sparse file %s", self.path)
504 with open(self.path, 'w') as sparse:
505 os.ftruncate(sparse.fileno(), self.min_size)
506
507 logger.debug("Initializing partition table for %s", self.path)
508 exec_native_cmd("parted -s %s mklabel %s" %
509 (self.path, self.ptable_format), self.native_sysroot)
510
511 logger.debug("Set disk identifier %x", self.identifier)
512 with open(self.path, 'r+b') as img:
513 img.seek(0x1B8)
514 img.write(self.identifier.to_bytes(4, 'little'))
515
516 logger.debug("Creating partitions")
517
518 for part in self.partitions:
519 if part.num == 0:
520 continue
521
Brad Bishop08902b02019-08-20 09:16:51 -0400522 if self.ptable_format == "msdos" and part.num == self.extendedpart:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500523 # Create an extended partition (note: extended
524 # partition is described in MBR and contains all
525 # logical partitions). The logical partitions save a
526 # sector for an EBR just before the start of a
527 # partition. The extended partition must start one
528 # sector before the start of the first logical
529 # partition. This way the first EBR is inside of the
530 # extended partition. Since the extended partitions
531 # starts a sector before the first logical partition,
532 # add a sector at the back, so that there is enough
533 # room for all logical partitions.
534 self._create_partition(self.path, "extended",
Andrew Geissler82c905d2020-04-13 13:39:40 -0500535 None, part.start - 2,
Brad Bishop08902b02019-08-20 09:16:51 -0400536 self.extended_size_sec)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500537
538 if part.fstype == "swap":
539 parted_fs_type = "linux-swap"
540 elif part.fstype == "vfat":
541 parted_fs_type = "fat32"
542 elif part.fstype == "msdos":
543 parted_fs_type = "fat16"
544 if not part.system_id:
545 part.system_id = '0x6' # FAT16
546 else:
547 # Type for ext2/ext3/ext4/btrfs
548 parted_fs_type = "ext2"
549
550 # Boot ROM of OMAP boards require vfat boot partition to have an
551 # even number of sectors.
552 if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \
553 and part.size_sec % 2:
554 logger.debug("Subtracting one sector from '%s' partition to "
555 "get even number of sectors for the partition",
556 part.mountpoint)
557 part.size_sec -= 1
558
559 self._create_partition(self.path, part.type,
560 parted_fs_type, part.start, part.size_sec)
561
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500562 if part.part_name:
563 logger.debug("partition %d: set name to %s",
564 part.num, part.part_name)
565 exec_native_cmd("sgdisk --change-name=%d:%s %s" % \
566 (part.num, part.part_name,
567 self.path), self.native_sysroot)
568
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500569 if part.part_type:
570 logger.debug("partition %d: set type UID to %s",
571 part.num, part.part_type)
572 exec_native_cmd("sgdisk --typecode=%d:%s %s" % \
573 (part.num, part.part_type,
574 self.path), self.native_sysroot)
575
576 if part.uuid and self.ptable_format == "gpt":
577 logger.debug("partition %d: set UUID to %s",
578 part.num, part.uuid)
579 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
580 (part.num, part.uuid, self.path),
581 self.native_sysroot)
582
583 if part.label and self.ptable_format == "gpt":
584 logger.debug("partition %d: set name to %s",
585 part.num, part.label)
586 exec_native_cmd("parted -s %s name %d %s" % \
587 (self.path, part.num, part.label),
588 self.native_sysroot)
589
590 if part.active:
591 flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot"
592 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
593 flag_name, part.num, self.path)
594 exec_native_cmd("parted -s %s set %d %s on" % \
595 (self.path, part.num, flag_name),
596 self.native_sysroot)
597 if part.system_id:
598 exec_native_cmd("sfdisk --part-type %s %s %s" % \
599 (self.path, part.num, part.system_id),
600 self.native_sysroot)
601
602 def cleanup(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500603 pass
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500604
605 def assemble(self):
606 logger.debug("Installing partitions")
607
608 for part in self.partitions:
609 source = part.source_file
610 if source:
611 # install source_file contents into a partition
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500612 sparse_copy(source, self.path, seek=part.start * self.sector_size)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500613
614 logger.debug("Installed %s in partition %d, sectors %d-%d, "
615 "size %d sectors", source, part.num, part.start,
616 part.start + part.size_sec - 1, part.size_sec)
617
618 partimage = self.path + '.p%d' % part.num
Andrew Geisslerc926e172021-05-07 16:11:35 -0500619 bb.utils.rename(source, partimage)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500620 self.partimages.append(partimage)