summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/plugins/imager/direct.py
diff options
context:
space:
mode:
authorEd Bartosh <ed.bartosh@linux.intel.com>2017-02-09 16:28:54 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-02-15 20:06:44 -0800
commitcdcc6e27530f13531b3bda9a15dc926f6ff9a837 (patch)
treea1993d5b949cde736661ffae78d55ac1b485eaa4 /scripts/lib/wic/plugins/imager/direct.py
parentadf5e2956ca4e3136be0ee785115a7e1941a135b (diff)
downloadpoky-cdcc6e27530f13531b3bda9a15dc926f6ff9a837.tar.gz
wic: move PartitionedImage class to direct.py
As PartitionedImage is only used in direct.py it makes sense to move it there. It's easier to maintain (and refactor) it this way. (From OE-Core rev: 2550622371f5c50857e5d58eabab01a1823c6fc3) Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/lib/wic/plugins/imager/direct.py')
-rw-r--r--scripts/lib/wic/plugins/imager/direct.py278
1 files changed, 276 insertions, 2 deletions
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
index fefe88e0df..12044318e8 100644
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -31,13 +31,12 @@ import tempfile
31from time import strftime 31from time import strftime
32 32
33from wic import msger 33from wic import msger
34from wic.filemap import sparse_copy
34from wic.ksparser import KickStart, KickStartError 35from wic.ksparser import KickStart, KickStartError
35from wic.plugin import pluginmgr 36from wic.plugin import pluginmgr
36from wic.pluginbase import ImagerPlugin 37from wic.pluginbase import ImagerPlugin
37from wic.utils.errors import CreatorError, ImageError 38from wic.utils.errors import CreatorError, ImageError
38from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd 39from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd
39from wic.utils.partitionedfs import PartitionedImage
40
41 40
42class DirectPlugin(ImagerPlugin): 41class DirectPlugin(ImagerPlugin):
43 """ 42 """
@@ -316,3 +315,278 @@ class DirectPlugin(ImagerPlugin):
316 315
317 # remove work directory 316 # remove work directory
318 shutil.rmtree(self.workdir, ignore_errors=True) 317 shutil.rmtree(self.workdir, ignore_errors=True)
318
319# Overhead of the MBR partitioning scheme (just one sector)
320MBR_OVERHEAD = 1
321
322# Overhead of the GPT partitioning scheme
323GPT_OVERHEAD = 34
324
325# Size of a sector in bytes
326SECTOR_SIZE = 512
327
328class PartitionedImage():
329 """
330 Partitioned image in a file.
331 """
332
333 def __init__(self, path, ptable_format, native_sysroot=None):
334 self.path = path # Path to the image file
335 self.numpart = 0 # Number of allocated partitions
336 self.realpart = 0 # Number of partitions in the partition table
337 self.offset = 0 # Offset of next partition (in sectors)
338 self.min_size = 0 # Minimum required disk size to fit
339 # all partitions (in bytes)
340 self.ptable_format = ptable_format # Partition table format
341 # Disk system identifier
342 self.identifier = int.from_bytes(os.urandom(4), 'little')
343
344 self.partitions = []
345 self.partimages = []
346 # Size of a sector used in calculations
347 self.sector_size = SECTOR_SIZE
348 self.native_sysroot = native_sysroot
349
350 def add_partition(self, part):
351 """
352 Add the next partition. Partitions have to be added in the
353 first-to-last order.
354 """
355 part.ks_pnum = len(self.partitions)
356
357 # Converting kB to sectors for parted
358 part.size_sec = part.disk_size * 1024 // self.sector_size
359
360 self.partitions.append(part)
361
362 def layout_partitions(self):
363 """ Layout the partitions, meaning calculate the position of every
364 partition on the disk. The 'ptable_format' parameter defines the
365 partition table format and may be "msdos". """
366
367 msger.debug("Assigning %s partitions to disks" % self.ptable_format)
368
369 # Go through partitions in the order they are added in .ks file
370 for num in range(len(self.partitions)):
371 part = self.partitions[num]
372
373 if self.ptable_format == 'msdos' and part.part_type:
374 # The --part-type can also be implemented for MBR partitions,
375 # in which case it would map to the 1-byte "partition type"
376 # filed at offset 3 of the partition entry.
377 raise ImageError("setting custom partition type is not " \
378 "implemented for msdos partitions")
379
380 # Get the disk where the partition is located
381 self.numpart += 1
382 if not part.no_table:
383 self.realpart += 1
384
385 if self.numpart == 1:
386 if self.ptable_format == "msdos":
387 overhead = MBR_OVERHEAD
388 elif self.ptable_format == "gpt":
389 overhead = GPT_OVERHEAD
390
391 # Skip one sector required for the partitioning scheme overhead
392 self.offset += overhead
393
394 if self.realpart > 3:
395 # Reserve a sector for EBR for every logical partition
396 # before alignment is performed.
397 if self.ptable_format == "msdos":
398 self.offset += 1
399
400 if part.align:
401 # If not first partition and we do have alignment set we need
402 # to align the partition.
403 # FIXME: This leaves a empty spaces to the disk. To fill the
404 # gaps we could enlargea the previous partition?
405
406 # Calc how much the alignment is off.
407 align_sectors = self.offset % (part.align * 1024 // self.sector_size)
408
409 if align_sectors:
410 # If partition is not aligned as required, we need
411 # to move forward to the next alignment point
412 align_sectors = (part.align * 1024 // self.sector_size) - align_sectors
413
414 msger.debug("Realignment for %s%s with %s sectors, original"
415 " offset %s, target alignment is %sK." %
416 (part.disk, self.numpart, align_sectors,
417 self.offset, part.align))
418
419 # increase the offset so we actually start the partition on right alignment
420 self.offset += align_sectors
421
422 part.start = self.offset
423 self.offset += part.size_sec
424
425 part.type = 'primary'
426 if not part.no_table:
427 part.num = self.realpart
428 else:
429 part.num = 0
430
431 if self.ptable_format == "msdos":
432 # only count the partitions that are in partition table
433 if len([p for p in self.partitions if not p.no_table]) > 4:
434 if self.realpart > 3:
435 part.type = 'logical'
436 part.num = self.realpart + 1
437
438 msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
439 "sectors (%d bytes)." \
440 % (part.mountpoint, part.disk, part.num,
441 part.start, self.offset - 1,
442 part.size_sec, part.size_sec * self.sector_size))
443
444 # Once all the partitions have been layed out, we can calculate the
445 # minumim disk size
446 self.min_size = self.offset
447 if self.ptable_format == "gpt":
448 self.min_size += GPT_OVERHEAD
449
450 self.min_size *= self.sector_size
451
452 def _create_partition(self, device, parttype, fstype, start, size):
453 """ Create a partition on an image described by the 'device' object. """
454
455 # Start is included to the size so we need to substract one from the end.
456 end = start + size - 1
457 msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" %
458 (parttype, start, end, size))
459
460 cmd = "parted -s %s unit s mkpart %s" % (device, parttype)
461 if fstype:
462 cmd += " %s" % fstype
463 cmd += " %d %d" % (start, end)
464
465 return exec_native_cmd(cmd, self.native_sysroot)
466
467 def create(self):
468 msger.debug("Creating sparse file %s" % self.path)
469 with open(self.path, 'w') as sparse:
470 os.ftruncate(sparse.fileno(), self.min_size)
471
472 msger.debug("Initializing partition table for %s" % self.path)
473 exec_native_cmd("parted -s %s mklabel %s" %
474 (self.path, self.ptable_format), self.native_sysroot)
475
476 msger.debug("Set disk identifier %x" % self.identifier)
477 with open(self.path, 'r+b') as img:
478 img.seek(0x1B8)
479 img.write(self.identifier.to_bytes(4, 'little'))
480
481 msger.debug("Creating partitions")
482
483 for part in self.partitions:
484 if part.num == 0:
485 continue
486
487 if self.ptable_format == "msdos" and part.num == 5:
488 # Create an extended partition (note: extended
489 # partition is described in MBR and contains all
490 # logical partitions). The logical partitions save a
491 # sector for an EBR just before the start of a
492 # partition. The extended partition must start one
493 # sector before the start of the first logical
494 # partition. This way the first EBR is inside of the
495 # extended partition. Since the extended partitions
496 # starts a sector before the first logical partition,
497 # add a sector at the back, so that there is enough
498 # room for all logical partitions.
499 self._create_partition(self.path, "extended",
500 None, part.start - 1,
501 self.offset - part.start + 1)
502
503 if part.fstype == "swap":
504 parted_fs_type = "linux-swap"
505 elif part.fstype == "vfat":
506 parted_fs_type = "fat32"
507 elif part.fstype == "msdos":
508 parted_fs_type = "fat16"
509 elif part.fstype == "ontrackdm6aux3":
510 parted_fs_type = "ontrackdm6aux3"
511 else:
512 # Type for ext2/ext3/ext4/btrfs
513 parted_fs_type = "ext2"
514
515 # Boot ROM of OMAP boards require vfat boot partition to have an
516 # even number of sectors.
517 if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \
518 and part.size_sec % 2:
519 msger.debug("Subtracting one sector from '%s' partition to " \
520 "get even number of sectors for the partition" % \
521 part.mountpoint)
522 part.size_sec -= 1
523
524 self._create_partition(self.path, part.type,
525 parted_fs_type, part.start, part.size_sec)
526
527 if part.part_type:
528 msger.debug("partition %d: set type UID to %s" % \
529 (part.num, part.part_type))
530 exec_native_cmd("sgdisk --typecode=%d:%s %s" % \
531 (part.num, part.part_type,
532 self.path), self.native_sysroot)
533
534 if part.uuid and self.ptable_format == "gpt":
535 msger.debug("partition %d: set UUID to %s" % \
536 (part.num, part.uuid))
537 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
538 (part.num, part.uuid, self.path),
539 self.native_sysroot)
540
541 if part.label and self.ptable_format == "gpt":
542 msger.debug("partition %d: set name to %s" % \
543 (part.num, part.label))
544 exec_native_cmd("parted -s %s name %d %s" % \
545 (self.path, part.num, part.label),
546 self.native_sysroot)
547
548 if part.active:
549 flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot"
550 msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \
551 (flag_name, part.num, self.path))
552 exec_native_cmd("parted -s %s set %d %s on" % \
553 (self.path, part.num, flag_name),
554 self.native_sysroot)
555 if part.system_id:
556 exec_native_cmd("sfdisk --part-type %s %s %s" % \
557 (self.path, part.num, part.system_id),
558 self.native_sysroot)
559
560 # Parted defaults to enabling the lba flag for fat16 partitions,
561 # which causes compatibility issues with some firmware (and really
562 # isn't necessary).
563 if parted_fs_type == "fat16":
564 if self.ptable_format == 'msdos':
565 msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \
566 (part.num, self.path))
567 exec_native_cmd("parted -s %s set %d lba off" % \
568 (self.path, part.num),
569 self.native_sysroot)
570
571 def cleanup(self):
572 # remove partition images
573 for image in self.partimages:
574 os.remove(image)
575
576 def assemble(self):
577 msger.debug("Installing partitions")
578
579 for part in self.partitions:
580 source = part.source_file
581 if source:
582 # install source_file contents into a partition
583 sparse_copy(source, self.path, part.start * self.sector_size)
584
585 msger.debug("Installed %s in partition %d, sectors %d-%d, "
586 "size %d sectors" % \
587 (source, part.num, part.start,
588 part.start + part.size_sec - 1, part.size_sec))
589
590 partimage = self.path + '.p%d' % part.num
591 os.rename(source, partimage)
592 self.partimages.append(partimage)