@@ 47,56 47,56 @@ static constexpr bool isslash(char c) {
return c == '\\' || c == '/';
}
-/// Make temp dirs down to fpath, call func, delete the ones created.
-/// Zero-alloc, but fpath isn't reverted to being useful at the end, hence the &&.
-template <class F>
-static std::optional<std::string> with_temp_at(std::string && fpath, F && func) {
- auto nul = std::string::npos;
- for(struct stat sb; stat(fpath.c_str(), &sb) < 0;)
- if(errno == ENOENT) {
- if(nul != std::string::npos)
- fpath[nul] = '\0';
-
- nul = fpath.rfind('/', nul);
- if(nul == std::string::npos)
- break;
- } else
- return fmt::format("stat({}): {}", fpath.c_str(), strerror(errno));
-
- for(auto mknul = nul; (mknul = fpath.find('\0', mknul)) != std::string::npos;) {
- fpath[mknul] = '/';
- if(mkdir(fpath.c_str(), 0500) < 0)
- return fmt::format("mkdir({}): {}", fpath.c_str(), strerror(errno));
+static void print_devpath(const efidp_data * dp, ssize_t dp_len) {
+ const auto size = efidp_format_device_path(nullptr, 0, dp, dp_len);
+ if(size < 0)
+ fmt::print("couldn't format?\n");
+ else {
+ std::string path(size, '\0');
+ efidp_format_device_path(path.data(), path.size(), dp, dp_len);
+ fmt::print("{}\n", path);
}
+}
- auto delete_paths = [&]() -> std::optional<std::string> {
- for(auto rmnul = std::string::npos; nul != std::string::npos;) {
- if(rmnul != std::string::npos)
- fpath[rmnul] = '\0';
-
- auto next = fpath.rfind('/', rmnul);
- if(next <= nul)
- break;
-
- if(rmdir(fpath.c_str()) < 0)
- return fmt::format("rmdir({}): {}", fpath.c_str(), strerror(errno));
- rmnul = next;
+std::optional<std::string> klapki::context::context::save(const config & cfg, state::state & state) {
+ std::vector<std::uint8_t> esp_devpath_raw;
+ efidp_data * esp_devpath{};
+ if(!this->our_kernels.empty()) {
+ do {
+ esp_devpath_raw.resize(esp_devpath_raw.size() + 128);
+
+ // extern ssize_t efi_generate_file_device_path(uint8_t *buf, ssize_t size,
+ // const char * const filepath,
+ // uint32_t options, ...)
+ // EFIBOOT_ABBREV_HD matches what's produced by bootctl(1) install, and produces just HD()/File(),
+ // however, this funxion requires the File() to exist, so by passing just the ESP root, we can append our potentially-not-yet-existent paths later on.
+ if(auto size = efi_generate_file_device_path(esp_devpath_raw.data(), esp_devpath_raw.size(), //
+ fmt::format("{}/", cfg.esp).c_str(), //
+ EFIBOOT_ABBREV_HD);
+ size >= 0)
+ esp_devpath_raw.resize(size);
+ else if(errno != ENOSPC)
+ return fmt::format("Making device path for ESP: {}", strerror(errno));
+ } while(errno == ENOSPC);
+
+
+ esp_devpath = reinterpret_cast<efidp_data *>(esp_devpath_raw.data());
+ { // esp_devpath is currently HD(some path)/File("\"). Trim it to just HD() for appending later
+ efidp_data * fnode;
+ (void)efidp_next_node(esp_devpath, const_cast<const efidp_data **>(&fnode));
+ fnode->type = EFIDP_END_TYPE;
+ fnode->subtype = EFIDP_END_ENTIRE;
}
- return {};
- };
- klapki::quickscope_wrapper path_deleter{delete_paths};
- TRY_OPT(func());
- TRY_OPT(delete_paths());
- nul = std::string::npos;
-
- return {};
-}
+ if(cfg.verbose) {
+ fmt::print("ESP devpath: ");
+ print_devpath(reinterpret_cast<const efidp_data *>(esp_devpath_raw.data()), esp_devpath_raw.size());
+ }
+ }
-std::optional<std::string> klapki::context::context::save(const config & cfg, state::state & state) {
for(auto && [bootnum, kern] : this->our_kernels) {
auto skern = std::find_if(std::begin(state.statecfg.wanted_entries), std::end(state.statecfg.wanted_entries),
[bn = bootnum](auto && skern) { return skern.bootnum_hint == bn; });
@@ 106,7 106,8 @@ std::optional<std::string> klapki::context::context::save(const config & cfg, st
if(bent == std::end(state.entries))
throw __func__;
- auto image_path = fmt::format("{}/{}{}{}", cfg.esp, kern.image_path.first, isslash(kern.image_path.first.back()) ? "" : "\\", kern.image_path.second);
+ auto image_path = fmt::format("{}{}{}{}", isslash(kern.image_path.first.front()) ? "" : "\\", kern.image_path.first,
+ isslash(kern.image_path.first.back()) ? "" : "\\", kern.image_path.second);
image_path.erase(std::remove_if(std::begin(image_path), std::end(image_path),
[prev = false](auto c) mutable {
auto cur = isslash(c);
@@ 118,44 119,22 @@ std::optional<std::string> klapki::context::context::save(const config & cfg, st
}
}),
std::end(image_path));
- std::transform(std::begin(image_path), std::end(image_path), std::begin(image_path), [](auto c) { return isslash(c) ? '/' : c; });
-
- // efi_generate_file_device_path() requires the image file to exist,
- // and the implementation used for ESP detexion there is (a) hidden so we can't link to it and (b) massive and not something I wish to, or can, maintain.
- // This blows, but we take precautions to revert the filesystem back to how we found it if we needed to modify it.
- std::vector<std::uint8_t> devpath;
- TRY_OPT(with_temp_at(std::move(image_path), [&, bootnum = bootnum]() -> std::optional<std::string> {
- do {
- devpath.resize(devpath.size() + 512);
-
- // extern ssize_t efi_generate_file_device_path(uint8_t *buf, ssize_t size,
- // const char * const filepath,
- // uint32_t options, ...)
- // EFIBOOT_ABBREV_HD matches what's produced by bootctl(1) install, and produces just HD()\File()
- if(auto size = efi_generate_file_device_path(devpath.data(), devpath.size(), //
- image_path.c_str(), //
- EFIBOOT_ABBREV_HD);
- size >= 0)
- devpath.resize(size);
- else if(errno != ENOSPC)
- return fmt::format("Making device path for {:04X}: {}", bootnum, strerror(errno));
- } while(errno == ENOSPC);
-
- if(cfg.verbose) {
- const auto size = efidp_format_device_path(nullptr, 0, reinterpret_cast<const efidp_data *>(devpath.data()), devpath.size());
- fmt::print("Entry {:04X} devpath: ", bootnum);
- if(size < 0)
- fmt::print("couldn't format?\n");
- else {
- std::string path(size, '\0');
- efidp_format_device_path(path.data(), path.size(), reinterpret_cast<const efidp_data *>(devpath.data()), devpath.size());
- fmt::print("{}\n", path);
- }
- }
-
- return {};
- }));
+ std::transform(std::begin(image_path), std::end(image_path), std::begin(image_path), [](auto c) { return isslash(c) ? '\\' : c; });
+ std::vector<std::uint8_t> devpath_file_node(efidp_make_file(nullptr, 0, image_path.data()));
+ if(efidp_make_file(devpath_file_node.data(), devpath_file_node.size(), image_path.data()) < 0)
+ return fmt::format("Entry {:04X}: creating devpath File(): {}", bootnum, strerror(errno));
+
+ efidp_data * devpath;
+ if(efidp_append_node(esp_devpath, reinterpret_cast<const efidp_data *>(devpath_file_node.data()), &devpath) < 0)
+ return fmt::format("Entry {:04X}: creating appending File(): {}", bootnum, strerror(errno));
+ quickscope_wrapper devpath_deleter{[&] { std::free(devpath); }};
+ const auto devpath_len = efidp_size(devpath);
+
+ if(cfg.verbose) {
+ fmt::print("Entry {:04X} devpath: ", bootnum);
+ print_devpath(devpath, devpath_len);
+ }
// Must be at start, we use position in derive() to match extraneous ones from cmdline
std::string templine{};
@@ 178,17 157,17 @@ std::optional<std::string> klapki::context::context::save(const config & cfg, st
// ssize_t dp_size, unsigned char *description,
// uint8_t *optional_data,
// size_t optional_data_size)
- bent->second.load_option_len = efi_loadopt_create(nullptr, 0, //
- bent->second.attributes, //
- reinterpret_cast<efidp_data *>(devpath.data()), devpath.size(), //
- reinterpret_cast<unsigned char *>(kern.description.data()), //
+ bent->second.load_option_len = efi_loadopt_create(nullptr, 0, //
+ bent->second.attributes, //
+ devpath, devpath_len, //
+ reinterpret_cast<unsigned char *>(kern.description.data()), //
reinterpret_cast<std::uint8_t *>(cmdline.data()), cmdline.size() * sizeof(std::uint16_t));
bent->second.load_option = std::shared_ptr<std::uint8_t[]>{new std::uint8_t[bent->second.load_option_len]};
- if(efi_loadopt_create(bent->second.load_option.get(), bent->second.load_option_len, //
- bent->second.attributes, //
- reinterpret_cast<efidp_data *>(devpath.data()), devpath.size(), //
- reinterpret_cast<unsigned char *>(kern.description.data()), //
+ if(efi_loadopt_create(bent->second.load_option.get(), bent->second.load_option_len, //
+ bent->second.attributes, //
+ devpath, devpath_len, //
+ reinterpret_cast<unsigned char *>(kern.description.data()), //
reinterpret_cast<std::uint8_t *>(cmdline.data()), cmdline.size() * sizeof(std::uint16_t)) < 0)
return fmt::format("Making load option for {:04X}: {}", bootnum, strerror(errno));
@@ 111,11 111,6 @@ std::variant<klapki::state::state, std::string> klapki::state::state::load(const
bool have_our_config = false;
if(int res = iterate_efi_vars([&](auto && guid, auto name) {
- char id_guid_[36 + 2 + 1];
- auto id_guid = id_guid_;
- efi_guid_to_id_guid(&guid, &id_guid);
- // fmt::print("{}\t\t{}\t\t{}\n", id_guid, guid == efi_guid_global, name);
-
if(is_boot_entry(guid, name))
boot_entries.emplace_back(std::strtoul(name + std::strlen("Boot"), nullptr, 16));
else if(is_boot_order(guid, name))
@@ 128,22 123,22 @@ std::variant<klapki::state::state, std::string> klapki::state::state::load(const
boot_order_flat bord{};
if(have_boot_order) {
- if(int res = get_boot_order(bord))
- return fmt::format("{}: klapki::state::state::load(): getting BootOrder: {}", argv0, strerror(res));
+ if(get_boot_order(bord) < 0)
+ return fmt::format("{}: klapki::state::state::load(): getting BootOrder: {}", argv0, strerror(errno));
} else
fmt::print(stderr, "{}: no BootOrder?\n", argv0);
std::map<std::uint16_t, boot_entry> entries;
for(auto bent : boot_entries)
- if(int res = get_boot_entry(entries, bent))
- return fmt::format("{}: klapki::state::state::load(): getting Boot{:04X}: {}", argv0, bent, strerror(res));
+ if(get_boot_entry(entries, bent) < 0)
+ return fmt::format("{}: klapki::state::state::load(): getting Boot{:04X}: {}", argv0, bent, strerror(errno));
stated_config statecfg{};
if(have_our_config) {
- if(int res = get_our_config(statecfg, us))
- return fmt::format("{}: klapki::state::state::load(): getting {}-{}: {}", argv0, efi_guid_klapki_s, us, strerror(res));
+ if(get_our_config(statecfg, us) < 0)
+ return fmt::format("{}: klapki::state::state::load(): getting {}-{}: {}", argv0, efi_guid_klapki_s, us, strerror(errno));
} else
fmt::print(stderr, "{}: no config for this host ({}) found; going to the top\n", argv0, us);