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