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