summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/engine.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/engine.py')
-rw-r--r--scripts/lib/wic/engine.py146
1 files changed, 146 insertions, 0 deletions
diff --git a/scripts/lib/wic/engine.py b/scripts/lib/wic/engine.py
index 4ffb08d1c8..303f323b83 100644
--- a/scripts/lib/wic/engine.py
+++ b/scripts/lib/wic/engine.py
@@ -31,6 +31,8 @@
31import logging 31import logging
32import os 32import os
33import tempfile 33import tempfile
34import json
35import subprocess
34 36
35from collections import namedtuple, OrderedDict 37from collections import namedtuple, OrderedDict
36from distutils.spawn import find_executable 38from distutils.spawn import find_executable
@@ -340,6 +342,143 @@ class Disk:
340 raise err 342 raise err
341 self._put_part_image(pnum) 343 self._put_part_image(pnum)
342 344
345 def write(self, target, expand):
346 """Write disk image to the media or file."""
347 def write_sfdisk_script(outf, parts):
348 for key, val in parts['partitiontable'].items():
349 if key in ("partitions", "device", "firstlba", "lastlba"):
350 continue
351 if key == "id":
352 key = "label-id"
353 outf.write("{}: {}\n".format(key, val))
354 outf.write("\n")
355 for part in parts['partitiontable']['partitions']:
356 line = ''
357 for name in ('attrs', 'name', 'size', 'type', 'uuid'):
358 if name == 'size' and part['type'] == 'f':
359 # don't write size for extended partition
360 continue
361 val = part.get(name)
362 if val:
363 line += '{}={}, '.format(name, val)
364 if line:
365 line = line[:-2] # strip ', '
366 if part.get('bootable'):
367 line += ' ,bootable'
368 outf.write("{}\n".format(line))
369 outf.flush()
370
371 def read_ptable(path):
372 out = exec_cmd("{} -dJ {}".format(self.sfdisk, path))
373 return json.loads(out)
374
375 def write_ptable(parts, target):
376 with tempfile.NamedTemporaryFile(prefix="wic-sfdisk-", mode='w') as outf:
377 write_sfdisk_script(outf, parts)
378 cmd = "{} --no-reread {} < {} 2>/dev/null".format(self.sfdisk, target, outf.name)
379 try:
380 subprocess.check_output(cmd, shell=True)
381 except subprocess.CalledProcessError as err:
382 raise WicError("Can't run '{}' command: {}".format(cmd, err))
383
384 if expand is None:
385 sparse_copy(self.imagepath, target)
386 else:
387 # copy first sectors that may contain bootloader
388 sparse_copy(self.imagepath, target, length=2048 * self._lsector_size)
389
390 # copy source partition table to the target
391 parts = read_ptable(self.imagepath)
392 write_ptable(parts, target)
393
394 # get size of unpartitioned space
395 free = None
396 for line in exec_cmd("{} -F {}".format(self.sfdisk, target)).splitlines():
397 if line.startswith("Unpartitioned space ") and line.endswith("sectors"):
398 free = int(line.split()[-2])
399 if free is None:
400 raise WicError("Can't get size of unpartitioned space")
401
402 # calculate expanded partitions sizes
403 sizes = {}
404 for num, part in enumerate(parts['partitiontable']['partitions'], 1):
405 if num in expand:
406 if expand[num] != 0: # don't resize partition if size is set to 0
407 sectors = expand[num] // self._lsector_size
408 free -= sectors - part['size']
409 part['size'] = sectors
410 sizes[num] = sectors
411 elif part['type'] != 'f':
412 sizes[num] = -1
413
414 for num, part in enumerate(parts['partitiontable']['partitions'], 1):
415 if sizes.get(num) == -1:
416 part['size'] += free // len(sizes)
417
418 # write resized partition table to the target
419 write_ptable(parts, target)
420
421 # read resized partition table
422 parts = read_ptable(target)
423
424 # copy partitions content
425 for num, part in enumerate(parts['partitiontable']['partitions'], 1):
426 pnum = str(num)
427 fstype = self.partitions[pnum].fstype
428
429 # copy unchanged partition
430 if part['size'] == self.partitions[pnum].size // self._lsector_size:
431 logger.info("copying unchanged partition {}".format(pnum))
432 sparse_copy(self._get_part_image(pnum), target, seek=part['start'] * self._lsector_size)
433 continue
434
435 # resize or re-create partitions
436 if fstype.startswith('ext') or fstype.startswith('fat') or \
437 fstype.startswith('linux-swap'):
438
439 partfname = None
440 with tempfile.NamedTemporaryFile(prefix="wic-part{}-".format(pnum)) as partf:
441 partfname = partf.name
442
443 if fstype.startswith('ext'):
444 logger.info("resizing ext partition {}".format(pnum))
445 partimg = self._get_part_image(pnum)
446 sparse_copy(partimg, partfname)
447 exec_cmd("{} -pf {}".format(self.e2fsck, partfname))
448 exec_cmd("{} {} {}s".format(\
449 self.resize2fs, partfname, part['size']))
450 elif fstype.startswith('fat'):
451 logger.info("copying content of the fat partition {}".format(pnum))
452 with tempfile.TemporaryDirectory(prefix='wic-fatdir-') as tmpdir:
453 # copy content to the temporary directory
454 cmd = "{} -snompi {} :: {}".format(self.mcopy,
455 self._get_part_image(pnum),
456 tmpdir)
457 exec_cmd(cmd)
458 # create new msdos partition
459 label = part.get("name")
460 label_str = "-n {}".format(label) if label else ''
461
462 cmd = "{} {} -C {} {}".format(self.mkdosfs, label_str, partfname,
463 part['size'])
464 exec_cmd(cmd)
465 # copy content from the temporary directory to the new partition
466 cmd = "{} -snompi {} {}/* ::".format(self.mcopy, partfname, tmpdir)
467 exec_cmd(cmd, as_shell=True)
468 elif fstype.startswith('linux-swap'):
469 logger.info("creating swap partition {}".format(pnum))
470 label = part.get("name")
471 label_str = "-L {}".format(label) if label else ''
472 uuid = part.get("uuid")
473 uuid_str = "-U {}".format(uuid) if uuid else ''
474 with open(partfname, 'w') as sparse:
475 os.ftruncate(sparse.fileno(), part['size'] * self._lsector_size)
476 exec_cmd("{} {} {} {}".format(self.mkswap, label_str, uuid_str, partfname))
477 sparse_copy(partfname, target, seek=part['start'] * self._lsector_size)
478 os.unlink(partfname)
479 elif part['type'] != 'f':
480 logger.warn("skipping partition {}: unsupported fstype {}".format(pnum, fstype))
481
343def wic_ls(args, native_sysroot): 482def wic_ls(args, native_sysroot):
344 """List contents of partitioned image or vfat partition.""" 483 """List contents of partitioned image or vfat partition."""
345 disk = Disk(args.path.image, native_sysroot) 484 disk = Disk(args.path.image, native_sysroot)
@@ -370,6 +509,13 @@ def wic_rm(args, native_sysroot):
370 disk = Disk(args.path.image, native_sysroot) 509 disk = Disk(args.path.image, native_sysroot)
371 disk.remove(args.path.part, args.path.path) 510 disk.remove(args.path.part, args.path.path)
372 511
512def wic_write(args, native_sysroot):
513 """
514 Write image to a target device.
515 """
516 disk = Disk(args.image, native_sysroot, ('fat', 'ext', 'swap'))
517 disk.write(args.target, args.expand)
518
373def find_canned(scripts_path, file_name): 519def find_canned(scripts_path, file_name):
374 """ 520 """
375 Find a file either by its path or by name in the canned files dir. 521 Find a file either by its path or by name in the canned files dir.