xref: /openbsd-src/sbin/disklabel/editor.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: editor.c,v 1.186 2009/04/04 16:04:44 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 #ifndef lint
20 static char rcsid[] = "$OpenBSD: editor.c,v 1.186 2009/04/04 16:04:44 krw Exp $";
21 #endif /* not lint */
22 
23 #include <sys/types.h>
24 #include <sys/param.h>
25 #include <sys/stat.h>
26 #include <sys/ioctl.h>
27 #define	DKTYPENAMES
28 #include <sys/disklabel.h>
29 
30 #include <ufs/ffs/fs.h>
31 
32 #include <ctype.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <libgen.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 
41 #include "extern.h"
42 #include "pathnames.h"
43 
44 /* flags for getuint() */
45 #define	DO_CONVERSIONS	0x00000001
46 #define	DO_ROUNDING	0x00000002
47 
48 #ifndef NUMBOOT
49 #define NUMBOOT 0
50 #endif
51 
52 /* structure to describe a portion of a disk */
53 struct diskchunk {
54 	u_int64_t start;
55 	u_int64_t stop;
56 };
57 
58 /* used when sorting mountpoints in mpsave() */
59 struct mountinfo {
60 	char *mountpoint;
61 	int partno;
62 };
63 
64 /* used when allocating all space according to recommendations */
65 
66 struct space_allocation {
67 	daddr64_t	minsz;	/* starts as blocks, xlated to sectors. */
68 	daddr64_t	maxsz;	/* starts as blocks, xlated to sectors. */
69 	int		rate;	/* % of extra space to use */
70 	char 	       *mp;
71 };
72 
73 #define MEG(x)	((x) * 1024LL * (1024 / 512))
74 
75 struct space_allocation alloc[MAXPARTITIONS] = {
76 	{   MEG(80),      MEG(1024),   5, "/"		},	/* a */
77 	{ MEG(1024),      MEG(4096),   5, "swap"	},	/* b */
78 	{         0,              0,   0, ""		},	/* c */
79 	{   MEG(80),      MEG(4096),  10, "/tmp"	},	/* d */
80 	{   MEG(80),      MEG(4096),  10, "/var"	},	/* e */
81 	{         0,              0,   0, ""		},	/* f */
82 	{ MEG(2048),     MEG(10240),  20, "/usr"	},	/* g */
83 	{  MEG(512),      MEG(1024),   5, "/usr/X11R6"	},	/* h */
84 	{         0,              0,   0, ""		},	/* i */
85 	{ MEG(2048),      MEG(3072),   5, "/usr/local"	},	/* j */
86 	{ MEG(1024),      MEG(2048),   5, "/usr/src"	},	/* k */
87 	{ MEG(2048),      MEG(2048),   0, "/usr/obj"	},	/* l */
88 	{  MEG(512), MEG(1024*1024),  35, "/home"	},	/* m */
89 	{         0,              0,   0, ""		},	/* n */
90 	{         0,              0,   0, ""		},	/* o */
91 	{         0,              0,   0, ""		}	/* p */
92 };
93 
94 void	edit_parms(struct disklabel *);
95 void	editor_allocspace(struct disklabel *, char **, int);
96 void	editor_add(struct disklabel *, char **, char *);
97 void	editor_change(struct disklabel *, char *);
98 u_int64_t editor_countfree(struct disklabel *);
99 void	editor_delete(struct disklabel *, char **, char *);
100 void	editor_help(char *);
101 void	editor_modify(struct disklabel *, char **, char *);
102 void	editor_name(struct disklabel *, char **, char *);
103 char	*getstring(char *, char *, char *);
104 u_int64_t getuint(struct disklabel *, char *, char *, u_int64_t, u_int64_t, u_int64_t, int);
105 int	has_overlap(struct disklabel *);
106 int	partition_cmp(const void *, const void *);
107 struct partition **sort_partitions(struct disklabel *);
108 void	getdisktype(struct disklabel *, char *, char *);
109 void	find_bounds(struct disklabel *);
110 void	set_bounds(struct disklabel *);
111 struct diskchunk *free_chunks(struct disklabel *);
112 void	mpcopy(char **, char **);
113 int	micmp(const void *, const void *);
114 int	mpequal(char **, char **);
115 int	mpsave(struct disklabel *, char **, char *, char *);
116 int	get_bsize(struct disklabel *, int);
117 int	get_fsize(struct disklabel *, int);
118 int	get_fstype(struct disklabel *, int);
119 int	get_mp(struct disklabel *, char **, int);
120 int	get_offset(struct disklabel *, int);
121 int	get_size(struct disklabel *, int);
122 void	get_geometry(int, struct disklabel **);
123 void	set_geometry(struct disklabel *, struct disklabel *, struct disklabel *,
124 	    char *);
125 void	zero_partitions(struct disklabel *);
126 u_int64_t max_partition_size(struct disklabel *, int);
127 void	display_edit(struct disklabel *, char **, char, u_int64_t);
128 
129 static u_int64_t starting_sector;
130 static u_int64_t ending_sector;
131 static int expert;
132 
133 /*
134  * Simple partition editor.
135  */
136 int
137 editor(struct disklabel *lp, int f, char *dev, char *fstabfile, int aflag)
138 {
139 	struct disklabel origlabel, lastlabel, tmplabel, label = *lp;
140 	struct disklabel *disk_geop;
141 	struct partition *pp;
142 	FILE *fp;
143 	char buf[BUFSIZ], *cmd, *arg;
144 	char **mountpoints = NULL, **omountpoints = NULL;
145 	char **origmountpoints = NULL, **tmpmountpoints = NULL;
146 
147 	/* Alloc and init mount point info */
148 	if (fstabfile) {
149 		if (!(mountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
150 		    !(omountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
151 		    !(origmountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
152 		    !(tmpmountpoints = calloc(MAXPARTITIONS, sizeof(char *))))
153 			errx(4, "out of memory");
154 	}
155 
156 	/* Don't allow disk type of "unknown" */
157 	getdisktype(&label, "You need to specify a type for this disk.", dev);
158 
159 	/* Get the on-disk geometries if possible */
160 	get_geometry(f, &disk_geop);
161 
162 	/* How big is the OpenBSD portion of the disk?  */
163 	find_bounds(&label);
164 
165 	/* Make sure there is no partition overlap. */
166 	if (has_overlap(&label))
167 		errx(1, "can't run when there is partition overlap.");
168 
169 	/* If we don't have a 'c' partition, create one. */
170 	pp = &label.d_partitions[RAW_PART];
171 	if (label.d_npartitions < 3 || DL_GETPSIZE(pp) == 0) {
172 		puts("No 'c' partition found, adding one that spans the disk.");
173 		if (label.d_npartitions < 3)
174 			label.d_npartitions = 3;
175 		DL_SETPOFFSET(pp, 0);
176 		DL_SETPSIZE(pp, DL_GETDSIZE(&label));
177 		pp->p_fstype = FS_UNUSED;
178 		pp->p_fragblock = pp->p_cpg = 0;
179 	}
180 
181 #ifdef SUN_CYLCHECK
182 	if (label.d_flags & D_VENDOR) {
183 		puts("This platform requires that partition offsets/sizes "
184 		    "be on cylinder boundaries.\n"
185 		    "Partition offsets/sizes will be rounded to the "
186 		    "nearest cylinder automatically.");
187 	}
188 #endif
189 
190 	/* Set d_bbsize and d_sbsize as necessary */
191 	if (label.d_bbsize == 0)
192 		label.d_bbsize = BBSIZE;
193 	if (label.d_sbsize == 0)
194 		label.d_sbsize = SBSIZE;
195 
196 	/* Interleave must be >= 1 */
197 	if (label.d_interleave == 0)
198 		label.d_interleave = 1;
199 
200 	/* Save the (U)ndo label, origmountpoints is already NULLs. */
201 	origlabel = label;
202 
203 	if (aflag)
204 		editor_allocspace(&label, mountpoints, 0);
205 
206 	puts("Initial label editor (enter '?' for help at any prompt)");
207 	lastlabel = label;
208 	for (;;) {
209 		fputs("> ", stdout);
210 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
211 			putchar('\n');
212 			buf[0] = 'q';
213 			buf[1] = '\0';
214 		}
215 		if ((cmd = strtok(buf, " \t\r\n")) == NULL)
216 			continue;
217 		arg = strtok(NULL, " \t\r\n");
218 
219 		if ((*cmd != 'u') && (*cmd != 'U')) {
220 			/*
221 			 * Save undo info in case the command tries to make
222 			 * changes but decides not to.
223 			 */
224 			tmplabel = lastlabel;
225 			lastlabel = label;
226 			if (mountpoints != NULL) {
227 				mpcopy(tmpmountpoints, omountpoints);
228 				mpcopy(omountpoints, mountpoints);
229 			}
230 		}
231 
232 		switch (*cmd) {
233 		case '?':
234 		case 'h':
235 			editor_help(arg ? arg : "");
236 			break;
237 
238 		case 'A':
239 			editor_allocspace(&label, mountpoints, 1);
240 			break;
241 		case 'a':
242 			editor_add(&label, mountpoints, arg);
243 			break;
244 
245 		case 'b':
246 			set_bounds(&label);
247 			break;
248 
249 		case 'c':
250 			editor_change(&label, arg);
251 			break;
252 
253 		case 'D':
254 			if (ioctl(f, DIOCGPDINFO, &label) == 0)
255 				dflag = 1;
256 			else
257 				warn("unable to get default partition table");
258 			break;
259 
260 		case 'd':
261 			editor_delete(&label, mountpoints, arg);
262 			break;
263 
264 		case 'e':
265 			edit_parms(&label);
266 			break;
267 
268 		case 'g':
269 			set_geometry(&label, disk_geop, lp, arg);
270 			break;
271 
272 		case 'm':
273 			editor_modify(&label, mountpoints, arg);
274 			break;
275 
276 		case 'n':
277 			if (mountpoints == NULL) {
278 				fputs("This option is not valid when run "
279 				    "without the -f flag.\n", stderr);
280 				break;
281 			}
282 			editor_name(&label, mountpoints, arg);
283 			break;
284 
285 		case 'p':
286 			display_edit(&label, mountpoints, arg ? *arg : 0,
287 			    editor_countfree(&label));
288 			break;
289 
290 		case 'l':
291 			display(stdout, &label, mountpoints, arg ? *arg : 0, 0);
292 			break;
293 
294 		case 'M': {
295 			sig_t opipe = signal(SIGPIPE, SIG_IGN);
296 			char *pager, *comm = NULL;
297 			extern const u_char manpage[];
298 			extern const int manpage_sz;
299 
300 			if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
301 				pager = _PATH_LESS;
302 
303 			if (asprintf(&comm, "gunzip -qc|%s", pager) != -1 &&
304 			    (fp = popen(comm, "w")) != NULL) {
305 				(void) fwrite(manpage, manpage_sz, 1, fp);
306 				pclose(fp);
307 			} else
308 				warn("unable to execute %s", pager);
309 
310 			free(comm);
311 			(void)signal(SIGPIPE, opipe);
312 			break;
313 		}
314 
315 		case 'q':
316 			if (donothing) {
317 				puts("In no change mode, not writing label.");
318 				return(1);
319 			}
320 			/* Save mountpoint info if there is any. */
321 			if (mountpoints != NULL)
322 				mpsave(&label, mountpoints, dev, fstabfile);
323 			/*
324 			 * If we didn't manufacture a new default label and
325 			 * didn't change the label read from disk, there is no
326 			 * need to do anything before exiting.
327 			 */
328 			if (!dflag && memcmp(lp, &label, sizeof(label)) == 0) {
329 				puts("No label changes.");
330 				return(1);
331 			}
332 			do {
333 				arg = getstring("Write new label?",
334 				    "Write the modified label to disk?",
335 				    "y");
336 			} while (arg && tolower(*arg) != 'y' && tolower(*arg) != 'n');
337 			if (arg && tolower(*arg) == 'y') {
338 				if (writelabel(f, bootarea, &label) == 0) {
339 					*lp = label;
340 					return(0);
341 				}
342 				warnx("unable to write label");
343 			}
344 			return(1);
345 			/* NOTREACHED */
346 			break;
347 
348 		case 'r': {
349 			struct diskchunk *chunks;
350 			int i;
351 			/* Display free space. */
352 			chunks = free_chunks(&label);
353 			for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0;
354 			    i++)
355 				fprintf(stderr, "Free sectors: %16llu - %16llu "
356 				    "(%16llu)\n",
357 			    	    chunks[i].start, chunks[i].stop - 1,
358 			   	    chunks[i].stop - chunks[i].start);
359 			fprintf(stderr, "Total free sectors: %llu.\n",
360 			    editor_countfree(&label));
361 		    	break;
362 		}
363 
364 		case 's':
365 			if (arg == NULL) {
366 				arg = getstring("Filename",
367 				    "Name of the file to save label into.",
368 				    NULL);
369 				if (arg == NULL || *arg == '\0')
370 					break;
371 			}
372 			if ((fp = fopen(arg, "w")) == NULL) {
373 				warn("cannot open %s", arg);
374 			} else {
375 				display(fp, &label, NULL, 0, 1);
376 				(void)fclose(fp);
377 			}
378 			break;
379 
380 		case 'U':
381 			if (memcmp(&label, &origlabel, sizeof(label)) == 0 &&
382 			    (mountpoints == NULL ||
383 			    mpequal(mountpoints, origmountpoints))) {
384 				puts("Nothing to undo!");
385 			} else {
386 				tmplabel = label;
387 				label = origlabel;
388 				lastlabel = tmplabel;
389 				/* Restore mountpoints */
390 				if (mountpoints != NULL) {
391 					mpcopy(tmpmountpoints, mountpoints);
392 					mpcopy(mountpoints, origmountpoints);
393 					mpcopy(omountpoints, tmpmountpoints);
394 				}
395 				puts("All changes undone.");
396 			}
397 			break;
398 
399 		case 'u':
400 			if (memcmp(&label, &lastlabel, sizeof(label)) == 0 &&
401 			    (mountpoints == NULL ||
402 			    mpequal(mountpoints, omountpoints))) {
403 				puts("Nothing to undo!");
404 			} else {
405 				tmplabel = label;
406 				label = lastlabel;
407 				lastlabel = tmplabel;
408 				/* Restore mountpoints */
409 				if (mountpoints != NULL) {
410 					mpcopy(tmpmountpoints, mountpoints);
411 					mpcopy(mountpoints, omountpoints);
412 					mpcopy(omountpoints, tmpmountpoints);
413 				}
414 				puts("Last change undone.");
415 			}
416 			break;
417 
418 		case 'w':
419 			if (donothing)  {
420 				puts("In no change mode, not writing label.");
421 				break;
422 			}
423 			/* Save mountpoint info if there is any. */
424 			if (mountpoints != NULL)
425 				mpsave(&label, mountpoints, dev, fstabfile);
426 			/* Write label to disk. */
427 			if (writelabel(f, bootarea, &label) != 0)
428 				warnx("unable to write label");
429 			else {
430 				dflag = 0;
431 				*lp = label;
432 			}
433 			break;
434 
435 		case 'X':
436 			expert = !expert;
437 			printf("%s expert mode\n", expert ? "Entering" :
438 			    "Exiting");
439 			break;
440 
441 		case 'x':
442 			return(1);
443 			break;
444 
445 		case 'z':
446 			zero_partitions(&label);
447 			if (mountpoints != NULL)
448 				mpcopy(mountpoints, origmountpoints);
449 			break;
450 
451 		case '\n':
452 			break;
453 
454 		default:
455 			printf("Unknown option: %c ('?' for help)\n", *cmd);
456 			break;
457 		}
458 
459 		/*
460 		 * If no changes were made to label or mountpoints, then
461 		 * restore undo info.
462 		 */
463 		if (memcmp(&label, &lastlabel, sizeof(label)) == 0 &&
464 		    (mountpoints == NULL ||
465 		    mpequal(mountpoints, omountpoints))) {
466 			lastlabel = tmplabel;
467 			if (mountpoints != NULL)
468 				mpcopy(omountpoints, tmpmountpoints);
469 		}
470 	}
471 }
472 
473 /*
474  * Allocate all disk space according to standard recommendations for a
475  * root disk.
476  */
477 void
478 editor_allocspace(struct disklabel *lp, char **mp, int forcealloc)
479 {
480 	struct partition *pp;
481 	daddr64_t secs, cylsecs, totsecs, xtrasecs;
482 	int i, lastpart;
483 
484 	cylsecs = lp->d_secpercyl;
485 	xtrasecs = totsecs = ending_sector - starting_sector;
486 
487 	pp = &lp->d_partitions[0];
488 	for (i = 0; i < MAXPARTITIONS; i++, pp++) {
489 		if (i == RAW_PART)
490 			continue;
491 		if (DL_GETPSIZE(pp) != 0 && !forcealloc)
492 			return;
493 		alloc[i].minsz = DL_BLKTOSEC(lp, alloc[i].minsz);
494 		alloc[i].maxsz = DL_BLKTOSEC(lp, alloc[i].maxsz);
495 		secs = alloc[i].minsz;
496 		if (secs == 0)
497 			continue;
498 		xtrasecs -= secs;
499 		lastpart = i;
500 	}
501 
502 	lp->d_npartitions = MAXPARTITIONS;
503 
504 	pp = &lp->d_partitions[0];
505 	for (i = 0; i <= lastpart; i++, pp++) {
506 		switch (i) {
507 		case 0:
508 			DL_SETPSIZE(pp, 0);
509 			DL_SETPOFFSET(pp, starting_sector);
510 			break;
511 		case RAW_PART:
512 			continue;
513 		case RAW_PART+1:
514 			DL_SETPSIZE(pp, 0);
515 			DL_SETPOFFSET(pp,DL_GETPOFFSET(pp-2)+DL_GETPSIZE(pp-2));
516 			break;
517 		default:
518 			DL_SETPSIZE(pp, 0);
519 			DL_SETPOFFSET(pp,DL_GETPOFFSET(pp-1)+DL_GETPSIZE(pp-1));
520 			break;
521 		}
522 		if (i == lastpart) {
523 			if (totsecs > alloc[i].maxsz)
524 				secs = alloc[i].maxsz;
525 			else
526 				secs = totsecs;
527 		} else {
528 			secs = alloc[i].minsz;
529 			if (xtrasecs > 0)
530 				secs += (xtrasecs / 100) * alloc[i].rate;
531 			if (secs > alloc[i].maxsz)
532 				secs = alloc[i].maxsz;
533 			secs = ((secs + cylsecs - 1) / cylsecs) * cylsecs;
534 			totsecs -= secs;
535 			while (totsecs < 0) {
536 				secs -= cylsecs;
537 				totsecs += cylsecs;
538 			}
539 			if (i == 0 && secs > starting_sector) {
540 				secs -= starting_sector;
541 				totsecs += starting_sector;
542 			}
543 		}
544 		if (secs < alloc[i].minsz) {
545 			totsecs += secs;
546 			fprintf(stderr, "no space to auto allocate '%c'"
547 			    " (%s)\n", 'a'+i, alloc[i].mp);
548 			if (i == 0)
549 				break;
550 			else
551 				continue;
552 		}
553 		DL_SETPSIZE(pp, secs);
554 		pp->p_fstype = (i == 1) ? FS_SWAP : FS_BSDFFS;
555 #if defined (__sparc__) && !defined(__sparc64__)
556 		/* can't boot from > 8k boot blocks */
557 		pp->p_fragblock =
558 		    DISKLABELV1_FFS_FRAGBLOCK(i == 0 ? 1024 : 2048, 8);
559 #else
560 		pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
561 #endif
562 		pp->p_cpg = 1;
563 		if (mp && alloc[i].mp[0] == '/') {
564 			free(mp[i]);
565 			if ((mp[i] = strdup(alloc[i].mp)) == NULL)
566 				errx(4, "out of memory");
567 		}
568 	}
569 
570 	/* Zap remaining, unallocated partitions. */
571 	for (; i < MAXPARTITIONS; i++, pp++)
572 		if (i != RAW_PART)
573 			memset(pp, 0, sizeof(*pp));
574 }
575 
576 /*
577  * Add a new partition.
578  */
579 void
580 editor_add(struct disklabel *lp, char **mp, char *p)
581 {
582 	struct partition *pp;
583 	struct diskchunk *chunks;
584 	char buf[2];
585 	int i, partno;
586 	u_int64_t freesectors, new_offset, new_size;
587 
588 	freesectors = editor_countfree(lp);
589 
590 	/* XXX - prompt user to steal space from another partition instead */
591 #ifdef SUN_CYLCHECK
592 	if ((lp->d_flags & D_VENDOR) && freesectors < lp->d_secpercyl) {
593 		fputs("No space left, you need to shrink a partition "
594 		    "(need at least one full cylinder)\n",
595 		    stderr);
596 		return;
597 	}
598 #endif
599 	if (freesectors == 0) {
600 		fputs("No space left, you need to shrink a partition\n",
601 		    stderr);
602 		return;
603 	}
604 
605 	if (p == NULL) {
606 		/*
607 		 * Use the first unused partition that is not 'c' as the
608 		 * default partition in the prompt string.
609 		 */
610 		pp = &lp->d_partitions[0];
611 		buf[0] = buf[1] = '\0';
612 		for (partno = 0; partno < MAXPARTITIONS; partno++, pp++) {
613 			if (DL_GETPSIZE(pp) == 0 && partno != RAW_PART) {
614 				buf[0] = partno + 'a';
615 				p = &buf[0];
616 				break;
617 			}
618 		}
619 		p = getstring("partition",
620 		    "The letter of the new partition, a - p.", p);
621 	}
622 	if (p == NULL) {
623 		fputs("Command aborted\n", stderr);
624 		return;
625 	}
626 	partno = p[0] - 'a';
627 	if (partno < 0 || partno == RAW_PART || partno >= MAXPARTITIONS) {
628 		fprintf(stderr, "Partition must be between 'a' and '%c' "
629 		    "(excluding 'c').\n", 'a' + MAXPARTITIONS - 1);
630 		return;
631 	}
632 	pp = &lp->d_partitions[partno];
633 
634 	if (pp->p_fstype != FS_UNUSED && DL_GETPSIZE(pp) != 0) {
635 		fprintf(stderr, "Partition '%c' exists.  Delete it first.\n",
636 		    p[0]);
637 		return;
638 	}
639 
640 	/*
641 	 * Increase d_npartitions if necessary. Ensure all new partitions are
642 	 * zero'ed to avoid inadvertant overlaps.
643 	 */
644 	for(; lp->d_npartitions <= partno; lp->d_npartitions++)
645 		memset(&lp->d_partitions[lp->d_npartitions], 0, sizeof(*pp));
646 
647 	/* Make sure selected partition is zero'd too. */
648 	memset(pp, 0, sizeof(*pp));
649 	chunks = free_chunks(lp);
650 
651 	/*
652 	 * Since we know there's free space, there must be at least one
653 	 * chunk. So find the largest chunk and assume we want to add the
654 	 * partition in that free space.
655 	 */
656 	new_size = new_offset = 0;
657 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
658 		if (chunks[i].stop - chunks[i].start > new_size) {
659 		    new_size = chunks[i].stop - chunks[i].start;
660 		    new_offset = chunks[i].start;
661 		}
662 	}
663 	DL_SETPSIZE(pp, new_size);
664 	DL_SETPOFFSET(pp, new_offset);
665 	pp->p_fstype = partno == 1 ? FS_SWAP : FS_BSDFFS;
666 #if defined (__sparc__) && !defined(__sparc64__)
667 	/* can't boot from > 8k boot blocks */
668 	pp->p_fragblock =
669 	    DISKLABELV1_FFS_FRAGBLOCK(partno == 0 ? 1024 : 2048, 8);
670 #else
671 	pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
672 #endif
673 	pp->p_cpg = 1;
674 
675 	if (get_offset(lp, partno) == 0 &&
676 	    get_size(lp, partno) == 0   &&
677 	    get_fstype(lp, partno) == 0 &&
678 	    get_mp(lp, mp, partno) == 0 &&
679 	    get_fsize(lp, partno) == 0  &&
680 	    get_bsize(lp, partno) == 0)
681 		return;
682 
683 	/* Bailed out at some point, so effectively delete the partition. */
684 	DL_SETPSIZE(pp, 0);
685 }
686 
687 /*
688  * Set the mountpoint of an existing partition ('name').
689  */
690 void
691 editor_name(struct disklabel *lp, char **mp, char *p)
692 {
693 	struct partition *pp;
694 	int partno;
695 
696 	/* Change which partition? */
697 	if (p == NULL) {
698 		p = getstring("partition to name",
699 		    "The letter of the partition to name, a - p.", NULL);
700 	}
701 	if (p == NULL) {
702 		fputs("Command aborted\n", stderr);
703 		return;
704 	}
705 	partno = p[0] - 'a';
706 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
707 		fprintf(stderr, "Partition must be between 'a' and '%c' "
708 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
709 		return;
710 	}
711 	pp = &lp->d_partitions[partno];
712 
713 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
714 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
715 		return;
716 	}
717 
718 	/* Not all fstypes can be named */
719 	if (pp->p_fstype == FS_UNUSED || pp->p_fstype == FS_SWAP ||
720 	    pp->p_fstype == FS_BOOT || pp->p_fstype == FS_OTHER ||
721 	    pp->p_fstype == FS_RAID) {
722 		fprintf(stderr, "You cannot name a filesystem of type %s.\n",
723 		    fstypenames[lp->d_partitions[partno].p_fstype]);
724 		return;
725 	}
726 
727 	get_mp(lp, mp, partno);
728 }
729 
730 /*
731  * Change an existing partition.
732  */
733 void
734 editor_modify(struct disklabel *lp, char **mp, char *p)
735 {
736 	struct partition origpart, *pp;
737 	int partno;
738 
739 	/* Change which partition? */
740 	if (p == NULL) {
741 		p = getstring("partition to modify",
742 		    "The letter of the partition to modify, a - p.", NULL);
743 	}
744 	if (p == NULL) {
745 		fputs("Command aborted\n", stderr);
746 		return;
747 	}
748 	partno = p[0] - 'a';
749 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
750 		fprintf(stderr, "Partition must be between 'a' and '%c' "
751 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
752 		return;
753 	}
754 	pp = &lp->d_partitions[partno];
755 
756 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
757 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
758 		return;
759 	}
760 
761 	origpart = *pp;
762 
763 	if (get_offset(lp, partno) == 0 &&
764 	    get_size(lp, partno) == 0   &&
765 	    get_fstype(lp, partno) == 0 &&
766 	    get_mp(lp, mp, partno) == 0 &&
767 	    get_fsize(lp, partno) == 0  &&
768 	    get_bsize(lp, partno) == 0)
769 		return;
770 
771 	/* Bailed out at some point, so undo any changes. */
772 	*pp = origpart;
773 }
774 
775 /*
776  * Delete an existing partition.
777  */
778 void
779 editor_delete(struct disklabel *lp, char **mp, char *p)
780 {
781 	struct partition *pp;
782 	int partno;
783 
784 	if (p == NULL) {
785 		p = getstring("partition to delete",
786 		    "The letter of the partition to delete, a - p, or '*'.",
787 		    NULL);
788 	}
789 	if (p == NULL) {
790 		fputs("Command aborted\n", stderr);
791 		return;
792 	}
793 	if (p[0] == '*') {
794 		zero_partitions(lp);
795 		return;
796 	}
797 	partno = p[0] - 'a';
798 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
799 		fprintf(stderr, "Partition must be between 'a' and '%c' "
800 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
801 		return;
802 	}
803 	pp = &lp->d_partitions[partno];
804 
805 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
806 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
807 		return;
808 	}
809 
810 	/* Really delete it (as opposed to just setting to "unused") */
811 	memset(pp, 0, sizeof(*pp));
812 
813 	if (mp != NULL && mp[partno] != NULL) {
814 		free(mp[partno]);
815 		mp[partno] = NULL;
816 	}
817 }
818 
819 /*
820  * Change the size of an existing partition.
821  */
822 void
823 editor_change(struct disklabel *lp, char *p)
824 {
825 	struct partition *pp;
826 	int partno;
827 
828 	if (p == NULL) {
829 		p = getstring("partition to change size",
830 		    "The letter of the partition to change size, a - p.", NULL);
831 	}
832 	if (p == NULL) {
833 		fputs("Command aborted\n", stderr);
834 		return;
835 	}
836 	partno = p[0] - 'a';
837 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
838 		fprintf(stderr, "Partition must be between 'a' and '%c' "
839 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
840 		return;
841 	}
842 	pp = &lp->d_partitions[partno];
843 
844 	if (DL_GETPSIZE(pp) == 0) {
845 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
846 		return;
847 	}
848 
849 	printf("Partition %c is currently %llu sectors in size, and can have "
850 	    "a maximum\nsize of %llu sectors.\n",
851 	    p[0], DL_GETPSIZE(pp), max_partition_size(lp, partno));
852 
853 	/* Get new size */
854 	get_size(lp, partno);
855 }
856 
857 /*
858  * Sort the partitions based on starting offset.
859  * This assumes there can be no overlap.
860  */
861 int
862 partition_cmp(const void *e1, const void *e2)
863 {
864 	struct partition *p1 = *(struct partition **)e1;
865 	struct partition *p2 = *(struct partition **)e2;
866 	u_int64_t o1 = DL_GETPOFFSET(p1);
867 	u_int64_t o2 = DL_GETPOFFSET(p2);
868 
869 	if (o1 < o2)
870 		return -1;
871 	else if (o1 > o2)
872 		return 1;
873 	else
874 		return 0;
875 }
876 
877 char *
878 getstring(char *prompt, char *helpstring, char *oval)
879 {
880 	static char buf[BUFSIZ];
881 	int n;
882 
883 	buf[0] = '\0';
884 	do {
885 		printf("%s: [%s] ", prompt, oval ? oval : "");
886 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
887 			buf[0] = '\0';
888 			if (feof(stdin)) {
889 				clearerr(stdin);
890 				putchar('\n');
891 				return(NULL);
892 			}
893 		}
894 		n = strlen(buf);
895 		if (n > 0 && buf[n-1] == '\n')
896 			buf[--n] = '\0';
897 		if (buf[0] == '?')
898 			puts(helpstring);
899 		else if (oval != NULL && buf[0] == '\0')
900 			strlcpy(buf, oval, sizeof(buf));
901 	} while (buf[0] == '?');
902 
903 	return(&buf[0]);
904 }
905 
906 /*
907  * Returns ULLONG_MAX on error
908  * Usually only called by helper functions.
909  */
910 u_int64_t
911 getuint(struct disklabel *lp, char *prompt, char *helpstring,
912     u_int64_t oval, u_int64_t maxval, u_int64_t offset, int flags)
913 {
914 	char buf[BUFSIZ], *endptr, *p, operator = '\0';
915 	u_int64_t rval = oval;
916 	size_t n;
917 	int mult = 1;
918 	double d, percent = 1.0;
919 
920 	/* We only care about the remainder */
921 	offset = offset % lp->d_secpercyl;
922 
923 	buf[0] = '\0';
924 	do {
925 		printf("%s: [%llu] ", prompt, oval);
926 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
927 			buf[0] = '\0';
928 			if (feof(stdin)) {
929 				clearerr(stdin);
930 				putchar('\n');
931 				return(ULLONG_MAX - 1);
932 			}
933 		}
934 		n = strlen(buf);
935 		if (n > 0 && buf[n-1] == '\n')
936 			buf[--n] = '\0';
937 		if (buf[0] == '?')
938 			puts(helpstring);
939 	} while (buf[0] == '?');
940 
941 	if (buf[0] == '*' && buf[1] == '\0') {
942 		rval = maxval;
943 	} else {
944 		/* deal with units */
945 		if (buf[0] != '\0' && n > 0) {
946 			if ((flags & DO_CONVERSIONS)) {
947 				switch (tolower(buf[n-1])) {
948 
949 				case 'c':
950 					mult = lp->d_secpercyl;
951 					buf[--n] = '\0';
952 					break;
953 				case 'b':
954 					mult = -lp->d_secsize;
955 					buf[--n] = '\0';
956 					break;
957 				case 'k':
958 					if (lp->d_secsize > 1024)
959 						mult = -lp->d_secsize / 1024;
960 					else
961 						mult = 1024 / lp->d_secsize;
962 					buf[--n] = '\0';
963 					break;
964 				case 'm':
965 					mult = 1048576 / lp->d_secsize;
966 					buf[--n] = '\0';
967 					break;
968 				case 'g':
969 					mult = 1073741824 / lp->d_secsize;
970 					buf[--n] = '\0';
971 					break;
972 				case '%':
973 					buf[--n] = '\0';
974 					percent = strtod(buf, NULL) / 100.0;
975 					snprintf(buf, sizeof(buf), "%lld",
976 					    DL_GETDSIZE(lp));
977 					break;
978 				case '&':
979 					buf[--n] = '\0';
980 					percent = strtod(buf, NULL) / 100.0;
981 					snprintf(buf, sizeof(buf), "%lld",
982 					    maxval);
983 					break;
984 				}
985 			}
986 
987 			/* Did they give us an operator? */
988 			p = &buf[0];
989 			if (*p == '+' || *p == '-')
990 				operator = *p++;
991 
992 			endptr = p;
993 			errno = 0;
994 			d = strtod(p, &endptr);
995 			if (errno == ERANGE)
996 				rval = ULLONG_MAX;	/* too big/small */
997 			else if (*endptr != '\0') {
998 				errno = EINVAL;		/* non-numbers in str */
999 				rval = ULLONG_MAX;
1000 			} else {
1001 				/* XXX - should check for overflow */
1002 				if (mult > 0)
1003 					rval = d * mult * percent;
1004 				else
1005 					/* Negative mult means divide (fancy) */
1006 					rval = d / (-mult) * percent;
1007 
1008 				/* Apply the operator */
1009 				if (operator == '+')
1010 					rval += oval;
1011 				else if (operator == '-')
1012 					rval = oval - rval;
1013 			}
1014 		}
1015 	}
1016 	if ((flags & DO_ROUNDING) && rval != ULLONG_MAX) {
1017 		/* Round to nearest cylinder unless given in sectors */
1018 		if (
1019 #ifdef SUN_CYLCHECK
1020 		    ((lp->d_flags & D_VENDOR) || mult != 1) &&
1021 #else
1022 		    mult != 1 &&
1023 #endif
1024 		    (rval + offset) % lp->d_secpercyl != 0) {
1025 			u_int64_t cyls;
1026 
1027 			/* Round to higher cylinder but no more than maxval */
1028 			cyls = (rval / lp->d_secpercyl) + 1;
1029 			if ((cyls * lp->d_secpercyl) - offset > maxval)
1030 				cyls--;
1031 			rval = (cyls * lp->d_secpercyl) - offset;
1032 			printf("Rounding to cylinder: %llu\n", rval);
1033 		}
1034 	}
1035 
1036 	return(rval);
1037 }
1038 
1039 /*
1040  * Check for partition overlap in lp and prompt the user to resolve the overlap
1041  * if any is found.  Returns 1 if unable to resolve, else 0.
1042  */
1043 int
1044 has_overlap(struct disklabel *lp)
1045 {
1046 	struct partition **spp;
1047 	int c, i, j;
1048 	char buf[BUFSIZ];
1049 
1050 	/* Get a sorted list of the in-use partitions. */
1051 	spp = sort_partitions(lp);
1052 
1053 	/* If there are less than two partitions in use, there is no overlap. */
1054 	if (spp[1] == NULL)
1055 		return(0);
1056 
1057 	/* Now that we have things sorted by starting sector check overlap */
1058 	for (i = 0; spp[i] != NULL; i++) {
1059 		for (j = i + 1; spp[j] != NULL; j++) {
1060 			/* `if last_sec_in_part + 1 > first_sec_in_next_part' */
1061 			if (DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]) > DL_GETPOFFSET(spp[j])) {
1062 				/* Overlap!  Convert to real part numbers. */
1063 				i = ((char *)spp[i] - (char *)lp->d_partitions)
1064 				    / sizeof(**spp);
1065 				j = ((char *)spp[j] - (char *)lp->d_partitions)
1066 				    / sizeof(**spp);
1067 				printf("\nError, partitions %c and %c overlap:\n",
1068 				    'a' + i, 'a' + j);
1069 				printf("#    %16.16s %16.16s  fstype "
1070 				    "[fsize bsize  cpg]\n", "size", "offset");
1071 				display_partition(stdout, lp, NULL, i, 0);
1072 				display_partition(stdout, lp, NULL, j, 0);
1073 
1074 				/* Get partition to disable or ^D */
1075 				do {
1076 					printf("Disable which one? (^D to abort) [%c %c] ",
1077 					    'a' + i, 'a' + j);
1078 					buf[0] = '\0';
1079 					if (!fgets(buf, sizeof(buf), stdin)) {
1080 						putchar('\n');
1081 						return(1);	/* ^D */
1082 					}
1083 					c = buf[0] - 'a';
1084 				} while (buf[1] != '\n' && buf[1] != '\0' &&
1085 				    c != i && c != j);
1086 
1087 				/* Mark the selected one as unused */
1088 				lp->d_partitions[c].p_fstype = FS_UNUSED;
1089 				return (has_overlap(lp));
1090 			}
1091 		}
1092 	}
1093 
1094 	return(0);
1095 }
1096 
1097 void
1098 edit_parms(struct disklabel *lp)
1099 {
1100 	char *p;
1101 	u_int64_t freesectors, ui;
1102 	struct disklabel oldlabel = *lp;
1103 
1104 	printf("Changing device parameters for %s:\n", specname);
1105 
1106 	/* disk type */
1107 	for (;;) {
1108 		p = getstring("disk type",
1109 		    "What kind of disk is this?  Usually SCSI, ESDI, ST506, or "
1110 		    "floppy (use ESDI for IDE).", dktypenames[lp->d_type]);
1111 		if (p == NULL) {
1112 			fputs("Command aborted\n", stderr);
1113 			return;
1114 		}
1115 		if (strcasecmp(p, "IDE") == 0)
1116 			ui = DTYPE_ESDI;
1117 		else
1118 			for (ui = 1; ui < DKMAXTYPES &&
1119 			    strcasecmp(p, dktypenames[ui]); ui++)
1120 				;
1121 		if (ui < DKMAXTYPES) {
1122 			break;
1123 		} else {
1124 			printf("\"%s\" is not a valid disk type.\n", p);
1125 			fputs("Valid types are: ", stdout);
1126 			for (ui = 1; ui < DKMAXTYPES; ui++) {
1127 				printf("\"%s\"", dktypenames[ui]);
1128 				if (ui < DKMAXTYPES - 1)
1129 					fputs(", ", stdout);
1130 			}
1131 			putchar('\n');
1132 		}
1133 	}
1134 	lp->d_type = ui;
1135 
1136 	/* pack/label id */
1137 	p = getstring("label name",
1138 	    "15 char string that describes this label, usually the disk name.",
1139 	    lp->d_packname);
1140 	if (p == NULL) {
1141 		fputs("Command aborted\n", stderr);
1142 		*lp = oldlabel;		/* undo damage */
1143 		return;
1144 	}
1145 	strncpy(lp->d_packname, p, sizeof(lp->d_packname));	/* checked */
1146 
1147 	/* sectors/track */
1148 	for (;;) {
1149 		ui = getuint(lp, "sectors/track",
1150 		    "The Number of sectors per track.", lp->d_nsectors,
1151 		    lp->d_nsectors, 0, 0);
1152 		if (ui == ULLONG_MAX - 1) {
1153 			fputs("Command aborted\n", stderr);
1154 			*lp = oldlabel;		/* undo damage */
1155 			return;
1156 		} if (ui == ULLONG_MAX)
1157 			fputs("Invalid entry\n", stderr);
1158 		else
1159 			break;
1160 	}
1161 	lp->d_nsectors = ui;
1162 
1163 	/* tracks/cylinder */
1164 	for (;;) {
1165 		ui = getuint(lp, "tracks/cylinder",
1166 		    "The number of tracks per cylinder.", lp->d_ntracks,
1167 		    lp->d_ntracks, 0, 0);
1168 		if (ui == ULLONG_MAX - 1) {
1169 			fputs("Command aborted\n", stderr);
1170 			*lp = oldlabel;		/* undo damage */
1171 			return;
1172 		} else if (ui == ULLONG_MAX)
1173 			fputs("Invalid entry\n", stderr);
1174 		else
1175 			break;
1176 	}
1177 	lp->d_ntracks = ui;
1178 
1179 	/* sectors/cylinder */
1180 	for (;;) {
1181 		ui = getuint(lp, "sectors/cylinder",
1182 		    "The number of sectors per cylinder (Usually sectors/track "
1183 		    "* tracks/cylinder).", lp->d_secpercyl, lp->d_secpercyl,
1184 		    0, 0);
1185 		if (ui == ULLONG_MAX - 1) {
1186 			fputs("Command aborted\n", stderr);
1187 			*lp = oldlabel;		/* undo damage */
1188 			return;
1189 		} else if (ui == ULLONG_MAX)
1190 			fputs("Invalid entry\n", stderr);
1191 		else
1192 			break;
1193 	}
1194 	lp->d_secpercyl = ui;
1195 
1196 	/* number of cylinders */
1197 	for (;;) {
1198 		ui = getuint(lp, "number of cylinders",
1199 		    "The total number of cylinders on the disk.",
1200 		    lp->d_ncylinders, lp->d_ncylinders, 0, 0);
1201 		if (ui == ULLONG_MAX - 1) {
1202 			fputs("Command aborted\n", stderr);
1203 			*lp = oldlabel;		/* undo damage */
1204 			return;
1205 		} else if (ui == ULLONG_MAX)
1206 			fputs("Invalid entry\n", stderr);
1207 		else
1208 			break;
1209 	}
1210 	lp->d_ncylinders = ui;
1211 
1212 	/* total sectors */
1213 	for (;;) {
1214 		u_int64_t nsec = MAX(DL_GETDSIZE(lp),
1215 		    (u_int64_t)lp->d_ncylinders * lp->d_secpercyl);
1216 		ui = getuint(lp, "total sectors",
1217 		    "The total number of sectors on the disk.",
1218 		    nsec, nsec, 0, 0);
1219 		if (ui == ULLONG_MAX - 1) {
1220 			fputs("Command aborted\n", stderr);
1221 			*lp = oldlabel;		/* undo damage */
1222 			return;
1223 		} else if (ui == ULLONG_MAX)
1224 			fputs("Invalid entry\n", stderr);
1225 		else if (ui > DL_GETDSIZE(lp) &&
1226 		    ending_sector == DL_GETDSIZE(lp)) {
1227 			puts("You may want to increase the size of the 'c' "
1228 			    "partition.");
1229 			break;
1230 		} else if (ui < DL_GETDSIZE(lp) &&
1231 		    ending_sector == DL_GETDSIZE(lp)) {
1232 			/* shrink free count */
1233 			freesectors = editor_countfree(lp);
1234 			if (DL_GETDSIZE(lp) - ui > freesectors)
1235 				fprintf(stderr,
1236 				    "Not enough free space to shrink by %llu "
1237 				    "sectors (only %llu sectors left)\n",
1238 				    DL_GETDSIZE(lp) - ui, freesectors);
1239 			else
1240 				break;
1241 		} else
1242 			break;
1243 	}
1244 	/* Adjust ending_sector if necessary. */
1245 	if (ending_sector > ui)
1246 		ending_sector = ui;
1247 	DL_SETDSIZE(lp, ui);
1248 
1249 	/* rpm */
1250 	for (;;) {
1251 		ui = getuint(lp, "rpm",
1252 		  "The rotational speed of the disk in revolutions per minute.",
1253 		  lp->d_rpm, lp->d_rpm, 0, 0);
1254 		if (ui == ULLONG_MAX - 1) {
1255 			fputs("Command aborted\n", stderr);
1256 			*lp = oldlabel;		/* undo damage */
1257 			return;
1258 		} else if (ui == ULLONG_MAX)
1259 			fputs("Invalid entry\n", stderr);
1260 		else
1261 			break;
1262 	}
1263 	lp->d_rpm = ui;
1264 
1265 	/* interleave */
1266 	for (;;) {
1267 		ui = getuint(lp, "interleave",
1268 		  "The physical sector interleave, set when formatting.  Almost always 1.",
1269 		  lp->d_interleave, lp->d_interleave, 0, 0);
1270 		if (ui == ULLONG_MAX - 1) {
1271 			fputs("Command aborted\n", stderr);
1272 			*lp = oldlabel;		/* undo damage */
1273 			return;
1274 		} else if (ui == ULLONG_MAX || ui == 0)
1275 			fputs("Invalid entry\n", stderr);
1276 		else
1277 			break;
1278 	}
1279 	lp->d_interleave = ui;
1280 }
1281 
1282 struct partition **
1283 sort_partitions(struct disklabel *lp)
1284 {
1285 	static struct partition *spp[MAXPARTITIONS+2];
1286 	int i, npartitions;
1287 
1288 	memset(spp, 0, sizeof(spp));
1289 
1290 	for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) {
1291 		if (lp->d_partitions[i].p_fstype != FS_UNUSED &&
1292 		    lp->d_partitions[i].p_fstype != FS_BOOT &&
1293 		    DL_GETPSIZE(&lp->d_partitions[i]) != 0)
1294 			spp[npartitions++] = &lp->d_partitions[i];
1295 	}
1296 
1297 	/*
1298 	 * Sort the partitions based on starting offset.
1299 	 * This is safe because we guarantee no overlap.
1300 	 */
1301 	if (npartitions > 1)
1302 		if (heapsort((void *)spp, npartitions, sizeof(spp[0]),
1303 		    partition_cmp))
1304 			err(4, "failed to sort partition table");
1305 
1306 	return(spp);
1307 }
1308 
1309 /*
1310  * Get a valid disk type if necessary.
1311  */
1312 void
1313 getdisktype(struct disklabel *lp, char *banner, char *dev)
1314 {
1315 	int i;
1316 	char *s, *def = "SCSI";
1317 	struct dtypes {
1318 		char *dev;
1319 		char *type;
1320 	} dtypes[] = {
1321 		{ "sd",   "SCSI" },
1322 		{ "rz",   "SCSI" },
1323 		{ "wd",   "IDE" },
1324 		{ "fd",   "FLOPPY" },
1325 		{ "xd",   "SMD" },
1326 		{ "xy",   "SMD" },
1327 		{ "hd",   "HP-IB" },
1328 		{ "ccd",  "CCD" },
1329 		{ "vnd",  "VND" },
1330 		{ "svnd", "VND" },
1331 		{ NULL,   NULL }
1332 	};
1333 
1334 	if ((s = basename(dev)) != NULL) {
1335 		if (*s == 'r')
1336 			s++;
1337 		i = strcspn(s, "0123456789");
1338 		s[i] = '\0';
1339 		dev = s;
1340 		for (i = 0; dtypes[i].dev != NULL; i++) {
1341 			if (strcmp(dev, dtypes[i].dev) == 0) {
1342 				def = dtypes[i].type;
1343 				break;
1344 			}
1345 		}
1346 	}
1347 
1348 	if (lp->d_type > DKMAXTYPES || lp->d_type == 0) {
1349 		puts(banner);
1350 		puts("Possible values are:");
1351 		printf("\"IDE\", ");
1352 		for (i = 1; i < DKMAXTYPES; i++) {
1353 			printf("\"%s\"", dktypenames[i]);
1354 			if (i < DKMAXTYPES - 1)
1355 				fputs(", ", stdout);
1356 		}
1357 		putchar('\n');
1358 
1359 		for (;;) {
1360 			s = getstring("Disk type",
1361 			    "What kind of disk is this?  Usually SCSI, IDE, "
1362 			    "ESDI, CCD, ST506, or floppy.", def);
1363 			if (s == NULL)
1364 				continue;
1365 			if (strcasecmp(s, "IDE") == 0) {
1366 				lp->d_type = DTYPE_ESDI;
1367 				return;
1368 			}
1369 			for (i = 1; i < DKMAXTYPES; i++)
1370 				if (strcasecmp(s, dktypenames[i]) == 0) {
1371 					lp->d_type = i;
1372 					return;
1373 				}
1374 			printf("\"%s\" is not a valid disk type.\n", s);
1375 			fputs("Valid types are: ", stdout);
1376 			for (i = 1; i < DKMAXTYPES; i++) {
1377 				printf("\"%s\"", dktypenames[i]);
1378 				if (i < DKMAXTYPES - 1)
1379 					fputs(", ", stdout);
1380 			}
1381 			putchar('\n');
1382 		}
1383 	}
1384 }
1385 
1386 /*
1387  * Get beginning and ending sectors of the OpenBSD portion of the disk
1388  * from the user.
1389  * XXX - should mention MBR values if DOSLABEL
1390  */
1391 void
1392 set_bounds(struct disklabel *lp)
1393 {
1394 	u_int64_t ui, start_temp;
1395 
1396 	/* Starting sector */
1397 	do {
1398 		ui = getuint(lp, "Starting sector",
1399 		  "The start of the OpenBSD portion of the disk.",
1400 		  starting_sector, DL_GETDSIZE(lp), 0, 0);
1401 		if (ui == ULLONG_MAX - 1) {
1402 			fputs("Command aborted\n", stderr);
1403 			return;
1404 		}
1405 	} while (ui >= DL_GETDSIZE(lp));
1406 	start_temp = ui;
1407 
1408 	/* Size */
1409 	do {
1410 		ui = getuint(lp, "Size ('*' for entire disk)",
1411 		  "The size of the OpenBSD portion of the disk ('*' for the "
1412 		  "entire disk).", ending_sector - starting_sector,
1413 		  DL_GETDSIZE(lp) - start_temp, 0, 0);
1414 		if (ui == ULLONG_MAX - 1) {
1415 			fputs("Command aborted\n", stderr);
1416 			return;
1417 		}
1418 	} while (ui > DL_GETDSIZE(lp) - start_temp);
1419 	ending_sector = start_temp + ui;
1420 	starting_sector = start_temp;
1421 }
1422 
1423 /*
1424  * Return a list of the "chunks" of free space available
1425  */
1426 struct diskchunk *
1427 free_chunks(struct disklabel *lp)
1428 {
1429 	struct partition **spp;
1430 	static struct diskchunk chunks[MAXPARTITIONS + 2];
1431 	u_int64_t start, stop;
1432 	int i, numchunks;
1433 
1434 	/* Sort the in-use partitions based on offset */
1435 	spp = sort_partitions(lp);
1436 
1437 	/* If there are no partitions, it's all free. */
1438 	if (spp[0] == NULL) {
1439 		chunks[0].start = starting_sector;
1440 		chunks[0].stop = ending_sector;
1441 		chunks[1].start = chunks[1].stop = 0;
1442 		return(chunks);
1443 	}
1444 
1445 	/* Find chunks of free space */
1446 	numchunks = 0;
1447 	if (DL_GETPOFFSET(spp[0]) > starting_sector) {
1448 		chunks[0].start = starting_sector;
1449 		chunks[0].stop = DL_GETPOFFSET(spp[0]);
1450 		numchunks++;
1451 	}
1452 	for (i = 0; spp[i] != NULL; i++) {
1453 		start = DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]);
1454 		if (start < starting_sector)
1455 			start = starting_sector;
1456 		else if (start > ending_sector)
1457 			start = ending_sector;
1458 		if (spp[i + 1] != NULL)
1459 			stop = DL_GETPOFFSET(spp[i+1]);
1460 		else
1461 			stop = ending_sector;
1462 		if (stop < starting_sector)
1463 			stop = starting_sector;
1464 		else if (stop > ending_sector)
1465 			stop = ending_sector;
1466 		if (start < stop) {
1467 			chunks[numchunks].start = start;
1468 			chunks[numchunks].stop = stop;
1469 			numchunks++;
1470 		}
1471 	}
1472 
1473 	/* Terminate and return */
1474 	chunks[numchunks].start = chunks[numchunks].stop = 0;
1475 	return(chunks);
1476 }
1477 
1478 /*
1479  * What is the OpenBSD portion of the disk?  Uses the MBR if applicable.
1480  */
1481 void
1482 find_bounds(struct disklabel *lp)
1483 {
1484 	int has_bounds = 0;
1485 
1486 	/* Defaults */
1487 	/* XXX - reserve a cylinder for hp300? */
1488 	starting_sector = 0;
1489 	ending_sector = DL_GETDSIZE(lp);
1490 
1491 #ifdef DOSLABEL
1492 	/*
1493 	 * If we have an MBR, use values from the OpenBSD partition.
1494 	 */
1495 	if (dosdp) {
1496 		if (dosdp->dp_typ == DOSPTYP_OPENBSD) {
1497 			struct partition *pp;
1498 			u_int64_t new_end;
1499 			int i;
1500 
1501 			/* Set start and end based on fdisk partition bounds */
1502 			starting_sector = letoh32(dosdp->dp_start);
1503 			ending_sector = starting_sector + letoh32(dosdp->dp_size);
1504 
1505 			/*
1506 			 * If there are any BSD or SWAP partitions beyond
1507 			 * ending_sector we extend ending_sector to include
1508 			 * them.  This is done because the BIOS geometry is
1509 			 * generally different from the disk geometry.
1510 			 */
1511 			for (i = new_end = 0; i < lp->d_npartitions; i++) {
1512 				pp = &lp->d_partitions[i];
1513 				if ((pp->p_fstype == FS_BSDFFS ||
1514 				    pp->p_fstype == FS_SWAP) &&
1515 				    DL_GETPSIZE(pp) + DL_GETPOFFSET(pp) >
1516 					new_end)
1517 					new_end = DL_GETPSIZE(pp) +
1518 					    DL_GETPOFFSET(pp);
1519 			}
1520 			if (new_end > ending_sector)
1521 				ending_sector = new_end;
1522 		} else {
1523 			/* Don't trounce the MBR */
1524 			starting_sector = 63;
1525 		}
1526 
1527 		has_bounds = 1;
1528 	}
1529 #endif
1530 #ifdef DPMELABEL
1531 	if (dpme_label) {
1532 		starting_sector = dpme_obsd_start;
1533 		ending_sector = dpme_obsd_start + dpme_obsd_size;
1534 		has_bounds = 1;
1535 	}
1536 #endif
1537 
1538 	if (has_bounds) {
1539 		printf("Treating sectors %llu-%llu as the OpenBSD portion of the "
1540 		    "disk.\nYou can use the 'b' command to change this.\n\n",
1541 		    starting_sector, ending_sector);
1542 	} else {
1543 #if (NUMBOOT == 1)
1544 		/* Boot blocks take up the first cylinder */
1545 		starting_sector = lp->d_secpercyl;
1546 		printf("Reserving the first data cylinder for boot blocks.\n"
1547 		    "You can use the 'b' command to change this.\n\n");
1548 #endif
1549 	}
1550 }
1551 
1552 /*
1553  * Calculate free space.
1554  */
1555 u_int64_t
1556 editor_countfree(struct disklabel *lp)
1557 {
1558 	struct diskchunk *chunks;
1559 	u_int64_t freesectors = 0;
1560 	int i;
1561 
1562 	chunks = free_chunks(lp);
1563 
1564 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++)
1565 		freesectors += chunks[i].stop - chunks[i].start;
1566 
1567 	return (freesectors);
1568 }
1569 
1570 void
1571 editor_help(char *arg)
1572 {
1573 
1574 	/* XXX - put these strings in a table instead? */
1575 	switch (*arg) {
1576 	case 'p':
1577 		puts(
1578 "The 'p' command prints the current partitions.  By default, it prints size\n"
1579 "and offset in sectors (a sector is usually 512 bytes).  The 'p' command\n"
1580 "takes an optional units argument.  Possible values are 'b' for bytes, 'c'\n"
1581 "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n");
1582 		break;
1583 	case 'l':
1584 	puts(
1585 "The 'l' command prints the header of the disk label.  By default, it prints\n"
1586 "size and offset in sectors (a sector is usually 512 bytes).  The 'p' command\n"
1587 "takes an optional units argument.  Possible values are 'b' for bytes, 'c'\n"
1588 "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n");
1589 		break;
1590 	case 'M':
1591 		puts(
1592 "The 'M' command pipes the entire OpenBSD manual page for disk label through\n"
1593 "the pager specified by the PAGER environment variable or 'less' if PAGER is\n"
1594 "not set.  It is especially useful during install when the normal system\n"
1595 "manual is not available.\n");
1596 		break;
1597 	case 'e':
1598 		puts(
1599 "The 'e' command is used to edit the disk drive parameters.  These include\n"
1600 "the number of sectors/track, tracks/cylinder, sectors/cylinder, number of\n"
1601 "cylinders on the disk , total sectors on the disk, rpm, interleave, disk\n"
1602 "type, and a descriptive label string.  You should not change these unless\n"
1603 "you know what you are doing\n");
1604 		break;
1605 	case 'a':
1606 		puts(
1607 "The 'a' command adds new partitions to the disk.  It takes as an optional\n"
1608 "argument the partition letter to add.  If you do not specify a partition\n"
1609 "letter, you will be prompted for it; the next available letter will be the\n"
1610 "default answer\n");
1611 		break;
1612 	case 'b':
1613 		puts(
1614 "The 'b' command is used to change the boundaries of the OpenBSD portion of\n"
1615 "the disk.  This is only useful on disks with an fdisk partition.  By default,\n"
1616 "on a disk with an fdisk partition, the boundaries are set to be the first\n"
1617 "and last sectors of the OpenBSD fdisk partition.  You should only change\n"
1618 "these if your fdisk partition table is incorrect or you have a disk larger\n"
1619 "than 8gig, since 8gig is the maximum size an fdisk partition can be.  You\n"
1620 "may enter '*' at the 'Size' prompt to indicate the entire size of the disk\n"
1621 "(minus the starting sector).  Use this option with care; if you extend the\n"
1622 "boundaries such that they overlap with another operating system you will\n"
1623 "corrupt the other operating system's data.\n");
1624 		break;
1625 	case 'c':
1626 		puts(
1627 "The 'c' command is used to change the size of an existing partition.  It\n"
1628 "takes as an optional argument the partition letter to change.  If you do not\n"
1629 "specify a partition letter, you will be prompted for one.  You may add a '+'\n"
1630 "or '-' prefix to the new size to increase or decrease the existing value\n"
1631 "instead of entering an absolute value.  You may also use a suffix to indicate\n"
1632 "the units the values is in terms of.  Possible suffixes are 'b' for bytes,\n"
1633 "'c' for cylinders, 'k' for kilobytes, 'm' for megabytes, 'g' for gigabytes or\n"
1634 "no suffix for sectors (usually 512 bytes).  You may also enter '*' to change\n"
1635 "the size to be the total number of free sectors remaining.\n");
1636 		break;
1637 	case 'D':
1638 		puts(
1639 "The 'D' command will set the disk label to the default values as reported\n"
1640 "by the disk itself.  This similates the case where there is no disk label.\n");
1641 		break;
1642 	case 'd':
1643 		puts(
1644 "The 'd' command is used to delete an existing partition.  It takes as an\n"
1645 "optional argument the partition letter to change.  If you do not specify a\n"
1646 "partition letter, you will be prompted for one.  You may not delete the ``c''\n"
1647 "partition as 'c' must always exist and by default is marked as 'unused' (so\n"
1648 "it does not take up any space).\n");
1649 		break;
1650 	case 'g':
1651 		puts(
1652 "The 'g' command is used select which disk geometry to use, the disk or a\n"
1653 "user geometry.  It takes as an optional argument ``d'' or ``u''.  If \n"
1654 "you do not specify the type as an argument, you will be prompted for it.\n");
1655 		break;
1656 	case 'm':
1657 		puts(
1658 "The 'm' command is used to modify an existing partition.  It takes as an\n"
1659 "optional argument the partition letter to change.  If you do not specify a\n"
1660 "partition letter, you will be prompted for one.  This option allows the user\n"
1661 "to change the filesystem type, starting offset, partition size, block fragment\n"
1662 "size, block size, and cylinders per group for the specified partition (not all\n"
1663 "parameters are configurable for non-BSD partitions).\n");
1664 		break;
1665 	case 'n':
1666 		puts(
1667 "The 'n' command is used to set the mount point for a partition (ie: name it).\n"
1668 "It takes as an optional argument the partition letter to name.  If you do\n"
1669 "not specify a partition letter, you will be prompted for one.  This option\n"
1670 "is only valid if disklabel was invoked with the -F flag.\n");
1671 		break;
1672 	case 'r':
1673 		puts(
1674 "The 'r' command is used to recalculate and display details about\n"
1675 "the available free space.\n");
1676 		break;
1677 	case 'u':
1678 		puts(
1679 "The 'u' command will undo (or redo) the last change.  Entering 'u' once will\n"
1680 "undo your last change.  Entering it again will restore the change.\n");
1681 		break;
1682 	case 's':
1683 		puts(
1684 "The 's' command is used to save a copy of the label to a file in ascii format\n"
1685 "(suitable for loading via disklabel's [-R] option).  It takes as an optional\n"
1686 "argument the filename to save the label to.  If you do not specify a filename,\n"
1687 "you will be prompted for one.\n");
1688 		break;
1689 	case 'w':
1690 		puts(
1691 "The 'w' command will write the current label to disk.  This option will\n"
1692 "commit any changes to the on-disk label.\n");
1693 		break;
1694 	case 'q':
1695 		puts(
1696 "The 'q' command quits the label editor.  If any changes have been made you\n"
1697 "will be asked whether or not to save the changes to the on-disk label.\n");
1698 		break;
1699 	case 'X':
1700 		puts(
1701 "The 'X' command toggles disklabel in to/out of 'expert mode'.  By default,\n"
1702 "some settings are reserved for experts only (such as the block and fragment\n"
1703 "size on ffs partitions).\n");
1704 		break;
1705 	case 'x':
1706 		puts(
1707 "The 'x' command exits the label editor without saving any changes to the\n"
1708 "on-disk label.\n");
1709 		break;
1710 	case 'z':
1711 		puts(
1712 "The 'z' command zeroes out the existing partition table, leaving only the 'c'\n"
1713 "partition.  The drive parameters are not changed.\n");
1714 		break;
1715 	default:
1716 		puts("Available commands:");
1717 		puts(
1718 "  ? [cmd]  - show help                  n [part] - set mount point\n"
1719 "  A        - auto partition all space   p [unit] - print partitions\n"
1720 "  a [part] - add partition              q        - quit & save changes\n"
1721 "  b        - set OpenBSD boundaries     s [path] - save label to file\n"
1722 "  c [part] - change partition size      r        - display free space\n"
1723 "  D        - reset label to default     U        - undo all changes\n"
1724 "  d [part] - delete partition           u        - undo last change\n"
1725 "  e        - edit drive parameters      w        - write label to disk\n"
1726 "  g [d|u]  - [d]isk or [u]ser geometry  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 "\t'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' (gigabytes)\n"
1733 "\t'c' (cylinders), '%' (% of total disk), '&' (% of free space).\n"
1734 "Values in non-sector units are truncated to the nearest cylinder boundary.");
1735 		break;
1736 	}
1737 }
1738 
1739 void
1740 mpcopy(char **to, char **from)
1741 {
1742 	int i;
1743 	char *top;
1744 
1745 	for (i = 0; i < MAXPARTITIONS; i++) {
1746 		if (from[i] != NULL) {
1747 			int len = strlen(from[i]) + 1;
1748 
1749 			top = realloc(to[i], len);
1750 			if (top == NULL)
1751 				errx(4, "out of memory");
1752 			to[i] = top;
1753 			(void)strlcpy(to[i], from[i], len);
1754 		} else if (to[i] != NULL) {
1755 			free(to[i]);
1756 			to[i] = NULL;
1757 		}
1758 	}
1759 }
1760 
1761 int
1762 mpequal(char **mp1, char **mp2)
1763 {
1764 	int i;
1765 
1766 	for (i = 0; i < MAXPARTITIONS; i++) {
1767 		if (mp1[i] == NULL && mp2[i] == NULL)
1768 			continue;
1769 
1770 		if ((mp1[i] != NULL && mp2[i] == NULL) ||
1771 		    (mp1[i] == NULL && mp2[i] != NULL) ||
1772 		    (strcmp(mp1[i], mp2[i]) != 0))
1773 			return(0);
1774 	}
1775 	return(1);
1776 }
1777 
1778 int
1779 mpsave(struct disklabel *lp, char **mp, char *cdev, char *fstabfile)
1780 {
1781 	int i, j, mpset;
1782 	char bdev[MAXPATHLEN], *p;
1783 	struct mountinfo mi[MAXPARTITIONS];
1784 	FILE *fp;
1785 
1786 	memset(&mi, 0, sizeof(mi));
1787 
1788 	for (i = 0, mpset = 0; i < MAXPARTITIONS; i++) {
1789 		if (mp[i] != NULL) {
1790 			mi[i].mountpoint = mp[i];
1791 			mi[i].partno = i;
1792 			mpset = 1;
1793 		}
1794 	}
1795 	/* Exit if there is nothing to do... */
1796 	if (!mpset)
1797 		return(0);
1798 
1799 	/* Convert cdev to bdev */
1800 	if (strncmp(_PATH_DEV, cdev, sizeof(_PATH_DEV) - 1) == 0 &&
1801 	    cdev[sizeof(_PATH_DEV) - 1] == 'r') {
1802 		snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV,
1803 		    &cdev[sizeof(_PATH_DEV)]);
1804 	} else {
1805 		if ((p = strrchr(cdev, '/')) == NULL || *(++p) != 'r')
1806 			return(1);
1807 		*p = '\0';
1808 		snprintf(bdev, sizeof(bdev), "%s%s", cdev, 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")) == NULL)
1817 		return(1);
1818 
1819 	for (i = 0; i < MAXPARTITIONS && mi[i].mountpoint != NULL; i++) {
1820 		j =  mi[i].partno;
1821 		fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev, 'a' + j,
1822 		    mi[i].mountpoint,
1823 		    fstypesnames[lp->d_partitions[j].p_fstype],
1824 		    j == 0 ? 1 : 2);
1825 	}
1826 	fclose(fp);
1827 	return(0);
1828 }
1829 
1830 int
1831 get_offset(struct disklabel *lp, int partno)
1832 {
1833 	struct diskchunk *chunks;
1834 	struct partition *pp = &lp->d_partitions[partno];
1835 	u_int64_t ui, maxsize;
1836 	int i, fstype;
1837 
1838 	ui = getuint(lp, "offset",
1839 	   "Starting sector for this partition.",
1840 	   DL_GETPOFFSET(pp),
1841 	   DL_GETPOFFSET(pp), 0, DO_CONVERSIONS |
1842 	   (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0));
1843 
1844 	if (ui == ULLONG_MAX - 1)
1845 		fputs("Command aborted\n", stderr);
1846 	else if (ui == ULLONG_MAX)
1847 		fputs("Invalid entry\n", stderr);
1848 	else if (ui < starting_sector || ui >= ending_sector)
1849 		fprintf(stderr, "The offset must be >= %llu and < %llu, "
1850 		    "the limits of the OpenBSD portion\n"
1851 		    "of the disk. The 'b' command can change these limits.\n",
1852 		    starting_sector, ending_sector);
1853 #ifdef SUN_AAT0
1854 	else if (partno == 0 && ui != 0)
1855 		fprintf(stderr, "This architecture requires that "
1856 		    "partition 'a' start at sector 0.\n");
1857 #endif
1858 	else {
1859 		fstype = pp->p_fstype;
1860 		pp->p_fstype = FS_UNUSED;
1861 		chunks = free_chunks(lp);
1862 		pp->p_fstype = fstype;
1863 		for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
1864 			if (ui < chunks[i].start || ui >= chunks[i].stop)
1865 				continue;
1866 			DL_SETPOFFSET(pp, ui);
1867 			maxsize = chunks[i].stop - DL_GETPOFFSET(pp);
1868 			if (DL_GETPSIZE(pp) > maxsize)
1869 				DL_SETPSIZE(pp, maxsize);
1870 			return (0);
1871 		}
1872 		fputs("The offset must be in a free area.\n", stderr);
1873 	}
1874 
1875 	/* Partition offset was not set. */
1876 	return (1);
1877 }
1878 
1879 int
1880 get_size(struct disklabel *lp, int partno)
1881 {
1882 	struct partition *pp = &lp->d_partitions[partno];
1883 	u_int64_t maxsize, ui;
1884 
1885 	maxsize = max_partition_size(lp, partno);
1886 
1887 	ui = getuint(lp, "size", "Size of the partition. "
1888 	    "You may also say +/- amount for a relative change.",
1889 	    DL_GETPSIZE(pp), maxsize, DL_GETPOFFSET(pp),
1890 	    DO_CONVERSIONS | ((pp->p_fstype == FS_BSDFFS ||
1891 	    pp->p_fstype == FS_SWAP) ?  DO_ROUNDING : 0));
1892 
1893 	if (ui == ULLONG_MAX - 1)
1894 		fputs("Command aborted\n", stderr);
1895 	else if (ui == ULLONG_MAX)
1896 		fputs("Invalid entry\n", stderr);
1897 	else if (ui == 0)
1898 		fputs("The size must be > 0\n", stderr);
1899 	else if (ui + DL_GETPOFFSET(pp) > ending_sector)
1900 		fprintf(stderr, "The size can't be more than "
1901 		    "%llu sectors, or the partition would\n"
1902 		    "extend beyond the last sector (%llu) of the "
1903 		    "OpenBSD portion of\nthe disk. "
1904 		    "The 'b' command can change this limit.\n",
1905 		    ending_sector - DL_GETPOFFSET(pp), ending_sector);
1906 	else if (ui > maxsize)
1907 		fprintf(stderr,"Sorry, there are only %llu sectors left\n",
1908 		    maxsize);
1909 	else {
1910 		DL_SETPSIZE(pp, ui);
1911 		return (0);
1912 	}
1913 
1914 	/* Partition size was not set. */
1915 	return (1);
1916 }
1917 
1918 int
1919 get_fsize(struct disklabel *lp, int partno)
1920 {
1921 	u_int64_t ui, fsize, frag;
1922 	struct partition *pp = &lp->d_partitions[partno];
1923 
1924 	if (!expert || pp->p_fstype != FS_BSDFFS)
1925 		return (0);
1926 
1927 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
1928 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
1929 	if (fsize == 0)
1930 		frag = 8;
1931 
1932 	for (;;) {
1933 		ui = getuint(lp, "fragment size",
1934 		    "Size of fs block fragments.  Usually 2048 or 512.",
1935 		    fsize, fsize, 0, 0);
1936 		if (ui == ULLONG_MAX - 1) {
1937 			fputs("Command aborted\n", stderr);
1938 			return(1);
1939 		} else if (ui == ULLONG_MAX)
1940 			fputs("Invalid entry\n", stderr);
1941 		else
1942 			break;
1943 	}
1944 	if (ui == 0)
1945 		puts("Zero fragment size implies zero block size");
1946 	pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(ui, frag);
1947 	return(0);
1948 }
1949 
1950 int
1951 get_bsize(struct disklabel *lp, int partno)
1952 {
1953 	u_int64_t ui, bsize, frag, fsize;
1954 	struct partition *pp = &lp->d_partitions[partno];
1955 
1956 	if (!expert || pp->p_fstype != FS_BSDFFS)
1957 		return (0);
1958 
1959 	/* Avoid dividing by zero... */
1960 	if (pp->p_fragblock == 0)
1961 		return(1);
1962 
1963 	bsize = DISKLABELV1_FFS_BSIZE(pp->p_fragblock);
1964 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
1965 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
1966 
1967 	for (;;) {
1968 		ui = getuint(lp, "block size",
1969 		    "Size of filesystem blocks.  Usually 16384 or 4096.",
1970 		    fsize * frag, fsize * frag,
1971 		    0, 0);
1972 
1973 		/* sanity checks */
1974 		if (ui == ULLONG_MAX - 1) {
1975 			fputs("Command aborted\n", stderr);
1976 			return(1);
1977 		} else if (ui == ULLONG_MAX)
1978 			fputs("Invalid entry\n", stderr);
1979 		else if (ui < getpagesize())
1980 			fprintf(stderr,
1981 			    "Error: block size must be at least as big "
1982 			    "as page size (%d).\n", getpagesize());
1983 		else if (ui % fsize != 0)
1984 			fputs("Error: block size must be a multiple of the "
1985 			    "fragment size.\n", stderr);
1986 		else if (ui / fsize < 1)
1987 			fputs("Error: block size must be at least as big as "
1988 			    "fragment size.\n", stderr);
1989 		else
1990 			break;
1991 	}
1992 	pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(ui / frag, frag);
1993 	return(0);
1994 }
1995 
1996 int
1997 get_fstype(struct disklabel *lp, int partno)
1998 {
1999 	char *p;
2000 	u_int64_t ui;
2001 	struct partition *pp = &lp->d_partitions[partno];
2002 
2003 	if (pp->p_fstype < FSMAXTYPES) {
2004 		p = getstring("FS type",
2005 		    "Filesystem type (usually 4.2BSD or swap)",
2006 		    fstypenames[pp->p_fstype]);
2007 		if (p == NULL) {
2008 			fputs("Command aborted\n", stderr);
2009 			return(1);
2010 		}
2011 		for (ui = 0; ui < FSMAXTYPES; ui++) {
2012 			if (!strcasecmp(p, fstypenames[ui])) {
2013 				pp->p_fstype = ui;
2014 				break;
2015 			}
2016 		}
2017 		if (ui >= FSMAXTYPES) {
2018 			printf("Unrecognized filesystem type '%s', treating as 'unknown'\n", p);
2019 			pp->p_fstype = FS_OTHER;
2020 		}
2021 	} else {
2022 		for (;;) {
2023 			ui = getuint(lp, "FS type (decimal)",
2024 			    "Filesystem type as a decimal number; usually 7 (4.2BSD) or 1 (swap).",
2025 			    pp->p_fstype, pp->p_fstype, 0, 0);
2026 			if (ui == ULLONG_MAX - 1) {
2027 				fputs("Command aborted\n", stderr);
2028 				return(1);
2029 			} if (ui == ULLONG_MAX)
2030 				fputs("Invalid entry\n", stderr);
2031 			else
2032 				break;
2033 		}
2034 		pp->p_fstype = ui;
2035 	}
2036 	return(0);
2037 }
2038 
2039 int
2040 get_mp(struct disklabel *lp, char **mp, int partno)
2041 {
2042 	char *p;
2043 	struct partition *pp = &lp->d_partitions[partno];
2044 
2045 	if (mp != NULL && pp->p_fstype != FS_UNUSED &&
2046 	    pp->p_fstype != FS_SWAP && pp->p_fstype != FS_BOOT &&
2047 	    pp->p_fstype != FS_OTHER) {
2048 		for (;;) {
2049 			p = getstring("mount point",
2050 			    "Where to mount this filesystem (ie: / /var /usr)",
2051 			    mp[partno] ? mp[partno] : "none");
2052 			if (p == NULL) {
2053 				fputs("Command aborted\n", stderr);
2054 				return(1);
2055 			}
2056 			if (strcasecmp(p, "none") == 0) {
2057 				free(mp[partno]);
2058 				mp[partno] = NULL;
2059 				break;
2060 			}
2061 			if (*p == '/') {
2062 				/* XXX - might as well realloc */
2063 				free(mp[partno]);
2064 				if ((mp[partno] = strdup(p)) == NULL)
2065 					errx(4, "out of memory");
2066 				break;
2067 			}
2068 			fputs("Mount points must start with '/'\n", stderr);
2069 		}
2070 	}
2071 	return(0);
2072 }
2073 
2074 int
2075 micmp(const void *a1, const void *a2)
2076 {
2077 	struct mountinfo *mi1 = (struct mountinfo *)a1;
2078 	struct mountinfo *mi2 = (struct mountinfo *)a2;
2079 
2080 	/* We want all the NULLs at the end... */
2081 	if (mi1->mountpoint == NULL && mi2->mountpoint == NULL)
2082 		return(0);
2083 	else if (mi1->mountpoint == NULL)
2084 		return(1);
2085 	else if (mi2->mountpoint == NULL)
2086 		return(-1);
2087 	else
2088 		return(strcmp(mi1->mountpoint, mi2->mountpoint));
2089 }
2090 
2091 void
2092 get_geometry(int f, struct disklabel **dgpp)
2093 {
2094 	struct stat st;
2095 	struct disklabel *disk_geop;
2096 
2097 	if (fstat(f, &st) == -1)
2098 		err(4, "Can't stat device");
2099 
2100 	/* Get disk geometry */
2101 	if ((disk_geop = calloc(1, sizeof(struct disklabel))) == NULL)
2102 		errx(4, "out of memory");
2103 	if (ioctl(f, DIOCGPDINFO, disk_geop) < 0 &&
2104 	    ioctl(f, DIOCGDINFO, disk_geop) < 0)
2105 		err(4, "ioctl DIOCGDINFO");
2106 	*dgpp = disk_geop;
2107 }
2108 
2109 void
2110 set_geometry(struct disklabel *lp, struct disklabel *dgp,
2111     struct disklabel *ugp, char *p)
2112 {
2113 	if (p == NULL) {
2114 		p = getstring("[d]isk or [u]ser geometry",
2115 		    "Enter 'd' to use the geometry based on what the disk "
2116 		    "itself thinks it is, or 'u' to use the geometry that "
2117 		    "was found in the label.",
2118 		    "d");
2119 	}
2120 	if (p == NULL) {
2121 		fputs("Command aborted\n", stderr);
2122 		return;
2123 	}
2124 	switch (*p) {
2125 	case 'd':
2126 	case 'D':
2127 		if (dgp == NULL)
2128 			fputs("BIOS geometry not defined.\n", stderr);
2129 		else {
2130 			lp->d_secsize = dgp->d_secsize;
2131 			lp->d_nsectors = dgp->d_nsectors;
2132 			lp->d_ntracks = dgp->d_ntracks;
2133 			lp->d_ncylinders = dgp->d_ncylinders;
2134 			lp->d_secpercyl = dgp->d_secpercyl;
2135 			DL_SETDSIZE(lp, DL_GETDSIZE(dgp));
2136 		}
2137 		break;
2138 	case 'u':
2139 	case 'U':
2140 		if (ugp == NULL)
2141 			fputs("BIOS geometry not defined.\n", stderr);
2142 		else {
2143 			lp->d_secsize = ugp->d_secsize;
2144 			lp->d_nsectors = ugp->d_nsectors;
2145 			lp->d_ntracks = ugp->d_ntracks;
2146 			lp->d_ncylinders = ugp->d_ncylinders;
2147 			lp->d_secpercyl = ugp->d_secpercyl;
2148 			DL_SETDSIZE(lp, DL_GETDSIZE(ugp));
2149 			if (dgp != NULL && ugp->d_secsize == dgp->d_secsize &&
2150 			    ugp->d_nsectors == dgp->d_nsectors &&
2151 			    ugp->d_ntracks == dgp->d_ntracks &&
2152 			    ugp->d_ncylinders == dgp->d_ncylinders &&
2153 			    ugp->d_secpercyl == dgp->d_secpercyl &&
2154 			    DL_GETDSIZE(ugp) == DL_GETDSIZE(dgp))
2155 				fputs("Note: user geometry is the same as disk "
2156 				    "geometry.\n", stderr);
2157 		}
2158 		break;
2159 	default:
2160 		fputs("You must enter either 'd' or 'u'.\n", stderr);
2161 		break;
2162 	}
2163 }
2164 
2165 void
2166 zero_partitions(struct disklabel *lp)
2167 {
2168 	int i;
2169 
2170 	for (i = 0; i < MAXPARTITIONS; i++)
2171 		memset(&lp->d_partitions[i], 0, sizeof(struct partition));
2172 	DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
2173 }
2174 
2175 u_int64_t
2176 max_partition_size(struct disklabel *lp, int partno)
2177 {
2178 	struct partition *pp = &lp->d_partitions[partno];
2179 	struct diskchunk *chunks;
2180 	u_int64_t maxsize, offset;
2181 	int fstype, i;
2182 
2183 	fstype = pp->p_fstype;
2184 	pp->p_fstype = FS_UNUSED;
2185 	chunks = free_chunks(lp);
2186 	pp->p_fstype = fstype;
2187 
2188 	offset = DL_GETPOFFSET(pp);
2189 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
2190 		if (offset < chunks[i].start || offset >= chunks[i].stop)
2191 			continue;
2192 		maxsize = chunks[i].stop - offset;
2193 		break;
2194 	}
2195 	return (maxsize);
2196 }
2197 
2198 void
2199 psize(daddr64_t sz, char unit, struct disklabel *lp)
2200 {
2201 	double d = scale(sz, unit, lp);
2202 	if (d < 0)
2203 		printf("%llu", sz);
2204 	else
2205 		printf("%.*f%c", unit == 'B' ? 0 : 1, d, unit);
2206 }
2207 
2208 void
2209 display_edit(struct disklabel *lp, char **mp, char unit, u_int64_t fr)
2210 {
2211 	int i;
2212 
2213 	unit = toupper(unit);
2214 
2215 	printf("OpenBSD area: ");
2216 	psize(starting_sector, unit, lp);
2217 	printf("-");
2218 	psize(ending_sector, unit, lp);
2219 	printf("; size: ");
2220 	psize(ending_sector - starting_sector, unit, lp);
2221 	printf("; free: ");
2222 	psize(fr, unit, lp);
2223 
2224 	printf("\n#    %16.16s %16.16s  fstype [fsize bsize  cpg]\n",
2225 	    "size", "offset");
2226 	for (i = 0; i < lp->d_npartitions; i++)
2227 		display_partition(stdout, lp, mp, i, unit);
2228 }
2229 
2230