From 5dc58c5745e846889ef24ca6f3dce679b54dda18 Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Mon, 14 Feb 2022 03:22:19 +0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20add=20btrfs=20subvolumes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 26 ++++------- cmake/StandardProjectSettings.cmake | 6 ++- meson.build | 7 ++- src/crypto.cpp | 4 +- src/disk.cpp | 71 ++++++++++++++++++++++++++++- src/disk.hpp | 9 ++-- src/tui.cpp | 38 +++++++++++++-- src/utils.cpp | 2 +- 8 files changed, 130 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 104f0bf..076e074 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,18 +22,12 @@ include(StaticAnalyzers) include(Sanitizers) include(CPM) -find_package(PkgConfig REQUIRED) -pkg_check_modules( - LIBNM - REQUIRED - IMPORTED_TARGET - libnm>=1.10.6) - -pkg_check_modules( - GLIBMM - REQUIRED - IMPORTED_TARGET - glibmm-2.4>=2.56.0) +#find_package(PkgConfig REQUIRED) +#pkg_check_modules( +# GLIBMM +# REQUIRED +# IMPORTED_TARGET +# glibmm-2.4>=2.56.0) CPMAddPackage( NAME ftxui @@ -80,7 +74,7 @@ endif() set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fwhole-program") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fwhole-program -fuse-linker-plugin") endif() # Link this 'library' to set the c++ standard / compile-time options requested @@ -138,9 +132,9 @@ enable_sanitizers(project_options) include_directories(${CMAKE_SOURCE_DIR}/src) -target_link_libraries(${PROJECT_NAME} PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::screen ftxui::dom ftxui::component simdjson::simdjson cpr::cpr PkgConfig::GLIBMM) -target_link_libraries(test-exec-interactive PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component simdjson::simdjson cpr::cpr PkgConfig::GLIBMM) -target_link_libraries(test-process-tailing PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component simdjson::simdjson cpr::cpr PkgConfig::GLIBMM) +target_link_libraries(${PROJECT_NAME} PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::screen ftxui::dom ftxui::component simdjson::simdjson cpr::cpr) +target_link_libraries(test-exec-interactive PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component simdjson::simdjson cpr::cpr) +target_link_libraries(test-process-tailing PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component simdjson::simdjson cpr::cpr) if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") target_link_libraries(${PROJECT_NAME} PRIVATE range-v3::range-v3) diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake index 0307e2c..61d2962 100644 --- a/cmake/StandardProjectSettings.cmake +++ b/cmake/StandardProjectSettings.cmake @@ -27,12 +27,16 @@ set(SIMDJSON_DISABLE_DEPRECATED_API ON CACHE INTERNAL "" FORCE) # Generate compile_commands.json to make it easier to work with clang based tools set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -flto") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") +if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fwhole-program -fuse-linker-plugin") +endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fwhole-program -fuse-linker-plugin") endif() -#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") #-static") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")# -static") option(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF) diff --git a/meson.build b/meson.build index 5e4eb36..d96d3b4 100644 --- a/meson.build +++ b/meson.build @@ -38,8 +38,7 @@ fmt = dependency('fmt', version : ['>=8.0.0'], fallback : ['fmt', 'fmt_dep']) ftxui = dependency('ftxui', modules : ['ftxui::screen', 'ftxui::dom', 'ftxui::component'], fallback : ['ftxui', 'ftxui_dep']) simdjson = dependency('simdjson', version : ['>=1.0.2'], fallback : ['simdjson', 'simdjson_dep']) cpr = dependency('cpr', version : ['>=1.7.0'], fallback : ['cpr', 'cpr_dep']) -libnm = dependency('libnm', version : ['>=1.10.6']) -glibmm = dependency('glibmm-2.4', version : ['>=2.56.0']) +#glibmm = dependency('glibmm-2.4', version : ['>=2.56.0']) src_files = files( 'src/view.hpp', @@ -113,13 +112,13 @@ if not is_debug_build endif possible_cc_flags += ['-fdata-sections', '-ffunction-sections'] - possible_link_flags = ['-Wl,--gc-sections'] + possible_link_flags = ['-Wl,--gc-sections', '-static-libgcc', '-static-libstdc++'] add_project_link_arguments(cc.get_supported_link_arguments(possible_link_flags), language : 'cpp') endif add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'cpp') -deps = [fmt, spdlog, ftxui, simdjson, cpr, glibmm] +deps = [fmt, spdlog, ftxui, simdjson, cpr] if cc.get_id() == 'clang' ranges = dependency('range-v3', version : ['>=0.11.0']) deps += [ranges] diff --git a/src/crypto.cpp b/src/crypto.cpp index 91a39d7..fe8943e 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -75,9 +75,9 @@ bool luks_open() noexcept { utils::find_partitions(); // Filter out partitions that don't contain crypt device - const auto& ignore_part = utils::list_non_crypt(); + /*const auto& ignore_part = utils::list_non_crypt(); - /* const auto& parts = utils::make_multiline(ignore_part); + const auto& parts = utils::make_multiline(ignore_part); for (const auto& part : parts) { utils::delete_partition_in_list(part); }*/ diff --git a/src/disk.cpp b/src/disk.cpp index c31015f..ff10cef 100644 --- a/src/disk.cpp +++ b/src/disk.cpp @@ -16,6 +16,75 @@ namespace fs = std::filesystem; namespace utils { +void btrfs_create_subvols([[maybe_unused]] const disk_part& disk, const std::string_view& mode) noexcept { + /* clang-format off */ + if (mode.empty()) { return; } + /* clang-format on */ + +#ifdef NDEVENV + // save mount options and name of the root partition + utils::exec("mount | grep \"on /mnt \" | grep -Po '(?<=\\().*(?=\\))' > /tmp/.root_mount_options"); + // utils::exec("lsblk -lno MOUNTPOINT,NAME | awk '/^\\/mnt / {print $2}' > /tmp/.root_partition"); + + if (mode == "manual") { + // Create subvolumes manually + std::string subvols{"@ @home @cache"}; + static constexpr auto subvols_body = "\nInput names of the subvolumes separated by spaces.\nThe first one will be used for mounting /.\n"; + if (!tui::detail::inputbox_widget(subvols, subvols_body, size(ftxui::HEIGHT, ftxui::GREATER_THAN, 4))) { + return; + } + const auto& saved_path = fs::current_path(); + fs::current_path("/mnt"); + auto subvol_list = utils::make_multiline(subvols, false, " "); + for (const auto& subvol : subvol_list) { + utils::exec(fmt::format(FMT_COMPILE("btrfs subvolume create {}"), subvol), true); + } + fs::current_path(saved_path); + // Mount subvolumes + umount("/mnt"); + // Mount the first subvolume as / + utils::exec(fmt::format(FMT_COMPILE("mount -o {},subvol=\"{}\" \"{}\" /mnt"), disk.mount_opts, subvol_list[0], disk.root)); + // Remove the first subvolume from the subvolume list + subvol_list.erase(subvol_list.begin()); + + // Loop to mount all created subvolumes + for (const auto& subvol : subvol_list) { + std::string mountp{"/home"}; + const auto& mountp_body = fmt::format(FMT_COMPILE("\nInput mountpoint of the subvolume {}\nas it would appear in installed system\n(without prepending /mnt).\n"), subvol); + if (!tui::detail::inputbox_widget(mountp, mountp_body, size(ftxui::HEIGHT, ftxui::GREATER_THAN, 4))) { + return; + } + + const auto& mountp_formatted = fmt::format(FMT_COMPILE("/mnt{}"), mountp); + fs::create_directories(mountp_formatted); + utils::exec(fmt::format(FMT_COMPILE("mount -o {},subvol=\"{}\" \"{}\" \"{}\""), disk.mount_opts, subvol, disk.root, mountp_formatted)); + } + return; + } + static constexpr auto content = "\nThis creates subvolumes:\n@ for /,\n@home for /home,\n@cache for /var/cache.\n"; + const auto& do_create = tui::detail::yesno_widget(content, size(ftxui::HEIGHT, ftxui::LESS_THAN, 15) | size(ftxui::WIDTH, ftxui::LESS_THAN, 75)); + /* clang-format off */ + if (!do_create) { return; } + /* clang-format on */ + + // Create subvolumes automatically + const auto& saved_path = fs::current_path(); + fs::current_path("/mnt"); + utils::exec("btrfs subvolume create @", true); + utils::exec("btrfs subvolume create @home", true); + utils::exec("btrfs subvolume create @cache", true); + // utils::exec("btrfs subvolume create @snapshots", true); + fs::current_path(saved_path); + // Mount subvolumes + umount("/mnt"); + utils::exec(fmt::format(FMT_COMPILE("mount -o {},subvol=@ \"{}\" /mnt"), disk.mount_opts, disk.root)); + fs::create_directories("/mnt/home"); + fs::create_directories("/mnt/var/cache"); + utils::exec(fmt::format(FMT_COMPILE("mount -o {},subvol=@home \"{}\" /mnt/home"), disk.mount_opts, disk.root)); + utils::exec(fmt::format(FMT_COMPILE("mount -o {},subvol=@cache \"{}\" /mnt/var/cache"), disk.mount_opts, disk.root)); +#endif +} + void mount_existing_subvols(const disk_part& disk) noexcept { // Set mount options const auto& format_name = utils::exec(fmt::format(FMT_COMPILE("echo {} | rev | cut -d/ -f1 | rev"), disk.part)); @@ -36,7 +105,7 @@ void mount_existing_subvols(const disk_part& disk) noexcept { // Ask for mountpoint const auto& content = fmt::format(FMT_COMPILE("\nInput mountpoint of the subvolume {}\nas it would appear in installed system\n(without prepending /mnt).\n"), subvol); std::string mountpoint{"/"}; - if (!tui::detail::inputbox_widget(mountpoint, content, size(ftxui::HEIGHT, ftxui::LESS_THAN, 9) | size(ftxui::WIDTH, ftxui::LESS_THAN, 30))) { + if (!tui::tui::detail::inputbox_widget(mountpoint, content, size(ftxui::HEIGHT, ftxui::LESS_THAN, 9) | size(ftxui::WIDTH, ftxui::LESS_THAN, 30))) { return; } const auto& mount_dir{fmt::format(FMT_COMPILE("/mnt/{}"), mountpoint)}; diff --git a/src/disk.hpp b/src/disk.hpp index 687737f..74e1d84 100644 --- a/src/disk.hpp +++ b/src/disk.hpp @@ -6,11 +6,14 @@ namespace utils { struct disk_part { - const std::string_view root; - const std::string_view part; + const std::string_view root{}; + const std::string_view part{}; + const std::string_view mount_opts{}; }; -void mount_existing_subvols(const disk_part& partition) noexcept; +void btrfs_create_subvols(const disk_part& disk, const std::string_view& mode) noexcept; +void mount_existing_subvols(const disk_part& disk) noexcept; + } // namespace utils #endif // DISK_HPP diff --git a/src/tui.cpp b/src/tui.cpp index abb4196..108e146 100644 --- a/src/tui.cpp +++ b/src/tui.cpp @@ -133,6 +133,33 @@ bool exit_done() noexcept { #endif } +void btrfs_subvolumes() noexcept { + const std::vector menu_entries = { + "automatic", + "manual", + }; + auto screen = ScreenInteractive::Fullscreen(); + std::int32_t selected{}; + std::string btrfsvols_mode{}; + auto ok_callback = [&] { + btrfsvols_mode = menu_entries[static_cast(selected)]; + screen.ExitLoopClosure()(); + }; + static constexpr auto btrfsvols_body = "\nAutomatic mode\nis designed to allow integration\nwith snapper, non-recursive snapshots,\nseparating system and user data and\nrestoring snapshots without losing data.\n"; + /* clang-format off */ + detail::menu_widget(menu_entries, ok_callback, &selected, &screen, btrfsvols_body); + + if (btrfsvols_mode.empty()) { return; } + /* clang-format on */ + + auto* config_instance = Config::instance(); + auto& config_data = config_instance->data(); + + const auto& mount_opts_info = std::get(config_data["MOUNT_OPTS"]); + const auto& root_part = std::get(config_data["ROOT_PART"]); + utils::btrfs_create_subvols({.root = root_part, .mount_opts = mount_opts_info}, btrfsvols_mode); +} + // Function will not allow incorrect UUID type for installed system. void generate_fstab() noexcept { const std::vector menu_entries = { @@ -1809,16 +1836,17 @@ void mount_partitions() noexcept { const auto& subvolumes_formated = utils::exec(fmt::format(FMT_COMPILE("{} | cut -d\" \" -f9"), subvolumes)); const auto& existing_subvolumes = detail::yesno_widget(fmt::format(FMT_COMPILE("\nFound subvolumes {}\n \nWould you like to mount them?\n "), subvolumes_formated), size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 75)); // Pre-existing subvolumes and user wants to mount them - if (existing_subvolumes) { - utils::mount_existing_subvols({root_part, part}); - } + /* clang-format off */ + if (existing_subvolumes) { utils::mount_existing_subvols({root_part, part}); } + /* clang-format on */ } else { // No subvolumes present. Make some new ones const auto& create_subvolumes = detail::yesno_widget("\nWould you like to create subvolumes in it? \n", size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 75)); + /* clang-format on */ if (create_subvolumes) { - spdlog::debug("Implement me!"); - // utils::btrfs_subvolumes({root_part, part}); + tui::btrfs_subvolumes(); } + /* clang-format on */ } } } diff --git a/src/utils.cpp b/src/utils.cpp index 89cba0d..7816f35 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -74,7 +74,7 @@ namespace utils { bool is_connected() noexcept { #ifdef NDEVENV /* clang-format off */ - auto r = cpr::Get(cpr::Url{"https://www.google.com"}, + auto r = cpr::Get(cpr::Url{"https://cachyos.org"}, cpr::Timeout{1000}); /* clang-format on */ return cpr::status::is_success(static_cast(r.status_code)) || cpr::status::is_redirect(static_cast(r.status_code));