1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
5 #include "string-table.h"
6 #include "string-util.h"
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."
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. */
28 PARTITION_ROOT_VERITY
,
30 PARTITION_ROOT_VERITY_SIG
,
31 PARTITION_USR_VERITY_SIG
);
34 PartitionDesignator
partition_verity_of(PartitionDesignator p
) {
38 return PARTITION_ROOT_VERITY
;
41 return PARTITION_USR_VERITY
;
44 return _PARTITION_DESIGNATOR_INVALID
;
48 PartitionDesignator
partition_verity_sig_of(PartitionDesignator p
) {
52 return PARTITION_ROOT_VERITY_SIG
;
55 return PARTITION_USR_VERITY_SIG
;
58 return _PARTITION_DESIGNATOR_INVALID
;
62 PartitionDesignator
partition_verity_to_data(PartitionDesignator d
) {
65 case PARTITION_ROOT_VERITY
:
66 return PARTITION_ROOT
;
68 case PARTITION_USR_VERITY
:
72 return _PARTITION_DESIGNATOR_INVALID
;
76 PartitionDesignator
partition_verity_sig_to_data(PartitionDesignator d
) {
79 case PARTITION_ROOT_VERITY_SIG
:
80 return PARTITION_ROOT
;
82 case PARTITION_USR_VERITY_SIG
:
86 return _PARTITION_DESIGNATOR_INVALID
;
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",
106 DEFINE_STRING_TABLE_LOOKUP(partition_designator
, PartitionDesignator
);
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",
119 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(partition_mountpoint
, PartitionDesignator
);
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 }
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. */
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
},
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
},
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
},
203 static const GptPartitionType
*gpt_partition_type_find_by_uuid(sd_id128_t id
) {
205 FOREACH_ARRAY(t
, gpt_partition_type_table
, ELEMENTSOF(gpt_partition_type_table
) - 1)
206 if (sd_id128_equal(id
, t
->uuid
))
212 const char* gpt_partition_type_uuid_to_string(sd_id128_t id
) {
213 const GptPartitionType
*pt
;
215 pt
= gpt_partition_type_find_by_uuid(id
);
222 const char* gpt_partition_type_uuid_to_string_harder(
224 char buffer
[static SD_ID128_UUID_STRING_MAX
]) {
230 s
= gpt_partition_type_uuid_to_string(id
);
234 return sd_id128_to_uuid_string(id
, buffer
);
237 int gpt_partition_type_from_string(const char *s
, GptPartitionType
*ret
) {
238 sd_id128_t id
= SD_ID128_NULL
;
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. */
251 if (sd_id128_is_null(id
)) {
252 r
= sd_id128_from_string(s
, &id
);
258 *ret
= gpt_partition_type_from_uuid(id
);
263 GptPartitionType
gpt_partition_type_override_architecture(GptPartitionType type
, Architecture arch
) {
266 FOREACH_ARRAY(t
, gpt_partition_type_table
, ELEMENTSOF(gpt_partition_type_table
) - 1)
267 if (t
->designator
== type
.designator
&& t
->arch
== arch
)
270 /* If we can't find an entry with the same designator and the requested architecture, just return the
271 * original partition type. */
275 Architecture
gpt_partition_type_uuid_to_arch(sd_id128_t id
) {
276 const GptPartitionType
*pt
;
278 pt
= gpt_partition_type_find_by_uuid(id
);
280 return _ARCHITECTURE_INVALID
;
285 int gpt_partition_label_valid(const char *s
) {
286 _cleanup_free_ char16_t
*recoded
= NULL
;
288 recoded
= utf8_to_utf16(s
, SIZE_MAX
);
292 return char16_strlen(recoded
) <= GPT_LABEL_MAX
;
295 GptPartitionType
gpt_partition_type_from_uuid(sd_id128_t id
) {
296 const GptPartitionType
*pt
;
298 pt
= gpt_partition_type_find_by_uuid(id
);
302 return (GptPartitionType
) {
304 .arch
= _ARCHITECTURE_INVALID
,
305 .designator
= _PARTITION_DESIGNATOR_INVALID
,
309 const char* gpt_partition_type_mountpoint_nulstr(GptPartitionType type
) {
310 return partition_mountpoint_to_string(type
.designator
);
313 bool gpt_partition_type_knows_read_only(GptPartitionType type
) {
314 return IN_SET(type
.designator
,
317 /* pretty much implied, but let's set the bit to make things really clear */
318 PARTITION_ROOT_VERITY
,
319 PARTITION_USR_VERITY
,
327 bool gpt_partition_type_knows_growfs(GptPartitionType type
) {
328 return IN_SET(type
.designator
,
338 bool gpt_partition_type_knows_no_auto(GptPartitionType type
) {
339 return IN_SET(type
.designator
,
341 PARTITION_ROOT_VERITY
,
343 PARTITION_USR_VERITY
,
352 bool gpt_partition_type_has_filesystem(GptPartitionType type
) {
353 return IN_SET(type
.designator
,
364 bool gpt_header_has_signature(const GptHeader
*p
) {
367 if (memcmp(p
->signature
, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
370 if (le32toh(p
->revision
) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
373 if (le32toh(p
->header_size
) < sizeof(GptHeader
))
376 if (le32toh(p
->header_size
) > 4096) /* larger than a sector? something is off… */
379 if (le64toh(p
->my_lba
) != 1) /* this sector must claim to be at sector offset 1 */