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