From 84b520dd580b7725de4c9e62b11ec490cb8d3f4f Mon Sep 17 00:00:00 2001 From: zsdc Date: Wed, 13 Mar 2024 00:40:09 +0200 Subject: grub: T4548: Fixed configuration files order To iterate files on ext* file systems GRUB reads their inodes one by one, ignoring names. This breaks our configuration logic that relies on proper loading order. This commit adds a helper `sort_inodes()` that needs to be used whenever GRUB configuration files are created. It recreates files, changing their inodes in a way where inodes order matches alphabetical order. (cherry picked from commit f74923202311e853b677e52cd83bae2be9605c26) --- python/vyos/system/grub.py | 36 ++++++++++++++++++++++++++++++++++++ src/op_mode/image_installer.py | 4 ++++ src/system/grub_update.py | 4 ++++ 3 files changed, 44 insertions(+) diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py index 2e8b20972..864ed65aa 100644 --- a/python/vyos/system/grub.py +++ b/python/vyos/system/grub.py @@ -17,6 +17,7 @@ import platform from pathlib import Path from re import MULTILINE, compile as re_compile +from shutil import copy2 from typing import Union from uuid import uuid5, NAMESPACE_URL, UUID @@ -422,3 +423,38 @@ def set_kernel_cmdline_options(cmdline_options: str, version_name: str, version_add(version_name=version_name, root_dir=root_dir, boot_opts_config=cmdline_options) + + +def sort_inodes(dir_path: str) -> None: + """Sort inodes for files inside a folder + Regenerate inodes for each file to get the same order for both inodes + and file names + + GRUB iterates files by inodes, not alphabetically. Therefore, if we + want to read them in proper order, we need to sort inodes for all + config files in a folder. + + Args: + dir_path (str): a path to directory + """ + dir_content: list[Path] = sorted(Path(dir_path).iterdir()) + temp_list_old: list[Path] = [] + temp_list_new: list[Path] = [] + + # create a copy of all files, to get new inodes + for item in dir_content: + # skip directories + if item.is_dir(): + continue + # create a new copy of file with a temporary name + copy_path = Path(f'{item.as_posix()}_tmp') + copy2(item, Path(copy_path)) + temp_list_old.append(item) + temp_list_new.append(copy_path) + + # delete old files and rename new ones + for item in temp_list_old: + item.unlink() + for item in temp_list_new: + new_name = Path(f'{item.as_posix()[0:-4]}') + item.rename(new_name) diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index 886745bc7..5e5b8b275 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -733,6 +733,10 @@ def install_image() -> None: grub.install(install_target.name, f'{DIR_DST_ROOT}/boot/', f'{DIR_DST_ROOT}/boot/efi') + # sort inodes (to make GRUB read config files in alphabetical order) + grub.sort_inodes(f'{DIR_DST_ROOT}/{grub.GRUB_DIR_VYOS}') + grub.sort_inodes(f'{DIR_DST_ROOT}/{grub.GRUB_DIR_VYOS_VERS}') + # umount filesystems and remove temporary files if is_raid_install(install_target): cleanup([install_target.name], diff --git a/src/system/grub_update.py b/src/system/grub_update.py index 5a7d8eb72..5a0534195 100644 --- a/src/system/grub_update.py +++ b/src/system/grub_update.py @@ -105,4 +105,8 @@ if __name__ == '__main__': else: render(grub_cfg_main, grub.TMPL_GRUB_MAIN, {}) + # sort inodes (to make GRUB read config files in alphabetical order) + grub.sort_inodes(f'{root_dir}/{grub.GRUB_DIR_VYOS}') + grub.sort_inodes(f'{root_dir}/{grub.GRUB_DIR_VYOS_VERS}') + exit(0) -- cgit v1.2.3