Skip to content

Commit

Permalink
Added disk validation for duplicate installs
Browse files Browse the repository at this point in the history
Installing the same image to different storage disks on the
same machine creates device conflicts with unexpected side
effects. This commit adds a validation based on the PTUUID
of the disk image to check if another device on the system
has the same ID and if yes, does not allow to install the
image again including a message which device takes the same
identifier. This references bsc#1228741
  • Loading branch information
schaefi committed Jan 20, 2025
1 parent d2799e8 commit 0eb2d27
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 7 deletions.
12 changes: 12 additions & 0 deletions doc/source/concept_and_workflow/customize_the_boot_process.rst
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,18 @@ the available kernel boot parameters for these modules:
for the passphrase if the image has been built with an initial
luks passphrase.

``rd.kiwi.oem.disk.consistency``
For OEM disk images providing an installation image. If set,
the installation image will check against all disks that are
not the selected target disk if there is any disk in the system
that has the same PTUUID compared to the image that is about
to be installed. If such a disk is found this indicates that
the same image was already installed to another storage disk
on the same system which will cause device id inconsistencies
for the entire system. In such a case an error message is
displayed providing information about the conflicting device
and the installation will be cancelled.

``rd.kiwi.oem.maxdisk=size[KMGT]``
Specifies the maximum disk size an unattended OEM installation uses for image
deployment. Unattended OEM deployments default to deploying on `/dev/sda` (or
Expand Down
25 changes: 25 additions & 0 deletions dracut/modules.d/90kiwi-dump/kiwi-dump-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,27 @@ function get_disk_list {
echo "${list_items}"
}

function validate_disk_selection {
local selected=$1
local disk_list
local entry
local count=0
disk_list=$(get_disk_list)
for entry in ${disk_list};do
if [ $((count % 2)) -eq 0 ];then
if [ ! "${entry}" = "${selected}" ];then
# check system_identifier for the not selected disk
# if one of them matches the system_identifier because
# that would produce a global system inconsistency
if disk_matches_system_identifier "${entry}";then
report_and_quit "Disk ${entry} has the same partition UUID"
fi
fi
fi
count=$((count + 1))
done
}

function get_selected_disk {
declare kiwi_oemunattended=${kiwi_oemunattended}
declare kiwi_oemunattended_id=${kiwi_oemunattended_id}
Expand Down Expand Up @@ -423,6 +444,10 @@ udev_pending

image_target=$(get_selected_disk)

if getargbool 0 rd.kiwi.oem.disk.consistency; then
validate_disk_selection "${image_target}"
fi

if getargbool 0 rd.kiwi.install.pxe; then
image_source_files=$(get_remote_image_source_files)
else
Expand Down
20 changes: 20 additions & 0 deletions dracut/modules.d/99kiwi-lib/kiwi-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ function setup_debug {
fi
}

function get_system_id {
local disk_device=$1
blkid -s PTUUID -o value "${disk_device}"
}

function disk_matches_system_identifier {
local disk_device=$1
local system_identifier=/system_identifier
local id_disk
local id_image
if [ -e "${system_identifier}" ];then
id_disk=$(get_system_id "${disk_device}")
read -r id_image < "${system_identifier}"
if [ "${id_disk}" = "${id_image}" ]; then
return 0
fi
fi
return 1
}

function set_root_map {
root_map=$1
export root_map
Expand Down
11 changes: 11 additions & 0 deletions kiwi/builder/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from kiwi.firmware import FirmWare
from kiwi.system.identifier import SystemIdentifier
from kiwi.path import Path
from kiwi.utils.block import BlockID
from kiwi.defaults import Defaults
from kiwi.utils.checksum import Checksum
from kiwi.system.kernel import Kernel
Expand Down Expand Up @@ -456,6 +457,7 @@ def _create_iso_install_kernel_and_initrd(self) -> None:
self.xml_state.get_installmedia_initrd_modules('set')
)
self._add_system_image_boot_options_to_boot_image()
self._add_system_identifier_to_boot_image()
self.boot_image_task.create_initrd(
self.mbrid, 'initrd_kiwi_install',
install_initrd=True
Expand All @@ -467,6 +469,15 @@ def _create_iso_install_kernel_and_initrd(self) -> None:
]
)

def _add_system_identifier_to_boot_image(self) -> None:
filename = ''.join(
[self.boot_image_task.boot_root_directory, '/system_identifier']
)
blockid = BlockID(self.diskname)
with open(filename, 'w') as system_identifier:
system_identifier.write(blockid.get_ptuuid())
self.boot_image_task.include_file(os.sep + os.path.basename(filename))

def _add_system_image_boot_options_to_boot_image(self) -> None:
filename = ''.join(
[self.boot_image_task.boot_root_directory, '/config.bootoptions']
Expand Down
10 changes: 10 additions & 0 deletions kiwi/utils/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ def get_uuid(self):
"""
return self.get_blkid('UUID')

def get_ptuuid(self):
"""
Retrieve partition uuid from block device
:return: uuid of the partition table
:rtype: str
"""
return self.get_blkid('PTUUID')

def get_filesystem(self):
"""
Retrieve filesystem type from block device
Expand Down
21 changes: 14 additions & 7 deletions test/unit/builder/install_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,15 @@ def test_setup_ix86(self):
@patch('kiwi.builder.install.Temporary')
@patch('kiwi.builder.install.Command.run')
@patch('kiwi.builder.install.Defaults.get_grub_boot_directory_name')
@patch('kiwi.builder.install.BlockID.get_ptuuid')
def test_create_install_iso(
self, mock_grub_dir, mock_command, mock_Temporary, mock_copy,
mock_setup_media_loader_directory, mock_create_boot_loader_config,
mock_DeviceProvider, mock_FileSystemSquashFs, mock_FileSystemIsoFs
self, mock_get_ptuuid, mock_grub_dir, mock_command, mock_Temporary,
mock_copy, mock_setup_media_loader_directory,
mock_create_boot_loader_config, mock_DeviceProvider,
mock_FileSystemSquashFs, mock_FileSystemIsoFs
):
mock_get_ptuuid.return_value = 'some_PTUUID'

temp_squashfs = Mock()
temp_squashfs.new_dir.return_value.name = 'temp-squashfs'

Expand Down Expand Up @@ -253,14 +257,17 @@ def side_effect(prefix, path):
['module1', 'module2']
)

self.boot_image_task.include_file.assert_called_once_with(
'/config.bootoptions'
)
assert self.boot_image_task.include_file.call_args_list == [
call('/config.bootoptions'),
call('/system_identifier')
]
assert m_open.call_args_list == [
call('temp_media_dir/config.isoclient', 'w'),
call('initrd_dir/system_identifier', 'w')
]
assert m_open.return_value.write.call_args_list == [
call('IMAGE="result-image.raw"\n')
call('IMAGE="result-image.raw"\n'),
call('some_PTUUID')
]

@patch('kiwi.builder.disk.create_boot_loader_config')
Expand Down
5 changes: 5 additions & 0 deletions test/unit/utils/block_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def test_get_uuid(self, mock_get_blkid):
self.blkid.get_uuid()
mock_get_blkid.assert_called_once_with('UUID')

@patch('kiwi.utils.block.BlockID.get_blkid')
def test_get_ptuuid(self, mock_get_blkid):
self.blkid.get_ptuuid()
mock_get_blkid.assert_called_once_with('PTUUID')

@patch('kiwi.utils.block.Command.run')
def test_get_partition_count(self, mock_Command_run):
lsblk_call = Mock()
Expand Down

0 comments on commit 0eb2d27

Please sign in to comment.