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