-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
👷 gucc: add fstab generation based on provided partitions
- Loading branch information
1 parent
aba9a8d
commit 8b40474
Showing
7 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#ifndef FSTAB_HPP | ||
#define FSTAB_HPP | ||
|
||
#include <optional> // for optional | ||
#include <string> // for string | ||
#include <string_view> // for string_view | ||
#include <vector> // for vector | ||
|
||
namespace gucc::fs { | ||
|
||
struct Partition final { | ||
std::string fstype; | ||
std::string mountpoint; | ||
std::string uuid_str; | ||
std::string device; | ||
|
||
// mount points that will be written in fstab, | ||
// excluding subvol={subvol name} | ||
// if device is ssd, mount options for ssd should be appended | ||
std::string mount_opts; | ||
std::optional<std::string> luks_mapper_name; | ||
std::optional<std::string> luks_uuid; | ||
|
||
/* | ||
// subvolumes per partition | ||
// e.g we have partition /dev/nvme0n1p1 with subvolumes: /@, /@home, /@cache | ||
std::optional<std::vector<BtrfsSubvolume>> subvols; | ||
*/ | ||
// subvolume name if the partition is btrrfs subvolume | ||
std::optional<std::string> subvolume; | ||
}; | ||
|
||
// Generate fstab | ||
auto generate_fstab(const std::vector<Partition>& partitions, std::string_view root_mountpoint, std::string_view crypttab_opts) noexcept -> bool; | ||
|
||
// Generate fstab into string | ||
auto generate_fstab_content(const std::vector<Partition>& partitions) noexcept -> std::string; | ||
|
||
} // namespace gucc::fs | ||
|
||
#endif // FSTAB_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#include "gucc/fstab.hpp" | ||
#include "gucc/io_utils.hpp" | ||
|
||
#include <cctype> // for tolower | ||
|
||
#include <algorithm> // for transform | ||
#include <filesystem> | ||
#include <fstream> | ||
|
||
#include <fmt/compile.h> | ||
#include <fmt/format.h> | ||
|
||
#include <spdlog/spdlog.h> | ||
|
||
#if defined(__clang__) | ||
#pragma clang diagnostic push | ||
#pragma clang diagnostic ignored "-Wold-style-cast" | ||
#elif defined(__GNUC__) | ||
#pragma GCC diagnostic push | ||
#pragma GCC diagnostic ignored "-Wuseless-cast" | ||
#pragma GCC diagnostic ignored "-Wold-style-cast" | ||
#endif | ||
|
||
#include <range/v3/algorithm/transform.hpp> | ||
|
||
#if defined(__clang__) | ||
#pragma clang diagnostic pop | ||
#elif defined(__GNUC__) | ||
#pragma GCC diagnostic pop | ||
#endif | ||
|
||
namespace fs = std::filesystem; | ||
using namespace std::string_view_literals; | ||
|
||
namespace { | ||
static constexpr auto FSTAB_HEADER = R"(# Static information about the filesystems. | ||
# See fstab(5) for details. | ||
# <file system> <dir> <type> <options> <dump> <pass> | ||
)"sv; | ||
|
||
constexpr auto convert_fsname_fstab(std::string_view fsname) noexcept -> std::string_view { | ||
if (fsname == "fat16"sv || fsname == "fat32"sv) { | ||
return "vfat"sv; | ||
} else if (fsname == "linuxswap"sv) { | ||
return "swap"sv; | ||
} | ||
return fsname; | ||
} | ||
|
||
auto string_tolower(std::string_view text) noexcept -> std::string { | ||
std::string res{text}; | ||
ranges::transform(res, res.begin(), | ||
[](char char_elem) { return static_cast<char>(std::tolower(static_cast<unsigned char>(char_elem))); }); | ||
return res; | ||
} | ||
|
||
} // namespace | ||
|
||
namespace gucc::fs { | ||
|
||
auto gen_fstab_entry(const Partition& partition) noexcept -> std::optional<std::string> { | ||
// Apperently some FS names named differently in /etc/fstab. | ||
const auto& fstype = [](auto&& fstype) -> std::string { | ||
auto lower_fstype = string_tolower(fstype); | ||
return std::string{convert_fsname_fstab(lower_fstype)}; | ||
}(partition.fstype); | ||
|
||
const auto& luks_mapper_name = partition.luks_mapper_name; | ||
const auto& mountpoint = partition.mountpoint; | ||
const auto& disk_name = ::fs::path{partition.device}.filename().string(); | ||
|
||
// NOTE: ignore swap support for now | ||
if (fstype == "swap"sv) { | ||
spdlog::info("fstab: skipping swap partition"); | ||
return std::nullopt; | ||
} | ||
|
||
const auto check_num = [](auto&& mountpoint, auto&& fstype) -> std::int32_t { | ||
if (mountpoint == "/"sv && fstype != "btrfs"sv) { | ||
return 1; | ||
} else if (mountpoint != "swap"sv && fstype != "btrfs"sv) { | ||
return 2; | ||
} | ||
return 0; | ||
}(mountpoint, fstype); | ||
|
||
const auto& mount_options = [](auto&& mount_opts, auto&& subvolume, auto&& fstype) -> std::string { | ||
if (fstype == "btrfs"sv && !subvolume.empty()) { | ||
return fmt::format(FMT_COMPILE("subvol={},{}"), subvolume, mount_opts); | ||
} | ||
return {mount_opts}; | ||
}(partition.mount_opts, partition.subvolume.value_or(""), fstype); | ||
|
||
std::string device_str{partition.device}; | ||
if (luks_mapper_name) { | ||
device_str = fmt::format(FMT_COMPILE("/dev/mapper/{}"), *luks_mapper_name); | ||
} else if (!partition.uuid_str.empty()) { | ||
device_str = fmt::format(FMT_COMPILE("UUID={}"), partition.uuid_str); | ||
} | ||
return std::make_optional<std::string>(fmt::format(FMT_COMPILE("# {}\n{:41} {:<14} {:<7} {:<10} 0 {}\n\n"), partition.device, device_str, mountpoint, fstype, mount_options, check_num)); | ||
} | ||
|
||
auto generate_fstab_content(const std::vector<Partition>& partitions) noexcept -> std::string { | ||
std::string fstab_content{FSTAB_HEADER}; | ||
|
||
for (auto&& partition : partitions) { | ||
if (partition.fstype == "zfs"sv) { | ||
// zfs partitions don't need an entry in fstab | ||
continue; | ||
} | ||
// if btrfs and root mountpoint | ||
/*if (partition.fstype == "btrfs"sv && partition.mountpoint == "/"sv) { | ||
// NOTE: should we handle differently here? | ||
}*/ | ||
auto fstab_entry = gen_fstab_entry(partition); | ||
if (!fstab_entry) { | ||
continue; | ||
} | ||
fstab_content += std::move(*fstab_entry); | ||
} | ||
|
||
return fstab_content; | ||
} | ||
|
||
auto generate_fstab(const std::vector<Partition>& partitions, std::string_view root_mountpoint) noexcept -> bool { | ||
const auto& fstab_filepath = fmt::format(FMT_COMPILE("{}/etc/fstab"), root_mountpoint); | ||
|
||
std::ofstream fstab_file{fstab_filepath, std::ios::out | std::ios::trunc}; | ||
if (!fstab_file.is_open()) { | ||
spdlog::error("Failed to open fstab for writing {}", fstab_filepath); | ||
return false; | ||
} | ||
fstab_file << fs::generate_fstab_content(partitions); | ||
return true; | ||
} | ||
|
||
} // namespace gucc::fs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#include "gucc/fstab.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; | ||
|
||
static constexpr auto FSTAB_BTRFS_TEST = R"(# Static information about the filesystems. | ||
# See fstab(5) for details. | ||
# <file system> <dir> <type> <options> <dump> <pass> | ||
# /dev/nvme0n1p1 | ||
UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f / btrfs subvol=/@,defaults,noatime,compress=zstd,space_cache=v2,commit=120 0 0 | ||
# /dev/nvme0n1p1 | ||
UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f /home btrfs subvol=/@home,defaults,noatime,compress=zstd,space_cache=v2,commit=120 0 0 | ||
# /dev/nvme0n1p1 | ||
UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f /var/cache btrfs subvol=/@cache,defaults,noatime,compress=zstd,space_cache=v2,commit=120 0 0 | ||
# /dev/nvme0n1p2 | ||
UUID=8EFB-4B84 /boot vfat defaults,noatime 0 2 | ||
)"sv; | ||
|
||
static constexpr auto FSTAB_XFS_TEST = R"(# Static information about the filesystems. | ||
# See fstab(5) for details. | ||
# <file system> <dir> <type> <options> <dump> <pass> | ||
# /dev/nvme0n1p1 | ||
UUID=6bdb3301-8efb-4b84-b0b7-4caeef26fd6f / xfs defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota 0 1 | ||
# /dev/nvme0n1p2 | ||
UUID=8EFB-4B84 /boot vfat defaults,noatime 0 2 | ||
)"sv; | ||
|
||
static constexpr auto FSTAB_LUKS_XFS_TEST = R"(# Static information about the filesystems. | ||
# See fstab(5) for details. | ||
# <file system> <dir> <type> <options> <dump> <pass> | ||
# /dev/nvme0n1p1 | ||
/dev/mapper/luks_device / xfs defaults,lazytime,noatime,attr2,inode64,logbsize=256k,noquota 0 1 | ||
# /dev/nvme0n1p2 | ||
UUID=8EFB-4B84 /boot vfat defaults,noatime 0 2 | ||
)"sv; | ||
|
||
static constexpr auto FSTAB_ZFS_TEST = R"(# Static information about the filesystems. | ||
# See fstab(5) for details. | ||
# <file system> <dir> <type> <options> <dump> <pass> | ||
# /dev/nvme0n1p2 | ||
UUID=8EFB-4B84 /boot vfat defaults,noatime 0 2 | ||
)"sv; | ||
|
||
int main() { | ||
auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg& msg) { | ||
// noop | ||
}); | ||
spdlog::set_default_logger(std::make_shared<spdlog::logger>("default", callback_sink)); | ||
|
||
// btrfs with snapshots | ||
{ | ||
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, .luks_mapper_name = {}, .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, .luks_mapper_name = {}, .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, .luks_mapper_name = {}, .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& fstab_content = gucc::fs::generate_fstab_content(partitions); | ||
assert(fstab_content == FSTAB_BTRFS_TEST); | ||
} | ||
// 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& fstab_content = gucc::fs::generate_fstab_content(partitions); | ||
assert(fstab_content == FSTAB_XFS_TEST); | ||
} | ||
// 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& fstab_content = gucc::fs::generate_fstab_content(partitions); | ||
assert(fstab_content == FSTAB_LUKS_XFS_TEST); | ||
} | ||
// 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& fstab_content = gucc::fs::generate_fstab_content(partitions); | ||
assert(fstab_content == FSTAB_ZFS_TEST); | ||
} | ||
} |