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