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