]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/gpt.c
cryptenroll: allow to use a public key on a token
[thirdparty/systemd.git] / src / shared / gpt.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "gpt.h"
4 #include "string-table.h"
5 #include "string-util.h"
6 #include "utf8.h"
7
8 /* Gently push people towards defining GPT type UUIDs for all architectures we know */
9 #if !defined(SD_GPT_ROOT_NATIVE) || \
10 !defined(SD_GPT_ROOT_NATIVE_VERITY) || \
11 !defined(SD_GPT_ROOT_NATIVE_VERITY_SIG) || \
12 !defined(SD_GPT_USR_NATIVE) || \
13 !defined(SD_GPT_USR_NATIVE_VERITY) || \
14 !defined(SD_GPT_USR_NATIVE_VERITY_SIG)
15 #pragma message "Please define GPT partition types for your architecture."
16 #endif
17
18 bool partition_designator_is_versioned(PartitionDesignator d) {
19 /* Returns true for all designators where we want to support a concept of "versioning", i.e. which
20 * likely contain software binaries (or hashes thereof) that make sense to be versioned as a
21 * whole. We use this check to automatically pick the newest version of these partitions, by version
22 * comparing the partition labels. */
23
24 return IN_SET(d,
25 PARTITION_ROOT,
26 PARTITION_USR,
27 PARTITION_ROOT_VERITY,
28 PARTITION_USR_VERITY,
29 PARTITION_ROOT_VERITY_SIG,
30 PARTITION_USR_VERITY_SIG);
31 }
32
33 PartitionDesignator partition_verity_of(PartitionDesignator p) {
34 switch (p) {
35
36 case PARTITION_ROOT:
37 return PARTITION_ROOT_VERITY;
38
39 case PARTITION_USR:
40 return PARTITION_USR_VERITY;
41
42 default:
43 return _PARTITION_DESIGNATOR_INVALID;
44 }
45 }
46
47 PartitionDesignator partition_verity_sig_of(PartitionDesignator p) {
48 switch (p) {
49
50 case PARTITION_ROOT:
51 return PARTITION_ROOT_VERITY_SIG;
52
53 case PARTITION_USR:
54 return PARTITION_USR_VERITY_SIG;
55
56 default:
57 return _PARTITION_DESIGNATOR_INVALID;
58 }
59 }
60
61 PartitionDesignator partition_verity_to_data(PartitionDesignator d) {
62 switch (d) {
63
64 case PARTITION_ROOT_VERITY:
65 return PARTITION_ROOT;
66
67 case PARTITION_USR_VERITY:
68 return PARTITION_USR;
69
70 default:
71 return _PARTITION_DESIGNATOR_INVALID;
72 }
73 }
74
75 PartitionDesignator partition_verity_sig_to_data(PartitionDesignator d) {
76 switch (d) {
77
78 case PARTITION_ROOT_VERITY_SIG:
79 return PARTITION_ROOT;
80
81 case PARTITION_USR_VERITY_SIG:
82 return PARTITION_USR;
83
84 default:
85 return _PARTITION_DESIGNATOR_INVALID;
86 }
87 }
88
89 static const char *const partition_designator_table[_PARTITION_DESIGNATOR_MAX] = {
90 [PARTITION_ROOT] = "root",
91 [PARTITION_USR] = "usr",
92 [PARTITION_HOME] = "home",
93 [PARTITION_SRV] = "srv",
94 [PARTITION_ESP] = "esp",
95 [PARTITION_XBOOTLDR] = "xbootldr",
96 [PARTITION_SWAP] = "swap",
97 [PARTITION_ROOT_VERITY] = "root-verity",
98 [PARTITION_USR_VERITY] = "usr-verity",
99 [PARTITION_ROOT_VERITY_SIG] = "root-verity-sig",
100 [PARTITION_USR_VERITY_SIG] = "usr-verity-sig",
101 [PARTITION_TMP] = "tmp",
102 [PARTITION_VAR] = "var",
103 };
104
105 DEFINE_STRING_TABLE_LOOKUP(partition_designator, PartitionDesignator);
106
107 static const char *const partition_mountpoint_table[_PARTITION_DESIGNATOR_MAX] = {
108 [PARTITION_ROOT] = "/\0",
109 [PARTITION_USR] = "/usr\0",
110 [PARTITION_HOME] = "/home\0",
111 [PARTITION_SRV] = "/srv\0",
112 [PARTITION_ESP] = "/efi\0/boot\0",
113 [PARTITION_XBOOTLDR] = "/boot\0",
114 [PARTITION_TMP] = "/var/tmp\0",
115 [PARTITION_VAR] = "/var\0",
116 };
117
118 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(partition_mountpoint, PartitionDesignator);
119
120 #define _GPT_ARCH_SEXTET(arch, name) \
121 { SD_GPT_ROOT_##arch, "root-" name, ARCHITECTURE_##arch, .designator = PARTITION_ROOT }, \
122 { SD_GPT_ROOT_##arch##_VERITY, "root-" name "-verity", ARCHITECTURE_##arch, .designator = PARTITION_ROOT_VERITY }, \
123 { SD_GPT_ROOT_##arch##_VERITY_SIG, "root-" name "-verity-sig", ARCHITECTURE_##arch, .designator = PARTITION_ROOT_VERITY_SIG }, \
124 { SD_GPT_USR_##arch, "usr-" name, ARCHITECTURE_##arch, .designator = PARTITION_USR }, \
125 { SD_GPT_USR_##arch##_VERITY, "usr-" name "-verity", ARCHITECTURE_##arch, .designator = PARTITION_USR_VERITY }, \
126 { SD_GPT_USR_##arch##_VERITY_SIG, "usr-" name "-verity-sig", ARCHITECTURE_##arch, .designator = PARTITION_USR_VERITY_SIG }
127
128 /* Two special cases: alias aarch64 to arm64, and amd64 to x86-64. The DSP mixes debianisms and CPUisms: for
129 * x86, it uses x86 and x86_64, but for aarch64 it uses arm64. This is confusing, and leads to issues for
130 * callers that have to know which -ism to use for which architecture. But we also don't really want to
131 * change the spec and add new partition labels, so add a user-friendly aliasing here, so that both are
132 * accepted but the end result on disk (ie: the partition label).
133 * So always list the canonical name FIRST, and then any aliases later, so that we can match on aliases,
134 * but always return the canonical name. And never return directly a match on the name, always re-resolve
135 * by UUID so that the canonical entry is always found. */
136
137 const GptPartitionType gpt_partition_type_table[] = {
138 _GPT_ARCH_SEXTET(ALPHA, "alpha"),
139 _GPT_ARCH_SEXTET(ARC, "arc"),
140 _GPT_ARCH_SEXTET(ARM, "arm"),
141 _GPT_ARCH_SEXTET(ARM, "armv7l"), /* Alias: must be listed after arm */
142 _GPT_ARCH_SEXTET(ARM64, "arm64"),
143 _GPT_ARCH_SEXTET(ARM64, "aarch64"), /* Alias: must be listed after arm64 */
144 _GPT_ARCH_SEXTET(IA64, "ia64"),
145 _GPT_ARCH_SEXTET(LOONGARCH64, "loongarch64"),
146 _GPT_ARCH_SEXTET(MIPS, "mips"),
147 _GPT_ARCH_SEXTET(MIPS64, "mips64"),
148 _GPT_ARCH_SEXTET(MIPS_LE, "mips-le"),
149 _GPT_ARCH_SEXTET(MIPS64_LE, "mips64-le"),
150 _GPT_ARCH_SEXTET(PARISC, "parisc"),
151 _GPT_ARCH_SEXTET(PPC, "ppc"),
152 _GPT_ARCH_SEXTET(PPC64, "ppc64"),
153 _GPT_ARCH_SEXTET(PPC64_LE, "ppc64-le"),
154 _GPT_ARCH_SEXTET(PPC64_LE, "ppc64le"), /* Alias: must be listed after ppc64-le */
155 _GPT_ARCH_SEXTET(RISCV32, "riscv32"),
156 _GPT_ARCH_SEXTET(RISCV64, "riscv64"),
157 _GPT_ARCH_SEXTET(S390, "s390"),
158 _GPT_ARCH_SEXTET(S390X, "s390x"),
159 _GPT_ARCH_SEXTET(TILEGX, "tilegx"),
160 _GPT_ARCH_SEXTET(X86, "x86"),
161 _GPT_ARCH_SEXTET(X86_64, "x86-64"),
162 _GPT_ARCH_SEXTET(X86_64, "x86_64"), /* Alias: must be listed after x86-64 */
163 _GPT_ARCH_SEXTET(X86_64, "amd64"), /* Alias: must be listed after x86-64 */
164 #ifdef SD_GPT_ROOT_NATIVE
165 { SD_GPT_ROOT_NATIVE, "root", native_architecture(), .designator = PARTITION_ROOT },
166 { SD_GPT_ROOT_NATIVE_VERITY, "root-verity", native_architecture(), .designator = PARTITION_ROOT_VERITY },
167 { SD_GPT_ROOT_NATIVE_VERITY_SIG, "root-verity-sig", native_architecture(), .designator = PARTITION_ROOT_VERITY_SIG },
168 { SD_GPT_USR_NATIVE, "usr", native_architecture(), .designator = PARTITION_USR },
169 { SD_GPT_USR_NATIVE_VERITY, "usr-verity", native_architecture(), .designator = PARTITION_USR_VERITY },
170 { SD_GPT_USR_NATIVE_VERITY_SIG, "usr-verity-sig", native_architecture(), .designator = PARTITION_USR_VERITY_SIG },
171 #endif
172 #ifdef SD_GPT_ROOT_SECONDARY
173 { SD_GPT_ROOT_SECONDARY, "root-secondary", ARCHITECTURE_SECONDARY, .designator = PARTITION_ROOT },
174 { SD_GPT_ROOT_SECONDARY_VERITY, "root-secondary-verity", ARCHITECTURE_SECONDARY, .designator = PARTITION_ROOT_VERITY },
175 { SD_GPT_ROOT_SECONDARY_VERITY_SIG, "root-secondary-verity-sig", ARCHITECTURE_SECONDARY, .designator = PARTITION_ROOT_VERITY_SIG },
176 { SD_GPT_USR_SECONDARY, "usr-secondary", ARCHITECTURE_SECONDARY, .designator = PARTITION_USR },
177 { SD_GPT_USR_SECONDARY_VERITY, "usr-secondary-verity", ARCHITECTURE_SECONDARY, .designator = PARTITION_USR_VERITY },
178 { SD_GPT_USR_SECONDARY_VERITY_SIG, "usr-secondary-verity-sig", ARCHITECTURE_SECONDARY, .designator = PARTITION_USR_VERITY_SIG },
179 #endif
180
181 { SD_GPT_ESP, "esp", _ARCHITECTURE_INVALID, .designator = PARTITION_ESP },
182 { SD_GPT_XBOOTLDR, "xbootldr", _ARCHITECTURE_INVALID, .designator = PARTITION_XBOOTLDR },
183 { SD_GPT_SWAP, "swap", _ARCHITECTURE_INVALID, .designator = PARTITION_SWAP },
184 { SD_GPT_HOME, "home", _ARCHITECTURE_INVALID, .designator = PARTITION_HOME },
185 { SD_GPT_SRV, "srv", _ARCHITECTURE_INVALID, .designator = PARTITION_SRV },
186 { SD_GPT_VAR, "var", _ARCHITECTURE_INVALID, .designator = PARTITION_VAR },
187 { SD_GPT_TMP, "tmp", _ARCHITECTURE_INVALID, .designator = PARTITION_TMP },
188 { SD_GPT_USER_HOME, "user-home", _ARCHITECTURE_INVALID, .designator = _PARTITION_DESIGNATOR_INVALID },
189 { SD_GPT_LINUX_GENERIC, "linux-generic", _ARCHITECTURE_INVALID, .designator = _PARTITION_DESIGNATOR_INVALID },
190 {}
191 };
192
193 static const GptPartitionType *gpt_partition_type_find_by_uuid(sd_id128_t id) {
194
195 FOREACH_ARRAY(t, gpt_partition_type_table, ELEMENTSOF(gpt_partition_type_table) - 1)
196 if (sd_id128_equal(id, t->uuid))
197 return t;
198
199 return NULL;
200 }
201
202 const char *gpt_partition_type_uuid_to_string(sd_id128_t id) {
203 const GptPartitionType *pt;
204
205 pt = gpt_partition_type_find_by_uuid(id);
206 if (!pt)
207 return NULL;
208
209 return pt->name;
210 }
211
212 const char *gpt_partition_type_uuid_to_string_harder(
213 sd_id128_t id,
214 char buffer[static SD_ID128_UUID_STRING_MAX]) {
215
216 const char *s;
217
218 assert(buffer);
219
220 s = gpt_partition_type_uuid_to_string(id);
221 if (s)
222 return s;
223
224 return sd_id128_to_uuid_string(id, buffer);
225 }
226
227 int gpt_partition_type_from_string(const char *s, GptPartitionType *ret) {
228 sd_id128_t id = SD_ID128_NULL;
229 int r;
230
231 assert(s);
232
233 FOREACH_ARRAY(t, gpt_partition_type_table, ELEMENTSOF(gpt_partition_type_table) - 1)
234 if (streq(s, t->name)) {
235 /* Don't return immediately, instead re-resolve by UUID so that we can support
236 * aliases like aarch64 -> arm64 transparently. */
237 id = t->uuid;
238 break;
239 }
240
241 if (sd_id128_is_null(id)) {
242 r = sd_id128_from_string(s, &id);
243 if (r < 0)
244 return r;
245 }
246
247 if (ret)
248 *ret = gpt_partition_type_from_uuid(id);
249
250 return 0;
251 }
252
253 GptPartitionType gpt_partition_type_override_architecture(GptPartitionType type, Architecture arch) {
254 assert(arch >= 0);
255
256 FOREACH_ARRAY(t, gpt_partition_type_table, ELEMENTSOF(gpt_partition_type_table) - 1)
257 if (t->designator == type.designator && t->arch == arch)
258 return *t;
259
260 /* If we can't find an entry with the same designator and the requested architecture, just return the
261 * original partition type. */
262 return type;
263 }
264
265 Architecture gpt_partition_type_uuid_to_arch(sd_id128_t id) {
266 const GptPartitionType *pt;
267
268 pt = gpt_partition_type_find_by_uuid(id);
269 if (!pt)
270 return _ARCHITECTURE_INVALID;
271
272 return pt->arch;
273 }
274
275 int gpt_partition_label_valid(const char *s) {
276 _cleanup_free_ char16_t *recoded = NULL;
277
278 recoded = utf8_to_utf16(s, SIZE_MAX);
279 if (!recoded)
280 return -ENOMEM;
281
282 return char16_strlen(recoded) <= GPT_LABEL_MAX;
283 }
284
285 GptPartitionType gpt_partition_type_from_uuid(sd_id128_t id) {
286 const GptPartitionType *pt;
287
288 pt = gpt_partition_type_find_by_uuid(id);
289 if (pt)
290 return *pt;
291
292 return (GptPartitionType) {
293 .uuid = id,
294 .arch = _ARCHITECTURE_INVALID,
295 .designator = _PARTITION_DESIGNATOR_INVALID,
296 };
297 }
298
299 const char *gpt_partition_type_mountpoint_nulstr(GptPartitionType type) {
300 return partition_mountpoint_to_string(type.designator);
301 }
302
303 bool gpt_partition_type_knows_read_only(GptPartitionType type) {
304 return IN_SET(type.designator,
305 PARTITION_ROOT,
306 PARTITION_USR,
307 /* pretty much implied, but let's set the bit to make things really clear */
308 PARTITION_ROOT_VERITY,
309 PARTITION_USR_VERITY,
310 PARTITION_HOME,
311 PARTITION_SRV,
312 PARTITION_VAR,
313 PARTITION_TMP,
314 PARTITION_XBOOTLDR);
315 }
316
317 bool gpt_partition_type_knows_growfs(GptPartitionType type) {
318 return IN_SET(type.designator,
319 PARTITION_ROOT,
320 PARTITION_USR,
321 PARTITION_HOME,
322 PARTITION_SRV,
323 PARTITION_VAR,
324 PARTITION_TMP,
325 PARTITION_XBOOTLDR);
326 }
327
328 bool gpt_partition_type_knows_no_auto(GptPartitionType type) {
329 return IN_SET(type.designator,
330 PARTITION_ROOT,
331 PARTITION_ROOT_VERITY,
332 PARTITION_USR,
333 PARTITION_USR_VERITY,
334 PARTITION_HOME,
335 PARTITION_SRV,
336 PARTITION_VAR,
337 PARTITION_TMP,
338 PARTITION_XBOOTLDR,
339 PARTITION_SWAP);
340 }
341
342 bool gpt_header_has_signature(const GptHeader *p) {
343 assert(p);
344
345 if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
346 return false;
347
348 if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
349 return false;
350
351 if (le32toh(p->header_size) < sizeof(GptHeader))
352 return false;
353
354 if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */
355 return false;
356
357 if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */
358 return false;
359
360 return true;
361 }