xref: /netbsd/usr.sbin/sysinst/gpt.c (revision c0e3ce44)
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