xref: /openbsd-src/sbin/disklabel/editor.c (revision bb12209f65cd56bcba616e06553811e22120014f)
1 /*	$OpenBSD: editor.c,v 1.414 2023/07/03 06:07:40 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2000 Todd C. Miller <millert@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>	/* MAXBSIZE DEV_BSIZE */
20 #include <sys/types.h>
21 #include <sys/signal.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <sys/dkio.h>
25 #include <sys/sysctl.h>
26 #define	DKTYPENAMES
27 #include <sys/disklabel.h>
28 
29 #include <ufs/ffs/fs.h>
30 
31 #include <ctype.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <libgen.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <limits.h>
41 
42 #include "extern.h"
43 #include "pathnames.h"
44 
45 #define	ROUNDUP(_s, _a)		((((_s) + (_a) - 1) / (_a)) * (_a))
46 #define	ROUNDDOWN(_s, _a)	(((_s) / (_a)) * (_a))
47 #define	CHUNKSZ(_c)		((_c)->stop - (_c)->start)
48 
49 /* flags for getuint64() */
50 #define	DO_CONVERSIONS	0x00000001
51 #define	DO_ROUNDING	0x00000002
52 
53 /* flags for alignpartition() */
54 #define	ROUND_OFFSET_UP		0x00000001
55 #define	ROUND_OFFSET_DOWN	0x00000002
56 #define	ROUND_SIZE_UP		0x00000004
57 #define	ROUND_SIZE_DOWN		0x00000008
58 #define	ROUND_SIZE_OVERLAP	0x00000010
59 
60 /* Special return values for getnumber and getuint64() */
61 #define	CMD_ABORTED	(ULLONG_MAX - 1)
62 #define	CMD_BADVALUE	(ULLONG_MAX)
63 
64 /* structure to describe a portion of a disk */
65 struct diskchunk {
66 	u_int64_t start;
67 	u_int64_t stop;
68 };
69 
70 /* used when sorting mountpoints in mpsave() */
71 struct mountinfo {
72 	char *mountpoint;
73 	int partno;
74 };
75 
76 /* used when allocating all space according to recommendations */
77 
78 struct space_allocation {
79 	u_int64_t	minsz;	/* starts as blocks, xlated to sectors. */
80 	u_int64_t	maxsz;	/* starts as blocks, xlated to sectors. */
81 	int		rate;	/* % of extra space to use */
82 	char	       *mp;
83 };
84 
85 /*
86  * NOTE! Changing partition sizes in the space_allocation tables
87  *       requires corresponding updates to the *.ok files in
88  *	 /usr/src/regress/sbin/disklabel.
89  */
90 
91 /* entries for swap and var are changed by editor_allocspace() */
92 struct space_allocation alloc_big[] = {
93 	{  MEG(150),         GIG(1),   5, "/"		},
94 	{   MEG(80),       MEG(256),  10, "swap"	},
95 	{  MEG(120),         GIG(4),   8, "/tmp"	},
96 	{   MEG(80),         GIG(4),  13, "/var"	},
97 	{ MEG(1500),        GIG(30),  10, "/usr"	},
98 	{  MEG(384),         GIG(1),   3, "/usr/X11R6"	},
99 	{    GIG(1),        GIG(20),  15, "/usr/local"	},
100 	{ MEG(1500),         GIG(3),   2, "/usr/src"	},
101 	{    GIG(5),         GIG(6),   4, "/usr/obj"	},
102 	{    GIG(1),       GIG(300),  30, "/home"	}
103 	/* Anything beyond this leave for the user to decide */
104 };
105 
106 struct space_allocation alloc_medium[] = {
107 	{  MEG(800),         GIG(2),   5, "/"		},
108 	{   MEG(80),       MEG(256),  10, "swap"	},
109 	{ MEG(1300),         GIG(3),  78, "/usr"	},
110 	{  MEG(256),         GIG(2),   7, "/home"	}
111 };
112 
113 struct space_allocation alloc_small[] = {
114 	{  MEG(700),         GIG(4),  95, "/"		},
115 	{    MEG(1),       MEG(256),   5, "swap"	}
116 };
117 
118 struct space_allocation alloc_stupid[] = {
119 	{    MEG(1),      MEG(2048), 100, "/"		}
120 };
121 
122 #ifndef nitems
123 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
124 #endif
125 
126 struct alloc_table {
127 	struct space_allocation *table;
128 	int sz;
129 };
130 
131 struct alloc_table alloc_table_default[] = {
132 	{ alloc_big,	nitems(alloc_big) },
133 	{ alloc_medium,	nitems(alloc_medium) },
134 	{ alloc_small,	nitems(alloc_small) },
135 	{ alloc_stupid,	nitems(alloc_stupid) }
136 };
137 struct alloc_table *alloc_table = alloc_table_default;
138 int alloc_table_nitems = 4;
139 
140 void	edit_packname(struct disklabel *);
141 void	editor_resize(struct disklabel *, const char *);
142 void	editor_add(struct disklabel *, const char *);
143 void	editor_change(struct disklabel *, const char *);
144 u_int64_t editor_countfree(const struct disklabel *);
145 void	editor_delete(struct disklabel *, const char *);
146 void	editor_help(void);
147 void	editor_modify(struct disklabel *, const char *);
148 void	editor_name(const struct disklabel *, const char *);
149 char	*getstring(const char *, const char *, const char *);
150 u_int64_t getuint64(const struct disklabel *, char *, char *, u_int64_t,
151     u_int64_t, int *);
152 u_int64_t getnumber(const char *, const char *, u_int32_t, u_int32_t);
153 int	getpartno(const struct disklabel *, const char *, const char *);
154 int	has_overlap(struct disklabel *);
155 int	partition_cmp(const void *, const void *);
156 const struct partition **sort_partitions(const struct disklabel *, int);
157 void	find_bounds(const struct disklabel *);
158 void	set_bounds(struct disklabel *);
159 void	set_duid(struct disklabel *);
160 int	set_fragblock(struct disklabel *, int);
161 const struct diskchunk *free_chunks(const struct disklabel *, int);
162 int	micmp(const void *, const void *);
163 int	mpequal(char **, char **);
164 int	get_fstype(struct disklabel *, int);
165 int	get_mp(const struct disklabel *, int);
166 int	get_offset(struct disklabel *, int);
167 int	get_size(struct disklabel *, int);
168 void	zero_partitions(struct disklabel *);
169 u_int64_t max_partition_size(const struct disklabel *, int);
170 void	display_edit(const struct disklabel *, char);
171 void	psize(u_int64_t sz, char unit, const struct disklabel *lp);
172 char	*get_token(char **);
173 int	apply_unit(double, u_char, u_int64_t *);
174 int	parse_sizespec(const char *, double *, char **);
175 int	parse_sizerange(char *, u_int64_t *, u_int64_t *);
176 int	parse_pct(char *, int *);
177 int	alignpartition(struct disklabel *, int, u_int64_t, u_int64_t, int);
178 int	allocate_space(struct disklabel *, const struct alloc_table *);
179 void	allocate_physmemincr(struct space_allocation *);
180 int	allocate_partition(struct disklabel *, struct space_allocation *);
181 const struct diskchunk *allocate_diskchunk(const struct disklabel *,
182     const struct space_allocation *);
183 
184 static u_int64_t starting_sector;
185 static u_int64_t ending_sector;
186 static int resizeok;
187 
188 /*
189  * Simple partition editor.
190  */
191 int
192 editor(int f)
193 {
194 	struct disklabel origlabel, lastlabel, tmplabel, newlab = lab;
195 	struct partition *pp;
196 	FILE *fp;
197 	char buf[BUFSIZ], *cmd, *arg;
198 	char **omountpoints = NULL;
199 	char **origmountpoints = NULL, **tmpmountpoints = NULL;
200 	int i, error = 0;
201 
202 	/* Alloc and init mount point info */
203 	if (!(omountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
204 	    !(origmountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
205 	    !(tmpmountpoints = calloc(MAXPARTITIONS, sizeof(char *))))
206 		err(1, NULL);
207 
208 	/* How big is the OpenBSD portion of the disk?  */
209 	find_bounds(&newlab);
210 
211 	/* Make sure there is no partition overlap. */
212 	if (has_overlap(&newlab))
213 		errx(1, "can't run when there is partition overlap.");
214 
215 	/* If we don't have a 'c' partition, create one. */
216 	pp = &newlab.d_partitions[RAW_PART];
217 	if (newlab.d_npartitions <= RAW_PART || DL_GETPSIZE(pp) == 0) {
218 		puts("No 'c' partition found, adding one that spans the disk.");
219 		if (newlab.d_npartitions <= RAW_PART)
220 			newlab.d_npartitions = RAW_PART + 1;
221 		DL_SETPOFFSET(pp, 0);
222 		DL_SETPSIZE(pp, DL_GETDSIZE(&newlab));
223 		pp->p_fstype = FS_UNUSED;
224 		pp->p_fragblock = pp->p_cpg = 0;
225 	}
226 
227 #ifdef SUN_CYLCHECK
228 	if ((newlab.d_flags & D_VENDOR) && !quiet) {
229 		puts("This platform requires that partition offsets/sizes "
230 		    "be on cylinder boundaries.\n"
231 		    "Partition offsets/sizes will be rounded to the "
232 		    "nearest cylinder automatically.");
233 	}
234 #endif
235 
236 	/* Save the (U|u)ndo labels and mountpoints. */
237 	mpcopy(origmountpoints, mountpoints);
238 	origlabel = newlab;
239 	lastlabel = newlab;
240 
241 	puts("Label editor (enter '?' for help at any prompt)");
242 	for (;;) {
243 		fprintf(stdout, "%s%s> ", dkname,
244 		    (memcmp(&lab, &newlab, sizeof(newlab)) == 0) ? "" : "*");
245 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
246 			putchar('\n');
247 			buf[0] = 'q';
248 			buf[1] = '\0';
249 		}
250 		if ((cmd = strtok(buf, " \t\r\n")) == NULL)
251 			continue;
252 		arg = strtok(NULL, " \t\r\n");
253 
254 		if ((*cmd != 'u') && (*cmd != 'U')) {
255 			/*
256 			 * Save undo info in case the command tries to make
257 			 * changes but decides not to.
258 			 */
259 			tmplabel = lastlabel;
260 			lastlabel = newlab;
261 			mpcopy(tmpmountpoints, omountpoints);
262 			mpcopy(omountpoints, mountpoints);
263 		}
264 
265 		switch (*cmd) {
266 		case '?':
267 		case 'h':
268 			editor_help();
269 			break;
270 
271 		case 'A':
272 			if (ioctl(f, DIOCGPDINFO, &newlab) == -1) {
273 				warn("DIOCGPDINFO");
274 				newlab = lastlabel;
275 			} else {
276 				int oquiet = quiet;
277 				aflag = 1;
278 				quiet = 0;
279 				editor_allocspace(&newlab);
280 				quiet = oquiet;
281 			}
282 			break;
283 		case 'a':
284 			editor_add(&newlab, arg);
285 			break;
286 
287 		case 'b':
288 			set_bounds(&newlab);
289 			break;
290 
291 		case 'c':
292 			editor_change(&newlab, arg);
293 			break;
294 
295 		case 'D':
296 			if (ioctl(f, DIOCGPDINFO, &newlab) == -1)
297 				warn("DIOCGPDINFO");
298 			else {
299 				dflag = 1;
300 				for (i = 0; i < MAXPARTITIONS; i++) {
301 					free(mountpoints[i]);
302 					mountpoints[i] = NULL;
303 				}
304 			}
305 			break;
306 
307 		case 'd':
308 			editor_delete(&newlab, arg);
309 			break;
310 
311 		case 'e':
312 			edit_packname(&newlab);
313 			break;
314 
315 		case 'i':
316 			set_duid(&newlab);
317 			break;
318 
319 		case 'm':
320 			editor_modify(&newlab, arg);
321 			break;
322 
323 		case 'n':
324 			if (!fstabfile) {
325 				fputs("This option is not valid when run "
326 				    "without the -F or -f flags.\n", stderr);
327 				break;
328 			}
329 			editor_name(&newlab, arg);
330 			break;
331 
332 		case 'p':
333 			display_edit(&newlab, arg ? *arg : 0);
334 			break;
335 
336 		case 'l':
337 			display(stdout, &newlab, arg ? *arg : 0, 0);
338 			break;
339 
340 		case 'M': {
341 			sig_t opipe = signal(SIGPIPE, SIG_IGN);
342 			char *pager, *comm = NULL;
343 			extern const u_char manpage[];
344 			extern const int manpage_sz;
345 
346 			if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
347 				pager = _PATH_LESS;
348 
349 			if (asprintf(&comm, "gunzip -qc|%s", pager) != -1 &&
350 			    (fp = popen(comm, "w")) != NULL) {
351 				(void) fwrite(manpage, manpage_sz, 1, fp);
352 				pclose(fp);
353 			} else
354 				warn("unable to execute %s", pager);
355 
356 			free(comm);
357 			(void)signal(SIGPIPE, opipe);
358 			break;
359 		}
360 
361 		case 'q':
362 			if (donothing) {
363 				puts("In no change mode, not writing label.");
364 				goto done;
365 			}
366 
367 			/*
368 			 * If we haven't changed the original label, and it
369 			 * wasn't a default label or an auto-allocated label,
370 			 * there is no need to do anything before exiting. Note
371 			 * that 'w' will reset dflag and aflag to allow 'q' to
372 			 * exit without further questions.
373 			 */
374 			if (!dflag && !aflag &&
375 			    memcmp(&lab, &newlab, sizeof(newlab)) == 0) {
376 				puts("No label changes.");
377 				/* Save mountpoint info. */
378 				mpsave(&newlab);
379 				goto done;
380 			}
381 			do {
382 				arg = getstring("Write new label?",
383 				    "Write the modified label to disk?",
384 				    "y");
385 			} while (arg && tolower((unsigned char)*arg) != 'y' &&
386 			    tolower((unsigned char)*arg) != 'n');
387 			if (arg && tolower((unsigned char)*arg) == 'y') {
388 				if (writelabel(f, &newlab) == 0) {
389 					newlab = lab; /* lab now has UID info */
390 					goto done;
391 				}
392 				warnx("unable to write label");
393 			}
394 			error = 1;
395 			goto done;
396 			/* NOTREACHED */
397 			break;
398 
399 		case 'R':
400 			if (aflag && resizeok)
401 				editor_resize(&newlab, arg);
402 			else
403 				fputs("Resize only implemented for auto "
404 				    "allocated labels\n", stderr);
405 			break;
406 
407 		case 'r': {
408 			const struct diskchunk *chunk;
409 			uint64_t total = 0;
410 			/* Display free space. */
411 			chunk = free_chunks(&newlab, -1);
412 			for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
413 				total += CHUNKSZ(chunk);
414 				fprintf(stderr, "Free sectors: %16llu - %16llu "
415 				    "(%16llu)\n",
416 				    chunk->start, chunk->stop - 1,
417 				    CHUNKSZ(chunk));
418 			}
419 			fprintf(stderr, "Total free sectors: %llu.\n", total);
420 			break;
421 		}
422 
423 		case 's':
424 			if (arg == NULL) {
425 				arg = getstring("Filename",
426 				    "Name of the file to save label into.",
427 				    NULL);
428 				if (arg == NULL || *arg == '\0')
429 					break;
430 			}
431 			if ((fp = fopen(arg, "w")) == NULL) {
432 				warn("cannot open %s", arg);
433 			} else {
434 				display(fp, &newlab, 0, 1);
435 				(void)fclose(fp);
436 			}
437 			break;
438 
439 		case 'U':
440 			/*
441 			 * If we allow 'U' repeatedly, information would be
442 			 * lost. This way multiple 'U's followed by 'u' will
443 			 * undo the 'U's.
444 			 */
445 			if (memcmp(&newlab, &origlabel, sizeof(newlab)) ||
446 			    !mpequal(mountpoints, origmountpoints)) {
447 				tmplabel = newlab;
448 				newlab = origlabel;
449 				lastlabel = tmplabel;
450 				mpcopy(tmpmountpoints, mountpoints);
451 				mpcopy(mountpoints, origmountpoints);
452 				mpcopy(omountpoints, tmpmountpoints);
453 			}
454 			puts("Original label and mount points restored.");
455 			break;
456 
457 		case 'u':
458 			tmplabel = newlab;
459 			newlab = lastlabel;
460 			lastlabel = tmplabel;
461 			mpcopy(tmpmountpoints, mountpoints);
462 			mpcopy(mountpoints, omountpoints);
463 			mpcopy(omountpoints, tmpmountpoints);
464 			puts("Last change undone.");
465 			break;
466 
467 		case 'w':
468 			if (donothing)  {
469 				puts("In no change mode, not writing label.");
470 				break;
471 			}
472 
473 			/* Write label to disk. */
474 			if (writelabel(f, &newlab) != 0)
475 				warnx("unable to write label");
476 			else {
477 				dflag = aflag = 0;
478 				newlab = lab; /* lab now has UID info */
479 			}
480 			break;
481 
482 		case 'x':
483 			goto done;
484 			break;
485 
486 		case 'z':
487 			zero_partitions(&newlab);
488 			break;
489 
490 		case '\n':
491 			break;
492 
493 		default:
494 			printf("Unknown option: %c ('?' for help)\n", *cmd);
495 			break;
496 		}
497 
498 		/*
499 		 * If no changes were made to label or mountpoints, then
500 		 * restore undo info.
501 		 */
502 		if (memcmp(&newlab, &lastlabel, sizeof(newlab)) == 0 &&
503 		    (mpequal(mountpoints, omountpoints))) {
504 			lastlabel = tmplabel;
505 			mpcopy(omountpoints, tmpmountpoints);
506 		}
507 	}
508 done:
509 	mpfree(omountpoints, DISCARD);
510 	mpfree(origmountpoints, DISCARD);
511 	mpfree(tmpmountpoints, DISCARD);
512 	return error;
513 }
514 
515 /*
516  * Allocate all disk space according to standard recommendations for a
517  * root disk.
518  */
519 int
520 editor_allocspace(struct disklabel *lp_org)
521 {
522 	struct disklabel label;
523 	u_int64_t pstart, pend;
524 	int i;
525 
526 	/* How big is the OpenBSD portion of the disk?  */
527 	find_bounds(lp_org);
528 
529 	resizeok = 1;
530 	for (i = 0;  i < MAXPARTITIONS; i++) {
531 		if (i == RAW_PART)
532 			continue;
533 		pstart = DL_GETPOFFSET(&lp_org->d_partitions[i]);
534 		pend = pstart + DL_GETPSIZE(&lp_org->d_partitions[i]);
535 		if (((pstart >= starting_sector && pstart < ending_sector) ||
536 		    (pend > starting_sector && pend <= ending_sector)))
537 			resizeok = 0; /* Part of OBSD area is in use! */
538 	}
539 
540 	for (i = 0; i < alloc_table_nitems; i++) {
541 		memcpy(&label, lp_org, sizeof(label));
542 		if (allocate_space(&label, &alloc_table[i]) == 0) {
543 			memcpy(lp_org, &label, sizeof(struct disklabel));
544 			return 0;
545 		}
546 	}
547 
548 	return 1;
549 }
550 
551 const struct diskchunk *
552 allocate_diskchunk(const struct disklabel *lp,
553     const struct space_allocation *sa)
554 {
555 	const struct diskchunk *chunk;
556 	static struct diskchunk largest;
557 	uint64_t maxstop;
558 
559 	largest.start = largest.stop = 0;
560 
561 	chunk = free_chunks(lp, -1);
562 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
563 		if (CHUNKSZ(chunk) > CHUNKSZ(&largest))
564 			largest = *chunk;
565 	}
566 	maxstop = largest.start + DL_BLKTOSEC(lp, sa->maxsz);
567 	if (maxstop > largest.stop)
568 		maxstop = largest.stop;
569 #ifdef SUN_CYLCHECK
570 	if (lp->d_flags & D_VENDOR) {
571 		largest.start = ROUNDUP(largest.start, lp->d_secpercyl);
572 		maxstop = ROUNDUP(maxstop, lp->d_secpercyl);
573 		if (maxstop > largest.stop)
574 			maxstop -= lp->d_secpercyl;
575 		if (largest.start >= maxstop)
576 			largest.start = largest.stop = maxstop = 0;
577 	}
578 #endif
579 	if (maxstop < largest.stop)
580 		largest.stop = maxstop;
581 	if (CHUNKSZ(&largest) < DL_BLKTOSEC(lp, sa->minsz))
582 		return NULL;
583 
584 	return &largest;
585 }
586 
587 int
588 allocate_partition(struct disklabel *lp, struct space_allocation *sa)
589 {
590 	const struct diskchunk *chunk;
591 	struct partition *pp;
592 	unsigned int partno;
593 
594 	for (partno = 0; partno < nitems(lp->d_partitions); partno++) {
595 		if (partno == RAW_PART)
596 			continue;
597 		pp = &lp->d_partitions[partno];
598 		if (DL_GETPSIZE(pp) == 0 || pp->p_fstype == FS_UNUSED)
599 			break;
600 	}
601 	if (partno >= nitems(lp->d_partitions))
602 		return 1;		/* No free partition. */
603 
604 	/* Find appropriate chunk of free space. */
605 	chunk = allocate_diskchunk(lp, sa);
606 	if (chunk == NULL)
607 		return 1;
608 
609 	if (strcasecmp(sa->mp, "raid") == 0)
610 		pp->p_fstype = FS_RAID;
611 	else if (strcasecmp(sa->mp, "swap") == 0)
612 		pp->p_fstype = FS_SWAP;
613 	else if (sa->mp[0] == '/')
614 		pp->p_fstype = FS_BSDFFS;
615 	else
616 		return 1;
617 
618 	DL_SETPSIZE(pp, chunk->stop - chunk->start);
619 	DL_SETPOFFSET(pp, chunk->start);
620 
621 	if (pp->p_fstype == FS_BSDFFS && DL_GETPSIZE(pp) > 0) {
622 		mountpoints[partno] = strdup(sa->mp);
623 		if (mountpoints[partno] == NULL)
624 			err(1, NULL);
625 		if (set_fragblock(lp, partno))
626 			return 1;
627 	}
628 
629 	return 0;
630 }
631 
632 void
633 allocate_physmemincr(struct space_allocation *sa)
634 {
635 	u_int64_t memblks;
636 	extern int64_t physmem;
637 
638 	if (physmem == 0)
639 		return;
640 
641 	memblks = physmem / DEV_BSIZE;
642 	if (strcasecmp(sa->mp, "swap") == 0) {
643 		if (memblks < MEG(256))
644 			sa->minsz = sa->maxsz = 2 * memblks;
645 		else
646 			sa->maxsz += memblks;
647 	} else if (strcasecmp(sa->mp, "/var") == 0) {
648 		sa->maxsz += 2 * memblks;
649 	}
650 }
651 
652 int
653 allocate_space(struct disklabel *lp, const struct alloc_table *alloc_table)
654 {
655 	struct space_allocation sa[MAXPARTITIONS];
656 	u_int64_t maxsz, xtrablks;
657 	int i;
658 
659 	xtrablks = DL_SECTOBLK(lp, editor_countfree(lp));
660 	memset(sa, 0, sizeof(sa));
661 	for (i = 0; i < alloc_table->sz; i++) {
662 		sa[i] = alloc_table->table[i];
663 		if (alloc_table->table == alloc_big)
664 			allocate_physmemincr(&sa[i]);
665 		if (xtrablks < sa[i].minsz)
666 			return 1;	/* Too few free blocks. */
667 		xtrablks -= sa[i].minsz;
668 	}
669 	sa[alloc_table->sz - 1].rate = 100; /* Last allocation is greedy. */
670 
671 	for (i = lp->d_npartitions; i < MAXPARTITIONS; i++) {
672 		if (i == RAW_PART)
673 			continue;
674 		memset(&lp->d_partitions[i], 0, sizeof(lp->d_partitions[i]));
675 	}
676 	lp->d_npartitions = MAXPARTITIONS;
677 
678 	mpfree(mountpoints, KEEP);
679 	for (i = 0; i < alloc_table->sz; i++) {
680 		if (sa[i].rate == 100)
681 			maxsz = sa[i].minsz + xtrablks;
682 		else
683 			maxsz = sa[i].minsz + (xtrablks / 100) * sa[i].rate;
684 		if (maxsz < sa[i].maxsz)
685 			sa[i].maxsz = maxsz;
686 		if (allocate_partition(lp, &sa[i])) {
687 			mpfree(mountpoints, KEEP);
688 			return 1;
689 		}
690 	}
691 
692 	return 0;
693 }
694 
695 /*
696  * Resize a partition, moving all subsequent partitions
697  */
698 void
699 editor_resize(struct disklabel *lp, const char *p)
700 {
701 	struct disklabel label;
702 	struct partition *pp, *prev;
703 	u_int64_t ui, sz, off;
704 	int partno, i, flags, shrunk;
705 
706 	label = *lp;
707 
708 	if ((partno = getpartno(&label, p, "resize")) == -1)
709 		return;
710 
711 	pp = &label.d_partitions[partno];
712 	sz = DL_GETPSIZE(pp);
713 	if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP) {
714 		fputs("Cannot resize spoofed partition\n", stderr);
715 		return;
716 	}
717 	flags = DO_CONVERSIONS;
718 	ui = getuint64(lp, "[+|-]new size (with unit)",
719 	    "new size or amount to grow (+) or shrink (-) partition including "
720 	    "unit", sz, sz + editor_countfree(lp), &flags);
721 
722 	if (ui == CMD_ABORTED)
723 		return;
724 	else if (ui == CMD_BADVALUE)
725 		return;
726 	else if (ui == 0) {
727 		fputs("The size must be > 0 sectors\n", stderr);
728 		return;
729 	}
730 
731 #ifdef SUN_CYLCHECK
732 	if (lp->d_flags & D_VENDOR)
733 		ui = ROUNDUP(ui, lp->d_secpercyl);
734 #endif
735 	if (DL_GETPOFFSET(pp) + ui > ending_sector) {
736 		fputs("Amount too big\n", stderr);
737 		return;
738 	}
739 
740 	DL_SETPSIZE(pp, ui);
741 	pp->p_fragblock = 0;
742 	if (set_fragblock(&label, partno) == 1)
743 		return;
744 
745 	/*
746 	 * Pack partitions above the resized partition, leaving unused
747 	 * partitions alone.
748 	 */
749 	shrunk = -1;
750 	prev = pp;
751 	for (i = partno + 1; i < MAXPARTITIONS; i++) {
752 		if (i == RAW_PART)
753 			continue;
754 		pp = &label.d_partitions[i];
755 		if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP)
756 			continue;
757 		sz = DL_GETPSIZE(pp);
758 		if (sz == 0)
759 			continue;
760 
761 		off = DL_GETPOFFSET(prev) + DL_GETPSIZE(prev);
762 
763 		if (off < ending_sector) {
764 			DL_SETPOFFSET(pp, off);
765 			if (off + DL_GETPSIZE(pp) > ending_sector) {
766 				DL_SETPSIZE(pp, ending_sector - off);
767 				pp->p_fragblock = 0;
768 				if (set_fragblock(&label, i) == 1)
769 					return;
770 				shrunk = i;
771 			}
772 		} else {
773 			fputs("Amount too big\n", stderr);
774 			return;
775 		}
776 		prev = pp;
777 	}
778 
779 	if (shrunk != -1)
780 		fprintf(stderr, "Partition %c shrunk to %llu sectors to make "
781 		    "room\n", 'a' + shrunk,
782 		    DL_GETPSIZE(&label.d_partitions[shrunk]));
783 	*lp = label;
784 }
785 
786 /*
787  * Add a new partition.
788  */
789 void
790 editor_add(struct disklabel *lp, const char *p)
791 {
792 	struct partition *pp;
793 	const struct diskchunk *chunk;
794 	int partno;
795 	u_int64_t new_offset, new_size;
796 
797 	chunk = free_chunks(lp, -1);
798 	new_size = new_offset = 0;
799 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
800 		if (CHUNKSZ(chunk) > new_size) {
801 			new_size = CHUNKSZ(chunk);
802 			new_offset = chunk->start;
803 		}
804 	}
805 
806 #ifdef SUN_CYLCHECK
807 	if ((lp->d_flags & D_VENDOR) && new_size < lp->d_secpercyl) {
808 		fputs("No space left, you need to shrink a partition "
809 		    "(need at least one full cylinder)\n",
810 		    stderr);
811 		return;
812 	}
813 #endif
814 	if (new_size == 0) {
815 		fputs("No space left, you need to shrink a partition\n",
816 		    stderr);
817 		return;
818 	}
819 
820 	if ((partno = getpartno(lp, p, "add")) == -1)
821 		return;
822 	pp = &lp->d_partitions[partno];
823 	memset(pp, 0, sizeof(*pp));
824 
825 	/*
826 	 * Increase d_npartitions if necessary. Ensure all new partitions are
827 	 * zero'ed to avoid inadvertent overlaps.
828 	 */
829 	for(; lp->d_npartitions <= partno; lp->d_npartitions++)
830 		memset(&lp->d_partitions[lp->d_npartitions], 0, sizeof(*pp));
831 
832 	DL_SETPSIZE(pp, new_size);
833 	DL_SETPOFFSET(pp, new_offset);
834 	pp->p_fstype = partno == 1 ? FS_SWAP : FS_BSDFFS;
835 
836 	if (get_offset(lp, partno) == 0 &&
837 	    get_size(lp, partno) == 0 &&
838 	    get_fstype(lp, partno) == 0 &&
839 	    get_mp(lp, partno) == 0 &&
840 	    set_fragblock(lp, partno) == 0)
841 		return;
842 
843 	/* Bailed out at some point, so effectively delete the partition. */
844 	memset(pp, 0, sizeof(*pp));
845 }
846 
847 /*
848  * Set the mountpoint of an existing partition ('name').
849  */
850 void
851 editor_name(const struct disklabel *lp, const char *p)
852 {
853 	int partno;
854 
855 	if ((partno = getpartno(lp, p, "name")) == -1)
856 		return;
857 
858 	get_mp(lp, partno);
859 }
860 
861 /*
862  * Change an existing partition.
863  */
864 void
865 editor_modify(struct disklabel *lp, const char *p)
866 {
867 	struct partition opp, *pp;
868 	int partno;
869 
870 	if ((partno = getpartno(lp, p, "modify")) == -1)
871 		return;
872 
873 	pp = &lp->d_partitions[partno];
874 	opp = *pp;
875 
876 	if (get_offset(lp, partno) == 0 &&
877 	    get_size(lp, partno) == 0   &&
878 	    get_fstype(lp, partno) == 0 &&
879 	    get_mp(lp, partno) == 0 &&
880 	    set_fragblock(lp, partno) == 0)
881 		return;
882 
883 	/* Bailed out at some point, so undo any changes. */
884 	*pp = opp;
885 }
886 
887 /*
888  * Delete an existing partition.
889  */
890 void
891 editor_delete(struct disklabel *lp, const char *p)
892 {
893 	struct partition *pp;
894 	int partno;
895 
896 	if ((partno = getpartno(lp, p, "delete")) == -1)
897 		return;
898 	if (partno == lp->d_npartitions) {
899 		zero_partitions(lp);
900 		return;
901 	}
902 	pp = &lp->d_partitions[partno];
903 
904 	/* Really delete it (as opposed to just setting to "unused") */
905 	memset(pp, 0, sizeof(*pp));
906 	free(mountpoints[partno]);
907 	mountpoints[partno] = NULL;
908 }
909 
910 /*
911  * Change the size of an existing partition.
912  */
913 void
914 editor_change(struct disklabel *lp, const char *p)
915 {
916 	struct partition *pp;
917 	int partno;
918 
919 	if ((partno = getpartno(lp, p, "change size")) == -1)
920 		return;
921 
922 	pp = &lp->d_partitions[partno];
923 	printf("Partition %c is currently %llu sectors in size, and can have "
924 	    "a maximum\nsize of %llu sectors.\n",
925 	    'a' + partno, DL_GETPSIZE(pp), max_partition_size(lp, partno));
926 
927 	/* Get new size */
928 	get_size(lp, partno);
929 }
930 
931 /*
932  * Sort the partitions based on starting offset.
933  * This assumes there can be no overlap.
934  */
935 int
936 partition_cmp(const void *e1, const void *e2)
937 {
938 	struct partition *p1 = *(struct partition **)e1;
939 	struct partition *p2 = *(struct partition **)e2;
940 	u_int64_t o1 = DL_GETPOFFSET(p1);
941 	u_int64_t o2 = DL_GETPOFFSET(p2);
942 
943 	if (o1 < o2)
944 		return -1;
945 	else if (o1 > o2)
946 		return 1;
947 	else
948 		return 0;
949 }
950 
951 char *
952 getstring(const char *prompt, const char *helpstring, const char *oval)
953 {
954 	static char buf[BUFSIZ];
955 	int n;
956 
957 	buf[0] = '\0';
958 	do {
959 		printf("%s: [%s] ", prompt, oval ? oval : "");
960 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
961 			buf[0] = '\0';
962 			if (feof(stdin)) {
963 				clearerr(stdin);
964 				putchar('\n');
965 				fputs("Command aborted\n", stderr);
966 				return NULL;
967 			}
968 		}
969 		n = strlen(buf);
970 		if (n > 0 && buf[n-1] == '\n')
971 			buf[--n] = '\0';
972 		if (buf[0] == '?')
973 			puts(helpstring);
974 		else if (oval != NULL && buf[0] == '\0')
975 			strlcpy(buf, oval, sizeof(buf));
976 	} while (buf[0] == '?');
977 
978 	return &buf[0];
979 }
980 
981 int
982 getpartno(const struct disklabel *lp, const char *p, const char *action)
983 {
984 	char buf[2] = { '\0', '\0'};
985 	const char *promptfmt = "partition to %s";
986 	const char *helpfmt = "Partition must be between 'a' and '%c' "
987 	    "(excluding 'c')%s.\n";
988 	const struct partition *pp;
989 	char *help = NULL, *prompt = NULL;
990 	unsigned char maxpart;
991 	unsigned int partno;
992 	int add, delete, inuse;
993 
994 	add = strcmp("add", action) == 0;
995 	delete = strcmp("delete", action) == 0;
996 	maxpart = 'a' - 1 + (add ? MAXPARTITIONS : lp->d_npartitions);
997 
998 	if (p == NULL) {
999 		if (asprintf(&prompt, promptfmt, action) == -1 ||
1000 		    asprintf(&help, helpfmt, maxpart, delete ? ", or '*'" : "")
1001 		    == -1) {
1002 			fprintf(stderr, "Unable to build prompt or help\n");
1003 			goto done;
1004 		}
1005 		if (add) {
1006 			/* Default to first unused partition. */
1007 			for (partno = 0; partno < MAXPARTITIONS; partno++) {
1008 				if (partno == RAW_PART)
1009 					continue;
1010 				pp = &lp->d_partitions[partno];
1011 				if (partno >= lp->d_npartitions ||
1012 				    DL_GETPSIZE(pp) == 0 ||
1013 				    pp->p_fstype == FS_UNUSED) {
1014 					buf[0] = 'a' + partno;
1015 					p = buf;
1016 					break;
1017 				}
1018 			}
1019 		}
1020 		p = getstring(prompt, help, p);
1021 		free(prompt);
1022 		free(help);
1023 		if (p == NULL || *p == '\0')
1024 			goto done;
1025 	}
1026 
1027 	if (delete && strlen(p) == 1 && *p == '*')
1028 		return lp->d_npartitions;
1029 
1030 	if (strlen(p) > 1 || *p < 'a' || *p > maxpart || *p == 'c') {
1031 		fprintf(stderr, helpfmt, maxpart, delete ? ", or '*'" : "");
1032 		goto done;
1033 	}
1034 
1035 	partno = *p - 'a';
1036 	pp = &lp->d_partitions[partno];
1037 	inuse = partno < lp->d_npartitions && DL_GETPSIZE(pp) > 0 &&
1038 	    pp->p_fstype != FS_UNUSED;
1039 
1040 	if ((add && !inuse) || (!add && inuse))
1041 		return partno;
1042 
1043 	fprintf(stderr, "Partition '%c' is %sin use.\n", *p,
1044 	    inuse ? "" : "not ");
1045 
1046  done:
1047 	return -1;
1048 }
1049 
1050 /*
1051  * Returns
1052  * 0 .. CMD_ABORTED - 1	==> valid value
1053  * CMD_BADVALUE		==> invalid value
1054  * CMD_ABORTED		==> ^D on input
1055  */
1056 u_int64_t
1057 getnumber(const char *prompt, const char *helpstring, u_int32_t oval,
1058     u_int32_t maxval)
1059 {
1060 	char buf[BUFSIZ], *p;
1061 	int rslt;
1062 	long long rval;
1063 	const char *errstr;
1064 
1065 	rslt = snprintf(buf, sizeof(buf), "%u", oval);
1066 	if (rslt < 0 || (unsigned int)rslt >= sizeof(buf))
1067 		return CMD_BADVALUE;
1068 
1069 	p = getstring(prompt, helpstring, buf);
1070 	if (p == NULL)
1071 		return CMD_ABORTED;
1072 	if (strlen(p) == 0)
1073 		return oval;
1074 
1075 	rval = strtonum(p, 0, maxval, &errstr);
1076 	if (errstr != NULL) {
1077 		printf("%s must be between 0 and %u\n", prompt, maxval);
1078 		return CMD_BADVALUE;
1079 	}
1080 
1081 	return rval;
1082 }
1083 
1084 /*
1085  * Returns
1086  * 0 .. CMD_ABORTED - 1	==> valid value
1087  * CMD_BADVALUE		==> invalid value
1088  * CMD_ABORTED		==> ^D on input
1089  */
1090 u_int64_t
1091 getuint64(const struct disklabel *lp, char *prompt, char *helpstring,
1092     u_int64_t oval, u_int64_t maxval, int *flags)
1093 {
1094 	char buf[21], *p, operator = '\0';
1095 	char *unit = NULL;
1096 	u_int64_t rval = oval;
1097 	double d;
1098 	int rslt;
1099 
1100 	rslt = snprintf(buf, sizeof(buf), "%llu", oval);
1101 	if (rslt < 0 || (unsigned int)rslt >= sizeof(buf))
1102 		goto invalid;
1103 
1104 	p = getstring(prompt, helpstring, buf);
1105 	if (p == NULL)
1106 		return CMD_ABORTED;
1107 	else if (p[0] == '\0')
1108 		rval = oval;
1109 	else if (p[0] == '*' && p[1] == '\0')
1110 		rval = maxval;
1111 	else {
1112 		if (*p == '+' || *p == '-')
1113 			operator = *p++;
1114 		if (parse_sizespec(p, &d, &unit) == -1)
1115 			goto invalid;
1116 		if (unit == NULL)
1117 			rval = d;
1118 		else if (flags != NULL && (*flags & DO_CONVERSIONS) == 0)
1119 			goto invalid;
1120 		else {
1121 			switch (tolower((unsigned char)*unit)) {
1122 			case 'b':
1123 				rval = d / lp->d_secsize;
1124 				break;
1125 			case 'c':
1126 				rval = d * lp->d_secpercyl;
1127 				break;
1128 			case '%':
1129 				rval = DL_GETDSIZE(lp) * (d / 100.0);
1130 				break;
1131 			case '&':
1132 				rval = maxval * (d / 100.0);
1133 				break;
1134 			default:
1135 				if (apply_unit(d, *unit, &rval) == -1)
1136 					goto invalid;
1137 				rval = DL_BLKTOSEC(lp, rval);
1138 				break;
1139 			}
1140 		}
1141 
1142 		/* Range check then apply [+-] operator */
1143 		if (operator == '+') {
1144 			if (CMD_ABORTED - oval > rval)
1145 				rval += oval;
1146 			else {
1147 				goto invalid;
1148 			}
1149 		} else if (operator == '-') {
1150 			if (oval >= rval)
1151 				rval = oval - rval;
1152 			else {
1153 				goto invalid;
1154 			}
1155 		}
1156 	}
1157 
1158 	if (flags != NULL) {
1159 		if (unit != NULL)
1160 			*flags |= DO_ROUNDING;
1161 #ifdef SUN_CYLCHECK
1162 		if (lp->d_flags & D_VENDOR)
1163 			*flags |= DO_ROUNDING;
1164 #endif
1165 	}
1166 	return rval;
1167 
1168 invalid:
1169 	fputs("Invalid entry\n", stderr);
1170 	return CMD_BADVALUE;
1171 }
1172 
1173 /*
1174  * Check for partition overlap in lp and prompt the user to resolve the overlap
1175  * if any is found.  Returns 1 if unable to resolve, else 0.
1176  */
1177 int
1178 has_overlap(struct disklabel *lp)
1179 {
1180 	const struct partition **spp;
1181 	int i, p1, p2;
1182 	char *line = NULL;
1183 	size_t linesize = 0;
1184 	ssize_t linelen;
1185 
1186 	for (;;) {
1187 		spp = sort_partitions(lp, -1);
1188 		for (i = 0; spp[i+1] != NULL; i++) {
1189 			if (DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]) >
1190 			    DL_GETPOFFSET(spp[i+1]))
1191 				break;
1192 		}
1193 		if (spp[i+1] == NULL) {
1194 			free(line);
1195 			return 0;
1196 		}
1197 
1198 		p1 = 'a' + (spp[i] - lp->d_partitions);
1199 		p2 = 'a' + (spp[i+1] - lp->d_partitions);
1200 		printf("\nError, partitions %c and %c overlap:\n", p1, p2);
1201 		printf("#    %16.16s %16.16s  fstype [fsize bsize    cpg]\n",
1202 		    "size", "offset");
1203 		display_partition(stdout, lp, p1 - 'a', 0);
1204 		display_partition(stdout, lp, p2 - 'a', 0);
1205 
1206 		for (;;) {
1207 			printf("Disable which one? (%c %c) ", p1, p2);
1208 			linelen = getline(&line, &linesize, stdin);
1209 			if (linelen == -1)
1210 				goto done;
1211 			if (linelen == 2 && (line[0] == p1 || line[0] == p2))
1212 				break;
1213 		}
1214 		lp->d_partitions[line[0] - 'a'].p_fstype = FS_UNUSED;
1215 	}
1216 
1217 done:
1218 	putchar('\n');
1219 	free(line);
1220 	return 1;
1221 }
1222 
1223 void
1224 edit_packname(struct disklabel *lp)
1225 {
1226 	char *p;
1227 	struct disklabel oldlabel = *lp;
1228 
1229 	printf("Changing label description for %s:\n", specname);
1230 
1231 	/* pack/label id */
1232 	p = getstring("label name",
1233 	    "15 char string that describes this label, usually the disk name.",
1234 	    lp->d_packname);
1235 	if (p == NULL) {
1236 		*lp = oldlabel;		/* undo damage */
1237 		return;
1238 	}
1239 	strncpy(lp->d_packname, p, sizeof(lp->d_packname));	/* checked */
1240 }
1241 
1242 const struct partition **
1243 sort_partitions(const struct disklabel *lp, int ignore)
1244 {
1245 	const static struct partition *spp[MAXPARTITIONS+2];
1246 	int i, npartitions;
1247 
1248 	memset(spp, 0, sizeof(spp));
1249 
1250 	for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) {
1251 		if (i != ignore && lp->d_partitions[i].p_fstype != FS_UNUSED &&
1252 		    DL_GETPSIZE(&lp->d_partitions[i]) != 0)
1253 			spp[npartitions++] = &lp->d_partitions[i];
1254 	}
1255 
1256 	/*
1257 	 * Sort the partitions based on starting offset.
1258 	 * This is safe because we guarantee no overlap.
1259 	 */
1260 	if (npartitions > 1)
1261 		if (mergesort((void *)spp, npartitions, sizeof(spp[0]),
1262 		    partition_cmp))
1263 			err(4, "failed to sort partition table");
1264 
1265 	return spp;
1266 }
1267 
1268 /*
1269  * Get beginning and ending sectors of the OpenBSD portion of the disk
1270  * from the user.
1271  */
1272 void
1273 set_bounds(struct disklabel *lp)
1274 {
1275 	u_int64_t ui, start_temp;
1276 
1277 	/* Starting sector */
1278 	for (;;) {
1279 		ui = getuint64(lp, "Starting sector",
1280 		    "The start of the OpenBSD portion of the disk.",
1281 		    starting_sector, DL_GETDSIZE(lp), NULL);
1282 		if (ui == CMD_ABORTED)
1283 			return;
1284 		else if (ui == CMD_BADVALUE)
1285 			;	/* Try again. */
1286 		else if (ui >= DL_GETDSIZE(lp))
1287 			fprintf(stderr, "starting sector must be < %llu\n",
1288 			    DL_GETDSIZE(lp));
1289 		else
1290 			break;
1291 	}
1292 	start_temp = ui;
1293 
1294 	/* Size */
1295 	for (;;) {
1296 		ui = getuint64(lp, "Size ('*' for entire disk)",
1297 		    "The size of the OpenBSD portion of the disk ('*' for the "
1298 		    "entire disk).", ending_sector - starting_sector,
1299 		    DL_GETDSIZE(lp) - start_temp, NULL);
1300 		if (ui == CMD_ABORTED)
1301 			return;
1302 		else if (ui == CMD_BADVALUE)
1303 			;	/* Try again. */
1304 		else if (ui > DL_GETDSIZE(lp) - start_temp)
1305 			fprintf(stderr, "size must be <= %llu\n",
1306 			    DL_GETDSIZE(lp) - start_temp);
1307 		else
1308 			break;
1309 	}
1310 	ending_sector = start_temp + ui;
1311 	DL_SETBEND(lp, ending_sector);
1312 	starting_sector = start_temp;
1313 	DL_SETBSTART(lp, starting_sector);
1314 }
1315 
1316 /*
1317  * Allow user to interactively change disklabel UID.
1318  */
1319 void
1320 set_duid(struct disklabel *lp)
1321 {
1322 	char *s;
1323 	int i;
1324 
1325 	printf("The disklabel UID is currently: "
1326 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\n",
1327 	    lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3],
1328 	    lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7]);
1329 
1330 	do {
1331 		s = getstring("duid", "The disklabel UID, given as a 16 "
1332 		    "character hexadecimal string.", NULL);
1333 		if (s == NULL || strlen(s) == 0) {
1334 			fputs("Command aborted\n", stderr);
1335 			return;
1336 		}
1337 		i = duid_parse(lp, s);
1338 		if (i != 0)
1339 			fputs("Invalid UID entered.\n", stderr);
1340 	} while (i != 0);
1341 }
1342 
1343 /*
1344  * Return a list of the "chunks" of free space available
1345  */
1346 const struct diskchunk *
1347 free_chunks(const struct disklabel *lp, int partno)
1348 {
1349 	const struct partition **spp;
1350 	static struct diskchunk chunks[MAXPARTITIONS + 2];
1351 	u_int64_t start, stop;
1352 	int i, numchunks;
1353 
1354 	/* Sort the in-use partitions based on offset */
1355 	spp = sort_partitions(lp, partno);
1356 
1357 	/* If there are no partitions, it's all free. */
1358 	if (spp[0] == NULL) {
1359 		chunks[0].start = starting_sector;
1360 		chunks[0].stop = ending_sector;
1361 		chunks[1].start = chunks[1].stop = 0;
1362 		return chunks;
1363 	}
1364 
1365 	/* Find chunks of free space */
1366 	numchunks = 0;
1367 	if (DL_GETPOFFSET(spp[0]) > starting_sector) {
1368 		chunks[0].start = starting_sector;
1369 		chunks[0].stop = DL_GETPOFFSET(spp[0]);
1370 		numchunks++;
1371 	}
1372 	for (i = 0; spp[i] != NULL; i++) {
1373 		start = DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]);
1374 		if (start < starting_sector)
1375 			start = starting_sector;
1376 		else if (start > ending_sector)
1377 			start = ending_sector;
1378 		if (spp[i + 1] != NULL)
1379 			stop = DL_GETPOFFSET(spp[i+1]);
1380 		else
1381 			stop = ending_sector;
1382 		if (stop < starting_sector)
1383 			stop = starting_sector;
1384 		else if (stop > ending_sector)
1385 			stop = ending_sector;
1386 		if (start < stop) {
1387 			chunks[numchunks].start = start;
1388 			chunks[numchunks].stop = stop;
1389 			numchunks++;
1390 		}
1391 	}
1392 
1393 	/* Terminate and return */
1394 	chunks[numchunks].start = chunks[numchunks].stop = 0;
1395 	return chunks;
1396 }
1397 
1398 void
1399 find_bounds(const struct disklabel *lp)
1400 {
1401 	starting_sector = DL_GETBSTART(lp);
1402 	ending_sector = DL_GETBEND(lp);
1403 
1404 	if (ending_sector) {
1405 		if (verbose)
1406 			printf("Treating sectors %llu-%llu as the OpenBSD"
1407 			    " portion of the disk.\nYou can use the 'b'"
1408 			    " command to change this.\n\n", starting_sector,
1409 			    ending_sector);
1410 	}
1411 }
1412 
1413 /*
1414  * Calculate free space.
1415  */
1416 u_int64_t
1417 editor_countfree(const struct disklabel *lp)
1418 {
1419 	const struct diskchunk *chunk;
1420 	u_int64_t freesectors = 0;
1421 
1422 	chunk = free_chunks(lp, -1);
1423 
1424 	for (; chunk->start != 0 || chunk->stop != 0; chunk++)
1425 		freesectors += CHUNKSZ(chunk);
1426 
1427 	return freesectors;
1428 }
1429 
1430 void
1431 editor_help(void)
1432 {
1433 	puts("Available commands:");
1434 	puts(
1435 " ? | h    - show help                 n [part] - set mount point\n"
1436 " A        - auto partition all space  p [unit] - print partitions\n"
1437 " a [part] - add partition             q        - quit & save changes\n"
1438 " b        - set OpenBSD boundaries    R [part] - resize auto allocated partition\n"
1439 " c [part] - change partition size     r        - display free space\n"
1440 " D        - reset label to default    s [path] - save label to file\n"
1441 " d [part] - delete partition          U        - undo all changes\n"
1442 " e        - edit label description    u        - undo last change\n"
1443 " i        - modify disklabel UID      w        - write label to disk\n"
1444 " l [unit] - print disk label header   x        - exit & lose changes\n"
1445 " M        - disklabel(8) man page     z        - delete all partitions\n"
1446 " m [part] - modify partition\n"
1447 "\n"
1448 "Suffixes can be used to indicate units other than sectors:\n"
1449 " 'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' (gigabytes) 't' (terabytes)\n"
1450 " 'c' (cylinders), '%' (% of total disk), '&' (% of free space).\n"
1451 "Values in non-sector units are truncated to the nearest cylinder boundary.");
1452 
1453 }
1454 
1455 void
1456 mpcopy(char **to, char **from)
1457 {
1458 	int i;
1459 
1460 	for (i = 0; i < MAXPARTITIONS; i++) {
1461 		free(to[i]);
1462 		to[i] = NULL;
1463 		if (from[i] != NULL) {
1464 			to[i] = strdup(from[i]);
1465 			if (to[i] == NULL)
1466 				err(1, NULL);
1467 		}
1468 	}
1469 }
1470 
1471 int
1472 mpequal(char **mp1, char **mp2)
1473 {
1474 	int i;
1475 
1476 	for (i = 0; i < MAXPARTITIONS; i++) {
1477 		if (mp1[i] == NULL && mp2[i] == NULL)
1478 			continue;
1479 
1480 		if ((mp1[i] != NULL && mp2[i] == NULL) ||
1481 		    (mp1[i] == NULL && mp2[i] != NULL) ||
1482 		    (strcmp(mp1[i], mp2[i]) != 0))
1483 			return 0;
1484 	}
1485 	return 1;
1486 }
1487 
1488 void
1489 mpsave(const struct disklabel *lp)
1490 {
1491 	int i, j;
1492 	char bdev[PATH_MAX], *p;
1493 	struct mountinfo mi[MAXPARTITIONS];
1494 	FILE *fp;
1495 	u_int8_t fstype;
1496 
1497 	if (!fstabfile)
1498 		return;
1499 
1500 	memset(&mi, 0, sizeof(mi));
1501 
1502 	for (i = 0; i < MAXPARTITIONS; i++) {
1503 		fstype = lp->d_partitions[i].p_fstype;
1504 		if (mountpoints[i] != NULL || fstype == FS_SWAP) {
1505 			mi[i].mountpoint = mountpoints[i];
1506 			mi[i].partno = i;
1507 		}
1508 	}
1509 
1510 	/* Convert specname to bdev */
1511 	if (uidflag) {
1512 		snprintf(bdev, sizeof(bdev),
1513 		    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
1514 		    lab.d_uid[0], lab.d_uid[1], lab.d_uid[2], lab.d_uid[3],
1515 		    lab.d_uid[4], lab.d_uid[5], lab.d_uid[6], lab.d_uid[7],
1516 		    specname[strlen(specname)-1]);
1517 	} else if (strncmp(_PATH_DEV, specname, sizeof(_PATH_DEV) - 1) == 0 &&
1518 	    specname[sizeof(_PATH_DEV) - 1] == 'r') {
1519 		snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV,
1520 		    &specname[sizeof(_PATH_DEV)]);
1521 	} else {
1522 		if ((p = strrchr(specname, '/')) == NULL || *(++p) != 'r')
1523 			return;
1524 		*p = '\0';
1525 		snprintf(bdev, sizeof(bdev), "%s%s", specname, p + 1);
1526 		*p = 'r';
1527 	}
1528 	bdev[strlen(bdev) - 1] = '\0';
1529 
1530 	/* Sort mountpoints so we don't try to mount /usr/local before /usr */
1531 	qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp);
1532 
1533 	if ((fp = fopen(fstabfile, "w"))) {
1534 		for (i = 0; i < MAXPARTITIONS; i++) {
1535 			j =  mi[i].partno;
1536 			fstype = lp->d_partitions[j].p_fstype;
1537 			if (fstype == FS_RAID)
1538 				continue;
1539 			if (fstype == FS_SWAP) {
1540 				fprintf(fp, "%s%c none swap sw\n", bdev, 'a'+j);
1541 			} else if (mi[i].mountpoint) {
1542 				fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev,
1543 				    'a' + j, mi[i].mountpoint,
1544 				    fstypesnames[fstype], j == 0 ? 1 : 2);
1545 			}
1546 		}
1547 		fclose(fp);
1548 	}
1549 }
1550 
1551 void
1552 mpfree(char **mp, int action)
1553 {
1554 	int part;
1555 
1556 	if (mp == NULL)
1557 		return;
1558 
1559 	for (part = 0; part < MAXPARTITIONS; part++) {
1560 		free(mp[part]);
1561 		mp[part] = NULL;
1562 	}
1563 
1564 	if (action == DISCARD) {
1565 		free(mp);
1566 		mp = NULL;
1567 	}
1568 }
1569 
1570 int
1571 get_offset(struct disklabel *lp, int partno)
1572 {
1573 	struct partition opp, *pp = &lp->d_partitions[partno];
1574 	u_int64_t ui, offsetalign;
1575 	int flags;
1576 
1577 	flags = DO_CONVERSIONS;
1578 	ui = getuint64(lp, "offset",
1579 	    "Starting sector for this partition.",
1580 	    DL_GETPOFFSET(pp),
1581 	    DL_GETPOFFSET(pp), &flags);
1582 
1583 	if (ui == CMD_ABORTED || ui == CMD_BADVALUE)
1584 		return 1;
1585 #ifdef SUN_AAT0
1586 	if (partno == 0 && ui != 0) {
1587 		fprintf(stderr, "This architecture requires that "
1588 		    "partition 'a' start at sector 0.\n");
1589 		return 1;
1590 	}
1591 #endif
1592 	opp = *pp;
1593 	DL_SETPOFFSET(pp, ui);
1594 	offsetalign = 1;
1595 	if ((flags & DO_ROUNDING) != 0 && pp->p_fstype == FS_BSDFFS)
1596 		offsetalign = lp->d_secpercyl;
1597 
1598 	if (alignpartition(lp, partno, offsetalign, 1, ROUND_OFFSET_UP) == 1) {
1599 		*pp = opp;
1600 		return 1;
1601 	}
1602 
1603 	return 0;
1604 }
1605 
1606 int
1607 get_size(struct disklabel *lp, int partno)
1608 {
1609 	struct partition opp, *pp = &lp->d_partitions[partno];
1610 	u_int64_t maxsize, ui, sizealign;
1611 	int flags;
1612 
1613 	maxsize = max_partition_size(lp, partno);
1614 	flags = DO_CONVERSIONS;
1615 	ui = getuint64(lp, "size", "Size of the partition. "
1616 	    "You may also say +/- amount for a relative change.",
1617 	    DL_GETPSIZE(pp), maxsize, &flags);
1618 
1619 	if (ui == CMD_ABORTED || ui == CMD_BADVALUE)
1620 		return 1;
1621 
1622 	opp = *pp;
1623 	DL_SETPSIZE(pp, ui);
1624 	sizealign = 1;
1625 	if ((flags & DO_ROUNDING) != 0 && (pp->p_fstype == FS_SWAP ||
1626 	    pp->p_fstype == FS_BSDFFS))
1627 		sizealign = lp->d_secpercyl;
1628 
1629 	if (alignpartition(lp, partno, 1, sizealign, ROUND_SIZE_UP) == 1) {
1630 		*pp = opp;
1631 		return 1;
1632 	}
1633 
1634 	return 0;
1635 }
1636 
1637 int
1638 set_fragblock(struct disklabel *lp, int partno)
1639 {
1640 	struct partition opp, *pp = &lp->d_partitions[partno];
1641 	u_int64_t bytes, offsetalign, sizealign;
1642 	u_int32_t frag, fsize;
1643 
1644 	if (pp->p_fstype != FS_BSDFFS)
1645 		return 0;
1646 
1647 	if (pp->p_cpg == 0)
1648 		pp->p_cpg = 1;
1649 
1650 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
1651 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
1652 	if (fsize == 0) {
1653 		fsize = 2048;
1654 		frag = 8;
1655 		bytes = DL_GETPSIZE(pp) * lp->d_secsize;
1656 		if (bytes > 128ULL * 1024 * 1024 * 1024)
1657 			fsize *= 2;
1658 		if (bytes > 512ULL * 1024 * 1024 * 1024)
1659 			fsize *= 2;
1660 		if (fsize < lp->d_secsize)
1661 			fsize = lp->d_secsize;
1662 		if (fsize > MAXBSIZE / frag)
1663 			fsize = MAXBSIZE / frag;
1664 		pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fsize, frag);
1665 	}
1666 #ifdef SUN_CYLCHECK
1667 	return 0;
1668 #endif
1669 	opp = *pp;
1670 	sizealign = (DISKLABELV1_FFS_FRAG(pp->p_fragblock) *
1671 	    DISKLABELV1_FFS_FSIZE(pp->p_fragblock)) / lp->d_secsize;
1672 	offsetalign = 1;
1673 	if (DL_GETPOFFSET(pp) != starting_sector)
1674 		offsetalign = sizealign;
1675 
1676 	if (alignpartition(lp, partno, offsetalign, sizealign, ROUND_OFFSET_UP |
1677 	    ROUND_SIZE_DOWN | ROUND_SIZE_OVERLAP) == 1) {
1678 		*pp = opp;
1679 		return 1;
1680 	}
1681 
1682 	return 0;
1683 }
1684 
1685 int
1686 get_fstype(struct disklabel *lp, int partno)
1687 {
1688 	char *p;
1689 	u_int64_t ui;
1690 	struct partition *pp = &lp->d_partitions[partno];
1691 
1692 	if (pp->p_fstype < FSMAXTYPES) {
1693 		p = getstring("FS type",
1694 		    "Filesystem type (usually 4.2BSD or swap)",
1695 		    fstypenames[pp->p_fstype]);
1696 		if (p == NULL) {
1697 			return 1;
1698 		}
1699 		for (ui = 0; ui < FSMAXTYPES; ui++) {
1700 			if (!strcasecmp(p, fstypenames[ui])) {
1701 				pp->p_fstype = ui;
1702 				break;
1703 			}
1704 		}
1705 		if (ui >= FSMAXTYPES) {
1706 			printf("Unrecognized filesystem type '%s', treating "
1707 			    "as 'unknown'\n", p);
1708 			pp->p_fstype = FS_OTHER;
1709 		}
1710 	} else {
1711 		for (;;) {
1712 			ui = getnumber("FS type (decimal)",
1713 			    "Filesystem type as a decimal number; usually 7 "
1714 			    "(4.2BSD) or 1 (swap).",
1715 			    pp->p_fstype, UINT8_MAX);
1716 			if (ui == CMD_ABORTED)
1717 				return 1;
1718 			else if (ui == CMD_BADVALUE)
1719 				;	/* Try again. */
1720 			else
1721 				break;
1722 		}
1723 		pp->p_fstype = ui;
1724 	}
1725 	return 0;
1726 }
1727 
1728 int
1729 get_mp(const struct disklabel *lp, int partno)
1730 {
1731 	const struct partition *pp = &lp->d_partitions[partno];
1732 	char *p;
1733 	int i;
1734 
1735 	if (fstabfile == NULL ||
1736 	    pp->p_fstype == FS_UNUSED ||
1737 	    pp->p_fstype == FS_SWAP ||
1738 	    pp->p_fstype == FS_BOOT ||
1739 	    pp->p_fstype == FS_OTHER ||
1740 	    pp->p_fstype == FS_RAID) {
1741 		/* No fstabfile, no names. Not all fstypes can be named */
1742 		return 0;
1743 	}
1744 
1745 	for (;;) {
1746 		p = getstring("mount point",
1747 		    "Where to mount this filesystem (ie: / /var /usr)",
1748 		    mountpoints[partno] ? mountpoints[partno] : "none");
1749 		if (p == NULL)
1750 			return 1;
1751 		if (strcasecmp(p, "none") == 0) {
1752 			free(mountpoints[partno]);
1753 			mountpoints[partno] = NULL;
1754 			break;
1755 		}
1756 		for (i = 0; i < MAXPARTITIONS; i++)
1757 			if (mountpoints[i] != NULL && i != partno &&
1758 			    strcmp(p, mountpoints[i]) == 0)
1759 				break;
1760 		if (i < MAXPARTITIONS) {
1761 			fprintf(stderr, "'%c' already being mounted at "
1762 			    "'%s'\n", 'a'+i, p);
1763 			break;
1764 		}
1765 		if (*p == '/') {
1766 			/* XXX - might as well realloc */
1767 			free(mountpoints[partno]);
1768 			if ((mountpoints[partno] = strdup(p)) == NULL)
1769 				err(1, NULL);
1770 			break;
1771 		}
1772 		fputs("Mount points must start with '/'\n", stderr);
1773 	}
1774 
1775 	return 0;
1776 }
1777 
1778 int
1779 micmp(const void *a1, const void *a2)
1780 {
1781 	struct mountinfo *mi1 = (struct mountinfo *)a1;
1782 	struct mountinfo *mi2 = (struct mountinfo *)a2;
1783 
1784 	/* We want all the NULLs at the end... */
1785 	if (mi1->mountpoint == NULL && mi2->mountpoint == NULL)
1786 		return 0;
1787 	else if (mi1->mountpoint == NULL)
1788 		return 1;
1789 	else if (mi2->mountpoint == NULL)
1790 		return -1;
1791 	else
1792 		return strcmp(mi1->mountpoint, mi2->mountpoint);
1793 }
1794 
1795 void
1796 zero_partitions(struct disklabel *lp)
1797 {
1798 	int i;
1799 
1800 	memset(lp->d_partitions, 0, sizeof(lp->d_partitions));
1801 	DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
1802 
1803 	mpfree(mountpoints, KEEP);
1804 }
1805 
1806 u_int64_t
1807 max_partition_size(const struct disklabel *lp, int partno)
1808 {
1809 	const struct diskchunk *chunk;
1810 	u_int64_t maxsize = 0, offset;
1811 
1812 	chunk = free_chunks(lp, partno);
1813 
1814 	offset = DL_GETPOFFSET(&lp->d_partitions[partno]);
1815 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
1816 		if (offset < chunk->start || offset >= chunk->stop)
1817 			continue;
1818 		maxsize = chunk->stop - offset;
1819 		break;
1820 	}
1821 	return maxsize;
1822 }
1823 
1824 void
1825 psize(u_int64_t sz, char unit, const struct disklabel *lp)
1826 {
1827 	double d = scale(sz, unit, lp);
1828 	if (d < 0)
1829 		printf("%llu", sz);
1830 	else
1831 		printf("%.*f%c", unit == 'B' ? 0 : 1, d, unit);
1832 }
1833 
1834 void
1835 display_edit(const struct disklabel *lp, char unit)
1836 {
1837 	u_int64_t fr;
1838 	int i;
1839 
1840 	fr = editor_countfree(lp);
1841 	unit = canonical_unit(lp, unit);
1842 
1843 	printf("OpenBSD area: ");
1844 	psize(starting_sector, 0, lp);
1845 	printf("-");
1846 	psize(ending_sector, 0, lp);
1847 	printf("; size: ");
1848 	psize(ending_sector - starting_sector, unit, lp);
1849 	printf("; free: ");
1850 	psize(fr, unit, lp);
1851 
1852 	printf("\n#    %16.16s %16.16s  fstype [fsize bsize   cpg]\n",
1853 	    "size", "offset");
1854 	for (i = 0; i < lp->d_npartitions; i++)
1855 		display_partition(stdout, lp, i, unit);
1856 }
1857 
1858 void
1859 parse_autotable(char *filename)
1860 {
1861 	FILE	*cfile;
1862 	size_t	 linesize = 0;
1863 	char	*line = NULL, *buf, *t;
1864 	uint	 idx = 0, pctsum = 0;
1865 	struct space_allocation *sa;
1866 
1867 	if (strcmp(filename, "-") == 0)
1868 		cfile = stdin;
1869 	else if ((cfile = fopen(filename, "r")) == NULL)
1870 		err(1, "%s", filename);
1871 	if ((alloc_table = calloc(1, sizeof(struct alloc_table))) == NULL)
1872 		err(1, NULL);
1873 	alloc_table_nitems = 1;
1874 
1875 	while (getline(&line, &linesize, cfile) != -1) {
1876 		if ((alloc_table[0].table = reallocarray(alloc_table[0].table,
1877 		    idx + 1, sizeof(*sa))) == NULL)
1878 			err(1, NULL);
1879 		sa = &(alloc_table[0].table[idx]);
1880 		memset(sa, 0, sizeof(*sa));
1881 		idx++;
1882 
1883 		buf = line;
1884 		if ((sa->mp = get_token(&buf)) == NULL ||
1885 		    (sa->mp[0] != '/' && strcasecmp(sa->mp, "swap") &&
1886 		    strcasecmp(sa->mp, "raid")))
1887 			errx(1, "%s: parse error on line %u", filename, idx);
1888 		if ((t = get_token(&buf)) == NULL ||
1889 		    parse_sizerange(t, &sa->minsz, &sa->maxsz) == -1)
1890 			errx(1, "%s: parse error on line %u", filename, idx);
1891 		if ((t = get_token(&buf)) != NULL &&
1892 		    parse_pct(t, &sa->rate) == -1)
1893 			errx(1, "%s: parse error on line %u", filename, idx);
1894 		if (sa->minsz > sa->maxsz)
1895 			errx(1, "%s: min size > max size on line %u", filename,
1896 			    idx);
1897 		pctsum += sa->rate;
1898 	}
1899 	if (pctsum > 100)
1900 		errx(1, "%s: sum of extra space allocation > 100%%", filename);
1901 	alloc_table[0].sz = idx;
1902 	free(line);
1903 	fclose(cfile);
1904 }
1905 
1906 char *
1907 get_token(char **s)
1908 {
1909 	char	*p, *r;
1910 	size_t	 tlen = 0;
1911 
1912 	p = *s;
1913 	while (**s != '\0' && !isspace((u_char)**s)) {
1914 		(*s)++;
1915 		tlen++;
1916 	}
1917 	if (tlen == 0)
1918 		return NULL;
1919 
1920 	/* eat whitespace */
1921 	while (isspace((u_char)**s))
1922 		(*s)++;
1923 
1924 	if ((r = strndup(p, tlen)) == NULL)
1925 		err(1, NULL);
1926 	return r;
1927 }
1928 
1929 int
1930 apply_unit(double val, u_char unit, u_int64_t *n)
1931 {
1932 	u_int64_t factor = 1;
1933 
1934 	switch (tolower(unit)) {
1935 	case 't':
1936 		factor *= 1024;
1937 		/* FALLTHROUGH */
1938 	case 'g':
1939 		factor *= 1024;
1940 		/* FALLTHROUGH */
1941 	case 'm':
1942 		factor *= 1024;
1943 		/* FALLTHROUGH */
1944 	case 'k':
1945 		factor *= 1024;
1946 		break;
1947 	default:
1948 		return -1;
1949 	}
1950 
1951 	val *= factor / DEV_BSIZE;
1952 	if (val > ULLONG_MAX)
1953 		return -1;
1954 	*n = val;
1955 	return 0;
1956 }
1957 
1958 int
1959 parse_sizespec(const char *buf, double *val, char **unit)
1960 {
1961 	errno = 0;
1962 	*val = strtod(buf, unit);
1963 	if (errno == ERANGE || *val < 0 || *val > ULLONG_MAX)
1964 		return -1;	/* too big/small */
1965 	if (*val == 0 && *unit == buf)
1966 		return -1;	/* No conversion performed. */
1967 	if (*unit != NULL && *unit[0] == '\0')
1968 		*unit = NULL;
1969 	return 0;
1970 }
1971 
1972 int
1973 parse_sizerange(char *buf, u_int64_t *min, u_int64_t *max)
1974 {
1975 	char	*p, *unit1 = NULL, *unit2 = NULL;
1976 	double	 val1 = 0, val2 = 0;
1977 
1978 	if (strcmp(buf, "*") == 0) {
1979 		*min = 0;
1980 		*max = UINT64_MAX;
1981 		goto done;
1982 	}
1983 
1984 	if ((p = strchr(buf, '-')) != NULL) {
1985 		p[0] = '\0';
1986 		p++;
1987 	}
1988 	*max = 0;
1989 	if (parse_sizespec(buf, &val1, &unit1) == -1)
1990 		return -1;
1991 	if (p != NULL && p[0] != '\0') {
1992 		if (p[0] == '*')
1993 			*max = UINT64_MAX;
1994 		else
1995 			if (parse_sizespec(p, &val2, &unit2) == -1)
1996 				return -1;
1997 	}
1998 	if (unit1 == NULL && (unit1 = unit2) == NULL)
1999 		return -1;
2000 	if (apply_unit(val1, unit1[0], min) == -1)
2001 		return -1;
2002 	if (val2 > 0) {
2003 		if (apply_unit(val2, unit2[0], max) == -1)
2004 			return -1;
2005 	} else
2006 		if (*max == 0)
2007 			*max = *min;
2008  done:
2009 	free(buf);
2010 	return 0;
2011 }
2012 
2013 int
2014 parse_pct(char *buf, int *n)
2015 {
2016 	const char	*errstr;
2017 
2018 	if (buf[strlen(buf) - 1] == '%')
2019 		buf[strlen(buf) - 1] = '\0';
2020 	*n = strtonum(buf, 0, 100, &errstr);
2021 	if (errstr) {
2022 		warnx("parse percent %s: %s", buf, errstr);
2023 		return -1;
2024 	}
2025 	free(buf);
2026 	return 0;
2027 }
2028 
2029 int
2030 alignpartition(struct disklabel *lp, int partno, u_int64_t startalign,
2031     u_int64_t stopalign, int flags)
2032 {
2033 	struct partition *pp = &lp->d_partitions[partno];
2034 	const struct diskchunk *chunk;
2035 	u_int64_t start, stop, maxstop;
2036 
2037 	start = DL_GETPOFFSET(pp);
2038 	if ((flags & ROUND_OFFSET_UP) == ROUND_OFFSET_UP)
2039 		start = ROUNDUP(start, startalign);
2040 	else if ((flags & ROUND_OFFSET_DOWN) == ROUND_OFFSET_DOWN)
2041 		start = ROUNDDOWN(start, startalign);
2042 
2043 	/* Find the chunk that contains 'start'. */
2044 	chunk = free_chunks(lp, partno);
2045 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
2046 		if (start >= chunk->start && start < chunk->stop)
2047 			break;
2048 	}
2049 	if (chunk->stop == 0) {
2050 		fprintf(stderr, "'%c' aligned offset %llu lies outside "
2051 		    "the OpenBSD bounds or inside another partition\n",
2052 		    'a' + partno, start);
2053 		return 1;
2054 	}
2055 
2056 	/* Calculate the new 'stop' sector, the sector after the partition. */
2057 	if ((flags & ROUND_SIZE_OVERLAP) == 0)
2058 		maxstop = ROUNDDOWN(chunk->stop, stopalign);
2059 	else
2060 		maxstop = ROUNDDOWN(ending_sector, stopalign);
2061 
2062 	stop = DL_GETPOFFSET(pp) + DL_GETPSIZE(pp);
2063 	if ((flags & ROUND_SIZE_UP) == ROUND_SIZE_UP)
2064 		stop = ROUNDUP(stop, stopalign);
2065 	else if ((flags & ROUND_SIZE_DOWN) == ROUND_SIZE_DOWN)
2066 		stop = ROUNDDOWN(stop, stopalign);
2067 	if (stop > maxstop)
2068 		stop = maxstop;
2069 
2070 	if (stop <= start) {
2071 		fprintf(stderr, "not enough space\n");
2072 		return 1;
2073 	}
2074 
2075 	if (start != DL_GETPOFFSET(pp))
2076 		DL_SETPOFFSET(pp, start);
2077 	if (stop != DL_GETPOFFSET(pp) + DL_GETPSIZE(pp))
2078 		DL_SETPSIZE(pp, stop - start);
2079 
2080 	return 0;
2081 }
2082