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