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