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