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