~nabijaczleweli/klapki.deb

ref: 50de836242adb578bc1bb6c288ddc450c4a4765c klapki.deb/src/state_commit.cpp -rw-r--r-- 4.7 KiB
50de8362наб Fix upstream homepage link 1 year, 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// The MIT License (MIT)

// Copyright (c) 2020 наб <nabijaczleweli@nabijaczleweli.xyz>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


#include "efi.hpp"
#include "state.hpp"
#include <fmt/format.h>
extern "C" {
#include <efivar/efivar.h>
// Doesn't include efivar.h:
#include <efivar/efivar-guids.h>
}


#define TRY_OPT(...)              \
	if(auto err = __VA_ARGS__; err) \
		return err;


std::optional<std::string> klapki::state::state::commit(std::string_view us, const state & original_state) const {
	if(this->statecfg != original_state.statecfg) {
		fmt::print("Updating state config\n");

		auto statecfg_raw = this->statecfg.serialise();

		// This does fail if the state is too big. Dunno what to do about it
		if(efi_set_variable(klapki::efi_guid_klapki, us.data(), statecfg_raw.data(), statecfg_raw.size(),
		                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0600) < 0)
			return fmt::format("Couldn't write new state config ({} bytes): {}", statecfg_raw.size(), strerror(errno));
	}


	TRY_OPT(std::visit(klapki::overload{
	                       [&](const klapki::state::boot_order_flat & now, const klapki::state::boot_order_flat & orig) -> std::optional<std::string> {
		                       if(now.order_cnt == orig.order_cnt && !std::memcmp(now.order.get(), orig.order.get(), now.order_cnt * sizeof(now.order[0])))
			                       return std::nullopt;

		                       fmt::print("Updating boot order\n");

		                       if(efi_set_variable(efi_guid_global, "BootOrder", reinterpret_cast<std::uint8_t *>(now.order.get()),
		                                           now.order_cnt * sizeof(now.order[0]),
		                                           EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0600) < 0)
			                       return fmt::format("Couldn't write new boot order: {}", strerror(errno));
		                       return {};
	                       },
	                       [&](const auto &, const auto &) -> std::optional<std::string> {
		                       throw __func__;  // nothing can be done now. context::save() should've set this, and the original state is also flat
	                       },
	                   },
	                   this->order, original_state.order));


	const auto max_bootnum = std::max(this->entries.rbegin()->first, original_state.entries.rbegin()->first);
	for(std::size_t i = 0; i <= max_bootnum; ++i) {
		const auto now  = this->entries.find(i);
		const auto orig = original_state.entries.find(i);

		if(now == std::end(this->entries) && orig == std::end(original_state.entries))  // gap
			continue;
		else if(now == std::end(this->entries) && orig != std::end(original_state.entries)) {  // deleted
			fmt::print("Deleting entry {:04X}\n", i);
			if(efi_del_variable(efi_guid_global, fmt::format("Boot{:04X}", i).c_str()) < 0)
				return fmt::format("Couldn't delete entry for {:04X}: {}", i, strerror(errno));
			continue;
		} else if(now != std::end(this->entries) && orig == std::end(original_state.entries))  // new
			;
		else if(now->second.load_option_len == orig->second.load_option_len &&
		        !std::memcmp(now->second.load_option.get(), orig->second.load_option.get(), now->second.load_option_len))  // exists in both, only write if changed
			continue;

		fmt::print("{} entry {:04X}\n", (orig == std::end(original_state.entries)) ? "Writing" : "Updating", i);
		if(efi_set_variable(efi_guid_global, fmt::format("Boot{:04X}", i).c_str(), now->second.load_option.get(), now->second.load_option_len,
		                    now->second.attributes, 0600) < 0)
			return fmt::format("Couldn't write entry {:04X}: {}", i, strerror(errno));
	}

	return {};
}