1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "extract-word.h"
8 #include "id128-util.h"
9 #include "parse-util.h"
10 #include "stdio-util.h"
11 #include "string-util.h"
12 #include "sysupdate-partition.h"
14 void partition_info_destroy(PartitionInfo
*p
) {
17 p
->label
= mfree(p
->label
);
18 p
->device
= mfree(p
->device
);
21 static int fdisk_partition_get_attrs_as_uint64(
22 struct fdisk_partition
*pa
,
32 /* Retrieve current flags as uint64_t mask */
34 a
= fdisk_partition_get_attrs(pa
);
41 _cleanup_free_
char *word
= NULL
;
43 r
= extract_first_word(&a
, &word
, ",", EXTRACT_DONT_COALESCE_SEPARATORS
);
49 if (streq(word
, "RequiredPartition"))
50 flags
|= SD_GPT_FLAG_REQUIRED_PARTITION
;
51 else if (streq(word
, "NoBlockIOProtocol"))
52 flags
|= SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL
;
53 else if (streq(word
, "LegacyBIOSBootable"))
54 flags
|= SD_GPT_FLAG_LEGACY_BIOS_BOOTABLE
;
59 /* Drop "GUID" prefix if specified */
60 e
= startswith(word
, "GUID:") ?: word
;
62 if (safe_atou(e
, &u
) < 0) {
63 log_debug("Unknown partition flag '%s', ignoring.", word
);
67 if (u
>= sizeof(flags
)*8) { /* partition flags on GPT are 64-bit. Let's ignore any further
68 bits should libfdisk report them */
69 log_debug("Partition flag above bit 63 (%s), ignoring.", word
);
73 flags
|= UINT64_C(1) << u
;
81 static int fdisk_partition_set_attrs_as_uint64(
82 struct fdisk_partition
*pa
,
85 _cleanup_free_
char *attrs
= NULL
;
90 for (unsigned i
= 0; i
< sizeof(flags
) * 8; i
++) {
91 if (!FLAGS_SET(flags
, UINT64_C(1) << i
))
94 r
= strextendf_with_separator(&attrs
, ",", "%u", i
);
99 return fdisk_partition_set_attrs(pa
, strempty(attrs
));
102 int read_partition_info(
103 struct fdisk_context
*c
,
104 struct fdisk_table
*t
,
106 PartitionInfo
*ret
) {
108 _cleanup_free_
char *label_copy
= NULL
, *device
= NULL
;
110 struct fdisk_partition
*p
;
111 uint64_t start
, size
, flags
;
113 GptPartitionType type
;
121 p
= fdisk_table_get_partition(t
, i
);
123 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to read partition metadata: %m");
125 if (fdisk_partition_is_used(p
) <= 0) {
126 *ret
= (PartitionInfo
) PARTITION_INFO_NULL
;
127 return 0; /* not found! */
130 if (fdisk_partition_has_partno(p
) <= 0 ||
131 fdisk_partition_has_start(p
) <= 0 ||
132 fdisk_partition_has_size(p
) <= 0)
133 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Found a partition without a number, position or size.");
135 partno
= fdisk_partition_get_partno(p
);
137 start
= fdisk_partition_get_start(p
);
138 assert(start
<= UINT64_MAX
/ 512U);
141 size
= fdisk_partition_get_size(p
);
142 assert(size
<= UINT64_MAX
/ 512U);
145 label
= fdisk_partition_get_name(p
);
147 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Found a partition without a label.");
149 r
= fdisk_partition_get_type_as_id128(p
, &ptid
);
151 return log_error_errno(r
, "Failed to read partition type UUID: %m");
153 r
= fdisk_partition_get_uuid_as_id128(p
, &id
);
155 return log_error_errno(r
, "Failed to read partition UUID: %m");
157 r
= fdisk_partition_get_attrs_as_uint64(p
, &flags
);
159 return log_error_errno(r
, "Failed to get partition flags: %m");
161 r
= fdisk_partition_to_string(p
, c
, FDISK_FIELD_DEVICE
, &device
);
163 return log_error_errno(r
, "Failed to get partition device name: %m");
165 label_copy
= strdup(label
);
169 type
= gpt_partition_type_from_uuid(ptid
);
171 *ret
= (PartitionInfo
) {
178 .label
= TAKE_PTR(label_copy
),
179 .device
= TAKE_PTR(device
),
180 .no_auto
= FLAGS_SET(flags
, SD_GPT_FLAG_NO_AUTO
) && gpt_partition_type_knows_no_auto(type
),
181 .read_only
= FLAGS_SET(flags
, SD_GPT_FLAG_READ_ONLY
) && gpt_partition_type_knows_read_only(type
),
182 .growfs
= FLAGS_SET(flags
, SD_GPT_FLAG_GROWFS
) && gpt_partition_type_knows_growfs(type
),
185 return 1; /* found! */
188 int find_suitable_partition(
191 sd_id128_t
*partition_type
,
192 PartitionInfo
*ret
) {
194 _cleanup_(partition_info_destroy
) PartitionInfo smallest
= PARTITION_INFO_NULL
;
195 _cleanup_(fdisk_unref_contextp
) struct fdisk_context
*c
= NULL
;
196 _cleanup_(fdisk_unref_tablep
) struct fdisk_table
*t
= NULL
;
203 c
= fdisk_new_context();
207 r
= fdisk_assign_device(c
, device
, /* readonly= */ true);
209 return log_error_errno(r
, "Failed to open device '%s': %m", device
);
211 if (!fdisk_is_labeltype(c
, FDISK_DISKLABEL_GPT
))
212 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
), "Disk %s has no GPT disk label, not suitable.", device
);
214 r
= fdisk_get_partitions(c
, &t
);
216 return log_error_errno(r
, "Failed to acquire partition table: %m");
218 n_partitions
= fdisk_table_get_nents(t
);
219 for (size_t i
= 0; i
< n_partitions
; i
++) {
220 _cleanup_(partition_info_destroy
) PartitionInfo pinfo
= PARTITION_INFO_NULL
;
222 r
= read_partition_info(c
, t
, i
, &pinfo
);
225 if (r
== 0) /* not assigned */
228 /* Filter out non-matching partition types */
229 if (partition_type
&& !sd_id128_equal(pinfo
.type
, *partition_type
))
232 if (!streq_ptr(pinfo
.label
, "_empty")) /* used */
235 if (space
!= UINT64_MAX
&& pinfo
.size
< space
) /* too small */
238 if (smallest
.partno
!= SIZE_MAX
&& smallest
.size
<= pinfo
.size
) /* already found smaller */
242 pinfo
= (PartitionInfo
) PARTITION_INFO_NULL
;
245 if (smallest
.partno
== SIZE_MAX
)
246 return log_error_errno(SYNTHETIC_ERRNO(ENOSPC
), "No available partition of a suitable size found.");
249 smallest
= (PartitionInfo
) PARTITION_INFO_NULL
;
256 const PartitionInfo
*info
,
257 PartitionChange change
) {
259 _cleanup_(fdisk_unref_partitionp
) struct fdisk_partition
*pa
= NULL
;
260 _cleanup_(fdisk_unref_contextp
) struct fdisk_context
*c
= NULL
;
261 bool tweak_no_auto
, tweak_read_only
, tweak_growfs
;
262 GptPartitionType type
;
267 assert(change
<= _PARTITION_CHANGE_MAX
);
269 if (change
== 0) /* Nothing to do */
272 c
= fdisk_new_context();
276 r
= fdisk_assign_device(c
, device
, /* readonly= */ false);
278 return log_error_errno(r
, "Failed to open device '%s': %m", device
);
280 assert_se((fd
= fdisk_get_devfd(c
)) >= 0);
282 /* Make sure udev doesn't read the device while we make changes (this lock is released automatically
283 * by the kernel when the fd is closed, i.e. when the fdisk context is freed, hence no explicit
284 * unlock by us here anywhere.) */
285 if (flock(fd
, LOCK_EX
) < 0)
286 return log_error_errno(errno
, "Failed to lock block device '%s': %m", device
);
288 if (!fdisk_is_labeltype(c
, FDISK_DISKLABEL_GPT
))
289 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON
), "Disk %s has no GPT disk label, not suitable.", device
);
291 r
= fdisk_get_partition(c
, info
->partno
, &pa
);
293 return log_error_errno(r
, "Failed to read partition %zu of GPT label of '%s': %m", info
->partno
, device
);
295 if (change
& PARTITION_LABEL
) {
296 r
= fdisk_partition_set_name(pa
, info
->label
);
298 return log_error_errno(r
, "Failed to update partition label: %m");
301 if (change
& PARTITION_UUID
) {
302 r
= fdisk_partition_set_uuid(pa
, SD_ID128_TO_UUID_STRING(info
->uuid
));
304 return log_error_errno(r
, "Failed to update partition UUID: %m");
307 type
= gpt_partition_type_from_uuid(info
->type
);
309 /* Tweak the read-only flag, but only if supported by the partition type */
311 FLAGS_SET(change
, PARTITION_NO_AUTO
) &&
312 gpt_partition_type_knows_no_auto(type
);
314 FLAGS_SET(change
, PARTITION_READ_ONLY
) &&
315 gpt_partition_type_knows_read_only(type
);
317 FLAGS_SET(change
, PARTITION_GROWFS
) &&
318 gpt_partition_type_knows_growfs(type
);
320 if (change
& PARTITION_FLAGS
) {
323 /* Update the full flags parameter, and import the read-only flag into it */
327 SET_FLAG(flags
, SD_GPT_FLAG_NO_AUTO
, info
->no_auto
);
329 SET_FLAG(flags
, SD_GPT_FLAG_READ_ONLY
, info
->read_only
);
331 SET_FLAG(flags
, SD_GPT_FLAG_GROWFS
, info
->growfs
);
333 r
= fdisk_partition_set_attrs_as_uint64(pa
, flags
);
335 return log_error_errno(r
, "Failed to update partition flags: %m");
337 } else if (tweak_no_auto
|| tweak_read_only
|| tweak_growfs
) {
338 uint64_t old_flags
, new_flags
;
340 /* So we aren't supposed to update the full flags parameter, but we are supposed to update
341 * the RO flag of it. */
343 r
= fdisk_partition_get_attrs_as_uint64(pa
, &old_flags
);
345 return log_error_errno(r
, "Failed to get old partition flags: %m");
347 new_flags
= old_flags
;
349 SET_FLAG(new_flags
, SD_GPT_FLAG_NO_AUTO
, info
->no_auto
);
351 SET_FLAG(new_flags
, SD_GPT_FLAG_READ_ONLY
, info
->read_only
);
353 SET_FLAG(new_flags
, SD_GPT_FLAG_GROWFS
, info
->growfs
);
355 if (new_flags
!= old_flags
) {
356 r
= fdisk_partition_set_attrs_as_uint64(pa
, new_flags
);
358 return log_error_errno(r
, "Failed to update partition flags: %m");
362 r
= fdisk_set_partition(c
, info
->partno
, pa
);
364 return log_error_errno(r
, "Failed to update partition: %m");
366 r
= fdisk_write_disklabel(c
);
368 return log_error_errno(r
, "Failed to write updated partition table: %m");