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