Skip to content

Commit

Permalink
👷 gucc: generate kernel params out of partitions scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
vnepogodin committed Jul 25, 2024
1 parent 335d519 commit 531b19b
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
./gucc/tests/test-fstab_gen
./gucc/tests/test-crypttab_gen
./gucc/tests/test-grub_config_gen
./gucc/tests/test-kernel_params
./gucc/tests/test-fetch_file
./gucc/tests/test-locale
./gucc/tests/test-package_profiles
Expand Down Expand Up @@ -93,6 +94,7 @@ jobs:
./gucc/tests/test-fstab_gen
./gucc/tests/test-crypttab_gen
./gucc/tests/test-grub_config_gen
./gucc/tests/test-kernel_params
./gucc/tests/test-fetch_file
./gucc/tests/test-locale
./gucc/tests/test-package_profiles
Expand Down
1 change: 1 addition & 0 deletions gucc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ add_library(${PROJECT_NAME} #SHARED
src/hwclock.cpp include/gucc/hwclock.hpp
src/package_profiles.cpp include/gucc/package_profiles.hpp
src/fetch_file.cpp include/gucc/fetch_file.hpp
src/kernel_params.cpp include/gucc/kernel_params.hpp
${GUCC_LOGGER_FILES}
#src/chwd_profiles.cpp src/chwd_profiles.hpp
#src/disk.cpp src/disk.hpp
Expand Down
22 changes: 22 additions & 0 deletions gucc/include/gucc/kernel_params.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef KERNEL_PARAMS_HPP
#define KERNEL_PARAMS_HPP

#include "gucc/partition.hpp"

#include <optional> // for optional
#include <string> // for string
#include <string_view> // for string_view
#include <vector> // for vector

namespace gucc::fs {

/// @brief Get kernel params from partitions information.
/// @param partitions The partitions information.
/// @param kernel_params The kernel params separated by space.
/// @param zfs_root_dataset The root dataset of ZFS.
/// @return A vector of strings representing the kernel parameters.
auto get_kernel_params(const std::vector<Partition>& partitions, std::string_view kernel_params, std::optional<std::string> zfs_root_dataset = std::nullopt) noexcept -> std::optional<std::vector<std::string>>;

} // namespace gucc::fs

#endif // KERNEL_PARAMS_HPP
1 change: 1 addition & 0 deletions gucc/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ gucc_lib = library('gucc',
'src/package_profiles.cpp',
'src/logger.cpp',
'src/fetch_file.cpp',
'src/kernel_params.cpp',
],
include_directories : [include_directories('include')],
dependencies: deps
Expand Down
99 changes: 99 additions & 0 deletions gucc/src/kernel_params.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include "gucc/kernel_params.hpp"
#include "gucc/file_utils.hpp"
#include "gucc/io_utils.hpp"
#include "gucc/string_utils.hpp"

#include <filesystem> // for fs::path
#include <optional> // for optional
#include <utility> // for make_optional

#include <fmt/compile.h>
#include <fmt/format.h>

#include <spdlog/spdlog.h>

using namespace std::string_literals;
using namespace std::string_view_literals;

namespace {

auto is_root_on_btrfs(const gucc::fs::Partition& partition) noexcept -> bool {
return partition.mountpoint == "/"sv && partition.fstype == "btrfs"sv;
}
auto is_root_on_zfs(const gucc::fs::Partition& partition) noexcept -> bool {
return partition.mountpoint == "/"sv && partition.fstype == "zfs"sv;
}

} // namespace

namespace gucc::fs {

// base actions taken from calamares
// see https://github.com/calamares/calamares/blob/calamares/src/modules/bootloader/main.py#L133
auto get_kernel_params(const std::vector<Partition>& partitions, std::string_view kernel_params, std::optional<std::string> zfs_root_dataset) noexcept -> std::optional<std::vector<std::string>> {
auto kernel_params_list = gucc::utils::make_multiline(kernel_params, false, ' ');
kernel_params_list.emplace_back("rw"s);

std::vector<std::string> cryptdevice_params{};
std::optional<std::string_view> root_uuid{};
// swap settings
std::optional<std::string_view> swap_uuid{};
std::optional<std::string_view> swap_mappername{};

for (auto&& partition : partitions) {
const bool has_luks = partition.luks_mapper_name.has_value();
if (partition.fstype == "linuxswap"sv && !has_luks) {
swap_uuid = std::make_optional<std::string_view>(partition.uuid_str);
} else if (partition.fstype == "linuxswap"sv && has_luks) {
swap_mappername = partition.luks_mapper_name;
}

if (partition.mountpoint == "/"sv && has_luks) {
cryptdevice_params = {fmt::format(FMT_COMPILE("cryptdevice=UUID={}:{}"), *partition.luks_uuid, *partition.luks_mapper_name)};
cryptdevice_params.push_back(fmt::format(FMT_COMPILE("root=/dev/mapper/{}"), *partition.luks_mapper_name));
}

if (partition.mountpoint == "/"sv) {
root_uuid = std::make_optional<std::string_view>(partition.uuid_str);
}

// specific logic for btrfs and zfs

// If a btrfs root subvolume was not set, it implies that the root
// is directly on the partition and this option is not required.
if (is_root_on_btrfs(partition) && partition.subvolume) {
kernel_params_list.push_back(fmt::format(FMT_COMPILE("rootflags=subvol={}"), *partition.subvolume));
}
// The root dataset's location must be specified for ZFS.
else if (is_root_on_zfs(partition)) {
if (!zfs_root_dataset) {
spdlog::error("kernel_params: root zfs dataset cannot be nullopt");
return std::nullopt;
}
kernel_params_list.push_back(fmt::format(FMT_COMPILE("root=ZFS={}"), *zfs_root_dataset));
}
}

if (!root_uuid.has_value() || root_uuid->empty()) {
spdlog::error("kernel_params: not found ROOT partition UUID");
return std::nullopt;
}

if (!cryptdevice_params.empty()) {
kernel_params_list.insert(kernel_params_list.end(), cryptdevice_params.begin(), cryptdevice_params.end());
} else {
kernel_params_list.push_back(fmt::format(FMT_COMPILE("root=UUID={}"), *root_uuid));
}

if (swap_uuid.has_value() && !swap_uuid->empty()) {
kernel_params_list.push_back(fmt::format(FMT_COMPILE("resume=UUID={}"), *swap_uuid));
}

if (swap_mappername.has_value() && !swap_mappername->empty()) {
kernel_params_list.push_back(fmt::format(FMT_COMPILE("resume=/dev/mapper/{}"), *swap_mappername));
}

return std::make_optional<std::vector<std::string>>(kernel_params_list);
}

} // namespace gucc::fs
8 changes: 8 additions & 0 deletions gucc/tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ executable(
link_with: [gucc_lib],
include_directories: [include_directories('../include')],
install: false)

executable(
'test-kernel_params',
files('unit-kernel_params.cpp'),
dependencies: deps,
link_with: [gucc_lib],
include_directories: [include_directories('../include')],
install: false)
141 changes: 141 additions & 0 deletions gucc/tests/unit-kernel_params.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#include "gucc/kernel_params.hpp"
#include "gucc/logger.hpp"

#include <cassert>

#include <string>
#include <string_view>
#include <vector>

#include <spdlog/sinks/callback_sink.h>
#include <spdlog/spdlog.h>

using namespace std::string_literals;
using namespace std::string_view_literals;

int main() {
auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg&) {
// noop
});
auto logger = std::make_shared<spdlog::logger>("default", callback_sink);
spdlog::set_default_logger(logger);
gucc::logger::set_logger(logger);

static constexpr auto DEFAULT_KERNEL_PARAMS = "quiet splash"sv;

// btrfs with subvolumes
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "btrfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s, .subvolume = "/@"s},
gucc::fs::Partition{.fstype = "btrfs"s, .mountpoint = "/home"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s, .subvolume = "/@home"s},
gucc::fs::Partition{.fstype = "btrfs"s, .mountpoint = "/var/cache"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s, .subvolume = "/@cache"s},
gucc::fs::Partition{.fstype = "fat32"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(kernel_params.has_value());
assert(kernel_params->size() == 5);
assert((*kernel_params == std::vector<std::string>{"quiet", "splash", "rw", "rootflags=subvol=/@", "root=UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"}));
}
// invalid xfs (empty uuid)
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "xfs"s, .mountpoint = "/"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota"s},
gucc::fs::Partition{.fstype = "fat16"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(!kernel_params.has_value());
}
// invalid xfs (without root partition)
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "xfs"s, .mountpoint = "/home"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota"s},
gucc::fs::Partition{.fstype = "fat16"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(!kernel_params.has_value());
}
// basic xfs
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "xfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota"s},
gucc::fs::Partition{.fstype = "fat16"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(kernel_params.has_value());
assert(kernel_params->size() == 4);
assert((*kernel_params == std::vector<std::string>{"quiet", "splash", "rw", "root=UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"}));
}
// swap xfs
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "xfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota"s},
gucc::fs::Partition{.fstype = "linuxswap"s, .mountpoint = ""s, .uuid_str = "59848b1b-c6be-48f4-b3e1-48179ea72dec"s, .device = "/dev/nvme0n1p3"s, .mount_opts = "defaults,noatime"s},
gucc::fs::Partition{.fstype = "vfat"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(kernel_params.has_value());
assert(kernel_params->size() == 5);
assert((*kernel_params == std::vector<std::string>{"quiet", "splash", "rw", "root=UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f", "resume=UUID=59848b1b-c6be-48f4-b3e1-48179ea72dec"}));
}
// luks xfs
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "xfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota"s, .luks_mapper_name = "luks_device"s, .luks_uuid = "00e1b836-81b6-433f-83ca-0fd373e3cd50"s},
gucc::fs::Partition{.fstype = "linuxswap"s, .mountpoint = ""s, .uuid_str = ""s, .device = "/dev/nvme0n1p3"s, .mount_opts = "defaults,noatime"s},
gucc::fs::Partition{.fstype = "vfat"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(kernel_params.has_value());
assert(kernel_params->size() == 5);
assert((*kernel_params == std::vector<std::string>{"quiet", "splash", "rw", "cryptdevice=UUID=00e1b836-81b6-433f-83ca-0fd373e3cd50:luks_device", "root=/dev/mapper/luks_device"}));
}
// luks swap xfs
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "xfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota"s, .luks_mapper_name = "luks_device"s, .luks_uuid = "00e1b836-81b6-433f-83ca-0fd373e3cd50"s},
gucc::fs::Partition{.fstype = "linuxswap"s, .mountpoint = ""s, .uuid_str = ""s, .device = "/dev/nvme0n1p3"s, .mount_opts = "defaults,noatime"s, .luks_mapper_name = "luks_swap_device"s, .luks_uuid = "59848b1b-c6be-48f4-b3e1-48179ea72dec"s},
gucc::fs::Partition{.fstype = "vfat"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(kernel_params.has_value());
assert(kernel_params->size() == 6);
assert((*kernel_params == std::vector<std::string>{"quiet", "splash", "rw", "cryptdevice=UUID=00e1b836-81b6-433f-83ca-0fd373e3cd50:luks_device", "root=/dev/mapper/luks_device", "resume=/dev/mapper/luks_swap_device"}));
}
// invalid zfs
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "zfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s},
gucc::fs::Partition{.fstype = "zfs"s, .mountpoint = "/home"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s},
gucc::fs::Partition{.fstype = "zfs"s, .mountpoint = "/var/cache"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s},
gucc::fs::Partition{.fstype = "vfat"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(!kernel_params.has_value());
}
// valid zfs
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "zfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s},
gucc::fs::Partition{.fstype = "zfs"s, .mountpoint = "/home"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s},
gucc::fs::Partition{.fstype = "zfs"s, .mountpoint = "/var/cache"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s},
gucc::fs::Partition{.fstype = "vfat"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS, "zpcachyos/ROOT");
assert(kernel_params.has_value());
assert(kernel_params->size() == 5);
assert((*kernel_params == std::vector<std::string>{"quiet", "splash", "rw", "root=ZFS=zpcachyos/ROOT", "root=UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"}));
}
// luks btrfs with subvolumes
{
const std::vector<gucc::fs::Partition> partitions{
gucc::fs::Partition{.fstype = "btrfs"s, .mountpoint = "/"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s, .subvolume = "/@"s, .luks_mapper_name = "luks-6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .luks_uuid = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s},
gucc::fs::Partition{.fstype = "btrfs"s, .mountpoint = "/home"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s, .subvolume = "/@home"s, .luks_mapper_name = "luks-6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .luks_uuid = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s},
gucc::fs::Partition{.fstype = "btrfs"s, .mountpoint = "/var/cache"s, .uuid_str = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .device = "/dev/nvme0n1p1"s, .mount_opts = "defaults,noatime,compress=zstd,space_cache=v2,commit=120"s, .subvolume = "/@cache"s, .luks_mapper_name = "luks-6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s, .luks_uuid = "6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"s},
gucc::fs::Partition{.fstype = "fat32"s, .mountpoint = "/boot"s, .uuid_str = "8EFB-4B84"s, .device = "/dev/nvme0n1p2"s, .mount_opts = "defaults,noatime"s},
};
const auto& kernel_params = gucc::fs::get_kernel_params(partitions, DEFAULT_KERNEL_PARAMS);
assert(kernel_params.has_value());
assert(kernel_params->size() == 6);
assert((*kernel_params == std::vector<std::string>{"quiet", "splash", "rw", "rootflags=subvol=/@", "cryptdevice=UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f:luks-6bdb3301-8efb-4b84-b0b7-4caeef26fd6f", "root=/dev/mapper/luks-6bdb3301-8efb-4b84-b0b7-4caeef26fd6f"}));
}
}

0 comments on commit 531b19b

Please sign in to comment.