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