xref: /openbsd/sbin/disklabel/editor.c (revision ab30cb2f)
1*ab30cb2fSdoug /*	$OpenBSD: editor.c,v 1.288 2014/10/11 03:08:26 doug 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>
2391f4f7d8Sdlg #include <sys/dkio.h>
248dde8bb6Sotto #include <sys/sysctl.h>
256fe57b42Smillert #define	DKTYPENAMES
266fe57b42Smillert #include <sys/disklabel.h>
276fe57b42Smillert 
286fe57b42Smillert #include <ufs/ffs/fs.h>
296fe57b42Smillert 
306fe57b42Smillert #include <ctype.h>
316fe57b42Smillert #include <err.h>
326fe57b42Smillert #include <errno.h>
336fe57b42Smillert #include <string.h>
34803ff7d5Smillert #include <libgen.h>
356fe57b42Smillert #include <stdio.h>
366fe57b42Smillert #include <stdlib.h>
376fe57b42Smillert #include <unistd.h>
386fe57b42Smillert 
39f21a098bSotto #include "extern.h"
404793b14cSmillert #include "pathnames.h"
414793b14cSmillert 
42117239d3Skrw /* flags for getuint64() */
436fe57b42Smillert #define	DO_CONVERSIONS	0x00000001
446fe57b42Smillert #define	DO_ROUNDING	0x00000002
456fe57b42Smillert 
46f98aebd4Smillert #ifndef NUMBOOT
47f98aebd4Smillert #define NUMBOOT 0
48f98aebd4Smillert #endif
49f98aebd4Smillert 
5096a888c6Smillert /* structure to describe a portion of a disk */
5196a888c6Smillert struct diskchunk {
521e0ad43cSotto 	u_int64_t start;
531e0ad43cSotto 	u_int64_t stop;
5496a888c6Smillert };
5596a888c6Smillert 
563f843443Smillert /* used when sorting mountpoints in mpsave() */
573f843443Smillert struct mountinfo {
583f843443Smillert 	char *mountpoint;
593f843443Smillert 	int partno;
603f843443Smillert };
613f843443Smillert 
62557f712bSkrw /* used when allocating all space according to recommendations */
63557f712bSkrw 
64557f712bSkrw struct space_allocation {
65eafadddfSkrw 	u_int64_t	minsz;	/* starts as blocks, xlated to sectors. */
66eafadddfSkrw 	u_int64_t	maxsz;	/* starts as blocks, xlated to sectors. */
67557f712bSkrw 	int		rate;	/* % of extra space to use */
68557f712bSkrw 	char	       *mp;
69557f712bSkrw };
70557f712bSkrw 
718dde8bb6Sotto /* entries for swap and var are changed by editor_allocspace() */
728dde8bb6Sotto const struct space_allocation alloc_big[] = {
7392adb55fSderaadt 	{   MEG(80),         GIG(1),   5, "/"		},
74559340afSotto 	{   MEG(80),       MEG(256),  10, "swap"	},
754ab111d2Sderaadt 	{  MEG(120),         GIG(4),   8, "/tmp"	},
7692adb55fSderaadt 	{   MEG(80),         GIG(4),  13, "/var"	},
7721a0d117Sderaadt 	{  MEG(900),         GIG(2),   5, "/usr"	},
7892adb55fSderaadt 	{  MEG(512),         GIG(1),   3, "/usr/X11R6"	},
796e1f4775Sotto 	{    GIG(2),        GIG(10),  10, "/usr/local"	},
802cb42124Sotto 	{    GIG(1),         GIG(2),   2, "/usr/src"	},
81001bee15Sotto #ifdef STATICLINKING
82001bee15Sotto 	{ MEG(2600),         GIG(3),   4, "/usr/obj"	},
83001bee15Sotto #else
842cb42124Sotto 	{ MEG(1300),         GIG(2),   4, "/usr/obj"	},
85001bee15Sotto #endif
86559340afSotto 	{    GIG(1),       GIG(300),  40, "/home"	}
874ab111d2Sderaadt 	/* Anything beyond this leave for the user to decide */
888dde8bb6Sotto };
898dde8bb6Sotto 
908dde8bb6Sotto const struct space_allocation alloc_medium[] = {
91905e8239Sderaadt 	{  MEG(800),         GIG(2),   5, "/"		},
928dde8bb6Sotto 	{   MEG(80),       MEG(256),  10, "swap"	},
93905e8239Sderaadt 	{  MEG(900),         GIG(3),  78, "/usr"	},
94905e8239Sderaadt 	{  MEG(256),         GIG(2),   7, "/home"	}
958dde8bb6Sotto };
968dde8bb6Sotto 
978dde8bb6Sotto const struct space_allocation alloc_small[] = {
9892adb55fSderaadt 	{  MEG(700),         GIG(4),  95, "/"		},
998dde8bb6Sotto 	{    MEG(1),       MEG(256),   5, "swap"	}
1008dde8bb6Sotto };
1018dde8bb6Sotto 
1028dde8bb6Sotto const struct space_allocation alloc_stupid[] = {
1038dde8bb6Sotto 	{    MEG(1),      MEG(2048), 100, "/"		}
1048dde8bb6Sotto };
1058dde8bb6Sotto 
106b2813ff1Sderaadt #ifndef nitems
107b2813ff1Sderaadt #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
108b2813ff1Sderaadt #endif
109b2813ff1Sderaadt 
1108a862940Sderaadt const struct {
1118a862940Sderaadt 	const struct space_allocation *table;
1128a862940Sderaadt 	int sz;
1138a862940Sderaadt } alloc_table[] = {
1148dde8bb6Sotto 	{ alloc_big,	nitems(alloc_big) },
1158dde8bb6Sotto 	{ alloc_medium,	nitems(alloc_medium) },
1168dde8bb6Sotto 	{ alloc_small,	nitems(alloc_small) },
1178dde8bb6Sotto 	{ alloc_stupid,	nitems(alloc_stupid) }
118557f712bSkrw };
119557f712bSkrw 
1209fdcb4d6Skrw void	edit_parms(struct disklabel *);
1216aaa4aabSotto void	editor_resize(struct disklabel *, char *);
12234ae4198Skrw void	editor_add(struct disklabel *, char *);
1239fdcb4d6Skrw void	editor_change(struct disklabel *, char *);
1249fdcb4d6Skrw u_int64_t editor_countfree(struct disklabel *);
12534ae4198Skrw void	editor_delete(struct disklabel *, char *);
1264d812bb6Slum void	editor_help(void);
12734ae4198Skrw void	editor_modify(struct disklabel *, char *);
12834ae4198Skrw void	editor_name(struct disklabel *, char *);
129c72b5b24Smillert char	*getstring(char *, char *, char *);
130117239d3Skrw u_int64_t getuint64(struct disklabel *, char *, char *, u_int64_t, u_int64_t,
131430e1380Skrw 	    u_int64_t, int);
1321f0f871dSkrw int	has_overlap(struct disklabel *);
133c72b5b24Smillert int	partition_cmp(const void *, const void *);
1340fbd3c97Skrw struct partition **sort_partitions(struct disklabel *);
135c72b5b24Smillert void	getdisktype(struct disklabel *, char *, char *);
13687023ed9Skrw void	find_bounds(struct disklabel *);
1379fdcb4d6Skrw void	set_bounds(struct disklabel *);
13869a6ffbcSjsing void	set_duid(struct disklabel *);
139c72b5b24Smillert struct diskchunk *free_chunks(struct disklabel *);
140c72b5b24Smillert int	micmp(const void *, const void *);
141c72b5b24Smillert int	mpequal(char **, char **);
142c72b5b24Smillert int	get_bsize(struct disklabel *, int);
143c72b5b24Smillert int	get_fsize(struct disklabel *, int);
144c72b5b24Smillert int	get_fstype(struct disklabel *, int);
14534ae4198Skrw int	get_mp(struct disklabel *, int);
146604d3bdeSkrw int	get_offset(struct disklabel *, int);
1479fdcb4d6Skrw int	get_size(struct disklabel *, int);
14887023ed9Skrw void	get_geometry(int, struct disklabel **);
14987023ed9Skrw void	set_geometry(struct disklabel *, struct disklabel *, struct disklabel *,
15087023ed9Skrw 	    char *);
1519fdcb4d6Skrw void	zero_partitions(struct disklabel *);
15214192793Skrw u_int64_t max_partition_size(struct disklabel *, int);
15334ae4198Skrw void	display_edit(struct disklabel *, char, u_int64_t);
154e07939ebSderaadt int64_t	getphysmem(void);
155e07939ebSderaadt void	psize(u_int64_t sz, char unit, struct disklabel *lp);
15696a888c6Smillert 
1571e0ad43cSotto static u_int64_t starting_sector;
1581e0ad43cSotto static u_int64_t ending_sector;
1592d8451b0Smillert static int expert;
160fc6e9c48Sotto static int overlap;
1616fe57b42Smillert 
1626fe57b42Smillert /*
1634d4a335eSkrw  * Simple partition editor.
1646fe57b42Smillert  */
1656fe57b42Smillert int
166d6d80bb0Skrw editor(int f)
1676fe57b42Smillert {
168d6d80bb0Skrw 	struct disklabel origlabel, lastlabel, tmplabel, newlab = lab;
1697e28fb0fSderaadt 	struct disklabel *disk_geop = NULL;
17096a888c6Smillert 	struct partition *pp;
1716fe57b42Smillert 	FILE *fp;
1726fe57b42Smillert 	char buf[BUFSIZ], *cmd, *arg;
17334ae4198Skrw 	char **omountpoints = NULL;
1744f3bbbf0Skrw 	char **origmountpoints = NULL, **tmpmountpoints = NULL;
1757e28fb0fSderaadt 	int i, error = 0;
176bd6726faSmillert 
177bd6726faSmillert 	/* Alloc and init mount point info */
17834ae4198Skrw 	if (!(omountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
1794f3bbbf0Skrw 	    !(origmountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
180bd6726faSmillert 	    !(tmpmountpoints = calloc(MAXPARTITIONS, sizeof(char *))))
181bd6726faSmillert 		errx(4, "out of memory");
1826fe57b42Smillert 
18396a888c6Smillert 	/* Don't allow disk type of "unknown" */
184430e1380Skrw 	getdisktype(&newlab, "You need to specify a type for this disk.",
185430e1380Skrw 	    specname);
1866fe57b42Smillert 
18787023ed9Skrw 	/* Get the on-disk geometries if possible */
18887023ed9Skrw 	get_geometry(f, &disk_geop);
189c33fcabaSmillert 
190d09f3941Smillert 	/* How big is the OpenBSD portion of the disk?  */
191d6d80bb0Skrw 	find_bounds(&newlab);
192d09f3941Smillert 
19396a888c6Smillert 	/* Make sure there is no partition overlap. */
194d6d80bb0Skrw 	if (has_overlap(&newlab))
1956fe57b42Smillert 		errx(1, "can't run when there is partition overlap.");
1966fe57b42Smillert 
19796a888c6Smillert 	/* If we don't have a 'c' partition, create one. */
198d6d80bb0Skrw 	pp = &newlab.d_partitions[RAW_PART];
199d6d80bb0Skrw 	if (newlab.d_npartitions < 3 || DL_GETPSIZE(pp) == 0) {
20096a888c6Smillert 		puts("No 'c' partition found, adding one that spans the disk.");
201d6d80bb0Skrw 		if (newlab.d_npartitions < 3)
202d6d80bb0Skrw 			newlab.d_npartitions = 3;
20334af67a3Sotto 		DL_SETPOFFSET(pp, 0);
204d6d80bb0Skrw 		DL_SETPSIZE(pp, DL_GETDSIZE(&newlab));
20596a888c6Smillert 		pp->p_fstype = FS_UNUSED;
206ddfcbf38Sotto 		pp->p_fragblock = pp->p_cpg = 0;
20796a888c6Smillert 	}
2080f820bbbSmillert 
209fc1a4cc6Sderaadt #ifdef SUN_CYLCHECK
210479754bbSbluhm 	if ((newlab.d_flags & D_VENDOR) && !aflag) {
211fc1a4cc6Sderaadt 		puts("This platform requires that partition offsets/sizes "
212fc1a4cc6Sderaadt 		    "be on cylinder boundaries.\n"
213fc1a4cc6Sderaadt 		    "Partition offsets/sizes will be rounded to the "
214fc1a4cc6Sderaadt 		    "nearest cylinder automatically.");
215fc1a4cc6Sderaadt 	}
2166fe57b42Smillert #endif
2176fe57b42Smillert 
218bd6726faSmillert 	/* Set d_bbsize and d_sbsize as necessary */
219d6d80bb0Skrw 	if (newlab.d_bbsize == 0)
220d6d80bb0Skrw 		newlab.d_bbsize = BBSIZE;
221d6d80bb0Skrw 	if (newlab.d_sbsize == 0)
222d6d80bb0Skrw 		newlab.d_sbsize = SBSIZE;
223f98aebd4Smillert 
22493160b9bSkrw 	/* Save the (U|u)ndo labels and mountpoints. */
22593160b9bSkrw 	mpcopy(origmountpoints, mountpoints);
226d6d80bb0Skrw 	origlabel = newlab;
227d6d80bb0Skrw 	lastlabel = newlab;
22834ae4198Skrw 
22934ae4198Skrw 	puts("Label editor (enter '?' for help at any prompt)");
2306fe57b42Smillert 	for (;;) {
2316fe57b42Smillert 		fputs("> ", stdout);
2326e0becc5Smillert 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
2336e0becc5Smillert 			putchar('\n');
2346e0becc5Smillert 			buf[0] = 'q';
2356e0becc5Smillert 			buf[1] = '\0';
2366e0becc5Smillert 		}
237260513deSmillert 		if ((cmd = strtok(buf, " \t\r\n")) == NULL)
238260513deSmillert 			continue;
239260513deSmillert 		arg = strtok(NULL, " \t\r\n");
2406fe57b42Smillert 
2414f3bbbf0Skrw 		if ((*cmd != 'u') && (*cmd != 'U')) {
2424f3bbbf0Skrw 			/*
2434f3bbbf0Skrw 			 * Save undo info in case the command tries to make
2444f3bbbf0Skrw 			 * changes but decides not to.
2454f3bbbf0Skrw 			 */
2464f3bbbf0Skrw 			tmplabel = lastlabel;
247d6d80bb0Skrw 			lastlabel = newlab;
2484f3bbbf0Skrw 			mpcopy(tmpmountpoints, omountpoints);
2494f3bbbf0Skrw 			mpcopy(omountpoints, mountpoints);
2504f3bbbf0Skrw 		}
2516fe57b42Smillert 
2524f3bbbf0Skrw 		switch (*cmd) {
2536fe57b42Smillert 		case '?':
254ea37abd3Sderaadt 		case 'h':
2554d812bb6Slum 			editor_help();
2566fe57b42Smillert 			break;
2576fe57b42Smillert 
258557f712bSkrw 		case 'A':
259d6d80bb0Skrw 			if (ioctl(f, DIOCGPDINFO, &newlab) == 0) {
260ef5288d7Skrw 				++aflag;
261d6d80bb0Skrw 				editor_allocspace(&newlab);
262ef5288d7Skrw 				--aflag;
263ab20a3eaSkrw 			} else
264d6d80bb0Skrw 				newlab = lastlabel;
265557f712bSkrw 			break;
2666fe57b42Smillert 		case 'a':
267d6d80bb0Skrw 			editor_add(&newlab, arg);
26896a888c6Smillert 			break;
26996a888c6Smillert 
27096a888c6Smillert 		case 'b':
271d6d80bb0Skrw 			set_bounds(&newlab);
2726fe57b42Smillert 			break;
2736fe57b42Smillert 
2746fe57b42Smillert 		case 'c':
275d6d80bb0Skrw 			editor_change(&newlab, arg);
2766fe57b42Smillert 			break;
2776fe57b42Smillert 
2789afbe9eeSmillert 		case 'D':
279d6d80bb0Skrw 			if (ioctl(f, DIOCGPDINFO, &newlab) == 0) {
28071bba4ecSkrw 				dflag = 1;
28193160b9bSkrw 				for (i=0; i<MAXPARTITIONS; i++) {
28293160b9bSkrw 					free(mountpoints[i]);
28393160b9bSkrw 					mountpoints[i] = NULL;
28493160b9bSkrw 				}
28593160b9bSkrw 			} else
286cdd7eb76Smillert 				warn("unable to get default partition table");
2879afbe9eeSmillert 			break;
2889afbe9eeSmillert 
2896fe57b42Smillert 		case 'd':
290d6d80bb0Skrw 			editor_delete(&newlab, arg);
2916fe57b42Smillert 			break;
2926fe57b42Smillert 
2939afbe9eeSmillert 		case 'e':
294d6d80bb0Skrw 			edit_parms(&newlab);
2959afbe9eeSmillert 			break;
2969afbe9eeSmillert 
297c33fcabaSmillert 		case 'g':
298d6d80bb0Skrw 			set_geometry(&newlab, disk_geop, &lab, arg);
299c33fcabaSmillert 			break;
300c33fcabaSmillert 
3010d63cfbaSjsing 		case 'i':
302d6d80bb0Skrw 			set_duid(&newlab);
3030d63cfbaSjsing 			break;
3040d63cfbaSjsing 
3056fe57b42Smillert 		case 'm':
306d6d80bb0Skrw 			editor_modify(&newlab, arg);
307bd6726faSmillert 			break;
308bd6726faSmillert 
309bd6726faSmillert 		case 'n':
3105c79e1cfSkrw 			if (!fstabfile) {
311bd6726faSmillert 				fputs("This option is not valid when run "
31284d0bb16Sderaadt 				    "without the -f flag.\n", stderr);
313bd6726faSmillert 				break;
314bd6726faSmillert 			}
315d6d80bb0Skrw 			editor_name(&newlab, arg);
3166fe57b42Smillert 			break;
3176fe57b42Smillert 
3186fe57b42Smillert 		case 'p':
319d6d80bb0Skrw 			display_edit(&newlab, arg ? *arg : 0,
320d6d80bb0Skrw 			    editor_countfree(&newlab));
3216fe57b42Smillert 			break;
3226fe57b42Smillert 
323aff3f969Sotto 		case 'l':
324d6d80bb0Skrw 			display(stdout, &newlab, arg ? *arg : 0, 0);
325aff3f969Sotto 			break;
326aff3f969Sotto 
327508086e9Smillert 		case 'M': {
328508086e9Smillert 			sig_t opipe = signal(SIGPIPE, SIG_IGN);
32945decb36Sderaadt 			char *pager, *comm = NULL;
330e7936562Sderaadt 			extern const u_char manpage[];
33108f8e31fSotto 			extern const int manpage_sz;
3325d12b01bSderaadt 
333489bd112Spjanzen 			if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
334508086e9Smillert 				pager = _PATH_LESS;
33508f8e31fSotto 
33645decb36Sderaadt 			if (asprintf(&comm, "gunzip -qc|%s", pager) != -1 &&
33745decb36Sderaadt 			    (fp = popen(comm, "w")) != NULL) {
33808f8e31fSotto 				(void) fwrite(manpage, manpage_sz, 1, fp);
3395d12b01bSderaadt 				pclose(fp);
340508086e9Smillert 			} else
341508086e9Smillert 				warn("unable to execute %s", pager);
342508086e9Smillert 
34345decb36Sderaadt 			free(comm);
344508086e9Smillert 			(void)signal(SIGPIPE, opipe);
3455d12b01bSderaadt 			break;
346508086e9Smillert 		}
3475d12b01bSderaadt 
3486fe57b42Smillert 		case 'q':
34969220492Smillert 			if (donothing) {
35069220492Smillert 				puts("In no change mode, not writing label.");
3517e28fb0fSderaadt 				goto done;
35269220492Smillert 			}
35393160b9bSkrw 
35471bba4ecSkrw                         /*
35593160b9bSkrw 			 * If we haven't changed the original label, and it
35693160b9bSkrw 			 * wasn't a default label or an auto-allocated label,
35793160b9bSkrw 			 * there is no need to do anything before exiting. Note
35893160b9bSkrw 			 * that 'w' will reset dflag and aflag to allow 'q' to
35993160b9bSkrw 			 * exit without further questions.
36071bba4ecSkrw 			 */
361ab20a3eaSkrw 			if (!dflag && !aflag &&
362d6d80bb0Skrw 			    memcmp(&lab, &newlab, sizeof(newlab)) == 0) {
363bd6726faSmillert 				puts("No label changes.");
364d6d80bb0Skrw 				/* Save mountpoint info. */
365d6d80bb0Skrw 				mpsave(&newlab);
3667e28fb0fSderaadt 				goto done;
3676fe57b42Smillert 			}
3686fe57b42Smillert 			do {
369d0e67762Smillert 				arg = getstring("Write new label?",
370d0e67762Smillert 				    "Write the modified label to disk?",
371d0e67762Smillert 				    "y");
372025f5691Sderaadt 			} while (arg && tolower((unsigned char)*arg) != 'y' &&
373025f5691Sderaadt 			    tolower((unsigned char)*arg) != 'n');
374025f5691Sderaadt 			if (arg && tolower((unsigned char)*arg) == 'y') {
375d6d80bb0Skrw 				if (writelabel(f, bootarea, &newlab) == 0) {
376d6d80bb0Skrw 					newlab = lab; /* lab now has UID info */
3777e28fb0fSderaadt 					goto done;
3786fe57b42Smillert 				}
379d0e67762Smillert 				warnx("unable to write label");
380d0e67762Smillert 			}
3817e28fb0fSderaadt 			error = 1;
3827e28fb0fSderaadt 			goto done;
3836fe57b42Smillert 			/* NOTREACHED */
3846fe57b42Smillert 			break;
3856fe57b42Smillert 
3866aaa4aabSotto 		case 'R':
387fc6e9c48Sotto 			if (aflag && !overlap)
388d6d80bb0Skrw 				editor_resize(&newlab, arg);
3896aaa4aabSotto 			else
3906aaa4aabSotto 				fputs("Resize only implemented for auto "
391fc6e9c48Sotto 				    "allocated labels\n", stderr);
3926aaa4aabSotto 			break;
3936aaa4aabSotto 
39425f9c360Skrw 		case 'r': {
39525f9c360Skrw 			struct diskchunk *chunks;
39625f9c360Skrw 			int i;
3979fdcb4d6Skrw 			/* Display free space. */
398d6d80bb0Skrw 			chunks = free_chunks(&newlab);
39925f9c360Skrw 			for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0;
40025f9c360Skrw 			    i++)
40125f9c360Skrw 				fprintf(stderr, "Free sectors: %16llu - %16llu "
40225f9c360Skrw 				    "(%16llu)\n",
40325f9c360Skrw 				    chunks[i].start, chunks[i].stop - 1,
40425f9c360Skrw 				    chunks[i].stop - chunks[i].start);
40525f9c360Skrw 			fprintf(stderr, "Total free sectors: %llu.\n",
406d6d80bb0Skrw 			    editor_countfree(&newlab));
407c0bdc608Smillert 			break;
40825f9c360Skrw 		}
409c0bdc608Smillert 
4106fe57b42Smillert 		case 's':
4116fe57b42Smillert 			if (arg == NULL) {
412c33fcabaSmillert 				arg = getstring("Filename",
4136fe57b42Smillert 				    "Name of the file to save label into.",
4146fe57b42Smillert 				    NULL);
415e97229a3Scnst 				if (arg == NULL || *arg == '\0')
4166fe57b42Smillert 					break;
4176fe57b42Smillert 			}
4186fe57b42Smillert 			if ((fp = fopen(arg, "w")) == NULL) {
4196fe57b42Smillert 				warn("cannot open %s", arg);
4206fe57b42Smillert 			} else {
421d6d80bb0Skrw 				display(fp, &newlab, 0, 1);
4226fe57b42Smillert 				(void)fclose(fp);
4236fe57b42Smillert 			}
4246fe57b42Smillert 			break;
4256fe57b42Smillert 
4264f3bbbf0Skrw 		case 'U':
42793160b9bSkrw 			/*
428430e1380Skrw 			 * If we allow 'U' repeatedly, information would be
429430e1380Skrw 			 * lost. This way multiple 'U's followed by 'u' will
430430e1380Skrw 			 * undo the 'U's.
43193160b9bSkrw 			 */
432d6d80bb0Skrw 			if (memcmp(&newlab, &origlabel, sizeof(newlab)) ||
43393160b9bSkrw 			    !mpequal(mountpoints, origmountpoints)) {
434d6d80bb0Skrw 				tmplabel = newlab;
435d6d80bb0Skrw 				newlab = origlabel;
4364f3bbbf0Skrw 				lastlabel = tmplabel;
4374f3bbbf0Skrw 				mpcopy(tmpmountpoints, mountpoints);
4384f3bbbf0Skrw 				mpcopy(mountpoints, origmountpoints);
4394f3bbbf0Skrw 				mpcopy(omountpoints, tmpmountpoints);
4404f3bbbf0Skrw 			}
44193160b9bSkrw 			puts("Original label and mount points restored.");
4424f3bbbf0Skrw 			break;
4434f3bbbf0Skrw 
4446fe57b42Smillert 		case 'u':
445d6d80bb0Skrw 			tmplabel = newlab;
446d6d80bb0Skrw 			newlab = lastlabel;
4476fe57b42Smillert 			lastlabel = tmplabel;
4484f3bbbf0Skrw 			mpcopy(tmpmountpoints, mountpoints);
449bd6726faSmillert 			mpcopy(mountpoints, omountpoints);
4504f3bbbf0Skrw 			mpcopy(omountpoints, tmpmountpoints);
4516fe57b42Smillert 			puts("Last change undone.");
4526fe57b42Smillert 			break;
4536fe57b42Smillert 
454040947cfSmillert 		case 'w':
455bd6726faSmillert 			if (donothing)  {
456040947cfSmillert 				puts("In no change mode, not writing label.");
457bd6726faSmillert 				break;
458bd6726faSmillert 			}
45993160b9bSkrw 
46041f684b9Skrw 			/* Write label to disk. */
461d6d80bb0Skrw 			if (writelabel(f, bootarea, &newlab) != 0)
462040947cfSmillert 				warnx("unable to write label");
46371bba4ecSkrw 			else {
464ab20a3eaSkrw 				dflag = aflag = 0;
465d6d80bb0Skrw 				newlab = lab; /* lab now has UID info */
46671bba4ecSkrw 			}
467040947cfSmillert 			break;
468040947cfSmillert 
4692d8451b0Smillert 		case 'X':
4702d8451b0Smillert 			expert = !expert;
4712d8451b0Smillert 			printf("%s expert mode\n", expert ? "Entering" :
4722d8451b0Smillert 			    "Exiting");
4732d8451b0Smillert 			break;
4742d8451b0Smillert 
4756fe57b42Smillert 		case 'x':
4767e28fb0fSderaadt 			goto done;
4776fe57b42Smillert 			break;
4786fe57b42Smillert 
4799afbe9eeSmillert 		case 'z':
480d6d80bb0Skrw 			zero_partitions(&newlab);
4816fe57b42Smillert 			break;
4826fe57b42Smillert 
4839afbe9eeSmillert 		case '\n':
4846fe57b42Smillert 			break;
4856fe57b42Smillert 
4866fe57b42Smillert 		default:
4876fe57b42Smillert 			printf("Unknown option: %c ('?' for help)\n", *cmd);
4886fe57b42Smillert 			break;
4896fe57b42Smillert 		}
4904f3bbbf0Skrw 
4914f3bbbf0Skrw 		/*
4924f3bbbf0Skrw 		 * If no changes were made to label or mountpoints, then
4934f3bbbf0Skrw 		 * restore undo info.
4944f3bbbf0Skrw 		 */
495d6d80bb0Skrw 		if (memcmp(&newlab, &lastlabel, sizeof(newlab)) == 0 &&
49693160b9bSkrw 		    (mpequal(mountpoints, omountpoints))) {
4974f3bbbf0Skrw 			lastlabel = tmplabel;
4984f3bbbf0Skrw 			mpcopy(omountpoints, tmpmountpoints);
4994f3bbbf0Skrw 		}
5006fe57b42Smillert 	}
5017e28fb0fSderaadt done:
502408ab9bcSkrw 	mpfree(omountpoints);
503408ab9bcSkrw 	mpfree(origmountpoints);
504408ab9bcSkrw 	mpfree(tmpmountpoints);
5057e28fb0fSderaadt 	if (disk_geop)
5067e28fb0fSderaadt 		free(disk_geop);
5077e28fb0fSderaadt 	return(error);
5086fe57b42Smillert }
5096fe57b42Smillert 
5108dde8bb6Sotto int64_t
5118dde8bb6Sotto getphysmem(void)
5128dde8bb6Sotto {
5138dde8bb6Sotto 	int64_t physmem;
5148dde8bb6Sotto 	size_t sz = sizeof(physmem);
5158dde8bb6Sotto 	int mib[] = { CTL_HW, HW_PHYSMEM64 };
516e07939ebSderaadt 
5178dde8bb6Sotto 	if (sysctl(mib, 2, &physmem, &sz, NULL, (size_t)0) == -1)
5188dde8bb6Sotto 		errx(4, "can't get mem size");
5198dde8bb6Sotto 	return physmem;
5208dde8bb6Sotto }
5218dde8bb6Sotto 
5226fe57b42Smillert /*
523557f712bSkrw  * Allocate all disk space according to standard recommendations for a
524557f712bSkrw  * root disk.
525557f712bSkrw  */
526557f712bSkrw void
5278dde8bb6Sotto editor_allocspace(struct disklabel *lp_org)
528557f712bSkrw {
5298dde8bb6Sotto 	struct disklabel *lp, label;
5308dde8bb6Sotto 	struct space_allocation *alloc;
53134ae4198Skrw 	struct space_allocation *ap;
532557f712bSkrw 	struct partition *pp;
53334ae4198Skrw 	struct diskchunk *chunks;
534eafadddfSkrw 	u_int64_t chunkstart, chunksize, cylsecs, secs, totsecs, xtrasecs;
53534ae4198Skrw 	char **partmp;
536a49bdda8Skrw 	int i, j, lastalloc, index = 0, fragsize, partno;
5378dde8bb6Sotto 	int64_t physmem;
5388dde8bb6Sotto 
53934ae4198Skrw 	/* How big is the OpenBSD portion of the disk?  */
5408dde8bb6Sotto 	find_bounds(lp_org);
541557f712bSkrw 
542fc6e9c48Sotto 	overlap = 0;
543fc6e9c48Sotto 	for (i = 0;  i < MAXPARTITIONS; i++) {
544eafadddfSkrw 		u_int64_t psz, pstart, pend;
545fc6e9c48Sotto 
546fc6e9c48Sotto 		pp = &lp_org->d_partitions[i];
547fc6e9c48Sotto 		psz = DL_GETPSIZE(pp);
548fc6e9c48Sotto 		pstart = DL_GETPOFFSET(pp);
549fc6e9c48Sotto 		pend = pstart + psz;
550fc6e9c48Sotto 		if (i != RAW_PART && psz != 0 &&
551fc6e9c48Sotto 		    ((pstart >= starting_sector && pstart <= ending_sector) ||
552fc6e9c48Sotto 		    (pend > starting_sector && pend < ending_sector))) {
553fc6e9c48Sotto 			overlap = 1;
554fc6e9c48Sotto 			break;
555fc6e9c48Sotto 		}
556fc6e9c48Sotto 	}
557fc6e9c48Sotto 
558930241e9Skrw 	physmem = getphysmem() / DEV_BSIZE;	/* Blocks not sectors here! */
559fc6e9c48Sotto 
5608dde8bb6Sotto 	cylsecs = lp_org->d_secpercyl;
5618dde8bb6Sotto again:
5628dde8bb6Sotto 	lp = &label;
5633d98fc8cSkrw 	for (i=0; i<MAXPARTITIONS; i++) {
5643d98fc8cSkrw 		free(mountpoints[i]);
5653d98fc8cSkrw 		mountpoints[i] = NULL;
5663d98fc8cSkrw 	}
5678dde8bb6Sotto 	memcpy(lp, lp_org, sizeof(struct disklabel));
5680a7398ceSderaadt 	lp->d_npartitions = MAXPARTITIONS;
5698dde8bb6Sotto 	lastalloc = alloc_table[index].sz;
570*ab30cb2fSdoug 	alloc = reallocarray(NULL, lastalloc, sizeof(struct space_allocation));
5718dde8bb6Sotto 	if (alloc == NULL)
5728dde8bb6Sotto 		errx(4, "out of memory");
5738dde8bb6Sotto 	memcpy(alloc, alloc_table[index].table,
5748dde8bb6Sotto 	    lastalloc * sizeof(struct space_allocation));
5758dde8bb6Sotto 
5768dde8bb6Sotto 	/* bump max swap based on phys mem, little physmem gets 2x swap */
5778dde8bb6Sotto 	if (index == 0) {
578930241e9Skrw 		if (physmem < MEG(256))
5798dde8bb6Sotto 			alloc[1].maxsz = 2 * physmem;
5808dde8bb6Sotto 		else
5818dde8bb6Sotto 			alloc[1].maxsz += physmem;
5828dde8bb6Sotto 		/* bump max /var to make room for 2 crash dumps */
5838dde8bb6Sotto 		alloc[3].maxsz += 2 * physmem;
5848dde8bb6Sotto 	}
5858dde8bb6Sotto 
58634ae4198Skrw 	xtrasecs = totsecs = editor_countfree(lp);
587557f712bSkrw 
58834ae4198Skrw 	for (i = 0; i < lastalloc; i++) {
5895f3e1104Skrw 		alloc[i].minsz = DL_BLKTOSEC(lp, alloc[i].minsz);
5905f3e1104Skrw 		alloc[i].maxsz = DL_BLKTOSEC(lp, alloc[i].maxsz);
59134ae4198Skrw 		if (xtrasecs > alloc[i].minsz)
59234ae4198Skrw 			xtrasecs -= alloc[i].minsz;
59334ae4198Skrw 		else
59434ae4198Skrw 			xtrasecs = 0;
595557f712bSkrw 	}
596557f712bSkrw 
59734ae4198Skrw 	for (i = 0; i < lastalloc; i++) {
59834ae4198Skrw 		/* Find next available partition. */
59934ae4198Skrw 		for (j = 0;  j < MAXPARTITIONS; j++)
60034ae4198Skrw 			if (DL_GETPSIZE(&lp->d_partitions[j]) == 0)
60134ae4198Skrw 				break;
602a243d7b5Skrw 		if (j == MAXPARTITIONS) {
603a243d7b5Skrw 			/* It did not work out, try next strategy */
604a243d7b5Skrw 			free(alloc);
605a243d7b5Skrw 			if (++index < nitems(alloc_table))
606a243d7b5Skrw 				goto again;
607a243d7b5Skrw 			else
60834ae4198Skrw 				return;
609a243d7b5Skrw 		}
610a49bdda8Skrw 		partno = j;
61134ae4198Skrw 		pp = &lp->d_partitions[j];
61234ae4198Skrw 		partmp = &mountpoints[j];
61334ae4198Skrw 		ap = &alloc[i];
614557f712bSkrw 
61534ae4198Skrw 		/* Figure out the size of the partition. */
61634ae4198Skrw 		if (i == lastalloc - 1) {
61734ae4198Skrw 			if (totsecs > ap->maxsz)
61834ae4198Skrw 				secs = ap->maxsz;
619557f712bSkrw 			else
6205f3e1104Skrw 				secs = totsecs;
6211f5ea549Skrw #ifdef SUN_CYLCHECK
6221f5ea549Skrw 			goto cylinderalign;
6231f5ea549Skrw #endif
624557f712bSkrw 		} else {
62534ae4198Skrw 			secs = ap->minsz;
6265f3e1104Skrw 			if (xtrasecs > 0)
62734ae4198Skrw 				secs += (xtrasecs / 100) * ap->rate;
62834ae4198Skrw 			if (secs > ap->maxsz)
62934ae4198Skrw 				secs = ap->maxsz;
6301f5ea549Skrw #ifdef SUN_CYLCHECK
6311f5ea549Skrw cylinderalign:
6325f3e1104Skrw 			secs = ((secs + cylsecs - 1) / cylsecs) * cylsecs;
6331f5ea549Skrw #endif
6345f3e1104Skrw 			totsecs -= secs;
6351f5ea549Skrw #ifdef SUN_CYLCHECK
6365f3e1104Skrw 			while (totsecs < 0) {
6375f3e1104Skrw 				secs -= cylsecs;
6385f3e1104Skrw 				totsecs += cylsecs;
639557f712bSkrw 			}
6401f5ea549Skrw #endif
641557f712bSkrw 		}
64234ae4198Skrw 
64334ae4198Skrw 		/* Find largest chunk of free space. */
64434ae4198Skrw 		chunks = free_chunks(lp);
64534ae4198Skrw 		chunkstart = 0;
64634ae4198Skrw 		chunksize = 0;
64734ae4198Skrw 		for (j = 0; chunks[j].start != 0 || chunks[j].stop != 0; j++)
64834ae4198Skrw 			if ((chunks[j].stop - chunks[j].start) > chunksize) {
64934ae4198Skrw 				chunkstart = chunks[j].start;
65034ae4198Skrw 				chunksize = chunks[j].stop - chunks[j].start;
651557f712bSkrw 			}
65234ae4198Skrw #ifdef SUN_CYLCHECK
65334ae4198Skrw 		if (lp->d_flags & D_VENDOR) {
65434ae4198Skrw 			/* Align chunk to cylinder boundaries. */
65534ae4198Skrw 			chunksize -= chunksize % cylsecs;
6566ab0bb66Skrw 			chunkstart = ((chunkstart + cylsecs - 1) / cylsecs) *
6576ab0bb66Skrw 			    cylsecs;
65834ae4198Skrw 		}
65934ae4198Skrw #endif
66034ae4198Skrw 		/* See if partition can fit into chunk. */
66134ae4198Skrw 		if (secs > chunksize) {
66234ae4198Skrw 			totsecs += secs - chunksize;
66334ae4198Skrw 			secs = chunksize;
66434ae4198Skrw 		}
66534ae4198Skrw 		if (secs < ap->minsz) {
6668dde8bb6Sotto 			/* It did not work out, try next strategy */
6678dde8bb6Sotto 			free(alloc);
6688dde8bb6Sotto 			if (++index < nitems(alloc_table))
6698dde8bb6Sotto 				goto again;
6708dde8bb6Sotto 			else
6718dde8bb6Sotto 				return;
672557f712bSkrw 		}
67334ae4198Skrw 
67434ae4198Skrw 		/* Everything seems ok so configure the partition. */
6755f3e1104Skrw 		DL_SETPSIZE(pp, secs);
67634ae4198Skrw 		DL_SETPOFFSET(pp, chunkstart);
677ef93a5eeSkrw 		fragsize = (lp->d_secsize == DEV_BSIZE) ? 2048 :
678ef93a5eeSkrw 		    lp->d_secsize;
679c88f83bdSotto 		if (secs * lp->d_secsize > 128ULL * 1024 * 1024 * 1024)
680c88f83bdSotto 			fragsize *= 2;
681c88f83bdSotto 		if (secs * lp->d_secsize > 512ULL * 1024 * 1024 * 1024)
682c88f83bdSotto 			fragsize *= 2;
683557f712bSkrw #if defined (__sparc__) && !defined(__sparc64__)
684557f712bSkrw 		/* can't boot from > 8k boot blocks */
685557f712bSkrw 		pp->p_fragblock =
6866e312d52Sotto 		    DISKLABELV1_FFS_FRAGBLOCK(i == 0 ? 1024 : fragsize, 8);
687557f712bSkrw #else
6886e312d52Sotto 		pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fragsize, 8);
689557f712bSkrw #endif
690557f712bSkrw 		pp->p_cpg = 1;
69134ae4198Skrw 		if (ap->mp[0] != '/')
69234ae4198Skrw 			pp->p_fstype = FS_SWAP;
69334ae4198Skrw 		else {
69434ae4198Skrw 			pp->p_fstype = FS_BSDFFS;
695a49bdda8Skrw 			get_bsize(lp, partno);
69634ae4198Skrw 			free(*partmp);
69734ae4198Skrw 			if ((*partmp = strdup(ap->mp)) == NULL)
698557f712bSkrw 				errx(4, "out of memory");
699557f712bSkrw 		}
700557f712bSkrw 	}
701557f712bSkrw 
7028dde8bb6Sotto 	free(alloc);
7038dde8bb6Sotto 	memcpy(lp_org, lp, sizeof(struct disklabel));
704557f712bSkrw }
705557f712bSkrw 
706557f712bSkrw /*
7076aaa4aabSotto  * Resize a partition, moving all subsequent partitions
7086aaa4aabSotto  */
7096aaa4aabSotto void
7106aaa4aabSotto editor_resize(struct disklabel *lp, char *p)
7116aaa4aabSotto {
7126aaa4aabSotto 	struct disklabel label;
7136aaa4aabSotto 	struct partition *pp, *prev;
714eafadddfSkrw 	u_int64_t secs, sz, off;
7156aaa4aabSotto #ifdef SUN_CYLCHECK
716eafadddfSkrw 	u_int64_t cylsecs;
7176aaa4aabSotto #endif
7186aaa4aabSotto 	int partno, i;
7196aaa4aabSotto 
7206aaa4aabSotto 	label = *lp;
7216aaa4aabSotto 
7226aaa4aabSotto 	/* Change which partition? */
7236aaa4aabSotto 	if (p == NULL) {
7246aaa4aabSotto 		p = getstring("partition to resize",
7256aaa4aabSotto 		    "The letter of the partition to name, a - p.", NULL);
7266aaa4aabSotto 	}
7276aaa4aabSotto 	if (p == NULL) {
7286aaa4aabSotto 		fputs("Command aborted\n", stderr);
7296aaa4aabSotto 		return;
7306aaa4aabSotto 	}
7316aaa4aabSotto 	partno = p[0] - 'a';
7326aaa4aabSotto         if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
7336aaa4aabSotto 		fprintf(stderr, "Partition must be between 'a' and '%c' "
7346aaa4aabSotto 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
7356aaa4aabSotto 		return;
7366aaa4aabSotto 	}
7376aaa4aabSotto 
7386aaa4aabSotto 	pp = &label.d_partitions[partno];
739fc6e9c48Sotto 	sz = DL_GETPSIZE(pp);
740fc6e9c48Sotto 	if (sz == 0) {
741fc6e9c48Sotto 		fputs("No such partition\n", stderr);
742fc6e9c48Sotto 		return;
743fc6e9c48Sotto 	}
744fc6e9c48Sotto 	if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP) {
745fc6e9c48Sotto 		fputs("Cannot resize spoofed partition\n", stderr);
746fc6e9c48Sotto 		return;
747fc6e9c48Sotto 	}
748117239d3Skrw 	secs = getuint64(lp, "[+|-]new size (with unit)",
7494659aa0dSotto 	    "new size or amount to grow (+) or shrink (-) partition including unit",
7504659aa0dSotto 	    sz, editor_countfree(lp), 0, DO_CONVERSIONS);
7516aaa4aabSotto 
7524659aa0dSotto 	if (secs <= 0) {
7536aaa4aabSotto 		fputs("Command aborted\n", stderr);
7546aaa4aabSotto 		return;
7556aaa4aabSotto 	}
7566aaa4aabSotto 
7576aaa4aabSotto #ifdef SUN_CYLCHECK
7586aaa4aabSotto 	cylsecs = lp->d_secpercyl;
7596aaa4aabSotto 	if (secs > 0)
7606aaa4aabSotto 		secs = ((secs + cylsecs - 1) / cylsecs) * cylsecs;
7616aaa4aabSotto 	else
7626aaa4aabSotto 		secs = ((secs - cylsecs + 1) / cylsecs) * cylsecs;
7636aaa4aabSotto #endif
7644659aa0dSotto 	if (DL_GETPOFFSET(pp) + secs > ending_sector) {
7656aaa4aabSotto 		fputs("Amount too big\n", stderr);
7666aaa4aabSotto 		return;
7676aaa4aabSotto 	}
7686aaa4aabSotto 
7694659aa0dSotto 	DL_SETPSIZE(pp, secs);
770e5f81948Sotto 	get_bsize(&label, partno);
7716aaa4aabSotto 
7726aaa4aabSotto 	/*
7736aaa4aabSotto 	 * Pack partitions above the resized partition, leaving unused
7746aaa4aabSotto 	 * partions alone.
7756aaa4aabSotto 	 */
7766aaa4aabSotto 	prev = pp;
7776aaa4aabSotto 	for (i = partno + 1; i < MAXPARTITIONS; i++) {
7786aaa4aabSotto 		if (i == RAW_PART)
7796aaa4aabSotto 			continue;
780fc6e9c48Sotto 		pp = &label.d_partitions[i];
781fc6e9c48Sotto 		if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP)
782fc6e9c48Sotto 			continue;
783fc6e9c48Sotto 		sz = DL_GETPSIZE(pp);
7846aaa4aabSotto 		if (sz == 0)
7856aaa4aabSotto 			continue;
7866aaa4aabSotto 
7876aaa4aabSotto 		off = DL_GETPOFFSET(prev) + DL_GETPSIZE(prev);
7886aaa4aabSotto 
7896aaa4aabSotto 		if (off < ending_sector) {
7906aaa4aabSotto 			DL_SETPOFFSET(pp, off);
7916aaa4aabSotto 			if (off + DL_GETPSIZE(pp) > ending_sector) {
7926aaa4aabSotto 				DL_SETPSIZE(pp, ending_sector - off);
7936aaa4aabSotto 				fprintf(stderr,
7946aaa4aabSotto 				    "Partition %c shrunk to make room\n",
7956aaa4aabSotto 				    i + 'a');
7966aaa4aabSotto 			}
7976aaa4aabSotto 		} else {
7986aaa4aabSotto 			fputs("No room left for all partitions\n", stderr);
7996aaa4aabSotto 			return;
8006aaa4aabSotto 		}
801e5f81948Sotto 		get_bsize(&label, i);
8026aaa4aabSotto 		prev = pp;
8036aaa4aabSotto 	}
8046aaa4aabSotto 	*lp = label;
8056aaa4aabSotto }
8066aaa4aabSotto 
8076aaa4aabSotto /*
8086fe57b42Smillert  * Add a new partition.
8096fe57b42Smillert  */
8106fe57b42Smillert void
81134ae4198Skrw editor_add(struct disklabel *lp, char *p)
8126fe57b42Smillert {
81396a888c6Smillert 	struct partition *pp;
81496a888c6Smillert 	struct diskchunk *chunks;
8155caa08b2Skrw 	char buf[2];
8166e312d52Sotto 	int i, partno, fragsize;
817f8ab7229Schl 	u_int64_t freesectors, new_offset, new_size;
8189fdcb4d6Skrw 
8199fdcb4d6Skrw 	freesectors = editor_countfree(lp);
8206fe57b42Smillert 
8216fe57b42Smillert 	/* XXX - prompt user to steal space from another partition instead */
822fc1a4cc6Sderaadt #ifdef SUN_CYLCHECK
8239fdcb4d6Skrw 	if ((lp->d_flags & D_VENDOR) && freesectors < lp->d_secpercyl) {
824fc1a4cc6Sderaadt 		fputs("No space left, you need to shrink a partition "
825fc1a4cc6Sderaadt 		    "(need at least one full cylinder)\n",
826fc1a4cc6Sderaadt 		    stderr);
827fc1a4cc6Sderaadt 		return;
828fc1a4cc6Sderaadt 	}
8298390cf28Smillert #endif
8309fdcb4d6Skrw 	if (freesectors == 0) {
8316fe57b42Smillert 		fputs("No space left, you need to shrink a partition\n",
8326fe57b42Smillert 		    stderr);
8336fe57b42Smillert 		return;
8346fe57b42Smillert 	}
8356fe57b42Smillert 
8365caa08b2Skrw 	if (p == NULL) {
8375caa08b2Skrw 		/*
8385caa08b2Skrw 		 * Use the first unused partition that is not 'c' as the
8395caa08b2Skrw 		 * default partition in the prompt string.
8405caa08b2Skrw 		 */
8415caa08b2Skrw 		pp = &lp->d_partitions[0];
8425caa08b2Skrw 		buf[0] = buf[1] = '\0';
8435caa08b2Skrw 		for (partno = 0; partno < MAXPARTITIONS; partno++, pp++) {
8445caa08b2Skrw 			if (DL_GETPSIZE(pp) == 0 && partno != RAW_PART) {
8455caa08b2Skrw 				buf[0] = partno + 'a';
8465caa08b2Skrw 				p = &buf[0];
8476fe57b42Smillert 				break;
8486fe57b42Smillert 			}
8495caa08b2Skrw 		}
850c33fcabaSmillert 		p = getstring("partition",
8516fe57b42Smillert 		    "The letter of the new partition, a - p.", p);
8526fe57b42Smillert 	}
8535caa08b2Skrw 	if (p == NULL) {
8545caa08b2Skrw 		fputs("Command aborted\n", stderr);
8555caa08b2Skrw 		return;
8565caa08b2Skrw 	}
8575caa08b2Skrw 	partno = p[0] - 'a';
8585caa08b2Skrw 	if (partno < 0 || partno == RAW_PART || partno >= MAXPARTITIONS) {
8595caa08b2Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
8605caa08b2Skrw 		    "(excluding 'c').\n", 'a' + MAXPARTITIONS - 1);
8615caa08b2Skrw 		return;
8625caa08b2Skrw 	}
8635caa08b2Skrw 	pp = &lp->d_partitions[partno];
8645caa08b2Skrw 
8655caa08b2Skrw 	if (pp->p_fstype != FS_UNUSED && DL_GETPSIZE(pp) != 0) {
8665caa08b2Skrw 		fprintf(stderr, "Partition '%c' exists.  Delete it first.\n",
8675caa08b2Skrw 		    p[0]);
8685caa08b2Skrw 		return;
8696fe57b42Smillert 	}
87096a888c6Smillert 
871caf41f96Skrw 	/*
872caf41f96Skrw 	 * Increase d_npartitions if necessary. Ensure all new partitions are
873855d4e83Ssobrado 	 * zero'ed to avoid inadvertent overlaps.
874caf41f96Skrw 	 */
875caf41f96Skrw 	for(; lp->d_npartitions <= partno; lp->d_npartitions++)
876caf41f96Skrw 		memset(&lp->d_partitions[lp->d_npartitions], 0, sizeof(*pp));
87796a888c6Smillert 
87889f4601dSkrw 	/* Make sure selected partition is zero'd too. */
87989f4601dSkrw 	memset(pp, 0, sizeof(*pp));
88015c15d8aSkrw 	chunks = free_chunks(lp);
88115c15d8aSkrw 
88215c15d8aSkrw 	/*
88315c15d8aSkrw 	 * Since we know there's free space, there must be at least one
88415c15d8aSkrw 	 * chunk. So find the largest chunk and assume we want to add the
88515c15d8aSkrw 	 * partition in that free space.
88615c15d8aSkrw 	 */
88715c15d8aSkrw 	new_size = new_offset = 0;
88815c15d8aSkrw 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
88915c15d8aSkrw 		if (chunks[i].stop - chunks[i].start > new_size) {
89015c15d8aSkrw 		    new_size = chunks[i].stop - chunks[i].start;
89115c15d8aSkrw 		    new_offset = chunks[i].start;
89215c15d8aSkrw 		}
89315c15d8aSkrw 	}
8941e0ad43cSotto 	DL_SETPSIZE(pp, new_size);
8951e0ad43cSotto 	DL_SETPOFFSET(pp, new_offset);
89696a888c6Smillert 	pp->p_fstype = partno == 1 ? FS_SWAP : FS_BSDFFS;
897c88f83bdSotto 	pp->p_cpg = 1;
898c88f83bdSotto 
899c88f83bdSotto 	if (get_offset(lp, partno) == 0 &&
900c88f83bdSotto 	    get_size(lp, partno) == 0) {
901d476f177Skrw 		fragsize = (lp->d_secsize == DEV_BSIZE) ? 2048 :
902d476f177Skrw 		    lp->d_secsize;
903c88f83bdSotto 		new_size = DL_GETPSIZE(pp) * lp->d_secsize;
904c88f83bdSotto 		if (new_size > 128ULL * 1024 * 1024 * 1024)
905c88f83bdSotto 			fragsize *= 2;
906c88f83bdSotto 		if (new_size > 512ULL * 1024 * 1024 * 1024)
907c88f83bdSotto 			fragsize *= 2;
9089b84e584Sotto 		if (fragsize > MAXBSIZE / 8)
9099b84e584Sotto 			fragsize = MAXBSIZE / 8;
9104a8b9208Stedu #if defined (__sparc__) && !defined(__sparc64__)
911d98d4df7Stedu 		/* can't boot from > 8k boot blocks */
912ddfcbf38Sotto 		pp->p_fragblock =
9136e312d52Sotto 		    DISKLABELV1_FFS_FRAGBLOCK(partno == 0 ? 1024 : fragsize, 8);
914d98d4df7Stedu #else
9156e312d52Sotto 		pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fragsize, 8);
916d98d4df7Stedu #endif
917c88f83bdSotto 		if (get_fstype(lp, partno) == 0 &&
91834ae4198Skrw 		    get_mp(lp, partno) == 0 &&
919a4c87e64Skrw 		    get_fsize(lp, partno) == 0  &&
920a4c87e64Skrw 		    get_bsize(lp, partno) == 0)
92196a888c6Smillert 			return;
922c88f83bdSotto 	}
923a4c87e64Skrw 	/* Bailed out at some point, so effectively delete the partition. */
924da2bd3f5Skrw 	memset(pp, 0, sizeof(*pp));
9256fe57b42Smillert }
9266fe57b42Smillert 
9276fe57b42Smillert /*
928bd6726faSmillert  * Set the mountpoint of an existing partition ('name').
929bd6726faSmillert  */
930bd6726faSmillert void
93134ae4198Skrw editor_name(struct disklabel *lp, char *p)
932bd6726faSmillert {
933bd6726faSmillert 	struct partition *pp;
934bd6726faSmillert 	int partno;
935bd6726faSmillert 
936bd6726faSmillert 	/* Change which partition? */
937bd6726faSmillert 	if (p == NULL) {
938c33fcabaSmillert 		p = getstring("partition to name",
939bd6726faSmillert 		    "The letter of the partition to name, a - p.", NULL);
940bd6726faSmillert 	}
941bd6726faSmillert 	if (p == NULL) {
942bd6726faSmillert 		fputs("Command aborted\n", stderr);
943bd6726faSmillert 		return;
944bd6726faSmillert 	}
945bd6726faSmillert 	partno = p[0] - 'a';
9466c729bd1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
9476c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
9486c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
949bd6726faSmillert 		return;
95066df1f0cSkrw 	}
95166df1f0cSkrw 	pp = &lp->d_partitions[partno];
95266df1f0cSkrw 
95366df1f0cSkrw 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
95466df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
955bd6726faSmillert 		return;
956bd6726faSmillert 	}
957bd6726faSmillert 
958bd6726faSmillert 	/* Not all fstypes can be named */
959bd6726faSmillert 	if (pp->p_fstype == FS_UNUSED || pp->p_fstype == FS_SWAP ||
960bd387921Stodd 	    pp->p_fstype == FS_BOOT || pp->p_fstype == FS_OTHER ||
961bd387921Stodd 	    pp->p_fstype == FS_RAID) {
962bd6726faSmillert 		fprintf(stderr, "You cannot name a filesystem of type %s.\n",
963baa55472Smillert 		    fstypenames[lp->d_partitions[partno].p_fstype]);
964bd6726faSmillert 		return;
965bd6726faSmillert 	}
966bd6726faSmillert 
96734ae4198Skrw 	get_mp(lp, partno);
968bd6726faSmillert }
969bd6726faSmillert 
970bd6726faSmillert /*
9716fe57b42Smillert  * Change an existing partition.
9726fe57b42Smillert  */
9736fe57b42Smillert void
97434ae4198Skrw editor_modify(struct disklabel *lp, char *p)
9756fe57b42Smillert {
9766fe57b42Smillert 	struct partition origpart, *pp;
977f8ab7229Schl 	int partno;
9786fe57b42Smillert 
9796fe57b42Smillert 	/* Change which partition? */
9806fe57b42Smillert 	if (p == NULL) {
981c33fcabaSmillert 		p = getstring("partition to modify",
9826fe57b42Smillert 		    "The letter of the partition to modify, a - p.", NULL);
9836fe57b42Smillert 	}
98496a888c6Smillert 	if (p == NULL) {
98596a888c6Smillert 		fputs("Command aborted\n", stderr);
98696a888c6Smillert 		return;
98796a888c6Smillert 	}
9886fe57b42Smillert 	partno = p[0] - 'a';
9896c729bd1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
9906c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
9916c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
9926fe57b42Smillert 		return;
99366df1f0cSkrw 	}
99466df1f0cSkrw 	pp = &lp->d_partitions[partno];
99566df1f0cSkrw 
99666df1f0cSkrw 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
99766df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
9986fe57b42Smillert 		return;
9996fe57b42Smillert 	}
10006fe57b42Smillert 
100166df1f0cSkrw 	origpart = *pp;
100266df1f0cSkrw 
1003a4c87e64Skrw 	if (get_offset(lp, partno) == 0 &&
1004a4c87e64Skrw 	    get_size(lp, partno) == 0   &&
1005a4c87e64Skrw 	    get_fstype(lp, partno) == 0 &&
100634ae4198Skrw 	    get_mp(lp, partno) == 0 &&
1007a4c87e64Skrw 	    get_fsize(lp, partno) == 0  &&
1008a4c87e64Skrw 	    get_bsize(lp, partno) == 0)
100996a888c6Smillert 		return;
10106fe57b42Smillert 
1011a4c87e64Skrw 	/* Bailed out at some point, so undo any changes. */
1012a4c87e64Skrw 	*pp = origpart;
10136fe57b42Smillert }
10146fe57b42Smillert 
10156fe57b42Smillert /*
10166fe57b42Smillert  * Delete an existing partition.
10176fe57b42Smillert  */
10186fe57b42Smillert void
101934ae4198Skrw editor_delete(struct disklabel *lp, char *p)
10206fe57b42Smillert {
102166df1f0cSkrw 	struct partition *pp;
1022135c90d1Skrw 	int partno;
10236fe57b42Smillert 
10246fe57b42Smillert 	if (p == NULL) {
1025c33fcabaSmillert 		p = getstring("partition to delete",
1026945ae268Smillert 		    "The letter of the partition to delete, a - p, or '*'.",
1027945ae268Smillert 		    NULL);
10286fe57b42Smillert 	}
102996a888c6Smillert 	if (p == NULL) {
103096a888c6Smillert 		fputs("Command aborted\n", stderr);
103196a888c6Smillert 		return;
103296a888c6Smillert 	}
1033945ae268Smillert 	if (p[0] == '*') {
10349fdcb4d6Skrw 		zero_partitions(lp);
1035945ae268Smillert 		return;
1036945ae268Smillert 	}
1037135c90d1Skrw 	partno = p[0] - 'a';
1038135c90d1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
10396c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
10406c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
104133262abfSmiod 		return;
104266df1f0cSkrw 	}
1043135c90d1Skrw 	pp = &lp->d_partitions[partno];
104466df1f0cSkrw 
104566df1f0cSkrw 	if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) {
104666df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
104733262abfSmiod 		return;
104866df1f0cSkrw 	}
104966df1f0cSkrw 
10506fe57b42Smillert 	/* Really delete it (as opposed to just setting to "unused") */
1051135c90d1Skrw 	memset(pp, 0, sizeof(*pp));
105234ae4198Skrw 	free(mountpoints[partno]);
105334ae4198Skrw 	mountpoints[partno] = NULL;
10546fe57b42Smillert }
10556fe57b42Smillert 
10566fe57b42Smillert /*
10576fe57b42Smillert  * Change the size of an existing partition.
10586fe57b42Smillert  */
10596fe57b42Smillert void
10609fdcb4d6Skrw editor_change(struct disklabel *lp, char *p)
10616fe57b42Smillert {
10624b9a3bdaSmillert 	struct partition *pp;
106366df1f0cSkrw 	int partno;
10646fe57b42Smillert 
10656fe57b42Smillert 	if (p == NULL) {
1066c33fcabaSmillert 		p = getstring("partition to change size",
10676fe57b42Smillert 		    "The letter of the partition to change size, a - p.", NULL);
10686fe57b42Smillert 	}
106996a888c6Smillert 	if (p == NULL) {
107096a888c6Smillert 		fputs("Command aborted\n", stderr);
107196a888c6Smillert 		return;
107296a888c6Smillert 	}
10736fe57b42Smillert 	partno = p[0] - 'a';
10746c729bd1Skrw 	if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) {
10756c729bd1Skrw 		fprintf(stderr, "Partition must be between 'a' and '%c' "
10766c729bd1Skrw 		    "(excluding 'c').\n", 'a' + lp->d_npartitions - 1);
10776fe57b42Smillert 		return;
10786fe57b42Smillert 	}
10794b9a3bdaSmillert 	pp = &lp->d_partitions[partno];
10806fe57b42Smillert 
108166df1f0cSkrw 	if (DL_GETPSIZE(pp) == 0) {
108266df1f0cSkrw 		fprintf(stderr, "Partition '%c' is not in use.\n", p[0]);
108366df1f0cSkrw 		return;
108466df1f0cSkrw 	}
108566df1f0cSkrw 
108614192793Skrw 	printf("Partition %c is currently %llu sectors in size, and can have "
108714192793Skrw 	    "a maximum\nsize of %llu sectors.\n",
108814192793Skrw 	    p[0], DL_GETPSIZE(pp), max_partition_size(lp, partno));
10897da73705Skrw 
109059ccf790Skrw 	/* Get new size */
10919fdcb4d6Skrw 	get_size(lp, partno);
10926fe57b42Smillert }
10936fe57b42Smillert 
10946fe57b42Smillert /*
10956fe57b42Smillert  * Sort the partitions based on starting offset.
10966fe57b42Smillert  * This assumes there can be no overlap.
10976fe57b42Smillert  */
10986fe57b42Smillert int
10998809fabbSderaadt partition_cmp(const void *e1, const void *e2)
11006fe57b42Smillert {
11016fe57b42Smillert 	struct partition *p1 = *(struct partition **)e1;
11026fe57b42Smillert 	struct partition *p2 = *(struct partition **)e2;
11031e0ad43cSotto 	u_int64_t o1 = DL_GETPOFFSET(p1);
11041e0ad43cSotto 	u_int64_t o2 = DL_GETPOFFSET(p2);
11056fe57b42Smillert 
11061e0ad43cSotto 	if (o1 < o2)
1107651d5bd9Sotto 		return -1;
11081e0ad43cSotto 	else if (o1 > o2)
1109651d5bd9Sotto 		return 1;
1110651d5bd9Sotto 	else
1111651d5bd9Sotto 		return 0;
11126fe57b42Smillert }
11136fe57b42Smillert 
11146fe57b42Smillert char *
11158809fabbSderaadt getstring(char *prompt, char *helpstring, char *oval)
11166fe57b42Smillert {
11176fe57b42Smillert 	static char buf[BUFSIZ];
11186fe57b42Smillert 	int n;
11196fe57b42Smillert 
11206fe57b42Smillert 	buf[0] = '\0';
11216fe57b42Smillert 	do {
11226fe57b42Smillert 		printf("%s: [%s] ", prompt, oval ? oval : "");
11236e0becc5Smillert 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
1124260513deSmillert 			buf[0] = '\0';
112596a888c6Smillert 			if (feof(stdin)) {
112624c6582eSmillert 				clearerr(stdin);
112796a888c6Smillert 				putchar('\n');
112896a888c6Smillert 				return(NULL);
112996a888c6Smillert 			}
11306e0becc5Smillert 		}
11316fe57b42Smillert 		n = strlen(buf);
11326fe57b42Smillert 		if (n > 0 && buf[n-1] == '\n')
11336fe57b42Smillert 			buf[--n] = '\0';
11346fe57b42Smillert 		if (buf[0] == '?')
11356fe57b42Smillert 			puts(helpstring);
11364fb6ab7cSmillert 		else if (oval != NULL && buf[0] == '\0')
11374fb6ab7cSmillert 			strlcpy(buf, oval, sizeof(buf));
11386fe57b42Smillert 	} while (buf[0] == '?');
11396fe57b42Smillert 
11406fe57b42Smillert 	return(&buf[0]);
11416fe57b42Smillert }
11426fe57b42Smillert 
11436fe57b42Smillert /*
11441e0ad43cSotto  * Returns ULLONG_MAX on error
114524a2c1a4Smillert  * Usually only called by helper functions.
11466fe57b42Smillert  */
11471e0ad43cSotto u_int64_t
1148117239d3Skrw getuint64(struct disklabel *lp, char *prompt, char *helpstring,
11491e0ad43cSotto     u_int64_t oval, u_int64_t maxval, u_int64_t offset, int flags)
11506fe57b42Smillert {
11516fe57b42Smillert 	char buf[BUFSIZ], *endptr, *p, operator = '\0';
11521e0ad43cSotto 	u_int64_t rval = oval;
1153e04edc8bSkrw 	int64_t mult = 1;
11546fe57b42Smillert 	size_t n;
115514cc915fSmillert 	double d, percent = 1.0;
11566fe57b42Smillert 
11574b9a3bdaSmillert 	/* We only care about the remainder */
11584b9a3bdaSmillert 	offset = offset % lp->d_secpercyl;
11594b9a3bdaSmillert 
11606fe57b42Smillert 	buf[0] = '\0';
11616fe57b42Smillert 	do {
11621e0ad43cSotto 		printf("%s: [%llu] ", prompt, oval);
11636e0becc5Smillert 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
11646e0becc5Smillert 			buf[0] = '\0';
116596a888c6Smillert 			if (feof(stdin)) {
116624c6582eSmillert 				clearerr(stdin);
116796a888c6Smillert 				putchar('\n');
11681e0ad43cSotto 				return(ULLONG_MAX - 1);
116996a888c6Smillert 			}
11706e0becc5Smillert 		}
11716fe57b42Smillert 		n = strlen(buf);
11726fe57b42Smillert 		if (n > 0 && buf[n-1] == '\n')
11736fe57b42Smillert 			buf[--n] = '\0';
11746fe57b42Smillert 		if (buf[0] == '?')
11756fe57b42Smillert 			puts(helpstring);
11766fe57b42Smillert 	} while (buf[0] == '?');
11776fe57b42Smillert 
11786fe57b42Smillert 	if (buf[0] == '*' && buf[1] == '\0') {
11796fe57b42Smillert 		rval = maxval;
11806fe57b42Smillert 	} else {
11816fe57b42Smillert 		/* deal with units */
11826fe57b42Smillert 		if (buf[0] != '\0' && n > 0) {
11836fe57b42Smillert 			if ((flags & DO_CONVERSIONS)) {
1184025f5691Sderaadt 				switch (tolower((unsigned char)buf[n-1])) {
11856fe57b42Smillert 
11866fe57b42Smillert 				case 'c':
11876fe57b42Smillert 					mult = lp->d_secpercyl;
11886fe57b42Smillert 					buf[--n] = '\0';
11896fe57b42Smillert 					break;
11906fe57b42Smillert 				case 'b':
11913f74e3efSkrw 					mult = -(int64_t)lp->d_secsize;
11926fe57b42Smillert 					buf[--n] = '\0';
11936fe57b42Smillert 					break;
11946fe57b42Smillert 				case 'k':
119550c0f47aSkrw 					if (lp->d_secsize > 1024)
11963f74e3efSkrw 						mult = -(int64_t)lp->d_secsize /
11973f74e3efSkrw 						    1024LL;
119850c0f47aSkrw 					else
1199e04edc8bSkrw 						mult = 1024LL / lp->d_secsize;
12006fe57b42Smillert 					buf[--n] = '\0';
12016fe57b42Smillert 					break;
12026fe57b42Smillert 				case 'm':
1203e04edc8bSkrw 					mult = (1024LL * 1024) / lp->d_secsize;
12046fe57b42Smillert 					buf[--n] = '\0';
12056fe57b42Smillert 					break;
12061a51a1eeSmillert 				case 'g':
1207e04edc8bSkrw 					mult = (1024LL * 1024 * 1024) /
1208e04edc8bSkrw 					    lp->d_secsize;
1209e04edc8bSkrw 					buf[--n] = '\0';
1210e04edc8bSkrw 					break;
1211e04edc8bSkrw 				case 't':
1212e04edc8bSkrw 					mult = (1024LL * 1024 * 1024 * 1024) /
1213e04edc8bSkrw 					    lp->d_secsize;
12141a51a1eeSmillert 					buf[--n] = '\0';
12151a51a1eeSmillert 					break;
121614cc915fSmillert 				case '%':
121714cc915fSmillert 					buf[--n] = '\0';
12184659aa0dSotto 					p = &buf[0];
12194659aa0dSotto 					if (*p == '+' || *p == '-')
12204659aa0dSotto 						operator = *p++;
12214659aa0dSotto 					percent = strtod(p, NULL) / 100.0;
1222605eea5fSkrw 					snprintf(buf, sizeof(buf), "%llu",
12231e0ad43cSotto 					    DL_GETDSIZE(lp));
122414cc915fSmillert 					break;
122514cc915fSmillert 				case '&':
122614cc915fSmillert 					buf[--n] = '\0';
12274659aa0dSotto 					p = &buf[0];
12284659aa0dSotto 					if (*p == '+' || *p == '-')
12294659aa0dSotto 						operator = *p++;
12304659aa0dSotto 					percent = strtod(p, NULL) / 100.0;
12311e0ad43cSotto 					snprintf(buf, sizeof(buf), "%lld",
123214cc915fSmillert 					    maxval);
123314cc915fSmillert 					break;
12346fe57b42Smillert 				}
123596a888c6Smillert 			}
12366fe57b42Smillert 
12376fe57b42Smillert 			/* Did they give us an operator? */
12386fe57b42Smillert 			p = &buf[0];
12396fe57b42Smillert 			if (*p == '+' || *p == '-')
12406fe57b42Smillert 				operator = *p++;
12416fe57b42Smillert 
12426fe57b42Smillert 			endptr = p;
124396a888c6Smillert 			errno = 0;
124496a888c6Smillert 			d = strtod(p, &endptr);
124596a888c6Smillert 			if (errno == ERANGE)
12461e0ad43cSotto 				rval = ULLONG_MAX;	/* too big/small */
124796a888c6Smillert 			else if (*endptr != '\0') {
12486fe57b42Smillert 				errno = EINVAL;		/* non-numbers in str */
12491e0ad43cSotto 				rval = ULLONG_MAX;
12506fe57b42Smillert 			} else {
125196a888c6Smillert 				/* XXX - should check for overflow */
125296a888c6Smillert 				if (mult > 0)
125314cc915fSmillert 					rval = d * mult * percent;
125496a888c6Smillert 				else
125596a888c6Smillert 					/* Negative mult means divide (fancy) */
125614cc915fSmillert 					rval = d / (-mult) * percent;
12576fe57b42Smillert 
125896a888c6Smillert 				/* Apply the operator */
12596fe57b42Smillert 				if (operator == '+')
12606fe57b42Smillert 					rval += oval;
12616fe57b42Smillert 				else if (operator == '-')
12626fe57b42Smillert 					rval = oval - rval;
12636fe57b42Smillert 			}
12646fe57b42Smillert 		}
12656fe57b42Smillert 	}
12668390cf28Smillert 	if ((flags & DO_ROUNDING) && rval != ULLONG_MAX) {
126796a888c6Smillert 		/* Round to nearest cylinder unless given in sectors */
12688390cf28Smillert 		if (
1269fc1a4cc6Sderaadt #ifdef SUN_CYLCHECK
1270fc1a4cc6Sderaadt 		    ((lp->d_flags & D_VENDOR) || mult != 1) &&
1271fc1a4cc6Sderaadt #else
12728390cf28Smillert 		    mult != 1 &&
1273dbffb156Smillert #endif
12748390cf28Smillert 		    (rval + offset) % lp->d_secpercyl != 0) {
12751e0ad43cSotto 			u_int64_t cyls;
1276dbffb156Smillert 
12778390cf28Smillert 			/* Round to higher cylinder but no more than maxval */
12788390cf28Smillert 			cyls = (rval / lp->d_secpercyl) + 1;
12798390cf28Smillert 			if ((cyls * lp->d_secpercyl) - offset > maxval)
1280dbffb156Smillert 				cyls--;
12814b9a3bdaSmillert 			rval = (cyls * lp->d_secpercyl) - offset;
1282ef5288d7Skrw 			if (!aflag)
1283ef5288d7Skrw 				printf("Rounding size to cylinder (%d sectors)"
1284ef5288d7Skrw 				    ": %llu\n", lp->d_secpercyl, rval);
12856fe57b42Smillert 		}
12864b9a3bdaSmillert 	}
12876fe57b42Smillert 
12886fe57b42Smillert 	return(rval);
12896fe57b42Smillert }
12906fe57b42Smillert 
12916fe57b42Smillert /*
12921f0f871dSkrw  * Check for partition overlap in lp and prompt the user to resolve the overlap
12931f0f871dSkrw  * if any is found.  Returns 1 if unable to resolve, else 0.
12946fe57b42Smillert  */
12956fe57b42Smillert int
12961f0f871dSkrw has_overlap(struct disklabel *lp)
12976fe57b42Smillert {
12986fe57b42Smillert 	struct partition **spp;
1299e6aa8bafSmillert 	int c, i, j;
1300e6aa8bafSmillert 	char buf[BUFSIZ];
13016fe57b42Smillert 
13020fbd3c97Skrw 	/* Get a sorted list of the in-use partitions. */
13030fbd3c97Skrw 	spp = sort_partitions(lp);
13046fe57b42Smillert 
13050fbd3c97Skrw 	/* If there are less than two partitions in use, there is no overlap. */
13060fbd3c97Skrw 	if (spp[1] == NULL)
13070fbd3c97Skrw 		return(0);
13086fe57b42Smillert 
13096fe57b42Smillert 	/* Now that we have things sorted by starting sector check overlap */
13100fbd3c97Skrw 	for (i = 0; spp[i] != NULL; i++) {
13110fbd3c97Skrw 		for (j = i + 1; spp[j] != NULL; j++) {
13126fe57b42Smillert 			/* `if last_sec_in_part + 1 > first_sec_in_next_part' */
1313430e1380Skrw 			if (DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]) >
1314430e1380Skrw 			    DL_GETPOFFSET(spp[j])) {
13156fe57b42Smillert 				/* Overlap!  Convert to real part numbers. */
13166fe57b42Smillert 				i = ((char *)spp[i] - (char *)lp->d_partitions)
13176fe57b42Smillert 				    / sizeof(**spp);
13186fe57b42Smillert 				j = ((char *)spp[j] - (char *)lp->d_partitions)
13196fe57b42Smillert 				    / sizeof(**spp);
1320430e1380Skrw 				printf("\nError, partitions %c and %c overlap:"
1321430e1380Skrw 				    "\n", 'a' + i, 'a' + j);
1322366bf641Skrw 				printf("#    %16.16s %16.16s  fstype "
1323651d5bd9Sotto 				    "[fsize bsize  cpg]\n", "size", "offset");
132434ae4198Skrw 				display_partition(stdout, lp, i, 0);
132534ae4198Skrw 				display_partition(stdout, lp, j, 0);
13266fe57b42Smillert 
1327e6aa8bafSmillert 				/* Get partition to disable or ^D */
1328e6aa8bafSmillert 				do {
1329430e1380Skrw 					printf("Disable which one? "
1330430e1380Skrw 					    "(^D to abort) [%c %c] ",
13316fe57b42Smillert 					    'a' + i, 'a' + j);
1332e6aa8bafSmillert 					buf[0] = '\0';
1333616cd1c4Smillert 					if (!fgets(buf, sizeof(buf), stdin)) {
1334616cd1c4Smillert 						putchar('\n');
1335e6aa8bafSmillert 						return(1);	/* ^D */
1336616cd1c4Smillert 					}
1337e6aa8bafSmillert 					c = buf[0] - 'a';
1338e6aa8bafSmillert 				} while (buf[1] != '\n' && buf[1] != '\0' &&
1339e6aa8bafSmillert 				    c != i && c != j);
1340e6aa8bafSmillert 
1341e6aa8bafSmillert 				/* Mark the selected one as unused */
13426fe57b42Smillert 				lp->d_partitions[c].p_fstype = FS_UNUSED;
13431f0f871dSkrw 				return (has_overlap(lp));
13446fe57b42Smillert 			}
13456fe57b42Smillert 		}
13466fe57b42Smillert 	}
1347f0b4d0a9Smillert 
1348e6aa8bafSmillert 	return(0);
13496fe57b42Smillert }
13506fe57b42Smillert 
13516fe57b42Smillert void
13529fdcb4d6Skrw edit_parms(struct disklabel *lp)
13536fe57b42Smillert {
13546fe57b42Smillert 	char *p;
13559fdcb4d6Skrw 	u_int64_t freesectors, ui;
135696a888c6Smillert 	struct disklabel oldlabel = *lp;
13576fe57b42Smillert 
1358ea37abd3Sderaadt 	printf("Changing device parameters for %s:\n", specname);
13596fe57b42Smillert 
13600f820bbbSmillert 	/* disk type */
13610f820bbbSmillert 	for (;;) {
1362c33fcabaSmillert 		p = getstring("disk type",
136341282a2aSmillert 		    "What kind of disk is this?  Usually SCSI, ESDI, ST506, or "
136441282a2aSmillert 		    "floppy (use ESDI for IDE).", dktypenames[lp->d_type]);
136596a888c6Smillert 		if (p == NULL) {
136696a888c6Smillert 			fputs("Command aborted\n", stderr);
136796a888c6Smillert 			return;
136896a888c6Smillert 		}
136941282a2aSmillert 		if (strcasecmp(p, "IDE") == 0)
137041282a2aSmillert 			ui = DTYPE_ESDI;
137141282a2aSmillert 		else
137241282a2aSmillert 			for (ui = 1; ui < DKMAXTYPES &&
137341282a2aSmillert 			    strcasecmp(p, dktypenames[ui]); ui++)
13740f820bbbSmillert 				;
13750f820bbbSmillert 		if (ui < DKMAXTYPES) {
13760f820bbbSmillert 			break;
13770f820bbbSmillert 		} else {
13780f820bbbSmillert 			printf("\"%s\" is not a valid disk type.\n", p);
13790f820bbbSmillert 			fputs("Valid types are: ", stdout);
13800f820bbbSmillert 			for (ui = 1; ui < DKMAXTYPES; ui++) {
13810f820bbbSmillert 				printf("\"%s\"", dktypenames[ui]);
13820f820bbbSmillert 				if (ui < DKMAXTYPES - 1)
13830f820bbbSmillert 					fputs(", ", stdout);
13840f820bbbSmillert 			}
13850f820bbbSmillert 			putchar('\n');
13860f820bbbSmillert 		}
13870f820bbbSmillert 	}
13880f820bbbSmillert 	lp->d_type = ui;
13890f820bbbSmillert 
13906fe57b42Smillert 	/* pack/label id */
1391c33fcabaSmillert 	p = getstring("label name",
13926fe57b42Smillert 	    "15 char string that describes this label, usually the disk name.",
13936fe57b42Smillert 	    lp->d_packname);
139496a888c6Smillert 	if (p == NULL) {
139596a888c6Smillert 		fputs("Command aborted\n", stderr);
139696a888c6Smillert 		*lp = oldlabel;		/* undo damage */
139796a888c6Smillert 		return;
139896a888c6Smillert 	}
13994fb6ab7cSmillert 	strncpy(lp->d_packname, p, sizeof(lp->d_packname));	/* checked */
14006fe57b42Smillert 
14016fe57b42Smillert 	/* sectors/track */
14026fe57b42Smillert 	for (;;) {
1403117239d3Skrw 		ui = getuint64(lp, "sectors/track",
1404cfd24250Skrw 		    "The Number of sectors per track.", lp->d_nsectors,
14054b9a3bdaSmillert 		    lp->d_nsectors, 0, 0);
14061e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
140796a888c6Smillert 			fputs("Command aborted\n", stderr);
140896a888c6Smillert 			*lp = oldlabel;		/* undo damage */
140996a888c6Smillert 			return;
14101e0ad43cSotto 		} if (ui == ULLONG_MAX)
14116fe57b42Smillert 			fputs("Invalid entry\n", stderr);
14126fe57b42Smillert 		else
14136fe57b42Smillert 			break;
14146fe57b42Smillert 	}
14156fe57b42Smillert 	lp->d_nsectors = ui;
14166fe57b42Smillert 
14176fe57b42Smillert 	/* tracks/cylinder */
14186fe57b42Smillert 	for (;;) {
1419117239d3Skrw 		ui = getuint64(lp, "tracks/cylinder",
14206fe57b42Smillert 		    "The number of tracks per cylinder.", lp->d_ntracks,
14214b9a3bdaSmillert 		    lp->d_ntracks, 0, 0);
14221e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
142396a888c6Smillert 			fputs("Command aborted\n", stderr);
142496a888c6Smillert 			*lp = oldlabel;		/* undo damage */
142596a888c6Smillert 			return;
14261e0ad43cSotto 		} else if (ui == ULLONG_MAX)
14276fe57b42Smillert 			fputs("Invalid entry\n", stderr);
14286fe57b42Smillert 		else
14296fe57b42Smillert 			break;
14306fe57b42Smillert 	}
14316fe57b42Smillert 	lp->d_ntracks = ui;
14326fe57b42Smillert 
14336fe57b42Smillert 	/* sectors/cylinder */
1434148b6188Smillert 	for (;;) {
1435117239d3Skrw 		ui = getuint64(lp, "sectors/cylinder",
1436148b6188Smillert 		    "The number of sectors per cylinder (Usually sectors/track "
14374b9a3bdaSmillert 		    "* tracks/cylinder).", lp->d_secpercyl, lp->d_secpercyl,
14384b9a3bdaSmillert 		    0, 0);
14391e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
144096a888c6Smillert 			fputs("Command aborted\n", stderr);
144196a888c6Smillert 			*lp = oldlabel;		/* undo damage */
144296a888c6Smillert 			return;
14431e0ad43cSotto 		} else if (ui == ULLONG_MAX)
1444148b6188Smillert 			fputs("Invalid entry\n", stderr);
1445148b6188Smillert 		else
1446148b6188Smillert 			break;
1447148b6188Smillert 	}
1448148b6188Smillert 	lp->d_secpercyl = ui;
14496fe57b42Smillert 
14506fe57b42Smillert 	/* number of cylinders */
14516fe57b42Smillert 	for (;;) {
1452117239d3Skrw 		ui = getuint64(lp, "number of cylinders",
14536fe57b42Smillert 		    "The total number of cylinders on the disk.",
14544b9a3bdaSmillert 		    lp->d_ncylinders, lp->d_ncylinders, 0, 0);
14551e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
145696a888c6Smillert 			fputs("Command aborted\n", stderr);
145796a888c6Smillert 			*lp = oldlabel;		/* undo damage */
145896a888c6Smillert 			return;
14591e0ad43cSotto 		} else if (ui == ULLONG_MAX)
14606fe57b42Smillert 			fputs("Invalid entry\n", stderr);
14616fe57b42Smillert 		else
14626fe57b42Smillert 			break;
14636fe57b42Smillert 	}
14646fe57b42Smillert 	lp->d_ncylinders = ui;
14656fe57b42Smillert 
14666fe57b42Smillert 	/* total sectors */
14676fe57b42Smillert 	for (;;) {
146834af67a3Sotto 		u_int64_t nsec = MAX(DL_GETDSIZE(lp),
146934af67a3Sotto 		    (u_int64_t)lp->d_ncylinders * lp->d_secpercyl);
1470117239d3Skrw 		ui = getuint64(lp, "total sectors",
14716fe57b42Smillert 		    "The total number of sectors on the disk.",
1472baaa8969Smillert 		    nsec, nsec, 0, 0);
14731e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
147496a888c6Smillert 			fputs("Command aborted\n", stderr);
147596a888c6Smillert 			*lp = oldlabel;		/* undo damage */
147696a888c6Smillert 			return;
14771e0ad43cSotto 		} else if (ui == ULLONG_MAX)
14786fe57b42Smillert 			fputs("Invalid entry\n", stderr);
14791e0ad43cSotto 		else if (ui > DL_GETDSIZE(lp) &&
14801e0ad43cSotto 		    ending_sector == DL_GETDSIZE(lp)) {
1481f98aebd4Smillert 			puts("You may want to increase the size of the 'c' "
1482f98aebd4Smillert 			    "partition.");
14836fe57b42Smillert 			break;
14841e0ad43cSotto 		} else if (ui < DL_GETDSIZE(lp) &&
14851e0ad43cSotto 		    ending_sector == DL_GETDSIZE(lp)) {
14866fe57b42Smillert 			/* shrink free count */
14879fdcb4d6Skrw 			freesectors = editor_countfree(lp);
14889fdcb4d6Skrw 			if (DL_GETDSIZE(lp) - ui > freesectors)
14896fe57b42Smillert 				fprintf(stderr,
14901e0ad43cSotto 				    "Not enough free space to shrink by %llu "
14911e0ad43cSotto 				    "sectors (only %llu sectors left)\n",
14929fdcb4d6Skrw 				    DL_GETDSIZE(lp) - ui, freesectors);
1493c4f83f03Skrw 			else
14946fe57b42Smillert 				break;
14956fe57b42Smillert 		} else
14966fe57b42Smillert 			break;
14976fe57b42Smillert 	}
149841ed49b7Smillert 	/* Adjust ending_sector if necessary. */
149978f0fb17Skrw 	if (ending_sector > ui) {
150096a888c6Smillert 		ending_sector = ui;
150178f0fb17Skrw 		DL_SETBEND(lp, ending_sector);
150278f0fb17Skrw 	}
15031e0ad43cSotto 	DL_SETDSIZE(lp, ui);
15046fe57b42Smillert }
1505a7e61405Smillert 
1506a7e61405Smillert struct partition **
15070fbd3c97Skrw sort_partitions(struct disklabel *lp)
1508a7e61405Smillert {
1509d18c2a43Skrw 	static struct partition *spp[MAXPARTITIONS+2];
15100fbd3c97Skrw 	int i, npartitions;
1511a7e61405Smillert 
1512d18c2a43Skrw 	memset(spp, 0, sizeof(spp));
1513d18c2a43Skrw 
1514a7e61405Smillert 	for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) {
1515a7e61405Smillert 		if (lp->d_partitions[i].p_fstype != FS_UNUSED &&
1516a7e61405Smillert 		    lp->d_partitions[i].p_fstype != FS_BOOT &&
15171e0ad43cSotto 		    DL_GETPSIZE(&lp->d_partitions[i]) != 0)
1518a7e61405Smillert 			spp[npartitions++] = &lp->d_partitions[i];
1519a7e61405Smillert 	}
1520a7e61405Smillert 
1521a7e61405Smillert 	/*
1522a7e61405Smillert 	 * Sort the partitions based on starting offset.
1523a7e61405Smillert 	 * This is safe because we guarantee no overlap.
1524a7e61405Smillert 	 */
1525a7e61405Smillert 	if (npartitions > 1)
1526a7e61405Smillert 		if (heapsort((void *)spp, npartitions, sizeof(spp[0]),
1527a7e61405Smillert 		    partition_cmp))
1528a7e61405Smillert 			err(4, "failed to sort partition table");
1529a7e61405Smillert 
1530a7e61405Smillert 	return(spp);
1531a7e61405Smillert }
15320f820bbbSmillert 
15330f820bbbSmillert /*
15340f820bbbSmillert  * Get a valid disk type if necessary.
15350f820bbbSmillert  */
15360f820bbbSmillert void
15378809fabbSderaadt getdisktype(struct disklabel *lp, char *banner, char *dev)
15380f820bbbSmillert {
15390f820bbbSmillert 	int i;
1540803ff7d5Smillert 	char *s, *def = "SCSI";
1541803ff7d5Smillert 	struct dtypes {
1542803ff7d5Smillert 		char *dev;
1543803ff7d5Smillert 		char *type;
1544803ff7d5Smillert 	} dtypes[] = {
1545c33fcabaSmillert 		{ "sd",   "SCSI" },
1546c33fcabaSmillert 		{ "rz",   "SCSI" },
1547c33fcabaSmillert 		{ "wd",   "IDE" },
1548c33fcabaSmillert 		{ "fd",   "FLOPPY" },
1549c33fcabaSmillert 		{ "xd",   "SMD" },
1550c33fcabaSmillert 		{ "xy",   "SMD" },
1551c33fcabaSmillert 		{ "hd",   "HP-IB" },
1552d7878011Sderaadt 		{ "ccd",  "CCD" },		/* deprecated */
1553c33fcabaSmillert 		{ "vnd",  "VND" },
1554c33fcabaSmillert 		{ "svnd", "VND" },
1555c33fcabaSmillert 		{ NULL,   NULL }
1556803ff7d5Smillert 	};
1557803ff7d5Smillert 
1558803ff7d5Smillert 	if ((s = basename(dev)) != NULL) {
1559803ff7d5Smillert 		if (*s == 'r')
1560803ff7d5Smillert 			s++;
1561803ff7d5Smillert 		i = strcspn(s, "0123456789");
1562803ff7d5Smillert 		s[i] = '\0';
1563803ff7d5Smillert 		dev = s;
1564803ff7d5Smillert 		for (i = 0; dtypes[i].dev != NULL; i++) {
1565803ff7d5Smillert 			if (strcmp(dev, dtypes[i].dev) == 0) {
1566803ff7d5Smillert 				def = dtypes[i].type;
1567803ff7d5Smillert 				break;
1568803ff7d5Smillert 			}
1569803ff7d5Smillert 		}
1570803ff7d5Smillert 	}
15710f820bbbSmillert 
15720f820bbbSmillert 	if (lp->d_type > DKMAXTYPES || lp->d_type == 0) {
15730f820bbbSmillert 		puts(banner);
15740f820bbbSmillert 		puts("Possible values are:");
1575eb5dd924Sderaadt 		printf("\"IDE\", ");
15760f820bbbSmillert 		for (i = 1; i < DKMAXTYPES; i++) {
15770f820bbbSmillert 			printf("\"%s\"", dktypenames[i]);
15780f820bbbSmillert 			if (i < DKMAXTYPES - 1)
15790f820bbbSmillert 				fputs(", ", stdout);
15800f820bbbSmillert 		}
15810f820bbbSmillert 		putchar('\n');
15820f820bbbSmillert 
15830f820bbbSmillert 		for (;;) {
1584c33fcabaSmillert 			s = getstring("Disk type",
1585803ff7d5Smillert 			    "What kind of disk is this?  Usually SCSI, IDE, "
1586d7878011Sderaadt 			    "ESDI, ST506, or floppy.", def);
158796a888c6Smillert 			if (s == NULL)
158896a888c6Smillert 				continue;
15895b412421Smillert 			if (strcasecmp(s, "IDE") == 0) {
15905b412421Smillert 				lp->d_type = DTYPE_ESDI;
15915b412421Smillert 				return;
15925b412421Smillert 			}
15930f820bbbSmillert 			for (i = 1; i < DKMAXTYPES; i++)
15940f820bbbSmillert 				if (strcasecmp(s, dktypenames[i]) == 0) {
15950f820bbbSmillert 					lp->d_type = i;
15960f820bbbSmillert 					return;
15970f820bbbSmillert 				}
15980f820bbbSmillert 			printf("\"%s\" is not a valid disk type.\n", s);
15990f820bbbSmillert 			fputs("Valid types are: ", stdout);
16000f820bbbSmillert 			for (i = 1; i < DKMAXTYPES; i++) {
16010f820bbbSmillert 				printf("\"%s\"", dktypenames[i]);
16020f820bbbSmillert 				if (i < DKMAXTYPES - 1)
16030f820bbbSmillert 					fputs(", ", stdout);
16040f820bbbSmillert 			}
16050f820bbbSmillert 			putchar('\n');
16060f820bbbSmillert 		}
16070f820bbbSmillert 	}
16080f820bbbSmillert }
160996a888c6Smillert 
161096a888c6Smillert /*
161196a888c6Smillert  * Get beginning and ending sectors of the OpenBSD portion of the disk
161296a888c6Smillert  * from the user.
161396a888c6Smillert  */
161496a888c6Smillert void
16159fdcb4d6Skrw set_bounds(struct disklabel *lp)
161696a888c6Smillert {
16171e0ad43cSotto 	u_int64_t ui, start_temp;
161896a888c6Smillert 
161996a888c6Smillert 	/* Starting sector */
162096a888c6Smillert 	do {
1621117239d3Skrw 		ui = getuint64(lp, "Starting sector",
162296a888c6Smillert 		  "The start of the OpenBSD portion of the disk.",
16231e0ad43cSotto 		  starting_sector, DL_GETDSIZE(lp), 0, 0);
16241e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
162596a888c6Smillert 			fputs("Command aborted\n", stderr);
162696a888c6Smillert 			return;
162796a888c6Smillert 		}
16281e0ad43cSotto 	} while (ui >= DL_GETDSIZE(lp));
162996a888c6Smillert 	start_temp = ui;
163096a888c6Smillert 
16314793b14cSmillert 	/* Size */
163296a888c6Smillert 	do {
1633117239d3Skrw 		ui = getuint64(lp, "Size ('*' for entire disk)",
1634f98aebd4Smillert 		  "The size of the OpenBSD portion of the disk ('*' for the "
1635f98aebd4Smillert 		  "entire disk).", ending_sector - starting_sector,
16361e0ad43cSotto 		  DL_GETDSIZE(lp) - start_temp, 0, 0);
16371e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
163896a888c6Smillert 			fputs("Command aborted\n", stderr);
163996a888c6Smillert 			return;
164096a888c6Smillert 		}
16411e0ad43cSotto 	} while (ui > DL_GETDSIZE(lp) - start_temp);
16424793b14cSmillert 	ending_sector = start_temp + ui;
164378f0fb17Skrw 	DL_SETBEND(lp, ending_sector);
164496a888c6Smillert 	starting_sector = start_temp;
164578f0fb17Skrw 	DL_SETBSTART(lp, starting_sector);
164696a888c6Smillert }
164796a888c6Smillert 
164896a888c6Smillert /*
16490d63cfbaSjsing  * Allow user to interactively change disklabel UID.
16500d63cfbaSjsing  */
16510d63cfbaSjsing void
165269a6ffbcSjsing set_duid(struct disklabel *lp)
16530d63cfbaSjsing {
16540d63cfbaSjsing 	char *s;
16550d63cfbaSjsing 	int i;
16560d63cfbaSjsing 
1657f6ad9e2dSjsing 	printf("The disklabel UID is currently: "
1658f6ad9e2dSjsing 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\n",
1659f6ad9e2dSjsing             lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3],
1660f6ad9e2dSjsing             lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7]);
16610d63cfbaSjsing 
16620d63cfbaSjsing 	do {
166369a6ffbcSjsing 		s = getstring("duid", "The disklabel UID, given as a 16 "
16640d63cfbaSjsing 		    "character hexadecimal string.", NULL);
1665ce98d1aeShalex 		if (s == NULL || strlen(s) == 0) {
16660d63cfbaSjsing 			fputs("Command aborted\n", stderr);
16670d63cfbaSjsing 			return;
16680d63cfbaSjsing 		}
166969a6ffbcSjsing 		i = duid_parse(lp, s);
16700d63cfbaSjsing 		if (i != 0)
16710d63cfbaSjsing 			fputs("Invalid UID entered.\n", stderr);
16720d63cfbaSjsing 	} while (i != 0);
16730d63cfbaSjsing }
16740d63cfbaSjsing 
16750d63cfbaSjsing /*
167696a888c6Smillert  * Return a list of the "chunks" of free space available
167796a888c6Smillert  */
167896a888c6Smillert struct diskchunk *
16798809fabbSderaadt free_chunks(struct disklabel *lp)
168096a888c6Smillert {
168196a888c6Smillert 	struct partition **spp;
168296a888c6Smillert 	static struct diskchunk chunks[MAXPARTITIONS + 2];
168399bd27d2Skrw 	u_int64_t start, stop;
168496a888c6Smillert 	int i, numchunks;
168596a888c6Smillert 
16860fbd3c97Skrw 	/* Sort the in-use partitions based on offset */
16870fbd3c97Skrw 	spp = sort_partitions(lp);
168896a888c6Smillert 
168996a888c6Smillert 	/* If there are no partitions, it's all free. */
16900fbd3c97Skrw 	if (spp[0] == NULL) {
16912d8451b0Smillert 		chunks[0].start = starting_sector;
169296a888c6Smillert 		chunks[0].stop = ending_sector;
169396a888c6Smillert 		chunks[1].start = chunks[1].stop = 0;
169496a888c6Smillert 		return(chunks);
169596a888c6Smillert 	}
169696a888c6Smillert 
169796a888c6Smillert 	/* Find chunks of free space */
169896a888c6Smillert 	numchunks = 0;
16990fbd3c97Skrw 	if (DL_GETPOFFSET(spp[0]) > starting_sector) {
17002d8451b0Smillert 		chunks[0].start = starting_sector;
17011e0ad43cSotto 		chunks[0].stop = DL_GETPOFFSET(spp[0]);
170296a888c6Smillert 		numchunks++;
170396a888c6Smillert 	}
17040fbd3c97Skrw 	for (i = 0; spp[i] != NULL; i++) {
170599bd27d2Skrw 		start = DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]);
1706aff3f969Sotto 		if (start < starting_sector)
1707aff3f969Sotto 			start = starting_sector;
1708aff3f969Sotto 		else if (start > ending_sector)
1709aff3f969Sotto 			start = ending_sector;
171099bd27d2Skrw 		if (spp[i + 1] != NULL)
171199bd27d2Skrw 			stop = DL_GETPOFFSET(spp[i+1]);
171299bd27d2Skrw 		else
171399bd27d2Skrw 			stop = ending_sector;
1714aff3f969Sotto 		if (stop < starting_sector)
1715aff3f969Sotto 			stop = starting_sector;
1716aff3f969Sotto 		else if (stop > ending_sector)
1717aff3f969Sotto 			stop = ending_sector;
171899bd27d2Skrw 		if (start < stop) {
171999bd27d2Skrw 			chunks[numchunks].start = start;
172099bd27d2Skrw 			chunks[numchunks].stop = stop;
172196a888c6Smillert 			numchunks++;
172296a888c6Smillert 		}
172396a888c6Smillert 	}
172496a888c6Smillert 
172596a888c6Smillert 	/* Terminate and return */
172696a888c6Smillert 	chunks[numchunks].start = chunks[numchunks].stop = 0;
172796a888c6Smillert 	return(chunks);
172896a888c6Smillert }
17294793b14cSmillert 
17304793b14cSmillert void
173187023ed9Skrw find_bounds(struct disklabel *lp)
17324793b14cSmillert {
17336534e983Sderaadt 	starting_sector = DL_GETBSTART(lp);
17346534e983Sderaadt 	ending_sector = DL_GETBEND(lp);
1735b2d4a455Smiod 
17366534e983Sderaadt 	if (ending_sector) {
173734ae4198Skrw 		if (verbose)
173834ae4198Skrw 			printf("Treating sectors %llu-%llu as the OpenBSD"
173934ae4198Skrw 			    " portion of the disk.\nYou can use the 'b'"
174034ae4198Skrw 			    " command to change this.\n\n", starting_sector,
174134ae4198Skrw 			    ending_sector);
1742b2d4a455Smiod 	} else {
174381b6e3e6Skrw #if NUMBOOT > 0
1744d3f02056Smillert 		/* Boot blocks take up the first cylinder */
1745d3f02056Smillert 		starting_sector = lp->d_secpercyl;
174634ae4198Skrw 		if (verbose)
174734ae4198Skrw 			printf("Reserving the first data cylinder for boot"
174834ae4198Skrw 			    " blocks.\nYou can use the 'b' command to change"
174934ae4198Skrw 			    " this.\n\n");
17504793b14cSmillert #endif
17514793b14cSmillert 	}
1752b2d4a455Smiod }
1753c0bdc608Smillert 
1754c0bdc608Smillert /*
1755c0bdc608Smillert  * Calculate free space.
1756c0bdc608Smillert  */
17579fdcb4d6Skrw u_int64_t
17589fdcb4d6Skrw editor_countfree(struct disklabel *lp)
1759c0bdc608Smillert {
1760d93cb2bbSkrw 	struct diskchunk *chunks;
17619fdcb4d6Skrw 	u_int64_t freesectors = 0;
1762c0bdc608Smillert 	int i;
1763c0bdc608Smillert 
1764d93cb2bbSkrw 	chunks = free_chunks(lp);
1765509930fbSotto 
1766d93cb2bbSkrw 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++)
17679fdcb4d6Skrw 		freesectors += chunks[i].stop - chunks[i].start;
17689fdcb4d6Skrw 
17699fdcb4d6Skrw 	return (freesectors);
1770c0bdc608Smillert }
1771617e6e4aSmillert 
1772617e6e4aSmillert void
17734d812bb6Slum editor_help(void)
1774617e6e4aSmillert {
1775617e6e4aSmillert 	puts("Available commands:");
1776617e6e4aSmillert 	puts(
17774d812bb6Slum " ? | h    - show help                 n [part] - set mount point\n"
177849159a67Skrw " A        - auto partition all space  p [unit] - print partitions\n"
177949159a67Skrw " a [part] - add partition             q        - quit & save changes\n"
17804659aa0dSotto " b        - set OpenBSD boundaries    R [part] - resize auto allocated partition\n"
178149159a67Skrw " c [part] - change partition size     r        - display free space\n"
17826aaa4aabSotto " D        - reset label to default    s [path] - save label to file\n"
17836aaa4aabSotto " d [part] - delete partition          U        - undo all changes\n"
17846aaa4aabSotto " e        - edit drive parameters     u        - undo last change\n"
17856aaa4aabSotto " g [d|u]  - [d]isk or [u]ser geometry w        - write label to disk\n"
17860d63cfbaSjsing " i        - modify disklabel UID      X        - toggle expert mode\n"
17870d63cfbaSjsing " l [unit] - print disk label header   x        - exit & lose changes\n"
17880d63cfbaSjsing " M        - disklabel(8) man page     z        - delete all partitions\n"
17890d63cfbaSjsing " m [part] - modify partition\n"
1790c4884206Skrw "\n"
1791c4884206Skrw "Suffixes can be used to indicate units other than sectors:\n"
1792e5f81948Sotto " 'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' (gigabytes) 't' (terabytes)\n"
1793e5f81948Sotto " 'c' (cylinders), '%' (% of total disk), '&' (% of free space).\n"
1794c4884206Skrw "Values in non-sector units are truncated to the nearest cylinder boundary.");
17954d812bb6Slum 
1796617e6e4aSmillert }
1797bd6726faSmillert 
17984f3bbbf0Skrw void
17998809fabbSderaadt mpcopy(char **to, char **from)
1800bd6726faSmillert {
1801bd6726faSmillert 	int i;
1802bd6726faSmillert 
1803bd6726faSmillert 	for (i = 0; i < MAXPARTITIONS; i++) {
1804bd6726faSmillert 		free(to[i]);
1805bd6726faSmillert 		to[i] = NULL;
1806408ab9bcSkrw 		if (from[i] != NULL) {
1807408ab9bcSkrw 			to[i] = strdup(from[i]);
1808408ab9bcSkrw 			if (to[i] == NULL)
1809408ab9bcSkrw 				errx(4, "out of memory");
1810bd6726faSmillert 		}
1811bd6726faSmillert 	}
1812bd6726faSmillert }
1813bd6726faSmillert 
1814bd6726faSmillert int
18158809fabbSderaadt mpequal(char **mp1, char **mp2)
1816bd6726faSmillert {
1817bd6726faSmillert 	int i;
1818bd6726faSmillert 
1819bd6726faSmillert 	for (i = 0; i < MAXPARTITIONS; i++) {
1820bd6726faSmillert 		if (mp1[i] == NULL && mp2[i] == NULL)
1821bd6726faSmillert 			continue;
1822bd6726faSmillert 
1823bd6726faSmillert 		if ((mp1[i] != NULL && mp2[i] == NULL) ||
1824bd6726faSmillert 		    (mp1[i] == NULL && mp2[i] != NULL) ||
1825bd6726faSmillert 		    (strcmp(mp1[i], mp2[i]) != 0))
1826bd6726faSmillert 			return(0);
1827bd6726faSmillert 	}
1828bd6726faSmillert 	return(1);
1829bd6726faSmillert }
1830bd6726faSmillert 
183193160b9bSkrw void
183234ae4198Skrw mpsave(struct disklabel *lp)
1833bd6726faSmillert {
1834d8b446ceSderaadt 	int i, j;
1835bd6726faSmillert 	char bdev[MAXPATHLEN], *p;
18363f843443Smillert 	struct mountinfo mi[MAXPARTITIONS];
1837bd6726faSmillert 	FILE *fp;
1838fe01da94Skrw 	u_int8_t fstype;
1839bd6726faSmillert 
184093160b9bSkrw 	if (!fstabfile)
184193160b9bSkrw 		return;
184293160b9bSkrw 
18433f843443Smillert 	memset(&mi, 0, sizeof(mi));
18443f843443Smillert 
1845d8b446ceSderaadt 	for (i = 0; i < MAXPARTITIONS; i++) {
1846fe01da94Skrw 		fstype = lp->d_partitions[i].p_fstype;
1847fe01da94Skrw 		if (mountpoints[i] != NULL || fstype == FS_SWAP) {
184834ae4198Skrw 			mi[i].mountpoint = mountpoints[i];
18493f843443Smillert 			mi[i].partno = i;
1850bd6726faSmillert 		}
1851bd6726faSmillert 	}
1852bd6726faSmillert 
185334ae4198Skrw 	/* Convert specname to bdev */
1854d6d80bb0Skrw 	if (uidflag) {
1855d6d80bb0Skrw 		snprintf(bdev, sizeof(bdev),
1856d6d80bb0Skrw 		    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
1857d6d80bb0Skrw 		    lab.d_uid[0], lab.d_uid[1], lab.d_uid[2], lab.d_uid[3],
1858d6d80bb0Skrw 		    lab.d_uid[4], lab.d_uid[5], lab.d_uid[6], lab.d_uid[7],
1859d6d80bb0Skrw 		    specname[strlen(specname)-1]);
1860d6d80bb0Skrw 	} else if (strncmp(_PATH_DEV, specname, sizeof(_PATH_DEV) - 1) == 0 &&
186134ae4198Skrw 	    specname[sizeof(_PATH_DEV) - 1] == 'r') {
1862bd6726faSmillert 		snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV,
186334ae4198Skrw 		    &specname[sizeof(_PATH_DEV)]);
1864bd6726faSmillert 	} else {
186534ae4198Skrw 		if ((p = strrchr(specname, '/')) == NULL || *(++p) != 'r')
186693160b9bSkrw 			return;
1867bd6726faSmillert 		*p = '\0';
186834ae4198Skrw 		snprintf(bdev, sizeof(bdev), "%s%s", specname, p + 1);
1869bd6726faSmillert 		*p = 'r';
1870bd6726faSmillert 	}
1871bd6726faSmillert 	bdev[strlen(bdev) - 1] = '\0';
1872bd6726faSmillert 
18733f843443Smillert 	/* Sort mountpoints so we don't try to mount /usr/local before /usr */
18743f843443Smillert 	qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp);
18753f843443Smillert 
1876d71533c9Stedu 	if ((fp = fopen(fstabfile, "w"))) {
1877fe01da94Skrw 		for (i = 0; i < MAXPARTITIONS; i++) {
18785fea0b85Smillert 			j =  mi[i].partno;
1879fe01da94Skrw 			fstype = lp->d_partitions[j].p_fstype;
1880fe01da94Skrw 			if (fstype == FS_SWAP) {
1881fe01da94Skrw 				fprintf(fp, "%s%c none swap sw\n", bdev, 'a'+j);
1882fe01da94Skrw 			} else if (mi[i].mountpoint) {
1883fe01da94Skrw 				fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev,
1884fe01da94Skrw 				    'a' + j, mi[i].mountpoint,
1885fe01da94Skrw 				    fstypesnames[fstype], j == 0 ? 1 : 2);
1886fe01da94Skrw 			}
1887bd6726faSmillert 		}
1888bd6726faSmillert 		fclose(fp);
188993160b9bSkrw 	}
1890bd6726faSmillert }
189124a2c1a4Smillert 
1892408ab9bcSkrw void
1893408ab9bcSkrw mpfree(char **mp)
1894408ab9bcSkrw {
1895408ab9bcSkrw 	int part;
1896408ab9bcSkrw 
1897408ab9bcSkrw 	if (mp == NULL)
1898408ab9bcSkrw 		return;
1899408ab9bcSkrw 
190075691760Skrw 	for (part = 0; part < MAXPARTITIONS; part++)
1901408ab9bcSkrw 		free(mp[part]);
1902408ab9bcSkrw 
1903408ab9bcSkrw 	free(mp);
1904408ab9bcSkrw }
1905408ab9bcSkrw 
190624a2c1a4Smillert int
1907604d3bdeSkrw get_offset(struct disklabel *lp, int partno)
190824a2c1a4Smillert {
1909604d3bdeSkrw 	struct diskchunk *chunks;
191024a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
191115c15d8aSkrw 	u_int64_t ui, maxsize;
1912604d3bdeSkrw 	int i, fstype;
191324a2c1a4Smillert 
1914117239d3Skrw 	ui = getuint64(lp, "offset",
19151e0ad43cSotto 	   "Starting sector for this partition.",
19161e0ad43cSotto 	   DL_GETPOFFSET(pp),
19171e0ad43cSotto 	   DL_GETPOFFSET(pp), 0, DO_CONVERSIONS |
191824a2c1a4Smillert 	   (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0));
1919e9ff19beSkrw 
1920e9ff19beSkrw 	if (ui == ULLONG_MAX - 1)
192124a2c1a4Smillert 		fputs("Command aborted\n", stderr);
1922e9ff19beSkrw 	else if (ui == ULLONG_MAX)
192324a2c1a4Smillert 		fputs("Invalid entry\n", stderr);
192440e98e9fSkrw 	else if (ui < starting_sector || ui >= ending_sector)
1925e9ff19beSkrw 		fprintf(stderr, "The offset must be >= %llu and < %llu, "
1926e9ff19beSkrw 		    "the limits of the OpenBSD portion\n"
1927e9ff19beSkrw 		    "of the disk. The 'b' command can change these limits.\n",
192840e98e9fSkrw 		    starting_sector, ending_sector);
1929fc1a4cc6Sderaadt #ifdef SUN_AAT0
193049bf537cSderaadt 	else if (partno == 0 && ui != 0)
193149bf537cSderaadt 		fprintf(stderr, "This architecture requires that "
193240f544cdSderaadt 		    "partition 'a' start at sector 0.\n");
193349bf537cSderaadt #endif
193415c15d8aSkrw 	else {
1935604d3bdeSkrw 		fstype = pp->p_fstype;
1936604d3bdeSkrw 		pp->p_fstype = FS_UNUSED;
1937604d3bdeSkrw 		chunks = free_chunks(lp);
1938604d3bdeSkrw 		pp->p_fstype = fstype;
1939e9ff19beSkrw 		for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
1940e9ff19beSkrw 			if (ui < chunks[i].start || ui >= chunks[i].stop)
194115c15d8aSkrw 				continue;
19421e0ad43cSotto 			DL_SETPOFFSET(pp, ui);
194315c15d8aSkrw 			maxsize = chunks[i].stop - DL_GETPOFFSET(pp);
194415c15d8aSkrw 			if (DL_GETPSIZE(pp) > maxsize)
194515c15d8aSkrw 				DL_SETPSIZE(pp, maxsize);
194624a2c1a4Smillert 			return (0);
194724a2c1a4Smillert 		}
194815c15d8aSkrw 		fputs("The offset must be in a free area.\n", stderr);
194915c15d8aSkrw 	}
1950e9ff19beSkrw 
1951e9ff19beSkrw 	/* Partition offset was not set. */
1952e9ff19beSkrw 	return (1);
195315c15d8aSkrw }
195424a2c1a4Smillert 
195524a2c1a4Smillert int
19569fdcb4d6Skrw get_size(struct disklabel *lp, int partno)
195724a2c1a4Smillert {
195824a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
195914192793Skrw 	u_int64_t maxsize, ui;
196014192793Skrw 
196114192793Skrw 	maxsize = max_partition_size(lp, partno);
196224a2c1a4Smillert 
1963117239d3Skrw 	ui = getuint64(lp, "size", "Size of the partition. "
19647da73705Skrw 	    "You may also say +/- amount for a relative change.",
196514192793Skrw 	    DL_GETPSIZE(pp), maxsize, DL_GETPOFFSET(pp),
1966525051f1Sotto 	    DO_CONVERSIONS | ((pp->p_fstype == FS_BSDFFS ||
1967525051f1Sotto 	    pp->p_fstype == FS_SWAP) ?  DO_ROUNDING : 0));
1968e9ff19beSkrw 
1969e9ff19beSkrw 	if (ui == ULLONG_MAX - 1)
197024a2c1a4Smillert 		fputs("Command aborted\n", stderr);
1971e9ff19beSkrw 	else if (ui == ULLONG_MAX)
197224a2c1a4Smillert 		fputs("Invalid entry\n", stderr);
197328e3704eSkrw 	else if (ui == 0)
19743f74e3efSkrw 		fputs("The size must be > 0 sectors\n", stderr);
197540e98e9fSkrw 	else if (ui + DL_GETPOFFSET(pp) > ending_sector)
197640e98e9fSkrw 		fprintf(stderr, "The size can't be more than "
197740e98e9fSkrw 		    "%llu sectors, or the partition would\n"
197840e98e9fSkrw 		    "extend beyond the last sector (%llu) of the "
197940e98e9fSkrw 		    "OpenBSD portion of\nthe disk. "
198040e98e9fSkrw 		    "The 'b' command can change this limit.\n",
198140e98e9fSkrw 		    ending_sector - DL_GETPOFFSET(pp), ending_sector);
198214192793Skrw 	else if (ui > maxsize)
198314192793Skrw 		fprintf(stderr,"Sorry, there are only %llu sectors left\n",
198414192793Skrw 		    maxsize);
198559ccf790Skrw 	else {
198659ccf790Skrw 		DL_SETPSIZE(pp, ui);
198724a2c1a4Smillert 		return (0);
198824a2c1a4Smillert 	}
1989e9ff19beSkrw 
1990e9ff19beSkrw 	/* Partition size was not set. */
1991e9ff19beSkrw 	return (1);
1992e9ff19beSkrw }
199324a2c1a4Smillert 
199424a2c1a4Smillert int
19958809fabbSderaadt get_fsize(struct disklabel *lp, int partno)
199624a2c1a4Smillert {
19971e0ad43cSotto 	u_int64_t ui, fsize, frag;
199824a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
199924a2c1a4Smillert 
2000a4c87e64Skrw 	if (!expert || pp->p_fstype != FS_BSDFFS)
2001a4c87e64Skrw 		return (0);
2002a4c87e64Skrw 
2003ddfcbf38Sotto 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
2004ddfcbf38Sotto 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
2005ddfcbf38Sotto 	if (fsize == 0)
2006ddfcbf38Sotto 		frag = 8;
2007ddfcbf38Sotto 
200824a2c1a4Smillert 	for (;;) {
2009117239d3Skrw 		ui = getuint64(lp, "fragment size",
2010c782b064Skrw 		    "Size of ffs block fragments. A multiple of the disk "
2011c782b064Skrw 		    "sector-size.", fsize, ULLONG_MAX-2, 0, 0);
20121e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
201324a2c1a4Smillert 			fputs("Command aborted\n", stderr);
201424a2c1a4Smillert 			return (1);
2015c782b064Skrw 		} else if (ui == ULLONG_MAX) {
201624a2c1a4Smillert 			fputs("Invalid entry\n", stderr);
2017c782b064Skrw 		} else if (ui < lp->d_secsize || (ui % lp->d_secsize) != 0) {
2018c782b064Skrw 			fprintf(stderr, "Error: fragment size must be a "
2019c782b064Skrw 			    "multiple of the disk sector size (%d)\n",
2020c782b064Skrw 			    lp->d_secsize);
2021c782b064Skrw 		} else
202224a2c1a4Smillert 			break;
202324a2c1a4Smillert 	}
202424a2c1a4Smillert 	if (ui == 0)
202524a2c1a4Smillert 		puts("Zero fragment size implies zero block size");
2026ddfcbf38Sotto 	pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(ui, frag);
202724a2c1a4Smillert 	return(0);
202824a2c1a4Smillert }
202924a2c1a4Smillert 
203024a2c1a4Smillert int
20318809fabbSderaadt get_bsize(struct disklabel *lp, int partno)
203224a2c1a4Smillert {
20336be94867Skrw 	u_int64_t adj, ui, bsize, frag, fsize, orig_offset, orig_size;
203424a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
20356be94867Skrw 	char *p;
203624a2c1a4Smillert 
2037a49bdda8Skrw 	if (pp->p_fstype != FS_BSDFFS)
2038a4c87e64Skrw 		return (0);
2039a4c87e64Skrw 
204024a2c1a4Smillert 	/* Avoid dividing by zero... */
2041ddfcbf38Sotto 	if (pp->p_fragblock == 0)
204224a2c1a4Smillert 		return(1);
2043ddfcbf38Sotto 
2044a49bdda8Skrw 	if (!expert)
2045a49bdda8Skrw 		goto align;
2046a49bdda8Skrw 
2047ddfcbf38Sotto 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
2048ddfcbf38Sotto 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
204924a2c1a4Smillert 
205024a2c1a4Smillert 	for (;;) {
2051117239d3Skrw 		ui = getuint64(lp, "block size",
20520cf7e76dSotto 		    "Size of ffs blocks. 1, 2, 4 or 8 times ffs fragment size.",
2053c782b064Skrw 		    fsize * frag, ULLONG_MAX - 2, 0, 0);
205424a2c1a4Smillert 
205524a2c1a4Smillert 		/* sanity checks */
20561e0ad43cSotto 		if (ui == ULLONG_MAX - 1) {
205724a2c1a4Smillert 			fputs("Command aborted\n", stderr);
205824a2c1a4Smillert 			return(1);
20591e0ad43cSotto 		} else if (ui == ULLONG_MAX)
206024a2c1a4Smillert 			fputs("Invalid entry\n", stderr);
206124a2c1a4Smillert 		else if (ui < getpagesize())
206224a2c1a4Smillert 			fprintf(stderr,
206324a2c1a4Smillert 			    "Error: block size must be at least as big "
206424a2c1a4Smillert 			    "as page size (%d).\n", getpagesize());
20650cf7e76dSotto 		else if (ui < fsize || (fsize != ui && fsize * 2 != ui &&
20660cf7e76dSotto 		    fsize * 4 != ui && fsize * 8 != ui))
20670cf7e76dSotto 			fprintf(stderr, "Error: block size must be 1, 2, 4 or "
20680cf7e76dSotto 			    "8 times fragment size (%llu).\n",
20692777acfaSchl 			    (unsigned long long) fsize);
207024a2c1a4Smillert 		else
207124a2c1a4Smillert 			break;
207224a2c1a4Smillert 	}
20730cf7e76dSotto 	frag = ui / fsize;
20740cf7e76dSotto 	pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fsize, frag);
2075a49bdda8Skrw 
2076a49bdda8Skrw #ifndef SUN_CYLCHECK
20776be94867Skrw 	p = getstring("Align partition to block size",
20786be94867Skrw 	    "Round the partition offset and size to multiples of bsize?", "y");
20796be94867Skrw 
20806be94867Skrw 	if (*p == 'n' || *p == 'N')
20816be94867Skrw 		return (0);
20826be94867Skrw #endif
20836be94867Skrw 
20846be94867Skrw align:
20856be94867Skrw 
20866be94867Skrw #ifndef SUN_CYLCHECK
20876be94867Skrw 	orig_size = DL_GETPSIZE(pp);
20886be94867Skrw 	orig_offset = DL_GETPOFFSET(pp);
20896be94867Skrw 
2090a49bdda8Skrw 	bsize = (DISKLABELV1_FFS_FRAG(pp->p_fragblock) *
2091a49bdda8Skrw 	    DISKLABELV1_FFS_FSIZE(pp->p_fragblock)) / lp->d_secsize;
2092a49bdda8Skrw 	if (DL_GETPOFFSET(pp) != starting_sector) {
2093a49bdda8Skrw 		/* Can't change offset of first partition. */
2094a49bdda8Skrw 		adj = bsize - (DL_GETPOFFSET(pp) % bsize);
2095a49bdda8Skrw 		if (adj != 0 && adj != bsize) {
2096a49bdda8Skrw 			DL_SETPOFFSET(pp, DL_GETPOFFSET(pp) + adj);
2097a49bdda8Skrw 			DL_SETPSIZE(pp, DL_GETPSIZE(pp) - adj);
2098a49bdda8Skrw 		}
2099a49bdda8Skrw 	}
2100a49bdda8Skrw 	/* Always align end. */
2101a49bdda8Skrw 	adj = (DL_GETPOFFSET(pp) + DL_GETPSIZE(pp)) % bsize;
2102a49bdda8Skrw 	if (adj > 0)
2103a49bdda8Skrw 		DL_SETPSIZE(pp, DL_GETPSIZE(pp) - adj);
21046be94867Skrw 
2105ef5288d7Skrw 	if (orig_offset != DL_GETPOFFSET(pp) && !aflag)
21066be94867Skrw 		printf("Rounding offset to bsize (%llu sectors): %llu\n",
21076be94867Skrw 		    bsize, DL_GETPOFFSET(pp));
2108ef5288d7Skrw 	if (orig_size != DL_GETPSIZE(pp) && !aflag)
21096be94867Skrw 		printf("Rounding size to bsize (%llu sectors): %llu\n",
21106be94867Skrw 		    bsize, DL_GETPSIZE(pp));
2111a49bdda8Skrw #endif
211224a2c1a4Smillert 	return(0);
211324a2c1a4Smillert }
211424a2c1a4Smillert 
211524a2c1a4Smillert int
21168809fabbSderaadt get_fstype(struct disklabel *lp, int partno)
211724a2c1a4Smillert {
211824a2c1a4Smillert 	char *p;
21191e0ad43cSotto 	u_int64_t ui;
212024a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
212124a2c1a4Smillert 
212224a2c1a4Smillert 	if (pp->p_fstype < FSMAXTYPES) {
2123c33fcabaSmillert 		p = getstring("FS type",
212424a2c1a4Smillert 		    "Filesystem type (usually 4.2BSD or swap)",
212524a2c1a4Smillert 		    fstypenames[pp->p_fstype]);
212624a2c1a4Smillert 		if (p == NULL) {
212724a2c1a4Smillert 			fputs("Command aborted\n", stderr);
212824a2c1a4Smillert 			return(1);
212924a2c1a4Smillert 		}
213024a2c1a4Smillert 		for (ui = 0; ui < FSMAXTYPES; ui++) {
213124a2c1a4Smillert 			if (!strcasecmp(p, fstypenames[ui])) {
213224a2c1a4Smillert 				pp->p_fstype = ui;
213324a2c1a4Smillert 				break;
213424a2c1a4Smillert 			}
213524a2c1a4Smillert 		}
213624a2c1a4Smillert 		if (ui >= FSMAXTYPES) {
2137430e1380Skrw 			printf("Unrecognized filesystem type '%s', treating "
2138430e1380Skrw 			    "as 'unknown'\n", p);
213924a2c1a4Smillert 			pp->p_fstype = FS_OTHER;
214024a2c1a4Smillert 		}
214124a2c1a4Smillert 	} else {
214224a2c1a4Smillert 		for (;;) {
2143117239d3Skrw 			ui = getuint64(lp, "FS type (decimal)",
2144430e1380Skrw 			    "Filesystem type as a decimal number; usually 7 "
2145430e1380Skrw 			    "(4.2BSD) or 1 (swap).",
214624a2c1a4Smillert 			    pp->p_fstype, pp->p_fstype, 0, 0);
21471e0ad43cSotto 			if (ui == ULLONG_MAX - 1) {
214824a2c1a4Smillert 				fputs("Command aborted\n", stderr);
214924a2c1a4Smillert 				return(1);
21501e0ad43cSotto 			} if (ui == ULLONG_MAX)
215124a2c1a4Smillert 				fputs("Invalid entry\n", stderr);
215224a2c1a4Smillert 			else
215324a2c1a4Smillert 				break;
215424a2c1a4Smillert 		}
215524a2c1a4Smillert 		pp->p_fstype = ui;
215624a2c1a4Smillert 	}
215724a2c1a4Smillert 	return(0);
215824a2c1a4Smillert }
215924a2c1a4Smillert 
216024a2c1a4Smillert int
216134ae4198Skrw get_mp(struct disklabel *lp, int partno)
216224a2c1a4Smillert {
216324a2c1a4Smillert 	struct partition *pp = &lp->d_partitions[partno];
2164ec9fde5fSkrw 	char *p;
2165ec9fde5fSkrw 	int i;
216624a2c1a4Smillert 
216734ae4198Skrw 	if (fstabfile && pp->p_fstype != FS_UNUSED &&
216824a2c1a4Smillert 	    pp->p_fstype != FS_SWAP && pp->p_fstype != FS_BOOT &&
216924a2c1a4Smillert 	    pp->p_fstype != FS_OTHER) {
2170ddaff619Smillert 		for (;;) {
2171c33fcabaSmillert 			p = getstring("mount point",
217224a2c1a4Smillert 			    "Where to mount this filesystem (ie: / /var /usr)",
217334ae4198Skrw 			    mountpoints[partno] ? mountpoints[partno] : "none");
217424a2c1a4Smillert 			if (p == NULL) {
217524a2c1a4Smillert 				fputs("Command aborted\n", stderr);
217624a2c1a4Smillert 				return(1);
217724a2c1a4Smillert 			}
2178ddaff619Smillert 			if (strcasecmp(p, "none") == 0) {
217934ae4198Skrw 				free(mountpoints[partno]);
218034ae4198Skrw 				mountpoints[partno] = NULL;
2181ddaff619Smillert 				break;
2182ddaff619Smillert 			}
2183ec9fde5fSkrw 			for (i = 0; i < MAXPARTITIONS; i++)
218493160b9bSkrw 				if (mountpoints[i] != NULL && i != partno &&
2185ec9fde5fSkrw 				    strcmp(p, mountpoints[i]) == 0)
2186ec9fde5fSkrw 					break;
2187ec9fde5fSkrw 			if (i < MAXPARTITIONS) {
218893160b9bSkrw 				fprintf(stderr, "'%c' already being mounted at "
218993160b9bSkrw 				    "'%s'\n", 'a'+i, p);
2190ec9fde5fSkrw 				break;
2191ec9fde5fSkrw 			}
2192ddaff619Smillert 			if (*p == '/') {
2193ddaff619Smillert 				/* XXX - might as well realloc */
219434ae4198Skrw 				free(mountpoints[partno]);
219534ae4198Skrw 				if ((mountpoints[partno] = strdup(p)) == NULL)
219624a2c1a4Smillert 					errx(4, "out of memory");
2197ddaff619Smillert 				break;
2198ddaff619Smillert 			}
2199ddaff619Smillert 			fputs("Mount points must start with '/'\n", stderr);
220024a2c1a4Smillert 		}
220124a2c1a4Smillert 	}
220224a2c1a4Smillert 	return(0);
220324a2c1a4Smillert }
22043f843443Smillert 
22053f843443Smillert int
22068809fabbSderaadt micmp(const void *a1, const void *a2)
22073f843443Smillert {
22083f843443Smillert 	struct mountinfo *mi1 = (struct mountinfo *)a1;
22093f843443Smillert 	struct mountinfo *mi2 = (struct mountinfo *)a2;
22103f843443Smillert 
22113f843443Smillert 	/* We want all the NULLs at the end... */
22123f843443Smillert 	if (mi1->mountpoint == NULL && mi2->mountpoint == NULL)
22133f843443Smillert 		return(0);
22143f843443Smillert 	else if (mi1->mountpoint == NULL)
22153f843443Smillert 		return(1);
22163f843443Smillert 	else if (mi2->mountpoint == NULL)
22173f843443Smillert 		return(-1);
22183f843443Smillert 	else
22193f843443Smillert 		return(strcmp(mi1->mountpoint, mi2->mountpoint));
22203f843443Smillert }
2221c33fcabaSmillert 
2222c33fcabaSmillert void
222387023ed9Skrw get_geometry(int f, struct disklabel **dgpp)
2224c33fcabaSmillert {
2225c33fcabaSmillert 	struct stat st;
2226c33fcabaSmillert 	struct disklabel *disk_geop;
222787023ed9Skrw 
2228c33fcabaSmillert 	if (fstat(f, &st) == -1)
2229c33fcabaSmillert 		err(4, "Can't stat device");
2230c33fcabaSmillert 
2231c33fcabaSmillert 	/* Get disk geometry */
2232c33fcabaSmillert 	if ((disk_geop = calloc(1, sizeof(struct disklabel))) == NULL)
2233c33fcabaSmillert 		errx(4, "out of memory");
22349a379a6cSkrw 	if (ioctl(f, DIOCGPDINFO, disk_geop) < 0)
22359a379a6cSkrw 		err(4, "ioctl DIOCGPDINFO");
2236c33fcabaSmillert 	*dgpp = disk_geop;
2237c33fcabaSmillert }
2238c33fcabaSmillert 
2239c33fcabaSmillert void
22408809fabbSderaadt set_geometry(struct disklabel *lp, struct disklabel *dgp,
224187023ed9Skrw     struct disklabel *ugp, char *p)
2242c33fcabaSmillert {
2243c33fcabaSmillert 	if (p == NULL) {
22449a36aa41Ssthen 		p = getstring("[d]isk or [u]ser geometry",
2245c33fcabaSmillert 		    "Enter 'd' to use the geometry based on what the disk "
22469a36aa41Ssthen 		    "itself thinks it is, or 'u' to use the geometry that "
22479a36aa41Ssthen 		    "was found in the label.",
2248c33fcabaSmillert 		    "d");
2249c33fcabaSmillert 	}
2250c33fcabaSmillert 	if (p == NULL) {
2251c33fcabaSmillert 		fputs("Command aborted\n", stderr);
2252c33fcabaSmillert 		return;
2253c33fcabaSmillert 	}
2254c33fcabaSmillert 	switch (*p) {
2255c33fcabaSmillert 	case 'd':
2256c33fcabaSmillert 	case 'D':
2257c33fcabaSmillert 		if (dgp == NULL)
2258c33fcabaSmillert 			fputs("BIOS geometry not defined.\n", stderr);
2259c33fcabaSmillert 		else {
2260c33fcabaSmillert 			lp->d_secsize = dgp->d_secsize;
2261c33fcabaSmillert 			lp->d_nsectors = dgp->d_nsectors;
2262c33fcabaSmillert 			lp->d_ntracks = dgp->d_ntracks;
2263c33fcabaSmillert 			lp->d_ncylinders = dgp->d_ncylinders;
2264c33fcabaSmillert 			lp->d_secpercyl = dgp->d_secpercyl;
226534af67a3Sotto 			DL_SETDSIZE(lp, DL_GETDSIZE(dgp));
2266c33fcabaSmillert 		}
2267c33fcabaSmillert 		break;
2268c33fcabaSmillert 	case 'u':
2269c33fcabaSmillert 	case 'U':
2270c33fcabaSmillert 		if (ugp == NULL)
2271c33fcabaSmillert 			fputs("BIOS geometry not defined.\n", stderr);
2272c33fcabaSmillert 		else {
2273c33fcabaSmillert 			lp->d_secsize = ugp->d_secsize;
2274c33fcabaSmillert 			lp->d_nsectors = ugp->d_nsectors;
2275c33fcabaSmillert 			lp->d_ntracks = ugp->d_ntracks;
2276c33fcabaSmillert 			lp->d_ncylinders = ugp->d_ncylinders;
2277c33fcabaSmillert 			lp->d_secpercyl = ugp->d_secpercyl;
227834af67a3Sotto 			DL_SETDSIZE(lp, DL_GETDSIZE(ugp));
2279c33fcabaSmillert 			if (dgp != NULL && ugp->d_secsize == dgp->d_secsize &&
2280c33fcabaSmillert 			    ugp->d_nsectors == dgp->d_nsectors &&
2281c33fcabaSmillert 			    ugp->d_ntracks == dgp->d_ntracks &&
2282c33fcabaSmillert 			    ugp->d_ncylinders == dgp->d_ncylinders &&
2283c33fcabaSmillert 			    ugp->d_secpercyl == dgp->d_secpercyl &&
228434af67a3Sotto 			    DL_GETDSIZE(ugp) == DL_GETDSIZE(dgp))
2285c33fcabaSmillert 				fputs("Note: user geometry is the same as disk "
2286c33fcabaSmillert 				    "geometry.\n", stderr);
2287c33fcabaSmillert 		}
2288c33fcabaSmillert 		break;
2289c33fcabaSmillert 	default:
22909a36aa41Ssthen 		fputs("You must enter either 'd' or 'u'.\n", stderr);
2291c33fcabaSmillert 		break;
2292c33fcabaSmillert 	}
2293c33fcabaSmillert }
22949afbe9eeSmillert 
22959afbe9eeSmillert void
22969fdcb4d6Skrw zero_partitions(struct disklabel *lp)
22979afbe9eeSmillert {
22989afbe9eeSmillert 	int i;
22999afbe9eeSmillert 
2300b4ed6301Skrw 	for (i = 0; i < MAXPARTITIONS; i++) {
23019afbe9eeSmillert 		memset(&lp->d_partitions[i], 0, sizeof(struct partition));
2302b4ed6301Skrw 		free(mountpoints[i]);
2303b4ed6301Skrw 		mountpoints[i] = NULL;
2304b4ed6301Skrw 	}
2305b4ed6301Skrw 
230634af67a3Sotto 	DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
23079afbe9eeSmillert }
230814192793Skrw 
230914192793Skrw u_int64_t
231014192793Skrw max_partition_size(struct disklabel *lp, int partno)
231114192793Skrw {
231214192793Skrw 	struct partition *pp = &lp->d_partitions[partno];
231314192793Skrw 	struct diskchunk *chunks;
231444ffe03bSotto 	u_int64_t maxsize = 0, offset;
231514192793Skrw 	int fstype, i;
231614192793Skrw 
231714192793Skrw 	fstype = pp->p_fstype;
231814192793Skrw 	pp->p_fstype = FS_UNUSED;
231914192793Skrw 	chunks = free_chunks(lp);
232014192793Skrw 	pp->p_fstype = fstype;
232114192793Skrw 
232214192793Skrw 	offset = DL_GETPOFFSET(pp);
232314192793Skrw 	for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) {
232414192793Skrw 		if (offset < chunks[i].start || offset >= chunks[i].stop)
232514192793Skrw 			continue;
232614192793Skrw 		maxsize = chunks[i].stop - offset;
232714192793Skrw 		break;
232814192793Skrw 	}
232914192793Skrw 	return (maxsize);
233014192793Skrw }
2331aff3f969Sotto 
2332aff3f969Sotto void
2333eafadddfSkrw psize(u_int64_t sz, char unit, struct disklabel *lp)
2334aff3f969Sotto {
2335aff3f969Sotto 	double d = scale(sz, unit, lp);
2336aff3f969Sotto 	if (d < 0)
2337aff3f969Sotto 		printf("%llu", sz);
2338aff3f969Sotto 	else
2339aff3f969Sotto 		printf("%.*f%c", unit == 'B' ? 0 : 1, d, unit);
2340aff3f969Sotto }
2341aff3f969Sotto 
2342aff3f969Sotto void
234334ae4198Skrw display_edit(struct disklabel *lp, char unit, u_int64_t fr)
2344aff3f969Sotto {
2345aff3f969Sotto 	int i;
2346aff3f969Sotto 
2347352d199bSkrw 	unit = canonical_unit(lp, unit);
2348aff3f969Sotto 
2349aff3f969Sotto 	printf("OpenBSD area: ");
235059882f1dSkrw 	psize(starting_sector, 0, lp);
2351aff3f969Sotto 	printf("-");
235259882f1dSkrw 	psize(ending_sector, 0, lp);
2353aff3f969Sotto 	printf("; size: ");
2354aff3f969Sotto 	psize(ending_sector - starting_sector, unit, lp);
2355aff3f969Sotto 	printf("; free: ");
2356aff3f969Sotto 	psize(fr, unit, lp);
2357aff3f969Sotto 
2358aff3f969Sotto 	printf("\n#    %16.16s %16.16s  fstype [fsize bsize  cpg]\n",
2359aff3f969Sotto 	    "size", "offset");
2360aff3f969Sotto 	for (i = 0; i < lp->d_npartitions; i++)
236134ae4198Skrw 		display_partition(stdout, lp, i, unit);
2362aff3f969Sotto }
2363