diff --git a/dracut/modules.d/59kiwi-lib/kiwi-partitions-lib.sh b/dracut/modules.d/59kiwi-lib/kiwi-partitions-lib.sh index f8cb9d5f743..e608721f057 100644 --- a/dracut/modules.d/59kiwi-lib/kiwi-partitions-lib.sh +++ b/dracut/modules.d/59kiwi-lib/kiwi-partitions-lib.sh @@ -90,7 +90,7 @@ function create_msdos_partitions { function create_gpt_partitions { # """ - # create partitions using sgdisk (gpt table) + # create partitions using sfdisk (gpt table) # """ local disk_device=$1 local partition_setup=$2 @@ -103,7 +103,7 @@ function create_gpt_partitions { # put partition setup in a command list(cmd_list) for cmd in ${partition_setup};do - # default values in sgdisk are used if 0 is provided + # default values in sfdisk are used if 0 is provided cmd=$(echo "${cmd}" | tr . 0) cmd_list[$index]=${cmd} index=$((index + 1)) @@ -117,7 +117,7 @@ function create_gpt_partitions { # delete a partition... partid=${cmd_list[$index + 1]} set_device_lock "${disk_device}" \ - sgdisk --delete "${partid}" "${disk_device}" + sfdisk --force --delete "${disk_device}" "${partid}" ;; "n") # create a partition... @@ -125,17 +125,27 @@ function create_gpt_partitions { partid=${cmd_list[$index + 2]} part_size_start=${cmd_list[$index + 3]} part_size_end=${cmd_list[$index + 4]} + if [ "${part_size_end}" = "0" ];then + part_size=+ + else + part_size=$((part_size_end - part_size_start + 1)) + fi + if [ "${part_size_start}" = "0" ];then + printf 'size=%s, name="%s"\n' \ + "${part_size}" "${part_name}" > /tmp/sfdisk.in + else + printf 'start=%s, size=%s, name="%s"\n' \ + "${part_size_start}" "${part_size}" "${part_name}" > /tmp/sfdisk.in + fi set_device_lock "${disk_device}" \ - sgdisk --new "${partid}:${part_size_start}:${part_size_end}" "${disk_device}" - set_device_lock "${disk_device}" \ - sgdisk --change-name "${partid}:${part_name}" "${disk_device}" + sfdisk --force -N "${partid}" "${disk_device}" < /tmp/sfdisk.in ;; "t") # change a partition type... part_type=${cmd_list[$index + 2]} partid=${cmd_list[$index + 1]} set_device_lock "${disk_device}" \ - sgdisk --typecode "${partid}:$(_to_guid "${part_type}")" "${disk_device}" + sfdisk --part-type "${disk_device}" "${partid}" "$(_to_guid "${part_type}")" ;; esac index=$((index + 1)) @@ -417,15 +427,29 @@ function activate_boot_partition { function create_hybrid_gpt { local disk_device=$1 local partition_count + local partition_id + local partition_start + local partition_size + local partition_type udev_pending partition_count=$(lsblk -r -o NAME,TYPE "${disk_device}" | grep -c part) if [ "${partition_count}" -gt 3 ]; then # The max number of partitions to embed is 3 - # see man sgdisk for details + # see man sfdisk for details partition_count=3 fi + : > /tmp/sfdisk.in + for partition_id in $(seq 1 "${partition_count}");do + partition_start=$(_get_gpt_partition_start_sector "${disk_device}" "${partition_id}") + partition_size=$(_get_gpt_partition_size "${disk_device}" "${partition_id}") + partition_type=$(_get_gpt_partition_mbr_type "${disk_device}" "${partition_id}") + printf '%s : start=%s, size=%s, type=%s\n' \ + "$(get_partition_node_name "${disk_device}" "${partition_id}")" \ + "${partition_start}" "${partition_size}" "${partition_type}" \ + >> /tmp/sfdisk.in + done if ! set_device_lock "${disk_device}" \ - sgdisk -h "$(seq -s : 1 "${partition_count}")" "${disk_device}" + sfdisk --label-nested=mbr "${disk_device}" < /tmp/sfdisk.in then die "Failed to create hybrid GPT/MBR !" fi @@ -515,6 +539,38 @@ function _get_msdos_partition_start_sector { grep "${partid} :" | cut -f1 -d, | cut -f2 -d= | tr -d " " } +function _get_gpt_partition_start_sector { + local disk_device=$1 + local partid=$2 + sfdisk --dump "${disk_device}" |\ + grep "${partid} :" | cut -f1 -d, | cut -f2 -d= | tr -d " " +} + +function _get_gpt_partition_size { + local disk_device=$1 + local partid=$2 + sfdisk --dump "${disk_device}" |\ + grep "${partid} :" | cut -f2 -d, | cut -f2 -d= | tr -d " " +} + +function _get_gpt_partition_mbr_type { + local disk_device=$1 + local partid=$2 + local part_type + part_type=$(sfdisk --part-type "${disk_device}" "${partid}") + if [ "${part_type}" = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" ];then + echo 82 + elif [ "${part_type}" = "E6D6D379-F507-44C2-A23C-238F2A3DF928" ];then + echo 8e + elif [ "${part_type}" = "A19D880F-05FC-4D3B-A006-743F0F84911E" ];then + echo fd + elif [ "${part_type}" = "9E1A2D38-C612-4316-AA26-8B49521E5A8B" ];then + echo 41 + else + echo 83 + fi +} + function _to_guid { # """ # convert two digit partition id to guid id diff --git a/dracut/modules.d/59kiwi-lib/module-setup.sh b/dracut/modules.d/59kiwi-lib/module-setup.sh index 8a3b890cf81..880c7ee46b3 100755 --- a/dracut/modules.d/59kiwi-lib/module-setup.sh +++ b/dracut/modules.d/59kiwi-lib/module-setup.sh @@ -17,7 +17,7 @@ install() { inst_multiple \ blkid blockdev dd mkdir rmdir env \ grep cut tail head tr bc true false mountpoint \ - basename sfdisk sgdisk mkswap readlink lsblk \ + basename sfdisk mkswap readlink lsblk \ btrfs xfs_growfs resize2fs \ e2fsck btrfsck xfs_repair \ vgs vgchange lvextend lvcreate lvresize pvresize \ diff --git a/kiwi/bootloader/config/systemd_boot.py b/kiwi/bootloader/config/systemd_boot.py index d0d6df83619..507c275e33c 100644 --- a/kiwi/bootloader/config/systemd_boot.py +++ b/kiwi/bootloader/config/systemd_boot.py @@ -18,6 +18,7 @@ import os import glob import logging +import shlex from string import Template from contextlib import ExitStack from typing import Dict @@ -137,7 +138,17 @@ def _create_embedded_fat_efi_image(self, path: str): ['qemu-img', 'create', path, f'{fat_image_mbsize}M'] ) Command.run( - ['sgdisk', '-n', ':1.0', '-t', '1:EF00', path] + [ + 'bash', '-c', ' '.join( + [ + 'printf', + shlex.quote('label: gpt\nsize=+, type=U\n'), + '|', + 'sfdisk', + shlex.quote(path) + ] + ) + ] ) with LoopDevice(path) as loop_provider: loop_provider.create(overwrite=False) diff --git a/kiwi/partitioner/gpt.py b/kiwi/partitioner/gpt.py index 8590f95da18..f09adc41f3a 100644 --- a/kiwi/partitioner/gpt.py +++ b/kiwi/partitioner/gpt.py @@ -15,7 +15,11 @@ # You should have received a copy of the GNU General Public License # along with kiwi. If not, see # +import json import logging +import re +import shlex +from uuid import UUID from typing import ( List, Optional ) @@ -23,9 +27,11 @@ # project from kiwi.command import Command from kiwi.partitioner.base import PartitionerBase +from kiwi.utils.temporary import Temporary from kiwi.exceptions import ( - KiwiPartitionerGptFlagError + KiwiPartitionerGptFlagError, + KiwiDiskGeometryError ) log = logging.getLogger('kiwi') @@ -39,7 +45,7 @@ def post_init(self) -> None: """ Post initialization method - Setup gdisk partition type/flag map + Setup sfdisk GPT partition type/flag map """ self.flag_map = { 'f.active': None, @@ -52,6 +58,15 @@ def post_init(self) -> None: 't.prep': '4100' } self.partition_map: dict[int, int] = {} + self.type_guid_map = { + 'EF02': '21686148-6449-6E6F-744E-656564454649', + '8300': '0FC63DAF-8483-4772-8E79-3D69D8477DE4', + '8200': '0657FD6D-A4AB-43C4-84E5-0933C84B4F4F', + '8E00': 'E6D6D379-F507-44C2-A23C-238F2A3DF928', + 'FD00': 'A19D880F-05FC-4D3B-A006-743F0F84911E', + 'EF00': 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B', + '4100': '9E1A2D38-C612-4316-AA26-8B49521E5A8B' + } def create( self, name: str, mbsize: int, type_name: str, flags: List[str] = None, @@ -73,24 +88,19 @@ def create( self.partition_count += 1 self.partition_map[self.partition_count] = self.partition_id if mbsize == 'all_free': - partition_end = '0' + partition_size = '+' else: - partition_end = '+' + format(mbsize) + 'M' + partition_size = format(mbsize) + 'MiB' if self.partition_count > 1 or not self.start_sector: - # A start sector value of 0 specifies the default value - # defined in sgdisk + # A start sector value of 0 specifies the default value + # defined in sfdisk self.start_sector = 0 - Command.run( - [ - 'sgdisk', '-n', ':'.join( - [ - format(self.partition_id), - format(self.start_sector), - partition_end - ] - ), '-c', ':'.join([format(self.partition_id), name]), - self.disk_device - ] + partition_fields = [f'size={partition_size}', f'name={json.dumps(name)}'] + if self.start_sector: + partition_fields.insert(0, f'start={self.start_sector}') + self._call_sfdisk( + [', '.join(partition_fields)], + ['--force', '-N', format(self.partition_id)] ) self.set_flag(self.partition_id, type_name) if flags: @@ -111,14 +121,9 @@ def set_flag(self, partition_id: int, flag_name: str) -> None: if self.flag_map[flag_name]: Command.run( [ - 'sgdisk', '-t', - ':'.join( - [ - format(partition_id), - format(self.flag_map[flag_name]) - ] - ), - self.disk_device + 'sfdisk', '--part-type', self.disk_device, + format(partition_id), + self._to_guid(format(self.flag_map[flag_name])) ] ) else: @@ -133,9 +138,8 @@ def set_uuid(self, partition_id: int, uuid: str) -> None: """ Command.run( [ - 'sgdisk', - '--typecode', f'{partition_id}:{uuid}', - self.disk_device + 'sfdisk', '--part-uuid', self.disk_device, + format(partition_id), format(UUID(uuid)) ] ) @@ -147,7 +151,7 @@ def set_hybrid_mbr(self) -> None: partition_number_to_embed = self.partition_count if partition_number_to_embed > 3: # the max number of partitions to embed is 3 - # for details see man sgdisk + # for details see man sfdisk log.debug( 'maximum number of GPT hybrid MBR partitions is 3, got %d', partition_number_to_embed @@ -159,44 +163,123 @@ def set_hybrid_mbr(self) -> None: ) for number in range(1, partition_number_to_embed + 1): if self.partition_map.get(number): - partition_ids.append(format(self.partition_map[number])) - Command.run( - ['sgdisk', '-h', ':'.join(partition_ids), self.disk_device] - ) + partition_id = self.partition_map[number] + start, size = self._get_partition_geometry(partition_id) + partition_type = self._get_partition_type(partition_id) + partition_ids.append( + '{device}{suffix}{number} : start={start}, ' + 'size={size}, type={type}'.format( + device=self.disk_device, + suffix='p' if self.disk_device[-1].isdigit() else '', + number=number, + start=start, + size=size, + type=self._to_mbr_type(partition_type) + ) + ) + self._call_sfdisk(partition_ids, ['--label-nested=mbr']) def set_mbr(self) -> None: """ Turn partition table into MBR (msdos table) """ partition_ids = [] - efi_partition_number = None for number in range(1, self.partition_id + 1): if self.partition_map.get(number): - partition_info = Command.run( - [ - 'sgdisk', '-i={0}'.format(self.partition_map[number]), - self.disk_device - ] + partition_id = self.partition_map[number] + start, size = self._get_partition_geometry(partition_id) + partition_type = self._get_partition_type(partition_id) + partition_ids.append( + '{device}{suffix}{number} : start={start}, ' + 'size={size}, type={type}'.format( + device=self.disk_device, + suffix='p' if self.disk_device[-1].isdigit() else '', + number=number, + start=start, + size=size, + type=self._to_mbr_type(partition_type) + ) ) - if '(EFI System)' in partition_info.output: - efi_partition_number = self.partition_map[number] - partition_ids.append(format(self.partition_map[number])) - if efi_partition_number: - # turn former EFI partition into standard linux partition - self.set_flag(efi_partition_number, 't.linux') - Command.run( - ['sgdisk', '-m', ':'.join(partition_ids), self.disk_device] - ) + self._call_sfdisk(['label: dos'] + partition_ids) def resize_table(self, entries: int = 128) -> None: """ Resize partition table - :param int entries: number of default entries + This is done by a dump/reload which automatically corrects + geometry differences in the table when sfdisk is used + + :param int entries: Specify the maximal number of GPT partitions """ - Command.run( + partition_table = Command.run( + ['sfdisk', '--dump', self.disk_device] + ).output.splitlines() + if entries != 128: + partition_table.insert(0, f'table-length: {entries}') + self._call_sfdisk(partition_table) + + def _call_sfdisk( + self, partition_setup: List[str], options: List[str] = None + ) -> None: + sfdisk_input = Temporary().new_file() + with open(sfdisk_input.name, 'w') as partition: + partition.write('\n'.join(partition_setup) + '\n') + sfdisk_command = ['sfdisk'] + sfdisk_command.extend( + [shlex.quote(option) for option in (options or [])] + ) + sfdisk_command.extend( + [shlex.quote(self.disk_device), '<', shlex.quote(sfdisk_input.name)] + ) + command = ' '.join( + sfdisk_command + ) + log.debug(f'Creating partition layout: {partition_setup}') + Command.run(['bash', '-c', command]) + + def _get_partition_geometry(self, partition_id: int) -> tuple[str, str]: + partition_info = Command.run( + ['sfdisk', '--dump', self.disk_device] + ).output + partition_node = self._get_partition_node(partition_id) + partition_match = re.search( + r'^{partition_node}\s+:\s+start=\s*(\d+), size=\s*(\d+)'.format( + partition_node=re.escape(partition_node) + ), + partition_info, + re.MULTILINE + ) + if not partition_match: + raise KiwiDiskGeometryError( + f'Failed to locate partition {partition_id}' + ) + return partition_match.group(1), partition_match.group(2) + + def _get_partition_type(self, partition_id: int) -> str: + return Command.run( + ['sfdisk', '--part-type', self.disk_device, format(partition_id)] + ).output.strip() + + def _to_guid(self, partition_type: str) -> str: + return self.type_guid_map.get(partition_type, partition_type) + + def _to_mbr_type(self, partition_type: str) -> str: + partition_type = partition_type.upper() + if partition_type == self.type_guid_map['8200']: + return '82' + if partition_type == self.type_guid_map['8E00']: + return '8e' + if partition_type == self.type_guid_map['FD00']: + return 'fd' + if partition_type == self.type_guid_map['4100']: + return '41' + return '83' + + def _get_partition_node(self, partition_id: int) -> str: + return ''.join( [ - 'sgdisk', '--resize-table', '{0}'.format(entries), - self.disk_device + self.disk_device, + 'p' if self.disk_device[-1].isdigit() else '', + format(partition_id) ] ) diff --git a/kiwi/storage/disk.py b/kiwi/storage/disk.py index b6ddb4cac63..68b1fb83c2e 100644 --- a/kiwi/storage/disk.py +++ b/kiwi/storage/disk.py @@ -17,6 +17,7 @@ # import os import logging +import shlex from collections import OrderedDict from typing import ( Dict, NamedTuple, Tuple, Optional @@ -554,9 +555,19 @@ def wipe(self): log.debug('potential fdasd errors were ignored') else: log.debug('Initialize %s disk', self.table_type) + sfdisk_input = Temporary().new_file() + label_type = 'dos' if self.table_type == 'msdos' else self.table_type + with open(sfdisk_input.name, 'w') as partition: + partition.write(f'label: {label_type}\n') Command.run( [ - 'sgdisk', '--zap-all', self.storage_provider.get_device() + 'bash', '-c', ' '.join( + [ + 'sfdisk', '--wipe', 'always', + shlex.quote(self.storage_provider.get_device()), + '<', shlex.quote(sfdisk_input.name) + ] + ) ] ) diff --git a/package/python-kiwi-spec-template b/package/python-kiwi-spec-template index fe065299e9f..f5807f8b2af 100644 --- a/package/python-kiwi-spec-template +++ b/package/python-kiwi-spec-template @@ -359,11 +359,6 @@ Requires: kiwi-systemdeps-iso-media = %{version}-%{release} Requires: binutils Requires: glibc-gconv-modules-extra %endif -%if 0%{?suse_version} -Requires: gptfdisk -%else -Requires: gdisk -%endif Requires: lvm2 Requires: kpartx Requires: cryptsetup @@ -512,15 +507,12 @@ Requires: btrfs-progs %else Recommends: btrfs-progs %endif -Requires: gdisk Requires: dracut-network %else %if 0%{?debian} || 0%{?ubuntu} Recommends: btrfs-tools -Requires: gdisk %else Requires: btrfsprogs -Requires: gptfdisk %endif %endif Requires: coreutils diff --git a/test/unit/bootloader/config/systemd_boot_test.py b/test/unit/bootloader/config/systemd_boot_test.py index bed3813bf65..81b0ad566ad 100644 --- a/test/unit/bootloader/config/systemd_boot_test.py +++ b/test/unit/bootloader/config/systemd_boot_test.py @@ -237,7 +237,7 @@ def test_create_embedded_fat_efi_image( ] assert mock_Command_run.call_args_list == [ call(['qemu-img', 'create', 'ESP', '20M']), - call(['sgdisk', '-n', ':1.0', '-t', '1:EF00', 'ESP']), + call(['bash', '-c', "printf 'label: gpt\nsize=+, type=U\n' | sfdisk ESP"]), call( ['mkdosfs', '-n', 'BOOT', 'efi_device'] ), diff --git a/test/unit/partitioner/gpt_test.py b/test/unit/partitioner/gpt_test.py index a32a380a166..4dd796b5f56 100644 --- a/test/unit/partitioner/gpt_test.py +++ b/test/unit/partitioner/gpt_test.py @@ -1,14 +1,18 @@ +import io import logging from unittest.mock import ( - patch, call, Mock + patch, call, Mock, MagicMock ) from pytest import ( raises, fixture ) from kiwi.partitioner.gpt import PartitionerGpt - -from kiwi.exceptions import KiwiPartitionerGptFlagError +from kiwi.command import CommandT +from kiwi.exceptions import ( + KiwiPartitionerGptFlagError, + KiwiDiskGeometryError +) class TestPartitionerGpt: @@ -26,12 +30,12 @@ def setup(self): def setup_method(self, cls): self.setup() - @patch('kiwi.partitioner.gpt.Command.run') + @patch('kiwi.partitioner.gpt.PartitionerGpt._call_sfdisk') @patch('kiwi.partitioner.gpt.PartitionerGpt.set_flag') - def test_create(self, mock_flag, mock_command): + def test_create(self, mock_flag, mock_call_sfdisk): self.partitioner.create('name', 100, 't.linux', ['t.csm']) - mock_command.assert_called_once_with( - ['sgdisk', '-n', '1:0:+100M', '-c', '1:name', '/dev/loop0'] + mock_call_sfdisk.assert_called_once_with( + ['size=100MiB, name="name"'], ['--force', '-N', '1'] ) call = mock_flag.call_args_list[0] assert mock_flag.call_args_list[0] == \ @@ -40,9 +44,9 @@ def test_create(self, mock_flag, mock_command): assert mock_flag.call_args_list[1] == \ call(1, 't.csm') - @patch('kiwi.partitioner.gpt.Command.run') + @patch('kiwi.partitioner.gpt.PartitionerGpt._call_sfdisk') @patch('kiwi.partitioner.gpt.PartitionerGpt.set_flag') - def test_create_custom_start_sector(self, mock_flag, mock_command): + def test_create_custom_start_sector(self, mock_flag, mock_call_sfdisk): disk_provider = Mock() disk_provider.get_device = Mock( return_value='/dev/loop0' @@ -50,25 +54,27 @@ def test_create_custom_start_sector(self, mock_flag, mock_command): partitioner = PartitionerGpt(disk_provider, 4096) partitioner.create('name', 100, 't.linux', ['t.csm']) partitioner.create('name', 100, 't.linux', ['t.csm']) - mock_command.assert_has_calls([ - call([ - 'sgdisk', '-n', '1:4096:+100M', '-c', '1:name', '/dev/loop0' - ]), - call([ - 'sgdisk', '-n', '2:0:+100M', '-c', '2:name', '/dev/loop0' - ]) + mock_call_sfdisk.assert_has_calls([ + call( + ['start=4096, size=100MiB, name="name"'], + ['--force', '-N', '1'] + ), + call( + ['size=100MiB, name="name"'], + ['--force', '-N', '2'] + ) ]) assert mock_flag.call_args_list[0] == \ call(1, 't.linux') assert mock_flag.call_args_list[1] == \ call(1, 't.csm') - @patch('kiwi.partitioner.gpt.Command.run') + @patch('kiwi.partitioner.gpt.PartitionerGpt._call_sfdisk') @patch('kiwi.partitioner.gpt.PartitionerGpt.set_flag') - def test_create_all_free(self, mock_flag, mock_command): + def test_create_all_free(self, mock_flag, mock_call_sfdisk): self.partitioner.create('name', 'all_free', 't.linux') - mock_command.assert_called_once_with( - ['sgdisk', '-n', '1:0:0', '-c', '1:name', '/dev/loop0'] + mock_call_sfdisk.assert_called_once_with( + ['size=+, name="name"'], ['--force', '-N', '1'] ) def test_set_flag_invalid(self): @@ -79,15 +85,22 @@ def test_set_flag_invalid(self): def test_set_flag(self, mock_command): self.partitioner.set_flag(1, 't.csm') mock_command.assert_called_once_with( - ['sgdisk', '-t', '1:EF02', '/dev/loop0'] + [ + 'sfdisk', '--part-type', '/dev/loop0', '1', + '21686148-6449-6E6F-744E-656564454649' + ] ) def test_set_flag_ignored(self): with self._caplog.at_level(logging.WARNING): self.partitioner.set_flag(1, 'f.active') - @patch('kiwi.partitioner.gpt.Command.run') - def test_set_hybrid_mbr(self, mock_command): + @patch('kiwi.partitioner.gpt.PartitionerGpt._call_sfdisk') + @patch('kiwi.partitioner.gpt.PartitionerGpt._get_partition_type') + @patch('kiwi.partitioner.gpt.PartitionerGpt._get_partition_geometry') + def test_set_hybrid_mbr_max_partitions_exceeded( + self, mock_geometry, mock_type, mock_call_sfdisk + ): self.partitioner.partition_id = 5 self.partitioner.partition_count = 5 self.partitioner.partition_map = { @@ -97,15 +110,89 @@ def test_set_hybrid_mbr(self, mock_command): 4: 4, 5: 5 } + + def type_call(args): + if args == 1: + return 'A19D880F-05FC-4D3B-A006-743F0F84911E' + else: + return '9E1A2D38-C612-4316-AA26-8B49521E5A8B' + + mock_type.side_effect = type_call + + mock_geometry.side_effect = [('2048', '4096')] * 3 self.partitioner.set_hybrid_mbr() - mock_command.assert_called_once_with( - ['sgdisk', '-h', '1:2:3', '/dev/loop0'] + assert len(mock_type.call_args_list) == 3 + + @patch('kiwi.partitioner.gpt.Command.run') + def test_set_hybrid_mbr_geometry_error(self, mock_Command_run): + self.partitioner.partition_id = 1 + self.partitioner.partition_count = 1 + self.partitioner.partition_map = { + 1: 1, + } + mock_Command_run.return_value = CommandT( + output='bogus', + error='', + returncode=0 ) + with raises(KiwiDiskGeometryError): + self.partitioner.set_hybrid_mbr() @patch('kiwi.partitioner.gpt.Command.run') - def test_set_mbr(self, mock_command): - command_output = Mock() - command_output.output = '...(EFI System)' + def test_set_hybrid_mbr(self, mock_Command_run): + self.partitioner.partition_id = 1 + self.partitioner.partition_count = 1 + self.partitioner.partition_map = { + 1: 1, + } + + def command_call(args): + print(args) + if '--dump' in args: + return CommandT( + output='\n'.join( + [ + 'label: gpt', + 'label-id: ID', + 'device: /dev/loop0', + 'unit: sectors', + 'first-lba: 2048', + 'last-lba: 4096', + 'sector-size: 512', + '', + '/dev/loop0p1 : start=2048, size=1024, type=TYPE' + ] + ), + error='', + returncode=0 + ) + elif '--part-type' in args: + return CommandT( + output='0FC63DAF-8483-4772-8E79-3D69D8477DE4\n', + error='', + returncode=0 + ) + else: + return CommandT( + output='', + error='', + returncode=0 + ) + + mock_Command_run.side_effect = command_call + + with patch('builtins.open', create=True) as mock_open: + mock_open.return_value = MagicMock(spec=io.IOBase) + file_handle = mock_open.return_value.__enter__.return_value + self.partitioner.set_hybrid_mbr() + file_handle.write.assert_called_once_with( + '/dev/loop0p1 : start=2048, size=1024, type=83\n' + ) + + @patch('kiwi.partitioner.gpt.PartitionerGpt._call_sfdisk') + @patch('kiwi.partitioner.gpt.PartitionerGpt._get_partition_type') + @patch('kiwi.partitioner.gpt.PartitionerGpt._get_partition_geometry') + def test_set_mbr(self, mock_geometry, mock_type, mock_call_sfdisk): self.partitioner.partition_id = 4 self.partitioner.partition_count = 4 self.partitioner.partition_map = { @@ -114,27 +201,70 @@ def test_set_mbr(self, mock_command): 3: 3, 4: 4 } - mock_command.return_value = command_output - self.partitioner.set_mbr() - assert mock_command.call_args_list == [ - call(['sgdisk', '-i=1', '/dev/loop0']), - call(['sgdisk', '-i=2', '/dev/loop0']), - call(['sgdisk', '-i=3', '/dev/loop0']), - call(['sgdisk', '-i=4', '/dev/loop0']), - call(['sgdisk', '-t', '4:8300', '/dev/loop0']), - call(['sgdisk', '-m', '1:2:3:4', '/dev/loop0']) + mock_geometry.side_effect = [ + ('2048', '4096'), + ('6144', '4096'), + ('10240', '4096'), + ('14336', '4096') + ] + mock_type.side_effect = [ + '0FC63DAF-8483-4772-8E79-3D69D8477DE4', + '0657FD6D-A4AB-43C4-84E5-0933C84B4F4F', + 'E6D6D379-F507-44C2-A23C-238F2A3DF928', + 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' ] + self.partitioner.set_mbr() + mock_call_sfdisk.assert_called_once_with( + [ + 'label: dos', + '/dev/loop0p1 : start=2048, size=4096, type=83', + '/dev/loop0p2 : start=6144, size=4096, type=82', + '/dev/loop0p3 : start=10240, size=4096, type=8e', + '/dev/loop0p4 : start=14336, size=4096, type=83' + ] + ) + @patch('kiwi.partitioner.gpt.PartitionerGpt._call_sfdisk') @patch('kiwi.partitioner.gpt.Command.run') - def test_resize_table(self, mock_command): + def test_resize_table(self, mock_command, mock_call_sfdisk): + command_output = Mock() + command_output.output = '\n'.join([ + 'label: gpt', + 'label-id: ID', + 'device: /dev/loop0', + 'unit: sectors', + 'first-lba: 2048', + 'last-lba: 4096', + 'sector-size: 512', + '', + '/dev/loop0p1 : start=2048, size=1024, type=TYPE' + ]) + mock_command.return_value = command_output self.partitioner.resize_table(42) mock_command.assert_called_once_with( - ['sgdisk', '--resize-table', '42', '/dev/loop0'] + ['sfdisk', '--dump', '/dev/loop0'] + ) + mock_call_sfdisk.assert_called_once_with( + [ + 'table-length: 42', + 'label: gpt', + 'label-id: ID', + 'device: /dev/loop0', + 'unit: sectors', + 'first-lba: 2048', + 'last-lba: 4096', + 'sector-size: 512', + '', + '/dev/loop0p1 : start=2048, size=1024, type=TYPE' + ] ) @patch('kiwi.partitioner.gpt.Command.run') def test_set_uuid(self, mock_Command_run): - self.partitioner.set_uuid(42, 'ID') + self.partitioner.set_uuid(42, 'c12a7328f81f11d2ba4b00a0c93ec93b') mock_Command_run.assert_called_once_with( - ['sgdisk', '--typecode', '42:ID', '/dev/loop0'] + [ + 'sfdisk', '--part-uuid', '/dev/loop0', + '42', 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b' + ] ) diff --git a/test/unit/storage/disk_test.py b/test/unit/storage/disk_test.py index 863e0928825..827eb5818a7 100644 --- a/test/unit/storage/disk_test.py +++ b/test/unit/storage/disk_test.py @@ -374,11 +374,15 @@ def test_activate_boot_partition_is_prep_partition(self, mock_command): self.disk.activate_boot_partition() self.partitioner.set_flag(1, 'f.active') + @patch('kiwi.storage.disk.Temporary.new_file') @patch('kiwi.storage.disk.Command.run') - def test_wipe_gpt(self, mock_command): - self.disk.wipe() + def test_wipe_gpt(self, mock_command, mock_temp): + mock_temp.return_value = self.tempfile + m_open = mock_open() + with patch('builtins.open', m_open, create=True): + self.disk.wipe() mock_command.assert_called_once_with( - ['sgdisk', '--zap-all', '/dev/loop0'] + ['bash', '-c', 'sfdisk --wipe always /dev/loop0 < tempfile'] ) @patch('kiwi.storage.disk.Command.run')