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