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