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