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