diff --git a/kiwi/system/root_bind.py b/kiwi/system/root_bind.py index 1883041e..0b3b4f85 100644 --- a/kiwi/system/root_bind.py +++ b/kiwi/system/root_bind.py @@ -17,6 +17,7 @@ # import os import shutil +from textwrap import dedent # project from kiwi.command import Command @@ -24,6 +25,7 @@ from kiwi.defaults import Defaults from kiwi.logger import log from kiwi.path import Path from kiwi.mount_manager import MountManager +from kiwi.utils.checksum import Checksum from kiwi.exceptions import ( KiwiMountKernelFileSystemsError, @@ -150,6 +152,9 @@ class RootBind(object): Command.run( ['ln', '-s', '-f', link_target, self.root_dir + config] ) + checksum = Checksum(config) + with open(self.root_dir + config + '.sha', 'w') as shasum: + shasum.write(checksum.sha256()) except Exception as e: self.cleanup() raise KiwiSetupIntermediateConfigError( @@ -189,7 +194,35 @@ class RootBind(object): config_files_to_delete = [] for config in self.cleanup_files: - config_files_to_delete.append(self.root_dir + config) + config_file = self.root_dir + config + shasum_file = config_file.replace('.kiwi', '.sha') + + config_files_to_delete.append(config_file) + config_files_to_delete.append(shasum_file) + + checksum = Checksum(config_file) + if not checksum.matches(checksum.sha256(), shasum_file): + message = dedent('''\n + Modifications to intermediate config file detected + + The file: {0} + + is a copy from the host system and symlinked + to its origin in the image root during build time. + Modifications to this file by e.g script code will not + have any effect because the file gets restored by one + of the following actions: + + 1. A package during installation provides it + 2. A custom version of the file is setup as overlay file + 3. The file is not provided by install or overlay and will + be deleted at the end of the build process + + If you need a custom version of that file please + provide it as an overlay file in your image + description + ''') + log.warning(message.format(config_file)) del self.cleanup_files[:] diff --git a/test/unit/system_root_bind_test.py b/test/unit/system_root_bind_test.py index a00ba2f0..a1997283 100644 --- a/test/unit/system_root_bind_test.py +++ b/test/unit/system_root_bind_test.py @@ -2,7 +2,10 @@ from mock import patch from mock import call import mock -from .test_helper import raises +from .test_helper import ( + raises, + patch_open +) from kiwi.exceptions import ( KiwiMountKernelFileSystemsError, @@ -98,8 +101,14 @@ class TestRootBind(object): shared_mount.bind_mount.assert_called_once_with() @patch('kiwi.command.Command.run') + @patch('kiwi.system.root_bind.Checksum') @patch('os.path.exists') - def test_intermediate_config(self, mock_exists, mock_command): + @patch_open + def test_intermediate_config( + self, mock_open, mock_exists, mock_Checksum, mock_command + ): + checksum = mock.Mock() + mock_Checksum.return_value = checksum mock_exists.return_value = True self.bind_root.setup_intermediate_config() assert mock_command.call_args_list == [ @@ -110,17 +119,27 @@ class TestRootBind(object): 'ln', '-s', '-f', 'foo.kiwi', 'root-dir/foo' ]) ] + checksum.sha256.assert_called_once_with() + mock_open.assert_called_once_with( + 'root-dir/foo.sha', 'w' + ) + @patch('kiwi.system.root_bind.Checksum') @patch('kiwi.system.root_bind.MountManager.is_mounted') @patch('kiwi.system.root_bind.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') @patch('os.path.islink') @patch('os.path.exists') @patch('shutil.move') + @patch('kiwi.logger.log.warning') def test_cleanup( - self, mock_move, mock_exists, mock_islink, mock_remove_hierarchy, - mock_command, mock_is_mounted + self, mock_log_warn, mock_move, mock_exists, mock_islink, + mock_remove_hierarchy, mock_command, mock_is_mounted, + mock_Checksum ): + checksum = mock.Mock() + checksum.matches.return_value = False + mock_Checksum.return_value = checksum os_exists_return_values = [False, True] def exists_side_effect(*args): @@ -133,18 +152,24 @@ class TestRootBind(object): self.mount_manager.umount_lazy.assert_called_once_with() mock_remove_hierarchy.assert_called_once_with('root-dir/mountpoint') mock_command.assert_called_once_with( - ['rm', '-f', 'root-dir/foo.kiwi', 'root-dir/foo'] + [ + 'rm', '-f', 'root-dir/foo.kiwi', + 'root-dir/foo.sha', 'root-dir/foo' + ] ) mock_move.assert_called_once_with( 'root-dir/foo.rpmnew', 'root-dir/foo' ) + assert mock_log_warn.called @patch('os.path.islink') @patch('kiwi.logger.log.warning') @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') + @patch('kiwi.system.root_bind.Checksum') def test_cleanup_continue_on_error( - self, mock_remove_hierarchy, mock_command, mock_warn, mock_islink + self, mock_Checksum, mock_remove_hierarchy, + mock_command, mock_warn, mock_islink ): mock_islink.return_value = True mock_remove_hierarchy.side_effect = Exception('rm') @@ -167,8 +192,9 @@ class TestRootBind(object): @patch('kiwi.logger.log.warning') @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') + @patch('kiwi.system.root_bind.Checksum') def test_cleanup_nothing_mounted( - self, mock_remove_hierarchy, mock_command, mock_warn + self, mock_Checksum, mock_remove_hierarchy, mock_command, mock_warn ): self.mount_manager.is_mounted.return_value = False self.mount_manager.mountpoint = '/mountpoint'