gpt.c revision 1.11 1 /* $NetBSD: gpt.c,v 1.11 2019/08/26 12:14:06 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 /*
232 * Find the partition matching this wedge info and record that we
233 * have a wedge already.
234 */
235 static void
236 update_part_from_wedge_info(struct gpt_disk_partitions *parts,
237 const struct dkwedge_info *dkw)
238 {
239 for (struct gpt_part_entry *p = parts->partitions; p != NULL;
240 p = p->gp_next) {
241 if (p->gp_start != dkw->dkw_offset ||
242 (uint64_t)p->gp_size != dkw->dkw_size)
243 continue;
244 p->gp_flags |= GPEF_WEDGE;
245 strlcpy(p->gp_dev_name, dkw->dkw_devname,
246 sizeof p->gp_dev_name);
247 return;
248 }
249 }
250
251 static struct disk_partitions *
252 gpt_read_from_disk(const char *dev, daddr_t start, daddr_t len,
253 const struct disk_partitioning_scheme *scheme)
254 {
255 char diskpath[MAXPATHLEN];
256 int fd;
257 struct dkwedge_info *dkw;
258 struct dkwedge_list dkwl;
259 size_t bufsize, dk;
260
261 assert(start == 0);
262 assert(have_gpt);
263
264 if (run_program(RUN_SILENT | RUN_ERROR_OK,
265 "gpt -rq header %s", dev) != 0)
266 return NULL;
267
268 /* read the partitions */
269 int i;
270 unsigned int p_index;
271 daddr_t p_start = 0, p_size = 0, avail_start = 0, avail_size = 0,
272 disk_size = 0;
273 char *textbuf, *t, *tt, p_type[STRSIZE];
274 static const char regpart_prefix[] = "GPT part - ";
275 struct gpt_disk_partitions *parts;
276 struct gpt_part_entry *last = NULL, *add_to = NULL;
277
278 if (collect(T_OUTPUT, &textbuf, "gpt -r show -a %s 2>/dev/null", dev)
279 < 1)
280 return NULL;
281
282 /* parse output and create our list */
283 parts = calloc(1, sizeof(*parts));
284 if (parts == NULL)
285 return NULL;
286
287 (void)strtok(textbuf, "\n"); /* ignore first line */
288 while ((t = strtok(NULL, "\n")) != NULL) {
289 i = 0; p_start = 0; p_size = 0; p_index = 0;
290 p_type[0] = 0;
291 while ((tt = strsep(&t, " \t")) != NULL) {
292 if (strlen(tt) == 0)
293 continue;
294 if (i == 0) {
295 if (add_to != NULL)
296 gpt_add_info(add_to, tt, t, false);
297 p_start = strtouq(tt, NULL, 10);
298 if (p_start == 0 && add_to != NULL)
299 break;
300 else
301 add_to = NULL;
302 }
303 if (i == 1)
304 p_size = strtouq(tt, NULL, 10);
305 if (i == 2)
306 p_index = strtouq(tt, NULL, 10);
307 if (i > 2 || (i == 2 && p_index == 0)) {
308 if (p_type[0])
309 strlcat(p_type, " ", STRSIZE);
310 strlcat(p_type, tt, STRSIZE);
311 }
312 i++;
313 }
314
315 if (p_start == 0 || p_size == 0)
316 continue;
317 else if (strcmp(p_type, "Pri GPT table") == 0) {
318 avail_start = p_start + p_size;
319 parts->prologue = avail_start;
320 parts->epilogue = p_size + 1;
321 parts->max_num_parts = p_size * GPT_PARTS_PER_SEC;
322 } else if (strcmp(p_type, "Sec GPT table") == 0)
323 avail_size = p_start - avail_start;
324 else if(strcmp(p_type, "Sec GPT header") == 0)
325 disk_size = p_start + p_size;
326 else if (p_index == 0 && strlen(p_type) > 0)
327 /* Utilitary entry (PMBR, etc) */
328 continue;
329 else if (p_index == 0) {
330 /* Free space */
331 continue;
332 } else {
333 /* Usual partition */
334 tt = p_type;
335 if (strncmp(tt, regpart_prefix,
336 strlen(regpart_prefix)) == 0)
337 tt += strlen(regpart_prefix);
338
339 /* Add to our linked list */
340 struct gpt_part_entry *np = calloc(1, sizeof(*np));
341 if (np == NULL)
342 break;
343
344 strlcpy(np->gp_label, tt, sizeof(np->gp_label));
345 np->gp_start = p_start;
346 np->gp_size = p_size;
347 np->gp_flags |= GPEF_ON_DISK;
348
349 if (last == NULL)
350 parts->partitions = np;
351 else
352 last->gp_next = np;
353 last = np;
354 add_to = np;
355 parts->dp.num_part++;
356 }
357 }
358 free(textbuf);
359
360 /* If the GPT was not complete (e.g. truncated image), barf */
361 if (disk_size <= 0) {
362 free(parts);
363 return NULL;
364 }
365
366 parts->dp.pscheme = scheme;
367 parts->dp.disk = dev;
368 parts->dp.disk_start = start;
369 parts->dp.disk_size = disk_size;
370 parts->dp.free_space = avail_size;
371 parts->has_gpt = true;
372
373 fd = opendisk(parts->dp.disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
374 for (struct gpt_part_entry *p = parts->partitions; p != NULL;
375 p = p->gp_next) {
376 #ifdef DEFAULT_UFS2
377 bool fs_is_default = false;
378 #endif
379
380 if (p->gp_type != NULL) {
381
382 if (p->gp_type->fsflags != 0) {
383 const char *lm = get_last_mounted(fd,
384 p->gp_start, &p->fs_type,
385 &p->fs_sub_type, p->gp_type->fsflags);
386 if (lm != NULL && *lm != 0) {
387 char *path = strdup(lm);
388 canonicalize_last_mounted(path);
389 p->last_mounted = path;
390 } else {
391 p->fs_type = p->gp_type->
392 default_fs_type;
393 #ifdef DEFAULT_UFS2
394 fs_is_default = true;
395 #endif
396 }
397 } else {
398 p->fs_type = p->gp_type->default_fs_type;
399 #ifdef DEFAULT_UFS2
400 fs_is_default = true;
401 #endif
402 }
403 #ifdef DEFAULT_UFS2
404 if (fs_is_default && p->fs_type == FS_BSDFFS)
405 p->fs_sub_type = 2;
406 #endif
407 }
408
409 parts->dp.free_space -= p->gp_size;
410 }
411
412 /*
413 * Check if we have any (matching/auto-configured) wedges already
414 */
415 dkw = NULL;
416 dkwl.dkwl_buf = dkw;
417 dkwl.dkwl_bufsize = 0;
418 if (ioctl(fd, DIOCLWEDGES, &dkwl) == 0) {
419 /* do not even try to deal with any races at this point */
420 bufsize = dkwl.dkwl_nwedges * sizeof(*dkw);
421 dkw = malloc(bufsize);
422 dkwl.dkwl_buf = dkw;
423 dkwl.dkwl_bufsize = bufsize;
424 if (dkw != NULL && ioctl(fd, DIOCLWEDGES, &dkwl) == 0) {
425 for (dk = 0; dk < dkwl.dkwl_ncopied; dk++)
426 update_part_from_wedge_info(parts, &dkw[dk]);
427 }
428 free(dkw);
429 }
430
431 close(fd);
432
433 return &parts->dp;
434 }
435
436 static struct disk_partitions *
437 gpt_create_new(const char *disk, daddr_t start, daddr_t len, daddr_t total,
438 bool is_boot_drive)
439 {
440 struct gpt_disk_partitions *parts;
441
442 if (start != 0) {
443 assert(0);
444 return NULL;
445 }
446
447 parts = calloc(1, sizeof(*parts));
448 if (!parts)
449 return NULL;
450
451 parts->dp.pscheme = &gpt_parts;
452 parts->dp.disk = disk;
453
454 gpt_md_init(is_boot_drive, &parts->max_num_parts, &parts->prologue,
455 &parts->epilogue);
456
457 parts->dp.disk_start = start;
458 parts->dp.disk_size = len;
459 parts->dp.free_space = len - start - parts->prologue - parts->epilogue;
460 parts->has_gpt = false;
461
462 return &parts->dp;
463 }
464
465 static bool
466 gpt_get_part_info(const struct disk_partitions *arg, part_id id,
467 struct disk_part_info *info)
468 {
469 static const struct part_type_desc gpt_unknown_type =
470 { .generic_ptype = PT_undef,
471 .short_desc = "<unknown>" };
472 const struct gpt_disk_partitions *parts =
473 (const struct gpt_disk_partitions*)arg;
474 const struct gpt_part_entry *p = parts->partitions;
475 part_id no;
476
477 for (no = 0; p != NULL && no < id; no++)
478 p = p->gp_next;
479
480 if (no != id || p == NULL)
481 return false;
482
483 memset(info, 0, sizeof(*info));
484 info->start = p->gp_start;
485 info->size = p->gp_size;
486 if (p->gp_type)
487 info->nat_type = &p->gp_type->gent;
488 else
489 info->nat_type = &gpt_unknown_type;
490 info->last_mounted = p->last_mounted;
491 info->fs_type = p->fs_type;
492 info->fs_sub_type = p->fs_sub_type;
493
494 return true;
495 }
496
497 static bool
498 gpt_get_part_attr_str(const struct disk_partitions *arg, part_id id,
499 char *str, size_t avail_space)
500 {
501 const struct gpt_disk_partitions *parts =
502 (const struct gpt_disk_partitions*)arg;
503 const struct gpt_part_entry *p = parts->partitions;
504 part_id no;
505 static const char *flags = NULL;
506
507 for (no = 0; p != NULL && no < id; no++)
508 p = p->gp_next;
509
510 if (no != id || p == NULL)
511 return false;
512
513 if (flags == NULL)
514 flags = msg_string(MSG_gpt_flags);
515
516 if (avail_space < 2)
517 return false;
518
519 if (p->gp_attr & GPT_ATTR_BOOT)
520 *str++ = flags[0];
521 *str = 0;
522
523 return true;
524 }
525
526 /*
527 * Find insert position and check for duplicates.
528 * If all goes well, insert the new "entry" in the "list".
529 * If there are collisions, report "no free space".
530 * We keep all lists sorted by start sector number,
531 */
532 static bool
533 gpt_insert_part_into_list(struct gpt_disk_partitions *parts,
534 struct gpt_part_entry **list,
535 struct gpt_part_entry *entry, const char **err_msg)
536 {
537 struct gpt_part_entry *p, *last;
538
539 /* find the first entry past the new one (if any) */
540 for (last = NULL, p = *list; p != NULL; last = p, p = p->gp_next) {
541 if (p->gp_start > entry->gp_start)
542 break;
543 }
544
545 /* check if last partition overlaps with new one */
546 if (last) {
547 if (last->gp_start + last->gp_size > entry->gp_start) {
548 if (err_msg)
549 *err_msg = msg_string(MSG_No_free_space);
550 return false;
551 }
552 }
553
554 if (p == NULL) {
555 entry->gp_next = NULL;
556 if (last != NULL) {
557 last->gp_next = entry;
558 }
559 } else {
560 /* check if new entry overlaps with next */
561 if (entry->gp_start + entry->gp_size > p->gp_start) {
562 if (err_msg)
563 *err_msg = msg_string(MSG_No_free_space);
564 return false;
565 }
566
567 entry->gp_next = p;
568 if (last != NULL)
569 last->gp_next = entry;
570 else
571 *list = entry;
572 }
573 if (*list == NULL)
574 *list = entry;
575
576 return true;
577 }
578
579 static bool
580 gpt_set_part_info(struct disk_partitions *arg, part_id id,
581 const struct disk_part_info *info, const char **err_msg)
582 {
583 struct gpt_disk_partitions *parts =
584 (struct gpt_disk_partitions*)arg;
585 struct gpt_part_entry *p = parts->partitions, *n;
586 part_id no;
587 daddr_t lendiff;
588
589 for (no = 0; p != NULL && no < id; no++)
590 p = p->gp_next;
591
592 if (no != id || p == NULL)
593 return false;
594
595 if ((p->gp_flags & GPEF_ON_DISK)) {
596 if (info->start != p->gp_start) {
597 /* partition moved, we need to delete and re-add */
598 n = calloc(1, sizeof(*n));
599 if (n == NULL) {
600 if (err_msg)
601 *err_msg = err_outofmem;
602 return false;
603 }
604 *n = *p;
605 p->gp_flags &= ~GPEF_ON_DISK;
606 if (!gpt_insert_part_into_list(parts, &parts->obsolete,
607 n, err_msg))
608 return false;
609 } else if (info->size != p->gp_size) {
610 p->gp_flags |= GPEF_RESIZED;
611 }
612 }
613
614 p->gp_flags |= GPEF_MODIFIED;
615
616 lendiff = info->size - p->gp_size;
617 parts->dp.free_space -= lendiff;
618 return gpt_info_to_part(p, info, err_msg);
619 }
620
621 static size_t
622 gpt_get_free_spaces_internal(const struct gpt_disk_partitions *parts,
623 struct disk_part_free_space *result, size_t max_num_result,
624 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
625 {
626 size_t cnt = 0;
627 daddr_t s, e, from, size, end_of_disk;
628 struct gpt_part_entry *p;
629
630 if (align > 1)
631 start = max(roundup(start, align), align);
632 if (start < 0 || start < (daddr_t)parts->prologue)
633 start = parts->prologue;
634 if (parts->dp.disk_start != 0 && parts->dp.disk_start > start)
635 start = parts->dp.disk_start;
636 if (min_space_size < 1)
637 min_space_size = 1;
638 end_of_disk = parts->dp.disk_start + parts->dp.disk_size
639 - parts->epilogue;
640 from = start;
641 while (from < end_of_disk && cnt < max_num_result) {
642 again:
643 size = parts->dp.disk_start + parts->dp.disk_size - from;
644 start = from;
645 if (start + size > end_of_disk)
646 size = end_of_disk - start;
647 for (p = parts->partitions; p != NULL; p = p->gp_next) {
648 s = p->gp_start;
649 e = p->gp_size + s;
650 if (s == ignore)
651 continue;
652 if (e < from)
653 continue;
654 if (s <= from && e > from) {
655 if (e - 1 >= end_of_disk)
656 return cnt;
657 from = e + 1;
658 if (align > 1) {
659 from = max(roundup(from, align), align);
660 if (from >= end_of_disk) {
661 size = 0;
662 break;
663 }
664 }
665 goto again;
666 }
667 if (s > from && s - from < size) {
668 size = s - from;
669 }
670 }
671 if (size >= min_space_size) {
672 result->start = start;
673 result->size = size;
674 result++;
675 cnt++;
676 }
677 from += size + 1;
678 if (align > 1)
679 from = max(roundup(from, align), align);
680 }
681
682 return cnt;
683 }
684
685 static daddr_t
686 gpt_max_free_space_at(const struct disk_partitions *arg, daddr_t start)
687 {
688 const struct gpt_disk_partitions *parts =
689 (const struct gpt_disk_partitions*)arg;
690 struct disk_part_free_space space;
691
692 if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 0,
693 start, start) == 1)
694 return space.size;
695
696 return 0;
697 }
698
699 static size_t
700 gpt_get_free_spaces(const struct disk_partitions *arg,
701 struct disk_part_free_space *result, size_t max_num_result,
702 daddr_t min_space_size, daddr_t align, daddr_t start,
703 daddr_t ignore)
704 {
705 const struct gpt_disk_partitions *parts =
706 (const struct gpt_disk_partitions*)arg;
707
708 return gpt_get_free_spaces_internal(parts, result,
709 max_num_result, min_space_size, align, start, ignore);
710 }
711
712
713 static bool
714 gpt_adapt(const struct disk_partitions *arg,
715 const struct disk_part_info *src, struct disk_part_info *dest)
716 {
717 /* slightly simplistic, enhance when needed */
718 memcpy(dest, src, sizeof(*dest));
719
720 if (src->nat_type == NULL)
721 return false;
722
723 dest->nat_type = arg->pscheme->get_generic_part_type(
724 src->nat_type->generic_ptype);
725 if (dest->nat_type == NULL)
726 dest->nat_type = arg->pscheme->get_generic_part_type(
727 PT_unknown);
728
729 return true;
730 }
731
732 static void
733 gpt_match_ptype(const char *name, struct gpt_ptype_desc *t)
734 {
735 size_t i;
736
737 for (i = 0; i < __arraycount(gpt_fs_types); i++) {
738 if (strcmp(name, gpt_fs_types[i].name) == 0) {
739 t->gent.generic_ptype = gpt_fs_types[i].ptype;
740 t->fsflags = gpt_fs_types[i].fsflags;
741 t->default_fs_type = gpt_fs_types[i].fstype;
742 return;
743 }
744 }
745
746 t->gent.generic_ptype = PT_unknown;
747 t->fsflags = 0;
748 t->default_fs_type = FS_BSDFFS;
749 }
750
751 static void
752 gpt_internal_add_ptype(const char *uid, const char *name, const char *desc)
753 {
754 strlcpy(gpt_ptype_descs[gpt_ptype_cnt].tid, uid,
755 sizeof(gpt_ptype_descs[gpt_ptype_cnt].tid));
756 gpt_ptype_descs[gpt_ptype_cnt].gent.short_desc = name;
757 gpt_ptype_descs[gpt_ptype_cnt].gent.description = desc;
758 gpt_match_ptype(name, &gpt_ptype_descs[gpt_ptype_cnt]);
759 gpt_ptype_cnt++;
760 }
761
762 static void
763 gpt_init_ptypes(void)
764 {
765 if (gpt_ptype_cnt == 0)
766 gpt_uuid_query(gpt_internal_add_ptype);
767 }
768
769 static size_t
770 gpt_type_count(void)
771 {
772 if (gpt_ptype_cnt == 0)
773 gpt_init_ptypes();
774
775 return gpt_ptype_cnt;
776 }
777
778 static const struct part_type_desc *
779 gpt_get_ptype(size_t ndx)
780 {
781 if (gpt_ptype_cnt == 0)
782 gpt_init_ptypes();
783
784 if (ndx >= gpt_ptype_cnt)
785 return NULL;
786
787 return &gpt_ptype_descs[ndx].gent;
788 }
789
790 static const struct part_type_desc *
791 gpt_get_generic_type(enum part_type gent)
792 {
793 if (gpt_ptype_cnt == 0)
794 gpt_init_ptypes();
795
796 for (size_t i = 0; i < gpt_ptype_cnt; i++)
797 if (gpt_ptype_descs[i].gent.generic_ptype == gent)
798 return &gpt_ptype_descs[i].gent;
799
800 return NULL;
801 }
802
803 static const struct gpt_ptype_desc *
804 gpt_find_native_type(const struct part_type_desc *gent)
805 {
806 if (gpt_ptype_cnt == 0)
807 gpt_init_ptypes();
808
809 if (gent == NULL)
810 return NULL;
811
812 for (size_t i = 0; i < gpt_ptype_cnt; i++)
813 if (gent == &gpt_ptype_descs[i].gent)
814 return &gpt_ptype_descs[i];
815
816 gent = gpt_get_generic_type(gent->generic_ptype);
817 if (gent == NULL)
818 return NULL;
819
820 /* this can not recurse deeper than once, we would not have found a
821 * generic type a few lines above if it would. */
822 return gpt_find_native_type(gent);
823 }
824
825 static const struct gpt_ptype_desc *
826 gpt_find_guid_type(const char *uid)
827 {
828 if (gpt_ptype_cnt == 0)
829 gpt_init_ptypes();
830
831 if (uid == NULL || uid[0] == 0)
832 return NULL;
833
834 for (size_t i = 0; i < gpt_ptype_cnt; i++)
835 if (strcmp(gpt_ptype_descs[i].tid, uid) == 0)
836 return &gpt_ptype_descs[i];
837
838 return NULL;
839 }
840
841 static const struct part_type_desc *
842 gpt_find_type(const char *desc)
843 {
844 if (gpt_ptype_cnt == 0)
845 gpt_init_ptypes();
846
847 if (desc == NULL || desc[0] == 0)
848 return NULL;
849
850 for (size_t i = 0; i < gpt_ptype_cnt; i++)
851 if (strcmp(gpt_ptype_descs[i].gent.short_desc, desc) == 0)
852 return &gpt_ptype_descs[i].gent;
853
854 return NULL;
855 }
856
857 static const struct part_type_desc *
858 gpt_get_fs_part_type(unsigned fstype, unsigned fs_sub_type)
859 {
860 size_t i;
861
862 for (i = 0; i < __arraycount(gpt_fs_types); i++)
863 if (fstype == gpt_fs_types[i].fstype)
864 return gpt_find_type(gpt_fs_types[i].name);
865
866 return gpt_get_generic_type(PT_root);
867 }
868
869 static daddr_t
870 gpt_get_part_alignment(const struct disk_partitions *parts)
871 {
872
873 assert(parts->disk_size > 0);
874 if (parts->disk_size < 0)
875 return 1;
876
877 /* Use 1MB offset/alignemnt for large (>128GB) disks */
878 if (parts->disk_size > HUGE_DISK_SIZE)
879 return 2048;
880 else if (parts->disk_size > TINY_DISK_SIZE)
881 return 64;
882 else
883 return 4;
884 }
885
886 static bool
887 gpt_can_add_partition(const struct disk_partitions *arg)
888 {
889 const struct gpt_disk_partitions *parts =
890 (const struct gpt_disk_partitions*)arg;
891 struct disk_part_free_space space;
892 daddr_t align;
893
894 if (parts->dp.num_part >= parts->max_num_parts)
895 return false;
896
897 align = gpt_get_part_alignment(arg);
898 if (parts->dp.free_space <= align)
899 return false;
900
901 if (gpt_get_free_spaces_internal(parts, &space, 1, align, align,
902 0, -1) < 1)
903 return false;
904
905 return true;
906 }
907
908 static bool
909 gpt_info_to_part(struct gpt_part_entry *p, const struct disk_part_info *info,
910 const char **err_msg)
911 {
912 p->gp_type = gpt_find_native_type(info->nat_type);
913 p->gp_start = info->start;
914 p->gp_size = info->size;
915 if (info->last_mounted != NULL && info->last_mounted !=
916 p->last_mounted) {
917 free(__UNCONST(p->last_mounted));
918 p->last_mounted = strdup(info->last_mounted);
919 }
920 p->fs_type = info->fs_type;
921 p->fs_sub_type = info->fs_sub_type;
922
923 return true;
924 }
925
926 static part_id
927 gpt_add_part(struct disk_partitions *arg,
928 const struct disk_part_info *info, const char **err_msg)
929 {
930 struct gpt_disk_partitions *parts =
931 (struct gpt_disk_partitions*)arg;
932 struct disk_part_free_space space;
933 struct disk_part_info data = *info;
934 struct gpt_part_entry *p;
935 bool ok;
936
937 if (err_msg != NULL)
938 *err_msg = NULL;
939
940 if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 1,
941 info->start, -1) < 1) {
942 if (err_msg)
943 *err_msg = msg_string(MSG_No_free_space);
944 return NO_PART;
945 }
946 if (parts->dp.num_part >= parts->max_num_parts) {
947 if (err_msg)
948 *err_msg = msg_string(MSG_err_too_many_partitions);
949 return NO_PART;
950 }
951
952 if (data.size > space.size)
953 data.size = space.size;
954
955 p = calloc(1, sizeof(*p));
956 if (p == NULL) {
957 if (err_msg != NULL)
958 *err_msg = INTERNAL_ERROR;
959 return NO_PART;
960 }
961 if (!gpt_info_to_part(p, &data, err_msg)) {
962 free(p);
963 return NO_PART;
964 }
965 p->gp_flags |= GPEF_MODIFIED;
966 ok = gpt_insert_part_into_list(parts, &parts->partitions, p, err_msg);
967 if (ok) {
968 parts->dp.num_part++;
969 parts->dp.free_space -= p->gp_size;
970 return parts->dp.num_part-1;
971 } else {
972 free(p);
973 return NO_PART;
974 }
975 }
976
977 static bool
978 gpt_delete_partition(struct disk_partitions *arg, part_id id,
979 const char **err_msg)
980 {
981 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
982 struct gpt_part_entry *p, *last = NULL;
983 part_id i;
984 bool res;
985
986 if (parts->dp.num_part == 0)
987 return false;
988
989 for (i = 0, p = parts->partitions;
990 i != id && i < parts->dp.num_part && p != NULL;
991 i++, p = p->gp_next)
992 last = p;
993
994 if (p == NULL) {
995 if (err_msg)
996 *err_msg = INTERNAL_ERROR;
997 return false;
998 }
999
1000 if (last == NULL)
1001 parts->partitions = p->gp_next;
1002 else
1003 last->gp_next = p->gp_next;
1004
1005 res = true;
1006 if (p->gp_flags & GPEF_ON_DISK) {
1007 if (!gpt_insert_part_into_list(parts, &parts->obsolete,
1008 p, err_msg))
1009 res = false;
1010 } else {
1011 free(p);
1012 }
1013
1014 if (res) {
1015 parts->dp.num_part--;
1016 parts->dp.free_space += p->gp_size;
1017 }
1018
1019 return res;
1020 }
1021
1022 static bool
1023 gpt_delete_all_partitions(struct disk_partitions *arg)
1024 {
1025 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
1026
1027 while (parts->dp.num_part > 0) {
1028 if (!gpt_delete_partition(&parts->dp, 0, NULL))
1029 return false;
1030 }
1031
1032 return true;
1033 }
1034
1035 static bool
1036 gpt_read_part(const char *disk, daddr_t start, struct gpt_part_entry *p)
1037 {
1038 char *textbuf, *t, *tt;
1039 static const char expected_hdr[] = "Details for index ";
1040
1041 /* run gpt show for this partition */
1042 if (collect(T_OUTPUT, &textbuf,
1043 "gpt -r show -b %" PRIu64 " %s 2>/dev/null", start, disk) < 1)
1044 return false;
1045
1046 /*
1047 * gpt show should respond with single partition details, but will
1048 * fall back to "show -a" output if something is wrong
1049 */
1050 t = strtok(textbuf, "\n"); /* first line is special */
1051 if (strncmp(t, expected_hdr, sizeof(expected_hdr)-1) != 0) {
1052 free(textbuf);
1053 return false;
1054 }
1055
1056 /* parse output into "old" */
1057 while ((t = strtok(NULL, "\n")) != NULL) {
1058 tt = strsep(&t, " \t");
1059 if (strlen(tt) == 0)
1060 continue;
1061 gpt_add_info(p, tt, t, true);
1062 }
1063 free(textbuf);
1064
1065 return true;
1066 }
1067
1068 static bool
1069 gpt_apply_attr(const char *disk, const char *cmd, off_t start, uint todo)
1070 {
1071 size_t i;
1072 char attr_str[STRSIZE];
1073
1074 if (todo == 0)
1075 return true;
1076
1077 strcpy(attr_str, "-a ");
1078 for (i = 0; todo != 0; i++) {
1079 if (!(gpt_avail_attrs[i].flag & todo))
1080 continue;
1081 todo &= ~gpt_avail_attrs[i].flag;
1082 if (attr_str[0])
1083 strlcat(attr_str, ",",
1084 sizeof(attr_str));
1085 strlcat(attr_str,
1086 gpt_avail_attrs[i].name,
1087 sizeof(attr_str));
1088 }
1089 if (run_program(RUN_SILENT,
1090 "gpt %s %s -b %" PRIu64 " %s", cmd, attr_str, start, disk) != 0)
1091 return false;
1092 return true;
1093 }
1094
1095 /*
1096 * Modify an existing on-disk partition.
1097 * Start and size can not be changed here, caller needs to deal
1098 * with that kind of changes upfront.
1099 */
1100 static bool
1101 gpt_modify_part(const char *disk, struct gpt_part_entry *p)
1102 {
1103 struct gpt_part_entry old;
1104 uint todo_set, todo_unset;
1105
1106 /*
1107 * Query current on-disk state
1108 */
1109 memset(&old, 0, sizeof old);
1110 if (!gpt_read_part(disk, p->gp_start, &old))
1111 return false;
1112
1113 /* Reject unsupported changes */
1114 if (old.gp_start != p->gp_start || old.gp_size != p->gp_size)
1115 return false;
1116
1117 /*
1118 * GUID should never change, but the internal copy
1119 * may not yet know it.
1120 */
1121 strcpy(p->gp_id, old.gp_id);
1122
1123 /* Check type */
1124 if (p->gp_type != old.gp_type) {
1125 if (run_program(RUN_SILENT,
1126 "gpt label -b %" PRIu64 " -T %s %s",
1127 p->gp_start, p->gp_type->tid, disk) != 0)
1128 return false;
1129 }
1130
1131 /* Check label */
1132 if (strcmp(p->gp_label, old.gp_label) != 0) {
1133 if (run_program(RUN_SILENT,
1134 "gpt label -b %" PRIu64 " -l \'%s\' %s",
1135 p->gp_start, p->gp_label, disk) != 0)
1136 return false;
1137 }
1138
1139 /* Check attributes */
1140 if (p->gp_attr != old.gp_attr) {
1141 if (p->gp_attr == 0) {
1142 if (run_program(RUN_SILENT,
1143 "gpt set -N -b %" PRIu64 " %s",
1144 p->gp_start, disk) != 0)
1145 return false;
1146 } else {
1147 todo_set = (p->gp_attr ^ old.gp_attr) & p->gp_attr;
1148 todo_unset = (p->gp_attr ^ old.gp_attr) & old.gp_attr;
1149 if (!gpt_apply_attr(disk, "unset", p->gp_start,
1150 todo_unset))
1151 return false;
1152 if (!gpt_apply_attr(disk, "set", p->gp_start,
1153 todo_set))
1154 return false;
1155 }
1156 }
1157
1158 return true;
1159 }
1160
1161 /*
1162 * verbatim copy from sys/dev/dkwedge/dkwedge_bsdlabel.c:
1163 * map FS_* to wedge strings
1164 */
1165 static const char *
1166 bsdlabel_fstype_to_str(uint8_t fstype)
1167 {
1168 const char *str;
1169
1170 /*
1171 * For each type known to FSTYPE_DEFN (from <sys/disklabel.h>),
1172 * a suitable case branch will convert the type number to a string.
1173 */
1174 switch (fstype) {
1175 #define FSTYPE_TO_STR_CASE(tag, number, name, fsck, mount) \
1176 case __CONCAT(FS_,tag): str = __CONCAT(DKW_PTYPE_,tag); break;
1177 FSTYPE_DEFN(FSTYPE_TO_STR_CASE)
1178 #undef FSTYPE_TO_STR_CASE
1179 default: str = NULL; break;
1180 }
1181
1182 return (str);
1183 }
1184
1185 static bool
1186 gpt_add_wedge(const char *disk, struct gpt_part_entry *p)
1187 {
1188 struct dkwedge_info dkw;
1189 const char *tname;
1190 char diskpath[MAXPATHLEN];
1191 int fd;
1192
1193 memset(&dkw, 0, sizeof(dkw));
1194 tname = bsdlabel_fstype_to_str(p->fs_type);
1195 if (tname)
1196 strlcpy(dkw.dkw_ptype, tname, sizeof(dkw.dkw_ptype));
1197
1198 strlcpy((char*)&dkw.dkw_wname, p->gp_id, sizeof(dkw.dkw_wname));
1199 dkw.dkw_offset = p->gp_start;
1200 dkw.dkw_size = p->gp_size;
1201
1202 fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0);
1203 if (fd < 0)
1204 return false;
1205 if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) {
1206 close(fd);
1207 return false;
1208 }
1209 close(fd);
1210
1211 strlcpy(p->gp_dev_name, dkw.dkw_devname, sizeof(p->gp_dev_name));
1212 p->gp_flags |= GPEF_WEDGE;
1213 return true;
1214 }
1215
1216 static void
1217 escape_spaces(char *dest, const char *src)
1218 {
1219 unsigned char c;
1220
1221 while (*src) {
1222 c = *src++;
1223 if (isspace(c) || c == '\\')
1224 *dest++ = '\\';
1225 *dest++ = c;
1226 }
1227 *dest = 0;
1228 }
1229
1230 static bool
1231 gpt_get_part_device(const struct disk_partitions *arg,
1232 part_id id, char *devname, size_t max_devname_len, int *part,
1233 enum dev_name_usage usage, bool with_path)
1234 {
1235 const struct gpt_disk_partitions *parts =
1236 (const struct gpt_disk_partitions*)arg;
1237 struct gpt_part_entry *p = parts->partitions;
1238 char tmpname[GPT_LABEL_LEN*2];
1239 part_id no;
1240
1241
1242 for (no = 0; p != NULL && no < id; no++)
1243 p = p->gp_next;
1244
1245 if (no != id || p == NULL)
1246 return false;
1247
1248 if (part)
1249 *part = -1;
1250
1251 if (!(p->gp_flags & GPEF_WEDGE) &&
1252 (usage == plain_name || usage == raw_dev_name))
1253 gpt_add_wedge(arg->disk, p);
1254
1255 switch (usage) {
1256 case logical_name:
1257 if (p->gp_label[0] != 0) {
1258 escape_spaces(tmpname, p->gp_label);
1259 snprintf(devname, max_devname_len,
1260 "NAME=%s", tmpname);
1261 } else {
1262 snprintf(devname, max_devname_len,
1263 "NAME=%s", p->gp_id);
1264 }
1265 break;
1266 case plain_name:
1267 assert(p->gp_flags & GPEF_WEDGE);
1268 if (with_path)
1269 snprintf(devname, max_devname_len, _PATH_DEV "%s",
1270 p->gp_dev_name);
1271 else
1272 strlcpy(devname, p->gp_dev_name, max_devname_len);
1273 break;
1274 case raw_dev_name:
1275 assert(p->gp_flags & GPEF_WEDGE);
1276 if (with_path)
1277 snprintf(devname, max_devname_len, _PATH_DEV "r%s",
1278 p->gp_dev_name);
1279 else
1280 snprintf(devname, max_devname_len, "r%s",
1281 p->gp_dev_name);
1282 break;
1283 default:
1284 return false;
1285 }
1286
1287 return true;
1288 }
1289
1290 static bool
1291 gpt_write_to_disk(struct disk_partitions *arg)
1292 {
1293 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
1294 struct gpt_part_entry *p, *n;
1295 char label_arg[sizeof(p->gp_label) + 10];
1296 char diskpath[MAXPATHLEN];
1297 int fd, bits = 0;
1298 bool root_is_new = false, efi_is_new = false;
1299 part_id root_id = NO_PART, efi_id = NO_PART, pno;
1300
1301 /*
1302 * Remove all wedges on this disk - they may become invalid and we
1303 * have no easy way to associate them with the partitioning data.
1304 * Instead we will explicitly request creation of wedges on demand
1305 * later.
1306 */
1307 fd = opendisk(arg->disk, O_RDWR, diskpath, sizeof(diskpath), 0);
1308 if (fd < 0)
1309 return false;
1310 if (ioctl(fd, DIOCRMWEDGES, &bits) == -1)
1311 return false;
1312 close(fd);
1313
1314 /*
1315 * Collect first root and efi partition (if available)
1316 */
1317 for (pno = 0, p = parts->partitions; p != NULL; p = p->gp_next, pno++) {
1318 if (root_id == NO_PART && p->gp_type != NULL) {
1319 if (p->gp_type->gent.generic_ptype == PT_root &&
1320 p->gp_start == pm->ptstart) {
1321 root_id = pno;
1322 root_is_new = !(p->gp_flags & GPEF_ON_DISK);
1323 } else if (efi_id == NO_PART &&
1324 p->gp_type->gent.generic_ptype == PT_EFI_SYSTEM) {
1325 efi_id = pno;
1326 efi_is_new = !(p->gp_flags & GPEF_ON_DISK);
1327 }
1328 }
1329 }
1330
1331 /*
1332 * If no GPT on disk yet, create it.
1333 */
1334 if (!parts->has_gpt) {
1335 char limit[30];
1336
1337 if (parts->max_num_parts > 0)
1338 sprintf(limit, "-p %zu", parts->max_num_parts);
1339 else
1340 limit[0] = 0;
1341 if (run_program(RUN_SILENT, "gpt create %s %s",
1342 limit, parts->dp.disk))
1343 return false;
1344 parts->has_gpt = true;
1345 }
1346
1347 /*
1348 * Delete all old partitions
1349 */
1350 for (p = parts->obsolete; p != NULL; p = n) {
1351 run_program(RUN_SILENT, "gpt -n remove -b %" PRIu64 " %s",
1352 p->gp_start, arg->disk);
1353 n = p->gp_next;
1354 free(p);
1355 }
1356 parts->obsolete = NULL;
1357
1358 /*
1359 * Modify existing but changed partitions
1360 */
1361 for (p = parts->partitions; p != NULL; p = p->gp_next) {
1362 if (!(p->gp_flags & GPEF_ON_DISK))
1363 continue;
1364
1365 if (p->gp_flags & GPEF_RESIZED) {
1366 run_program(RUN_SILENT,
1367 "gpt -n resize -b %" PRIu64 " -s %" PRIu64 "s %s",
1368 p->gp_start, p->gp_size, arg->disk);
1369 p->gp_flags &= ~GPEF_RESIZED;
1370 }
1371
1372 if (!(p->gp_flags & GPEF_MODIFIED))
1373 continue;
1374
1375 if (!gpt_modify_part(parts->dp.disk, p))
1376 return false;
1377 }
1378
1379 /*
1380 * Add new partitions
1381 */
1382 for (p = parts->partitions; p != NULL; p = p->gp_next) {
1383 if (p->gp_flags & GPEF_ON_DISK)
1384 continue;
1385 if (!(p->gp_flags & GPEF_MODIFIED))
1386 continue;
1387
1388 if (p->gp_label[0] == 0)
1389 label_arg[0] = 0;
1390 else
1391 sprintf(label_arg, "-l \'%s\'", p->gp_label);
1392
1393 if (p->gp_type != NULL)
1394 run_program(RUN_SILENT,
1395 "gpt -n add -b %" PRIu64 " -s %" PRIu64
1396 "s -t %s %s %s",
1397 p->gp_start, p->gp_size, p->gp_type->tid,
1398 label_arg, arg->disk);
1399 else
1400 run_program(RUN_SILENT,
1401 "gpt -n add -b %" PRIu64 " -s %" PRIu64
1402 "s %s %s",
1403 p->gp_start, p->gp_size, label_arg, arg->disk);
1404 gpt_apply_attr(arg->disk, "set", p->gp_start, p->gp_attr);
1405 gpt_read_part(arg->disk, p->gp_start, p);
1406 p->gp_flags |= GPEF_ON_DISK;
1407 }
1408
1409 /*
1410 * Additional MD bootloader magic...
1411 */
1412 if (!md_gpt_post_write(&parts->dp, root_id, root_is_new, efi_id,
1413 efi_is_new))
1414 return false;
1415
1416 return true;
1417 }
1418
1419 static part_id
1420 gpt_find_by_name(struct disk_partitions *arg, const char *name)
1421 {
1422 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
1423 struct gpt_part_entry *p;
1424 part_id pno;
1425
1426 for (pno = 0, p = parts->partitions; p != NULL;
1427 p = p->gp_next, pno++) {
1428 if (strcmp(p->gp_label, name) == 0)
1429 return pno;
1430 if (strcmp(p->gp_id, name) == 0)
1431 return pno;
1432 }
1433
1434 return NO_PART;
1435 }
1436
1437 bool
1438 gpt_parts_check(void)
1439 {
1440
1441 check_available_binaries();
1442
1443 return have_gpt && have_dk;
1444 }
1445
1446 static void
1447 gpt_free(struct disk_partitions *arg)
1448 {
1449 struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
1450 struct gpt_part_entry *p, *n;
1451
1452 assert(parts != NULL);
1453 for (p = parts->partitions; p != NULL; p = n) {
1454 free(__UNCONST(p->last_mounted));
1455 n = p->gp_next;
1456 free(p);
1457 }
1458 free(parts);
1459 }
1460
1461 static bool
1462 gpt_custom_attribute_writable(const struct disk_partitions *arg,
1463 part_id ptn, size_t attr_no)
1464 {
1465 const struct gpt_disk_partitions *parts =
1466 (const struct gpt_disk_partitions*)arg;
1467 size_t i;
1468 struct gpt_part_entry *p;
1469
1470 if (attr_no >= arg->pscheme->custom_attribute_count)
1471 return false;
1472
1473 const msg label = arg->pscheme->custom_attributes[attr_no].label;
1474
1475 /* we can not edit the uuid attribute */
1476 if (label == MSG_ptn_uuid)
1477 return false;
1478
1479 /* the label is always editable */
1480 if (label == MSG_ptn_label)
1481 return true;
1482
1483 /* the GPT type is read only */
1484 if (label == MSG_ptn_gpt_type)
1485 return false;
1486
1487 /* BOOTME makes no sense on swap partitions */
1488 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1489 if (i == ptn)
1490 break;
1491
1492 if (p == NULL)
1493 return false;
1494
1495 if (p->fs_type == FS_SWAP ||
1496 (p->gp_type != NULL && p->gp_type->gent.generic_ptype == PT_swap))
1497 return false;
1498
1499 return true;
1500 }
1501
1502 static const char *
1503 gpt_get_label_str(const struct disk_partitions *arg, part_id ptn)
1504 {
1505 const struct gpt_disk_partitions *parts =
1506 (const struct gpt_disk_partitions*)arg;
1507 size_t i;
1508 struct gpt_part_entry *p;
1509
1510 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1511 if (i == ptn)
1512 break;
1513
1514 if (p == NULL)
1515 return NULL;
1516
1517 if (p->gp_label[0] != 0)
1518 return p->gp_label;
1519 return p->gp_id;
1520 }
1521
1522 static bool
1523 gpt_format_custom_attribute(const struct disk_partitions *arg,
1524 part_id ptn, size_t attr_no, const struct disk_part_info *info,
1525 char *out, size_t out_space)
1526 {
1527 const struct gpt_disk_partitions *parts =
1528 (const struct gpt_disk_partitions*)arg;
1529 size_t i;
1530 struct gpt_part_entry *p, data;
1531
1532 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1533 if (i == ptn)
1534 break;
1535
1536 if (p == NULL)
1537 return false;
1538
1539 if (attr_no >= parts->dp.pscheme->custom_attribute_count)
1540 return false;
1541
1542 const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
1543
1544 if (info != NULL) {
1545 data = *p;
1546 gpt_info_to_part(&data, info, NULL);
1547 p = &data;
1548 }
1549
1550 if (label == MSG_ptn_label)
1551 strlcpy(out, p->gp_label, out_space);
1552 else if (label == MSG_ptn_uuid)
1553 strlcpy(out, p->gp_id, out_space);
1554 else if (label == MSG_ptn_gpt_type) {
1555 if (p->gp_type != NULL)
1556 strlcpy(out, p->gp_type->gent.description, out_space);
1557 else if (out_space > 1)
1558 out[0] = 0;
1559 } else if (label == MSG_ptn_boot)
1560 strlcpy(out, msg_string(p->gp_attr & GPT_ATTR_BOOT ?
1561 MSG_Yes : MSG_No), out_space);
1562 else
1563 return false;
1564
1565 return true;
1566 }
1567
1568 static bool
1569 gpt_custom_attribute_toggle(struct disk_partitions *arg,
1570 part_id ptn, size_t attr_no)
1571 {
1572 const struct gpt_disk_partitions *parts =
1573 (const struct gpt_disk_partitions*)arg;
1574 size_t i;
1575 struct gpt_part_entry *p;
1576
1577 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1578 if (i == ptn)
1579 break;
1580
1581 if (p == NULL)
1582 return false;
1583
1584 if (attr_no >= parts->dp.pscheme->custom_attribute_count)
1585 return false;
1586
1587 const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
1588 if (label != MSG_ptn_boot)
1589 return false;
1590
1591 if (p->gp_attr & GPT_ATTR_BOOT) {
1592 p->gp_attr &= ~GPT_ATTR_BOOT;
1593 } else {
1594 for (i = 0, p = parts->partitions; p != NULL;
1595 i++, p = p->gp_next)
1596 if (i == ptn)
1597 p->gp_attr |= GPT_ATTR_BOOT;
1598 else
1599 p->gp_attr &= ~GPT_ATTR_BOOT;
1600 }
1601 return true;
1602 }
1603
1604 static bool
1605 gpt_custom_attribute_set_str(struct disk_partitions *arg,
1606 part_id ptn, size_t attr_no, const char *new_val)
1607 {
1608 const struct gpt_disk_partitions *parts =
1609 (const struct gpt_disk_partitions*)arg;
1610 size_t i;
1611 struct gpt_part_entry *p;
1612
1613 for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
1614 if (i == ptn)
1615 break;
1616
1617 if (p == NULL)
1618 return false;
1619
1620 if (attr_no >= parts->dp.pscheme->custom_attribute_count)
1621 return false;
1622
1623 const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
1624
1625 if (label != MSG_ptn_label)
1626 return false;
1627
1628 strlcpy(p->gp_label, new_val, sizeof(p->gp_label));
1629 return true;
1630 }
1631
1632 static bool
1633 gpt_have_boot_support(const char *disk)
1634 {
1635 #ifdef HAVE_GPT_BOOT
1636 return true;
1637 #else
1638 return false;
1639 #endif
1640 }
1641
1642 const struct disk_part_custom_attribute gpt_custom_attrs[] = {
1643 { .label = MSG_ptn_label, .type = pet_str },
1644 { .label = MSG_ptn_uuid, .type = pet_str },
1645 { .label = MSG_ptn_gpt_type, .type = pet_str },
1646 { .label = MSG_ptn_boot, .type = pet_bool },
1647 };
1648
1649 const struct disk_partitioning_scheme
1650 gpt_parts = {
1651 .name = MSG_parttype_gpt,
1652 .short_name = MSG_parttype_gpt_short,
1653 .part_flag_desc = MSG_gpt_flag_desc,
1654 .custom_attribute_count = __arraycount(gpt_custom_attrs),
1655 .custom_attributes = gpt_custom_attrs,
1656 .get_part_types_count = gpt_type_count,
1657 .get_part_type = gpt_get_ptype,
1658 .get_generic_part_type = gpt_get_generic_type,
1659 .get_fs_part_type = gpt_get_fs_part_type,
1660 .get_part_alignment = gpt_get_part_alignment,
1661 .read_from_disk = gpt_read_from_disk,
1662 .create_new_for_disk = gpt_create_new,
1663 .have_boot_support = gpt_have_boot_support,
1664 .find_by_name = gpt_find_by_name,
1665 .can_add_partition = gpt_can_add_partition,
1666 .custom_attribute_writable = gpt_custom_attribute_writable,
1667 .format_custom_attribute = gpt_format_custom_attribute,
1668 .custom_attribute_toggle = gpt_custom_attribute_toggle,
1669 .custom_attribute_set_str = gpt_custom_attribute_set_str,
1670 .other_partition_identifier = gpt_get_label_str,
1671 .get_part_device = gpt_get_part_device,
1672 .max_free_space_at = gpt_max_free_space_at,
1673 .get_free_spaces = gpt_get_free_spaces,
1674 .adapt_foreign_part_info = gpt_adapt,
1675 .get_part_info = gpt_get_part_info,
1676 .get_part_attr_str = gpt_get_part_attr_str,
1677 .set_part_info = gpt_set_part_info,
1678 .add_partition = gpt_add_part,
1679 .delete_all_partitions = gpt_delete_all_partitions,
1680 .delete_partition = gpt_delete_partition,
1681 .write_to_disk = gpt_write_to_disk,
1682 .free = gpt_free,
1683 };
1684