xref: /openbsd-src/sbin/disklabel/editor.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: editor.c,v 1.91 2003/09/24 20:40:19 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.91 2003/09/24 20:40:19 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 #if defined (__sparc__) && !defined(__sparc64__)
518 	/* can't boot from > 8k boot blocks */
519 	pp->p_fsize = partno == 0 ? 1024 : 2048;
520 #else
521 	pp->p_fsize = 2048;
522 #endif
523 	pp->p_frag = 8;
524 	pp->p_cpg = 16;
525 	old_offset = pp->p_offset;
526 	old_size = pp->p_size;
527 
528 getoff1:
529 	/* Get offset */
530 	if (get_offset(lp, partno) != 0) {
531 		pp->p_size = 0;			/* effective delete */
532 		return;
533 	}
534 
535 	/* Recompute recommended size based on new offset */
536 	ui = pp->p_fstype;
537 	pp->p_fstype = FS_UNUSED;
538 	chunks = free_chunks(lp);
539 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
540 		if (pp->p_offset >= chunks[i].start &&
541 		    pp->p_offset < chunks[i].stop) {
542 			pp->p_size = chunks[i].stop - pp->p_offset;
543 			break;
544 		}
545 	}
546 	pp->p_fstype = ui;
547 
548 	/* Get size */
549 	if (get_size(lp, partno, freep, 1) != 0 || pp->p_size == 0) {
550 		pp->p_size = 0;			/* effective delete */
551 		return;
552 	}
553 
554 	/* Check for overlap */
555 	if (has_overlap(lp, freep, 0)) {
556 		printf("\nPlease re-enter an offset and size for partition "
557 		    "%c.\n", 'a' + partno);
558 		pp->p_offset = old_offset;
559 		pp->p_size = old_size;
560 		goto getoff1;		/* Yeah, I know... */
561 	}
562 
563 	/* Get filesystem type and mountpoint */
564 	if (get_fstype(lp, partno) != 0 || get_mp(lp, mp, partno) != 0) {
565 		pp->p_size = 0;			/* effective delete */
566 		return;
567 	}
568 
569 	if (expert && pp->p_fstype == FS_BSDFFS) {
570 		/* Get fsize, bsize, and cpg */
571 		if (get_fsize(lp, partno) != 0 || get_bsize(lp, partno) != 0 ||
572 		    get_cpg(lp, partno) != 0) {
573 			pp->p_size = 0;			/* effective delete */
574 			return;
575 		}
576 	}
577 
578 	/* Update free sector count and make sure things stay contiguous. */
579 	*freep -= pp->p_size;
580 	if (pp->p_size + pp->p_offset > ending_sector ||
581 	    has_overlap(lp, freep, -1))
582 		make_contiguous(lp);
583 }
584 
585 /*
586  * Set the mountpoint of an existing partition ('name').
587  */
588 void
589 editor_name(struct disklabel *lp, char **mp, char *p)
590 {
591 	struct partition *pp;
592 	int partno;
593 
594 	/* Change which partition? */
595 	if (p == NULL) {
596 		p = getstring("partition to name",
597 		    "The letter of the partition to name, a - p.", NULL);
598 	}
599 	if (p == NULL) {
600 		fputs("Command aborted\n", stderr);
601 		return;
602 	}
603 	partno = p[0] - 'a';
604 	pp = &lp->d_partitions[partno];
605 	if (partno < 0 || partno >= lp->d_npartitions) {
606 		fprintf(stderr, "Partition must be between 'a' and '%c'.\n",
607 		    'a' + lp->d_npartitions - 1);
608 		return;
609 	} else if (partno >= lp->d_npartitions ||
610 	    (pp->p_fstype == FS_UNUSED && pp->p_size == 0)) {
611 		fprintf(stderr, "Partition '%c' is not in use.\n", 'a' + partno);
612 		return;
613 	}
614 
615 	/* Not all fstypes can be named */
616 	if (pp->p_fstype == FS_UNUSED || pp->p_fstype == FS_SWAP ||
617 	    pp->p_fstype == FS_BOOT || pp->p_fstype == FS_OTHER) {
618 		fprintf(stderr, "You cannot name a filesystem of type %s.\n",
619 		    fstypenames[lp->d_partitions[partno].p_fstype]);
620 		return;
621 	}
622 
623 	get_mp(lp, mp, partno);
624 }
625 
626 /*
627  * Change an existing partition.
628  */
629 void
630 editor_modify(struct disklabel *lp, char **mp, u_int32_t *freep, char *p)
631 {
632 	struct partition origpart, *pp;
633 	int partno;
634 
635 	/* Change which partition? */
636 	if (p == NULL) {
637 		p = getstring("partition to modify",
638 		    "The letter of the partition to modify, a - p.", NULL);
639 	}
640 	if (p == NULL) {
641 		fputs("Command aborted\n", stderr);
642 		return;
643 	}
644 	partno = p[0] - 'a';
645 	pp = &lp->d_partitions[partno];
646 	origpart = lp->d_partitions[partno];
647 	if (partno < 0 || partno >= lp->d_npartitions) {
648 		fprintf(stderr, "Partition must be between 'a' and '%c'.\n",
649 		    'a' + lp->d_npartitions - 1);
650 		return;
651 	} else if (partno >= lp->d_npartitions ||
652 	    (pp->p_fstype == FS_UNUSED && pp->p_size == 0)) {
653 		fprintf(stderr, "Partition '%c' is not in use.\n", 'a' + partno);
654 		return;
655 	}
656 
657 	/* Get filesystem type */
658 	if (get_fstype(lp, partno) != 0) {
659 		*pp = origpart;			/* undo changes */
660 		return;
661 	}
662 
663 	/* Did they disable/enable the partition? */
664 	if ((pp->p_fstype == FS_UNUSED || pp->p_fstype == FS_BOOT) &&
665 	    origpart.p_fstype != FS_UNUSED && origpart.p_fstype != FS_BOOT)
666 		*freep += origpart.p_size;
667 	else if (pp->p_fstype != FS_UNUSED && pp->p_fstype != FS_BOOT &&
668 	    (origpart.p_fstype == FS_UNUSED || origpart.p_fstype == FS_BOOT)) {
669 		if (pp->p_size > *freep) {
670 			fprintf(stderr,
671 			    "Warning, need %u sectors but there are only %u "
672 			    "free.  Setting size to %u.\n", pp->p_size, *freep,
673 			    *freep);
674 			pp->p_fstype = *freep;
675 			*freep = 0;
676 		} else
677 			*freep -= pp->p_size;		/* have enough space */
678 	}
679 
680 getoff2:
681 	/* Get offset */
682 	if (get_offset(lp, partno) != 0) {
683 		*pp = origpart;			/* undo changes */
684 		return;
685 	}
686 
687 	/* Get size */
688 	if (get_size(lp, partno, freep, 0) != 0 || pp->p_size == 0) {
689 		pp->p_size = 0;			/* effective delete */
690 		return;
691 	}
692 
693 	/* Check for overlap and restore if not resolved */
694 	if (has_overlap(lp, freep, 0)) {
695 		puts("\nPlease re-enter an offset and size");
696 		pp->p_offset = origpart.p_offset;
697 		pp->p_size = origpart.p_size;
698 		goto getoff2;		/* Yeah, I know... */
699 	}
700 
701 	/* get mount point */
702 	if (get_mp(lp, mp, partno) != 0) {
703 		*pp = origpart;			/* undo changes */
704 		return;
705 	}
706 
707 	if (expert && (pp->p_fstype == FS_BSDFFS || pp->p_fstype == FS_UNUSED)){
708 		/* get fsize */
709 		if (get_fsize(lp, partno) != 0) {
710 			*pp = origpart;		/* undo changes */
711 			return;
712 		}
713 
714 		/* get bsize */
715 		if (get_bsize(lp, partno) != 0) {
716 			*pp = origpart;		/* undo changes */
717 			return;
718 		}
719 
720 		if (pp->p_fstype == FS_BSDFFS) {
721 			/* get cpg */
722 			if (get_cpg(lp, partno) != 0) {
723 				*pp = origpart;	/* undo changes */
724 				return;
725 			}
726 		}
727 	}
728 
729 	/* Make sure things stay contiguous. */
730 	if (pp->p_size + pp->p_offset > ending_sector ||
731 	    has_overlap(lp, freep, -1))
732 		make_contiguous(lp);
733 }
734 
735 /*
736  * Delete an existing partition.
737  */
738 void
739 editor_delete(struct disklabel *lp, char **mp, u_int32_t *freep, char *p)
740 {
741 	int c;
742 
743 	if (p == NULL) {
744 		p = getstring("partition to delete",
745 		    "The letter of the partition to delete, a - p, or '*'.",
746 		    NULL);
747 	}
748 	if (p == NULL) {
749 		fputs("Command aborted\n", stderr);
750 		return;
751 	}
752 	if (p[0] == '*') {
753 		for (c = 0; c < lp->d_npartitions; c++) {
754 			if (c == RAW_PART)
755 				continue;
756 
757 			/* Update free sector count. */
758 			if (lp->d_partitions[c].p_fstype != FS_UNUSED &&
759 			    lp->d_partitions[c].p_fstype != FS_BOOT &&
760 			    lp->d_partitions[c].p_size != 0)
761 				*freep += lp->d_partitions[c].p_size;
762 
763 			(void)memset(&lp->d_partitions[c], 0,
764 			    sizeof(lp->d_partitions[c]));
765 		}
766 		return;
767 	}
768 	c = p[0] - 'a';
769 	if (c < 0 || c >= lp->d_npartitions)
770 		fprintf(stderr, "Partition must be between 'a' and '%c'.\n",
771 		    'a' + lp->d_npartitions - 1);
772 	else if (c >= lp->d_npartitions || (lp->d_partitions[c].p_fstype ==
773 	    FS_UNUSED && lp->d_partitions[c].p_size == 0))
774 		fprintf(stderr, "Partition '%c' is not in use.\n", 'a' + c);
775 	else if (c == RAW_PART)
776 		fputs(
777 "You may not delete the 'c' partition.  The 'c' partition must exist and\n"
778 "should span the entire disk.  By default it is of type 'unused' and so\n"
779 "does not take up any space.\n", stderr);
780 	else {
781 		/* Update free sector count. */
782 		if (lp->d_partitions[c].p_offset < ending_sector &&
783 		    lp->d_partitions[c].p_offset >= starting_sector &&
784 		    lp->d_partitions[c].p_fstype != FS_UNUSED &&
785 		    lp->d_partitions[c].p_fstype != FS_BOOT &&
786 		    lp->d_partitions[c].p_size != 0)
787 			*freep += lp->d_partitions[c].p_size;
788 
789 		/* Really delete it (as opposed to just setting to "unused") */
790 		(void)memset(&lp->d_partitions[c], 0,
791 		    sizeof(lp->d_partitions[c]));
792 	}
793 	if (mp != NULL && mp[c] != NULL) {
794 		free(mp[c]);
795 		mp[c] = NULL;
796 	}
797 }
798 
799 /*
800  * Simplified display() for use with the builtin editor.
801  */
802 void
803 editor_display(struct disklabel *lp, char **mp, u_int32_t *freep, char unit)
804 {
805 	int i;
806 	int width;
807 
808 	printf("device: %s\n", specname);
809 	printf("type: %s\n", dktypenames[lp->d_type]);
810 	printf("disk: %.*s\n", (int)sizeof(lp->d_typename), lp->d_typename);
811 	printf("label: %.*s\n", (int)sizeof(lp->d_packname), lp->d_packname);
812 	printf("bytes/sector: %ld\n", (long)lp->d_secsize);
813 	printf("sectors/track: %ld\n", (long)lp->d_nsectors);
814 	printf("tracks/cylinder: %ld\n", (long)lp->d_ntracks);
815 	printf("sectors/cylinder: %ld\n", (long)lp->d_secpercyl);
816 	printf("cylinders: %ld\n", (long)lp->d_ncylinders);
817 	printf("total sectors: %ld\n", (long)lp->d_secperunit);
818 	printf("free sectors: %u\n", *freep);
819 	printf("rpm: %ld\n", (long)lp->d_rpm);
820 	printf("\n%d partitions:\n", lp->d_npartitions);
821 	width = width_partition(lp, unit);
822 	printf("#    %*.*s %*.*s    fstype   [fsize bsize   cpg]\n",
823 		width, width, "size", width, width, "offset");
824 	for (i = 0; i < lp->d_npartitions; i++)
825 		display_partition(stdout, lp, mp, i, unit, width);
826 }
827 
828 /*
829  * Find the next reasonable starting offset and returns it.
830  * Assumes there is a least one free sector left (returns 0 if not).
831  */
832 u_int32_t
833 next_offset(struct disklabel *lp, u_int32_t *sizep)
834 {
835 	struct partition **spp;
836 	struct diskchunk *chunks;
837 	u_int16_t npartitions;
838 	u_int32_t new_offset, new_size;
839 	int i, good_offset;
840 
841 	/* Get a sorted list of the partitions */
842 	if ((spp = sort_partitions(lp, &npartitions)) == NULL)
843 		return(starting_sector);
844 
845 	new_offset = starting_sector;
846 	for (i = 0; i < npartitions; i++ ) {
847 		/*
848 		 * Is new_offset inside this partition?  If so,
849 		 * make it the next sector after the partition ends.
850 		 */
851 		if (spp[i]->p_offset + spp[i]->p_size < ending_sector &&
852 		    ((new_offset >= spp[i]->p_offset &&
853 		    new_offset < spp[i]->p_offset + spp[i]->p_size) ||
854 		    (new_offset + *sizep >= spp[i]->p_offset && new_offset
855 		    + *sizep <= spp[i]->p_offset + spp[i]->p_size)))
856 			new_offset = spp[i]->p_offset + spp[i]->p_size;
857 	}
858 
859 	/* Did we find a suitable offset? */
860 	for (good_offset = 1, i = 0; i < npartitions; i++ ) {
861 		if (new_offset + *sizep >= spp[i]->p_offset &&
862 		    new_offset + *sizep <= spp[i]->p_offset + spp[i]->p_size) {
863 			/* Nope */
864 			good_offset = 0;
865 			break;
866 		}
867 	}
868 
869 	/* Specified size is too big, find something that fits */
870 	if (!good_offset) {
871 		chunks = free_chunks(lp);
872 		new_size = 0;
873 		for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
874 			if (chunks[i].stop - chunks[i].start > new_size) {
875 			    new_size = chunks[i].stop - chunks[i].start;
876 			    new_offset = chunks[i].start;
877 			}
878 		}
879 		/* XXX - should do something intelligent if new_size == 0 */
880 		*sizep = new_size;
881 	}
882 
883 	(void)free(spp);
884 	return(new_offset);
885 }
886 
887 /*
888  * Change the size of an existing partition.
889  */
890 void
891 editor_change(struct disklabel *lp, u_int32_t *freep, char *p)
892 {
893 	int partno;
894 	u_int32_t newsize;
895 	struct partition *pp;
896 
897 	if (p == NULL) {
898 		p = getstring("partition to change size",
899 		    "The letter of the partition to change size, a - p.", NULL);
900 	}
901 	if (p == NULL) {
902 		fputs("Command aborted\n", stderr);
903 		return;
904 	}
905 	partno = p[0] - 'a';
906 	if (partno < 0 || partno >= lp->d_npartitions) {
907 		fprintf(stderr, "Partition must be between 'a' and '%c'.\n",
908 		    'a' + lp->d_npartitions - 1);
909 		return;
910 	} else if (partno >= lp->d_npartitions ||
911 	    lp->d_partitions[partno].p_size == 0) {
912 		fprintf(stderr, "Partition '%c' is not in use.\n", 'a' + partno);
913 		return;
914 	}
915 	pp = &lp->d_partitions[partno];
916 
917 	printf("Partition %c is currently %u sectors in size (%u free).\n",
918 	    partno + 'a', pp->p_size, *freep);
919 	/* XXX - make maxsize lp->d_secperunit if FS_UNUSED/FS_BOOT? */
920 	newsize = getuint(lp, partno, "new size", "Size of the partition.  "
921 	    "You may also say +/- amount for a relative change.",
922 	    pp->p_size, pp->p_size + *freep, pp->p_offset, DO_CONVERSIONS |
923 	    (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0));
924 	if (newsize == UINT_MAX - 1) {
925 		fputs("Command aborted\n", stderr);
926 		return;
927 	} else if (newsize == UINT_MAX) {
928 		fputs("Invalid entry\n", stderr);
929 		return;
930 	} else if (newsize == pp->p_size)
931 		return;
932 
933 	if (pp->p_fstype != FS_UNUSED && pp->p_fstype != FS_BOOT) {
934 		if (newsize > pp->p_size) {
935 			if (newsize - pp->p_size > *freep) {
936 				fprintf(stderr,
937 				    "Only %u sectors free, you asked for %u\n",
938 				    *freep, newsize - pp->p_size);
939 				return;
940 			}
941 			*freep -= newsize - pp->p_size;
942 		} else if (newsize < pp->p_size) {
943 			*freep += pp->p_size - newsize;
944 		}
945 	} else {
946 		if (partno == RAW_PART && newsize +
947 		    pp->p_offset > lp->d_secperunit) {
948 			fputs("'c' partition may not be larger than the disk\n",
949 			    stderr);
950 			return;
951 		}
952 	}
953 	pp->p_size = newsize;
954 	if (newsize + pp->p_offset > ending_sector ||
955 	    has_overlap(lp, freep, -1))
956 		make_contiguous(lp);
957 }
958 
959 void
960 make_contiguous(struct disklabel *lp)
961 {
962 	struct partition **spp;
963 	u_int16_t npartitions;
964 	int i;
965 
966 	/* Get a sorted list of the partitions */
967 	if ((spp = sort_partitions(lp, &npartitions)) == NULL)
968 		return;
969 
970 	/*
971 	 * Make everything contiguous but don't muck with start of the first one
972 	 * or partitions not in the BSD part of the label.
973 	 */
974 	for (i = 1; i < npartitions; i++) {
975 		if (spp[i]->p_offset >= starting_sector ||
976 		    spp[i]->p_offset < ending_sector)
977 			spp[i]->p_offset =
978 			    spp[i - 1]->p_offset + spp[i - 1]->p_size;
979 	}
980 
981 	(void)free(spp);
982 }
983 
984 /*
985  * Sort the partitions based on starting offset.
986  * This assumes there can be no overlap.
987  */
988 int
989 partition_cmp(const void *e1, const void *e2)
990 {
991 	struct partition *p1 = *(struct partition **)e1;
992 	struct partition *p2 = *(struct partition **)e2;
993 
994 	return((int)(p1->p_offset - p2->p_offset));
995 }
996 
997 char *
998 getstring(char *prompt, char *helpstring, char *oval)
999 {
1000 	static char buf[BUFSIZ];
1001 	int n;
1002 
1003 	buf[0] = '\0';
1004 	do {
1005 		printf("%s: [%s] ", prompt, oval ? oval : "");
1006 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
1007 			buf[0] = '\0';
1008 			if (feof(stdin)) {
1009 				clearerr(stdin);
1010 				putchar('\n');
1011 				return(NULL);
1012 			}
1013 		}
1014 		n = strlen(buf);
1015 		if (n > 0 && buf[n-1] == '\n')
1016 			buf[--n] = '\0';
1017 		if (buf[0] == '?')
1018 			puts(helpstring);
1019 		else if (oval != NULL && buf[0] == '\0')
1020 			strlcpy(buf, oval, sizeof(buf));
1021 	} while (buf[0] == '?');
1022 
1023 	return(&buf[0]);
1024 }
1025 
1026 /*
1027  * Returns UINT_MAX on error
1028  * Usually only called by helper functions.
1029  */
1030 u_int32_t
1031 getuint(struct disklabel *lp, int partno, char *prompt, char *helpstring,
1032     u_int32_t oval, u_int32_t maxval, u_int32_t offset, int flags)
1033 {
1034 	char buf[BUFSIZ], *endptr, *p, operator = '\0';
1035 	u_int32_t rval = oval;
1036 	size_t n;
1037 	int mult = 1;
1038 	double d;
1039 
1040 	/* We only care about the remainder */
1041 	offset = offset % lp->d_secpercyl;
1042 
1043 	buf[0] = '\0';
1044 	do {
1045 		printf("%s: [%u] ", prompt, oval);
1046 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
1047 			buf[0] = '\0';
1048 			if (feof(stdin)) {
1049 				clearerr(stdin);
1050 				putchar('\n');
1051 				return(UINT_MAX - 1);
1052 			}
1053 		}
1054 		n = strlen(buf);
1055 		if (n > 0 && buf[n-1] == '\n')
1056 			buf[--n] = '\0';
1057 		if (buf[0] == '?')
1058 			puts(helpstring);
1059 	} while (buf[0] == '?');
1060 
1061 	if (buf[0] == '*' && buf[1] == '\0') {
1062 		rval = maxval;
1063 	} else {
1064 		/* deal with units */
1065 		if (buf[0] != '\0' && n > 0) {
1066 			if ((flags & DO_CONVERSIONS)) {
1067 				switch (tolower(buf[n-1])) {
1068 
1069 				case 'c':
1070 					mult = lp->d_secpercyl;
1071 					buf[--n] = '\0';
1072 					break;
1073 				case 'b':
1074 					mult = -lp->d_secsize;
1075 					buf[--n] = '\0';
1076 					break;
1077 				case 'k':
1078 					mult = 1024 / lp->d_secsize;
1079 					buf[--n] = '\0';
1080 					break;
1081 				case 'm':
1082 					mult = 1048576 / lp->d_secsize;
1083 					buf[--n] = '\0';
1084 					break;
1085 				case 'g':
1086 					mult = 1073741824 / lp->d_secsize;
1087 					buf[--n] = '\0';
1088 					break;
1089 				}
1090 			}
1091 
1092 			/* Did they give us an operator? */
1093 			p = &buf[0];
1094 			if (*p == '+' || *p == '-')
1095 				operator = *p++;
1096 
1097 			endptr = p;
1098 			errno = 0;
1099 			d = strtod(p, &endptr);
1100 			if (errno == ERANGE)
1101 				rval = UINT_MAX;	/* too big/small */
1102 			else if (*endptr != '\0') {
1103 				errno = EINVAL;		/* non-numbers in str */
1104 				rval = UINT_MAX;
1105 			} else {
1106 				/* XXX - should check for overflow */
1107 				if (mult > 0)
1108 					rval = d * mult;
1109 				else
1110 					/* Negative mult means divide (fancy) */
1111 					rval = d / (-mult);
1112 
1113 				/* Apply the operator */
1114 				if (operator == '+')
1115 					rval += oval;
1116 				else if (operator == '-')
1117 					rval = oval - rval;
1118 			}
1119 		}
1120 	}
1121 	if ((flags & DO_ROUNDING) && rval < UINT_MAX) {
1122 #ifndef CYLCHECK
1123 		/* Round to nearest cylinder unless given in sectors */
1124 		if (mult != 1)
1125 #endif
1126 		{
1127 			u_int32_t cyls;
1128 
1129 			/* If we round up past the end, round down instead */
1130 			cyls = (u_int32_t)((rval / (double)lp->d_secpercyl)
1131 			    + 0.5);
1132 			if (cyls != 0 && lp->d_secpercyl != 0) {
1133 				if ((cyls * lp->d_secpercyl) - offset > maxval)
1134 					cyls--;
1135 
1136 				if (rval != (cyls * lp->d_secpercyl) - offset) {
1137 					rval = (cyls * lp->d_secpercyl) - offset;
1138 					printf("Rounding to nearest cylinder: %u\n",
1139 					    rval);
1140 				}
1141 			}
1142 		}
1143 	}
1144 
1145 	return(rval);
1146 }
1147 
1148 /*
1149  * Check for partition overlap in lp and prompt the user
1150  * to resolve the overlap if any is found.  Returns 1
1151  * if unable to resolve, else 0.
1152  */
1153 int
1154 has_overlap(struct disklabel *lp, u_int32_t *freep, int resolve)
1155 {
1156 	struct partition **spp;
1157 	u_int16_t npartitions;
1158 	int c, i, j;
1159 	char buf[BUFSIZ];
1160 
1161 	/* Get a sorted list of the partitions */
1162 	spp = sort_partitions(lp, &npartitions);
1163 
1164 	if (npartitions < RAW_PART) {
1165 		(void)free(spp);
1166 		return(0);			/* nothing to do */
1167 	}
1168 
1169 	/* Now that we have things sorted by starting sector check overlap */
1170 	for (i = 0; i < npartitions; i++) {
1171 		for (j = i + 1; j < npartitions; j++) {
1172 			/* `if last_sec_in_part + 1 > first_sec_in_next_part' */
1173 			if (spp[i]->p_offset + spp[i]->p_size > spp[j]->p_offset) {
1174 				/* Don't print, just return */
1175 				if (resolve == -1) {
1176 					(void)free(spp);
1177 					return(1);
1178 				}
1179 
1180 				/* Overlap!  Convert to real part numbers. */
1181 				i = ((char *)spp[i] - (char *)lp->d_partitions)
1182 				    / sizeof(**spp);
1183 				j = ((char *)spp[j] - (char *)lp->d_partitions)
1184 				    / sizeof(**spp);
1185 				printf("\nError, partitions %c and %c overlap:\n",
1186 				    'a' + i, 'a' + j);
1187 				puts("         size   offset    fstype   [fsize bsize   cpg]");
1188 				display_partition(stdout, lp, NULL, i, 0, 0);
1189 				display_partition(stdout, lp, NULL, j, 0, 0);
1190 
1191 				/* Did they ask us to resolve it ourselves? */
1192 				if (resolve != 1) {
1193 					(void)free(spp);
1194 					return(1);
1195 				}
1196 
1197 				/* Get partition to disable or ^D */
1198 				do {
1199 					printf("Disable which one? (^D to abort) [%c %c] ",
1200 					    'a' + i, 'a' + j);
1201 					buf[0] = '\0';
1202 					if (!fgets(buf, sizeof(buf), stdin)) {
1203 						putchar('\n');
1204 						return(1);	/* ^D */
1205 					}
1206 					c = buf[0] - 'a';
1207 				} while (buf[1] != '\n' && buf[1] != '\0' &&
1208 				    c != i && c != j);
1209 
1210 				/* Mark the selected one as unused */
1211 				lp->d_partitions[c].p_fstype = FS_UNUSED;
1212 				*freep += lp->d_partitions[c].p_size;
1213 				(void)free(spp);
1214 				return(has_overlap(lp, freep, resolve));
1215 			}
1216 		}
1217 	}
1218 
1219 	(void)free(spp);
1220 	return(0);
1221 }
1222 
1223 void
1224 edit_parms(struct disklabel *lp, u_int32_t *freep)
1225 {
1226 	char *p;
1227 	u_int32_t ui;
1228 	struct disklabel oldlabel = *lp;
1229 
1230 	printf("Changing device parameters for %s:\n", specname);
1231 
1232 	/* disk type */
1233 	for (;;) {
1234 		p = getstring("disk type",
1235 		    "What kind of disk is this?  Usually SCSI, ESDI, ST506, or "
1236 		    "floppy (use ESDI for IDE).", dktypenames[lp->d_type]);
1237 		if (p == NULL) {
1238 			fputs("Command aborted\n", stderr);
1239 			return;
1240 		}
1241 		if (strcasecmp(p, "IDE") == 0)
1242 			ui = DTYPE_ESDI;
1243 		else
1244 			for (ui = 1; ui < DKMAXTYPES &&
1245 			    strcasecmp(p, dktypenames[ui]); ui++)
1246 				;
1247 		if (ui < DKMAXTYPES) {
1248 			break;
1249 		} else {
1250 			printf("\"%s\" is not a valid disk type.\n", p);
1251 			fputs("Valid types are: ", stdout);
1252 			for (ui = 1; ui < DKMAXTYPES; ui++) {
1253 				printf("\"%s\"", dktypenames[ui]);
1254 				if (ui < DKMAXTYPES - 1)
1255 					fputs(", ", stdout);
1256 			}
1257 			putchar('\n');
1258 		}
1259 	}
1260 	lp->d_type = ui;
1261 
1262 	/* pack/label id */
1263 	p = getstring("label name",
1264 	    "15 char string that describes this label, usually the disk name.",
1265 	    lp->d_packname);
1266 	if (p == NULL) {
1267 		fputs("Command aborted\n", stderr);
1268 		*lp = oldlabel;		/* undo damage */
1269 		return;
1270 	}
1271 	strncpy(lp->d_packname, p, sizeof(lp->d_packname));	/* checked */
1272 
1273 	/* sectors/track */
1274 	for (;;) {
1275 		ui = getuint(lp, 0, "sectors/track",
1276 		    "The Numer of sectors per track.", lp->d_nsectors,
1277 		    lp->d_nsectors, 0, 0);
1278 		if (ui == UINT_MAX - 1) {
1279 			fputs("Command aborted\n", stderr);
1280 			*lp = oldlabel;		/* undo damage */
1281 			return;
1282 		} if (ui == UINT_MAX)
1283 			fputs("Invalid entry\n", stderr);
1284 		else
1285 			break;
1286 	}
1287 	lp->d_nsectors = ui;
1288 
1289 	/* tracks/cylinder */
1290 	for (;;) {
1291 		ui = getuint(lp, 0, "tracks/cylinder",
1292 		    "The number of tracks per cylinder.", lp->d_ntracks,
1293 		    lp->d_ntracks, 0, 0);
1294 		if (ui == UINT_MAX - 1) {
1295 			fputs("Command aborted\n", stderr);
1296 			*lp = oldlabel;		/* undo damage */
1297 			return;
1298 		} else if (ui == UINT_MAX)
1299 			fputs("Invalid entry\n", stderr);
1300 		else
1301 			break;
1302 	}
1303 	lp->d_ntracks = ui;
1304 
1305 	/* sectors/cylinder */
1306 	for (;;) {
1307 		ui = getuint(lp, 0, "sectors/cylinder",
1308 		    "The number of sectors per cylinder (Usually sectors/track "
1309 		    "* tracks/cylinder).", lp->d_secpercyl, lp->d_secpercyl,
1310 		    0, 0);
1311 		if (ui == UINT_MAX - 1) {
1312 			fputs("Command aborted\n", stderr);
1313 			*lp = oldlabel;		/* undo damage */
1314 			return;
1315 		} else if (ui == UINT_MAX)
1316 			fputs("Invalid entry\n", stderr);
1317 		else
1318 			break;
1319 	}
1320 	lp->d_secpercyl = ui;
1321 
1322 	/* number of cylinders */
1323 	for (;;) {
1324 		ui = getuint(lp, 0, "number of cylinders",
1325 		    "The total number of cylinders on the disk.",
1326 		    lp->d_ncylinders, lp->d_ncylinders, 0, 0);
1327 		if (ui == UINT_MAX - 1) {
1328 			fputs("Command aborted\n", stderr);
1329 			*lp = oldlabel;		/* undo damage */
1330 			return;
1331 		} else if (ui == UINT_MAX)
1332 			fputs("Invalid entry\n", stderr);
1333 		else
1334 			break;
1335 	}
1336 	lp->d_ncylinders = ui;
1337 
1338 	/* total sectors */
1339 	for (;;) {
1340 		ui = getuint(lp, 0, "total sectors",
1341 		    "The total number of sectors on the disk.",
1342 		    lp->d_secperunit ? lp->d_secperunit :
1343 		    lp->d_ncylinders * lp->d_ncylinders,
1344 		    lp->d_ncylinders * lp->d_ncylinders, 0, 0);
1345 		if (ui == UINT_MAX - 1) {
1346 			fputs("Command aborted\n", stderr);
1347 			*lp = oldlabel;		/* undo damage */
1348 			return;
1349 		} else if (ui == UINT_MAX)
1350 			fputs("Invalid entry\n", stderr);
1351 		else if (ui > lp->d_secperunit &&
1352 		    ending_sector == lp->d_secperunit) {
1353 			/* grow free count */
1354 			*freep += ui - lp->d_secperunit;
1355 			puts("You may want to increase the size of the 'c' "
1356 			    "partition.");
1357 			break;
1358 		} else if (ui < lp->d_secperunit &&
1359 		    ending_sector == lp->d_secperunit) {
1360 			/* shrink free count */
1361 			if (lp->d_secperunit - ui > *freep)
1362 				fprintf(stderr,
1363 				    "Not enough free space to shrink by %u "
1364 				    "sectors (only %u sectors left)\n",
1365 				    lp->d_secperunit - ui, *freep);
1366 			else {
1367 				*freep -= lp->d_secperunit - ui;
1368 				break;
1369 			}
1370 		} else
1371 			break;
1372 	}
1373 	/* Adjust ending_sector if necessary. */
1374 	if (ending_sector > ui)
1375 		ending_sector = ui;
1376 	lp->d_secperunit = ui;
1377 
1378 	/* rpm */
1379 	for (;;) {
1380 		ui = getuint(lp, 0, "rpm",
1381 		  "The rotational speed of the disk in revolutions per minute.",
1382 		  lp->d_rpm, lp->d_rpm, 0, 0);
1383 		if (ui == UINT_MAX - 1) {
1384 			fputs("Command aborted\n", stderr);
1385 			*lp = oldlabel;		/* undo damage */
1386 			return;
1387 		} else if (ui == UINT_MAX)
1388 			fputs("Invalid entry\n", stderr);
1389 		else
1390 			break;
1391 	}
1392 	lp->d_rpm = ui;
1393 
1394 	/* interleave */
1395 	for (;;) {
1396 		ui = getuint(lp, 0, "interleave",
1397 		  "The physical sector interleave, set when formatting.  Almost always 1.",
1398 		  lp->d_interleave, lp->d_interleave, 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 || ui == 0)
1404 			fputs("Invalid entry\n", stderr);
1405 		else
1406 			break;
1407 	}
1408 	lp->d_interleave = ui;
1409 }
1410 
1411 struct partition **
1412 sort_partitions(struct disklabel *lp, u_int16_t *npart)
1413 {
1414 	u_int16_t npartitions;
1415 	struct partition **spp;
1416 	int i;
1417 
1418 	/* How many "real" partitions do we have? */
1419 	for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) {
1420 		if (lp->d_partitions[i].p_fstype != FS_UNUSED &&
1421 		    lp->d_partitions[i].p_fstype != FS_BOOT &&
1422 		    lp->d_partitions[i].p_size != 0)
1423 			npartitions++;
1424 	}
1425 	if (npartitions == 0) {
1426 		*npart = 0;
1427 		return(NULL);
1428 	}
1429 
1430 	/* Create an array of pointers to the partition data */
1431 	if ((spp = malloc(sizeof(struct partition *) * npartitions)) == NULL)
1432 		errx(4, "out of memory");
1433 	for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) {
1434 		if (lp->d_partitions[i].p_fstype != FS_UNUSED &&
1435 		    lp->d_partitions[i].p_fstype != FS_BOOT &&
1436 		    lp->d_partitions[i].p_size != 0)
1437 			spp[npartitions++] = &lp->d_partitions[i];
1438 	}
1439 
1440 	/*
1441 	 * Sort the partitions based on starting offset.
1442 	 * This is safe because we guarantee no overlap.
1443 	 */
1444 	if (npartitions > 1)
1445 		if (heapsort((void *)spp, npartitions, sizeof(spp[0]),
1446 		    partition_cmp))
1447 			err(4, "failed to sort partition table");
1448 
1449 	*npart = npartitions;
1450 	return(spp);
1451 }
1452 
1453 /*
1454  * Get a valid disk type if necessary.
1455  */
1456 void
1457 getdisktype(struct disklabel *lp, char *banner, char *dev)
1458 {
1459 	int i;
1460 	char *s, *def = "SCSI";
1461 	struct dtypes {
1462 		char *dev;
1463 		char *type;
1464 	} dtypes[] = {
1465 		{ "sd",   "SCSI" },
1466 		{ "rz",   "SCSI" },
1467 		{ "wd",   "IDE" },
1468 		{ "fd",   "FLOPPY" },
1469 		{ "xd",   "SMD" },
1470 		{ "xy",   "SMD" },
1471 		{ "hd",   "HP-IB" },
1472 		{ "ccd",  "CCD" },
1473 		{ "vnd",  "VND" },
1474 		{ "svnd", "VND" },
1475 		{ NULL,   NULL }
1476 	};
1477 
1478 	if ((s = basename(dev)) != NULL) {
1479 		if (*s == 'r')
1480 			s++;
1481 		i = strcspn(s, "0123456789");
1482 		s[i] = '\0';
1483 		dev = s;
1484 		for (i = 0; dtypes[i].dev != NULL; i++) {
1485 			if (strcmp(dev, dtypes[i].dev) == 0) {
1486 				def = dtypes[i].type;
1487 				break;
1488 			}
1489 		}
1490 	}
1491 
1492 	if (lp->d_type > DKMAXTYPES || lp->d_type == 0) {
1493 		puts(banner);
1494 		puts("Possible values are:");
1495 		printf("\"IDE\", ");
1496 		for (i = 1; i < DKMAXTYPES; i++) {
1497 			printf("\"%s\"", dktypenames[i]);
1498 			if (i < DKMAXTYPES - 1)
1499 				fputs(", ", stdout);
1500 		}
1501 		putchar('\n');
1502 
1503 		for (;;) {
1504 			s = getstring("Disk type",
1505 			    "What kind of disk is this?  Usually SCSI, IDE, "
1506 			    "ESDI, CCD, ST506, or floppy.", def);
1507 			if (s == NULL)
1508 				continue;
1509 			if (strcasecmp(s, "IDE") == 0) {
1510 				lp->d_type = DTYPE_ESDI;
1511 				return;
1512 			}
1513 			for (i = 1; i < DKMAXTYPES; i++)
1514 				if (strcasecmp(s, dktypenames[i]) == 0) {
1515 					lp->d_type = i;
1516 					return;
1517 				}
1518 			printf("\"%s\" is not a valid disk type.\n", s);
1519 			fputs("Valid types are: ", stdout);
1520 			for (i = 1; i < DKMAXTYPES; i++) {
1521 				printf("\"%s\"", dktypenames[i]);
1522 				if (i < DKMAXTYPES - 1)
1523 					fputs(", ", stdout);
1524 			}
1525 			putchar('\n');
1526 		}
1527 	}
1528 }
1529 
1530 /*
1531  * Get beginning and ending sectors of the OpenBSD portion of the disk
1532  * from the user.
1533  * XXX - should mention MBR values if DOSLABEL
1534  */
1535 void
1536 set_bounds(struct disklabel *lp, u_int32_t *freep)
1537 {
1538 	u_int32_t ui, start_temp;
1539 
1540 	/* Starting sector */
1541 	do {
1542 		ui = getuint(lp, 0, "Starting sector",
1543 		  "The start of the OpenBSD portion of the disk.",
1544 		  starting_sector, lp->d_secperunit, 0, 0);
1545 		if (ui == UINT_MAX - 1) {
1546 			fputs("Command aborted\n", stderr);
1547 			return;
1548 		}
1549 	} while (ui >= lp->d_secperunit);
1550 	start_temp = ui;
1551 
1552 	/* Size */
1553 	do {
1554 		ui = getuint(lp, 0, "Size ('*' for entire disk)",
1555 		  "The size of the OpenBSD portion of the disk ('*' for the "
1556 		  "entire disk).", ending_sector - starting_sector,
1557 		  lp->d_secperunit - start_temp, 0, 0);
1558 		if (ui == UINT_MAX - 1) {
1559 			fputs("Command aborted\n", stderr);
1560 			return;
1561 		}
1562 	} while (ui > lp->d_secperunit - start_temp);
1563 	ending_sector = start_temp + ui;
1564 	starting_sector = start_temp;
1565 
1566 	/* Recalculate the free sectors */
1567 	editor_countfree(lp, freep);
1568 }
1569 
1570 /*
1571  * Return a list of the "chunks" of free space available
1572  */
1573 struct diskchunk *
1574 free_chunks(struct disklabel *lp)
1575 {
1576 	u_int16_t npartitions;
1577 	struct partition **spp;
1578 	static struct diskchunk chunks[MAXPARTITIONS + 2];
1579 	int i, numchunks;
1580 
1581 	/* Sort the partitions based on offset */
1582 	spp = sort_partitions(lp, &npartitions);
1583 
1584 	/* If there are no partitions, it's all free. */
1585 	if (spp == NULL) {
1586 		chunks[0].start = starting_sector;
1587 		chunks[0].stop = ending_sector;
1588 		chunks[1].start = chunks[1].stop = 0;
1589 		return(chunks);
1590 	}
1591 
1592 	/* Find chunks of free space */
1593 	numchunks = 0;
1594 	if (spp && spp[0]->p_offset > 0) {
1595 		chunks[0].start = starting_sector;
1596 		chunks[0].stop = spp[0]->p_offset;
1597 		numchunks++;
1598 	}
1599 	for (i = 0; i < npartitions; i++) {
1600 		if (i + 1 < npartitions) {
1601 			if (spp[i]->p_offset + spp[i]->p_size < spp[i+1]->p_offset) {
1602 				chunks[numchunks].start =
1603 				    spp[i]->p_offset + spp[i]->p_size;
1604 				chunks[numchunks].stop = spp[i+1]->p_offset;
1605 				numchunks++;
1606 			}
1607 		} else {
1608 			/* Last partition */
1609 			if (spp[i]->p_offset + spp[i]->p_size < ending_sector) {
1610 
1611 				chunks[numchunks].start =
1612 				    spp[i]->p_offset + spp[i]->p_size;
1613 				chunks[numchunks].stop = ending_sector;
1614 				numchunks++;
1615 			}
1616 		}
1617 	}
1618 
1619 	/* Terminate and return */
1620 	chunks[numchunks].start = chunks[numchunks].stop = 0;
1621 	(void)free(spp);
1622 	return(chunks);
1623 }
1624 
1625 /*
1626  * What is the OpenBSD portion of the disk?  Uses the MBR if applicable.
1627  */
1628 void
1629 find_bounds(struct disklabel *lp, struct disklabel *bios_lp)
1630 {
1631 #ifdef DOSLABEL
1632 	struct partition *pp = &lp->d_partitions[RAW_PART];
1633 #endif
1634 	/* Defaults */
1635 	/* XXX - reserve a cylinder for hp300? */
1636 	starting_sector = 0;
1637 	ending_sector = lp->d_secperunit;
1638 
1639 #ifdef DOSLABEL
1640 	/*
1641 	 * If we have an MBR, use values from the {Open,Free,Net}BSD partition
1642 	 */
1643 	if (dosdp) {
1644 	    if (dosdp->dp_typ == DOSPTYP_OPENBSD ||
1645 		    dosdp->dp_typ == DOSPTYP_FREEBSD ||
1646 		    dosdp->dp_typ == DOSPTYP_NETBSD) {
1647 			u_int32_t i, new_end;
1648 
1649 			/* Set start and end based on fdisk partition bounds */
1650 			starting_sector = get_le(&dosdp->dp_start);
1651 			ending_sector = starting_sector + get_le(&dosdp->dp_size);
1652 
1653 			/*
1654 			 * If the ending sector of the BSD fdisk partition
1655 			 * is equal to the ending sector of the BIOS geometry
1656 			 * but the real sector count > BIOS sector count,
1657 			 * adjust the bounds accordingly.  We do this because
1658 			 * the BIOS geometry is limited to disks of ~4gig.
1659 			 */
1660 			if (bios_lp && ending_sector == bios_lp->d_secperunit &&
1661 			    lp->d_secperunit > bios_lp->d_secperunit)
1662 				ending_sector = lp->d_secperunit;
1663 
1664 			/*
1665 			 * If there are any BSD or SWAP partitions beyond
1666 			 * ending_sector we extend ending_sector to include
1667 			 * them.  This is done because the BIOS geometry is
1668 			 * generally different from the disk geometry.
1669 			 */
1670 			for (i = new_end = 0; i < lp->d_npartitions; i++) {
1671 				pp = &lp->d_partitions[i];
1672 				if ((pp->p_fstype == FS_BSDFFS ||
1673 				    pp->p_fstype == FS_SWAP) &&
1674 				    pp->p_size + pp->p_offset > new_end)
1675 					new_end = pp->p_size + pp->p_offset;
1676 			}
1677 			if (new_end > ending_sector)
1678 				ending_sector = new_end;
1679 		} else {
1680 			/* Don't trounce the MBR */
1681 			starting_sector = 63;
1682 		}
1683 
1684 		printf("\nTreating sectors %u-%u as the OpenBSD portion of the "
1685 		    "disk.\nYou can use the 'b' command to change this.\n",
1686 		    starting_sector, ending_sector);
1687 	}
1688 #elif (NUMBOOT == 1)
1689 	/* Boot blocks take up the first cylinder */
1690 	starting_sector = lp->d_secpercyl;
1691 	printf("\nReserving the first data cylinder for boot blocks.\n"
1692 	    "You can use the 'b' command to change this.\n");
1693 #endif
1694 }
1695 
1696 /*
1697  * Calculate free space.
1698  */
1699 void
1700 editor_countfree(struct disklabel *lp, u_int32_t *freep)
1701 {
1702 	struct partition *pp;
1703 	int i;
1704 
1705 	*freep = ending_sector - starting_sector;
1706 	for (i = 0; i < lp->d_npartitions; i++) {
1707 		    pp = &lp->d_partitions[i];
1708 		    if (pp->p_fstype != FS_UNUSED && pp->p_fstype != FS_BOOT &&
1709 			pp->p_size > 0 &&
1710 			pp->p_offset + pp->p_size <= ending_sector &&
1711 			pp->p_offset >= starting_sector)
1712 			*freep -= pp->p_size;
1713 	}
1714 }
1715 
1716 void
1717 editor_help(char *arg)
1718 {
1719 
1720 	/* XXX - put these strings in a table instead? */
1721 	switch (*arg) {
1722 	case 'p':
1723 		puts(
1724 "The 'p' command prints the current disk label.  By default, it prints the\n"
1725 "size and offset in sectors (a sector is usually 512 bytes).  The 'p' command\n"
1726 "takes an optional units argument.  Possible values are 'b' for bytes, 'c'\n"
1727 "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n");
1728 		break;
1729 	case 'M':
1730 		puts(
1731 "The 'M' command pipes the entire OpenBSD manual page for disk label through\n"
1732 "the pager specified by the PAGER environment variable or 'less' if PAGER is\n"
1733 "not set.  It is especially useful during install when the normal system\n"
1734 "manual is not available.\n");
1735 		break;
1736 	case 'e':
1737 		puts(
1738 "The 'e' command is used to edit the disk drive parameters.  These include\n"
1739 "the number of sectors/track, tracks/cylinder, sectors/cylinder, number of\n"
1740 "cylinders on the disk , total sectors on the disk, rpm, interleave, disk\n"
1741 "type, and a descriptive label string.  You should not change these unless\n"
1742 "you know what you are doing\n");
1743 		break;
1744 	case 'a':
1745 		puts(
1746 "The 'a' command adds new partitions to the disk.  It takes as an optional\n"
1747 "argument the partition letter to add.  If you do not specify a partition\n"
1748 "letter, you will be prompted for it; the next available letter will be the\n"
1749 "default answer\n");
1750 		break;
1751 	case 'b':
1752 		puts(
1753 "The 'b' command is used to change the boundaries of the OpenBSD portion of\n"
1754 "the disk.  This is only useful on disks with an fdisk partition.  By default,\n"
1755 "on a disk with an fdisk partition, the boundaries are set to be the first\n"
1756 "and last sectors of the OpenBSD fdisk partition.  You should only change\n"
1757 "these if your fdisk partition table is incorrect or you have a disk larger\n"
1758 "than 8gig, since 8gig is the maximum size an fdisk partition can be.  You\n"
1759 "may enter '*' at the 'Size' prompt to indicate the entire size of the disk\n"
1760 "(minus the starting sector).  Use this option with care; if you extend the\n"
1761 "boundaries such that they overlap with another operating system you will\n"
1762 "corrupt the other operating system's data.\n");
1763 		break;
1764 	case 'c':
1765 		puts(
1766 "The 'c' command is used to change the size of an existing partition.  It\n"
1767 "takes as an optional argument the partition letter to change.  If you do not\n"
1768 "specify a partition letter, you will be prompted for one.  You may add a '+'\n"
1769 "or '-' prefix to the new size to increase or decrease the existing value\n"
1770 "instead of entering an absolute value.  You may also use a suffix to indicate\n"
1771 "the units the values is in terms of.  Possible suffixes are 'b' for bytes,\n"
1772 "'c' for cylinders, 'k' for kilobytes, 'm' for megabytes, 'g' for gigabytes or\n"
1773 "no suffix for sectors (usually 512 bytes).  You may also enter '*' to change\n"
1774 "the size to be the total number of free sectors remaining.\n");
1775 		break;
1776 	case 'D':
1777 		puts(
1778 "The 'D' command will set the disk label to the default values as reported\n"
1779 "by the disk itself.  This similates the case where there is no disk label.\n");
1780 		break;
1781 	case 'd':
1782 		puts(
1783 "The 'd' command is used to delete an existing partition.  It takes as an\n"
1784 "optional argument the partition letter to change.  If you do not specify a\n"
1785 "partition letter, you will be prompted for one.  You may not delete the ``c''\n"
1786 "partition as 'c' must always exist and by default is marked as 'unused' (so\n"
1787 "it does not take up any space).\n");
1788 		break;
1789 	case 'g':
1790 		puts(
1791 "The 'g' command is used select which disk geometry to use, the disk, BIOS, or\n"
1792 "user geometry.  It takes as an optional argument ``d'', ``b'', or ``u''.  If \n"
1793 "you do not specify the type as an argument, you will be prompted for it.\n");
1794 		break;
1795 	case 'm':
1796 		puts(
1797 "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"
1798 "partition letter, you will be prompted for one.  This option allows the user\n"
1799 "to change the filesystem type, starting offset, partition size, block fragment\n"
1800 "size, block size, and cylinders per group for the specified partition (not all\n"
1801 "parameters are configurable for non-BSD partitions).\n");
1802 		break;
1803 	case 'n':
1804 		puts(
1805 "The 'n' command is used to set the mount point for a partition (ie: name it).\n"
1806 "It takes as an optional argument the partition letter to name.  If you do\n"
1807 "not specify a partition letter, you will be prompted for one.  This option\n"
1808 "is only valid if disklabel was invoked with the -F flag.\n");
1809 		break;
1810 	case 'r':
1811 		puts(
1812 "The 'r' command is used to recalculate the free space available.  This option\n"
1813 "should really not be necessary under normal circumstances but can be useful if\n"
1814 "disklabel gets confused.\n");
1815 		break;
1816 	case 'u':
1817 		puts(
1818 "The 'u' command will undo (or redo) the last change.  Entering 'u' once will\n"
1819 "undo your last change.  Entering it again will restore the change.\n");
1820 		break;
1821 	case 's':
1822 		puts(
1823 "The 's' command is used to save a copy of the label to a file in ascii format\n"
1824 "(suitable for loading via disklabel's [-R] option).  It takes as an optional\n"
1825 "argument the filename to save the label to.  If you do not specify a filename,\n"
1826 "you will be prompted for one.\n");
1827 		break;
1828 	case 'w':
1829 		puts(
1830 "The 'w' command will write the current label to disk.  This option will\n"
1831 "commit any changes to the on-disk label.\n");
1832 		break;
1833 	case 'q':
1834 		puts(
1835 "The 'q' command quits the label editor.  If any changes have been made you\n"
1836 "will be asked whether or not to save the changes to the on-disk label.\n");
1837 		break;
1838 	case 'X':
1839 		puts(
1840 "The 'X' command toggles disklabel in to/out of 'expert mode'.  By default,\n"
1841 "some settings are reserved for experts only (such as the block and fragment\n"
1842 "size on ffs partitions).\n");
1843 		break;
1844 	case 'x':
1845 		puts(
1846 "The 'x' command exits the label editor without saving any changes to the\n"
1847 "on-disk label.\n");
1848 		break;
1849 	case 'z':
1850 		puts(
1851 "The 'z' command zeroes out the existing partition table, leaving only the 'c'\n"
1852 "partition.  The drive parameters are not changed.\n");
1853 		break;
1854 	default:
1855 		puts("Available commands:");
1856 		puts("\tp [unit]  - print label.");
1857 		puts("\tM         - show entire OpenBSD man page for disklabel.");
1858 		puts("\te         - edit drive parameters.");
1859 		puts("\ta [part]  - add new partition.");
1860 		puts("\tb         - set OpenBSD disk boundaries.");
1861 		puts("\tc [part]  - change partition size.");
1862 		puts("\td [part]  - delete partition.");
1863 		puts("\tD         - set label to default.");
1864 		puts("\tg [d|b]   - Use [d]isk or [b]ios geometry.");
1865 		puts("\tm [part]  - modify existing partition.");
1866 		puts("\tn [part]  - set the mount point for a partition.");
1867 		puts("\tr         - recalculate free space.");
1868 		puts("\tu         - undo last change.");
1869 		puts("\ts [path]  - save label to file.");
1870 		puts("\tw         - write label to disk.");
1871 		puts("\tq         - quit and save changes.");
1872 		puts("\tx         - exit without saving changes.");
1873 		puts("\tX         - toggle expert mode.");
1874 		puts("\tz         - zero out partition table.");
1875 		puts("\t? [cmnd]  - this message or command specific help.");
1876 		puts(
1877 "Numeric parameters may use suffixes to indicate units:\n\t"
1878 "'b' for bytes, 'c' for cylinders, 'k' for kilobytes, 'm' for megabytes,\n\t"
1879 "'g' for gigabytes or no suffix for sectors (usually 512 bytes).\n\t"
1880 "Non-sector units will be rounded to the nearest cylinder.\n"
1881 "Entering '?' at most prompts will give you (simple) context sensitive help.");
1882 		break;
1883 	}
1884 }
1885 
1886 char **
1887 mpcopy(char **to, char **from)
1888 {
1889 	int i;
1890 	char *top;
1891 
1892 	for (i = 0; i < MAXPARTITIONS; i++) {
1893 		if (from[i] != NULL) {
1894 			int len = strlen(from[i]) + 1;
1895 
1896 			top = realloc(to[i], len);
1897 			if (top == NULL)
1898 				errx(4, "out of memory");
1899 			to[i] = top;
1900 			(void)strlcpy(to[i], from[i], len);
1901 		} else if (to[i] != NULL) {
1902 			free(to[i]);
1903 			to[i] = NULL;
1904 		}
1905 	}
1906 	return(to);
1907 }
1908 
1909 int
1910 mpequal(char **mp1, char **mp2)
1911 {
1912 	int i;
1913 
1914 	for (i = 0; i < MAXPARTITIONS; i++) {
1915 		if (mp1[i] == NULL && mp2[i] == NULL)
1916 			continue;
1917 
1918 		if ((mp1[i] != NULL && mp2[i] == NULL) ||
1919 		    (mp1[i] == NULL && mp2[i] != NULL) ||
1920 		    (strcmp(mp1[i], mp2[i]) != 0))
1921 			return(0);
1922 	}
1923 	return(1);
1924 }
1925 
1926 int
1927 mpsave(struct disklabel *lp, char **mp, char *cdev, char *fstabfile)
1928 {
1929 	int i, j, mpset;
1930 	char bdev[MAXPATHLEN], *p;
1931 	struct mountinfo mi[MAXPARTITIONS];
1932 	FILE *fp;
1933 
1934 	memset(&mi, 0, sizeof(mi));
1935 
1936 	for (i = 0, mpset = 0; i < MAXPARTITIONS; i++) {
1937 		if (mp[i] != NULL) {
1938 			mi[i].mountpoint = mp[i];
1939 			mi[i].partno = i;
1940 			mpset = 1;
1941 		}
1942 	}
1943 	/* Exit if there is nothing to do... */
1944 	if (!mpset)
1945 		return(0);
1946 
1947 	/* Convert cdev to bdev */
1948 	if (strncmp(_PATH_DEV, cdev, sizeof(_PATH_DEV) - 1) == 0 &&
1949 	    cdev[sizeof(_PATH_DEV) - 1] == 'r') {
1950 		snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV,
1951 		    &cdev[sizeof(_PATH_DEV)]);
1952 	} else {
1953 		if ((p = strrchr(cdev, '/')) == NULL || *(++p) != 'r')
1954 			return(1);
1955 		*p = '\0';
1956 		snprintf(bdev, sizeof(bdev), "%s%s", cdev, p + 1);
1957 		*p = 'r';
1958 	}
1959 	bdev[strlen(bdev) - 1] = '\0';
1960 
1961 	/* Sort mountpoints so we don't try to mount /usr/local before /usr */
1962 	qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp);
1963 
1964 	if ((fp = fopen(fstabfile, "w")) == NULL)
1965 		return(1);
1966 
1967 	for (i = 0; i < MAXPARTITIONS && mi[i].mountpoint != NULL; i++) {
1968 		j =  mi[i].partno;
1969 		fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev, 'a' + j,
1970 		    mi[i].mountpoint,
1971 		    fstypesnames[lp->d_partitions[j].p_fstype],
1972 		    j == 0 ? 1 : 2);
1973 	}
1974 	fclose(fp);
1975 	return(0);
1976 }
1977 
1978 int
1979 get_offset(struct disklabel *lp, int partno)
1980 {
1981 	u_int32_t ui;
1982 	struct partition *pp = &lp->d_partitions[partno];
1983 
1984 	for (;;) {
1985 		ui = getuint(lp, partno, "offset",
1986 		   "Starting sector for this partition.", pp->p_offset,
1987 		   pp->p_offset, 0, DO_CONVERSIONS |
1988 		   (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0));
1989 		if (ui == UINT_MAX - 1) {
1990 			fputs("Command aborted\n", stderr);
1991 			return(1);
1992 		} else if (ui == UINT_MAX)
1993 			fputs("Invalid entry\n", stderr);
1994 		else if (ui < starting_sector)
1995 			fprintf(stderr, "The OpenBSD portion of the disk starts"
1996 			    " at sector %u, you tried to add a partition at %u."
1997 			    "  You can use the 'b' command to change the size "
1998 			    "of the OpenBSD portion.\n" , starting_sector, ui);
1999 		else if (ui >= ending_sector)
2000 			fprintf(stderr, "The OpenBSD portion of the disk ends "
2001 			    "at sector %u, you tried to add a partition at %u."
2002 			    "  You can use the 'b' command to change the size "
2003 			    "of the OpenBSD portion.\n", ending_sector, ui);
2004 #ifdef AAT0
2005 		else if (partno == 0 && ui != 0)
2006 			fprintf(stderr, "This architecture requires that "
2007 			    "partition 'a' start at sector 0.\n");
2008 #endif
2009 		else
2010 			break;
2011 	}
2012 	pp->p_offset = ui;
2013 	return(0);
2014 }
2015 
2016 int
2017 get_size(struct disklabel *lp, int partno, u_int32_t *freep, int new)
2018 {
2019 	u_int32_t ui;
2020 	struct partition *pp = &lp->d_partitions[partno];
2021 
2022 	for (;;) {
2023 		ui = getuint(lp, partno, "size", "Size of the partition.",
2024 		    pp->p_size, *freep, pp->p_offset, DO_CONVERSIONS |
2025 		    ((pp->p_fstype == FS_BSDFFS || pp->p_fstype == FS_SWAP) ?
2026 		    DO_ROUNDING : 0));
2027 		if (ui == UINT_MAX - 1) {
2028 			fputs("Command aborted\n", stderr);
2029 			return(1);
2030 		} else if (ui == UINT_MAX) {
2031 			fputs("Invalid entry\n", stderr);
2032 			continue;
2033 		}
2034 		if (new) {
2035 			if (ui > *freep)
2036 				/* XXX - steal space from another partition */
2037 				fprintf(stderr,"Sorry, there are only %u "
2038 				    "sectors left\n", *freep);
2039 			else if (pp->p_offset + ui > ending_sector)
2040 				fprintf(stderr, "The OpenBSD portion of the "
2041 				    "disk ends at sector %u, you tried to add "
2042 				    "a partition ending at sector %u.  You can "
2043 				    "use the 'b' command to change the size of "
2044 				    "the OpenBSD portion.\n",
2045 				    ending_sector, pp->p_offset + ui);
2046 			else
2047 				break;			/* ok */
2048 		} else {
2049 			if (ui == pp->p_size)
2050 				break;			/* no change */
2051 			if (partno == RAW_PART &&
2052 			    ui + pp->p_offset > lp->d_secperunit) {
2053 				fputs("'c' partition may not be larger than the disk\n",
2054 				    stderr);
2055 			} else if (pp->p_fstype == FS_UNUSED ||
2056 			    pp->p_fstype == FS_BOOT) {
2057 				/* don't care what's free */
2058 				pp->p_size = ui;
2059 				break;
2060 			} else {
2061 				if (ui > pp->p_size + *freep)
2062 					/* XXX - steal from another partition */
2063 					fprintf(stderr,
2064 					    "Size may not be larger than %u "
2065 					    "sectors\n", pp->p_size + *freep);
2066 				else {
2067 					*freep += pp->p_size - ui;
2068 					pp->p_size = ui;
2069 					break;			/* ok */
2070 				}
2071 			}
2072 		}
2073 	}
2074 	pp->p_size = ui;
2075 	return(0);
2076 }
2077 
2078 int
2079 get_fsize(struct disklabel *lp, int partno)
2080 {
2081 	u_int32_t ui;
2082 	struct partition *pp = &lp->d_partitions[partno];
2083 
2084 	for (;;) {
2085 		ui = getuint(lp, partno, "fragment size",
2086 		    "Size of fs block fragments.  Usually 2048 or 512.",
2087 		    pp->p_fsize, pp->p_fsize, 0, 0);
2088 		if (ui == UINT_MAX - 1) {
2089 			fputs("Command aborted\n", stderr);
2090 			return(1);
2091 		} else if (ui == UINT_MAX)
2092 			fputs("Invalid entry\n", stderr);
2093 		else
2094 			break;
2095 	}
2096 	if (ui == 0)
2097 		puts("Zero fragment size implies zero block size");
2098 	pp->p_fsize = ui;
2099 	return(0);
2100 }
2101 
2102 int
2103 get_bsize(struct disklabel *lp, int partno)
2104 {
2105 	u_int32_t ui;
2106 	struct partition *pp = &lp->d_partitions[partno];
2107 
2108 	/* Avoid dividing by zero... */
2109 	if (pp->p_fsize == 0) {
2110 		pp->p_frag = 0;
2111 		return(1);
2112 	}
2113 
2114 	for (;;) {
2115 		ui = getuint(lp, partno, "block size",
2116 		    "Size of filesystem blocks.  Usually 16384 or 4096.",
2117 		    pp->p_fsize * pp->p_frag, pp->p_fsize * pp->p_frag,
2118 		    0, 0);
2119 
2120 		/* sanity checks */
2121 		if (ui == UINT_MAX - 1) {
2122 			fputs("Command aborted\n", stderr);
2123 			return(1);
2124 		} else if (ui == UINT_MAX)
2125 			fputs("Invalid entry\n", stderr);
2126 		else if (ui < getpagesize())
2127 			fprintf(stderr,
2128 			    "Error: block size must be at least as big "
2129 			    "as page size (%d).\n", getpagesize());
2130 		else if (ui % pp->p_fsize != 0)
2131 			fputs("Error: block size must be a multiple of the "
2132 			    "fragment size.\n", stderr);
2133 		else if (ui / pp->p_fsize < 1)
2134 			fputs("Error: block size must be at least as big as "
2135 			    "fragment size.\n", stderr);
2136 		else
2137 			break;
2138 	}
2139 	pp->p_frag = ui / pp->p_fsize;
2140 	return(0);
2141 }
2142 
2143 int
2144 get_cpg(struct disklabel *lp, int partno)
2145 {
2146 	u_int32_t ui;
2147 	struct partition *pp = &lp->d_partitions[partno];
2148 
2149 	for (;;) {
2150 		ui = getuint(lp, partno, "cpg",
2151 		    "Number of filesystem cylinders per group."
2152 		    "  Usually 16 or 8.",
2153 		    pp->p_cpg ? pp->p_cpg : 16, 16, 0, 0);
2154 		if (ui == UINT_MAX - 1) {
2155 			fputs("Command aborted\n", stderr);
2156 			return(1);
2157 		} else if (ui == UINT_MAX)
2158 			fputs("Invalid entry\n", stderr);
2159 		else
2160 			break;
2161 	}
2162 	pp->p_cpg = ui;
2163 	return(0);
2164 }
2165 
2166 int
2167 get_fstype(struct disklabel *lp, int partno)
2168 {
2169 	char *p;
2170 	u_int32_t ui;
2171 	struct partition *pp = &lp->d_partitions[partno];
2172 
2173 	if (pp->p_fstype < FSMAXTYPES) {
2174 		p = getstring("FS type",
2175 		    "Filesystem type (usually 4.2BSD or swap)",
2176 		    fstypenames[pp->p_fstype]);
2177 		if (p == NULL) {
2178 			fputs("Command aborted\n", stderr);
2179 			return(1);
2180 		}
2181 		for (ui = 0; ui < FSMAXTYPES; ui++) {
2182 			if (!strcasecmp(p, fstypenames[ui])) {
2183 				pp->p_fstype = ui;
2184 				break;
2185 			}
2186 		}
2187 		if (ui >= FSMAXTYPES) {
2188 			printf("Unrecognized filesystem type '%s', treating as 'unknown'\n", p);
2189 			pp->p_fstype = FS_OTHER;
2190 		}
2191 	} else {
2192 		for (;;) {
2193 			ui = getuint(lp, partno, "FS type (decimal)",
2194 			    "Filesystem type as a decimal number; usually 7 (4.2BSD) or 1 (swap).",
2195 			    pp->p_fstype, pp->p_fstype, 0, 0);
2196 			if (ui == UINT_MAX - 1) {
2197 				fputs("Command aborted\n", stderr);
2198 				return(1);
2199 			} if (ui == UINT_MAX)
2200 				fputs("Invalid entry\n", stderr);
2201 			else
2202 				break;
2203 		}
2204 		pp->p_fstype = ui;
2205 	}
2206 	return(0);
2207 }
2208 
2209 int
2210 get_mp(struct disklabel *lp, char **mp, int partno)
2211 {
2212 	char *p;
2213 	struct partition *pp = &lp->d_partitions[partno];
2214 
2215 	if (mp != NULL && pp->p_fstype != FS_UNUSED &&
2216 	    pp->p_fstype != FS_SWAP && pp->p_fstype != FS_BOOT &&
2217 	    pp->p_fstype != FS_OTHER) {
2218 		for (;;) {
2219 			p = getstring("mount point",
2220 			    "Where to mount this filesystem (ie: / /var /usr)",
2221 			    mp[partno] ? mp[partno] : "none");
2222 			if (p == NULL) {
2223 				fputs("Command aborted\n", stderr);
2224 				return(1);
2225 			}
2226 			if (strcasecmp(p, "none") == 0) {
2227 				if (mp[partno] != NULL) {
2228 					free(mp[partno]);
2229 					mp[partno] = NULL;
2230 				}
2231 				break;
2232 			}
2233 			if (*p == '/') {
2234 				/* XXX - might as well realloc */
2235 				if (mp[partno] != NULL)
2236 					free(mp[partno]);
2237 				if ((mp[partno] = strdup(p)) == NULL)
2238 					errx(4, "out of memory");
2239 				break;
2240 			}
2241 			fputs("Mount points must start with '/'\n", stderr);
2242 		}
2243 	}
2244 	return(0);
2245 }
2246 
2247 int
2248 micmp(const void *a1, const void *a2)
2249 {
2250 	struct mountinfo *mi1 = (struct mountinfo *)a1;
2251 	struct mountinfo *mi2 = (struct mountinfo *)a2;
2252 
2253 	/* We want all the NULLs at the end... */
2254 	if (mi1->mountpoint == NULL && mi2->mountpoint == NULL)
2255 		return(0);
2256 	else if (mi1->mountpoint == NULL)
2257 		return(1);
2258 	else if (mi2->mountpoint == NULL)
2259 		return(-1);
2260 	else
2261 		return(strcmp(mi1->mountpoint, mi2->mountpoint));
2262 }
2263 
2264 void
2265 get_geometry(int f, struct disklabel **dgpp, struct disklabel **bgpp)
2266 {
2267 #ifdef CPU_BIOS
2268 	int mib[4];
2269 	size_t size;
2270 	dev_t devno;
2271 	bios_diskinfo_t di;
2272 #endif
2273 	struct stat st;
2274 	struct disklabel *disk_geop;
2275 #ifdef CPU_BIOS
2276 	struct disklabel *bios_geop;
2277 #endif
2278 	if (fstat(f, &st) == -1)
2279 		err(4, "Can't stat device");
2280 
2281 	/* Get disk geometry */
2282 	if ((disk_geop = calloc(1, sizeof(struct disklabel))) == NULL)
2283 		errx(4, "out of memory");
2284 	if (ioctl(f, DIOCGPDINFO, disk_geop) < 0 &&
2285 	    ioctl(f, DIOCGDINFO, disk_geop) < 0)
2286 		err(4, "ioctl DIOCGDINFO");
2287 	*dgpp = disk_geop;
2288 
2289 	/* Get BIOS geometry */
2290 	*bgpp = NULL;
2291 #ifdef CPU_BIOS
2292 	mib[0] = CTL_MACHDEP;
2293 	mib[1] = CPU_CHR2BLK;
2294 	mib[2] = st.st_rdev;
2295 	size = sizeof(devno);
2296 	if (sysctl(mib, 3, &devno, &size, NULL, 0) == -1) {
2297 		warn("sysctl(machdep.chr2blk)");
2298 		return;
2299 	}
2300 	devno = MAKEBOOTDEV(major(devno), 0, 0, DISKUNIT(devno), RAW_PART);
2301 
2302 	mib[0] = CTL_MACHDEP;
2303 	mib[1] = CPU_BIOS;
2304 	mib[2] = BIOS_DISKINFO;
2305 	mib[3] = devno;
2306 	size = sizeof(di);
2307 	if (sysctl(mib, 4, &di, &size, NULL, 0) == -1) {
2308 		warn("Can't get bios geometry");
2309 		return;
2310 	}
2311 	if ((bios_geop = calloc(1, sizeof(struct disklabel))) == NULL)
2312 		errx(4, "out of memory");
2313 
2314 	bios_geop->d_secsize = DEV_BSIZE;
2315 	bios_geop->d_nsectors = di.bios_sectors;
2316 	bios_geop->d_ntracks = di.bios_heads;
2317 	bios_geop->d_ncylinders = di.bios_cylinders;
2318 	bios_geop->d_secpercyl = di.bios_sectors * di.bios_heads;
2319 	bios_geop->d_secperunit = di.bios_cylinders *
2320 	    di.bios_heads * di.bios_sectors;
2321 	*bgpp = bios_geop;
2322 #endif
2323 }
2324 
2325 void
2326 set_geometry(struct disklabel *lp, struct disklabel *dgp,
2327     struct disklabel *bgp, struct disklabel *ugp, char *p)
2328 {
2329 	if (p == NULL) {
2330 		p = getstring("[d]isk, [b]ios, or [u]ser geometry",
2331 		    "Enter 'd' to use the geometry based on what the disk "
2332 		    "itself thinks it is, 'b' to use what the BIOS says,"
2333 		    "or 'u' to use the geometry that was found on in the label.",
2334 		    "d");
2335 	}
2336 	if (p == NULL) {
2337 		fputs("Command aborted\n", stderr);
2338 		return;
2339 	}
2340 	switch (*p) {
2341 	case 'b':
2342 	case 'B':
2343 		if (bgp == NULL)
2344 			fputs("BIOS geometry not defined.\n", stderr);
2345 		else {
2346 			lp->d_secsize = bgp->d_secsize;
2347 			lp->d_nsectors = bgp->d_nsectors;
2348 			lp->d_ntracks = bgp->d_ntracks;
2349 			lp->d_ncylinders = bgp->d_ncylinders;
2350 			lp->d_secpercyl = bgp->d_secpercyl;
2351 			lp->d_secperunit = bgp->d_secperunit;
2352 		}
2353 		break;
2354 	case 'd':
2355 	case 'D':
2356 		if (dgp == NULL)
2357 			fputs("BIOS geometry not defined.\n", stderr);
2358 		else {
2359 			lp->d_secsize = dgp->d_secsize;
2360 			lp->d_nsectors = dgp->d_nsectors;
2361 			lp->d_ntracks = dgp->d_ntracks;
2362 			lp->d_ncylinders = dgp->d_ncylinders;
2363 			lp->d_secpercyl = dgp->d_secpercyl;
2364 			lp->d_secperunit = dgp->d_secperunit;
2365 		}
2366 		break;
2367 	case 'u':
2368 	case 'U':
2369 		if (ugp == NULL)
2370 			fputs("BIOS geometry not defined.\n", stderr);
2371 		else {
2372 			lp->d_secsize = ugp->d_secsize;
2373 			lp->d_nsectors = ugp->d_nsectors;
2374 			lp->d_ntracks = ugp->d_ntracks;
2375 			lp->d_ncylinders = ugp->d_ncylinders;
2376 			lp->d_secpercyl = ugp->d_secpercyl;
2377 			lp->d_secperunit = ugp->d_secperunit;
2378 			if (dgp != NULL && ugp->d_secsize == dgp->d_secsize &&
2379 			    ugp->d_nsectors == dgp->d_nsectors &&
2380 			    ugp->d_ntracks == dgp->d_ntracks &&
2381 			    ugp->d_ncylinders == dgp->d_ncylinders &&
2382 			    ugp->d_secpercyl == dgp->d_secpercyl &&
2383 			    ugp->d_secperunit == dgp->d_secperunit)
2384 				fputs("Note: user geometry is the same as disk "
2385 				    "geometry.\n", stderr);
2386 		}
2387 		break;
2388 	default:
2389 		fputs("You must enter either 'd', 'b', or 'u'.\n", stderr);
2390 		break;
2391 	}
2392 }
2393 
2394 void
2395 zero_partitions(struct disklabel *lp, u_int32_t *freep)
2396 {
2397 	int i;
2398 
2399 	for (i = 0; i < MAXPARTITIONS; i++)
2400 		memset(&lp->d_partitions[i], 0, sizeof(struct partition));
2401 	lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
2402 	editor_countfree(lp, freep);
2403 }
2404