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