diff options
| author | Ed Bartosh <ed.bartosh@linux.intel.com> | 2017-08-25 23:12:27 +0300 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-08-27 22:30:07 +0100 |
| commit | cdef76e42414c092667761f8d9476e0d29828e75 (patch) | |
| tree | d8ce5b0f0ab12fde54b5373c217a54d7ea63bfb3 /scripts/lib/wic/engine.py | |
| parent | cee58f1d411e3321182f18bf2230aa5882178b1f (diff) | |
| download | poky-cdef76e42414c092667761f8d9476e0d29828e75.tar.gz | |
wic: implement 'wic write' command
This command writes image to the media or another file with
the possibility to expand partitions to fill free target space.
[YOCTO #11278]
(From OE-Core rev: ac5fc0d691aad66ac01a5cde34c331c928e9e25a)
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/engine.py')
| -rw-r--r-- | scripts/lib/wic/engine.py | 146 |
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 @@ | |||
| 31 | import logging | 31 | import logging |
| 32 | import os | 32 | import os |
| 33 | import tempfile | 33 | import tempfile |
| 34 | import json | ||
| 35 | import subprocess | ||
| 34 | 36 | ||
| 35 | from collections import namedtuple, OrderedDict | 37 | from collections import namedtuple, OrderedDict |
| 36 | from distutils.spawn import find_executable | 38 | from 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 | |||
| 343 | def wic_ls(args, native_sysroot): | 482 | def 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 | ||
| 512 | def 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 | |||
| 373 | def find_canned(scripts_path, file_name): | 519 | def 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. |
