xref: /openbsd/sbin/disklabel/editor.c (revision c88f83bd)
1*c88f83bdSotto /*	$OpenBSD: editor.c,v 1.231 2010/04/04 14:12:12 otto Exp $	*/
26fe57b42Smillert 
36fe57b42Smillert /*
42d8451b0Smillert  * Copyright (c) 1997-2000 Todd C. Miller <Todd.Miller@courtesan.com>
56fe57b42Smillert  *
606f01696Smillert  * Permission to use, copy, modify, and distribute this software for any
706f01696Smillert  * purpose with or without fee is hereby granted, provided that the above
806f01696Smillert  * copyright notice and this permission notice appear in all copies.
96fe57b42Smillert  *
10328f1f07Smillert  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11328f1f07Smillert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12328f1f07Smillert  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13328f1f07Smillert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14328f1f07Smillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15328f1f07Smillert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16328f1f07Smillert  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176fe57b42Smillert  */
186fe57b42Smillert 
196fe57b42Smillert #include <sys/types.h>
206fe57b42Smillert #include <sys/param.h>
21c33fcabaSmillert #include <sys/stat.h>
22c33fcabaSmillert #include <sys/ioctl.h>
238dde8bb6Sotto #include <sys/sysctl.h>
246fe57b42Smillert #define	DKTYPENAMES
256fe57b42Smillert #include <sys/disklabel.h>
266fe57b42Smillert 
276fe57b42Smillert #include <ufs/ffs/fs.h>
286fe57b42Smillert 
296fe57b42Smillert #include <ctype.h>
306fe57b42Smillert #include <err.h>
316fe57b42Smillert #include <errno.h>
326fe57b42Smillert #include <string.h>
33803ff7d5Smillert #include <libgen.h>
346fe57b42Smillert #include <stdio.h>
356fe57b42Smillert #include <stdlib.h>
366fe57b42Smillert #include <unistd.h>
376fe57b42Smillert 
38f21a098bSotto #include "extern.h"
394793b14cSmillert #include "pathnames.h"
404793b14cSmillert 
416fe57b42Smillert /* flags for getuint() */
426fe57b42Smillert #define	DO_CONVERSIONS	0x00000001
436fe57b42Smillert #define	DO_ROUNDING	0x00000002
446fe57b42Smillert 
45f98aebd4Smillert #ifndef NUMBOOT
46f98aebd4Smillert #define NUMBOOT 0
47f98aebd4Smillert #endif
48f98aebd4Smillert 
4996a888c6Smillert /* structure to describe a portion of a disk */
5096a888c6Smillert struct diskchunk {
511e0ad43cSotto 	u_int64_t start;
521e0ad43cSotto 	u_int64_t stop;
5396a888c6Smillert };
5496a888c6Smillert 
553f843443Smillert /* used when sorting mountpoints in mpsave() */
563f843443Smillert struct mountinfo {
573f843443Smillert 	char *mountpoint;
583f843443Smillert 	int partno;
593f843443Smillert };
603f843443Smillert 
61557f712bSkrw /* used when allocating all space according to recommendations */
62557f712bSkrw 
63557f712bSkrw struct space_allocation {
645f3e1104Skrw 	daddr64_t	minsz;	/* starts as blocks, xlated to sectors. */
655f3e1104Skrw 	daddr64_t	maxsz;	/* starts as blocks, xlated to sectors. */
66557f712bSkrw 	int		rate;	/* % of extra space to use */
67557f712bSkrw 	char 	       *mp;
68557f712bSkrw };
69557f712bSkrw 
708dde8bb6Sotto /* entries for swap and var are changed by editor_allocspace() */
718dde8bb6Sotto const struct space_allocation alloc_big[] = {
7292adb55fSderaadt 	{   MEG(80),         GIG(1),   5, "/"		},
738dde8bb6Sotto 	{   MEG(80),       MEG(256),   5, "swap"	},
744ab111d2Sderaadt 	{  MEG(120),         GIG(4),   8, "/tmp"	},
7592adb55fSderaadt 	{   MEG(80),         GIG(4),  13, "/var"	},
7621a0d117Sderaadt 	{  MEG(900),         GIG(2),   5, "/usr"	},
7792adb55fSderaadt 	{  MEG(512),         GIG(1),   3, "/usr/X11R6"	},
7892adb55fSderaadt 	{    GIG(2),         GIG(6),   5, "/usr/local"	},
7992adb55fSderaadt 	{    GIG(1),         GIG(2),   3, "/usr/src"	},
8092adb55fSderaadt 	{    GIG(1),         GIG(2),   3, "/usr/obj"	},
8121a0d117Sderaadt 	{    GIG(1),       GIG(300),  50, "/home"	}
824ab111d2Sderaadt 	/* Anything beyond this leave for the user to decide */
838dde8bb6Sotto };
848dde8bb6Sotto 
858dde8bb6Sotto const struct space_allocation alloc_medium[] = {
86905e8239Sderaadt 	{  MEG(800),         GIG(2),   5, "/"		},
878dde8bb6Sotto 	{   MEG(80),       MEG(256),  10, "swap"	},
88905e8239Sderaadt 	{  MEG(900),         GIG(3),  78, "/usr"	},
89905e8239Sderaadt 	{  MEG(256),         GIG(2),   7, "/home"	}
908dde8bb6Sotto };
918dde8bb6Sotto 
928dde8bb6Sotto const struct space_allocation alloc_small[] = {
9392adb55fSderaadt 	{  MEG(700),         GIG(4),  95, "/"		},
948dde8bb6Sotto 	{    MEG(1),       MEG(256),   5, "swap"	}
958dde8bb6Sotto };
968dde8bb6Sotto 
978dde8bb6Sotto const struct space_allocation alloc_stupid[] = {
988dde8bb6Sotto 	{    MEG(1),      MEG(2048), 100, "/"		}
998dde8bb6Sotto };
1008dde8bb6Sotto 
101b2813ff1Sderaadt #ifndef nitems
102b2813ff1Sderaadt #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
103b2813ff1Sderaadt #endif
104b2813ff1Sderaadt 
1058a862940Sderaadt const struct {
1068a862940Sderaadt 	const struct space_allocation *table;
1078a862940Sderaadt 	int sz;
1088a862940Sderaadt } alloc_table[] = {
1098dde8bb6Sotto 	{ alloc_big,	nitems(alloc_big) },
1108dde8bb6Sotto 	{ alloc_medium,	nitems(alloc_medium) },
1118dde8bb6Sotto 	{ alloc_small,	nitems(alloc_small) },
1128dde8bb6Sotto 	{ alloc_stupid,	nitems(alloc_stupid) }
113557f712bSkrw };
114557f712bSkrw 
1159fdcb4d6Skrw void	edit_parms(struct disklabel *);
1166aaa4aabSotto void	editor_resize(struct disklabel *, char *);
11734ae4198Skrw void	editor_add(struct disklabel *, char *);
1189fdcb4d6Skrw void	editor_change(struct disklabel *, char *);
1199fdcb4d6Skrw u_int64_t editor_countfree(struct disklabel *);
12034ae4198Skrw void	editor_delete(struct disklabel *, char *);
121c72b5b24Smillert void	editor_help(char *);
12234ae4198Skrw void	editor_modify(struct disklabel *, char *);
12334ae4198Skrw void	editor_name(struct disklabel *, char *);
124c72b5b24Smillert char	*getstring(char *, char *, char *);
125dea75673Skrw u_int64_t getuint(struct disklabel *, char *, char *, u_int64_t, u_int64_t, u_int64_t, int);
1261f0f871dSkrw int	has_overlap(struct disklabel *);
127c72b5b24Smillert int	partition_cmp(const void *, const void *);
1280fbd3c97Skrw struct partition **sort_partitions(struct disklabel *);
129c72b5b24Smillert void	getdisktype(struct disklabel *, char *, char *);
13087023ed9Skrw void	find_bounds(struct disklabel *);
1319fdcb4d6Skrw void	set_bounds(struct disklabel *);
132c72b5b24Smillert struct diskchunk *free_chunks(struct disklabel *);
1334f3bbbf0Skrw void	mpcopy(char **, char **);
134c72b5b24Smillert int	micmp(const void *, const void *);
135c72b5b24Smillert int	mpequal(char **, char **);
136c72b5b24Smillert int	get_bsize(struct disklabel *, int);
137c72b5b24Smillert int	get_fsize(struct disklabel *, int);
138c72b5b24Smillert int	get_fstype(struct disklabel *, int);
13934ae4198Skrw int	get_mp(struct disklabel *, int);
140604d3bdeSkrw int	get_offset(struct disklabel *, int);
1419fdcb4d6Skrw int	get_size(struct disklabel *, int);
14287023ed9Skrw void	get_geometry(int, struct disklabel **);
14387023ed9Skrw void	set_geometry(struct disklabel *, struct disklabel *, struct disklabel *,
14487023ed9Skrw 	    char *);
1459fdcb4d6Skrw void	zero_partitions(struct disklabel *);
14614192793Skrw u_int64_t max_partition_size(struct disklabel *, int);
14734ae4198Skrw void	display_edit(struct disklabel *, char, u_int64_t);
14896a888c6Smillert 
1491e0ad43cSotto static u_int64_t starting_sector;
1501e0ad43cSotto static u_int64_t ending_sector;
1512d8451b0Smillert static int expert;
152fc6e9c48Sotto static int overlap;
1536fe57b42Smillert 
1546fe57b42Smillert /*
1554d4a335eSkrw  * Simple partition editor.
1566fe57b42Smillert  */
1576fe57b42Smillert int
15834ae4198Skrw editor(struct disklabel *lp, int f)
1596fe57b42Smillert {
1604f3bbbf0Skrw 	struct disklabel origlabel, lastlabel, tmplabel, label = *lp;
1617e28fb0fSderaadt 	struct disklabel *disk_geop = NULL;
16296a888c6Smillert 	struct partition *pp;
1636fe57b42Smillert 	FILE *fp;
1646fe57b42Smillert 	char buf[BUFSIZ], *cmd, *arg;
16534ae4198Skrw 	char **omountpoints = NULL;
1664f3bbbf0Skrw 	char **origmountpoints = NULL, **tmpmountpoints = NULL;
1677e28fb0fSderaadt 	int i, error = 0;
168bd6726faSmillert 
169bd6726faSmillert 	/* Alloc and init mount point info */
17034ae4198Skrw 	if (!(omountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
1714f3bbbf0Skrw 	    !(origmountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
172bd6726faSmillert 	    !(tmpmountpoints = calloc(MAXPARTITIONS, sizeof(char *))))
173bd6726faSmillert 		errx(4, "out of memory");
1746fe57b42Smillert 
17596a888c6Smillert 	/* Don't allow disk type of "unknown" */
17634ae4198Skrw 	getdisktype(&label, "You need to specify a type for this disk.", specname);
1776fe57b42Smillert 
17887023ed9Skrw 	/* Get the on-disk geometries if possible */
17987023ed9Skrw 	get_geometry(f, &disk_geop);
180c33fcabaSmillert 
181d09f3941Smillert 	/* How big is the OpenBSD portion of the disk?  */
18287023ed9Skrw 	find_bounds(&label);
183d09f3941Smillert 
18496a888c6Smillert 	/* Make sure there is no partition overlap. */
1851f0f871dSkrw 	if (has_overlap(&label))
1866fe57b42Smillert 		errx(1, "can't run when there is partition overlap.");
1876fe57b42Smillert 
18896a888c6Smillert 	/* If we don't have a 'c' partition, create one. */
189d09f3941Smillert 	pp = &label.d_partitions[RAW_PART];
1901e0ad43cSotto 	if (label.d_npartitions < 3 || DL_GETPSIZE(pp) == 0) {
19196a888c6Smillert 		puts("No 'c' partition found, adding one that spans the disk.");
19296a888c6Smillert 		if (label.d_npartitions < 3)
19396a888c6Smillert 			label.d_npartitions = 3;
19434af67a3Sotto 		DL_SETPOFFSET(pp, 0);
19534af67a3Sotto 		DL_SETPSIZE(pp, DL_GETDSIZE(&label));
19696a888c6Smillert 		pp->p_fstype = FS_UNUSED;
197ddfcbf38Sotto 		pp->p_fragblock = pp->p_cpg = 0;
19896a888c6Smillert 	}
1990f820bbbSmillert 
200fc1a4cc6Sderaadt #ifdef SUN_CYLCHECK
201fc1a4cc6Sderaadt 	if (label.d_flags & D_VENDOR) {
202fc1a4cc6Sderaadt 		puts("This platform requires that partition offsets/sizes "
203fc1a4cc6Sderaadt 		    "be on cylinder boundaries.\n"
204fc1a4cc6Sderaadt 		    "Partition offsets/sizes will be rounded to the "
205fc1a4cc6Sderaadt 		    "nearest cylinder automatically.");
206fc1a4cc6Sderaadt 	}
2076fe57b42Smillert #endif
2086fe57b42Smillert 
209bd6726faSmillert 	/* Set d_bbsize and d_sbsize as necessary */
21055403f76Smillert 	if (label.d_bbsize == 0)
21155403f76Smillert 		label.d_bbsize = BBSIZE;
21255403f76Smillert 	if (label.d_sbsize == 0)
21355403f76Smillert 		label.d_sbsize = SBSIZE;
214f98aebd4Smillert 
215440b1d70Smillert 	/* Interleave must be >= 1 */
216440b1d70Smillert 	if (label.d_interleave == 0)
217440b1d70Smillert 		label.d_interleave = 1;
218440b1d70Smillert 
21993160b9bSkrw 	/* Save the (U|u)ndo labels and mountpoints. */
22093160b9bSkrw 	mpcopy(origmountpoints, mountpoints);
2214f3bbbf0Skrw 	origlabel = label;
2226fe57b42Smillert 	lastlabel = label;
22334ae4198Skrw 
22434ae4198Skrw 	puts("Label editor (enter '?' for help at any prompt)");
2256fe57b42Smillert 	for (;;) {
2266fe57b42Smillert 		fputs("> ", stdout);
2276e0becc5Smillert 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
2286e0becc5Smillert 			putchar('\n');
2296e0becc5Smillert 			buf[0] = 'q';
2306e0becc5Smillert 			buf[1] = '\0';
2316e0becc5Smillert 		}
232260513deSmillert 		if ((cmd = strtok(buf, " \t\r\n")) == NULL)
233260513deSmillert 			continue;
234260513deSmillert 		arg = strtok(NULL, " \t\r\n");
2356fe57b42Smillert 
2364f3bbbf0Skrw 		if ((*cmd != 'u') && (*cmd != 'U')) {
2374f3bbbf0Skrw 			/*
2384f3bbbf0Skrw 			 * Save undo info in case the command tries to make
2394f3bbbf0Skrw 			 * changes but decides not to.
2404f3bbbf0Skrw 			 */
2414f3bbbf0Skrw 			tmplabel = lastlabel;
2424f3bbbf0Skrw 			lastlabel = label;
2434f3bbbf0Skrw 			mpcopy(tmpmountpoints, omountpoints);
2444f3bbbf0Skrw 			mpcopy(omountpoints, mountpoints);
2454f3bbbf0Skrw 		}
2466fe57b42Smillert 
2474f3bbbf0Skrw 		switch (*cmd) {
2486fe57b42Smillert 		case '?':
249ea37abd3Sderaadt 		case 'h':
250617e6e4aSmillert 			editor_help(arg ? arg : "");
2516fe57b42Smillert 			break;
2526fe57b42Smillert 
253557f712bSkrw 		case 'A':
254ab20a3eaSkrw 			if (ioctl(f, DIOCGPDINFO, &label) == 0) {
255ab20a3eaSkrw 				aflag = 1;
25634ae4198Skrw 				editor_allocspace(&label);
257ab20a3eaSkrw 			} else
25834ae4198Skrw 				label = lastlabel;
259557f712bSkrw 			break;
2606fe57b42Smillert 		case 'a':
26134ae4198Skrw 			editor_add(&label, arg);
26296a888c6Smillert 			break;
26396a888c6Smillert 
26496a888c6Smillert 		case 'b':
2659fdcb4d6Skrw 			set_bounds(&label);
2666fe57b42Smillert 			break;
2676fe57b42Smillert 
2686fe57b42Smillert 		case 'c':
2699fdcb4d6Skrw 			editor_change(&label, arg);
2706fe57b42Smillert 			break;
2716fe57b42Smillert 
2729afbe9eeSmillert 		case 'D':
27393160b9bSkrw 			if (ioctl(f, DIOCGPDINFO, &label) == 0) {
27471bba4ecSkrw 				dflag = 1;
27593160b9bSkrw 				for (i=0; i<MAXPARTITIONS; i++) {
27693160b9bSkrw 					free(mountpoints[i]);
27793160b9bSkrw 					mountpoints[i] = NULL;
27893160b9bSkrw 				}
27993160b9bSkrw 			} else
280cdd7eb76Smillert 				warn("unable to get default partition table");
2819afbe9eeSmillert 			break;
2829afbe9eeSmillert 
2836fe57b42Smillert 		case 'd':
28434ae4198Skrw 			editor_delete(&label, arg);
2856fe57b42Smillert 			break;
2866fe57b42Smillert 
2879afbe9eeSmillert 		case 'e':
2889fdcb4d6Skrw 			edit_parms(&label);
2899afbe9eeSmillert 			break;
2909afbe9eeSmillert 
291c33fcabaSmillert 		case 'g':
29287023ed9Skrw 			set_geometry(&label, disk_geop, lp, arg);
293c33fcabaSmillert 			break;
294c33fcabaSmillert 
2956fe57b42Smillert 		case 'm':
29634ae4198Skrw 			editor_modify(&label, arg);
297bd6726faSmillert 			break;
298bd6726faSmillert 
299bd6726faSmillert 		case 'n':
3005c79e1cfSkrw 			if (!fstabfile) {
301bd6726faSmillert 				fputs("This option is not valid when run "
30284d0bb16Sderaadt 				    "without the -f flag.\n", stderr);
303bd6726faSmillert 				break;
304bd6726faSmillert 			}
30534ae4198Skrw 			editor_name(&label, arg);
3066fe57b42Smillert 			break;
3076fe57b42Smillert 
3086fe57b42Smillert 		case 'p':
30934ae4198Skrw 			display_edit(&label, arg ? *arg : 0, editor_countfree(&label));
3106fe57b42Smillert 			break;
3116fe57b42Smillert 
312aff3f969Sotto 		case 'l':
31334ae4198Skrw 			display(stdout, &label, arg ? *arg : 0, 0);
314aff3f969Sotto 			break;
315aff3f969Sotto 
316508086e9Smillert 		case 'M': {
317508086e9Smillert 			sig_t opipe = signal(SIGPIPE, SIG_IGN);
31845decb36Sderaadt 			char *pager, *comm = NULL;
319e7936562Sderaadt 			extern const u_char manpage[];
32008f8e31fSotto 			extern const int manpage_sz;
3215d12b01bSderaadt 
322489bd112Spjanzen 			if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
323508086e9Smillert 				pager = _PATH_LESS;
32408f8e31fSotto 
32545decb36Sderaadt 			if (asprintf(&comm, "gunzip -qc|%s", pager) != -1 &&
32645decb36Sderaadt 			    (fp = popen(comm, "w")) != NULL) {
32708f8e31fSotto 				(void) fwrite(manpage, manpage_sz, 1, fp);
3285d12b01bSderaadt 				pclose(fp);
329508086e9Smillert 			} else
330508086e9Smillert 				warn("unable to execute %s", pager);
331508086e9Smillert 
33245decb36Sderaadt 			free(comm);
333508086e9Smillert 			(void)signal(SIGPIPE, opipe);
3345d12b01bSderaadt 			break;
335508086e9Smillert 		}
3365d12b01bSderaadt 
3376fe57b42Smillert 		case 'q':
33869220492Smillert 			if (donothing) {
33969220492Smillert 				puts("In no change mode, not writing label.");
3407e28fb0fSderaadt 				goto done;
34169220492Smillert 			}
342bd6726faSmillert 			/* Save mountpoint info if there is any. */
34334ae4198Skrw 			mpsave(&label);
34493160b9bSkrw 
34571bba4ecSkrw                         /*
34693160b9bSkrw 			 * If we haven't changed the original label, and it
34793160b9bSkrw 			 * wasn't a default label or an auto-allocated label,
34893160b9bSkrw 			 * there is no need to do anything before exiting. Note
34993160b9bSkrw 			 * that 'w' will reset dflag and aflag to allow 'q' to
35093160b9bSkrw 			 * exit without further questions.
35171bba4ecSkrw  			 */
352ab20a3eaSkrw 			if (!dflag && !aflag &&
353ab20a3eaSkrw 			    memcmp(lp, &label, sizeof(label)) == 0) {
354bd6726faSmillert 				puts("No label changes.");
3557e28fb0fSderaadt 				goto done;
3566fe57b42Smillert 			}
3576fe57b42Smillert 			do {
358d0e67762Smillert 				arg = getstring("Write new label?",
359d0e67762Smillert 				    "Write the modified label to disk?",
360d0e67762Smillert 				    "y");
36196a888c6Smillert 			} while (arg && tolower(*arg) != 'y' && tolower(*arg) != 'n');
36296a888c6Smillert 			if (arg && tolower(*arg) == 'y') {
363d0e67762Smillert 				if (writelabel(f, bootarea, &label) == 0) {
3646fe57b42Smillert 					*lp = label;
3657e28fb0fSderaadt 					goto done;
3666fe57b42Smillert 				}
367d0e67762Smillert 				warnx("unable to write label");
368d0e67762Smillert 			}
3697e28fb0fSderaadt 			error = 1;
3707e28fb0fSderaadt 			goto done;
3716fe57b42Smillert 			/* NOTREACHED */
3726fe57b42Smillert 			break;
3736fe57b42Smillert 
3746aaa4aabSotto 		case 'R':
375fc6e9c48Sotto 			if (aflag && !overlap)
3766aaa4aabSotto 				editor_resize(&label, arg);
3776aaa4aabSotto 			else
3786aaa4aabSotto 				fputs("Resize only implemented for auto "
379fc6e9c48Sotto 				    "allocated labels\n", stderr);
3806aaa4aabSotto 			break;
3816aaa4aabSotto 
38225f9c360Skrw 		case 'r': {
38325f9c360Skrw 			struct diskchunk *chunks;
38425f9c360Skrw 			int i;
3859fdcb4d6Skrw 			/* Display free space. */
38625f9c360Skrw 			chunks = free_chunks(&label);
38725f9c360Skrw 			for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0;
38825f9c360Skrw 			    i++)
38925f9c360Skrw 				fprintf(stderr, "Free sectors: %16llu - %16llu "
39025f9c360Skrw 				    "(%16llu)\n",
39125f9c360Skrw 			    	    chunks[i].start, chunks[i].stop - 1,
39225f9c360Skrw 			   	    chunks[i].stop - chunks[i].start);
39325f9c360Skrw 			fprintf(stderr, "Total free sectors: %llu.\n",
3949fdcb4d6Skrw 			    editor_countfree(&label));
395c0bdc608Smillert 		    	break;
39625f9c360Skrw 		}
397c0bdc608Smillert 
3986fe57b42Smillert 		case 's':
3996fe57b42Smillert 			if (arg == NULL) {
400c33fcabaSmillert 				arg = getstring("Filename",
4016fe57b42Smillert 				    "Name of the file to save label into.",
4026fe57b42Smillert 				    NULL);
403e97229a3Scnst 				if (arg == NULL || *arg == '\0')
4046fe57b42Smillert 					break;
4056fe57b42Smillert 			}
4066fe57b42Smillert 			if ((fp = fopen(arg, "w")) == NULL) {
4076fe57b42Smillert 				warn("cannot open %s", arg);
4086fe57b42Smillert 			} else {
40934ae4198Skrw 				display(fp, &label, 0, 1);
4106fe57b42Smillert 				(void)fclose(fp);
4116fe57b42Smillert 			}
4126fe57b42Smillert 			break;
4136fe57b42Smillert 
4144f3bbbf0Skrw 		case 'U':
41593160b9bSkrw 			/*
41693160b9bSkrw 			 * If we allow 'U' repeatedly, information would be lost. This way
41793160b9bSkrw 			 * multiple 'U's followed by 'u' would undo the 'U's.
41893160b9bSkrw 			 */
41993160b9bSkrw 			if (memcmp(&label, &origlabel, sizeof(label)) ||
42093160b9bSkrw 			    !mpequal(mountpoints, origmountpoints)) {
4214f3bbbf0Skrw 				tmplabel = label;
4224f3bbbf0Skrw 				label = origlabel;
4234f3bbbf0Skrw 				lastlabel = tmplabel;
4244f3bbbf0Skrw 				mpcopy(tmpmountpoints, mountpoints);
4254f3bbbf0Skrw 				mpcopy(mountpoints, origmountpoints);
4264f3bbbf0Skrw 				mpcopy(omountpoints, tmpmountpoints);
4274f3bbbf0Skrw 			}
42893160b9bSkrw 			puts("Original label and mount points restored.");
4294f3bbbf0Skrw 			break;
4304f3bbbf0Skrw 
4316fe57b42Smillert 		case 'u':
4326fe57b42Smillert 			tmplabel = label;
4336fe57b42Smillert 			label = lastlabel;
4346fe57b42Smillert 			lastlabel = tmplabel;
4354f3bbbf0Skrw 			mpcopy(tmpmountpoints, mountpoints);
436bd6726faSmillert 			mpcopy(mountpoints, omountpoints);
4374f3bbbf0Skrw 			mpcopy(omountpoints, tmpmountpoints);
4386fe57b42Smillert 			puts("Last change undone.");
4396fe57b42Smillert 			break;
4406fe57b42Smillert 
441040947cfSmillert 		case 'w':
442bd6726faSmillert 			if (donothing)  {
443040947cfSmillert 				puts("In no change mode, not writing label.");
444bd6726faSmillert 				break;
445bd6726faSmillert 			}
44693160b9bSkrw 
44741f684b9Skrw 			/* Write label to disk. */
44841f684b9Skrw 			if (writelabel(f, bootarea, &label) != 0)
449040947cfSmillert 				warnx("unable to write label");
45071bba4ecSkrw 			else {
451ab20a3eaSkrw 				dflag = aflag = 0;
4525af08e9cSmillert 				*lp = label;
45371bba4ecSkrw 			}
454040947cfSmillert 			break;
455040947cfSmillert 
4562d8451b0Smillert 		case 'X':
4572d8451b0Smillert 			expert = !expert;
4582d8451b0Smillert 			printf("%s expert mode\n", expert ? "Entering" :
4592d8451b0Smillert 			    "Exiting");
4602d8451b0Smillert 			break;
4612d8451b0Smillert 
4626fe57b42Smillert 		case 'x':
4637e28fb0fSderaadt 			goto done;
4646fe57b42Smillert 			break;
4656fe57b42Smillert 
4669afbe9eeSmillert 		case 'z':
4679fdcb4d6Skrw 			zero_partitions(&label);
4686fe57b42Smillert 			break;
4696fe57b42Smillert 
4709afbe9eeSmillert 		case '\n':
4716fe57b42Smillert 			break;
4726fe57b42Smillert 
4736fe57b42Smillert 		default:
4746fe57b42Smillert 			printf("Unknown option: %c ('?' for help)\n", *cmd);
4756fe57b42Smillert 			break;
4766fe57b42Smillert 		}
4774f3bbbf0Skrw 
4784f3bbbf0Skrw 		/*
4794f3bbbf0Skrw 		 * If no changes were made to label or mountpoints, then
4804f3bbbf0Skrw 		 * restore undo info.
4814f3bbbf0Skrw 		 */
4824f3bbbf0Skrw 		if (memcmp(&label, &lastlabel, sizeof(label)) == 0 &&
48393160b9bSkrw 		    (mpequal(mountpoints, omountpoints))) {
4844f3bbbf0Skrw 			lastlabel = tmplabel;
4854f3bbbf0Skrw 			mpcopy(omountpoints, tmpmountpoints);
4864f3bbbf0Skrw 		}
4876fe57b42Smillert 	}
4887e28fb0fSderaadt done:
4897e28fb0fSderaadt 	free(omountpoints);
4907e28fb0fSderaadt 	free(origmountpoints);
4917e28fb0fSderaadt 	free(tmpmountpoints);
4927e28fb0fSderaadt 	if (disk_geop)
4937e28fb0fSderaadt 		free(disk_geop);
4947e28fb0fSderaadt 	return(error);
4956fe57b42Smillert }
4966fe57b42Smillert 
4978dde8bb6Sotto int64_t
4988dde8bb6Sotto getphysmem(void)
4998dde8bb6Sotto {
5008dde8bb6Sotto 	int64_t physmem;
5018dde8bb6Sotto 	size_t sz = sizeof(physmem);
5028dde8bb6Sotto 	int mib[] = { CTL_HW, HW_PHYSMEM64 };
5038dde8bb6Sotto 	if (sysctl(mib, 2, &physmem, &sz, NULL, (size_t)0) == -1)
5048dde8bb6Sotto 		errx(4, "can't get mem size");
5058dde8bb6Sotto 	return physmem;
5068dde8bb6Sotto }
5078dde8bb6Sotto 
5086fe57b42Smillert /*
509557f712bSkrw  * Allocate all disk space according to standard recommendations for a
510557f712bSkrw  * root disk.
511557f712bSkrw  */
512557f712bSkrw void
5138dde8bb6Sotto editor_allocspace(struct disklabel *lp_org)
514557f712bSkrw {
5158dde8bb6Sotto 	struct disklabel *lp, label;
5168dde8bb6Sotto 	struct space_allocation *alloc;
51734ae4198Skrw 	struct space_allocation *ap;
518557f712bSkrw 	struct partition *pp;
51934ae4198Skrw 	struct diskchunk *chunks;
52034ae4198Skrw 	daddr64_t secs, chunkstart, chunksize, cylsecs, totsecs, xtrasecs;
52134ae4198Skrw 	char **partmp;
5226e312d52Sotto 	int i, j, lastalloc, index = 0, fragsize;
5238dde8bb6Sotto 	int64_t physmem;
5248dde8bb6Sotto 
52534ae4198Skrw 	/* How big is the OpenBSD portion of the disk?  */
5268dde8bb6Sotto 	find_bounds(lp_org);
527557f712bSkrw 
528fc6e9c48Sotto 	overlap = 0;
529fc6e9c48Sotto 	for (i = 0;  i < MAXPARTITIONS; i++) {
530fc6e9c48Sotto 		daddr64_t psz, pstart, pend;
531fc6e9c48Sotto 
532fc6e9c48Sotto 		pp = &lp_org->d_partitions[i];
533fc6e9c48Sotto 		psz = DL_GETPSIZE(pp);
534fc6e9c48Sotto 		pstart = DL_GETPOFFSET(pp);
535fc6e9c48Sotto 		pend = pstart + psz;
536fc6e9c48Sotto 		if (i != RAW_PART && psz != 0 &&
537fc6e9c48Sotto 		    ((pstart >= starting_sector && pstart <= ending_sector) ||
538fc6e9c48Sotto 		    (pend > starting_sector && pend < ending_sector))) {
539fc6e9c48Sotto 			overlap = 1;
540fc6e9c48Sotto 			break;
541fc6e9c48Sotto 		}
542fc6e9c48Sotto 	}
543fc6e9c48Sotto 
544fc6e9c48Sotto 	physmem = getphysmem() / lp_org->d_secsize;
545fc6e9c48Sotto 
5468dde8bb6Sotto 	cylsecs = lp_org->d_secpercyl;
5478dde8bb6Sotto again:
5488dde8bb6Sotto 	lp = &label;
5493d98fc8cSkrw 	for (i=0; i<MAXPARTITIONS; i++) {
5503d98fc8cSkrw 		free(mountpoints[i]);
5513d98fc8cSkrw 		mountpoints[i] = NULL;
5523d98fc8cSkrw 	}
5538dde8bb6Sotto 	memcpy(lp, lp_org, sizeof(struct disklabel));
5540a7398ceSderaadt 	lp->d_npartitions = MAXPARTITIONS;
5558dde8bb6Sotto 	lastalloc = alloc_table[index].sz;
5568dde8bb6Sotto 	alloc = malloc(lastalloc * sizeof(struct space_allocation));
5578dde8bb6Sotto 	if (alloc == NULL)
5588dde8bb6Sotto 		errx(4, "out of memory");
5598dde8bb6Sotto 	memcpy(alloc, alloc_table[index].table,
5608dde8bb6Sotto 	    lastalloc * sizeof(struct space_allocation));
5618dde8bb6Sotto 
5628dde8bb6Sotto 	/* bump max swap based on phys mem, little physmem gets 2x swap */
5638dde8bb6Sotto 	if (index == 0) {
5648dde8bb6Sotto 		if (physmem < 256LL * 1024 * 1024 / lp->d_secsize)
5658dde8bb6Sotto 			alloc[1].maxsz = 2 * physmem;
5668dde8bb6Sotto 		else
5678dde8bb6Sotto 			alloc[1].maxsz += physmem;
5688dde8bb6Sotto 		/* bump max /var to make room for 2 crash dumps */
5698dde8bb6Sotto 		alloc[3].maxsz += 2 * physmem;
5708dde8bb6Sotto 	}
5718dde8bb6Sotto 
57234ae4198Skrw 	xtrasecs = totsecs = editor_countfree(lp);
573557f712bSkrw 
57434ae4198Skrw 	for (i = 0; i < lastalloc; i++) {
5755f3e1104Skrw 		alloc[i].minsz = DL_BLKTOSEC(lp, alloc[i].minsz);
5765f3e1104Skrw 		alloc[i].maxsz = DL_BLKTOSEC(lp, alloc[i].maxsz);
57734ae4198Skrw 		if (xtrasecs > alloc[i].minsz)
57834ae4198Skrw 			xtrasecs -= alloc[i].minsz;
57934ae4198Skrw 		else
58034ae4198Skrw 			xtrasecs = 0;
581557f712bSkrw 	}
582557f712bSkrw 
58334ae4198Skrw 	for (i = 0; i < lastalloc; i++) {
58434ae4198Skrw 		/* Find next available partition. */
58534ae4198Skrw 		for (j = 0;  j < MAXPARTITIONS; j++)
58634ae4198Skrw 			if (DL_GETPSIZE(&lp->d_partitions[j]) == 0)
58734ae4198Skrw 				break;
58834ae4198Skrw 		if (j == MAXPARTITIONS)
58934ae4198Skrw 			return;
59034ae4198Skrw 		pp = &lp->d_partitions[j];
59134ae4198Skrw 		partmp = &mountpoints[j];
59234ae4198Skrw 		ap = &alloc[i];
593557f712bSkrw 
59434ae4198Skrw 		/* Figure out the size of the partition. */
59534ae4198Skrw 		if (i == lastalloc - 1) {
59634ae4198Skrw 			if (totsecs > ap->maxsz)
59734ae4198Skrw 				secs = ap->maxsz;
598557f712bSkrw 			else
5995f3e1104Skrw 				secs = totsecs;
6001f5ea549Skrw #ifdef SUN_CYLCHECK
6011f5ea549Skrw 			goto cylinderalign;
6021f5ea549Skrw #endif
603557f712bSkrw 		} else {
60434ae4198Skrw 			secs = ap->minsz;
6055f3e1104Skrw 			if (xtrasecs > 0)
60634ae4198Skrw 				secs += (xtrasecs / 100) * ap->rate;
60734ae4198Skrw 			if (secs > ap->maxsz)
60834ae4198Skrw 				secs = ap->maxsz;
6091f5ea549Skrw #ifdef SUN_CYLCHECK
6101f5ea549Skrw cylinderalign:
6115f3e1104Skrw 			secs = ((secs + cylsecs - 1) / cylsecs) * cylsecs;
6121f5ea549Skrw #endif
6135f3e1104Skrw 			totsecs -= secs;
6141f5ea549Skrw #ifdef SUN_CYLCHECK
6155f3e1104Skrw 			while (totsecs < 0) {
6165f3e1104Skrw 				secs -= cylsecs;
6175f3e1104Skrw 				totsecs += cylsecs;
618557f712bSkrw 			}
6191f5ea549Skrw #endif
620557f712bSkrw 		}
62134ae4198Skrw 
62234ae4198Skrw 		/* Find largest chunk of free space. */
62334ae4198Skrw 		chunks = free_chunks(lp);
62434ae4198Skrw 		chunkstart = 0;
62534ae4198Skrw 		chunksize = 0;
62634ae4198Skrw 		for (j = 0; chunks[j].start != 0 || chunks[j].stop != 0; j++)
62734ae4198Skrw 			if ((chunks[j].stop - chunks[j].start) > chunksize) {
62834ae4198Skrw 				chunkstart = chunks[j].start;
62934ae4198Skrw 				chunksize = chunks[j].stop - chunks[j].start;
630557f712bSkrw 			}
63134ae4198Skrw #ifdef SUN_CYLCHECK
63234ae4198Skrw 		if (lp->d_flags & D_VENDOR) {
63334ae4198Skrw 			/* Align chunk to cylinder boundaries. */
63434ae4198Skrw 			chunksize -= chunksize % cylsecs;
6356ab0bb66Skrw 			chunkstart = ((chunkstart + cylsecs - 1) / cylsecs) *
6366ab0bb66Skrw 			    cylsecs;
63734ae4198Skrw 		}
63834ae4198Skrw #endif
63934ae4198Skrw 		/* See if partition can fit into chunk. */
64034ae4198Skrw 		if (secs > chunksize) {
64134ae4198Skrw 			totsecs += secs - chunksize;
64234ae4198Skrw 			secs = chunksize;
64334ae4198Skrw 		}
64434ae4198Skrw 		if (secs < ap->minsz) {
6458dde8bb6Sotto 			/* It did not work out, try next strategy */
6468dde8bb6Sotto 			free(alloc);
6478dde8bb6Sotto 			if (++index < nitems(alloc_table))
6488dde8bb6Sotto 				goto again;
6498dde8bb6Sotto 			else
6508dde8bb6Sotto 				return;
651557f712bSkrw 		}
65234ae4198Skrw 
65334ae4198Skrw 		/* Everything seems ok so configure the partition. */
6545f3e1104Skrw 		DL_SETPSIZE(pp, secs);
65534ae4198Skrw 		DL_SETPOFFSET(pp, chunkstart);
6566e312d52Sotto 		fragsize = 2048;
657*c88f83bdSotto 		if (secs * lp->d_secsize > 128ULL * 1024 * 1024 * 1024)
658*c88f83bdSotto 			fragsize *= 2;
659*c88f83bdSotto 		if (secs * lp->d_secsize > 512ULL * 1024 * 1024 * 1024)
660*c88f83bdSotto 			fragsize *= 2;
661557f712bSkrw #if defined (__sparc__) && !defined(__sparc64__)
662557f712bSkrw 		/* can't boot from > 8k boot blocks */
663557f712bSkrw 		pp->p_fragblock =
6646e312d52Sotto 		    DISKLABELV1_FFS_FRAGBLOCK(i == 0 ? 1024 : fragsize, 8);
665557f712bSkrw #else
6666e312d52Sotto 		pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fragsize, 8);
667557f712bSkrw #endif
668557f712bSkrw 		pp->p_cpg = 1;
66934ae4198Skrw 		if (ap->mp[0] != '/')
67034ae4198Skrw 			pp->p_fstype = FS_SWAP;
67134ae4198Skrw 		else {
67234ae4198Skrw 			pp->p_fstype = FS_BSDFFS;
67334ae4198Skrw 			free(*partmp);
67434ae4198Skrw 			if ((*partmp = strdup(ap->mp)) == NULL)
675557f712bSkrw 				errx(4, "out of memory");
676557f712bSkrw 		}
677557f712bSkrw 	}
678557f712bSkrw 
6798dde8bb6Sotto 	free(alloc);
6808dde8bb6Sotto 	memcpy(lp_org, lp, sizeof(struct disklabel));
681557f712bSkrw }
682557f712bSkrw 
683557f712bSkrw /*
6846aaa4aabSotto  * Resize a partition, moving all subsequent partitions
6856aaa4aabSotto  */
6866aaa4aabSotto void
6876aaa4aabSotto editor_resize(struct disklabel *lp, char *p)
6886aaa4aabSotto {
6896aaa4aabSotto 	struct disklabel label;
6906aaa4aabSotto 	struct partition *pp, *prev;
6916aaa4aabSotto 	daddr64_t secs, sz, off;
6926aaa4aabSotto #ifdef SUN_CYLCHECK
6936aaa4aabSotto 	daddr64_t cylsecs;
6946aaa4aabSotto #endif
6956aaa4aabSotto 	int partno, i;
6966aaa4aabSotto 
6976aaa4aabSotto 	label = *lp;
6986aaa4aabSotto 
6996aaa4aabSotto 	/* Change which partition? */
7006aaa4aabSotto 	if (p == NULL) {
7016aaa4aabSotto 		p = getstring("partition to resize",
7026aaa4aabSotto 		    "The letter of the partition to name, a - p.", NULL);
7036aaa4aabSotto 	}
7046aaa4aabSotto 	if (p == NULL) {
7056aaa4aabSotto 		fputs("Command aborted\n", stderr);
7066aaa4aabSotto 		return;
7076aaa4aabSotto 	}
7086aaa4aabSotto 	partno = p[0] - 'a';
7096aaa4aabSotto         if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
7106aaa4aabSotto 		fprintf(stderr, "Partition must be between 'a' and '%c' "
7116aaa4aabSotto 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
7126aaa4aabSotto 		return;
7136aaa4aabSotto 	}
7146aaa4aabSotto 
7156aaa4aabSotto 	pp = &label.d_partitions[partno];
716fc6e9c48Sotto 	sz = DL_GETPSIZE(pp);
717fc6e9c48Sotto 	if (sz == 0) {
718fc6e9c48Sotto 		fputs("No such partition\n", stderr);
719fc6e9c48Sotto 		return;
720fc6e9c48Sotto 	}
721fc6e9c48Sotto 	if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP) {
722fc6e9c48Sotto 		fputs("Cannot resize spoofed partition\n", stderr);
723fc6e9c48Sotto 		return;
724fc6e9c48Sotto 	}
7256aaa4aabSotto 	secs = getuint(lp, "resize", "amount to grow (+) or shrink (-)",
726fc6e9c48Sotto 	    0, editor_countfree(lp), 0, DO_CONVERSIONS);
7276aaa4aabSotto 
728fc6e9c48Sotto 	if (secs == 0 || secs == -1) {
7296aaa4aabSotto 		fputs("Command aborted\n", stderr);
7306aaa4aabSotto 		return;
7316aaa4aabSotto 	}
7326aaa4aabSotto 
7336aaa4aabSotto #ifdef SUN_CYLCHECK
7346aaa4aabSotto 	cylsecs = lp->d_secpercyl;
7356aaa4aabSotto 	if (secs > 0)
7366aaa4aabSotto 		secs = ((secs + cylsecs - 1) / cylsecs) * cylsecs;
7376aaa4aabSotto 	else
7386aaa4aabSotto 		secs = ((secs - cylsecs + 1) / cylsecs) * cylsecs;
7396aaa4aabSotto #endif
7406aaa4aabSotto 	if (DL_GETPOFFSET(pp) + sz + secs > ending_sector) {
7416aaa4aabSotto 		fputs("Amount too big\n", stderr);
7426aaa4aabSotto 		return;
7436aaa4aabSotto 	}
7446aaa4aabSotto 	if (sz + secs < 0) {
7456aaa4aabSotto 		fputs("Amount too small\n", stderr);
7466aaa4aabSotto 		return;
7476aaa4aabSotto 	}
7486aaa4aabSotto 
7496aaa4aabSotto 	DL_SETPSIZE(pp, sz + secs);
7506aaa4aabSotto 
7516aaa4aabSotto 	/*
7526aaa4aabSotto 	 * Pack partitions above the resized partition, leaving unused
7536aaa4aabSotto 	 * partions alone.
7546aaa4aabSotto 	 */
7556aaa4aabSotto 	prev = pp;
7566aaa4aabSotto 	for (i = partno + 1; i < MAXPARTITIONS; i++) {
7576aaa4aabSotto 		if (i == RAW_PART)
7586aaa4aabSotto 			continue;
759fc6e9c48Sotto 		pp = &label.d_partitions[i];
760fc6e9c48Sotto 		if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP)
761fc6e9c48Sotto 			continue;
762fc6e9c48Sotto 		sz = DL_GETPSIZE(pp);
7636aaa4aabSotto 		if (sz == 0)
7646aaa4aabSotto 			continue;
7656aaa4aabSotto 
7666aaa4aabSotto 		off = DL_GETPOFFSET(prev) + DL_GETPSIZE(prev);
7676aaa4aabSotto 
7686aaa4aabSotto 		if (off < ending_sector) {
7696aaa4aabSotto 			DL_SETPOFFSET(pp, off);
7706aaa4aabSotto 			if (off + DL_GETPSIZE(pp) > ending_sector) {
7716aaa4aabSotto 				DL_SETPSIZE(pp, ending_sector - off);
7726aaa4aabSotto 				fprintf(stderr,
7736aaa4aabSotto 				    "Partition %c shrunk to make room\n",
7746aaa4aabSotto 				    i + 'a');
7756aaa4aabSotto 			}
7766aaa4aabSotto 		} else {
7776aaa4aabSotto 			fputs("No room left for all partitions\n", stderr);
7786aaa4aabSotto 			return;
7796aaa4aabSotto 		}
7806aaa4aabSotto 		prev = pp;
7816aaa4aabSotto 	}
7826aaa4aabSotto 	*lp = label;
7836aaa4aabSotto }
7846aaa4aabSotto 
7856aaa4aabSotto /*
7866fe57b42Smillert  * Add a new partition.
7876fe57b42Smillert  */
7886fe57b42Smillert void
78934ae4198Skrw editor_add(struct disklabel *lp, char *p)
7906fe57b42Smillert {
79196a888c6Smillert 	struct partition *pp;
79296a888c6Smillert 	struct diskchunk *chunks;
7935caa08b2Skrw 	char buf[2];
7946e312d52Sotto 	int i, partno, fragsize;
795f8ab7229Schl 	u_int64_t freesectors, new_offset, new_size;
7969fdcb4d6Skrw 
7979fdcb4d6Skrw 	freesectors = editor_countfree(lp);
7986fe57b42Smillert 
7996fe57b42Smillert 	/* XXX - prompt user to steal space from another partition instead */
800fc1a4cc6Sderaadt #ifdef SUN_CYLCHECK
8019fdcb4d6Skrw 	if ((lp->d_flags & D_VENDOR) && freesectors < lp->d_secpercyl) {
802fc1a4cc6Sderaadt 		fputs("No space left, you need to shrink a partition "
803fc1a4cc6Sderaadt 		    "(need at least one full cylinder)\n",
804fc1a4cc6Sderaadt 		    stderr);
805fc1a4cc6Sderaadt 		return;
806fc1a4cc6Sderaadt 	}
8078390cf28Smillert #endif
8089fdcb4d6Skrw 	if (freesectors == 0) {
8096fe57b42Smillert 		fputs("No space left, you need to shrink a partition\n",
8106fe57b42Smillert 		    stderr);
8116fe57b42Smillert 		return;
8126fe57b42Smillert 	}
8136fe57b42Smillert 
8145caa08b2Skrw 	if (p == NULL) {
8155caa08b2Skrw 		/*
8165caa08b2Skrw 		 * Use the first unused partition that is not 'c' as the
8175caa08b2Skrw 		 * default partition in the prompt string.
8185caa08b2Skrw 		 */
8195caa08b2Skrw 		pp = &lp->d_partitions[0];
8205caa08b2Skrw 		buf[0] = buf[1] = '\0';
8215caa08b2Skrw 		for (partno = 0; partno < MAXPARTITIONS; partno++, pp++) {
8225caa08b2Skrw 			if (DL_GETPSIZE(pp) == 0 && partno != RAW_PART) {
8235caa08b2Skrw 				buf[0] = partno + 'a';
8245caa08b2Skrw 				p = &buf[0];
8256fe57b42Smillert 				break;
8266fe57b42Smillert 			}
8275caa08b2Skrw 		}
828c33fcabaSmillert 		p = getstring("partition",
8296fe57b42Smillert 		    "The letter of the new partition, a - p.", p);
8306fe57b42Smillert 	}
8315caa08b2Skrw 	if (p == NULL) {
8325caa08b2Skrw 		fputs("Command aborted\n", stderr);
8335caa08b2Skrw 		return;
8345caa08b2Skrw 	}
8355caa08b2Skrw 	partno = p[0] - 'a';
8365caa08b2Skrw 	if (partno < 0 || partno == RAW_PART || partno >= MAXPARTITIONS) {
8375caa08b2Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
8385caa08b2Skrw 		    "(excluding 'c').\n", 'a' + MAXPARTITIONS - 1);
8395caa08b2Skrw 		return;
8405caa08b2Skrw 	}
8415caa08b2Skrw 	pp = &lp->d_partitions[partno];
8425caa08b2Skrw 
8435caa08b2Skrw 	if (pp->p_fstype != FS_UNUSED && DL_GETPSIZE(pp) != 0) {
8445caa08b2Skrw 		fprintf(stderr, "Partition '%c' exists.  Delete it first.\n",
8455caa08b2Skrw 		    p[0]);
8465caa08b2Skrw 		return;
8476fe57b42Smillert 	}
84896a888c6Smillert 
849caf41f96Skrw 	/*
850caf41f96Skrw 	 * Increase d_npartitions if necessary. Ensure all new partitions are
851855d4e83Ssobrado 	 * zero'ed to avoid inadvertent overlaps.
852caf41f96Skrw 	 */
853caf41f96Skrw 	for(; lp->d_npartitions <= partno; lp->d_npartitions++)
854caf41f96Skrw 		memset(&lp->d_partitions[lp->d_npartitions], 0, sizeof(*pp));
85596a888c6Smillert 
85689f4601dSkrw 	/* Make sure selected partition is zero'd too. */
85789f4601dSkrw 	memset(pp, 0, sizeof(*pp));
85815c15d8aSkrw 	chunks = free_chunks(lp);
85915c15d8aSkrw 
86015c15d8aSkrw 	/*
86115c15d8aSkrw 	 * Since we know there's free space, there must be at least one
86215c15d8aSkrw 	 * chunk. So find the largest chunk and assume we want to add the
86315c15d8aSkrw 	 * partition in that free space.
86415c15d8aSkrw 	 */
86515c15d8aSkrw 	new_size = new_offset = 0;
86615c15d8aSkrw 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
86715c15d8aSkrw 		if (chunks[i].stop - chunks[i].start > new_size) {
86815c15d8aSkrw 		    new_size = chunks[i].stop - chunks[i].start;
86915c15d8aSkrw 		    new_offset = chunks[i].start;
87015c15d8aSkrw 		}
87115c15d8aSkrw 	}
8721e0ad43cSotto 	DL_SETPSIZE(pp, new_size);
8731e0ad43cSotto 	DL_SETPOFFSET(pp, new_offset);
87496a888c6Smillert 	pp->p_fstype = partno == 1 ? FS_SWAP : FS_BSDFFS;
875*c88f83bdSotto 	pp->p_cpg = 1;
876*c88f83bdSotto 
877*c88f83bdSotto 	if (get_offset(lp, partno) == 0 &&
878*c88f83bdSotto 	    get_size(lp, partno) == 0) {
8796e312d52Sotto 		fragsize = 2048;
880*c88f83bdSotto 		new_size = DL_GETPSIZE(pp) * lp->d_secsize;
881*c88f83bdSotto 		if (new_size > 128ULL * 1024 * 1024 * 1024)
882*c88f83bdSotto 			fragsize *= 2;
883*c88f83bdSotto 		if (new_size > 512ULL * 1024 * 1024 * 1024)
884*c88f83bdSotto 			fragsize *= 2;
8854a8b9208Stedu #if defined (__sparc__) && !defined(__sparc64__)
886d98d4df7Stedu 		/* can't boot from > 8k boot blocks */
887ddfcbf38Sotto 		pp->p_fragblock =
8886e312d52Sotto 		    DISKLABELV1_FFS_FRAGBLOCK(partno == 0 ? 1024 : fragsize, 8);
889d98d4df7Stedu #else
8906e312d52Sotto 		pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fragsize, 8);
891d98d4df7Stedu #endif
892*c88f83bdSotto 		if (get_fstype(lp, partno) == 0 &&
89334ae4198Skrw 		    get_mp(lp, partno) == 0 &&
894a4c87e64Skrw 		    get_fsize(lp, partno) == 0  &&
895a4c87e64Skrw 		    get_bsize(lp, partno) == 0)
89696a888c6Smillert 			return;
897*c88f83bdSotto 	}
898a4c87e64Skrw 	/* Bailed out at some point, so effectively delete the partition. */
899a4c87e64Skrw 	DL_SETPSIZE(pp, 0);
9006fe57b42Smillert }
9016fe57b42Smillert 
9026fe57b42Smillert /*
903bd6726faSmillert  * Set the mountpoint of an existing partition ('name').
904bd6726faSmillert  */
905bd6726faSmillert void
90634ae4198Skrw editor_name(struct disklabel *lp, char *p)
907bd6726faSmillert {
908bd6726faSmillert 	struct partition *pp;
909bd6726faSmillert 	int partno;
910bd6726faSmillert 
911bd6726faSmillert 	/* Change which partition? */
912bd6726faSmillert 	if (p == NULL) {
913c33fcabaSmillert 		p = getstring("partition to name",
914bd6726faSmillert 		    "The letter of the partition to name, a - p.", NULL);
915bd6726faSmillert 	}
916bd6726faSmillert 	if (p == NULL) {
917bd6726faSmillert 		fputs("Command aborted\n", stderr);
918bd6726faSmillert 		return;
919bd6726faSmillert 	}
920bd6726faSmillert 	partno = p[0] - 'a';
9216c729bd1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
9226c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
9236c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
924bd6726faSmillert 		return;
92566df1f0cSkrw 	}
92666df1f0cSkrw 	pp = &lp->d_partitions[partno];
92766df1f0cSkrw 
92866df1f0cSkrw 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
92966df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
930bd6726faSmillert 		return;
931bd6726faSmillert 	}
932bd6726faSmillert 
933bd6726faSmillert 	/* Not all fstypes can be named */
934bd6726faSmillert 	if (pp->p_fstype == FS_UNUSED || pp->p_fstype == FS_SWAP ||
935bd387921Stodd 	    pp->p_fstype == FS_BOOT || pp->p_fstype == FS_OTHER ||
936bd387921Stodd 	    pp->p_fstype == FS_RAID) {
937bd6726faSmillert 		fprintf(stderr, "You cannot name a filesystem of type %s.\n",
938baa55472Smillert 		    fstypenames[lp->d_partitions[partno].p_fstype]);
939bd6726faSmillert 		return;
940bd6726faSmillert 	}
941bd6726faSmillert 
94234ae4198Skrw 	get_mp(lp, partno);
943bd6726faSmillert }
944bd6726faSmillert 
945bd6726faSmillert /*
9466fe57b42Smillert  * Change an existing partition.
9476fe57b42Smillert  */
9486fe57b42Smillert void
94934ae4198Skrw editor_modify(struct disklabel *lp, char *p)
9506fe57b42Smillert {
9516fe57b42Smillert 	struct partition origpart, *pp;
952f8ab7229Schl 	int partno;
9536fe57b42Smillert 
9546fe57b42Smillert 	/* Change which partition? */
9556fe57b42Smillert 	if (p == NULL) {
956c33fcabaSmillert 		p = getstring("partition to modify",
9576fe57b42Smillert 		    "The letter of the partition to modify, a - p.", NULL);
9586fe57b42Smillert 	}
95996a888c6Smillert 	if (p == NULL) {
96096a888c6Smillert 		fputs("Command aborted\n", stderr);
96196a888c6Smillert 		return;
96296a888c6Smillert 	}
9636fe57b42Smillert 	partno = p[0] - 'a';
9646c729bd1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
9656c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
9666c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
9676fe57b42Smillert 		return;
96866df1f0cSkrw 	}
96966df1f0cSkrw 	pp = &lp->d_partitions[partno];
97066df1f0cSkrw 
97166df1f0cSkrw 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
97266df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
9736fe57b42Smillert 		return;
9746fe57b42Smillert 	}
9756fe57b42Smillert 
97666df1f0cSkrw 	origpart = *pp;
97766df1f0cSkrw 
978a4c87e64Skrw 	if (get_offset(lp, partno) == 0 &&
979a4c87e64Skrw 	    get_size(lp, partno) == 0   &&
980a4c87e64Skrw 	    get_fstype(lp, partno) == 0 &&
98134ae4198Skrw 	    get_mp(lp, partno) == 0 &&
982a4c87e64Skrw 	    get_fsize(lp, partno) == 0  &&
983a4c87e64Skrw 	    get_bsize(lp, partno) == 0)
98496a888c6Smillert 		return;
9856fe57b42Smillert 
986a4c87e64Skrw 	/* Bailed out at some point, so undo any changes. */
987a4c87e64Skrw 	*pp = origpart;
9886fe57b42Smillert }
9896fe57b42Smillert 
9906fe57b42Smillert /*
9916fe57b42Smillert  * Delete an existing partition.
9926fe57b42Smillert  */
9936fe57b42Smillert void
99434ae4198Skrw editor_delete(struct disklabel *lp, char *p)
9956fe57b42Smillert {
99666df1f0cSkrw 	struct partition *pp;
997135c90d1Skrw 	int partno;
9986fe57b42Smillert 
9996fe57b42Smillert 	if (p == NULL) {
1000c33fcabaSmillert 		p = getstring("partition to delete",
1001945ae268Smillert 		    "The letter of the partition to delete, a - p, or '*'.",
1002945ae268Smillert 		    NULL);
10036fe57b42Smillert 	}
100496a888c6Smillert 	if (p == NULL) {
100596a888c6Smillert 		fputs("Command aborted\n", stderr);
100696a888c6Smillert 		return;
100796a888c6Smillert 	}
1008945ae268Smillert 	if (p[0] == '*') {
10099fdcb4d6Skrw 		zero_partitions(lp);
1010945ae268Smillert 		return;
1011945ae268Smillert 	}
1012135c90d1Skrw 	partno = p[0] - 'a';
1013135c90d1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
10146c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
10156c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
101633262abfSmiod 		return;
101766df1f0cSkrw 	}
1018135c90d1Skrw 	pp = &lp->d_partitions[partno];
101966df1f0cSkrw 
102066df1f0cSkrw 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
102166df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
102233262abfSmiod 		return;
102366df1f0cSkrw 	}
102466df1f0cSkrw 
10256fe57b42Smillert 	/* Really delete it (as opposed to just setting to "unused") */
1026135c90d1Skrw 	memset(pp, 0, sizeof(*pp));
102734ae4198Skrw 	free(mountpoints[partno]);
102834ae4198Skrw 	mountpoints[partno] = NULL;
10296fe57b42Smillert }
10306fe57b42Smillert 
10316fe57b42Smillert /*
10326fe57b42Smillert  * Change the size of an existing partition.
10336fe57b42Smillert  */
10346fe57b42Smillert void
10359fdcb4d6Skrw editor_change(struct disklabel *lp, char *p)
10366fe57b42Smillert {
10374b9a3bdaSmillert 	struct partition *pp;
103866df1f0cSkrw 	int partno;
10396fe57b42Smillert 
10406fe57b42Smillert 	if (p == NULL) {
1041c33fcabaSmillert 		p = getstring("partition to change size",
10426fe57b42Smillert 		    "The letter of the partition to change size, a - p.", NULL);
10436fe57b42Smillert 	}
104496a888c6Smillert 	if (p == NULL) {
104596a888c6Smillert 		fputs("Command aborted\n", stderr);
104696a888c6Smillert 		return;
104796a888c6Smillert 	}
10486fe57b42Smillert 	partno = p[0] - 'a';
10496c729bd1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
10506c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
10516c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
10526fe57b42Smillert 		return;
10536fe57b42Smillert 	}
10544b9a3bdaSmillert 	pp = &lp->d_partitions[partno];
10556fe57b42Smillert 
105666df1f0cSkrw 	if (DL_GETPSIZE(pp) == 0) {
105766df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
105866df1f0cSkrw 		return;
105966df1f0cSkrw 	}
106066df1f0cSkrw 
106114192793Skrw 	printf("Partition %c is currently %llu sectors in size, and can have "
106214192793Skrw 	    "a maximum\nsize of %llu sectors.\n",
106314192793Skrw 	    p[0], DL_GETPSIZE(pp), max_partition_size(lp, partno));
10647da73705Skrw 
106559ccf790Skrw 	/* Get new size */
10669fdcb4d6Skrw 	get_size(lp, partno);
10676fe57b42Smillert }
10686fe57b42Smillert 
10696fe57b42Smillert /*
10706fe57b42Smillert  * Sort the partitions based on starting offset.
10716fe57b42Smillert  * This assumes there can be no overlap.
10726fe57b42Smillert  */
10736fe57b42Smillert int
10748809fabbSderaadt partition_cmp(const void *e1, const void *e2)
10756fe57b42Smillert {
10766fe57b42Smillert 	struct partition *p1 = *(struct partition **)e1;
10776fe57b42Smillert 	struct partition *p2 = *(struct partition **)e2;
10781e0ad43cSotto 	u_int64_t o1 = DL_GETPOFFSET(p1);
10791e0ad43cSotto 	u_int64_t o2 = DL_GETPOFFSET(p2);
10806fe57b42Smillert 
10811e0ad43cSotto 	if (o1 < o2)
1082651d5bd9Sotto 		return -1;
10831e0ad43cSotto 	else if (o1 > o2)
1084651d5bd9Sotto 		return 1;
1085651d5bd9Sotto 	else
1086651d5bd9Sotto 		return 0;
10876fe57b42Smillert }
10886fe57b42Smillert 
10896fe57b42Smillert char *
10908809fabbSderaadt getstring(char *prompt, char *helpstring, char *oval)
10916fe57b42Smillert {
10926fe57b42Smillert 	static char buf[BUFSIZ];
10936fe57b42Smillert 	int n;
10946fe57b42Smillert 
10956fe57b42Smillert 	buf[0] = '\0';
10966fe57b42Smillert 	do {
10976fe57b42Smillert 		printf("%s: [%s] ", prompt, oval ? oval : "");
10986e0becc5Smillert 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
1099260513deSmillert 			buf[0] = '\0';
110096a888c6Smillert 			if (feof(stdin)) {
110124c6582eSmillert 				clearerr(stdin);
110296a888c6Smillert 				putchar('\n');
110396a888c6Smillert 				return(NULL);
110496a888c6Smillert 			}
11056e0becc5Smillert 		}
11066fe57b42Smillert 		n = strlen(buf);
11076fe57b42Smillert 		if (n > 0 && buf[n-1] == '\n')
11086fe57b42Smillert 			buf[--n] = '\0';
11096fe57b42Smillert 		if (buf[0] == '?')
11106fe57b42Smillert 			puts(helpstring);
11114fb6ab7cSmillert 		else if (oval != NULL && buf[0] == '\0')
11124fb6ab7cSmillert 			strlcpy(buf, oval, sizeof(buf));
11136fe57b42Smillert 	} while (buf[0] == '?');
11146fe57b42Smillert 
11156fe57b42Smillert 	return(&buf[0]);
11166fe57b42Smillert }
11176fe57b42Smillert 
11186fe57b42Smillert /*
11191e0ad43cSotto  * Returns ULLONG_MAX on error
112024a2c1a4Smillert  * Usually only called by helper functions.
11216fe57b42Smillert  */
11221e0ad43cSotto u_int64_t
1123dea75673Skrw getuint(struct disklabel *lp, char *prompt, char *helpstring,
11241e0ad43cSotto     u_int64_t oval, u_int64_t maxval, u_int64_t offset, int flags)
11256fe57b42Smillert {
11266fe57b42Smillert 	char buf[BUFSIZ], *endptr, *p, operator = '\0';
11271e0ad43cSotto 	u_int64_t rval = oval;
11286fe57b42Smillert 	size_t n;
11296fe57b42Smillert 	int mult = 1;
113014cc915fSmillert 	double d, percent = 1.0;
11316fe57b42Smillert 
11324b9a3bdaSmillert 	/* We only care about the remainder */
11334b9a3bdaSmillert 	offset = offset % lp->d_secpercyl;
11344b9a3bdaSmillert 
11356fe57b42Smillert 	buf[0] = '\0';
11366fe57b42Smillert 	do {
11371e0ad43cSotto 		printf("%s: [%llu] ", prompt, oval);
11386e0becc5Smillert 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
11396e0becc5Smillert 			buf[0] = '\0';
114096a888c6Smillert 			if (feof(stdin)) {
114124c6582eSmillert 				clearerr(stdin);
114296a888c6Smillert 				putchar('\n');
11431e0ad43cSotto 				return(ULLONG_MAX - 1);
114496a888c6Smillert 			}
11456e0becc5Smillert 		}
11466fe57b42Smillert 		n = strlen(buf);
11476fe57b42Smillert 		if (n > 0 && buf[n-1] == '\n')
11486fe57b42Smillert 			buf[--n] = '\0';
11496fe57b42Smillert 		if (buf[0] == '?')
11506fe57b42Smillert 			puts(helpstring);
11516fe57b42Smillert 	} while (buf[0] == '?');
11526fe57b42Smillert 
11536fe57b42Smillert 	if (buf[0] == '*' && buf[1] == '\0') {
11546fe57b42Smillert 		rval = maxval;
11556fe57b42Smillert 	} else {
11566fe57b42Smillert 		/* deal with units */
11576fe57b42Smillert 		if (buf[0] != '\0' && n > 0) {
11586fe57b42Smillert 			if ((flags & DO_CONVERSIONS)) {
115996a888c6Smillert 				switch (tolower(buf[n-1])) {
11606fe57b42Smillert 
11616fe57b42Smillert 				case 'c':
11626fe57b42Smillert 					mult = lp->d_secpercyl;
11636fe57b42Smillert 					buf[--n] = '\0';
11646fe57b42Smillert 					break;
11656fe57b42Smillert 				case 'b':
11666fe57b42Smillert 					mult = -lp->d_secsize;
11676fe57b42Smillert 					buf[--n] = '\0';
11686fe57b42Smillert 					break;
11696fe57b42Smillert 				case 'k':
117050c0f47aSkrw 					if (lp->d_secsize > 1024)
117150c0f47aSkrw 						mult = -lp->d_secsize / 1024;
117250c0f47aSkrw 					else
11736fe57b42Smillert 						mult = 1024 / lp->d_secsize;
11746fe57b42Smillert 					buf[--n] = '\0';
11756fe57b42Smillert 					break;
11766fe57b42Smillert 				case 'm':
11776fe57b42Smillert 					mult = 1048576 / lp->d_secsize;
11786fe57b42Smillert 					buf[--n] = '\0';
11796fe57b42Smillert 					break;
11801a51a1eeSmillert 				case 'g':
11811a51a1eeSmillert 					mult = 1073741824 / lp->d_secsize;
11821a51a1eeSmillert 					buf[--n] = '\0';
11831a51a1eeSmillert 					break;
118414cc915fSmillert 				case '%':
118514cc915fSmillert 					buf[--n] = '\0';
118614cc915fSmillert 					percent = strtod(buf, NULL) / 100.0;
11871e0ad43cSotto 					snprintf(buf, sizeof(buf), "%lld",
11881e0ad43cSotto 					    DL_GETDSIZE(lp));
118914cc915fSmillert 					break;
119014cc915fSmillert 				case '&':
119114cc915fSmillert 					buf[--n] = '\0';
119214cc915fSmillert 					percent = strtod(buf, NULL) / 100.0;
11931e0ad43cSotto 					snprintf(buf, sizeof(buf), "%lld",
119414cc915fSmillert 					    maxval);
119514cc915fSmillert 					break;
11966fe57b42Smillert 				}
119796a888c6Smillert 			}
11986fe57b42Smillert 
11996fe57b42Smillert 			/* Did they give us an operator? */
12006fe57b42Smillert 			p = &buf[0];
12016fe57b42Smillert 			if (*p == '+' || *p == '-')
12026fe57b42Smillert 				operator = *p++;
12036fe57b42Smillert 
12046fe57b42Smillert 			endptr = p;
120596a888c6Smillert 			errno = 0;
120696a888c6Smillert 			d = strtod(p, &endptr);
120796a888c6Smillert 			if (errno == ERANGE)
12081e0ad43cSotto 				rval = ULLONG_MAX;	/* too big/small */
120996a888c6Smillert 			else if (*endptr != '\0') {
12106fe57b42Smillert 				errno = EINVAL;		/* non-numbers in str */
12111e0ad43cSotto 				rval = ULLONG_MAX;
12126fe57b42Smillert 			} else {
121396a888c6Smillert 				/* XXX - should check for overflow */
121496a888c6Smillert 				if (mult > 0)
121514cc915fSmillert 					rval = d * mult * percent;
121696a888c6Smillert 				else
121796a888c6Smillert 					/* Negative mult means divide (fancy) */
121814cc915fSmillert 					rval = d / (-mult) * percent;
12196fe57b42Smillert 
122096a888c6Smillert 				/* Apply the operator */
12216fe57b42Smillert 				if (operator == '+')
12226fe57b42Smillert 					rval += oval;
12236fe57b42Smillert 				else if (operator == '-')
12246fe57b42Smillert 					rval = oval - rval;
12256fe57b42Smillert 			}
12266fe57b42Smillert 		}
12276fe57b42Smillert 	}
12288390cf28Smillert 	if ((flags & DO_ROUNDING) && rval != ULLONG_MAX) {
122996a888c6Smillert 		/* Round to nearest cylinder unless given in sectors */
12308390cf28Smillert 		if (
1231fc1a4cc6Sderaadt #ifdef SUN_CYLCHECK
1232fc1a4cc6Sderaadt 		    ((lp->d_flags & D_VENDOR) || mult != 1) &&
1233fc1a4cc6Sderaadt #else
12348390cf28Smillert 		    mult != 1 &&
1235dbffb156Smillert #endif
12368390cf28Smillert 		    (rval + offset) % lp->d_secpercyl != 0) {
12371e0ad43cSotto 			u_int64_t cyls;
1238dbffb156Smillert 
12398390cf28Smillert 			/* Round to higher cylinder but no more than maxval */
12408390cf28Smillert 			cyls = (rval / lp->d_secpercyl) + 1;
12418390cf28Smillert 			if ((cyls * lp->d_secpercyl) - offset > maxval)
1242dbffb156Smillert 				cyls--;
12434b9a3bdaSmillert 			rval = (cyls * lp->d_secpercyl) - offset;
12448390cf28Smillert 			printf("Rounding to cylinder: %llu\n", rval);
12456fe57b42Smillert 		}
12464b9a3bdaSmillert 	}
12476fe57b42Smillert 
12486fe57b42Smillert 	return(rval);
12496fe57b42Smillert }
12506fe57b42Smillert 
12516fe57b42Smillert /*
12521f0f871dSkrw  * Check for partition overlap in lp and prompt the user to resolve the overlap
12531f0f871dSkrw  * if any is found.  Returns 1 if unable to resolve, else 0.
12546fe57b42Smillert  */
12556fe57b42Smillert int
12561f0f871dSkrw has_overlap(struct disklabel *lp)
12576fe57b42Smillert {
12586fe57b42Smillert 	struct partition **spp;
1259e6aa8bafSmillert 	int c, i, j;
1260e6aa8bafSmillert 	char buf[BUFSIZ];
12616fe57b42Smillert 
12620fbd3c97Skrw 	/* Get a sorted list of the in-use partitions. */
12630fbd3c97Skrw 	spp = sort_partitions(lp);
12646fe57b42Smillert 
12650fbd3c97Skrw 	/* If there are less than two partitions in use, there is no overlap. */
12660fbd3c97Skrw 	if (spp[1] == NULL)
12670fbd3c97Skrw 		return(0);
12686fe57b42Smillert 
12696fe57b42Smillert 	/* Now that we have things sorted by starting sector check overlap */
12700fbd3c97Skrw 	for (i = 0; spp[i] != NULL; i++) {
12710fbd3c97Skrw 		for (j = i + 1; spp[j] != NULL; j++) {
12726fe57b42Smillert 			/* `if last_sec_in_part + 1 > first_sec_in_next_part' */
12731e0ad43cSotto 			if (DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]) > DL_GETPOFFSET(spp[j])) {
12746fe57b42Smillert 				/* Overlap!  Convert to real part numbers. */
12756fe57b42Smillert 				i = ((char *)spp[i] - (char *)lp->d_partitions)
12766fe57b42Smillert 				    / sizeof(**spp);
12776fe57b42Smillert 				j = ((char *)spp[j] - (char *)lp->d_partitions)
12786fe57b42Smillert 				    / sizeof(**spp);
12796fe57b42Smillert 				printf("\nError, partitions %c and %c overlap:\n",
12806fe57b42Smillert 				    'a' + i, 'a' + j);
1281366bf641Skrw 				printf("#    %16.16s %16.16s  fstype "
1282651d5bd9Sotto 				    "[fsize bsize  cpg]\n", "size", "offset");
128334ae4198Skrw 				display_partition(stdout, lp, i, 0);
128434ae4198Skrw 				display_partition(stdout, lp, j, 0);
12856fe57b42Smillert 
1286e6aa8bafSmillert 				/* Get partition to disable or ^D */
1287e6aa8bafSmillert 				do {
1288616cd1c4Smillert 					printf("Disable which one? (^D to abort) [%c %c] ",
12896fe57b42Smillert 					    'a' + i, 'a' + j);
1290e6aa8bafSmillert 					buf[0] = '\0';
1291616cd1c4Smillert 					if (!fgets(buf, sizeof(buf), stdin)) {
1292616cd1c4Smillert 						putchar('\n');
1293e6aa8bafSmillert 						return(1);	/* ^D */
1294616cd1c4Smillert 					}
1295e6aa8bafSmillert 					c = buf[0] - 'a';
1296e6aa8bafSmillert 				} while (buf[1] != '\n' && buf[1] != '\0' &&
1297e6aa8bafSmillert 				    c != i && c != j);
1298e6aa8bafSmillert 
1299e6aa8bafSmillert 				/* Mark the selected one as unused */
13006fe57b42Smillert 				lp->d_partitions[c].p_fstype = FS_UNUSED;
13011f0f871dSkrw 				return (has_overlap(lp));
13026fe57b42Smillert 			}
13036fe57b42Smillert 		}
13046fe57b42Smillert 	}
1305f0b4d0a9Smillert 
1306e6aa8bafSmillert 	return(0);
13076fe57b42Smillert }
13086fe57b42Smillert 
13096fe57b42Smillert void
13109fdcb4d6Skrw edit_parms(struct disklabel *lp)
13116fe57b42Smillert {
13126fe57b42Smillert 	char *p;
13139fdcb4d6Skrw 	u_int64_t freesectors, ui;
131496a888c6Smillert 	struct disklabel oldlabel = *lp;
13156fe57b42Smillert 
1316ea37abd3Sderaadt 	printf("Changing device parameters for %s:\n", specname);
13176fe57b42Smillert 
13180f820bbbSmillert 	/* disk type */
13190f820bbbSmillert 	for (;;) {
1320c33fcabaSmillert 		p = getstring("disk type",
132141282a2aSmillert 		    "What kind of disk is this?  Usually SCSI, ESDI, ST506, or "
132241282a2aSmillert 		    "floppy (use ESDI for IDE).", dktypenames[lp->d_type]);
132396a888c6Smillert 		if (p == NULL) {
132496a888c6Smillert 			fputs("Command aborted\n", stderr);
132596a888c6Smillert 			return;
132696a888c6Smillert 		}
132741282a2aSmillert 		if (strcasecmp(p, "IDE") == 0)
132841282a2aSmillert 			ui = DTYPE_ESDI;
132941282a2aSmillert 		else
133041282a2aSmillert 			for (ui = 1; ui < DKMAXTYPES &&
133141282a2aSmillert 			    strcasecmp(p, dktypenames[ui]); ui++)
13320f820bbbSmillert 				;
13330f820bbbSmillert 		if (ui < DKMAXTYPES) {
13340f820bbbSmillert 			break;
13350f820bbbSmillert 		} else {
13360f820bbbSmillert 			printf("\"%s\" is not a valid disk type.\n", p);
13370f820bbbSmillert 			fputs("Valid types are: ", stdout);
13380f820bbbSmillert 			for (ui = 1; ui < DKMAXTYPES; ui++) {
13390f820bbbSmillert 				printf("\"%s\"", dktypenames[ui]);
13400f820bbbSmillert 				if (ui < DKMAXTYPES - 1)
13410f820bbbSmillert 					fputs(", ", stdout);
13420f820bbbSmillert 			}
13430f820bbbSmillert 			putchar('\n');
13440f820bbbSmillert 		}
13450f820bbbSmillert 	}
13460f820bbbSmillert 	lp->d_type = ui;
13470f820bbbSmillert 
13486fe57b42Smillert 	/* pack/label id */
1349c33fcabaSmillert 	p = getstring("label name",
13506fe57b42Smillert 	    "15 char string that describes this label, usually the disk name.",
13516fe57b42Smillert 	    lp->d_packname);
135296a888c6Smillert 	if (p == NULL) {
135396a888c6Smillert 		fputs("Command aborted\n", stderr);
135496a888c6Smillert 		*lp = oldlabel;		/* undo damage */
135596a888c6Smillert 		return;
135696a888c6Smillert 	}
13574fb6ab7cSmillert 	strncpy(lp->d_packname, p, sizeof(lp->d_packname));	/* checked */
13586fe57b42Smillert 
13596fe57b42Smillert 	/* sectors/track */
13606fe57b42Smillert 	for (;;) {
1361dea75673Skrw 		ui = getuint(lp, "sectors/track",
1362cfd24250Skrw 		    "The Number of sectors per track.", lp->d_nsectors,
13634b9a3bdaSmillert 		    lp->d_nsectors, 0, 0);
13641e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
136596a888c6Smillert 			fputs("Command aborted\n", stderr);
136696a888c6Smillert 			*lp = oldlabel;		/* undo damage */
136796a888c6Smillert 			return;
13681e0ad43cSotto 		} if (ui == ULLONG_MAX)
13696fe57b42Smillert 			fputs("Invalid entry\n", stderr);
13706fe57b42Smillert 		else
13716fe57b42Smillert 			break;
13726fe57b42Smillert 	}
13736fe57b42Smillert 	lp->d_nsectors = ui;
13746fe57b42Smillert 
13756fe57b42Smillert 	/* tracks/cylinder */
13766fe57b42Smillert 	for (;;) {
1377dea75673Skrw 		ui = getuint(lp, "tracks/cylinder",
13786fe57b42Smillert 		    "The number of tracks per cylinder.", lp->d_ntracks,
13794b9a3bdaSmillert 		    lp->d_ntracks, 0, 0);
13801e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
138196a888c6Smillert 			fputs("Command aborted\n", stderr);
138296a888c6Smillert 			*lp = oldlabel;		/* undo damage */
138396a888c6Smillert 			return;
13841e0ad43cSotto 		} else if (ui == ULLONG_MAX)
13856fe57b42Smillert 			fputs("Invalid entry\n", stderr);
13866fe57b42Smillert 		else
13876fe57b42Smillert 			break;
13886fe57b42Smillert 	}
13896fe57b42Smillert 	lp->d_ntracks = ui;
13906fe57b42Smillert 
13916fe57b42Smillert 	/* sectors/cylinder */
1392148b6188Smillert 	for (;;) {
1393dea75673Skrw 		ui = getuint(lp, "sectors/cylinder",
1394148b6188Smillert 		    "The number of sectors per cylinder (Usually sectors/track "
13954b9a3bdaSmillert 		    "* tracks/cylinder).", lp->d_secpercyl, lp->d_secpercyl,
13964b9a3bdaSmillert 		    0, 0);
13971e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
139896a888c6Smillert 			fputs("Command aborted\n", stderr);
139996a888c6Smillert 			*lp = oldlabel;		/* undo damage */
140096a888c6Smillert 			return;
14011e0ad43cSotto 		} else if (ui == ULLONG_MAX)
1402148b6188Smillert 			fputs("Invalid entry\n", stderr);
1403148b6188Smillert 		else
1404148b6188Smillert 			break;
1405148b6188Smillert 	}
1406148b6188Smillert 	lp->d_secpercyl = ui;
14076fe57b42Smillert 
14086fe57b42Smillert 	/* number of cylinders */
14096fe57b42Smillert 	for (;;) {
1410dea75673Skrw 		ui = getuint(lp, "number of cylinders",
14116fe57b42Smillert 		    "The total number of cylinders on the disk.",
14124b9a3bdaSmillert 		    lp->d_ncylinders, lp->d_ncylinders, 0, 0);
14131e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
141496a888c6Smillert 			fputs("Command aborted\n", stderr);
141596a888c6Smillert 			*lp = oldlabel;		/* undo damage */
141696a888c6Smillert 			return;
14171e0ad43cSotto 		} else if (ui == ULLONG_MAX)
14186fe57b42Smillert 			fputs("Invalid entry\n", stderr);
14196fe57b42Smillert 		else
14206fe57b42Smillert 			break;
14216fe57b42Smillert 	}
14226fe57b42Smillert 	lp->d_ncylinders = ui;
14236fe57b42Smillert 
14246fe57b42Smillert 	/* total sectors */
14256fe57b42Smillert 	for (;;) {
142634af67a3Sotto 		u_int64_t nsec = MAX(DL_GETDSIZE(lp),
142734af67a3Sotto 		    (u_int64_t)lp->d_ncylinders * lp->d_secpercyl);
1428dea75673Skrw 		ui = getuint(lp, "total sectors",
14296fe57b42Smillert 		    "The total number of sectors on the disk.",
1430baaa8969Smillert 		    nsec, nsec, 0, 0);
14311e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
143296a888c6Smillert 			fputs("Command aborted\n", stderr);
143396a888c6Smillert 			*lp = oldlabel;		/* undo damage */
143496a888c6Smillert 			return;
14351e0ad43cSotto 		} else if (ui == ULLONG_MAX)
14366fe57b42Smillert 			fputs("Invalid entry\n", stderr);
14371e0ad43cSotto 		else if (ui > DL_GETDSIZE(lp) &&
14381e0ad43cSotto 		    ending_sector == DL_GETDSIZE(lp)) {
1439f98aebd4Smillert 			puts("You may want to increase the size of the 'c' "
1440f98aebd4Smillert 			    "partition.");
14416fe57b42Smillert 			break;
14421e0ad43cSotto 		} else if (ui < DL_GETDSIZE(lp) &&
14431e0ad43cSotto 		    ending_sector == DL_GETDSIZE(lp)) {
14446fe57b42Smillert 			/* shrink free count */
14459fdcb4d6Skrw 			freesectors = editor_countfree(lp);
14469fdcb4d6Skrw 			if (DL_GETDSIZE(lp) - ui > freesectors)
14476fe57b42Smillert 				fprintf(stderr,
14481e0ad43cSotto 				    "Not enough free space to shrink by %llu "
14491e0ad43cSotto 				    "sectors (only %llu sectors left)\n",
14509fdcb4d6Skrw 				    DL_GETDSIZE(lp) - ui, freesectors);
1451c4f83f03Skrw 			else
14526fe57b42Smillert 				break;
14536fe57b42Smillert 		} else
14546fe57b42Smillert 			break;
14556fe57b42Smillert 	}
145641ed49b7Smillert 	/* Adjust ending_sector if necessary. */
145796a888c6Smillert 	if (ending_sector > ui)
145896a888c6Smillert 		ending_sector = ui;
14591e0ad43cSotto 	DL_SETDSIZE(lp, ui);
14606fe57b42Smillert 
14616fe57b42Smillert 	/* rpm */
14626fe57b42Smillert 	for (;;) {
1463dea75673Skrw 		ui = getuint(lp, "rpm",
1464a7e61405Smillert 		  "The rotational speed of the disk in revolutions per minute.",
14654b9a3bdaSmillert 		  lp->d_rpm, lp->d_rpm, 0, 0);
14661e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
146796a888c6Smillert 			fputs("Command aborted\n", stderr);
146896a888c6Smillert 			*lp = oldlabel;		/* undo damage */
146996a888c6Smillert 			return;
14701e0ad43cSotto 		} else if (ui == ULLONG_MAX)
14716fe57b42Smillert 			fputs("Invalid entry\n", stderr);
14726fe57b42Smillert 		else
14736fe57b42Smillert 			break;
14746fe57b42Smillert 	}
14756fe57b42Smillert 	lp->d_rpm = ui;
1476440b1d70Smillert 
1477440b1d70Smillert 	/* interleave */
1478440b1d70Smillert 	for (;;) {
1479dea75673Skrw 		ui = getuint(lp, "interleave",
1480440b1d70Smillert 		  "The physical sector interleave, set when formatting.  Almost always 1.",
14814b9a3bdaSmillert 		  lp->d_interleave, lp->d_interleave, 0, 0);
14821e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
1483440b1d70Smillert 			fputs("Command aborted\n", stderr);
1484440b1d70Smillert 			*lp = oldlabel;		/* undo damage */
1485440b1d70Smillert 			return;
14861e0ad43cSotto 		} else if (ui == ULLONG_MAX || ui == 0)
1487440b1d70Smillert 			fputs("Invalid entry\n", stderr);
1488440b1d70Smillert 		else
1489440b1d70Smillert 			break;
1490440b1d70Smillert 	}
1491440b1d70Smillert 	lp->d_interleave = ui;
14926fe57b42Smillert }
1493a7e61405Smillert 
1494a7e61405Smillert struct partition **
14950fbd3c97Skrw sort_partitions(struct disklabel *lp)
1496a7e61405Smillert {
1497d18c2a43Skrw 	static struct partition *spp[MAXPARTITIONS+2];
14980fbd3c97Skrw 	int i, npartitions;
1499a7e61405Smillert 
1500d18c2a43Skrw 	memset(spp, 0, sizeof(spp));
1501d18c2a43Skrw 
1502a7e61405Smillert 	for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) {
1503a7e61405Smillert 		if (lp->d_partitions[i].p_fstype != FS_UNUSED &&
1504a7e61405Smillert 		    lp->d_partitions[i].p_fstype != FS_BOOT &&
15051e0ad43cSotto 		    DL_GETPSIZE(&lp->d_partitions[i]) != 0)
1506a7e61405Smillert 			spp[npartitions++] = &lp->d_partitions[i];
1507a7e61405Smillert 	}
1508a7e61405Smillert 
1509a7e61405Smillert 	/*
1510a7e61405Smillert 	 * Sort the partitions based on starting offset.
1511a7e61405Smillert 	 * This is safe because we guarantee no overlap.
1512a7e61405Smillert 	 */
1513a7e61405Smillert 	if (npartitions > 1)
1514a7e61405Smillert 		if (heapsort((void *)spp, npartitions, sizeof(spp[0]),
1515a7e61405Smillert 		    partition_cmp))
1516a7e61405Smillert 			err(4, "failed to sort partition table");
1517a7e61405Smillert 
1518a7e61405Smillert 	return(spp);
1519a7e61405Smillert }
15200f820bbbSmillert 
15210f820bbbSmillert /*
15220f820bbbSmillert  * Get a valid disk type if necessary.
15230f820bbbSmillert  */
15240f820bbbSmillert void
15258809fabbSderaadt getdisktype(struct disklabel *lp, char *banner, char *dev)
15260f820bbbSmillert {
15270f820bbbSmillert 	int i;
1528803ff7d5Smillert 	char *s, *def = "SCSI";
1529803ff7d5Smillert 	struct dtypes {
1530803ff7d5Smillert 		char *dev;
1531803ff7d5Smillert 		char *type;
1532803ff7d5Smillert 	} dtypes[] = {
1533c33fcabaSmillert 		{ "sd",   "SCSI" },
1534c33fcabaSmillert 		{ "rz",   "SCSI" },
1535c33fcabaSmillert 		{ "wd",   "IDE" },
1536c33fcabaSmillert 		{ "fd",   "FLOPPY" },
1537c33fcabaSmillert 		{ "xd",   "SMD" },
1538c33fcabaSmillert 		{ "xy",   "SMD" },
1539c33fcabaSmillert 		{ "hd",   "HP-IB" },
1540c33fcabaSmillert 		{ "ccd",  "CCD" },
1541c33fcabaSmillert 		{ "vnd",  "VND" },
1542c33fcabaSmillert 		{ "svnd", "VND" },
1543c33fcabaSmillert 		{ NULL,   NULL }
1544803ff7d5Smillert 	};
1545803ff7d5Smillert 
1546803ff7d5Smillert 	if ((s = basename(dev)) != NULL) {
1547803ff7d5Smillert 		if (*s == 'r')
1548803ff7d5Smillert 			s++;
1549803ff7d5Smillert 		i = strcspn(s, "0123456789");
1550803ff7d5Smillert 		s[i] = '\0';
1551803ff7d5Smillert 		dev = s;
1552803ff7d5Smillert 		for (i = 0; dtypes[i].dev != NULL; i++) {
1553803ff7d5Smillert 			if (strcmp(dev, dtypes[i].dev) == 0) {
1554803ff7d5Smillert 				def = dtypes[i].type;
1555803ff7d5Smillert 				break;
1556803ff7d5Smillert 			}
1557803ff7d5Smillert 		}
1558803ff7d5Smillert 	}
15590f820bbbSmillert 
15600f820bbbSmillert 	if (lp->d_type > DKMAXTYPES || lp->d_type == 0) {
15610f820bbbSmillert 		puts(banner);
15620f820bbbSmillert 		puts("Possible values are:");
1563eb5dd924Sderaadt 		printf("\"IDE\", ");
15640f820bbbSmillert 		for (i = 1; i < DKMAXTYPES; i++) {
15650f820bbbSmillert 			printf("\"%s\"", dktypenames[i]);
15660f820bbbSmillert 			if (i < DKMAXTYPES - 1)
15670f820bbbSmillert 				fputs(", ", stdout);
15680f820bbbSmillert 		}
15690f820bbbSmillert 		putchar('\n');
15700f820bbbSmillert 
15710f820bbbSmillert 		for (;;) {
1572c33fcabaSmillert 			s = getstring("Disk type",
1573803ff7d5Smillert 			    "What kind of disk is this?  Usually SCSI, IDE, "
1574803ff7d5Smillert 			    "ESDI, CCD, ST506, or floppy.", def);
157596a888c6Smillert 			if (s == NULL)
157696a888c6Smillert 				continue;
15775b412421Smillert 			if (strcasecmp(s, "IDE") == 0) {
15785b412421Smillert 				lp->d_type = DTYPE_ESDI;
15795b412421Smillert 				return;
15805b412421Smillert 			}
15810f820bbbSmillert 			for (i = 1; i < DKMAXTYPES; i++)
15820f820bbbSmillert 				if (strcasecmp(s, dktypenames[i]) == 0) {
15830f820bbbSmillert 					lp->d_type = i;
15840f820bbbSmillert 					return;
15850f820bbbSmillert 				}
15860f820bbbSmillert 			printf("\"%s\" is not a valid disk type.\n", s);
15870f820bbbSmillert 			fputs("Valid types are: ", stdout);
15880f820bbbSmillert 			for (i = 1; i < DKMAXTYPES; i++) {
15890f820bbbSmillert 				printf("\"%s\"", dktypenames[i]);
15900f820bbbSmillert 				if (i < DKMAXTYPES - 1)
15910f820bbbSmillert 					fputs(", ", stdout);
15920f820bbbSmillert 			}
15930f820bbbSmillert 			putchar('\n');
15940f820bbbSmillert 		}
15950f820bbbSmillert 	}
15960f820bbbSmillert }
159796a888c6Smillert 
159896a888c6Smillert /*
159996a888c6Smillert  * Get beginning and ending sectors of the OpenBSD portion of the disk
160096a888c6Smillert  * from the user.
160196a888c6Smillert  */
160296a888c6Smillert void
16039fdcb4d6Skrw set_bounds(struct disklabel *lp)
160496a888c6Smillert {
16051e0ad43cSotto 	u_int64_t ui, start_temp;
160696a888c6Smillert 
160796a888c6Smillert 	/* Starting sector */
160896a888c6Smillert 	do {
1609dea75673Skrw 		ui = getuint(lp, "Starting sector",
161096a888c6Smillert 		  "The start of the OpenBSD portion of the disk.",
16111e0ad43cSotto 		  starting_sector, DL_GETDSIZE(lp), 0, 0);
16121e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
161396a888c6Smillert 			fputs("Command aborted\n", stderr);
161496a888c6Smillert 			return;
161596a888c6Smillert 		}
16161e0ad43cSotto 	} while (ui >= DL_GETDSIZE(lp));
161796a888c6Smillert 	start_temp = ui;
161896a888c6Smillert 
16194793b14cSmillert 	/* Size */
162096a888c6Smillert 	do {
1621dea75673Skrw 		ui = getuint(lp, "Size ('*' for entire disk)",
1622f98aebd4Smillert 		  "The size of the OpenBSD portion of the disk ('*' for the "
1623f98aebd4Smillert 		  "entire disk).", ending_sector - starting_sector,
16241e0ad43cSotto 		  DL_GETDSIZE(lp) - start_temp, 0, 0);
16251e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
162696a888c6Smillert 			fputs("Command aborted\n", stderr);
162796a888c6Smillert 			return;
162896a888c6Smillert 		}
16291e0ad43cSotto 	} while (ui > DL_GETDSIZE(lp) - start_temp);
16304793b14cSmillert 	ending_sector = start_temp + ui;
163196a888c6Smillert 	starting_sector = start_temp;
163296a888c6Smillert }
163396a888c6Smillert 
163496a888c6Smillert /*
163596a888c6Smillert  * Return a list of the "chunks" of free space available
163696a888c6Smillert  */
163796a888c6Smillert struct diskchunk *
16388809fabbSderaadt free_chunks(struct disklabel *lp)
163996a888c6Smillert {
164096a888c6Smillert 	struct partition **spp;
164196a888c6Smillert 	static struct diskchunk chunks[MAXPARTITIONS + 2];
164299bd27d2Skrw 	u_int64_t start, stop;
164396a888c6Smillert 	int i, numchunks;
164496a888c6Smillert 
16450fbd3c97Skrw 	/* Sort the in-use partitions based on offset */
16460fbd3c97Skrw 	spp = sort_partitions(lp);
164796a888c6Smillert 
164896a888c6Smillert 	/* If there are no partitions, it's all free. */
16490fbd3c97Skrw 	if (spp[0] == NULL) {
16502d8451b0Smillert 		chunks[0].start = starting_sector;
165196a888c6Smillert 		chunks[0].stop = ending_sector;
165296a888c6Smillert 		chunks[1].start = chunks[1].stop = 0;
165396a888c6Smillert 		return(chunks);
165496a888c6Smillert 	}
165596a888c6Smillert 
165696a888c6Smillert 	/* Find chunks of free space */
165796a888c6Smillert 	numchunks = 0;
16580fbd3c97Skrw 	if (DL_GETPOFFSET(spp[0]) > starting_sector) {
16592d8451b0Smillert 		chunks[0].start = starting_sector;
16601e0ad43cSotto 		chunks[0].stop = DL_GETPOFFSET(spp[0]);
166196a888c6Smillert 		numchunks++;
166296a888c6Smillert 	}
16630fbd3c97Skrw 	for (i = 0; spp[i] != NULL; i++) {
166499bd27d2Skrw 		start = DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]);
1665aff3f969Sotto 		if (start < starting_sector)
1666aff3f969Sotto 			start = starting_sector;
1667aff3f969Sotto 		else if (start > ending_sector)
1668aff3f969Sotto 			start = ending_sector;
166999bd27d2Skrw 		if (spp[i + 1] != NULL)
167099bd27d2Skrw 			stop = DL_GETPOFFSET(spp[i+1]);
167199bd27d2Skrw 		else
167299bd27d2Skrw 			stop = ending_sector;
1673aff3f969Sotto 		if (stop < starting_sector)
1674aff3f969Sotto 			stop = starting_sector;
1675aff3f969Sotto 		else if (stop > ending_sector)
1676aff3f969Sotto 			stop = ending_sector;
167799bd27d2Skrw 		if (start < stop) {
167899bd27d2Skrw 			chunks[numchunks].start = start;
167999bd27d2Skrw 			chunks[numchunks].stop = stop;
168096a888c6Smillert 			numchunks++;
168196a888c6Smillert 		}
168296a888c6Smillert 	}
168396a888c6Smillert 
168496a888c6Smillert 	/* Terminate and return */
168596a888c6Smillert 	chunks[numchunks].start = chunks[numchunks].stop = 0;
168696a888c6Smillert 	return(chunks);
168796a888c6Smillert }
16884793b14cSmillert 
16894793b14cSmillert void
169087023ed9Skrw find_bounds(struct disklabel *lp)
16914793b14cSmillert {
16926534e983Sderaadt 	starting_sector = DL_GETBSTART(lp);
16936534e983Sderaadt 	ending_sector = DL_GETBEND(lp);
1694b2d4a455Smiod 
16956534e983Sderaadt 	if (ending_sector) {
169634ae4198Skrw 		if (verbose)
169734ae4198Skrw 			printf("Treating sectors %llu-%llu as the OpenBSD"
169834ae4198Skrw 			    " portion of the disk.\nYou can use the 'b'"
169934ae4198Skrw 			    " command to change this.\n\n", starting_sector,
170034ae4198Skrw 			    ending_sector);
1701b2d4a455Smiod 	} else {
1702b2d4a455Smiod #if (NUMBOOT == 1)
1703d3f02056Smillert 		/* Boot blocks take up the first cylinder */
1704d3f02056Smillert 		starting_sector = lp->d_secpercyl;
170534ae4198Skrw 		if (verbose)
170634ae4198Skrw 			printf("Reserving the first data cylinder for boot"
170734ae4198Skrw 			    " blocks.\nYou can use the 'b' command to change"
170834ae4198Skrw 			    " this.\n\n");
17094793b14cSmillert #endif
17104793b14cSmillert 	}
1711b2d4a455Smiod }
1712c0bdc608Smillert 
1713c0bdc608Smillert /*
1714c0bdc608Smillert  * Calculate free space.
1715c0bdc608Smillert  */
17169fdcb4d6Skrw u_int64_t
17179fdcb4d6Skrw editor_countfree(struct disklabel *lp)
1718c0bdc608Smillert {
1719d93cb2bbSkrw 	struct diskchunk *chunks;
17209fdcb4d6Skrw 	u_int64_t freesectors = 0;
1721c0bdc608Smillert 	int i;
1722c0bdc608Smillert 
1723d93cb2bbSkrw 	chunks = free_chunks(lp);
1724509930fbSotto 
1725d93cb2bbSkrw 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++)
17269fdcb4d6Skrw 		freesectors += chunks[i].stop - chunks[i].start;
17279fdcb4d6Skrw 
17289fdcb4d6Skrw 	return (freesectors);
1729c0bdc608Smillert }
1730617e6e4aSmillert 
1731617e6e4aSmillert void
17328809fabbSderaadt editor_help(char *arg)
1733617e6e4aSmillert {
1734617e6e4aSmillert 
1735617e6e4aSmillert 	/* XXX - put these strings in a table instead? */
1736617e6e4aSmillert 	switch (*arg) {
1737617e6e4aSmillert 	case 'p':
1738617e6e4aSmillert 		puts(
1739aff3f969Sotto "The 'p' command prints the current partitions.  By default, it prints size\n"
1740aff3f969Sotto "and offset in sectors (a sector is usually 512 bytes).  The 'p' command\n"
1741aff3f969Sotto "takes an optional units argument.  Possible values are 'b' for bytes, 'c'\n"
1742aff3f969Sotto "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n");
1743eb176802Sjsg 		break;
1744aff3f969Sotto 	case 'l':
1745aff3f969Sotto 	puts(
1746aff3f969Sotto "The 'l' command prints the header of the disk label.  By default, it prints\n"
1747617e6e4aSmillert "size and offset in sectors (a sector is usually 512 bytes).  The 'p' command\n"
1748617e6e4aSmillert "takes an optional units argument.  Possible values are 'b' for bytes, 'c'\n"
1749617e6e4aSmillert "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n");
1750617e6e4aSmillert 		break;
1751617e6e4aSmillert 	case 'M':
1752617e6e4aSmillert 		puts(
17539afbe9eeSmillert "The 'M' command pipes the entire OpenBSD manual page for disk label through\n"
1754508086e9Smillert "the pager specified by the PAGER environment variable or 'less' if PAGER is\n"
1755508086e9Smillert "not set.  It is especially useful during install when the normal system\n"
1756508086e9Smillert "manual is not available.\n");
1757617e6e4aSmillert 		break;
1758617e6e4aSmillert 	case 'e':
1759617e6e4aSmillert 		puts(
1760617e6e4aSmillert "The 'e' command is used to edit the disk drive parameters.  These include\n"
1761617e6e4aSmillert "the number of sectors/track, tracks/cylinder, sectors/cylinder, number of\n"
1762617e6e4aSmillert "cylinders on the disk , total sectors on the disk, rpm, interleave, disk\n"
1763617e6e4aSmillert "type, and a descriptive label string.  You should not change these unless\n"
1764617e6e4aSmillert "you know what you are doing\n");
1765617e6e4aSmillert 		break;
1766617e6e4aSmillert 	case 'a':
1767617e6e4aSmillert 		puts(
1768617e6e4aSmillert "The 'a' command adds new partitions to the disk.  It takes as an optional\n"
1769617e6e4aSmillert "argument the partition letter to add.  If you do not specify a partition\n"
1770617e6e4aSmillert "letter, you will be prompted for it; the next available letter will be the\n"
1771617e6e4aSmillert "default answer\n");
1772617e6e4aSmillert 		break;
17736aaa4aabSotto 	case 'A':
17746aaa4aabSotto 		puts(
17756aaa4aabSotto "The 'A' command clears the existing partitions and creates a new label\n"
17766aaa4aabSotto "based on the size of the disk\n");
17776aaa4aabSotto 		break;
1778617e6e4aSmillert 	case 'b':
1779617e6e4aSmillert 		puts(
1780617e6e4aSmillert "The 'b' command is used to change the boundaries of the OpenBSD portion of\n"
1781617e6e4aSmillert "the disk.  This is only useful on disks with an fdisk partition.  By default,\n"
1782617e6e4aSmillert "on a disk with an fdisk partition, the boundaries are set to be the first\n"
1783617e6e4aSmillert "and last sectors of the OpenBSD fdisk partition.  You should only change\n"
1784617e6e4aSmillert "these if your fdisk partition table is incorrect or you have a disk larger\n"
1785617e6e4aSmillert "than 8gig, since 8gig is the maximum size an fdisk partition can be.  You\n"
1786617e6e4aSmillert "may enter '*' at the 'Size' prompt to indicate the entire size of the disk\n"
1787617e6e4aSmillert "(minus the starting sector).  Use this option with care; if you extend the\n"
1788617e6e4aSmillert "boundaries such that they overlap with another operating system you will\n"
1789617e6e4aSmillert "corrupt the other operating system's data.\n");
1790617e6e4aSmillert 		break;
1791617e6e4aSmillert 	case 'c':
1792617e6e4aSmillert 		puts(
1793617e6e4aSmillert "The 'c' command is used to change the size of an existing partition.  It\n"
1794617e6e4aSmillert "takes as an optional argument the partition letter to change.  If you do not\n"
1795617e6e4aSmillert "specify a partition letter, you will be prompted for one.  You may add a '+'\n"
1796617e6e4aSmillert "or '-' prefix to the new size to increase or decrease the existing value\n"
1797617e6e4aSmillert "instead of entering an absolute value.  You may also use a suffix to indicate\n"
1798617e6e4aSmillert "the units the values is in terms of.  Possible suffixes are 'b' for bytes,\n"
1799617e6e4aSmillert "'c' for cylinders, 'k' for kilobytes, 'm' for megabytes, 'g' for gigabytes or\n"
1800617e6e4aSmillert "no suffix for sectors (usually 512 bytes).  You may also enter '*' to change\n"
1801617e6e4aSmillert "the size to be the total number of free sectors remaining.\n");
1802617e6e4aSmillert 		break;
18039afbe9eeSmillert 	case 'D':
18049afbe9eeSmillert 		puts(
18059afbe9eeSmillert "The 'D' command will set the disk label to the default values as reported\n"
18069afbe9eeSmillert "by the disk itself.  This similates the case where there is no disk label.\n");
18079afbe9eeSmillert 		break;
1808617e6e4aSmillert 	case 'd':
1809617e6e4aSmillert 		puts(
1810617e6e4aSmillert "The 'd' command is used to delete an existing partition.  It takes as an\n"
1811617e6e4aSmillert "optional argument the partition letter to change.  If you do not specify a\n"
1812617e6e4aSmillert "partition letter, you will be prompted for one.  You may not delete the ``c''\n"
1813617e6e4aSmillert "partition as 'c' must always exist and by default is marked as 'unused' (so\n"
1814617e6e4aSmillert "it does not take up any space).\n");
1815617e6e4aSmillert 		break;
1816c33fcabaSmillert 	case 'g':
1817c33fcabaSmillert 		puts(
181887023ed9Skrw "The 'g' command is used select which disk geometry to use, the disk or a\n"
181987023ed9Skrw "user geometry.  It takes as an optional argument ``d'' or ``u''.  If \n"
1820c33fcabaSmillert "you do not specify the type as an argument, you will be prompted for it.\n");
1821c33fcabaSmillert 		break;
1822617e6e4aSmillert 	case 'm':
1823617e6e4aSmillert 		puts(
182487023ed9Skrw "The 'm' command is used to modify an existing partition.  It takes as an\n"
182587023ed9Skrw "optional argument the partition letter to change.  If you do not specify a\n"
1826617e6e4aSmillert "partition letter, you will be prompted for one.  This option allows the user\n"
1827617e6e4aSmillert "to change the filesystem type, starting offset, partition size, block fragment\n"
1828617e6e4aSmillert "size, block size, and cylinders per group for the specified partition (not all\n"
1829617e6e4aSmillert "parameters are configurable for non-BSD partitions).\n");
1830617e6e4aSmillert 		break;
1831bd6726faSmillert 	case 'n':
1832bd6726faSmillert 		puts(
1833fb932baaSaaron "The 'n' command is used to set the mount point for a partition (ie: name it).\n"
1834bd6726faSmillert "It takes as an optional argument the partition letter to name.  If you do\n"
1835bd6726faSmillert "not specify a partition letter, you will be prompted for one.  This option\n"
1836ac7ae62cSderaadt "is only valid if disklabel was invoked with the -f flag.\n");
1837bd6726faSmillert 		break;
18386aaa4aabSotto 	case 'R':
18396aaa4aabSotto 		puts(
18406aaa4aabSotto "Resize a a partition, compacting unused space between partitions\n"
18416aaa4aabSotto "with a higher offset. The last partition will be shrunk if needed.\n"
1842fc6e9c48Sotto "Works only for auto allocated labels.\n");
18436aaa4aabSotto 		break;
1844617e6e4aSmillert 	case 'r':
1845617e6e4aSmillert 		puts(
184625f9c360Skrw "The 'r' command is used to recalculate and display details about\n"
184725f9c360Skrw "the available free space.\n");
1848617e6e4aSmillert 		break;
1849617e6e4aSmillert 	case 'u':
1850617e6e4aSmillert 		puts(
1851617e6e4aSmillert "The 'u' command will undo (or redo) the last change.  Entering 'u' once will\n"
1852617e6e4aSmillert "undo your last change.  Entering it again will restore the change.\n");
1853617e6e4aSmillert 		break;
1854617e6e4aSmillert 	case 's':
1855617e6e4aSmillert 		puts(
1856617e6e4aSmillert "The 's' command is used to save a copy of the label to a file in ascii format\n"
1857617e6e4aSmillert "(suitable for loading via disklabel's [-R] option).  It takes as an optional\n"
1858617e6e4aSmillert "argument the filename to save the label to.  If you do not specify a filename,\n"
1859617e6e4aSmillert "you will be prompted for one.\n");
1860617e6e4aSmillert 		break;
1861617e6e4aSmillert 	case 'w':
1862617e6e4aSmillert 		puts(
1863617e6e4aSmillert "The 'w' command will write the current label to disk.  This option will\n"
1864617e6e4aSmillert "commit any changes to the on-disk label.\n");
1865617e6e4aSmillert 		break;
1866617e6e4aSmillert 	case 'q':
1867617e6e4aSmillert 		puts(
1868617e6e4aSmillert "The 'q' command quits the label editor.  If any changes have been made you\n"
1869617e6e4aSmillert "will be asked whether or not to save the changes to the on-disk label.\n");
1870617e6e4aSmillert 		break;
18719afbe9eeSmillert 	case 'X':
18729afbe9eeSmillert 		puts(
18739afbe9eeSmillert "The 'X' command toggles disklabel in to/out of 'expert mode'.  By default,\n"
18749afbe9eeSmillert "some settings are reserved for experts only (such as the block and fragment\n"
18759afbe9eeSmillert "size on ffs partitions).\n");
18769afbe9eeSmillert 		break;
1877617e6e4aSmillert 	case 'x':
1878617e6e4aSmillert 		puts(
1879617e6e4aSmillert "The 'x' command exits the label editor without saving any changes to the\n"
1880617e6e4aSmillert "on-disk label.\n");
1881617e6e4aSmillert 		break;
18829afbe9eeSmillert 	case 'z':
18839afbe9eeSmillert 		puts(
18849afbe9eeSmillert "The 'z' command zeroes out the existing partition table, leaving only the 'c'\n"
18859afbe9eeSmillert "partition.  The drive parameters are not changed.\n");
18869afbe9eeSmillert 		break;
1887617e6e4aSmillert 	default:
1888617e6e4aSmillert 		puts("Available commands:");
1889617e6e4aSmillert 		puts(
189049159a67Skrw "  ? [cmd]  - show help                  n [part] - set mount point\n"
189149159a67Skrw "  A        - auto partition all space   p [unit] - print partitions\n"
189249159a67Skrw "  a [part] - add partition              q        - quit & save changes\n"
18936aaa4aabSotto "  b        - set OpenBSD boundaries     R [part] - resize a partition\n"
189449159a67Skrw "  c [part] - change partition size      r        - display free space\n"
18956aaa4aabSotto "  D        - reset label to default     s [path] - save label to file\n"
18966aaa4aabSotto "  d [part] - delete partition           U        - undo all changes\n"
18976aaa4aabSotto "  e        - edit drive parameters      u        - undo last change\n"
18986aaa4aabSotto "  g [d|u]  - [d]isk or [u]ser geometry  w        - write label to disk\n"
18996aaa4aabSotto "  l [unit] - print disk label header    X        - toggle expert mode\n"
19006aaa4aabSotto "  M        - disklabel(8) man page      x        - exit & lose changes\n"
19016aaa4aabSotto "  m [part] - modify partition           z        - delete all partitions\n"
1902c4884206Skrw "\n"
1903c4884206Skrw "Suffixes can be used to indicate units other than sectors:\n"
1904c4884206Skrw "\t'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' (gigabytes)\n"
1905c4884206Skrw "\t'c' (cylinders), '%' (% of total disk), '&' (% of free space).\n"
1906c4884206Skrw "Values in non-sector units are truncated to the nearest cylinder boundary.");
1907617e6e4aSmillert 		break;
1908617e6e4aSmillert 	}
1909617e6e4aSmillert }
1910bd6726faSmillert 
19114f3bbbf0Skrw void
19128809fabbSderaadt mpcopy(char **to, char **from)
1913bd6726faSmillert {
1914bd6726faSmillert 	int i;
19150612d09dSderaadt 	char *top;
1916bd6726faSmillert 
1917bd6726faSmillert 	for (i = 0; i < MAXPARTITIONS; i++) {
1918bd6726faSmillert 		if (from[i] != NULL) {
1919dcab0d16Sderaadt 			int len = strlen(from[i]) + 1;
1920dcab0d16Sderaadt 
19210612d09dSderaadt 			top = realloc(to[i], len);
19220612d09dSderaadt 			if (top == NULL)
1923bd6726faSmillert 				errx(4, "out of memory");
19240612d09dSderaadt 			to[i] = top;
1925dcab0d16Sderaadt 			(void)strlcpy(to[i], from[i], len);
1926bd6726faSmillert 		} else if (to[i] != NULL) {
1927bd6726faSmillert 			free(to[i]);
1928bd6726faSmillert 			to[i] = NULL;
1929bd6726faSmillert 		}
1930bd6726faSmillert 	}
1931bd6726faSmillert }
1932bd6726faSmillert 
1933bd6726faSmillert int
19348809fabbSderaadt mpequal(char **mp1, char **mp2)
1935bd6726faSmillert {
1936bd6726faSmillert 	int i;
1937bd6726faSmillert 
1938bd6726faSmillert 	for (i = 0; i < MAXPARTITIONS; i++) {
1939bd6726faSmillert 		if (mp1[i] == NULL && mp2[i] == NULL)
1940bd6726faSmillert 			continue;
1941bd6726faSmillert 
1942bd6726faSmillert 		if ((mp1[i] != NULL && mp2[i] == NULL) ||
1943bd6726faSmillert 		    (mp1[i] == NULL && mp2[i] != NULL) ||
1944bd6726faSmillert 		    (strcmp(mp1[i], mp2[i]) != 0))
1945bd6726faSmillert 			return(0);
1946bd6726faSmillert 	}
1947bd6726faSmillert 	return(1);
1948bd6726faSmillert }
1949bd6726faSmillert 
195093160b9bSkrw void
195134ae4198Skrw mpsave(struct disklabel *lp)
1952bd6726faSmillert {
1953d8b446ceSderaadt 	int i, j;
1954bd6726faSmillert 	char bdev[MAXPATHLEN], *p;
19553f843443Smillert 	struct mountinfo mi[MAXPARTITIONS];
1956bd6726faSmillert 	FILE *fp;
1957bd6726faSmillert 
195893160b9bSkrw 	if (!fstabfile)
195993160b9bSkrw 		return;
196093160b9bSkrw 
19613f843443Smillert 	memset(&mi, 0, sizeof(mi));
19623f843443Smillert 
1963d8b446ceSderaadt 	for (i = 0; i < MAXPARTITIONS; i++) {
196434ae4198Skrw 		if (mountpoints[i] != NULL) {
196534ae4198Skrw 			mi[i].mountpoint = mountpoints[i];
19663f843443Smillert 			mi[i].partno = i;
1967bd6726faSmillert 		}
1968bd6726faSmillert 	}
1969bd6726faSmillert 
197034ae4198Skrw 	/* Convert specname to bdev */
197134ae4198Skrw 	if (strncmp(_PATH_DEV, specname, sizeof(_PATH_DEV) - 1) == 0 &&
197234ae4198Skrw 	    specname[sizeof(_PATH_DEV) - 1] == 'r') {
1973bd6726faSmillert 		snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV,
197434ae4198Skrw 		    &specname[sizeof(_PATH_DEV)]);
1975bd6726faSmillert 	} else {
197634ae4198Skrw 		if ((p = strrchr(specname, '/')) == NULL || *(++p) != 'r')
197793160b9bSkrw 			return;
1978bd6726faSmillert 		*p = '\0';
197934ae4198Skrw 		snprintf(bdev, sizeof(bdev), "%s%s", specname, p + 1);
1980bd6726faSmillert 		*p = 'r';
1981bd6726faSmillert 	}
1982bd6726faSmillert 	bdev[strlen(bdev) - 1] = '\0';
1983bd6726faSmillert 
19843f843443Smillert 	/* Sort mountpoints so we don't try to mount /usr/local before /usr */
19853f843443Smillert 	qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp);
19863f843443Smillert 
198793160b9bSkrw 	if (fp = fopen(fstabfile, "w")) {
198893160b9bSkrw 		for (i = 0; i < MAXPARTITIONS && mi[i].mountpoint; i++) {
19895fea0b85Smillert 			j =  mi[i].partno;
19905fea0b85Smillert 			fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev, 'a' + j,
19913f843443Smillert 			    mi[i].mountpoint,
19925fea0b85Smillert 			    fstypesnames[lp->d_partitions[j].p_fstype],
19935fea0b85Smillert 			    j == 0 ? 1 : 2);
1994bd6726faSmillert 		}
1995bd6726faSmillert 		fclose(fp);
199693160b9bSkrw 	}
1997bd6726faSmillert }
199824a2c1a4Smillert 
199924a2c1a4Smillert int
2000604d3bdeSkrw get_offset(struct disklabel *lp, int partno)
200124a2c1a4Smillert {
2002604d3bdeSkrw 	struct diskchunk *chunks;
200324a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
200415c15d8aSkrw 	u_int64_t ui, maxsize;
2005604d3bdeSkrw 	int i, fstype;
200624a2c1a4Smillert 
2007dea75673Skrw 	ui = getuint(lp, "offset",
20081e0ad43cSotto 	   "Starting sector for this partition.",
20091e0ad43cSotto 	   DL_GETPOFFSET(pp),
20101e0ad43cSotto 	   DL_GETPOFFSET(pp), 0, DO_CONVERSIONS |
201124a2c1a4Smillert 	   (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0));
2012e9ff19beSkrw 
2013e9ff19beSkrw 	if (ui == ULLONG_MAX - 1)
201424a2c1a4Smillert 		fputs("Command aborted\n", stderr);
2015e9ff19beSkrw 	else if (ui == ULLONG_MAX)
201624a2c1a4Smillert 		fputs("Invalid entry\n", stderr);
201740e98e9fSkrw 	else if (ui < starting_sector || ui >= ending_sector)
2018e9ff19beSkrw 		fprintf(stderr, "The offset must be >= %llu and < %llu, "
2019e9ff19beSkrw 		    "the limits of the OpenBSD portion\n"
2020e9ff19beSkrw 		    "of the disk. The 'b' command can change these limits.\n",
202140e98e9fSkrw 		    starting_sector, ending_sector);
2022fc1a4cc6Sderaadt #ifdef SUN_AAT0
202349bf537cSderaadt 	else if (partno == 0 && ui != 0)
202449bf537cSderaadt 		fprintf(stderr, "This architecture requires that "
202540f544cdSderaadt 		    "partition 'a' start at sector 0.\n");
202649bf537cSderaadt #endif
202715c15d8aSkrw 	else {
2028604d3bdeSkrw 		fstype = pp->p_fstype;
2029604d3bdeSkrw 		pp->p_fstype = FS_UNUSED;
2030604d3bdeSkrw 		chunks = free_chunks(lp);
2031604d3bdeSkrw 		pp->p_fstype = fstype;
2032e9ff19beSkrw 		for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
2033e9ff19beSkrw 			if (ui < chunks[i].start || ui >= chunks[i].stop)
203415c15d8aSkrw 				continue;
20351e0ad43cSotto 			DL_SETPOFFSET(pp, ui);
203615c15d8aSkrw 			maxsize = chunks[i].stop - DL_GETPOFFSET(pp);
203715c15d8aSkrw 			if (DL_GETPSIZE(pp) > maxsize)
203815c15d8aSkrw 				DL_SETPSIZE(pp, maxsize);
203924a2c1a4Smillert 			return (0);
204024a2c1a4Smillert 		}
204115c15d8aSkrw 		fputs("The offset must be in a free area.\n", stderr);
204215c15d8aSkrw 	}
2043e9ff19beSkrw 
2044e9ff19beSkrw 	/* Partition offset was not set. */
2045e9ff19beSkrw 	return (1);
204615c15d8aSkrw }
204724a2c1a4Smillert 
204824a2c1a4Smillert int
20499fdcb4d6Skrw get_size(struct disklabel *lp, int partno)
205024a2c1a4Smillert {
205124a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
205214192793Skrw 	u_int64_t maxsize, ui;
205314192793Skrw 
205414192793Skrw 	maxsize = max_partition_size(lp, partno);
205524a2c1a4Smillert 
2056dea75673Skrw 	ui = getuint(lp, "size", "Size of the partition. "
20577da73705Skrw 	    "You may also say +/- amount for a relative change.",
205814192793Skrw 	    DL_GETPSIZE(pp), maxsize, DL_GETPOFFSET(pp),
2059525051f1Sotto 	    DO_CONVERSIONS | ((pp->p_fstype == FS_BSDFFS ||
2060525051f1Sotto 	    pp->p_fstype == FS_SWAP) ?  DO_ROUNDING : 0));
2061e9ff19beSkrw 
2062e9ff19beSkrw 	if (ui == ULLONG_MAX - 1)
206324a2c1a4Smillert 		fputs("Command aborted\n", stderr);
2064e9ff19beSkrw 	else if (ui == ULLONG_MAX)
206524a2c1a4Smillert 		fputs("Invalid entry\n", stderr);
206628e3704eSkrw 	else if (ui == 0)
206728e3704eSkrw 		fputs("The size must be > 0\n", stderr);
206840e98e9fSkrw 	else if (ui + DL_GETPOFFSET(pp) > ending_sector)
206940e98e9fSkrw 		fprintf(stderr, "The size can't be more than "
207040e98e9fSkrw 		    "%llu sectors, or the partition would\n"
207140e98e9fSkrw 		    "extend beyond the last sector (%llu) of the "
207240e98e9fSkrw 		    "OpenBSD portion of\nthe disk. "
207340e98e9fSkrw 		    "The 'b' command can change this limit.\n",
207440e98e9fSkrw 		    ending_sector - DL_GETPOFFSET(pp), ending_sector);
207514192793Skrw 	else if (ui > maxsize)
207614192793Skrw 		fprintf(stderr,"Sorry, there are only %llu sectors left\n",
207714192793Skrw 		    maxsize);
207859ccf790Skrw 	else {
207959ccf790Skrw 		DL_SETPSIZE(pp, ui);
208024a2c1a4Smillert 		return (0);
208124a2c1a4Smillert 	}
2082e9ff19beSkrw 
2083e9ff19beSkrw 	/* Partition size was not set. */
2084e9ff19beSkrw 	return (1);
2085e9ff19beSkrw }
208624a2c1a4Smillert 
208724a2c1a4Smillert int
20888809fabbSderaadt get_fsize(struct disklabel *lp, int partno)
208924a2c1a4Smillert {
20901e0ad43cSotto 	u_int64_t ui, fsize, frag;
209124a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
209224a2c1a4Smillert 
2093a4c87e64Skrw 	if (!expert || pp->p_fstype != FS_BSDFFS)
2094a4c87e64Skrw 		return (0);
2095a4c87e64Skrw 
2096ddfcbf38Sotto 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
2097ddfcbf38Sotto 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
2098ddfcbf38Sotto 	if (fsize == 0)
2099ddfcbf38Sotto 		frag = 8;
2100ddfcbf38Sotto 
210124a2c1a4Smillert 	for (;;) {
2102dea75673Skrw 		ui = getuint(lp, "fragment size",
21033c92d7f2Stedu 		    "Size of fs block fragments.  Usually 2048 or 512.",
2104ddfcbf38Sotto 		    fsize, fsize, 0, 0);
21051e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
210624a2c1a4Smillert 			fputs("Command aborted\n", stderr);
210724a2c1a4Smillert 			return(1);
21081e0ad43cSotto 		} else if (ui == ULLONG_MAX)
210924a2c1a4Smillert 			fputs("Invalid entry\n", stderr);
211024a2c1a4Smillert 		else
211124a2c1a4Smillert 			break;
211224a2c1a4Smillert 	}
211324a2c1a4Smillert 	if (ui == 0)
211424a2c1a4Smillert 		puts("Zero fragment size implies zero block size");
2115ddfcbf38Sotto 	pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(ui, frag);
211624a2c1a4Smillert 	return(0);
211724a2c1a4Smillert }
211824a2c1a4Smillert 
211924a2c1a4Smillert int
21208809fabbSderaadt get_bsize(struct disklabel *lp, int partno)
212124a2c1a4Smillert {
21221e0ad43cSotto 	u_int64_t ui, bsize, frag, fsize;
212324a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
212424a2c1a4Smillert 
2125a4c87e64Skrw 	if (!expert || pp->p_fstype != FS_BSDFFS)
2126a4c87e64Skrw 		return (0);
2127a4c87e64Skrw 
212824a2c1a4Smillert 	/* Avoid dividing by zero... */
2129ddfcbf38Sotto 	if (pp->p_fragblock == 0)
213024a2c1a4Smillert 		return(1);
2131ddfcbf38Sotto 
2132ddfcbf38Sotto 	bsize = DISKLABELV1_FFS_BSIZE(pp->p_fragblock);
2133ddfcbf38Sotto 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
2134ddfcbf38Sotto 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
213524a2c1a4Smillert 
213624a2c1a4Smillert 	for (;;) {
2137dea75673Skrw 		ui = getuint(lp, "block size",
21383c92d7f2Stedu 		    "Size of filesystem blocks.  Usually 16384 or 4096.",
2139ddfcbf38Sotto 		    fsize * frag, fsize * frag,
214024a2c1a4Smillert 		    0, 0);
214124a2c1a4Smillert 
214224a2c1a4Smillert 		/* sanity checks */
21431e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
214424a2c1a4Smillert 			fputs("Command aborted\n", stderr);
214524a2c1a4Smillert 			return(1);
21461e0ad43cSotto 		} else if (ui == ULLONG_MAX)
214724a2c1a4Smillert 			fputs("Invalid entry\n", stderr);
214824a2c1a4Smillert 		else if (ui < getpagesize())
214924a2c1a4Smillert 			fprintf(stderr,
215024a2c1a4Smillert 			    "Error: block size must be at least as big "
215124a2c1a4Smillert 			    "as page size (%d).\n", getpagesize());
2152ddfcbf38Sotto 		else if (ui % fsize != 0)
215324a2c1a4Smillert 			fputs("Error: block size must be a multiple of the "
215424a2c1a4Smillert 			    "fragment size.\n", stderr);
2155ddfcbf38Sotto 		else if (ui / fsize < 1)
215624a2c1a4Smillert 			fputs("Error: block size must be at least as big as "
215724a2c1a4Smillert 			    "fragment size.\n", stderr);
215824a2c1a4Smillert 		else
215924a2c1a4Smillert 			break;
216024a2c1a4Smillert 	}
2161ddfcbf38Sotto 	pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(ui / frag, frag);
216224a2c1a4Smillert 	return(0);
216324a2c1a4Smillert }
216424a2c1a4Smillert 
216524a2c1a4Smillert int
21668809fabbSderaadt get_fstype(struct disklabel *lp, int partno)
216724a2c1a4Smillert {
216824a2c1a4Smillert 	char *p;
21691e0ad43cSotto 	u_int64_t ui;
217024a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
217124a2c1a4Smillert 
217224a2c1a4Smillert 	if (pp->p_fstype < FSMAXTYPES) {
2173c33fcabaSmillert 		p = getstring("FS type",
217424a2c1a4Smillert 		    "Filesystem type (usually 4.2BSD or swap)",
217524a2c1a4Smillert 		    fstypenames[pp->p_fstype]);
217624a2c1a4Smillert 		if (p == NULL) {
217724a2c1a4Smillert 			fputs("Command aborted\n", stderr);
217824a2c1a4Smillert 			return(1);
217924a2c1a4Smillert 		}
218024a2c1a4Smillert 		for (ui = 0; ui < FSMAXTYPES; ui++) {
218124a2c1a4Smillert 			if (!strcasecmp(p, fstypenames[ui])) {
218224a2c1a4Smillert 				pp->p_fstype = ui;
218324a2c1a4Smillert 				break;
218424a2c1a4Smillert 			}
218524a2c1a4Smillert 		}
218624a2c1a4Smillert 		if (ui >= FSMAXTYPES) {
218724a2c1a4Smillert 			printf("Unrecognized filesystem type '%s', treating as 'unknown'\n", p);
218824a2c1a4Smillert 			pp->p_fstype = FS_OTHER;
218924a2c1a4Smillert 		}
219024a2c1a4Smillert 	} else {
219124a2c1a4Smillert 		for (;;) {
2192dea75673Skrw 			ui = getuint(lp, "FS type (decimal)",
219324a2c1a4Smillert 			    "Filesystem type as a decimal number; usually 7 (4.2BSD) or 1 (swap).",
219424a2c1a4Smillert 			    pp->p_fstype, pp->p_fstype, 0, 0);
21951e0ad43cSotto 			if (ui == ULLONG_MAX - 1) {
219624a2c1a4Smillert 				fputs("Command aborted\n", stderr);
219724a2c1a4Smillert 				return(1);
21981e0ad43cSotto 			} if (ui == ULLONG_MAX)
219924a2c1a4Smillert 				fputs("Invalid entry\n", stderr);
220024a2c1a4Smillert 			else
220124a2c1a4Smillert 				break;
220224a2c1a4Smillert 		}
220324a2c1a4Smillert 		pp->p_fstype = ui;
220424a2c1a4Smillert 	}
220524a2c1a4Smillert 	return(0);
220624a2c1a4Smillert }
220724a2c1a4Smillert 
220824a2c1a4Smillert int
220934ae4198Skrw get_mp(struct disklabel *lp, int partno)
221024a2c1a4Smillert {
221124a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
2212ec9fde5fSkrw 	char *p;
2213ec9fde5fSkrw 	int i;
221424a2c1a4Smillert 
221534ae4198Skrw 	if (fstabfile && pp->p_fstype != FS_UNUSED &&
221624a2c1a4Smillert 	    pp->p_fstype != FS_SWAP && pp->p_fstype != FS_BOOT &&
221724a2c1a4Smillert 	    pp->p_fstype != FS_OTHER) {
2218ddaff619Smillert 		for (;;) {
2219c33fcabaSmillert 			p = getstring("mount point",
222024a2c1a4Smillert 			    "Where to mount this filesystem (ie: / /var /usr)",
222134ae4198Skrw 			    mountpoints[partno] ? mountpoints[partno] : "none");
222224a2c1a4Smillert 			if (p == NULL) {
222324a2c1a4Smillert 				fputs("Command aborted\n", stderr);
222424a2c1a4Smillert 				return(1);
222524a2c1a4Smillert 			}
2226ddaff619Smillert 			if (strcasecmp(p, "none") == 0) {
222734ae4198Skrw 				free(mountpoints[partno]);
222834ae4198Skrw 				mountpoints[partno] = NULL;
2229ddaff619Smillert 				break;
2230ddaff619Smillert 			}
2231ec9fde5fSkrw 			for (i = 0; i < MAXPARTITIONS; i++)
223293160b9bSkrw 				if (mountpoints[i] != NULL && i != partno &&
2233ec9fde5fSkrw 				    strcmp(p, mountpoints[i]) == 0)
2234ec9fde5fSkrw 					break;
2235ec9fde5fSkrw 			if (i < MAXPARTITIONS) {
223693160b9bSkrw 				fprintf(stderr, "'%c' already being mounted at "
223793160b9bSkrw 				    "'%s'\n", 'a'+i, p);
2238ec9fde5fSkrw 				break;
2239ec9fde5fSkrw 			}
2240ddaff619Smillert 			if (*p == '/') {
2241ddaff619Smillert 				/* XXX - might as well realloc */
224234ae4198Skrw 				free(mountpoints[partno]);
224334ae4198Skrw 				if ((mountpoints[partno] = strdup(p)) == NULL)
224424a2c1a4Smillert 					errx(4, "out of memory");
2245ddaff619Smillert 				break;
2246ddaff619Smillert 			}
2247ddaff619Smillert 			fputs("Mount points must start with '/'\n", stderr);
224824a2c1a4Smillert 		}
224924a2c1a4Smillert 	}
225024a2c1a4Smillert 	return(0);
225124a2c1a4Smillert }
22523f843443Smillert 
22533f843443Smillert int
22548809fabbSderaadt micmp(const void *a1, const void *a2)
22553f843443Smillert {
22563f843443Smillert 	struct mountinfo *mi1 = (struct mountinfo *)a1;
22573f843443Smillert 	struct mountinfo *mi2 = (struct mountinfo *)a2;
22583f843443Smillert 
22593f843443Smillert 	/* We want all the NULLs at the end... */
22603f843443Smillert 	if (mi1->mountpoint == NULL && mi2->mountpoint == NULL)
22613f843443Smillert 		return(0);
22623f843443Smillert 	else if (mi1->mountpoint == NULL)
22633f843443Smillert 		return(1);
22643f843443Smillert 	else if (mi2->mountpoint == NULL)
22653f843443Smillert 		return(-1);
22663f843443Smillert 	else
22673f843443Smillert 		return(strcmp(mi1->mountpoint, mi2->mountpoint));
22683f843443Smillert }
2269c33fcabaSmillert 
2270c33fcabaSmillert void
227187023ed9Skrw get_geometry(int f, struct disklabel **dgpp)
2272c33fcabaSmillert {
2273c33fcabaSmillert 	struct stat st;
2274c33fcabaSmillert 	struct disklabel *disk_geop;
227587023ed9Skrw 
2276c33fcabaSmillert 	if (fstat(f, &st) == -1)
2277c33fcabaSmillert 		err(4, "Can't stat device");
2278c33fcabaSmillert 
2279c33fcabaSmillert 	/* Get disk geometry */
2280c33fcabaSmillert 	if ((disk_geop = calloc(1, sizeof(struct disklabel))) == NULL)
2281c33fcabaSmillert 		errx(4, "out of memory");
2282c33fcabaSmillert 	if (ioctl(f, DIOCGPDINFO, disk_geop) < 0 &&
2283c33fcabaSmillert 	    ioctl(f, DIOCGDINFO, disk_geop) < 0)
2284c33fcabaSmillert 		err(4, "ioctl DIOCGDINFO");
2285c33fcabaSmillert 	*dgpp = disk_geop;
2286c33fcabaSmillert }
2287c33fcabaSmillert 
2288c33fcabaSmillert void
22898809fabbSderaadt set_geometry(struct disklabel *lp, struct disklabel *dgp,
229087023ed9Skrw     struct disklabel *ugp, char *p)
2291c33fcabaSmillert {
2292c33fcabaSmillert 	if (p == NULL) {
22939a36aa41Ssthen 		p = getstring("[d]isk or [u]ser geometry",
2294c33fcabaSmillert 		    "Enter 'd' to use the geometry based on what the disk "
22959a36aa41Ssthen 		    "itself thinks it is, or 'u' to use the geometry that "
22969a36aa41Ssthen 		    "was found in the label.",
2297c33fcabaSmillert 		    "d");
2298c33fcabaSmillert 	}
2299c33fcabaSmillert 	if (p == NULL) {
2300c33fcabaSmillert 		fputs("Command aborted\n", stderr);
2301c33fcabaSmillert 		return;
2302c33fcabaSmillert 	}
2303c33fcabaSmillert 	switch (*p) {
2304c33fcabaSmillert 	case 'd':
2305c33fcabaSmillert 	case 'D':
2306c33fcabaSmillert 		if (dgp == NULL)
2307c33fcabaSmillert 			fputs("BIOS geometry not defined.\n", stderr);
2308c33fcabaSmillert 		else {
2309c33fcabaSmillert 			lp->d_secsize = dgp->d_secsize;
2310c33fcabaSmillert 			lp->d_nsectors = dgp->d_nsectors;
2311c33fcabaSmillert 			lp->d_ntracks = dgp->d_ntracks;
2312c33fcabaSmillert 			lp->d_ncylinders = dgp->d_ncylinders;
2313c33fcabaSmillert 			lp->d_secpercyl = dgp->d_secpercyl;
231434af67a3Sotto 			DL_SETDSIZE(lp, DL_GETDSIZE(dgp));
2315c33fcabaSmillert 		}
2316c33fcabaSmillert 		break;
2317c33fcabaSmillert 	case 'u':
2318c33fcabaSmillert 	case 'U':
2319c33fcabaSmillert 		if (ugp == NULL)
2320c33fcabaSmillert 			fputs("BIOS geometry not defined.\n", stderr);
2321c33fcabaSmillert 		else {
2322c33fcabaSmillert 			lp->d_secsize = ugp->d_secsize;
2323c33fcabaSmillert 			lp->d_nsectors = ugp->d_nsectors;
2324c33fcabaSmillert 			lp->d_ntracks = ugp->d_ntracks;
2325c33fcabaSmillert 			lp->d_ncylinders = ugp->d_ncylinders;
2326c33fcabaSmillert 			lp->d_secpercyl = ugp->d_secpercyl;
232734af67a3Sotto 			DL_SETDSIZE(lp, DL_GETDSIZE(ugp));
2328c33fcabaSmillert 			if (dgp != NULL && ugp->d_secsize == dgp->d_secsize &&
2329c33fcabaSmillert 			    ugp->d_nsectors == dgp->d_nsectors &&
2330c33fcabaSmillert 			    ugp->d_ntracks == dgp->d_ntracks &&
2331c33fcabaSmillert 			    ugp->d_ncylinders == dgp->d_ncylinders &&
2332c33fcabaSmillert 			    ugp->d_secpercyl == dgp->d_secpercyl &&
233334af67a3Sotto 			    DL_GETDSIZE(ugp) == DL_GETDSIZE(dgp))
2334c33fcabaSmillert 				fputs("Note: user geometry is the same as disk "
2335c33fcabaSmillert 				    "geometry.\n", stderr);
2336c33fcabaSmillert 		}
2337c33fcabaSmillert 		break;
2338c33fcabaSmillert 	default:
23399a36aa41Ssthen 		fputs("You must enter either 'd' or 'u'.\n", stderr);
2340c33fcabaSmillert 		break;
2341c33fcabaSmillert 	}
2342c33fcabaSmillert }
23439afbe9eeSmillert 
23449afbe9eeSmillert void
23459fdcb4d6Skrw zero_partitions(struct disklabel *lp)
23469afbe9eeSmillert {
23479afbe9eeSmillert 	int i;
23489afbe9eeSmillert 
2349b4ed6301Skrw 	for (i = 0; i < MAXPARTITIONS; i++) {
23509afbe9eeSmillert 		memset(&lp->d_partitions[i], 0, sizeof(struct partition));
2351b4ed6301Skrw 		free(mountpoints[i]);
2352b4ed6301Skrw 		mountpoints[i] = NULL;
2353b4ed6301Skrw 	}
2354b4ed6301Skrw 
235534af67a3Sotto 	DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
23569afbe9eeSmillert }
235714192793Skrw 
235814192793Skrw u_int64_t
235914192793Skrw max_partition_size(struct disklabel *lp, int partno)
236014192793Skrw {
236114192793Skrw 	struct partition *pp = &lp->d_partitions[partno];
236214192793Skrw 	struct diskchunk *chunks;
236344ffe03bSotto 	u_int64_t maxsize = 0, offset;
236414192793Skrw 	int fstype, i;
236514192793Skrw 
236614192793Skrw 	fstype = pp->p_fstype;
236714192793Skrw 	pp->p_fstype = FS_UNUSED;
236814192793Skrw 	chunks = free_chunks(lp);
236914192793Skrw 	pp->p_fstype = fstype;
237014192793Skrw 
237114192793Skrw 	offset = DL_GETPOFFSET(pp);
237214192793Skrw 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
237314192793Skrw 		if (offset < chunks[i].start || offset >= chunks[i].stop)
237414192793Skrw 			continue;
237514192793Skrw 		maxsize = chunks[i].stop - offset;
237614192793Skrw 		break;
237714192793Skrw 	}
237814192793Skrw 	return (maxsize);
237914192793Skrw }
2380aff3f969Sotto 
2381aff3f969Sotto void
2382aff3f969Sotto psize(daddr64_t sz, char unit, struct disklabel *lp)
2383aff3f969Sotto {
2384aff3f969Sotto 	double d = scale(sz, unit, lp);
2385aff3f969Sotto 	if (d < 0)
2386aff3f969Sotto 		printf("%llu", sz);
2387aff3f969Sotto 	else
2388aff3f969Sotto 		printf("%.*f%c", unit == 'B' ? 0 : 1, d, unit);
2389aff3f969Sotto }
2390aff3f969Sotto 
2391aff3f969Sotto void
239234ae4198Skrw display_edit(struct disklabel *lp, char unit, u_int64_t fr)
2393aff3f969Sotto {
2394aff3f969Sotto 	int i;
2395aff3f969Sotto 
2396352d199bSkrw 	unit = canonical_unit(lp, unit);
2397aff3f969Sotto 
2398aff3f969Sotto 	printf("OpenBSD area: ");
239959882f1dSkrw 	psize(starting_sector, 0, lp);
2400aff3f969Sotto 	printf("-");
240159882f1dSkrw 	psize(ending_sector, 0, lp);
2402aff3f969Sotto 	printf("; size: ");
2403aff3f969Sotto 	psize(ending_sector - starting_sector, unit, lp);
2404aff3f969Sotto 	printf("; free: ");
2405aff3f969Sotto 	psize(fr, unit, lp);
2406aff3f969Sotto 
2407aff3f969Sotto 	printf("\n#    %16.16s %16.16s  fstype [fsize bsize  cpg]\n",
2408aff3f969Sotto 	    "size", "offset");
2409aff3f969Sotto 	for (i = 0; i < lp->d_npartitions; i++)
241034ae4198Skrw 		display_partition(stdout, lp, i, unit);
2411aff3f969Sotto }
2412aff3f969Sotto 
2413