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