]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
19ce38ce | 2 | |
69a283c5 | 3 | #include "alloc-util.h" |
19ce38ce | 4 | #include "gpt.h" |
dd894023 | 5 | #include "string-table.h" |
19ce38ce | 6 | #include "string-util.h" |
22a0a36e | 7 | #include "utf8.h" |
19ce38ce | 8 | |
d42e4fa2 | 9 | /* Gently push people towards defining GPT type UUIDs for all architectures we know */ |
92e72028 ZJS |
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) | |
d42e4fa2 LP |
16 | #pragma message "Please define GPT partition types for your architecture." |
17 | #endif | |
18 | ||
dd894023 DDM |
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, | |
dd894023 | 27 | PARTITION_USR, |
dd894023 | 28 | PARTITION_ROOT_VERITY, |
dd894023 | 29 | PARTITION_USR_VERITY, |
dd894023 | 30 | PARTITION_ROOT_VERITY_SIG, |
22e932f4 | 31 | PARTITION_USR_VERITY_SIG); |
dd894023 DDM |
32 | } |
33 | ||
34 | PartitionDesignator partition_verity_of(PartitionDesignator p) { | |
35 | switch (p) { | |
36 | ||
37 | case PARTITION_ROOT: | |
38 | return PARTITION_ROOT_VERITY; | |
39 | ||
dd894023 DDM |
40 | case PARTITION_USR: |
41 | return PARTITION_USR_VERITY; | |
42 | ||
dd894023 DDM |
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 | ||
dd894023 DDM |
54 | case PARTITION_USR: |
55 | return PARTITION_USR_VERITY_SIG; | |
56 | ||
dd894023 DDM |
57 | default: |
58 | return _PARTITION_DESIGNATOR_INVALID; | |
59 | } | |
60 | } | |
61 | ||
86adf4a5 LP |
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 | } | |
dd894023 | 89 | |
3cde36ff | 90 | static const char *const partition_designator_table[_PARTITION_DESIGNATOR_MAX] = { |
dd894023 | 91 | [PARTITION_ROOT] = "root", |
dd894023 | 92 | [PARTITION_USR] = "usr", |
dd894023 DDM |
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", | |
dd894023 | 99 | [PARTITION_USR_VERITY] = "usr-verity", |
dd894023 | 100 | [PARTITION_ROOT_VERITY_SIG] = "root-verity-sig", |
dd894023 | 101 | [PARTITION_USR_VERITY_SIG] = "usr-verity-sig", |
dd894023 DDM |
102 | [PARTITION_TMP] = "tmp", |
103 | [PARTITION_VAR] = "var", | |
dd894023 DDM |
104 | }; |
105 | ||
106 | DEFINE_STRING_TABLE_LOOKUP(partition_designator, PartitionDesignator); | |
107 | ||
3cde36ff | 108 | static const char *const partition_mountpoint_table[_PARTITION_DESIGNATOR_MAX] = { |
58b4ad76 | 109 | [PARTITION_ROOT] = "/\0", |
58b4ad76 | 110 | [PARTITION_USR] = "/usr\0", |
58b4ad76 DDM |
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 | ||
0ecfcc97 | 119 | DEFINE_STRING_TABLE_LOOKUP_TO_STRING(partition_mountpoint, PartitionDesignator); |
58b4ad76 | 120 | |
a7f787d6 | 121 | #define _GPT_ARCH_SEXTET(arch, name) \ |
22e932f4 DDM |
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 } | |
a7f787d6 | 128 | |
08a2bb7b LB |
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 | ||
dc972b07 | 138 | const GptPartitionType gpt_partition_type_table[] = { |
6b47cc98 ZJS |
139 | _GPT_ARCH_SEXTET(ALPHA, "alpha"), |
140 | _GPT_ARCH_SEXTET(ARC, "arc"), | |
a7f787d6 | 141 | _GPT_ARCH_SEXTET(ARM, "arm"), |
8340d500 | 142 | _GPT_ARCH_SEXTET(ARM, "armv7l"), /* Alias: must be listed after arm */ |
16bcaebc | 143 | _GPT_ARCH_SEXTET(ARM64, "arm64"), |
08a2bb7b | 144 | _GPT_ARCH_SEXTET(ARM64, "aarch64"), /* Alias: must be listed after arm64 */ |
a7f787d6 | 145 | _GPT_ARCH_SEXTET(IA64, "ia64"), |
0444a6e4 | 146 | _GPT_ARCH_SEXTET(LOONGARCH64, "loongarch64"), |
f0b151ce | 147 | _GPT_ARCH_SEXTET(LOONGARCH64, "loong64"), /* Alias: must be listed after loongarch64 */ |
7c6dd200 RH |
148 | _GPT_ARCH_SEXTET(MIPS, "mips"), |
149 | _GPT_ARCH_SEXTET(MIPS64, "mips64"), | |
6b47cc98 | 150 | _GPT_ARCH_SEXTET(MIPS_LE, "mips-le"), |
f0b151ce | 151 | _GPT_ARCH_SEXTET(MIPS_LE, "mipsel"), /* Alias: must be listed after mips-le */ |
6b47cc98 | 152 | _GPT_ARCH_SEXTET(MIPS64_LE, "mips64-le"), |
f0b151ce | 153 | _GPT_ARCH_SEXTET(MIPS64_LE, "mips64el"), /* Alias: must be listed after mips64-le */ |
5a9276f6 | 154 | _GPT_ARCH_SEXTET(PARISC, "parisc"), |
f0b151ce | 155 | _GPT_ARCH_SEXTET(PARISC, "hppa"), /* Alias: must be listed after parisc */ |
6b47cc98 ZJS |
156 | _GPT_ARCH_SEXTET(PPC, "ppc"), |
157 | _GPT_ARCH_SEXTET(PPC64, "ppc64"), | |
655eb073 | 158 | _GPT_ARCH_SEXTET(PPC64_LE, "ppc64-le"), |
8340d500 | 159 | _GPT_ARCH_SEXTET(PPC64_LE, "ppc64le"), /* Alias: must be listed after ppc64-le */ |
f0b151ce | 160 | _GPT_ARCH_SEXTET(PPC64_LE, "ppc64el"), /* Alias: must be listed after ppc64-le */ |
a7f787d6 ZJS |
161 | _GPT_ARCH_SEXTET(RISCV32, "riscv32"), |
162 | _GPT_ARCH_SEXTET(RISCV64, "riscv64"), | |
6b47cc98 ZJS |
163 | _GPT_ARCH_SEXTET(S390, "s390"), |
164 | _GPT_ARCH_SEXTET(S390X, "s390x"), | |
165 | _GPT_ARCH_SEXTET(TILEGX, "tilegx"), | |
a7f787d6 | 166 | _GPT_ARCH_SEXTET(X86, "x86"), |
f0b151ce LB |
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 */ | |
a7f787d6 | 171 | _GPT_ARCH_SEXTET(X86_64, "x86-64"), |
8340d500 | 172 | _GPT_ARCH_SEXTET(X86_64, "x86_64"), /* Alias: must be listed after x86-64 */ |
08a2bb7b | 173 | _GPT_ARCH_SEXTET(X86_64, "amd64"), /* Alias: must be listed after x86-64 */ |
92e72028 | 174 | #ifdef SD_GPT_ROOT_NATIVE |
dd894023 DDM |
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 }, | |
19ce38ce | 181 | #endif |
92e72028 | 182 | #ifdef SD_GPT_ROOT_SECONDARY |
5249e953 DDM |
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 }, | |
19ce38ce | 189 | #endif |
a7f787d6 | 190 | |
dd894023 DDM |
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 }, | |
df655bf3 DDM |
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 }, | |
dc972b07 | 200 | {} |
19ce38ce ZJS |
201 | }; |
202 | ||
cc97a3a5 LP |
203 | static const GptPartitionType *gpt_partition_type_find_by_uuid(sd_id128_t id) { |
204 | ||
716a413a DDM |
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; | |
19ce38ce ZJS |
208 | |
209 | return NULL; | |
210 | } | |
211 | ||
bfd5a068 | 212 | const char* gpt_partition_type_uuid_to_string(sd_id128_t id) { |
cc97a3a5 LP |
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 | ||
bfd5a068 | 222 | const char* gpt_partition_type_uuid_to_string_harder( |
19ce38ce | 223 | sd_id128_t id, |
b7416360 | 224 | char buffer[static SD_ID128_UUID_STRING_MAX]) { |
19ce38ce ZJS |
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 | ||
b7416360 | 234 | return sd_id128_to_uuid_string(id, buffer); |
19ce38ce ZJS |
235 | } |
236 | ||
22e932f4 | 237 | int gpt_partition_type_from_string(const char *s, GptPartitionType *ret) { |
08a2bb7b | 238 | sd_id128_t id = SD_ID128_NULL; |
22e932f4 DDM |
239 | int r; |
240 | ||
19ce38ce | 241 | assert(s); |
19ce38ce | 242 | |
716a413a DDM |
243 | FOREACH_ARRAY(t, gpt_partition_type_table, ELEMENTSOF(gpt_partition_type_table) - 1) |
244 | if (streq(s, t->name)) { | |
08a2bb7b LB |
245 | /* Don't return immediately, instead re-resolve by UUID so that we can support |
246 | * aliases like aarch64 -> arm64 transparently. */ | |
716a413a | 247 | id = t->uuid; |
08a2bb7b | 248 | break; |
19ce38ce ZJS |
249 | } |
250 | ||
08a2bb7b LB |
251 | if (sd_id128_is_null(id)) { |
252 | r = sd_id128_from_string(s, &id); | |
253 | if (r < 0) | |
254 | return r; | |
255 | } | |
22e932f4 DDM |
256 | |
257 | if (ret) | |
258 | *ret = gpt_partition_type_from_uuid(id); | |
259 | ||
260 | return 0; | |
19ce38ce | 261 | } |
22a0a36e | 262 | |
7767b83f DDM |
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 | ||
51d1c8f2 | 275 | Architecture gpt_partition_type_uuid_to_arch(sd_id128_t id) { |
cc97a3a5 LP |
276 | const GptPartitionType *pt; |
277 | ||
278 | pt = gpt_partition_type_find_by_uuid(id); | |
279 | if (!pt) | |
280 | return _ARCHITECTURE_INVALID; | |
51d1c8f2 | 281 | |
cc97a3a5 | 282 | return pt->arch; |
51d1c8f2 DDM |
283 | } |
284 | ||
22a0a36e LP |
285 | int gpt_partition_label_valid(const char *s) { |
286 | _cleanup_free_ char16_t *recoded = NULL; | |
287 | ||
ba091282 | 288 | recoded = utf8_to_utf16(s, SIZE_MAX); |
22a0a36e LP |
289 | if (!recoded) |
290 | return -ENOMEM; | |
291 | ||
6001df65 | 292 | return char16_strlen(recoded) <= GPT_LABEL_MAX; |
22a0a36e | 293 | } |
e81acfd2 | 294 | |
22e932f4 | 295 | GptPartitionType gpt_partition_type_from_uuid(sd_id128_t id) { |
cc97a3a5 LP |
296 | const GptPartitionType *pt; |
297 | ||
298 | pt = gpt_partition_type_find_by_uuid(id); | |
299 | if (pt) | |
300 | return *pt; | |
6ddd80ae | 301 | |
dd894023 DDM |
302 | return (GptPartitionType) { |
303 | .uuid = id, | |
304 | .arch = _ARCHITECTURE_INVALID, | |
305 | .designator = _PARTITION_DESIGNATOR_INVALID, | |
306 | }; | |
6ddd80ae DDM |
307 | } |
308 | ||
bfd5a068 | 309 | const char* gpt_partition_type_mountpoint_nulstr(GptPartitionType type) { |
22e932f4 | 310 | return partition_mountpoint_to_string(type.designator); |
e81acfd2 LP |
311 | } |
312 | ||
22e932f4 DDM |
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 */ | |
dd894023 | 318 | PARTITION_ROOT_VERITY, |
22e932f4 DDM |
319 | PARTITION_USR_VERITY, |
320 | PARTITION_HOME, | |
321 | PARTITION_SRV, | |
322 | PARTITION_VAR, | |
323 | PARTITION_TMP, | |
324 | PARTITION_XBOOTLDR); | |
e81acfd2 LP |
325 | } |
326 | ||
22e932f4 DDM |
327 | bool gpt_partition_type_knows_growfs(GptPartitionType type) { |
328 | return IN_SET(type.designator, | |
329 | PARTITION_ROOT, | |
dd894023 | 330 | PARTITION_USR, |
22e932f4 DDM |
331 | PARTITION_HOME, |
332 | PARTITION_SRV, | |
333 | PARTITION_VAR, | |
334 | PARTITION_TMP, | |
335 | PARTITION_XBOOTLDR); | |
e81acfd2 LP |
336 | } |
337 | ||
22e932f4 DDM |
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, | |
dd894023 | 343 | PARTITION_USR_VERITY, |
22e932f4 DDM |
344 | PARTITION_HOME, |
345 | PARTITION_SRV, | |
346 | PARTITION_VAR, | |
347 | PARTITION_TMP, | |
348 | PARTITION_XBOOTLDR, | |
349 | PARTITION_SWAP); | |
ff0771bf | 350 | } |
6b0651df | 351 | |
90a25577 DDM |
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 | ||
6b0651df LP |
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 | } |