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