xref: /openbsd/sbin/disklabel/editor.c (revision 133306f0)
1 /*	$OpenBSD: editor.c,v 1.74 2000/10/22 23:59:40 millert 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.74 2000/10/22 23:59:40 millert 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, **tmpmountpoints;
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 	struct partition *pp = &lp->d_partitions[RAW_PART];
1696 
1697 	/* Defaults */
1698 	/* XXX - reserve a cylinder for hp300? */
1699 	starting_sector = 0;
1700 	ending_sector = lp->d_secperunit;
1701 
1702 #ifdef DOSLABEL
1703 	/*
1704 	 * If we have an MBR, use values from the {Open,Free,Net}BSD partition
1705 	 */
1706 	if (dosdp) {
1707 	    if (dosdp->dp_typ == DOSPTYP_OPENBSD ||
1708 		    dosdp->dp_typ == DOSPTYP_FREEBSD ||
1709 		    dosdp->dp_typ == DOSPTYP_NETBSD) {
1710 			u_int32_t i, new_end;
1711 
1712 			/* Set start and end based on fdisk partition bounds */
1713 			starting_sector = get_le(&dosdp->dp_start);
1714 			ending_sector = starting_sector + get_le(&dosdp->dp_size);
1715 
1716 			/*
1717 			 * If the ending sector of the BSD fdisk partition
1718 			 * is equal to the ending sector of the BIOS geometry
1719 			 * but the real sector count > BIOS sector count,
1720 			 * adjust the bounds accordingly.  We do this because
1721 			 * the BIOS geometry is limited to disks of ~4gig.
1722 			 */
1723 			if (bios_lp && ending_sector == bios_lp->d_secperunit &&
1724 			    lp->d_secperunit > bios_lp->d_secperunit)
1725 				ending_sector = lp->d_secperunit;
1726 
1727 			/*
1728 			 * If there are any BSD or SWAP partitions beyond
1729 			 * ending_sector we extend ending_sector to include
1730 			 * them.  This is done because the BIOS geometry is
1731 			 * generally different from the disk geometry.
1732 			 */
1733 			for (i = new_end = 0; i < lp->d_npartitions; i++) {
1734 				pp = &lp->d_partitions[i];
1735 				if ((pp->p_fstype == FS_BSDFFS ||
1736 				    pp->p_fstype == FS_SWAP) &&
1737 				    pp->p_size + pp->p_offset > new_end)
1738 					new_end = pp->p_size + pp->p_offset;
1739 			}
1740 			if (new_end > ending_sector)
1741 				ending_sector = new_end;
1742 		} else {
1743 			/* Don't trounce the MBR */
1744 			starting_sector = 63;
1745 		}
1746 
1747 		printf("\nTreating sectors %u-%u as the OpenBSD portion of the "
1748 		    "disk.\nYou can use the 'b' command to change this.\n",
1749 		    starting_sector, ending_sector);
1750 	}
1751 #elif (NUMBOOT == 1)
1752 	/* Boot blocks take up the first cylinder */
1753 	starting_sector = lp->d_secpercyl;
1754 	printf("\nReserving the first data cylinder for boot blocks.\n"
1755 	    "You can use the 'b' command to change this.\n");
1756 #endif
1757 }
1758 
1759 /*
1760  * Calculate free space.
1761  */
1762 void
1763 editor_countfree(lp, freep)
1764 	struct disklabel *lp;
1765 	u_int32_t *freep;
1766 {
1767 	struct partition *pp;
1768 	int i;
1769 
1770 	*freep = ending_sector - starting_sector;
1771 	for (i = 0; i < lp->d_npartitions; i++) {
1772 		    pp = &lp->d_partitions[i];
1773 		    if (pp->p_fstype != FS_UNUSED && pp->p_fstype != FS_BOOT &&
1774 			pp->p_size > 0 &&
1775 			pp->p_offset + pp->p_size <= ending_sector &&
1776 			pp->p_offset >= starting_sector)
1777 			*freep -= pp->p_size;
1778 	}
1779 }
1780 
1781 void
1782 editor_help(arg)
1783 	char *arg;
1784 {
1785 
1786 	/* XXX - put these strings in a table instead? */
1787 	switch (*arg) {
1788 	case 'p':
1789 		puts(
1790 "The 'p' command prints the current disk label.  By default, it prints the\n"
1791 "size and offset in sectors (a sector is usually 512 bytes).  The 'p' command\n"
1792 "takes an optional units argument.  Possible values are 'b' for bytes, 'c'\n"
1793 "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n");
1794 		break;
1795 	case 'M':
1796 		puts(
1797 "The 'M' command pipes the entire OpenBSD manual page for disk label through\n"
1798 "the pager specified by the PAGER environment variable or 'less' if PAGER is\n"
1799 "not set.  It is especially useful during install when the normal system\n"
1800 "manual is not available.\n");
1801 		break;
1802 	case 'e':
1803 		puts(
1804 "The 'e' command is used to edit the disk drive parameters.  These include\n"
1805 "the number of sectors/track, tracks/cylinder, sectors/cylinder, number of\n"
1806 "cylinders on the disk , total sectors on the disk, rpm, interleave, disk\n"
1807 "type, and a descriptive label string.  You should not change these unless\n"
1808 "you know what you are doing\n");
1809 		break;
1810 	case 'a':
1811 		puts(
1812 "The 'a' command adds new partitions to the disk.  It takes as an optional\n"
1813 "argument the partition letter to add.  If you do not specify a partition\n"
1814 "letter, you will be prompted for it; the next available letter will be the\n"
1815 "default answer\n");
1816 		break;
1817 	case 'b':
1818 		puts(
1819 "The 'b' command is used to change the boundaries of the OpenBSD portion of\n"
1820 "the disk.  This is only useful on disks with an fdisk partition.  By default,\n"
1821 "on a disk with an fdisk partition, the boundaries are set to be the first\n"
1822 "and last sectors of the OpenBSD fdisk partition.  You should only change\n"
1823 "these if your fdisk partition table is incorrect or you have a disk larger\n"
1824 "than 8gig, since 8gig is the maximum size an fdisk partition can be.  You\n"
1825 "may enter '*' at the 'Size' prompt to indicate the entire size of the disk\n"
1826 "(minus the starting sector).  Use this option with care; if you extend the\n"
1827 "boundaries such that they overlap with another operating system you will\n"
1828 "corrupt the other operating system's data.\n");
1829 		break;
1830 	case 'c':
1831 		puts(
1832 "The 'c' command is used to change the size of an existing partition.  It\n"
1833 "takes as an optional argument the partition letter to change.  If you do not\n"
1834 "specify a partition letter, you will be prompted for one.  You may add a '+'\n"
1835 "or '-' prefix to the new size to increase or decrease the existing value\n"
1836 "instead of entering an absolute value.  You may also use a suffix to indicate\n"
1837 "the units the values is in terms of.  Possible suffixes are 'b' for bytes,\n"
1838 "'c' for cylinders, 'k' for kilobytes, 'm' for megabytes, 'g' for gigabytes or\n"
1839 "no suffix for sectors (usually 512 bytes).  You may also enter '*' to change\n"
1840 "the size to be the total number of free sectors remaining.\n");
1841 		break;
1842 	case 'D':
1843 		puts(
1844 "The 'D' command will set the disk label to the default values as reported\n"
1845 "by the disk itself.  This similates the case where there is no disk label.\n");
1846 		break;
1847 	case 'd':
1848 		puts(
1849 "The 'd' command is used to delete an existing partition.  It takes as an\n"
1850 "optional argument the partition letter to change.  If you do not specify a\n"
1851 "partition letter, you will be prompted for one.  You may not delete the ``c''\n"
1852 "partition as 'c' must always exist and by default is marked as 'unused' (so\n"
1853 "it does not take up any space).\n");
1854 		break;
1855 	case 'g':
1856 		puts(
1857 "The 'g' command is used select which disk geometry to use, the disk, BIOS, or\n"
1858 "user geometry.  It takes as an optional argument ``d'', ``b'', or ``u''.  If \n"
1859 "you do not specify the type as an argument, you will be prompted for it.\n");
1860 		break;
1861 	case 'm':
1862 		puts(
1863 "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"
1864 "partition letter, you will be prompted for one.  This option allows the user\n"
1865 "to change the filesystem type, starting offset, partition size, block fragment\n"
1866 "size, block size, and cylinders per group for the specified partition (not all\n"
1867 "parameters are configurable for non-BSD partitions).\n");
1868 		break;
1869 	case 'n':
1870 		puts(
1871 "The 'n' command is used to set the mount point for a partition (ie: name it).\n"
1872 "It takes as an optional argument the partition letter to name.  If you do\n"
1873 "not specify a partition letter, you will be prompted for one.  This option\n"
1874 "is only valid if disklabel was invoked with the -F flag.\n");
1875 		break;
1876 	case 'r':
1877 		puts(
1878 "The 'r' command is used to recalculate the free space available.  This option\n"
1879 "should really not be necessary under normal circumstances but can be useful if\n"
1880 "disklabel gets confused.\n");
1881 		break;
1882 	case 'u':
1883 		puts(
1884 "The 'u' command will undo (or redo) the last change.  Entering 'u' once will\n"
1885 "undo your last change.  Entering it again will restore the change.\n");
1886 		break;
1887 	case 's':
1888 		puts(
1889 "The 's' command is used to save a copy of the label to a file in ascii format\n"
1890 "(suitable for loading via disklabel's [-R] option).  It takes as an optional\n"
1891 "argument the filename to save the label to.  If you do not specify a filename,\n"
1892 "you will be prompted for one.\n");
1893 		break;
1894 	case 'w':
1895 		puts(
1896 "The 'w' command will write the current label to disk.  This option will\n"
1897 "commit any changes to the on-disk label.\n");
1898 		break;
1899 	case 'q':
1900 		puts(
1901 "The 'q' command quits the label editor.  If any changes have been made you\n"
1902 "will be asked whether or not to save the changes to the on-disk label.\n");
1903 		break;
1904 	case 'X':
1905 		puts(
1906 "The 'X' command toggles disklabel in to/out of 'expert mode'.  By default,\n"
1907 "some settings are reserved for experts only (such as the block and fragment\n"
1908 "size on ffs partitions).\n");
1909 		break;
1910 	case 'x':
1911 		puts(
1912 "The 'x' command exits the label editor without saving any changes to the\n"
1913 "on-disk label.\n");
1914 		break;
1915 	case 'z':
1916 		puts(
1917 "The 'z' command zeroes out the existing partition table, leaving only the 'c'\n"
1918 "partition.  The drive parameters are not changed.\n");
1919 		break;
1920 	default:
1921 		puts("Available commands:");
1922 		puts("\tp [unit]  - print label.");
1923 		puts("\tM         - show entire OpenBSD man page for disklabel.");
1924 		puts("\te         - edit drive parameters.");
1925 		puts("\ta [part]  - add new partition.");
1926 		puts("\tb         - set OpenBSD disk boundaries.");
1927 		puts("\tc [part]  - change partition size.");
1928 		puts("\td [part]  - delete partition.");
1929 		puts("\tD         - set label to default.");
1930 		puts("\tg [d|b]   - Use [d]isk or [b]ios geometry.");
1931 		puts("\tm [part]  - modify existing partition.");
1932 		puts("\tn [part]  - set the mount point for a partition.");
1933 		puts("\tr         - recalculate free space.");
1934 		puts("\tu         - undo last change.");
1935 		puts("\ts [path]  - save label to file.");
1936 		puts("\tw         - write label to disk.");
1937 		puts("\tq         - quit and save changes.");
1938 		puts("\tx         - exit without saving changes.");
1939 		puts("\tX         - toggle expert mode.");
1940 		puts("\tz         - zero out partition table.");
1941 		puts("\t? [cmnd]  - this message or command specific help.");
1942 		puts(
1943 "Numeric parameters may use suffixes to indicate units:\n\t"
1944 "'b' for bytes, 'c' for cylinders, 'k' for kilobytes, 'm' for megabytes,\n\t"
1945 "'g' for gigabytes or no suffix for sectors (usually 512 bytes).\n\t"
1946 "Non-sector units will be rounded to the nearest cylinder.\n"
1947 "Entering '?' at most prompts will give you (simple) context sensitive help.");
1948 		break;
1949 	}
1950 }
1951 
1952 char **
1953 mpcopy(to, from)
1954 	char **to;
1955 	char **from;
1956 {
1957 	int i;
1958 
1959 	for (i = 0; i < MAXPARTITIONS; i++) {
1960 		if (from[i] != NULL) {
1961 			to[i] = realloc(to[i], strlen(from[i]) + 1);
1962 			if (to[i] == NULL)
1963 				errx(4, "out of memory");
1964 			(void)strcpy(to[i], from[i]);
1965 		} else if (to[i] != NULL) {
1966 			free(to[i]);
1967 			to[i] = NULL;
1968 		}
1969 	}
1970 	return(to);
1971 }
1972 
1973 int
1974 mpequal(mp1, mp2)
1975 	char **mp1;
1976 	char **mp2;
1977 {
1978 	int i;
1979 
1980 	for (i = 0; i < MAXPARTITIONS; i++) {
1981 		if (mp1[i] == NULL && mp2[i] == NULL)
1982 			continue;
1983 
1984 		if ((mp1[i] != NULL && mp2[i] == NULL) ||
1985 		    (mp1[i] == NULL && mp2[i] != NULL) ||
1986 		    (strcmp(mp1[i], mp2[i]) != 0))
1987 			return(0);
1988 	}
1989 	return(1);
1990 }
1991 
1992 int
1993 mpsave(lp, mp, cdev, fstabfile)
1994 	struct disklabel *lp;
1995 	char **mp;
1996 	char *cdev;
1997 	char *fstabfile;
1998 {
1999 	int i, j, mpset;
2000 	char bdev[MAXPATHLEN], *p;
2001 	struct mountinfo mi[MAXPARTITIONS];
2002 	FILE *fp;
2003 
2004 	memset(&mi, 0, sizeof(mi));
2005 
2006 	for (i = 0, mpset = 0; i < MAXPARTITIONS; i++) {
2007 		if (mp[i] != NULL) {
2008 			mi[i].mountpoint = mp[i];
2009 			mi[i].partno = i;
2010 			mpset = 1;
2011 		}
2012 	}
2013 	/* Exit if there is nothing to do... */
2014 	if (!mpset)
2015 		return(0);
2016 
2017 	/* Convert cdev to bdev */
2018 	if (strncmp(_PATH_DEV, cdev, sizeof(_PATH_DEV) - 1) == 0 &&
2019 	    cdev[sizeof(_PATH_DEV) - 1] == 'r') {
2020 		snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV,
2021 		    &cdev[sizeof(_PATH_DEV)]);
2022 	} else {
2023 		if ((p = strrchr(cdev, '/')) == NULL || *(++p) != 'r')
2024 			return(1);
2025 		*p = '\0';
2026 		snprintf(bdev, sizeof(bdev), "%s%s", cdev, p + 1);
2027 		*p = 'r';
2028 	}
2029 	bdev[strlen(bdev) - 1] = '\0';
2030 
2031 	/* Sort mountpoints so we don't try to mount /usr/local before /usr */
2032 	qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp);
2033 
2034 	if ((fp = fopen(fstabfile, "w")) == NULL)
2035 		return(1);
2036 
2037 	for (i = 0; i < MAXPARTITIONS && mi[i].mountpoint != NULL; i++) {
2038 		j =  mi[i].partno;
2039 		fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev, 'a' + j,
2040 		    mi[i].mountpoint,
2041 		    fstypesnames[lp->d_partitions[j].p_fstype],
2042 		    j == 0 ? 1 : 2);
2043 	}
2044 	fclose(fp);
2045 	return(0);
2046 }
2047 
2048 int
2049 get_offset(lp, partno)
2050 	struct disklabel *lp;
2051 	int partno;
2052 {
2053 	u_int32_t ui;
2054 	struct partition *pp = &lp->d_partitions[partno];
2055 
2056 	for (;;) {
2057 		ui = getuint(lp, partno, "offset",
2058 		   "Starting sector for this partition.", pp->p_offset,
2059 		   pp->p_offset, 0, DO_CONVERSIONS |
2060 		   (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0));
2061 		if (ui == UINT_MAX - 1) {
2062 			fputs("Command aborted\n", stderr);
2063 			return(1);
2064 		} else if (ui == UINT_MAX)
2065 			fputs("Invalid entry\n", stderr);
2066 		else if (ui < starting_sector)
2067 			fprintf(stderr, "The OpenBSD portion of the disk starts"
2068 			    " at sector %u, you tried to add a partition at %u."
2069 			    "  You can use the 'b' command to change the size "
2070 			    "of the OpenBSD portion.\n" , starting_sector, ui);
2071 		else if (ui >= ending_sector)
2072 			fprintf(stderr, "The OpenBSD portion of the disk ends "
2073 			    "at sector %u, you tried to add a partition at %u."
2074 			    "  You can use the 'b' command to change the size "
2075 			    "of the OpenBSD portion.\n", ending_sector, ui);
2076 #ifdef AAT0
2077 		else if (partno == 0 && ui != 0)
2078 			fprintf(stderr, "This architecture requires that "
2079 			    "partition 'a' start at sector 0.\n");
2080 #endif
2081 		else
2082 			break;
2083 	}
2084 	pp->p_offset = ui;
2085 	return(0);
2086 }
2087 
2088 int
2089 get_size(lp, partno, freep, new)
2090 	struct disklabel *lp;
2091 	int partno;
2092 	u_int32_t *freep;
2093 	int new;
2094 {
2095 	u_int32_t ui;
2096 	struct partition *pp = &lp->d_partitions[partno];
2097 
2098 	for (;;) {
2099 		ui = getuint(lp, partno, "size", "Size of the partition.",
2100 		    pp->p_size, *freep, pp->p_offset, DO_CONVERSIONS |
2101 		    ((pp->p_fstype == FS_BSDFFS || pp->p_fstype == FS_SWAP) ?
2102 		    DO_ROUNDING : 0));
2103 		if (ui == UINT_MAX - 1) {
2104 			fputs("Command aborted\n", stderr);
2105 			return(1);
2106 		} else if (ui == UINT_MAX) {
2107 			fputs("Invalid entry\n", stderr);
2108 			continue;
2109 		}
2110 		if (new) {
2111 			if (ui > *freep)
2112 				/* XXX - steal space from another partition */
2113 				fprintf(stderr,"Sorry, there are only %u "
2114 				    "sectors left\n", *freep);
2115 			else if (pp->p_offset + ui > ending_sector)
2116 				fprintf(stderr, "The OpenBSD portion of the "
2117 				    "disk ends at sector %u, you tried to add "
2118 				    "a partition ending at sector %u.  You can "
2119 				    "use the 'b' command to change the size of "
2120 				    "the OpenBSD portion.\n",
2121 				    ending_sector, pp->p_offset + ui);
2122 			else
2123 				break;			/* ok */
2124 		} else {
2125 			if (ui == pp->p_size)
2126 				break;			/* no change */
2127 			if (partno == RAW_PART &&
2128 			    ui + pp->p_offset > lp->d_secperunit) {
2129 				fputs("'c' partition may not be larger than the disk\n",
2130 				    stderr);
2131 			} else if (pp->p_fstype == FS_UNUSED ||
2132 			    pp->p_fstype == FS_BOOT) {
2133 				/* don't care what's free */
2134 				pp->p_size = ui;
2135 				break;
2136 			} else {
2137 				if (ui > pp->p_size + *freep)
2138 					/* XXX - steal from another partition */
2139 					fprintf(stderr,
2140 					    "Size may not be larger than %u "
2141 					    "sectors\n", pp->p_size + *freep);
2142 				else {
2143 					*freep += pp->p_size - ui;
2144 					pp->p_size = ui;
2145 					break;			/* ok */
2146 				}
2147 			}
2148 		}
2149 	}
2150 	pp->p_size = ui;
2151 	return(0);
2152 }
2153 
2154 int
2155 get_fsize(lp, partno)
2156 	struct disklabel *lp;
2157 	int partno;
2158 {
2159 	u_int32_t ui;
2160 	struct partition *pp = &lp->d_partitions[partno];
2161 
2162 	for (;;) {
2163 		ui = getuint(lp, partno, "fragment size",
2164 		    "Size of fs block fragments.  Usually 1024 or 512.",
2165 		    pp->p_fsize, pp->p_fsize, 0, 0);
2166 		if (ui == UINT_MAX - 1) {
2167 			fputs("Command aborted\n", stderr);
2168 			return(1);
2169 		} else if (ui == UINT_MAX)
2170 			fputs("Invalid entry\n", stderr);
2171 		else
2172 			break;
2173 	}
2174 	if (ui == 0)
2175 		puts("Zero fragment size implies zero block size");
2176 	pp->p_fsize = ui;
2177 	return(0);
2178 }
2179 
2180 int
2181 get_bsize(lp, partno)
2182 	struct disklabel *lp;
2183 	int partno;
2184 {
2185 	u_int32_t ui;
2186 	struct partition *pp = &lp->d_partitions[partno];
2187 
2188 	/* Avoid dividing by zero... */
2189 	if (pp->p_fsize == 0) {
2190 		pp->p_frag = 0;
2191 		return(1);
2192 	}
2193 
2194 	for (;;) {
2195 		ui = getuint(lp, partno, "block size",
2196 		    "Size of filesystem blocks.  Usually 8192 or 4096.",
2197 		    pp->p_fsize * pp->p_frag, pp->p_fsize * pp->p_frag,
2198 		    0, 0);
2199 
2200 		/* sanity checks */
2201 		if (ui == UINT_MAX - 1) {
2202 			fputs("Command aborted\n", stderr);
2203 			return(1);
2204 		} else if (ui == UINT_MAX)
2205 			fputs("Invalid entry\n", stderr);
2206 		else if (ui < getpagesize())
2207 			fprintf(stderr,
2208 			    "Error: block size must be at least as big "
2209 			    "as page size (%d).\n", getpagesize());
2210 		else if (ui % pp->p_fsize != 0)
2211 			fputs("Error: block size must be a multiple of the "
2212 			    "fragment size.\n", stderr);
2213 		else if (ui / pp->p_fsize < 1)
2214 			fputs("Error: block size must be at least as big as "
2215 			    "fragment size.\n", stderr);
2216 		else
2217 			break;
2218 	}
2219 	pp->p_frag = ui / pp->p_fsize;
2220 	return(0);
2221 }
2222 
2223 int
2224 get_cpg(lp, partno)
2225 	struct disklabel *lp;
2226 	int partno;
2227 {
2228 	u_int32_t ui;
2229 	struct partition *pp = &lp->d_partitions[partno];
2230 
2231 	for (;;) {
2232 		ui = getuint(lp, partno, "cpg",
2233 		    "Number of filesystem cylinders per group."
2234 		    "  Usually 16 or 8.",
2235 		    pp->p_cpg ? pp->p_cpg : 16, 16, 0, 0);
2236 		if (ui == UINT_MAX - 1) {
2237 			fputs("Command aborted\n", stderr);
2238 			return(1);
2239 		} else if (ui == UINT_MAX)
2240 			fputs("Invalid entry\n", stderr);
2241 		else
2242 			break;
2243 	}
2244 	pp->p_cpg = ui;
2245 	return(0);
2246 }
2247 
2248 int
2249 get_fstype(lp, partno)
2250 	struct disklabel *lp;
2251 	int partno;
2252 {
2253 	char *p;
2254 	u_int32_t ui;
2255 	struct partition *pp = &lp->d_partitions[partno];
2256 
2257 	if (pp->p_fstype < FSMAXTYPES) {
2258 		p = getstring("FS type",
2259 		    "Filesystem type (usually 4.2BSD or swap)",
2260 		    fstypenames[pp->p_fstype]);
2261 		if (p == NULL) {
2262 			fputs("Command aborted\n", stderr);
2263 			return(1);
2264 		}
2265 		for (ui = 0; ui < FSMAXTYPES; ui++) {
2266 			if (!strcasecmp(p, fstypenames[ui])) {
2267 				pp->p_fstype = ui;
2268 				break;
2269 			}
2270 		}
2271 		if (ui >= FSMAXTYPES) {
2272 			printf("Unrecognized filesystem type '%s', treating as 'unknown'\n", p);
2273 			pp->p_fstype = FS_OTHER;
2274 		}
2275 	} else {
2276 		for (;;) {
2277 			ui = getuint(lp, partno, "FS type (decimal)",
2278 			    "Filesystem type as a decimal number; usually 7 (4.2BSD) or 1 (swap).",
2279 			    pp->p_fstype, pp->p_fstype, 0, 0);
2280 			if (ui == UINT_MAX - 1) {
2281 				fputs("Command aborted\n", stderr);
2282 				return(1);
2283 			} if (ui == UINT_MAX)
2284 				fputs("Invalid entry\n", stderr);
2285 			else
2286 				break;
2287 		}
2288 		pp->p_fstype = ui;
2289 	}
2290 	return(0);
2291 }
2292 
2293 int
2294 get_mp(lp, mp, partno)
2295 	struct disklabel *lp;
2296 	char **mp;
2297 	int partno;
2298 {
2299 	char *p;
2300 	struct partition *pp = &lp->d_partitions[partno];
2301 
2302 	if (mp != NULL && pp->p_fstype != FS_UNUSED &&
2303 	    pp->p_fstype != FS_SWAP && pp->p_fstype != FS_BOOT &&
2304 	    pp->p_fstype != FS_OTHER) {
2305 		for (;;) {
2306 			p = getstring("mount point",
2307 			    "Where to mount this filesystem (ie: / /var /usr)",
2308 			    mp[partno] ? mp[partno] : "none");
2309 			if (p == NULL) {
2310 				fputs("Command aborted\n", stderr);
2311 				return(1);
2312 			}
2313 			if (strcasecmp(p, "none") == 0) {
2314 				if (mp[partno] != NULL) {
2315 					free(mp[partno]);
2316 					mp[partno] = NULL;
2317 				}
2318 				break;
2319 			}
2320 			if (*p == '/') {
2321 				/* XXX - might as well realloc */
2322 				if (mp[partno] != NULL)
2323 					free(mp[partno]);
2324 				if ((mp[partno] = strdup(p)) == NULL)
2325 					errx(4, "out of memory");
2326 				break;
2327 			}
2328 			fputs("Mount points must start with '/'\n", stderr);
2329 		}
2330 	}
2331 	return(0);
2332 }
2333 
2334 int
2335 micmp(a1, a2)
2336 	const void *a1;
2337 	const void *a2;
2338 {
2339 	struct mountinfo *mi1 = (struct mountinfo *)a1;
2340 	struct mountinfo *mi2 = (struct mountinfo *)a2;
2341 
2342 	/* We want all the NULLs at the end... */
2343 	if (mi1->mountpoint == NULL && mi2->mountpoint == NULL)
2344 		return(0);
2345 	else if (mi1->mountpoint == NULL)
2346 		return(1);
2347 	else if (mi2->mountpoint == NULL)
2348 		return(-1);
2349 	else
2350 		return(strcmp(mi1->mountpoint, mi2->mountpoint));
2351 }
2352 
2353 void
2354 get_geometry(f, dgpp, bgpp)
2355 	int f;
2356 	struct disklabel **dgpp;
2357 	struct disklabel **bgpp;
2358 {
2359 #ifdef CPU_BIOS
2360 	int mib[4];
2361 	size_t size;
2362 	dev_t devno;
2363 	bios_diskinfo_t di;
2364 #endif
2365 	struct stat st;
2366 	struct disklabel *disk_geop;
2367 	struct disklabel *bios_geop;
2368 
2369 	if (fstat(f, &st) == -1)
2370 		err(4, "Can't stat device");
2371 
2372 	/* Get disk geometry */
2373 	if ((disk_geop = calloc(1, sizeof(struct disklabel))) == NULL)
2374 		errx(4, "out of memory");
2375 	if (ioctl(f, DIOCGPDINFO, disk_geop) < 0 &&
2376 	    ioctl(f, DIOCGDINFO, disk_geop) < 0)
2377 		err(4, "ioctl DIOCGDINFO");
2378 	*dgpp = disk_geop;
2379 
2380 	/* Get BIOS geometry */
2381 	*bgpp = NULL;
2382 #ifdef CPU_BIOS
2383 	mib[0] = CTL_MACHDEP;
2384 	mib[1] = CPU_CHR2BLK;
2385 	mib[2] = st.st_rdev;
2386 	size = sizeof(devno);
2387 	if (sysctl(mib, 3, &devno, &size, NULL, 0) == -1) {
2388 		warn("sysctl(machdep.chr2blk)");
2389 		return;
2390 	}
2391 	devno = MAKEBOOTDEV(major(devno), 0, 0, DISKUNIT(devno), RAW_PART);
2392 
2393 	mib[0] = CTL_MACHDEP;
2394 	mib[1] = CPU_BIOS;
2395 	mib[2] = BIOS_DISKINFO;
2396 	mib[3] = devno;
2397 	size = sizeof(di);
2398 	if (sysctl(mib, 4, &di, &size, NULL, 0) == -1) {
2399 		warn("Can't get bios geometry");
2400 		return;
2401 	}
2402 	if ((bios_geop = calloc(1, sizeof(struct disklabel))) == NULL)
2403 		errx(4, "out of memory");
2404 
2405 	bios_geop->d_secsize = DEV_BSIZE;
2406 	bios_geop->d_nsectors = di.bios_sectors;
2407 	bios_geop->d_ntracks = di.bios_heads;
2408 	bios_geop->d_ncylinders = di.bios_cylinders;
2409 	bios_geop->d_secpercyl = di.bios_sectors * di.bios_heads;
2410 	bios_geop->d_secperunit = di.bios_cylinders *
2411 	    di.bios_heads * di.bios_sectors;
2412 	*bgpp = bios_geop;
2413 #endif
2414 }
2415 
2416 void
2417 set_geometry(lp, dgp, bgp, ugp, p)
2418 	struct disklabel *lp;
2419 	struct disklabel *dgp;
2420 	struct disklabel *bgp;
2421 	struct disklabel *ugp;
2422 	char *p;
2423 {
2424 	if (p == NULL) {
2425 		p = getstring("[d]isk, [b]ios, or [u]ser geometry",
2426 		    "Enter 'd' to use the geometry based on what the disk "
2427 		    "itself thinks it is, 'b' to use what the BIOS says,"
2428 		    "or 'u' to use the geometry that was found on in the label.",
2429 		    "d");
2430 	}
2431 	if (p == NULL) {
2432 		fputs("Command aborted\n", stderr);
2433 		return;
2434 	}
2435 	switch (*p) {
2436 	case 'b':
2437 	case 'B':
2438 		if (bgp == NULL)
2439 			fputs("BIOS geometry not defined.\n", stderr);
2440 		else {
2441 			lp->d_secsize = bgp->d_secsize;
2442 			lp->d_nsectors = bgp->d_nsectors;
2443 			lp->d_ntracks = bgp->d_ntracks;
2444 			lp->d_ncylinders = bgp->d_ncylinders;
2445 			lp->d_secpercyl = bgp->d_secpercyl;
2446 			lp->d_secperunit = bgp->d_secperunit;
2447 		}
2448 		break;
2449 	case 'd':
2450 	case 'D':
2451 		if (dgp == NULL)
2452 			fputs("BIOS geometry not defined.\n", stderr);
2453 		else {
2454 			lp->d_secsize = dgp->d_secsize;
2455 			lp->d_nsectors = dgp->d_nsectors;
2456 			lp->d_ntracks = dgp->d_ntracks;
2457 			lp->d_ncylinders = dgp->d_ncylinders;
2458 			lp->d_secpercyl = dgp->d_secpercyl;
2459 			lp->d_secperunit = dgp->d_secperunit;
2460 		}
2461 		break;
2462 	case 'u':
2463 	case 'U':
2464 		if (ugp == NULL)
2465 			fputs("BIOS geometry not defined.\n", stderr);
2466 		else {
2467 			lp->d_secsize = ugp->d_secsize;
2468 			lp->d_nsectors = ugp->d_nsectors;
2469 			lp->d_ntracks = ugp->d_ntracks;
2470 			lp->d_ncylinders = ugp->d_ncylinders;
2471 			lp->d_secpercyl = ugp->d_secpercyl;
2472 			lp->d_secperunit = ugp->d_secperunit;
2473 			if (dgp != NULL && ugp->d_secsize == dgp->d_secsize &&
2474 			    ugp->d_nsectors == dgp->d_nsectors &&
2475 			    ugp->d_ntracks == dgp->d_ntracks &&
2476 			    ugp->d_ncylinders == dgp->d_ncylinders &&
2477 			    ugp->d_secpercyl == dgp->d_secpercyl &&
2478 			    ugp->d_secperunit == dgp->d_secperunit)
2479 				fputs("Note: user geometry is the same as disk "
2480 				    "geometry.\n", stderr);
2481 		}
2482 		break;
2483 	default:
2484 		fputs("You must enter either 'd', 'b', or 'u'.\n", stderr);
2485 		break;
2486 	}
2487 }
2488 
2489 void
2490 zero_partitions(lp, freep)
2491 	struct disklabel *lp;
2492 	u_int32_t *freep;
2493 {
2494 	int i;
2495 
2496 	for (i = 0; i < MAXPARTITIONS; i++)
2497 		memset(&lp->d_partitions[i], 0, sizeof(struct partition));
2498 	lp->d_partitions[RAW_PART].p_offset = starting_sector;
2499 	lp->d_partitions[RAW_PART].p_size = ending_sector - starting_sector;
2500 	editor_countfree(lp, freep);
2501 }
2502