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