gpt.c revision 1.7 1 /* $NetBSD: gpt.c,v 1.7 2019/08/02 10:44:22 martin Exp $ */
2
3 /*
4 * Copyright 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30 #include "defs.h"
31 #include "mbr.h"
32 #include "md.h"
33 #include "gpt_uuid.h"
34 #include <assert.h>
35 #include <paths.h>
36 #include <sys/param.h>
37 #include <sys/ioctl.h>
38 #include <util.h>
39
40 bool gpt_parts_check(void); /* check for needed binaries */
41
42
43 /*************** GPT ************************************************/
44 /* a GPT based disk_partitions interface */
45
46 #define GUID_STR_LEN 40
47 #define GPT_PTYPE_MAX 32 /* should be > gpt type -l | wc -l */
48 #define GPT_DEV_LEN 16 /* dkNN */
49
50 #define GPT_PARTS_PER_SEC 4 /* a 512 byte sector hols 4 entries */
51 #define GPT_DEFAULT_MAX_PARTS 128
52
53 /* a usable label will be short, so we can get away with an arbitrary limit */
54 #define GPT_LABEL_LEN 96
55
56 #define GPT_ATTR_BIOSBOOT 1
57 #define GPT_ATTR_BOOTME 2
58 #define GPT_ATTR_BOOTONCE 4
59 #define GPT_ATTR_BOOTFAILED 8
60 #define GPT_ATTR_NOBLOCKIO 16
61 #define GPT_ATTR_REQUIRED 32
62
63 /* when we don't care for BIOS or UEFI boot, use the combined boot flags */
64 #define GPT_ATTR_BOOT (GPT_ATTR_BIOSBOOT|GPT_ATTR_BOOTME)
65
66 struct gpt_attr_desc {
67 const char *name;
68 uint flag;
69 };
70 static const struct gpt_attr_desc gpt_avail_attrs[] = {
71 { "biosboot", GPT_ATTR_BIOSBOOT },
72 { "bootme", GPT_ATTR_BOOTME },
73 { "bootonce", GPT_ATTR_BOOTONCE },
74 { "bootfailed", GPT_ATTR_BOOTFAILED },
75 { "noblockio", GPT_ATTR_NOBLOCKIO },
76 { "required", GPT_ATTR_REQUIRED },
77 { NULL, 0 }
78 };
79
80 struct gpt_ptype_desc {
81 struct part_type_desc gent;
82 char tid[GUID_STR_LEN];
83 uint fsflags, default_fs_type;
84 };
85
86 static const
87 struct {
88 const char *name;
89 uint fstype;
90 enum part_type ptype;
91 uint fsflags;
92 } gpt_fs_types[] = {
93 { .name = "ffs", .fstype = FS_BSDFFS, .ptype = PT_root,
94 .fsflags = GLM_LIKELY_FFS },
95 { .name = "swap", .fstype = FS_SWAP, .ptype = PT_swap },
96 { .name = "windows", .fstype = FS_MSDOS, .ptype = PT_FAT,
97 .fsflags = GLM_MAYBE_FAT32|GLM_MAYBE_NTFS },
98 { .name = "windows", .fstype = FS_NTFS, .ptype = PT_FAT,
99 .fsflags = GLM_MAYBE_FAT32|GLM_MAYBE_NTFS },
100 { .name = "efi", .fstype = FS_MSDOS, .ptype = PT_EFI_SYSTEM,
101 .fsflags = GLM_MAYBE_FAT32 },
102 { .name = "bios", .fstype = FS_MSDOS, .ptype = PT_FAT,
103 .fsflags = GLM_MAYBE_FAT32 },
104 { .name = "lfs", .fstype = FS_BSDLFS, .ptype = PT_root },
105 { .name = "linux-data", .fstype = FS_EX2FS, .ptype = PT_root },
106 { .name = "apple", .fstype = FS_HFS, .ptype = PT_unknown },
107 { .name = "ccd", .fstype = FS_CCD, .ptype = PT_unknown },
108 { .name = "cgd", .fstype = FS_CGD, .ptype = PT_unknown },
109 { .name = "raid", .fstype = FS_RAID, .ptype = PT_root },
110 { .name = "vmcore", .fstype = FS_VMKCORE, .ptype = PT_unknown },
111 { .name = "vmfs", .fstype = FS_VMFS, .ptype = PT_unknown },
112 { .name = "vmresered", .fstype = FS_VMWRESV, .ptype = PT_unknown }
113 };
114
115 static size_t gpt_ptype_cnt;
116 static struct gpt_ptype_desc gpt_ptype_descs[GPT_PTYPE_MAX];
117
118 /* similar to struct gpt_ent, but matching our needs */
119 struct gpt_part_entry {
120 const struct gpt_ptype_desc *gp_type;
121 char gp_id[GUID_STR_LEN]; /* partition guid as string */
122 daddr_t gp_start, gp_size;
123 uint gp_attr; /* various attribute bits */
124 char gp_label[GPT_LABEL_LEN]; /* user defined label */
125 char gp_dev_name[GPT_DEV_LEN]; /* name of wedge */
126 const char *last_mounted; /* last mounted if known */
127 uint fs_type, fs_sub_type; /* FS_* and maybe sub type */
128 uint gp_flags;
129 #define GPEF_ON_DISK 1 /* This entry exists on-disk */
130 #define GPEF_MODIFIED 2 /* this entry has been changed */
131 #define GPEF_WEDGE 4 /* wedge for this exists */
132 #define GPEF_RESIZED 8 /* size has changed */
133 struct gpt_part_entry *gp_next;
134 };
135
136 static const struct gpt_ptype_desc *gpt_find_native_type(
137 const struct part_type_desc *gent);
138 static const struct gpt_ptype_desc *gpt_find_guid_type(const char*);
139 static bool
140 gpt_info_to_part(struct gpt_part_entry *p, const struct disk_part_info *info,
141 const char **err_msg);
142
143 const struct disk_partitioning_scheme gpt_parts;
144 struct gpt_disk_partitions {
145 struct disk_partitions dp;
146 /*
147 * We keep a list of our current valid partitions, pointed
148 * to by "partitions".
149 * dp.num_part is the number of entries in "partitions".
150 * When partitions that have a representation on disk already
151 * are deleted, we move them to the "obsolete" list so we
152 * can issue the proper commands to remove it when writing back.
153 */
154 struct gpt_part_entry *partitions, /* current partitions */
155 *obsolete; /* deleted partitions */
156 size_t max_num_parts; /* how many entries max? */
157 size_t prologue, epilogue; /* number of sectors res. */
158 bool has_gpt; /* disk already has a GPT */
159 };
160
161 /*
162 * Init global variables from MD details
163 */
164 static void
165 gpt_md_init(bool is_boot_disk, size_t *max_parts, size_t *head, size_t *tail)
166 {
167 size_t num;
168
169 if (is_boot_disk) {
170 #ifdef MD_GPT_INITIAL_SIZE
171 #if MD_GPT_INITIAL_SIZE < 2*512
172 #error impossible small GPT prologue
173 #endif
174 num = ((MD_GPT_INITIAL_SIZE-(2*512))/512)*GPT_PARTS_PER_SEC;
175 #else
176 num = GPT_DEFAULT_MAX_PARTS;
177 #endif
178 } else {
179 num = GPT_DEFAULT_MAX_PARTS;
180 }
181 *max_parts = num;
182 *head = 2 + num/GPT_PARTS_PER_SEC;
183 *tail = 1 + num/GPT_PARTS_PER_SEC;
184 }
185
186 /*
187 * Parse a part of "gpt show" output into a struct gpt_part_entry.
188 * Output is from "show -a" format if details = false, otherwise
189 * from details for a specific partition (show -i or show -b)
190 */
191 static void
192 gpt_add_info(struct gpt_part_entry *part, const char *tag, char *val,
193 bool details)
194 {
195 char *s, *e;
196
197 if (details && strcmp(tag, "Start:") == 0) {
198 part->gp_start = strtouq(val, NULL, 10);
199 } else if (details && strcmp(tag, "Size:") == 0) {
200 part->gp_size = strtouq(val, NULL, 10);
201 } else if (details && strcmp(tag, "Type:") == 0) {
202 s = strchr(val, '(');
203 if (!s)
204 return;
205 e = strchr(s, ')');
206 if (!e)
207 return;
208 *e = 0;
209 part->gp_type = gpt_find_guid_type(s+1);
210 } else if (strcmp(tag, "TypeID:") == 0) {
211 part->gp_type = gpt_find_guid_type(val);
212 } else if (strcmp(tag, "GUID:") == 0) {
213 strlcpy(part->gp_id, val, sizeof(part->gp_id));
214 } else if (strcmp(tag, "Label:") == 0) {
215 strlcpy(part->gp_label, val, sizeof(part->gp_label));
216 } else if (strcmp(tag, "Attributes:") == 0) {
217 char *n;
218
219 while ((n = strsep(&val, ", ")) != NULL) {
220 if (*n == 0)
221 continue;
222 for (const struct gpt_attr_desc *p = gpt_avail_attrs;
223 p->name != NULL; p++) {
224 if (strcmp(p->name, n) == 0)
225 part->gp_attr |= p->flag;
226 }
227 }
228 }
229 }
230
231 static struct disk_partitions *
232 gpt_read_from_disk(const char *dev, daddr_t start, daddr_t len)
233 {
234 char diskpath[MAXPATHLEN];
235 int fd;
236
237 assert(start == 0);
238 assert(have_gpt);
239
240 if (run_program(RUN_SILENT | RUN_ERROR_OK,
241 "gpt -rq header %s", dev) != 0)
242 return NULL;
243
244 /* read the partitions */
245 int i;
246 unsigned int p_index;
247 daddr_t p_start = 0, p_size = 0, avail_start = 0, avail_size = 0,
248 disk_size = 0;
249 char *textbuf, *t, *tt, p_type[STRSIZE];
250 static const char regpart_prefix[] = "GPT part - ";
251 struct gpt_disk_partitions *parts;
252 struct gpt_part_entry *last = NULL, *add_to = NULL;
253
254 if (collect(T_OUTPUT, &textbuf, "gpt -r show -a %s 2>/dev/null", dev)
255 < 1)
256 return NULL;
257
258 /* parse output and create our list */
259 parts = calloc(1, sizeof(*parts));
260 if (parts == NULL)
261 return NULL;
262
263 (void)strtok(textbuf, "\n"); /* ignore first line */
264 while ((t = strtok(NULL, "\n")) != NULL) {
265 i = 0; p_start = 0; p_size = 0; p_index = 0;
266 p_type[0] = 0;
267 while ((tt = strsep(&t, " \t")) != NULL) {
268 if (strlen(tt) == 0)
269 continue;
270 if (i == 0) {
271 if (add_to != NULL)
272 gpt_add_info(add_to, tt, t, false);
273 p_start = strtouq(tt, NULL, 10);
274 if (p_start == 0 && add_to != NULL)
275 break;
276 else
277 add_to = NULL;
278 }
279 if (i == 1)
280 p_size = strtouq(tt, NULL, 10);
281 if (i == 2)
282 p_index = strtouq(tt, NULL, 10);
283 if (i > 2 || (i == 2 && p_index == 0)) {
284 if (p_type[0])
285 strlcat(p_type, " ", STRSIZE);
286 strlcat(p_type, tt, STRSIZE);
287 }
288 i++;
289 }
290
291 if (p_start == 0 || p_size == 0)
292 continue;
293 else if (strcmp(p_type, "Pri GPT table") == 0) {
294 avail_start = p_start + p_size;
295 parts->prologue = avail_start;
296 parts->epilogue = p_size + 1;
297 parts->max_num_parts = p_size * GPT_PARTS_PER_SEC;
298 } else if (strcmp(p_type, "Sec GPT table") == 0)
299 avail_size = p_start - avail_start;
300 else if(strcmp(p_type, "Sec GPT header") == 0)
301 disk_size = p_start + p_size;
302 else if (p_index == 0 && strlen(p_type) > 0)
303 /* Utilitary entry (PMBR, etc) */
304 continue;
305 else if (p_index == 0) {
306 /* Free space */
307 continue;
308 } else {
309 /* Usual partition */
310 tt = p_type;
311 if (strncmp(tt, regpart_prefix,
312 strlen(regpart_prefix)) == 0)
313 tt += strlen(regpart_prefix);
314
315 /* Add to our linked list */
316 struct gpt_part_entry *np = calloc(1, sizeof(*np));
317 if (np == NULL)
318 break;
319
320 strlcpy(np->gp_label, tt, sizeof(np->gp_label));
321 np->gp_start = p_start;
322 np->gp_size = p_size;
323 np->gp_flags |= GPEF_ON_DISK;
324
325 if (last == NULL)
326 parts->partitions = np;
327 else
328 last->gp_next = np;
329 last = np;
330 add_to = np;
331 parts->dp.num_part++;
332 }
333 }
334 free(textbuf);
335
336 /* If the GPT was not complete (e.g. truncated image), barf */
337 if (disk_size <= 0) {
338 free(parts);
339 return NULL;
340 }
341
342 parts->dp.pscheme = &gpt_parts;
343 parts->dp.disk = dev;
344 parts->dp.disk_start = start;
345 parts->dp.disk_size = disk_size;
346 parts->dp.free_space = avail_size;
347 parts->has_gpt = true;
348
349 fd = opendisk(parts->dp.disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
350 for (struct gpt_part_entry *p = parts->partitions; p != NULL;
351 p = p->gp_next) {
352 #ifdef DEFAULT_UFS2
353 bool fs_is_default = false;
354 #endif
355
356 if (p->gp_type != NULL) {
357
358 if (p->gp_type->fsflags != 0) {
359 const char *lm = get_last_mounted(fd,
360 p->gp_start, &p->fs_type,
361 &p->fs_sub_type, p->gp_type->fsflags);
362 if (lm != NULL && *lm != 0) {
363 char *path = strdup(lm);
364 canonicalize_last_mounted(path);
365 p->last_mounted = path;
366 } else {
367 p->fs_type = p->gp_type->
368 default_fs_type;
369 #ifdef DEFAULT_UFS2
370 fs_is_default = true;
371 #endif
372 }
373 } else {
374 p->fs_type = p->gp_type->default_fs_type;
375 #ifdef DEFAULT_UFS2
376 fs_is_default = true;
377 #endif
378 }
379 #ifdef DEFAULT_UFS2
380 if (fs_is_default && p->fs_type == FS_BSDFFS)
381 p->fs_sub_type = 2;
382 #endif
383 }
384
385 parts->dp.free_space -= p->gp_size;
386 }
387 close(fd);
388
389 return &parts->dp;
390 }
391
392 static struct disk_partitions *
393 gpt_create_new(const char *disk, daddr_t start, daddr_t len, daddr_t total,
394 bool is_boot_drive)
395 {
396 struct gpt_disk_partitions *parts;
397
398 if (start != 0) {
399 assert(0);
400 return NULL;
401 }
402
403 parts = calloc(1, sizeof(*parts));
404 if (!parts)
405 return NULL;
406
407 parts->dp.pscheme = &gpt_parts;
408 parts->dp.disk = disk;
409
410 gpt_md_init(is_boot_drive, &parts->max_num_parts, &parts->prologue,
411 &parts->epilogue);
412
413 parts->dp.disk_start = start;
414 parts->dp.disk_size = len;
415 parts->dp.free_space = len - start - parts->prologue - parts->epilogue;
416 parts->has_gpt = false;
417
418 return &parts->dp;
419 }
420
421 static bool
422 gpt_get_part_info(const struct disk_partitions *arg, part_id id,
423 struct disk_part_info *info)
424 {
425 static const struct part_type_desc gpt_unknown_type =
426 { .generic_ptype = PT_undef,
427 .short_desc = "<unknown>" };
428 const struct gpt_disk_partitions *parts =
429 (const struct gpt_disk_partitions*)arg;
430 const struct gpt_part_entry *p = parts->partitions;
431 part_id no;
432
433 for (no = 0; p != NULL && no < id; no++)
434 p = p->gp_next;
435
436 if (no != id || p == NULL)
437 return false;
438
439 memset(info, 0, sizeof(*info));
440 info->start = p->gp_start;
441 info->size = p->gp_size;
442 if (p->gp_type)
443 info->nat_type = &p->gp_type->gent;
444 else
445 info->nat_type = &gpt_unknown_type;
446 info->last_mounted = p->last_mounted;
447 info->fs_type = p->fs_type;
448 info->fs_sub_type = p->fs_sub_type;
449
450 return true;
451 }
452
453 static bool
454 gpt_get_part_attr_str(const struct disk_partitions *arg, part_id id,
455 char *str, size_t avail_space)
456 {
457 const struct gpt_disk_partitions *parts =
458 (const struct gpt_disk_partitions*)arg;
459 const struct gpt_part_entry *p = parts->partitions;
460 part_id no;
461 static const char *flags = NULL;
462
463 for (no = 0; p != NULL && no < id; no++)
464 p = p->gp_next;
465
466 if (no != id || p == NULL)
467 return false;
468
469 if (flags == NULL)
470 flags = msg_string(MSG_gpt_flags);
471
472 if (avail_space < 2)
473 return false;
474
475 if (p->gp_attr & GPT_ATTR_BOOT)
476 *str++ = flags[0];
477 *str = 0;
478
479 return true;
480 }
481
482 /*
483 * Find insert position and check for duplicates.
484 * If all goes well, insert the new "entry" in the "list".
485 * If there are collisions, report "no free space".
486 * We keep all lists sorted by start sector number,
487 */
488 static bool
489 gpt_insert_part_into_list(struct gpt_disk_partitions *parts,
490 struct gpt_part_entry **list,
491 struct gpt_part_entry *entry, const char **err_msg)
492 {
493 struct gpt_part_entry *p, *last;
494
495 /* find the first entry past the new one (if any) */
496 for (last = NULL, p = *list; p != NULL; last = p, p = p->gp_next) {
497 if (p->gp_start > entry->gp_start)
498 break;
499 }
500
501 /* check if last partition overlaps with new one */
502 if (last) {
503 if (last->gp_start + last->gp_size > entry->gp_start) {
504 if (err_msg)
505 *err_msg = msg_string(MSG_No_free_space);
506 return false;
507 }
508 }
509
510 if (p == NULL) {
511 entry->gp_next = NULL;
512 if (last != NULL) {
513 last->gp_next = entry;
514 }
515 } else {
516 /* check if new entry overlaps with next */
517 if (entry->gp_start + entry->gp_size > p->gp_start) {
518 if (err_msg)
519 *err_msg = msg_string(MSG_No_free_space);
520 return false;
521 }
522
523 entry->gp_next = p;
524 if (last != NULL)
525 last->gp_next = entry;
526 else
527 *list = entry;
528 }
529 if (*list == NULL)
530 *list = entry;
531
532 return true;
533 }
534
535 static bool
536 gpt_set_part_info(struct disk_partitions *arg, part_id id,
537 const struct disk_part_info *info, const char **err_msg)
538 {
539 struct gpt_disk_partitions *parts =
540 (struct gpt_disk_partitions*)arg;
541 struct gpt_part_entry *p = parts->partitions, *n;
542 part_id no;
543 daddr_t lendiff;
544
545 for (no = 0; p != NULL && no < id; no++)
546 p = p->gp_next;
547
548 if (no != id || p == NULL)
549 return false;
550
551 if ((p->gp_flags & GPEF_ON_DISK)) {
552 if (info->start != p->gp_start) {
553 /* partition moved, we need to delete and re-add */
554 n = calloc(1, sizeof(*n));
555 if (n == NULL) {
556 if (err_msg)
557 *err_msg = err_outofmem;
558 return false;
559 }
560 *n = *p;
561 p->gp_flags &= ~GPEF_ON_DISK;
562 if (!gpt_insert_part_into_list(parts, &parts->obsolete,
563 n, err_msg))
564 return false;
565 } else if (info->size != p->gp_size) {
566 p->gp_flags |= GPEF_RESIZED;
567 }
568 }
569
570 p->gp_flags |= GPEF_MODIFIED;
571
572 lendiff = info->size - p->gp_size;
573 parts->dp.free_space -= lendiff;
574 return gpt_info_to_part(p, info, err_msg);
575 }
576
577 static size_t
578 gpt_get_free_spaces_internal(const struct gpt_disk_partitions *parts,
579 struct disk_part_free_space *result, size_t max_num_result,
580 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
581 {
582 size_t cnt = 0;
583 daddr_t s, e, from, size, end_of_disk;
584 struct gpt_part_entry *p;
585
586 if (align > 1)
587 start = max(roundup(start, align), align);
588 if (start < 0 || start < (daddr_t)parts->prologue)
589 start = parts->prologue;
590 if (parts->dp.disk_start != 0 && parts->dp.disk_start > start)
591 start = parts->dp.disk_start;
592 if (min_space_size < 1)
593 min_space_size = 1;
594 end_of_disk = parts->dp.disk_start + parts->dp.disk_size
595 - parts->epilogue;
596 from = start;
597 while (from < end_of_disk && cnt < max_num_result) {
598 again:
599 size = parts->dp.disk_start + parts->dp.disk_size - from;
600 start = from;
601 if (start + size > end_of_disk)
602 size = end_of_disk - start;
603 for (p = parts->partitions; p != NULL; p = p->gp_next) {
604 s = p->gp_start;
605 e = p->gp_size + s;
606 if (s == ignore)
607 continue;
608 if (e < from)
609 continue;
610 if (s <= from && e > from) {
611 if (e - 1 >= end_of_disk)
612 return cnt;
613 from = e + 1;
614 if (align > 1) {
615 from = max(roundup(from, align), align);
616 if (from >= end_of_disk) {
617 size = 0;
618 break;
619 }
620 }
621 goto again;
622 }
623 if (s > from && s - from < size) {
624 size = s - from;
625 }
626 }
627 if (size >= min_space_size) {
628 result->start = start;
629 result->size = size;
630 result++;
631 cnt++;
632 }
633 from += size + 1;
634 if (align > 1)
635 from = max(roundup(from, align), align);
636 }
637
638 return cnt;
639 }
640
641 static daddr_t
642 gpt_max_free_space_at(const struct disk_partitions *arg, daddr_t start)
643 {
644 const struct gpt_disk_partitions *parts =
645 (const struct gpt_disk_partitions*)arg;
646 struct disk_part_free_space space;
647
648 if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 0,
649 start, start) == 1)
650 return space.size;
651
652 return 0;
653 }
654
655 static size_t
656 gpt_get_free_spaces(const struct disk_partitions *arg,
657 struct disk_part_free_space *result, size_t max_num_result,
658 daddr_t min_space_size, daddr_t align, daddr_t start,
659 daddr_t ignore)
660 {
661 const struct gpt_disk_partitions *parts =
662 (const struct gpt_disk_partitions*)arg;
663
664 return gpt_get_free_spaces_internal(parts, result,
665 max_num_result, min_space_size, align, start, ignore);
666 }
667
668
669 static bool
670 gpt_adapt(const struct disk_partitions *arg,
671 const struct disk_part_info *src, struct disk_part_info *dest)
672 {
673 /* slightly simplistic, enhance when needed */
674 memcpy(dest, src, sizeof(*dest));
675
676 if (src->nat_type == NULL)
677 return false;
678
679 dest->nat_type = arg->pscheme->get_generic_part_type(
680 src->nat_type->generic_ptype);
681 if (dest->nat_type == NULL)
682 dest->nat_type = arg->pscheme->get_generic_part_type(
683 PT_unknown);
684
685 return true;
686 }
687
688 static void
689 gpt_match_ptype(const char *name, struct gpt_ptype_desc *t)
690 {
691 size_t i;
692
693 for (i = 0; i < __arraycount(gpt_fs_types); i++) {
694 if (strcmp(name, gpt_fs_types[i].name) == 0) {
695 t->gent.generic_ptype = gpt_fs_types[i].ptype;
696 t->fsflags = gpt_fs_types[i].fsflags;
697 t->default_fs_type = gpt_fs_types[i].fstype;
698 return;
699 }
700 }
701
702 t->gent.generic_ptype = PT_unknown;
703 t->fsflags = 0;
704 t->default_fs_type = FS_BSDFFS;
705 }
706
707 static void
708 gpt_internal_add_ptype(const char *uid, const char *name, const char *desc)
709 {
710 strlcpy(gpt_ptype_descs[gpt_ptype_cnt].tid, uid,
711 sizeof(gpt_ptype_descs[gpt_ptype_cnt].tid));
712 gpt_ptype_descs[gpt_ptype_cnt].gent.short_desc = name;
713 gpt_ptype_descs[gpt_ptype_cnt].gent.description = desc;
714 gpt_match_ptype(name, &gpt_ptype_descs[gpt_ptype_cnt]);
715 gpt_ptype_cnt++;
716 }
717
718 static void
719 gpt_init_ptypes(void)
720 {
721 if (gpt_ptype_cnt == 0)
722 gpt_uuid_query(gpt_internal_add_ptype);
723 }
724
725 static size_t
726 gpt_type_count(void)
727 {
728 if (gpt_ptype_cnt == 0)
729 gpt_init_ptypes();
730
731 return gpt_ptype_cnt;
732 }
733
734 static const struct part_type_desc *
735 gpt_get_ptype(size_t ndx)
736 {
737 if (gpt_ptype_cnt == 0)
738 gpt_init_ptypes();
739
740 if (ndx >= gpt_ptype_cnt)
741 return NULL;
742
743 return &gpt_ptype_descs[ndx].gent;
744 }
745
746 static const struct part_type_desc *
747 gpt_get_generic_type(enum part_type gent)
748 {
749 if (gpt_ptype_cnt == 0)
750 gpt_init_ptypes();
751
752 for (size_t i = 0; i < gpt_ptype_cnt; i++)
753 if (gpt_ptype_descs[i].gent.generic_ptype == gent)
754 return &gpt_ptype_descs[i].gent;
755
756 return NULL;
757 }
758
759 static const struct gpt_ptype_desc *
760 gpt_find_native_type(const struct part_type_desc *gent)
761 {
762 if (gpt_ptype_cnt == 0)
763 gpt_init_ptypes();
764
765 if (gent == NULL)
766 return NULL;
767
768 for (size_t i = 0; i < gpt_ptype_cnt; i++)
769 if (gent == &gpt_ptype_descs[i].gent)
770 return &gpt_ptype_descs[i];
771
772 gent = gpt_get_generic_type(gent->generic_ptype);
773 if (gent == NULL)
774 return NULL;
775
776 /* this can not recurse deeper than once, we would not have found a
777 * generic type a few lines above if it would. */
778 return gpt_find_native_type(gent);
779 }
780
781 static const struct gpt_ptype_desc *
782 gpt_find_guid_type(const char *uid)
783 {
784 if (gpt_ptype_cnt == 0)
785 gpt_init_ptypes();
786
787 if (uid == NULL || uid[0] == 0)
788 return NULL;
789
790 for (size_t i = 0; i < gpt_ptype_cnt; i++)
791 if (strcmp(gpt_ptype_descs[i].tid, uid) == 0)
792 return &gpt_ptype_descs[i];
793
794 return NULL;
795 }
796
797 static const struct part_type_desc *
798 gpt_find_type(const char *desc)
799 {
800 if (gpt_ptype_cnt == 0)
801 gpt_init_ptypes();
802
803 if (desc == NULL || desc[0] == 0)
804 return NULL;
805
806 for (size_t i = 0; i < gpt_ptype_cnt; i++)
807 if (strcmp(gpt_ptype_descs[i].gent.short_desc, desc) == 0)
808 return &gpt_ptype_descs[i].gent;
809
810 return NULL;
811 }
812
813 static const struct part_type_desc *
814 gpt_get_fs_part_type(unsigned fstype, unsigned fs_sub_type)
815 {
816 size_t i;
817
818 for (i = 0; i < __arraycount(gpt_fs_types); i++)
819 if (fstype == gpt_fs_types[i].fstype)
820 return gpt_find_type(gpt_fs_types[i].name);
821
822 return gpt_get_generic_type(PT_root);
823 }
824
825 static daddr_t
826 gpt_get_part_alignment(const struct disk_partitions *parts)
827 {
828
829 assert(parts->disk_size > 0);
830 if (parts->disk_size < 0)
831 return 1;
832
833 /* Use 1MB offset/alignemnt for large (>128GB) disks */
834 if (parts->disk_size > HUGE_DISK_SIZE)
835 return 2048;
836 else if (parts->disk_size > TINY_DISK_SIZE)
837 return 64;
838 else
839 return 4;
840 }
841
842 static bool
843 gpt_can_add_partition(const struct disk_partitions *arg)
844 {
845 const struct gpt_disk_partitions *parts =
846 (const struct gpt_disk_partitions*)arg;
847 struct disk_part_free_space space;
848 daddr_t align;
849
850 if (parts->dp.num_part >= parts->max_num_parts)
851 return false;
852
853 align = gpt_get_part_alignment(arg);
854 if (parts->dp.free_space <= align)
855 return false;
856
857 if (gpt_get_free_spaces_internal(parts, &space, 1, align, align,
858 0, -1) < 1)
859 return false;
860
861 return true;
862 }
863
864 static bool
865 gpt_info_to_part(struct gpt_part_entry *p, const struct disk_part_info *info,
866 const char **err_msg)
867 {
868 p->gp_type = gpt_find_native_type(info->nat_type);
869 p->gp_start = info->start;
870 p->gp_size = info->size;
871 if (info->last_mounted != NULL && info->last_mounted !=
872 p->last_mounted) {
873 free(__UNCONST(p->last_mounted));
874 p->last_mounted = strdup(info->last_mounted);
875 }
876 p->fs_type = info->fs_type;
877 p->fs_sub_type = info->fs_sub_type;
878
879 return true;
880 }
881
882 static part_id
883 gpt_add_part(struct disk_partitions *arg,
884 const struct disk_part_info *info, const char **err_msg)
885 {
886 struct gpt_disk_partitions *parts =
887 (struct gpt_disk_partitions*)arg;
888 struct disk_part_free_space space;
889 struct disk_part_info data = *info;
890 struct gpt_part_entry *p;
891 bool ok;
892
893 if (err_msg != NULL)
894 *err_msg = NULL;
895
896 if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 1,
897 info->start, -1) < 1) {
898 if (err_msg)
899 *err_msg = msg_string(MSG_No_free_space);
900 return NO_PART;
901 }
902 if (parts->dp.num_part >= parts->max_num_parts) {
903 if (err_msg)
904 *err_msg = msg_string(MSG_err_too_many_partitions);
905 return NO_PART;
906 }
907
908 if (data.size > space.size)
909 data.size = space.size;
910
911 p = calloc(1, sizeof(*p));
912 if (p == NULL) {
913 if (err_msg != NULL)
914 *err_msg = INTERNAL_ERROR;
915 return NO_PART;
916 }
917 if (!gpt_info_to_part(p, &data, err_msg)) {
918 free(p);
919 return NO_PART;
920 }
921 p->gp_flags |= GPEF_MODIFIED;
922 ok = gpt_insert_part_into_list(parts, &parts->partitions, p, err_msg);
923 if (ok) {
924 parts->dp.num_part++;
925 parts->dp.free_space -= p->gp_size;
926 return parts->dp.num_part-1;
927 } else {
928 free(p);
929 return NO_PART;
930 }
931 }
932
933 static bool
934 gpt_delete_partition(struct disk_partitions *arg, part_id id,
935 const char **err_msg)
936 {
937 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
938 struct gpt_part_entry *p, *last = NULL;
939 part_id i;
940 bool res;
941
942 if (parts->dp.num_part == 0)
943 return false;
944
945 for (i = 0, p = parts->partitions;
946 i != id && i < parts->dp.num_part && p != NULL;
947 i++, p = p->gp_next)
948 last = p;
949
950 if (p == NULL) {
951 if (err_msg)
952 *err_msg = INTERNAL_ERROR;
953 return false;
954 }
955
956 if (last == NULL)
957 parts->partitions = p->gp_next;
958 else
959 last->gp_next = p->gp_next;
960
961 res = true;
962 if (p->gp_flags & GPEF_ON_DISK) {
963 if (!gpt_insert_part_into_list(parts, &parts->obsolete,
964 p, err_msg))
965 res = false;
966 } else {
967 free(p);
968 }
969
970 if (res) {
971 parts->dp.num_part--;
972 parts->dp.free_space += p->gp_size;
973 }
974
975 return res;
976 }
977
978 static bool
979 gpt_delete_all_partitions(struct disk_partitions *arg)
980 {
981 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
982
983 while (parts->dp.num_part > 0) {
984 if (!gpt_delete_partition(&parts->dp, 0, NULL))
985 return false;
986 }
987
988 return true;
989 }
990
991 static bool
992 gpt_read_part(const char *disk, daddr_t start, struct gpt_part_entry *p)
993 {
994 char *textbuf, *t, *tt;
995 static const char expected_hdr[] = "Details for index ";
996
997 /* run gpt show for this partition */
998 if (collect(T_OUTPUT, &textbuf,
999 "gpt -r show -b %" PRIu64 " %s 2>/dev/null", start, disk) < 1)
1000 return false;
1001
1002 /*
1003 * gpt show should respond with single partition details, but will
1004 * fall back to "show -a" output if something is wrong
1005 */
1006 t = strtok(textbuf, "\n"); /* first line is special */
1007 if (strncmp(t, expected_hdr, sizeof(expected_hdr)-1) != 0) {
1008 free(textbuf);
1009 return false;
1010 }
1011
1012 /* parse output into "old" */
1013 while ((t = strtok(NULL, "\n")) != NULL) {
1014 tt = strsep(&t, " \t");
1015 if (strlen(tt) == 0)
1016 continue;
1017 gpt_add_info(p, tt, t, true);
1018 }
1019 free(textbuf);
1020
1021 return true;
1022 }
1023
1024 static bool
1025 gpt_apply_attr(const char *disk, const char *cmd, off_t start, uint todo)
1026 {
1027 size_t i;
1028 char attr_str[STRSIZE];
1029
1030 if (todo == 0)
1031 return true;
1032
1033 strcpy(attr_str, "-a ");
1034 for (i = 0; todo != 0; i++) {
1035 if (!(gpt_avail_attrs[i].flag & todo))
1036 continue;
1037 todo &= ~gpt_avail_attrs[i].flag;
1038 if (attr_str[0])
1039 strlcat(attr_str, ",",
1040 sizeof(attr_str));
1041 strlcat(attr_str,
1042 gpt_avail_attrs[i].name,
1043 sizeof(attr_str));
1044 }
1045 if (run_program(RUN_SILENT,
1046 "gpt %s %s -b %" PRIu64 " %s", cmd, attr_str, start, disk) != 0)
1047 return false;
1048 return true;
1049 }
1050
1051 /*
1052 * Modify an existing on-disk partition.
1053 * Start and size can not be changed here, caller needs to deal
1054 * with that kind of changes upfront.
1055 */
1056 static bool
1057 gpt_modify_part(const char *disk, struct gpt_part_entry *p)
1058 {
1059 struct gpt_part_entry old;
1060 uint todo_set, todo_unset;
1061
1062 /*
1063 * Query current on-disk state
1064 */
1065 memset(&old, 0, sizeof old);
1066 if (!gpt_read_part(disk, p->gp_start, &old))
1067 return false;
1068
1069 /* Reject unsupported changes */
1070 if (old.gp_start != p->gp_start || old.gp_size != p->gp_size)
1071 return false;
1072
1073 /*
1074 * GUID should never change, but the internal copy
1075 * may not yet know it.
1076 */
1077 strcpy(p->gp_id, old.gp_id);
1078
1079 /* Check type */
1080 if (p->gp_type != old.gp_type) {
1081 if (run_program(RUN_SILENT,
1082 "gpt label -b %" PRIu64 " -T %s %s",
1083 p->gp_start, p->gp_type->tid, disk) != 0)
1084 return false;
1085 }
1086
1087 /* Check label */
1088 if (strcmp(p->gp_label, old.gp_label) != 0) {
1089 if (run_program(RUN_SILENT,
1090 "gpt label -b %" PRIu64 " -l %s %s",
1091 p->gp_start, p->gp_label, disk) != 0)
1092 return false;
1093 }
1094
1095 /* Check attributes */
1096 if (p->gp_attr != old.gp_attr) {
1097 if (p->gp_attr == 0) {
1098 if (run_program(RUN_SILENT,
1099 "gpt set -N -b %" PRIu64 " %s",
1100 p->gp_start, disk) != 0)
1101 return false;
1102 } else {
1103 todo_set = (p->gp_attr ^ old.gp_attr) & p->gp_attr;
1104 todo_unset = (p->gp_attr ^ old.gp_attr) & old.gp_attr;
1105 if (!gpt_apply_attr(disk, "unset", p->gp_start,
1106 todo_unset))
1107 return false;
1108 if (!gpt_apply_attr(disk, "set", p->gp_start,
1109 todo_set))
1110 return false;
1111 }
1112 }
1113
1114 return true;
1115 }
1116
1117 /*
1118 * verbatim copy from sys/dev/dkwedge/dkwedge_bsdlabel.c:
1119 * map FS_* to wedge strings
1120 */
1121 static const char *
1122 bsdlabel_fstype_to_str(uint8_t fstype)
1123 {
1124 const char *str;
1125
1126 /*
1127 * For each type known to FSTYPE_DEFN (from <sys/disklabel.h>),
1128 * a suitable case branch will convert the type number to a string.
1129 */
1130 switch (fstype) {
1131 #define FSTYPE_TO_STR_CASE(tag, number, name, fsck, mount) \
1132 case __CONCAT(FS_,tag): str = __CONCAT(DKW_PTYPE_,tag); break;
1133 FSTYPE_DEFN(FSTYPE_TO_STR_CASE)
1134 #undef FSTYPE_TO_STR_CASE
1135 default: str = NULL; break;
1136 }
1137
1138 return (str);
1139 }
1140
1141 static bool
1142 gpt_add_wedge(const char *disk, struct gpt_part_entry *p)
1143 {
1144 struct dkwedge_info dkw;
1145 const char *tname;
1146 char diskpath[MAXPATHLEN];
1147 int fd;
1148
1149 memset(&dkw, 0, sizeof(dkw));
1150 tname = bsdlabel_fstype_to_str(p->fs_type);
1151 if (tname)
1152 strlcpy(dkw.dkw_ptype, tname, sizeof(dkw.dkw_ptype));
1153
1154 strlcpy((char*)&dkw.dkw_wname, p->gp_id, sizeof(dkw.dkw_wname));
1155 dkw.dkw_offset = p->gp_start;
1156 dkw.dkw_size = p->gp_size;
1157
1158 fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0);
1159 if (fd < 0)
1160 return false;
1161 if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) {
1162 close(fd);
1163 return false;
1164 }
1165 close(fd);
1166
1167 strlcpy(p->gp_dev_name, dkw.dkw_devname, sizeof(p->gp_dev_name));
1168 p->gp_flags |= GPEF_WEDGE;
1169 return true;
1170 }
1171
1172 static bool
1173 gpt_get_part_device(const struct disk_partitions *arg,
1174 part_id id, char *devname, size_t max_devname_len, int *part,
1175 enum dev_name_usage usage, bool with_path)
1176 {
1177 const struct gpt_disk_partitions *parts =
1178 (const struct gpt_disk_partitions*)arg;
1179 struct gpt_part_entry *p = parts->partitions;
1180 part_id no;
1181
1182
1183 for (no = 0; p != NULL && no < id; no++)
1184 p = p->gp_next;
1185
1186 if (no != id || p == NULL)
1187 return false;
1188
1189 if (part)
1190 *part = -1;
1191
1192 if (!(p->gp_flags & GPEF_WEDGE) &&
1193 (usage == plain_name || usage == raw_dev_name))
1194 gpt_add_wedge(arg->disk, p);
1195
1196 switch (usage) {
1197 case logical_name:
1198 if (p->gp_label[0] != 0)
1199 snprintf(devname, max_devname_len,
1200 "NAME=%s", p->gp_label);
1201 else
1202 snprintf(devname, max_devname_len,
1203 "NAME=%s", p->gp_id);
1204 break;
1205 case plain_name:
1206 assert(p->gp_flags & GPEF_WEDGE);
1207 if (with_path)
1208 snprintf(devname, max_devname_len, _PATH_DEV "%s",
1209 p->gp_dev_name);
1210 else
1211 strlcpy(devname, p->gp_dev_name, max_devname_len);
1212 break;
1213 case raw_dev_name:
1214 assert(p->gp_flags & GPEF_WEDGE);
1215 if (with_path)
1216 snprintf(devname, max_devname_len, _PATH_DEV "r%s",
1217 p->gp_dev_name);
1218 else
1219 snprintf(devname, max_devname_len, "r%s",
1220 p->gp_dev_name);
1221 break;
1222 default:
1223 return false;
1224 }
1225
1226 return true;
1227 }
1228
1229 static bool
1230 gpt_write_to_disk(struct disk_partitions *arg)
1231 {
1232 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
1233 struct gpt_part_entry *p, *n;
1234 char label_arg[sizeof(p->gp_label) + 4];
1235 char diskpath[MAXPATHLEN];
1236 int fd, bits = 0;
1237 bool root_is_new = false, efi_is_new = false;
1238 part_id root_id = NO_PART, efi_id = NO_PART, pno;
1239
1240 /*
1241 * Remove all wedges on this disk - they may become invalid and we
1242 * have no easy way to associate them with the partitioning data.
1243 * Instead we will explicitly request creation of wedges on demand
1244 * later.
1245 */
1246 fd = opendisk(arg->disk, O_RDWR, diskpath, sizeof(diskpath), 0);
1247 if (fd < 0)
1248 return false;
1249 if (ioctl(fd, DIOCRMWEDGES, &bits) == -1)
1250 return false;
1251 close(fd);
1252
1253 /*
1254 * Mark all partitions as "have no wedge yet". While there,
1255 * collect first root and efi partition (if available)
1256 */
1257 for (pno = 0, p = parts->partitions; p != NULL; p = p->gp_next, pno++) {
1258 p->gp_flags &= ~GPEF_WEDGE;
1259 if (root_id == NO_PART && p->gp_type != NULL) {
1260 if (p->gp_type->gent.generic_ptype == PT_root &&
1261 p->gp_start == pm->ptstart) {
1262 root_id = pno;
1263 root_is_new = !(p->gp_flags & GPEF_ON_DISK);
1264 } else if (efi_id == NO_PART &&
1265 p->gp_type->gent.generic_ptype == PT_EFI_SYSTEM) {
1266 efi_id = pno;
1267 efi_is_new = !(p->gp_flags & GPEF_ON_DISK);
1268 }
1269 }
1270 }
1271
1272 /*
1273 * If no GPT on disk yet, create it.
1274 */
1275 if (!parts->has_gpt) {
1276 char limit[30];
1277
1278 if (parts->max_num_parts > 0)
1279 sprintf(limit, "-p %zu", parts->max_num_parts);
1280 else
1281 limit[0] = 0;
1282 if (run_program(RUN_SILENT, "gpt create %s %s",
1283 limit, parts->dp.disk))
1284 return false;
1285 parts->has_gpt = true;
1286 }
1287
1288 /*
1289 * Delete all old partitions
1290 */
1291 for (p = parts->obsolete; p != NULL; p = n) {
1292 run_program(RUN_SILENT, "gpt -n remove -b %" PRIu64 " %s",
1293 p->gp_start, arg->disk);
1294 n = p->gp_next;
1295 free(p);
1296 }
1297 parts->obsolete = NULL;
1298
1299 /*
1300 * Modify existing but changed partitions
1301 */
1302 for (p = parts->partitions; p != NULL; p = p->gp_next) {
1303 if (!(p->gp_flags & GPEF_ON_DISK))
1304 continue;
1305
1306 if (p->gp_flags & GPEF_RESIZED) {
1307 run_program(RUN_SILENT,
1308 "gpt -n resize -b %" PRIu64 " -s %" PRIu64 "s %s",
1309 p->gp_start, p->gp_size, arg->disk);
1310 p->gp_flags &= ~GPEF_RESIZED;
1311 }
1312
1313 if (!(p->gp_flags & GPEF_MODIFIED))
1314 continue;
1315
1316 if (!gpt_modify_part(parts->dp.disk, p))
1317 return false;
1318 }
1319
1320 /*
1321 * Add new partitions
1322 */
1323 for (p = parts->partitions; p != NULL; p = p->gp_next) {
1324 if (p->gp_flags & GPEF_ON_DISK)
1325 continue;
1326 if (!(p->gp_flags & GPEF_MODIFIED))
1327 continue;
1328
1329 if (p->gp_label[0] == 0)
1330 label_arg[0] = 0;
1331 else
1332 sprintf(label_arg, "-l %s", p->gp_label);
1333
1334 if (p->gp_type != NULL)
1335 run_program(RUN_SILENT,
1336 "gpt -n add -b %" PRIu64 " -s %" PRIu64
1337 "s -t %s %s %s",
1338 p->gp_start, p->gp_size, p->gp_type->tid,
1339 label_arg, arg->disk);
1340 else
1341 run_program(RUN_SILENT,
1342 "gpt -n add -b %" PRIu64 " -s %" PRIu64
1343 "s %s %s",
1344 p->gp_start, p->gp_size, label_arg, arg->disk);
1345 gpt_apply_attr(arg->disk, "set", p->gp_start, p->gp_attr);
1346 gpt_read_part(arg->disk, p->gp_start, p);
1347 p->gp_flags |= GPEF_ON_DISK;
1348 }
1349
1350 /*
1351 * Additional MD bootloader magic...
1352 */
1353 if (!md_gpt_post_write(&parts->dp, root_id, root_is_new, efi_id,
1354 efi_is_new))
1355 return false;
1356
1357 return true;
1358 }
1359
1360 bool
1361 gpt_parts_check(void)
1362 {
1363
1364 check_available_binaries();
1365
1366 return have_gpt && have_dk;
1367 }
1368
1369 static void
1370 gpt_free(struct disk_partitions *arg)
1371 {
1372 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
1373 struct gpt_part_entry *p, *n;
1374
1375 assert(parts != NULL);
1376 for (p = parts->partitions; p != NULL; p = n) {
1377 free(__UNCONST(p->last_mounted));
1378 n = p->gp_next;
1379 free(p);
1380 }
1381 free(parts);
1382 }
1383
1384 static bool
1385 gpt_custom_attribute_writable(const struct disk_partitions *arg,
1386 part_id ptn, size_t attr_no)
1387 {
1388 const struct gpt_disk_partitions *parts =
1389 (const struct gpt_disk_partitions*)arg;
1390 size_t i;
1391 struct gpt_part_entry *p;
1392
1393 if (attr_no >= arg->pscheme->custom_attribute_count)
1394 return false;
1395
1396 const msg label = arg->pscheme->custom_attributes[attr_no].label;
1397
1398 /* we can not edit the uuid attribute */
1399 if (label == MSG_ptn_uuid)
1400 return false;
1401
1402 /* the label is always editable */
1403 if (label == MSG_ptn_label)
1404 return true;
1405
1406 /* the GPT type is read only */
1407 if (label == MSG_ptn_gpt_type)
1408 return false;
1409
1410 /* BOOTME makes no sense on swap partitions */
1411 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1412 if (i == ptn)
1413 break;
1414
1415 if (p == NULL)
1416 return false;
1417
1418 if (p->fs_type == FS_SWAP ||
1419 (p->gp_type != NULL && p->gp_type->gent.generic_ptype == PT_swap))
1420 return false;
1421
1422 return true;
1423 }
1424
1425 static const char *
1426 gpt_get_label_str(const struct disk_partitions *arg, part_id ptn)
1427 {
1428 const struct gpt_disk_partitions *parts =
1429 (const struct gpt_disk_partitions*)arg;
1430 size_t i;
1431 struct gpt_part_entry *p;
1432
1433 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1434 if (i == ptn)
1435 break;
1436
1437 if (p == NULL)
1438 return NULL;
1439
1440 if (p->gp_label[0] != 0)
1441 return p->gp_label;
1442 return p->gp_id;
1443 }
1444
1445 static bool
1446 gpt_format_custom_attribute(const struct disk_partitions *arg,
1447 part_id ptn, size_t attr_no, const struct disk_part_info *info,
1448 char *out, size_t out_space)
1449 {
1450 const struct gpt_disk_partitions *parts =
1451 (const struct gpt_disk_partitions*)arg;
1452 size_t i;
1453 struct gpt_part_entry *p, data;
1454
1455 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1456 if (i == ptn)
1457 break;
1458
1459 if (p == NULL)
1460 return false;
1461
1462 if (attr_no >= parts->dp.pscheme->custom_attribute_count)
1463 return false;
1464
1465 const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
1466
1467 if (info != NULL) {
1468 data = *p;
1469 gpt_info_to_part(&data, info, NULL);
1470 p = &data;
1471 }
1472
1473 if (label == MSG_ptn_label)
1474 strlcpy(out, p->gp_label, out_space);
1475 else if (label == MSG_ptn_uuid)
1476 strlcpy(out, p->gp_id, out_space);
1477 else if (label == MSG_ptn_gpt_type) {
1478 if (p->gp_type != NULL)
1479 strlcpy(out, p->gp_type->gent.description, out_space);
1480 else if (out_space > 1)
1481 out[0] = 0;
1482 } else if (label == MSG_ptn_boot)
1483 strlcpy(out, msg_string(p->gp_attr & GPT_ATTR_BOOT ?
1484 MSG_Yes : MSG_No), out_space);
1485 else
1486 return false;
1487
1488 return true;
1489 }
1490
1491 static bool
1492 gpt_custom_attribute_toggle(struct disk_partitions *arg,
1493 part_id ptn, size_t attr_no)
1494 {
1495 const struct gpt_disk_partitions *parts =
1496 (const struct gpt_disk_partitions*)arg;
1497 size_t i;
1498 struct gpt_part_entry *p;
1499
1500 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1501 if (i == ptn)
1502 break;
1503
1504 if (p == NULL)
1505 return false;
1506
1507 if (attr_no >= parts->dp.pscheme->custom_attribute_count)
1508 return false;
1509
1510 const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
1511 if (label != MSG_ptn_boot)
1512 return false;
1513
1514 if (p->gp_attr & GPT_ATTR_BOOT) {
1515 p->gp_attr &= ~GPT_ATTR_BOOT;
1516 } else {
1517 for (i = 0, p = parts->partitions; p != NULL;
1518 i++, p = p->gp_next)
1519 if (i == ptn)
1520 p->gp_attr |= GPT_ATTR_BOOT;
1521 else
1522 p->gp_attr &= ~GPT_ATTR_BOOT;
1523 }
1524 return true;
1525 }
1526
1527 static bool
1528 gpt_custom_attribute_set_str(struct disk_partitions *arg,
1529 part_id ptn, size_t attr_no, const char *new_val)
1530 {
1531 const struct gpt_disk_partitions *parts =
1532 (const struct gpt_disk_partitions*)arg;
1533 size_t i;
1534 struct gpt_part_entry *p;
1535
1536 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1537 if (i == ptn)
1538 break;
1539
1540 if (p == NULL)
1541 return false;
1542
1543 if (attr_no >= parts->dp.pscheme->custom_attribute_count)
1544 return false;
1545
1546 const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
1547
1548 if (label != MSG_ptn_label)
1549 return false;
1550
1551 strlcpy(p->gp_label, new_val, sizeof(p->gp_label));
1552 return true;
1553 }
1554
1555 static bool
1556 gpt_have_boot_support(const char *disk)
1557 {
1558 #ifdef HAVE_GPT_BOOT
1559 return true;
1560 #else
1561 return false;
1562 #endif
1563 }
1564
1565 const struct disk_part_custom_attribute gpt_custom_attrs[] = {
1566 { .label = MSG_ptn_label, .type = pet_str },
1567 { .label = MSG_ptn_uuid, .type = pet_str },
1568 { .label = MSG_ptn_gpt_type, .type = pet_str },
1569 { .label = MSG_ptn_boot, .type = pet_bool },
1570 };
1571
1572 const struct disk_partitioning_scheme
1573 gpt_parts = {
1574 .name = MSG_parttype_gpt,
1575 .short_name = MSG_parttype_gpt_short,
1576 .part_flag_desc = MSG_gpt_flag_desc,
1577 .custom_attribute_count = __arraycount(gpt_custom_attrs),
1578 .custom_attributes = gpt_custom_attrs,
1579 .get_part_types_count = gpt_type_count,
1580 .get_part_type = gpt_get_ptype,
1581 .get_generic_part_type = gpt_get_generic_type,
1582 .get_fs_part_type = gpt_get_fs_part_type,
1583 .get_part_alignment = gpt_get_part_alignment,
1584 .read_from_disk = gpt_read_from_disk,
1585 .create_new_for_disk = gpt_create_new,
1586 .have_boot_support = gpt_have_boot_support,
1587 .can_add_partition = gpt_can_add_partition,
1588 .custom_attribute_writable = gpt_custom_attribute_writable,
1589 .format_custom_attribute = gpt_format_custom_attribute,
1590 .custom_attribute_toggle = gpt_custom_attribute_toggle,
1591 .custom_attribute_set_str = gpt_custom_attribute_set_str,
1592 .other_partition_identifier = gpt_get_label_str,
1593 .get_part_device = gpt_get_part_device,
1594 .max_free_space_at = gpt_max_free_space_at,
1595 .get_free_spaces = gpt_get_free_spaces,
1596 .adapt_foreign_part_info = gpt_adapt,
1597 .get_part_info = gpt_get_part_info,
1598 .get_part_attr_str = gpt_get_part_attr_str,
1599 .set_part_info = gpt_set_part_info,
1600 .add_partition = gpt_add_part,
1601 .delete_all_partitions = gpt_delete_all_partitions,
1602 .delete_partition = gpt_delete_partition,
1603 .write_to_disk = gpt_write_to_disk,
1604 .free = gpt_free,
1605 };
1606