xref: /openbsd/sbin/disklabel/editor.c (revision 854d339c)
1 /*	$OpenBSD: editor.c,v 1.418 2024/03/22 21:49:52 jan Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2000 Todd C. Miller <millert@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>	/* MAXBSIZE DEV_BSIZE */
20 #include <sys/types.h>
21 #include <sys/signal.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <sys/dkio.h>
25 #include <sys/sysctl.h>
26 #define	DKTYPENAMES
27 #include <sys/disklabel.h>
28 
29 #include <ufs/ffs/fs.h>
30 
31 #include <ctype.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <libgen.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <limits.h>
41 
42 #include "extern.h"
43 #include "pathnames.h"
44 
45 #define	ROUNDUP(_s, _a)		((((_s) + (_a) - 1) / (_a)) * (_a))
46 #define	ROUNDDOWN(_s, _a)	(((_s) / (_a)) * (_a))
47 #define	CHUNKSZ(_c)		((_c)->stop - (_c)->start)
48 
49 /* flags for getuint64() */
50 #define	DO_CONVERSIONS	0x00000001
51 #define	DO_ROUNDING	0x00000002
52 
53 /* flags for alignpartition() */
54 #define	ROUND_OFFSET_UP		0x00000001
55 #define	ROUND_OFFSET_DOWN	0x00000002
56 #define	ROUND_SIZE_UP		0x00000004
57 #define	ROUND_SIZE_DOWN		0x00000008
58 #define	ROUND_SIZE_OVERLAP	0x00000010
59 
60 /* Special return values for getnumber and getuint64() */
61 #define	CMD_ABORTED	(ULLONG_MAX - 1)
62 #define	CMD_BADVALUE	(ULLONG_MAX)
63 
64 /* structure to describe a portion of a disk */
65 struct diskchunk {
66 	u_int64_t start;
67 	u_int64_t stop;
68 };
69 
70 /* used when sorting mountpoints in mpsave() */
71 struct mountinfo {
72 	char *mountpoint;
73 	int partno;
74 };
75 
76 /* used when allocating all space according to recommendations */
77 
78 struct space_allocation {
79 	u_int64_t	minsz;	/* starts as blocks, xlated to sectors. */
80 	u_int64_t	maxsz;	/* starts as blocks, xlated to sectors. */
81 	int		rate;	/* % of extra space to use */
82 	char	       *mp;
83 };
84 
85 /*
86  * NOTE! Changing partition sizes in the space_allocation tables
87  *       requires corresponding updates to the *.ok files in
88  *	 /usr/src/regress/sbin/disklabel.
89  */
90 
91 /* entries for swap and var are changed by editor_allocspace() */
92 struct space_allocation alloc_big[] = {
93 	{  MEG(150),         GIG(1),   5, "/"		},
94 	{   MEG(80),       MEG(256),  10, "swap"	},
95 	{  MEG(120),         GIG(4),   8, "/tmp"	},
96 	{   MEG(80),         GIG(4),  13, "/var"	},
97 	{ MEG(1500),        GIG(30),  10, "/usr"	},
98 	{  MEG(384),         GIG(1),   3, "/usr/X11R6"	},
99 	{    GIG(1),        GIG(20),  15, "/usr/local"	},
100 	{    GIG(2),         GIG(5),   2, "/usr/src"	},
101 	{    GIG(5),         GIG(6),   4, "/usr/obj"	},
102 	{    GIG(1),       GIG(300),  30, "/home"	}
103 	/* Anything beyond this leave for the user to decide */
104 };
105 
106 struct space_allocation alloc_medium[] = {
107 	{  MEG(800),         GIG(2),   5, "/"		},
108 	{   MEG(80),       MEG(256),  10, "swap"	},
109 	{ MEG(1300),         GIG(3),  78, "/usr"	},
110 	{  MEG(256),         GIG(2),   7, "/home"	}
111 };
112 
113 struct space_allocation alloc_small[] = {
114 	{  MEG(700),         GIG(4),  95, "/"		},
115 	{    MEG(1),       MEG(256),   5, "swap"	}
116 };
117 
118 struct space_allocation alloc_stupid[] = {
119 	{    MEG(1),      MEG(2048), 100, "/"		}
120 };
121 
122 #ifndef nitems
123 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
124 #endif
125 
126 struct alloc_table {
127 	struct space_allocation *table;
128 	int sz;
129 };
130 
131 struct alloc_table alloc_table_default[] = {
132 	{ alloc_big,	nitems(alloc_big) },
133 	{ alloc_medium,	nitems(alloc_medium) },
134 	{ alloc_small,	nitems(alloc_small) },
135 	{ alloc_stupid,	nitems(alloc_stupid) }
136 };
137 struct alloc_table *alloc_table = alloc_table_default;
138 int alloc_table_nitems = 4;
139 
140 void	edit_packname(struct disklabel *);
141 void	editor_resize(struct disklabel *, const char *);
142 void	editor_add(struct disklabel *, const char *);
143 void	editor_change(struct disklabel *, const char *);
144 u_int64_t editor_countfree(const struct disklabel *);
145 void	editor_delete(struct disklabel *, const char *);
146 void	editor_help(void);
147 void	editor_modify(struct disklabel *, const char *);
148 void	editor_name(const struct disklabel *, const char *);
149 char	*getstring(const char *, const char *, const char *);
150 u_int64_t getuint64(const struct disklabel *, char *, char *, u_int64_t,
151     u_int64_t, int *);
152 u_int64_t getnumber(const char *, const char *, u_int32_t, u_int32_t);
153 int	getpartno(const struct disklabel *, const char *, const char *);
154 int	has_overlap(struct disklabel *);
155 int	partition_cmp(const void *, const void *);
156 const struct partition **sort_partitions(const struct disklabel *, int);
157 void	find_bounds(const struct disklabel *);
158 void	set_bounds(struct disklabel *);
159 void	set_duid(struct disklabel *);
160 int	set_fragblock(struct disklabel *, int);
161 const struct diskchunk *free_chunks(const struct disklabel *, int);
162 int	micmp(const void *, const void *);
163 int	mpequal(char **, char **);
164 int	get_fstype(struct disklabel *, int);
165 int	get_mp(const struct disklabel *, int);
166 int	get_offset(struct disklabel *, int);
167 int	get_size(struct disklabel *, int);
168 void	zero_partitions(struct disklabel *);
169 u_int64_t max_partition_size(const struct disklabel *, int);
170 void	display_edit(const struct disklabel *, char);
171 void	psize(u_int64_t sz, char unit, const struct disklabel *lp);
172 char	*get_token(char **);
173 int	apply_unit(double, u_char, u_int64_t *);
174 int	parse_sizespec(const char *, double *, char **);
175 int	parse_sizerange(char *, u_int64_t *, u_int64_t *);
176 int	parse_pct(char *, int *);
177 int	alignpartition(struct disklabel *, int, u_int64_t, u_int64_t, int);
178 int	allocate_space(struct disklabel *, const struct alloc_table *);
179 void	allocate_physmemincr(struct space_allocation *);
180 int	allocate_partition(struct disklabel *, struct space_allocation *);
181 const struct diskchunk *allocate_diskchunk(const struct disklabel *,
182     const struct space_allocation *);
183 
184 static u_int64_t starting_sector;
185 static u_int64_t ending_sector;
186 static int resizeok;
187 
188 /*
189  * Simple partition editor.
190  */
191 int
editor(int f)192 editor(int f)
193 {
194 	struct disklabel origlabel, lastlabel, tmplabel, newlab = lab;
195 	struct partition *pp;
196 	FILE *fp;
197 	char buf[BUFSIZ], *cmd, *arg;
198 	char **omountpoints = NULL;
199 	char **origmountpoints = NULL, **tmpmountpoints = NULL;
200 	int i, error = 0;
201 
202 	/* Alloc and init mount point info */
203 	if (!(omountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
204 	    !(origmountpoints = calloc(MAXPARTITIONS, sizeof(char *))) ||
205 	    !(tmpmountpoints = calloc(MAXPARTITIONS, sizeof(char *))))
206 		err(1, NULL);
207 
208 	/* How big is the OpenBSD portion of the disk?  */
209 	find_bounds(&newlab);
210 
211 	/* Make sure there is no partition overlap. */
212 	if (has_overlap(&newlab))
213 		errx(1, "can't run when there is partition overlap.");
214 
215 	/* If we don't have a 'c' partition, create one. */
216 	pp = &newlab.d_partitions[RAW_PART];
217 	if (newlab.d_npartitions <= RAW_PART || DL_GETPSIZE(pp) == 0) {
218 		puts("No 'c' partition found, adding one that spans the disk.");
219 		if (newlab.d_npartitions <= RAW_PART)
220 			newlab.d_npartitions = RAW_PART + 1;
221 		DL_SETPOFFSET(pp, 0);
222 		DL_SETPSIZE(pp, DL_GETDSIZE(&newlab));
223 		pp->p_fstype = FS_UNUSED;
224 		pp->p_fragblock = pp->p_cpg = 0;
225 	}
226 
227 #ifdef SUN_CYLCHECK
228 	if ((newlab.d_flags & D_VENDOR) && !quiet) {
229 		puts("This platform requires that partition offsets/sizes "
230 		    "be on cylinder boundaries.\n"
231 		    "Partition offsets/sizes will be rounded to the "
232 		    "nearest cylinder automatically.");
233 	}
234 #endif
235 
236 	/* Save the (U|u)ndo labels and mountpoints. */
237 	mpcopy(origmountpoints, mountpoints);
238 	origlabel = newlab;
239 	lastlabel = newlab;
240 
241 	puts("Label editor (enter '?' for help at any prompt)");
242 	for (;;) {
243 		fprintf(stdout, "%s%s> ", dkname,
244 		    (memcmp(&lab, &newlab, sizeof(newlab)) == 0) ? "" : "*");
245 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
246 			putchar('\n');
247 			buf[0] = 'q';
248 			buf[1] = '\0';
249 		}
250 		if ((cmd = strtok(buf, " \t\r\n")) == NULL)
251 			continue;
252 		arg = strtok(NULL, " \t\r\n");
253 
254 		if ((*cmd != 'u') && (*cmd != 'U')) {
255 			/*
256 			 * Save undo info in case the command tries to make
257 			 * changes but decides not to.
258 			 */
259 			tmplabel = lastlabel;
260 			lastlabel = newlab;
261 			mpcopy(tmpmountpoints, omountpoints);
262 			mpcopy(omountpoints, mountpoints);
263 		}
264 
265 		switch (*cmd) {
266 		case '?':
267 		case 'h':
268 			editor_help();
269 			break;
270 
271 		case 'A':
272 			if (ioctl(f, DIOCGPDINFO, &newlab) == -1) {
273 				warn("DIOCGPDINFO");
274 				newlab = lastlabel;
275 			} else {
276 				int oquiet = quiet;
277 				aflag = 1;
278 				quiet = 0;
279 				editor_allocspace(&newlab);
280 				quiet = oquiet;
281 			}
282 			break;
283 		case 'a':
284 			editor_add(&newlab, arg);
285 			break;
286 
287 		case 'b':
288 			set_bounds(&newlab);
289 			break;
290 
291 		case 'c':
292 			editor_change(&newlab, arg);
293 			break;
294 
295 		case 'D':
296 			if (ioctl(f, DIOCGPDINFO, &newlab) == -1)
297 				warn("DIOCGPDINFO");
298 			else {
299 				dflag = 1;
300 				for (i = 0; i < MAXPARTITIONS; i++) {
301 					free(mountpoints[i]);
302 					mountpoints[i] = NULL;
303 				}
304 			}
305 			break;
306 
307 		case 'd':
308 			editor_delete(&newlab, arg);
309 			break;
310 
311 		case 'e':
312 			edit_packname(&newlab);
313 			break;
314 
315 		case 'i':
316 			set_duid(&newlab);
317 			break;
318 
319 		case 'm':
320 			editor_modify(&newlab, arg);
321 			break;
322 
323 		case 'n':
324 			if (!fstabfile) {
325 				fputs("This option is not valid when run "
326 				    "without the -F or -f flags.\n", stderr);
327 				break;
328 			}
329 			editor_name(&newlab, arg);
330 			break;
331 
332 		case 'p':
333 			display_edit(&newlab, arg ? *arg : 0);
334 			break;
335 
336 		case 'l':
337 			display(stdout, &newlab, arg ? *arg : 0, 0);
338 			break;
339 
340 		case 'M': {
341 			sig_t opipe = signal(SIGPIPE, SIG_IGN);
342 			char *pager, *comm = NULL;
343 			extern const u_char manpage[];
344 			extern const int manpage_sz;
345 
346 			if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
347 				pager = _PATH_LESS;
348 
349 			if (asprintf(&comm, "gunzip -qc|%s", pager) != -1 &&
350 			    (fp = popen(comm, "w")) != NULL) {
351 				(void) fwrite(manpage, manpage_sz, 1, fp);
352 				pclose(fp);
353 			} else
354 				warn("unable to execute %s", pager);
355 
356 			free(comm);
357 			(void)signal(SIGPIPE, opipe);
358 			break;
359 		}
360 
361 		case 'q':
362 			if (donothing) {
363 				puts("In no change mode, not writing label.");
364 				goto done;
365 			}
366 
367 			/*
368 			 * If we haven't changed the original label, and it
369 			 * wasn't a default label or an auto-allocated label,
370 			 * there is no need to do anything before exiting. Note
371 			 * that 'w' will reset dflag and aflag to allow 'q' to
372 			 * exit without further questions.
373 			 */
374 			if (!dflag && !aflag &&
375 			    memcmp(&lab, &newlab, sizeof(newlab)) == 0) {
376 				puts("No label changes.");
377 				/* Save mountpoint info. */
378 				mpsave(&newlab);
379 				goto done;
380 			}
381 			do {
382 				arg = getstring("Write new label?",
383 				    "Write the modified label to disk?",
384 				    "y");
385 			} while (arg && tolower((unsigned char)*arg) != 'y' &&
386 			    tolower((unsigned char)*arg) != 'n');
387 			if (arg && tolower((unsigned char)*arg) == 'y') {
388 				if (writelabel(f, &newlab) == 0) {
389 					newlab = lab; /* lab now has UID info */
390 					goto done;
391 				}
392 				warnx("unable to write label");
393 			}
394 			error = 1;
395 			goto done;
396 			/* NOTREACHED */
397 			break;
398 
399 		case 'R':
400 			if (aflag && resizeok)
401 				editor_resize(&newlab, arg);
402 			else
403 				fputs("Resize only implemented for auto "
404 				    "allocated labels\n", stderr);
405 			break;
406 
407 		case 'r': {
408 			const struct diskchunk *chunk;
409 			uint64_t total = 0;
410 			/* Display free space. */
411 			chunk = free_chunks(&newlab, -1);
412 			for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
413 				total += CHUNKSZ(chunk);
414 				fprintf(stderr, "Free sectors: %16llu - %16llu "
415 				    "(%16llu)\n",
416 				    chunk->start, chunk->stop - 1,
417 				    CHUNKSZ(chunk));
418 			}
419 			fprintf(stderr, "Total free sectors: %llu.\n", total);
420 			break;
421 		}
422 
423 		case 's':
424 			if (arg == NULL) {
425 				arg = getstring("Filename",
426 				    "Name of the file to save label into.",
427 				    NULL);
428 				if (arg == NULL || *arg == '\0')
429 					break;
430 			}
431 			if ((fp = fopen(arg, "w")) == NULL) {
432 				warn("cannot open %s", arg);
433 			} else {
434 				display(fp, &newlab, 0, 1);
435 				(void)fclose(fp);
436 			}
437 			break;
438 
439 		case 'U':
440 			/*
441 			 * If we allow 'U' repeatedly, information would be
442 			 * lost. This way multiple 'U's followed by 'u' will
443 			 * undo the 'U's.
444 			 */
445 			if (memcmp(&newlab, &origlabel, sizeof(newlab)) ||
446 			    !mpequal(mountpoints, origmountpoints)) {
447 				tmplabel = newlab;
448 				newlab = origlabel;
449 				lastlabel = tmplabel;
450 				mpcopy(tmpmountpoints, mountpoints);
451 				mpcopy(mountpoints, origmountpoints);
452 				mpcopy(omountpoints, tmpmountpoints);
453 			}
454 			puts("Original label and mount points restored.");
455 			break;
456 
457 		case 'u':
458 			tmplabel = newlab;
459 			newlab = lastlabel;
460 			lastlabel = tmplabel;
461 			mpcopy(tmpmountpoints, mountpoints);
462 			mpcopy(mountpoints, omountpoints);
463 			mpcopy(omountpoints, tmpmountpoints);
464 			puts("Last change undone.");
465 			break;
466 
467 		case 'w':
468 			if (donothing)  {
469 				puts("In no change mode, not writing label.");
470 				break;
471 			}
472 
473 			/* Write label to disk. */
474 			if (writelabel(f, &newlab) != 0)
475 				warnx("unable to write label");
476 			else {
477 				dflag = aflag = 0;
478 				newlab = lab; /* lab now has UID info */
479 			}
480 			break;
481 
482 		case 'x':
483 			goto done;
484 			break;
485 
486 		case 'z':
487 			zero_partitions(&newlab);
488 			break;
489 
490 		case '\n':
491 			break;
492 
493 		default:
494 			printf("Unknown option: %c ('?' for help)\n", *cmd);
495 			break;
496 		}
497 
498 		/*
499 		 * If no changes were made to label or mountpoints, then
500 		 * restore undo info.
501 		 */
502 		if (memcmp(&newlab, &lastlabel, sizeof(newlab)) == 0 &&
503 		    (mpequal(mountpoints, omountpoints))) {
504 			lastlabel = tmplabel;
505 			mpcopy(omountpoints, tmpmountpoints);
506 		}
507 	}
508 done:
509 	mpfree(omountpoints, DISCARD);
510 	mpfree(origmountpoints, DISCARD);
511 	mpfree(tmpmountpoints, DISCARD);
512 	return error;
513 }
514 
515 /*
516  * Allocate all disk space according to standard recommendations for a
517  * root disk.
518  */
519 int
editor_allocspace(struct disklabel * lp_org)520 editor_allocspace(struct disklabel *lp_org)
521 {
522 	struct disklabel label;
523 	struct partition *pp;
524 	u_int64_t pstart, pend;
525 	int i;
526 
527 	/* How big is the OpenBSD portion of the disk?  */
528 	find_bounds(lp_org);
529 
530 	resizeok = 1;
531 	for (i = 0;  i < MAXPARTITIONS; i++) {
532 		if (i == RAW_PART)
533 			continue;
534 		pp = &lp_org->d_partitions[i];
535 		if (DL_GETPSIZE(pp) == 0 || pp->p_fstype == FS_UNUSED)
536 			continue;
537 		pstart = DL_GETPOFFSET(pp);
538 		pend = pstart + DL_GETPSIZE(pp);
539 		if (((pstart >= starting_sector && pstart < ending_sector) ||
540 		    (pend > starting_sector && pend <= ending_sector)))
541 			resizeok = 0; /* Part of OBSD area is in use! */
542 	}
543 
544 	for (i = 0; i < alloc_table_nitems; i++) {
545 		memcpy(&label, lp_org, sizeof(label));
546 		if (allocate_space(&label, &alloc_table[i]) == 0) {
547 			memcpy(lp_org, &label, sizeof(struct disklabel));
548 			return 0;
549 		}
550 	}
551 
552 	return 1;
553 }
554 
555 const struct diskchunk *
allocate_diskchunk(const struct disklabel * lp,const struct space_allocation * sa)556 allocate_diskchunk(const struct disklabel *lp,
557     const struct space_allocation *sa)
558 {
559 	const struct diskchunk *chunk;
560 	static struct diskchunk largest;
561 	uint64_t maxstop;
562 
563 	largest.start = largest.stop = 0;
564 
565 	chunk = free_chunks(lp, -1);
566 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
567 		if (CHUNKSZ(chunk) > CHUNKSZ(&largest))
568 			largest = *chunk;
569 	}
570 	maxstop = largest.start + DL_BLKTOSEC(lp, sa->maxsz);
571 	if (maxstop > largest.stop)
572 		maxstop = largest.stop;
573 #ifdef SUN_CYLCHECK
574 	if (lp->d_flags & D_VENDOR) {
575 		largest.start = ROUNDUP(largest.start, lp->d_secpercyl);
576 		maxstop = ROUNDUP(maxstop, lp->d_secpercyl);
577 		if (maxstop > largest.stop)
578 			maxstop -= lp->d_secpercyl;
579 		if (largest.start >= maxstop)
580 			largest.start = largest.stop = maxstop = 0;
581 	}
582 #endif
583 	if (maxstop < largest.stop)
584 		largest.stop = maxstop;
585 	if (CHUNKSZ(&largest) < DL_BLKTOSEC(lp, sa->minsz))
586 		return NULL;
587 
588 	return &largest;
589 }
590 
591 int
allocate_partition(struct disklabel * lp,struct space_allocation * sa)592 allocate_partition(struct disklabel *lp, struct space_allocation *sa)
593 {
594 	const struct diskchunk *chunk;
595 	struct partition *pp;
596 	unsigned int partno;
597 
598 	for (partno = 0; partno < nitems(lp->d_partitions); partno++) {
599 		if (partno == RAW_PART)
600 			continue;
601 		pp = &lp->d_partitions[partno];
602 		if (DL_GETPSIZE(pp) == 0 || pp->p_fstype == FS_UNUSED)
603 			break;
604 	}
605 	if (partno >= nitems(lp->d_partitions))
606 		return 1;		/* No free partition. */
607 
608 	/* Find appropriate chunk of free space. */
609 	chunk = allocate_diskchunk(lp, sa);
610 	if (chunk == NULL)
611 		return 1;
612 
613 	if (strcasecmp(sa->mp, "raid") == 0)
614 		pp->p_fstype = FS_RAID;
615 	else if (strcasecmp(sa->mp, "swap") == 0)
616 		pp->p_fstype = FS_SWAP;
617 	else if (sa->mp[0] == '/')
618 		pp->p_fstype = FS_BSDFFS;
619 	else
620 		return 1;
621 
622 	DL_SETPSIZE(pp, chunk->stop - chunk->start);
623 	DL_SETPOFFSET(pp, chunk->start);
624 
625 	if (pp->p_fstype == FS_BSDFFS && DL_GETPSIZE(pp) > 0) {
626 		mountpoints[partno] = strdup(sa->mp);
627 		if (mountpoints[partno] == NULL)
628 			err(1, NULL);
629 		if (set_fragblock(lp, partno))
630 			return 1;
631 	}
632 
633 	return 0;
634 }
635 
636 void
allocate_physmemincr(struct space_allocation * sa)637 allocate_physmemincr(struct space_allocation *sa)
638 {
639 	u_int64_t memblks;
640 	extern int64_t physmem;
641 
642 	if (physmem == 0)
643 		return;
644 
645 	memblks = physmem / DEV_BSIZE;
646 	if (strcasecmp(sa->mp, "swap") == 0) {
647 		if (memblks < MEG(256))
648 			sa->minsz = sa->maxsz = 2 * memblks;
649 		else
650 			sa->maxsz += memblks;
651 	} else if (strcasecmp(sa->mp, "/var") == 0) {
652 		sa->maxsz += 2 * memblks;
653 	}
654 }
655 
656 int
allocate_space(struct disklabel * lp,const struct alloc_table * alloc_table)657 allocate_space(struct disklabel *lp, const struct alloc_table *alloc_table)
658 {
659 	struct space_allocation sa[MAXPARTITIONS];
660 	u_int64_t maxsz, xtrablks;
661 	int i;
662 
663 	xtrablks = DL_SECTOBLK(lp, editor_countfree(lp));
664 	memset(sa, 0, sizeof(sa));
665 	for (i = 0; i < alloc_table->sz; i++) {
666 		sa[i] = alloc_table->table[i];
667 		if (alloc_table->table == alloc_big)
668 			allocate_physmemincr(&sa[i]);
669 		if (xtrablks < sa[i].minsz)
670 			return 1;	/* Too few free blocks. */
671 		xtrablks -= sa[i].minsz;
672 	}
673 	sa[alloc_table->sz - 1].rate = 100; /* Last allocation is greedy. */
674 
675 	for (i = lp->d_npartitions; i < MAXPARTITIONS; i++) {
676 		if (i == RAW_PART)
677 			continue;
678 		memset(&lp->d_partitions[i], 0, sizeof(lp->d_partitions[i]));
679 	}
680 	lp->d_npartitions = MAXPARTITIONS;
681 
682 	mpfree(mountpoints, KEEP);
683 	for (i = 0; i < alloc_table->sz; i++) {
684 		if (sa[i].rate == 100)
685 			maxsz = sa[i].minsz + xtrablks;
686 		else
687 			maxsz = sa[i].minsz + (xtrablks / 100) * sa[i].rate;
688 		if (maxsz < sa[i].maxsz)
689 			sa[i].maxsz = maxsz;
690 		if (allocate_partition(lp, &sa[i])) {
691 			mpfree(mountpoints, KEEP);
692 			return 1;
693 		}
694 	}
695 
696 	return 0;
697 }
698 
699 /*
700  * Resize a partition, moving all subsequent partitions
701  */
702 void
editor_resize(struct disklabel * lp,const char * p)703 editor_resize(struct disklabel *lp, const char *p)
704 {
705 	struct disklabel label;
706 	struct partition *pp, *prev;
707 	u_int64_t ui, sz, off;
708 	int partno, i, flags, shrunk;
709 
710 	label = *lp;
711 
712 	if ((partno = getpartno(&label, p, "resize")) == -1)
713 		return;
714 
715 	pp = &label.d_partitions[partno];
716 	sz = DL_GETPSIZE(pp);
717 	if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP) {
718 		fputs("Cannot resize spoofed partition\n", stderr);
719 		return;
720 	}
721 	flags = DO_CONVERSIONS;
722 	ui = getuint64(lp, "[+|-]new size (with unit)",
723 	    "new size or amount to grow (+) or shrink (-) partition including "
724 	    "unit", sz, sz + editor_countfree(lp), &flags);
725 
726 	if (ui == CMD_ABORTED)
727 		return;
728 	else if (ui == CMD_BADVALUE)
729 		return;
730 	else if (ui == 0) {
731 		fputs("The size must be > 0 sectors\n", stderr);
732 		return;
733 	}
734 
735 #ifdef SUN_CYLCHECK
736 	if (lp->d_flags & D_VENDOR)
737 		ui = ROUNDUP(ui, lp->d_secpercyl);
738 #endif
739 	if (DL_GETPOFFSET(pp) + ui > ending_sector) {
740 		fputs("Amount too big\n", stderr);
741 		return;
742 	}
743 
744 	DL_SETPSIZE(pp, ui);
745 	pp->p_fragblock = 0;
746 	if (set_fragblock(&label, partno) == 1)
747 		return;
748 
749 	/*
750 	 * Pack partitions above the resized partition, leaving unused
751 	 * partitions alone.
752 	 */
753 	shrunk = -1;
754 	prev = pp;
755 	for (i = partno + 1; i < MAXPARTITIONS; i++) {
756 		if (i == RAW_PART)
757 			continue;
758 		pp = &label.d_partitions[i];
759 		if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP)
760 			continue;
761 		sz = DL_GETPSIZE(pp);
762 		if (sz == 0)
763 			continue;
764 
765 		off = DL_GETPOFFSET(prev) + DL_GETPSIZE(prev);
766 
767 		if (off < ending_sector) {
768 			DL_SETPOFFSET(pp, off);
769 			if (off + DL_GETPSIZE(pp) > ending_sector) {
770 				DL_SETPSIZE(pp, ending_sector - off);
771 				pp->p_fragblock = 0;
772 				if (set_fragblock(&label, i) == 1)
773 					return;
774 				shrunk = i;
775 			}
776 		} else {
777 			fputs("Amount too big\n", stderr);
778 			return;
779 		}
780 		prev = pp;
781 	}
782 
783 	if (shrunk != -1)
784 		fprintf(stderr, "Partition %c shrunk to %llu sectors to make "
785 		    "room\n", 'a' + shrunk,
786 		    DL_GETPSIZE(&label.d_partitions[shrunk]));
787 	*lp = label;
788 }
789 
790 /*
791  * Add a new partition.
792  */
793 void
editor_add(struct disklabel * lp,const char * p)794 editor_add(struct disklabel *lp, const char *p)
795 {
796 	struct partition *pp;
797 	const struct diskchunk *chunk;
798 	int partno;
799 	u_int64_t new_offset, new_size;
800 
801 	chunk = free_chunks(lp, -1);
802 	new_size = new_offset = 0;
803 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
804 		if (CHUNKSZ(chunk) > new_size) {
805 			new_size = CHUNKSZ(chunk);
806 			new_offset = chunk->start;
807 		}
808 	}
809 
810 #ifdef SUN_CYLCHECK
811 	if ((lp->d_flags & D_VENDOR) && new_size < lp->d_secpercyl) {
812 		fputs("No space left, you need to shrink a partition "
813 		    "(need at least one full cylinder)\n",
814 		    stderr);
815 		return;
816 	}
817 #endif
818 	if (new_size == 0) {
819 		fputs("No space left, you need to shrink a partition\n",
820 		    stderr);
821 		return;
822 	}
823 
824 	if ((partno = getpartno(lp, p, "add")) == -1)
825 		return;
826 	pp = &lp->d_partitions[partno];
827 	memset(pp, 0, sizeof(*pp));
828 
829 	/*
830 	 * Increase d_npartitions if necessary. Ensure all new partitions are
831 	 * zero'ed to avoid inadvertent overlaps.
832 	 */
833 	for(; lp->d_npartitions <= partno; lp->d_npartitions++)
834 		memset(&lp->d_partitions[lp->d_npartitions], 0, sizeof(*pp));
835 
836 	DL_SETPSIZE(pp, new_size);
837 	DL_SETPOFFSET(pp, new_offset);
838 	pp->p_fstype = partno == 1 ? FS_SWAP : FS_BSDFFS;
839 
840 	if (get_offset(lp, partno) == 0 &&
841 	    get_size(lp, partno) == 0 &&
842 	    get_fstype(lp, partno) == 0 &&
843 	    get_mp(lp, partno) == 0 &&
844 	    set_fragblock(lp, partno) == 0)
845 		return;
846 
847 	/* Bailed out at some point, so effectively delete the partition. */
848 	memset(pp, 0, sizeof(*pp));
849 }
850 
851 /*
852  * Set the mountpoint of an existing partition ('name').
853  */
854 void
editor_name(const struct disklabel * lp,const char * p)855 editor_name(const struct disklabel *lp, const char *p)
856 {
857 	int partno;
858 
859 	if ((partno = getpartno(lp, p, "name")) == -1)
860 		return;
861 
862 	get_mp(lp, partno);
863 }
864 
865 /*
866  * Change an existing partition.
867  */
868 void
editor_modify(struct disklabel * lp,const char * p)869 editor_modify(struct disklabel *lp, const char *p)
870 {
871 	struct partition opp, *pp;
872 	int partno;
873 
874 	if ((partno = getpartno(lp, p, "modify")) == -1)
875 		return;
876 
877 	pp = &lp->d_partitions[partno];
878 	opp = *pp;
879 
880 	if (get_offset(lp, partno) == 0 &&
881 	    get_size(lp, partno) == 0   &&
882 	    get_fstype(lp, partno) == 0 &&
883 	    get_mp(lp, partno) == 0 &&
884 	    set_fragblock(lp, partno) == 0)
885 		return;
886 
887 	/* Bailed out at some point, so undo any changes. */
888 	*pp = opp;
889 }
890 
891 /*
892  * Delete an existing partition.
893  */
894 void
editor_delete(struct disklabel * lp,const char * p)895 editor_delete(struct disklabel *lp, const char *p)
896 {
897 	struct partition *pp;
898 	int partno;
899 
900 	if ((partno = getpartno(lp, p, "delete")) == -1)
901 		return;
902 	if (partno == lp->d_npartitions) {
903 		zero_partitions(lp);
904 		return;
905 	}
906 	pp = &lp->d_partitions[partno];
907 
908 	/* Really delete it (as opposed to just setting to "unused") */
909 	memset(pp, 0, sizeof(*pp));
910 	free(mountpoints[partno]);
911 	mountpoints[partno] = NULL;
912 }
913 
914 /*
915  * Change the size of an existing partition.
916  */
917 void
editor_change(struct disklabel * lp,const char * p)918 editor_change(struct disklabel *lp, const char *p)
919 {
920 	struct partition *pp;
921 	int partno;
922 
923 	if ((partno = getpartno(lp, p, "change size")) == -1)
924 		return;
925 
926 	pp = &lp->d_partitions[partno];
927 	printf("Partition %c is currently %llu sectors in size, and can have "
928 	    "a maximum\nsize of %llu sectors.\n",
929 	    'a' + partno, DL_GETPSIZE(pp), max_partition_size(lp, partno));
930 
931 	/* Get new size */
932 	get_size(lp, partno);
933 }
934 
935 /*
936  * Sort the partitions based on starting offset.
937  * This assumes there can be no overlap.
938  */
939 int
partition_cmp(const void * e1,const void * e2)940 partition_cmp(const void *e1, const void *e2)
941 {
942 	struct partition *p1 = *(struct partition **)e1;
943 	struct partition *p2 = *(struct partition **)e2;
944 	u_int64_t o1 = DL_GETPOFFSET(p1);
945 	u_int64_t o2 = DL_GETPOFFSET(p2);
946 
947 	if (o1 < o2)
948 		return -1;
949 	else if (o1 > o2)
950 		return 1;
951 	else
952 		return 0;
953 }
954 
955 char *
getstring(const char * prompt,const char * helpstring,const char * oval)956 getstring(const char *prompt, const char *helpstring, const char *oval)
957 {
958 	static char buf[BUFSIZ];
959 	int n;
960 
961 	buf[0] = '\0';
962 	do {
963 		printf("%s: [%s] ", prompt, oval ? oval : "");
964 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
965 			buf[0] = '\0';
966 			if (feof(stdin)) {
967 				clearerr(stdin);
968 				putchar('\n');
969 				fputs("Command aborted\n", stderr);
970 				return NULL;
971 			}
972 		}
973 		n = strlen(buf);
974 		if (n > 0 && buf[n-1] == '\n')
975 			buf[--n] = '\0';
976 		if (buf[0] == '?')
977 			puts(helpstring);
978 		else if (oval != NULL && buf[0] == '\0')
979 			strlcpy(buf, oval, sizeof(buf));
980 	} while (buf[0] == '?');
981 
982 	return &buf[0];
983 }
984 
985 int
getpartno(const struct disklabel * lp,const char * p,const char * action)986 getpartno(const struct disklabel *lp, const char *p, const char *action)
987 {
988 	char buf[2] = { '\0', '\0'};
989 	const char *promptfmt = "partition to %s";
990 	const char *helpfmt = "Partition must be between 'a' and '%c' "
991 	    "(excluding 'c')%s.\n";
992 	const struct partition *pp;
993 	char *help = NULL, *prompt = NULL;
994 	unsigned char maxpart;
995 	unsigned int partno;
996 	int add, delete, inuse;
997 
998 	add = strcmp("add", action) == 0;
999 	delete = strcmp("delete", action) == 0;
1000 	maxpart = 'a' - 1 + (add ? MAXPARTITIONS : lp->d_npartitions);
1001 
1002 	if (p == NULL) {
1003 		if (asprintf(&prompt, promptfmt, action) == -1 ||
1004 		    asprintf(&help, helpfmt, maxpart, delete ? ", or '*'" : "")
1005 		    == -1) {
1006 			fprintf(stderr, "Unable to build prompt or help\n");
1007 			goto done;
1008 		}
1009 		if (add) {
1010 			/* Default to first unused partition. */
1011 			for (partno = 0; partno < MAXPARTITIONS; partno++) {
1012 				if (partno == RAW_PART)
1013 					continue;
1014 				pp = &lp->d_partitions[partno];
1015 				if (partno >= lp->d_npartitions ||
1016 				    DL_GETPSIZE(pp) == 0 ||
1017 				    pp->p_fstype == FS_UNUSED) {
1018 					buf[0] = 'a' + partno;
1019 					p = buf;
1020 					break;
1021 				}
1022 			}
1023 		}
1024 		p = getstring(prompt, help, p);
1025 		free(prompt);
1026 		free(help);
1027 		if (p == NULL || *p == '\0')
1028 			goto done;
1029 	}
1030 
1031 	if (delete && strlen(p) == 1 && *p == '*')
1032 		return lp->d_npartitions;
1033 
1034 	if (strlen(p) > 1 || *p < 'a' || *p > maxpart || *p == 'c') {
1035 		fprintf(stderr, helpfmt, maxpart, delete ? ", or '*'" : "");
1036 		goto done;
1037 	}
1038 
1039 	partno = *p - 'a';
1040 	pp = &lp->d_partitions[partno];
1041 	inuse = partno < lp->d_npartitions && DL_GETPSIZE(pp) > 0 &&
1042 	    pp->p_fstype != FS_UNUSED;
1043 
1044 	if ((add && !inuse) || (!add && inuse))
1045 		return partno;
1046 
1047 	fprintf(stderr, "Partition '%c' is %sin use.\n", *p,
1048 	    inuse ? "" : "not ");
1049 
1050  done:
1051 	return -1;
1052 }
1053 
1054 /*
1055  * Returns
1056  * 0 .. CMD_ABORTED - 1	==> valid value
1057  * CMD_BADVALUE		==> invalid value
1058  * CMD_ABORTED		==> ^D on input
1059  */
1060 u_int64_t
getnumber(const char * prompt,const char * helpstring,u_int32_t oval,u_int32_t maxval)1061 getnumber(const char *prompt, const char *helpstring, u_int32_t oval,
1062     u_int32_t maxval)
1063 {
1064 	char buf[BUFSIZ], *p;
1065 	int rslt;
1066 	long long rval;
1067 	const char *errstr;
1068 
1069 	rslt = snprintf(buf, sizeof(buf), "%u", oval);
1070 	if (rslt < 0 || (unsigned int)rslt >= sizeof(buf))
1071 		return CMD_BADVALUE;
1072 
1073 	p = getstring(prompt, helpstring, buf);
1074 	if (p == NULL)
1075 		return CMD_ABORTED;
1076 	if (strlen(p) == 0)
1077 		return oval;
1078 
1079 	rval = strtonum(p, 0, maxval, &errstr);
1080 	if (errstr != NULL) {
1081 		printf("%s must be between 0 and %u\n", prompt, maxval);
1082 		return CMD_BADVALUE;
1083 	}
1084 
1085 	return rval;
1086 }
1087 
1088 /*
1089  * Returns
1090  * 0 .. CMD_ABORTED - 1	==> valid value
1091  * CMD_BADVALUE		==> invalid value
1092  * CMD_ABORTED		==> ^D on input
1093  */
1094 u_int64_t
getuint64(const struct disklabel * lp,char * prompt,char * helpstring,u_int64_t oval,u_int64_t maxval,int * flags)1095 getuint64(const struct disklabel *lp, char *prompt, char *helpstring,
1096     u_int64_t oval, u_int64_t maxval, int *flags)
1097 {
1098 	char buf[21], *p, operator = '\0';
1099 	char *unit = NULL;
1100 	u_int64_t rval = oval;
1101 	double d;
1102 	int rslt;
1103 
1104 	rslt = snprintf(buf, sizeof(buf), "%llu", oval);
1105 	if (rslt < 0 || (unsigned int)rslt >= sizeof(buf))
1106 		goto invalid;
1107 
1108 	p = getstring(prompt, helpstring, buf);
1109 	if (p == NULL)
1110 		return CMD_ABORTED;
1111 	else if (p[0] == '\0')
1112 		rval = oval;
1113 	else if (p[0] == '*' && p[1] == '\0')
1114 		rval = maxval;
1115 	else {
1116 		if (*p == '+' || *p == '-')
1117 			operator = *p++;
1118 		if (parse_sizespec(p, &d, &unit) == -1)
1119 			goto invalid;
1120 		if (unit == NULL)
1121 			rval = d;
1122 		else if (flags != NULL && (*flags & DO_CONVERSIONS) == 0)
1123 			goto invalid;
1124 		else {
1125 			switch (tolower((unsigned char)*unit)) {
1126 			case 'b':
1127 				rval = d / lp->d_secsize;
1128 				break;
1129 			case 'c':
1130 				rval = d * lp->d_secpercyl;
1131 				break;
1132 			case '%':
1133 				rval = DL_GETDSIZE(lp) * (d / 100.0);
1134 				break;
1135 			case '&':
1136 				rval = maxval * (d / 100.0);
1137 				break;
1138 			default:
1139 				if (apply_unit(d, *unit, &rval) == -1)
1140 					goto invalid;
1141 				rval = DL_BLKTOSEC(lp, rval);
1142 				break;
1143 			}
1144 		}
1145 
1146 		/* Range check then apply [+-] operator */
1147 		if (operator == '+') {
1148 			if (CMD_ABORTED - oval > rval)
1149 				rval += oval;
1150 			else {
1151 				goto invalid;
1152 			}
1153 		} else if (operator == '-') {
1154 			if (oval >= rval)
1155 				rval = oval - rval;
1156 			else {
1157 				goto invalid;
1158 			}
1159 		}
1160 	}
1161 
1162 	if (flags != NULL) {
1163 		if (unit != NULL)
1164 			*flags |= DO_ROUNDING;
1165 #ifdef SUN_CYLCHECK
1166 		if (lp->d_flags & D_VENDOR)
1167 			*flags |= DO_ROUNDING;
1168 #endif
1169 	}
1170 	return rval;
1171 
1172 invalid:
1173 	fputs("Invalid entry\n", stderr);
1174 	return CMD_BADVALUE;
1175 }
1176 
1177 /*
1178  * Check for partition overlap in lp and prompt the user to resolve the overlap
1179  * if any is found.  Returns 1 if unable to resolve, else 0.
1180  */
1181 int
has_overlap(struct disklabel * lp)1182 has_overlap(struct disklabel *lp)
1183 {
1184 	const struct partition **spp;
1185 	int i, p1, p2;
1186 	char *line = NULL;
1187 	size_t linesize = 0;
1188 	ssize_t linelen;
1189 
1190 	for (;;) {
1191 		spp = sort_partitions(lp, -1);
1192 		for (i = 0; spp[i+1] != NULL; i++) {
1193 			if (DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]) >
1194 			    DL_GETPOFFSET(spp[i+1]))
1195 				break;
1196 		}
1197 		if (spp[i+1] == NULL) {
1198 			free(line);
1199 			return 0;
1200 		}
1201 
1202 		p1 = 'a' + (spp[i] - lp->d_partitions);
1203 		p2 = 'a' + (spp[i+1] - lp->d_partitions);
1204 		printf("\nError, partitions %c and %c overlap:\n", p1, p2);
1205 		printf("#    %16.16s %16.16s  fstype [fsize bsize    cpg]\n",
1206 		    "size", "offset");
1207 		display_partition(stdout, lp, p1 - 'a', 0);
1208 		display_partition(stdout, lp, p2 - 'a', 0);
1209 
1210 		for (;;) {
1211 			printf("Disable which one? (%c %c) ", p1, p2);
1212 			linelen = getline(&line, &linesize, stdin);
1213 			if (linelen == -1)
1214 				goto done;
1215 			if (linelen == 2 && (line[0] == p1 || line[0] == p2))
1216 				break;
1217 		}
1218 		lp->d_partitions[line[0] - 'a'].p_fstype = FS_UNUSED;
1219 	}
1220 
1221 done:
1222 	putchar('\n');
1223 	free(line);
1224 	return 1;
1225 }
1226 
1227 void
edit_packname(struct disklabel * lp)1228 edit_packname(struct disklabel *lp)
1229 {
1230 	char *p;
1231 	struct disklabel oldlabel = *lp;
1232 
1233 	printf("Changing label description for %s:\n", specname);
1234 
1235 	/* pack/label id */
1236 	p = getstring("label name",
1237 	    "15 char string that describes this label, usually the disk name.",
1238 	    lp->d_packname);
1239 	if (p == NULL) {
1240 		*lp = oldlabel;		/* undo damage */
1241 		return;
1242 	}
1243 	strncpy(lp->d_packname, p, sizeof(lp->d_packname));	/* checked */
1244 }
1245 
1246 const struct partition **
sort_partitions(const struct disklabel * lp,int ignore)1247 sort_partitions(const struct disklabel *lp, int ignore)
1248 {
1249 	const static struct partition *spp[MAXPARTITIONS+2];
1250 	int i, npartitions;
1251 
1252 	memset(spp, 0, sizeof(spp));
1253 
1254 	for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) {
1255 		if (i != ignore && lp->d_partitions[i].p_fstype != FS_UNUSED &&
1256 		    DL_GETPSIZE(&lp->d_partitions[i]) != 0)
1257 			spp[npartitions++] = &lp->d_partitions[i];
1258 	}
1259 
1260 	/*
1261 	 * Sort the partitions based on starting offset.
1262 	 * This is safe because we guarantee no overlap.
1263 	 */
1264 	if (npartitions > 1)
1265 		if (mergesort((void *)spp, npartitions, sizeof(spp[0]),
1266 		    partition_cmp))
1267 			err(4, "failed to sort partition table");
1268 
1269 	return spp;
1270 }
1271 
1272 /*
1273  * Get beginning and ending sectors of the OpenBSD portion of the disk
1274  * from the user.
1275  */
1276 void
set_bounds(struct disklabel * lp)1277 set_bounds(struct disklabel *lp)
1278 {
1279 	u_int64_t ui, start_temp;
1280 
1281 	/* Starting sector */
1282 	for (;;) {
1283 		ui = getuint64(lp, "Starting sector",
1284 		    "The start of the OpenBSD portion of the disk.",
1285 		    starting_sector, DL_GETDSIZE(lp), NULL);
1286 		if (ui == CMD_ABORTED)
1287 			return;
1288 		else if (ui == CMD_BADVALUE)
1289 			;	/* Try again. */
1290 		else if (ui >= DL_GETDSIZE(lp))
1291 			fprintf(stderr, "starting sector must be < %llu\n",
1292 			    DL_GETDSIZE(lp));
1293 		else
1294 			break;
1295 	}
1296 	start_temp = ui;
1297 
1298 	/* Size */
1299 	for (;;) {
1300 		ui = getuint64(lp, "Size ('*' for entire disk)",
1301 		    "The size of the OpenBSD portion of the disk ('*' for the "
1302 		    "entire disk).", ending_sector - starting_sector,
1303 		    DL_GETDSIZE(lp) - start_temp, NULL);
1304 		if (ui == CMD_ABORTED)
1305 			return;
1306 		else if (ui == CMD_BADVALUE)
1307 			;	/* Try again. */
1308 		else if (ui > DL_GETDSIZE(lp) - start_temp)
1309 			fprintf(stderr, "size must be <= %llu\n",
1310 			    DL_GETDSIZE(lp) - start_temp);
1311 		else
1312 			break;
1313 	}
1314 	ending_sector = start_temp + ui;
1315 	DL_SETBEND(lp, ending_sector);
1316 	starting_sector = start_temp;
1317 	DL_SETBSTART(lp, starting_sector);
1318 }
1319 
1320 /*
1321  * Allow user to interactively change disklabel UID.
1322  */
1323 void
set_duid(struct disklabel * lp)1324 set_duid(struct disklabel *lp)
1325 {
1326 	char *s;
1327 	int i;
1328 
1329 	printf("The disklabel UID is currently: "
1330 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\n",
1331 	    lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3],
1332 	    lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7]);
1333 
1334 	do {
1335 		s = getstring("duid", "The disklabel UID, given as a 16 "
1336 		    "character hexadecimal string.", NULL);
1337 		if (s == NULL || strlen(s) == 0) {
1338 			fputs("Command aborted\n", stderr);
1339 			return;
1340 		}
1341 		i = duid_parse(lp, s);
1342 		if (i != 0)
1343 			fputs("Invalid UID entered.\n", stderr);
1344 	} while (i != 0);
1345 }
1346 
1347 /*
1348  * Return a list of the "chunks" of free space available
1349  */
1350 const struct diskchunk *
free_chunks(const struct disklabel * lp,int partno)1351 free_chunks(const struct disklabel *lp, int partno)
1352 {
1353 	const struct partition **spp;
1354 	static struct diskchunk chunks[MAXPARTITIONS + 2];
1355 	u_int64_t start, stop;
1356 	int i, numchunks;
1357 
1358 	/* Sort the in-use partitions based on offset */
1359 	spp = sort_partitions(lp, partno);
1360 
1361 	/* If there are no partitions, it's all free. */
1362 	if (spp[0] == NULL) {
1363 		chunks[0].start = starting_sector;
1364 		chunks[0].stop = ending_sector;
1365 		chunks[1].start = chunks[1].stop = 0;
1366 		return chunks;
1367 	}
1368 
1369 	/* Find chunks of free space */
1370 	numchunks = 0;
1371 	if (DL_GETPOFFSET(spp[0]) > starting_sector) {
1372 		chunks[0].start = starting_sector;
1373 		chunks[0].stop = DL_GETPOFFSET(spp[0]);
1374 		numchunks++;
1375 	}
1376 	for (i = 0; spp[i] != NULL; i++) {
1377 		start = DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]);
1378 		if (start < starting_sector)
1379 			start = starting_sector;
1380 		else if (start > ending_sector)
1381 			start = ending_sector;
1382 		if (spp[i + 1] != NULL)
1383 			stop = DL_GETPOFFSET(spp[i+1]);
1384 		else
1385 			stop = ending_sector;
1386 		if (stop < starting_sector)
1387 			stop = starting_sector;
1388 		else if (stop > ending_sector)
1389 			stop = ending_sector;
1390 		if (start < stop) {
1391 			chunks[numchunks].start = start;
1392 			chunks[numchunks].stop = stop;
1393 			numchunks++;
1394 		}
1395 	}
1396 
1397 	/* Terminate and return */
1398 	chunks[numchunks].start = chunks[numchunks].stop = 0;
1399 	return chunks;
1400 }
1401 
1402 void
find_bounds(const struct disklabel * lp)1403 find_bounds(const struct disklabel *lp)
1404 {
1405 	starting_sector = DL_GETBSTART(lp);
1406 	ending_sector = DL_GETBEND(lp);
1407 
1408 	if (ending_sector) {
1409 		if (verbose)
1410 			printf("Treating sectors %llu-%llu as the OpenBSD"
1411 			    " portion of the disk.\nYou can use the 'b'"
1412 			    " command to change this.\n\n", starting_sector,
1413 			    ending_sector);
1414 	}
1415 }
1416 
1417 /*
1418  * Calculate free space.
1419  */
1420 u_int64_t
editor_countfree(const struct disklabel * lp)1421 editor_countfree(const struct disklabel *lp)
1422 {
1423 	const struct diskchunk *chunk;
1424 	u_int64_t freesectors = 0;
1425 
1426 	chunk = free_chunks(lp, -1);
1427 
1428 	for (; chunk->start != 0 || chunk->stop != 0; chunk++)
1429 		freesectors += CHUNKSZ(chunk);
1430 
1431 	return freesectors;
1432 }
1433 
1434 void
editor_help(void)1435 editor_help(void)
1436 {
1437 	puts("Available commands:");
1438 	puts(
1439 " ? | h    - show help                 n [part] - set mount point\n"
1440 " A        - auto partition all space  p [unit] - print partitions\n"
1441 " a [part] - add partition             q        - quit & save changes\n"
1442 " b        - set OpenBSD boundaries    R [part] - resize auto allocated partition\n"
1443 " c [part] - change partition size     r        - display free space\n"
1444 " D        - reset label to default    s [path] - save label to file\n"
1445 " d [part] - delete partition          U        - undo all changes\n"
1446 " e        - edit label description    u        - undo last change\n"
1447 " i        - modify disklabel UID      w        - write label to disk\n"
1448 " l [unit] - print disk label header   x        - exit & lose changes\n"
1449 " M        - disklabel(8) man page     z        - delete all partitions\n"
1450 " m [part] - modify partition\n"
1451 "\n"
1452 "Suffixes can be used to indicate units other than sectors:\n"
1453 " 'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' (gigabytes) 't' (terabytes)\n"
1454 " 'c' (cylinders), '%' (% of total disk), '&' (% of free space).\n"
1455 "Values in non-sector units are truncated to the nearest cylinder boundary.");
1456 
1457 }
1458 
1459 void
mpcopy(char ** to,char ** from)1460 mpcopy(char **to, char **from)
1461 {
1462 	int i;
1463 
1464 	for (i = 0; i < MAXPARTITIONS; i++) {
1465 		free(to[i]);
1466 		to[i] = NULL;
1467 		if (from[i] != NULL) {
1468 			to[i] = strdup(from[i]);
1469 			if (to[i] == NULL)
1470 				err(1, NULL);
1471 		}
1472 	}
1473 }
1474 
1475 int
mpequal(char ** mp1,char ** mp2)1476 mpequal(char **mp1, char **mp2)
1477 {
1478 	int i;
1479 
1480 	for (i = 0; i < MAXPARTITIONS; i++) {
1481 		if (mp1[i] == NULL && mp2[i] == NULL)
1482 			continue;
1483 
1484 		if ((mp1[i] != NULL && mp2[i] == NULL) ||
1485 		    (mp1[i] == NULL && mp2[i] != NULL) ||
1486 		    (strcmp(mp1[i], mp2[i]) != 0))
1487 			return 0;
1488 	}
1489 	return 1;
1490 }
1491 
1492 void
mpsave(const struct disklabel * lp)1493 mpsave(const struct disklabel *lp)
1494 {
1495 	int i, j;
1496 	char bdev[PATH_MAX], *p;
1497 	struct mountinfo mi[MAXPARTITIONS];
1498 	FILE *fp;
1499 	u_int8_t fstype;
1500 
1501 	if (!fstabfile)
1502 		return;
1503 
1504 	memset(&mi, 0, sizeof(mi));
1505 
1506 	for (i = 0; i < MAXPARTITIONS; i++) {
1507 		fstype = lp->d_partitions[i].p_fstype;
1508 		if (mountpoints[i] != NULL || fstype == FS_SWAP) {
1509 			mi[i].mountpoint = mountpoints[i];
1510 			mi[i].partno = i;
1511 		}
1512 	}
1513 
1514 	/* Convert specname to bdev */
1515 	if (uidflag) {
1516 		snprintf(bdev, sizeof(bdev),
1517 		    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
1518 		    lab.d_uid[0], lab.d_uid[1], lab.d_uid[2], lab.d_uid[3],
1519 		    lab.d_uid[4], lab.d_uid[5], lab.d_uid[6], lab.d_uid[7],
1520 		    specname[strlen(specname)-1]);
1521 	} else if (strncmp(_PATH_DEV, specname, sizeof(_PATH_DEV) - 1) == 0 &&
1522 	    specname[sizeof(_PATH_DEV) - 1] == 'r') {
1523 		snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV,
1524 		    &specname[sizeof(_PATH_DEV)]);
1525 	} else {
1526 		if ((p = strrchr(specname, '/')) == NULL || *(++p) != 'r')
1527 			return;
1528 		*p = '\0';
1529 		snprintf(bdev, sizeof(bdev), "%s%s", specname, p + 1);
1530 		*p = 'r';
1531 	}
1532 	bdev[strlen(bdev) - 1] = '\0';
1533 
1534 	/* Sort mountpoints so we don't try to mount /usr/local before /usr */
1535 	qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp);
1536 
1537 	if ((fp = fopen(fstabfile, "w"))) {
1538 		for (i = 0; i < MAXPARTITIONS; i++) {
1539 			j =  mi[i].partno;
1540 			fstype = lp->d_partitions[j].p_fstype;
1541 			if (fstype == FS_RAID)
1542 				continue;
1543 			if (fstype == FS_SWAP) {
1544 				fprintf(fp, "%s%c none swap sw\n", bdev, 'a'+j);
1545 			} else if (mi[i].mountpoint) {
1546 				fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev,
1547 				    'a' + j, mi[i].mountpoint,
1548 				    fstypesnames[fstype], j == 0 ? 1 : 2);
1549 			}
1550 		}
1551 		fclose(fp);
1552 	}
1553 }
1554 
1555 void
mpfree(char ** mp,int action)1556 mpfree(char **mp, int action)
1557 {
1558 	int part;
1559 
1560 	if (mp == NULL)
1561 		return;
1562 
1563 	for (part = 0; part < MAXPARTITIONS; part++) {
1564 		free(mp[part]);
1565 		mp[part] = NULL;
1566 	}
1567 
1568 	if (action == DISCARD) {
1569 		free(mp);
1570 		mp = NULL;
1571 	}
1572 }
1573 
1574 int
get_offset(struct disklabel * lp,int partno)1575 get_offset(struct disklabel *lp, int partno)
1576 {
1577 	struct partition opp, *pp = &lp->d_partitions[partno];
1578 	u_int64_t ui, offsetalign;
1579 	int flags;
1580 
1581 	flags = DO_CONVERSIONS;
1582 	ui = getuint64(lp, "offset",
1583 	    "Starting sector for this partition.",
1584 	    DL_GETPOFFSET(pp),
1585 	    DL_GETPOFFSET(pp), &flags);
1586 
1587 	if (ui == CMD_ABORTED || ui == CMD_BADVALUE)
1588 		return 1;
1589 #ifdef SUN_AAT0
1590 	if (partno == 0 && ui != 0) {
1591 		fprintf(stderr, "This architecture requires that "
1592 		    "partition 'a' start at sector 0.\n");
1593 		return 1;
1594 	}
1595 #endif
1596 	opp = *pp;
1597 	DL_SETPOFFSET(pp, ui);
1598 	offsetalign = 1;
1599 	if ((flags & DO_ROUNDING) != 0 && pp->p_fstype == FS_BSDFFS)
1600 		offsetalign = lp->d_secpercyl;
1601 
1602 	if (alignpartition(lp, partno, offsetalign, 1, ROUND_OFFSET_UP) == 1) {
1603 		*pp = opp;
1604 		return 1;
1605 	}
1606 
1607 	return 0;
1608 }
1609 
1610 int
get_size(struct disklabel * lp,int partno)1611 get_size(struct disklabel *lp, int partno)
1612 {
1613 	struct partition opp, *pp = &lp->d_partitions[partno];
1614 	u_int64_t maxsize, ui, sizealign;
1615 	int flags;
1616 
1617 	maxsize = max_partition_size(lp, partno);
1618 	flags = DO_CONVERSIONS;
1619 	ui = getuint64(lp, "size", "Size of the partition. "
1620 	    "You may also say +/- amount for a relative change.",
1621 	    DL_GETPSIZE(pp), maxsize, &flags);
1622 
1623 	if (ui == CMD_ABORTED || ui == CMD_BADVALUE)
1624 		return 1;
1625 
1626 	opp = *pp;
1627 	DL_SETPSIZE(pp, ui);
1628 	sizealign = 1;
1629 	if ((flags & DO_ROUNDING) != 0 && (pp->p_fstype == FS_SWAP ||
1630 	    pp->p_fstype == FS_BSDFFS))
1631 		sizealign = lp->d_secpercyl;
1632 
1633 	if (alignpartition(lp, partno, 1, sizealign, ROUND_SIZE_UP) == 1) {
1634 		*pp = opp;
1635 		return 1;
1636 	}
1637 
1638 	return 0;
1639 }
1640 
1641 int
set_fragblock(struct disklabel * lp,int partno)1642 set_fragblock(struct disklabel *lp, int partno)
1643 {
1644 	struct partition opp, *pp = &lp->d_partitions[partno];
1645 	u_int64_t bytes, offsetalign, sizealign;
1646 	u_int32_t frag, fsize;
1647 
1648 	if (pp->p_fstype != FS_BSDFFS)
1649 		return 0;
1650 
1651 	if (pp->p_cpg == 0)
1652 		pp->p_cpg = 1;
1653 
1654 	fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
1655 	frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
1656 	if (fsize == 0) {
1657 		fsize = 2048;
1658 		frag = 8;
1659 		bytes = DL_GETPSIZE(pp) * lp->d_secsize;
1660 		if (bytes > 128ULL * 1024 * 1024 * 1024)
1661 			fsize *= 2;
1662 		if (bytes > 512ULL * 1024 * 1024 * 1024)
1663 			fsize *= 2;
1664 		if (fsize < lp->d_secsize)
1665 			fsize = lp->d_secsize;
1666 		if (fsize > MAXBSIZE / frag)
1667 			fsize = MAXBSIZE / frag;
1668 		pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fsize, frag);
1669 	}
1670 #ifdef SUN_CYLCHECK
1671 	return 0;
1672 #endif
1673 	opp = *pp;
1674 	sizealign = (DISKLABELV1_FFS_FRAG(pp->p_fragblock) *
1675 	    DISKLABELV1_FFS_FSIZE(pp->p_fragblock)) / lp->d_secsize;
1676 	offsetalign = 1;
1677 	if (DL_GETPOFFSET(pp) != starting_sector)
1678 		offsetalign = sizealign;
1679 
1680 	if (alignpartition(lp, partno, offsetalign, sizealign, ROUND_OFFSET_UP |
1681 	    ROUND_SIZE_DOWN | ROUND_SIZE_OVERLAP) == 1) {
1682 		*pp = opp;
1683 		return 1;
1684 	}
1685 
1686 	return 0;
1687 }
1688 
1689 int
get_fstype(struct disklabel * lp,int partno)1690 get_fstype(struct disklabel *lp, int partno)
1691 {
1692 	char *p;
1693 	u_int64_t ui;
1694 	struct partition *pp = &lp->d_partitions[partno];
1695 
1696 	if (pp->p_fstype < FSMAXTYPES) {
1697 		p = getstring("FS type",
1698 		    "Filesystem type (usually 4.2BSD or swap)",
1699 		    fstypenames[pp->p_fstype]);
1700 		if (p == NULL) {
1701 			return 1;
1702 		}
1703 		for (ui = 0; ui < FSMAXTYPES; ui++) {
1704 			if (!strcasecmp(p, fstypenames[ui])) {
1705 				pp->p_fstype = ui;
1706 				break;
1707 			}
1708 		}
1709 		if (ui >= FSMAXTYPES) {
1710 			printf("Unrecognized filesystem type '%s', treating "
1711 			    "as 'unknown'\n", p);
1712 			pp->p_fstype = FS_OTHER;
1713 		}
1714 	} else {
1715 		for (;;) {
1716 			ui = getnumber("FS type (decimal)",
1717 			    "Filesystem type as a decimal number; usually 7 "
1718 			    "(4.2BSD) or 1 (swap).",
1719 			    pp->p_fstype, UINT8_MAX);
1720 			if (ui == CMD_ABORTED)
1721 				return 1;
1722 			else if (ui == CMD_BADVALUE)
1723 				;	/* Try again. */
1724 			else
1725 				break;
1726 		}
1727 		pp->p_fstype = ui;
1728 	}
1729 	return 0;
1730 }
1731 
1732 int
get_mp(const struct disklabel * lp,int partno)1733 get_mp(const struct disklabel *lp, int partno)
1734 {
1735 	const struct partition *pp = &lp->d_partitions[partno];
1736 	char *p;
1737 	int i;
1738 
1739 	if (fstabfile == NULL ||
1740 	    pp->p_fstype == FS_UNUSED ||
1741 	    pp->p_fstype == FS_SWAP ||
1742 	    pp->p_fstype == FS_BOOT ||
1743 	    pp->p_fstype == FS_OTHER ||
1744 	    pp->p_fstype == FS_RAID) {
1745 		/* No fstabfile, no names. Not all fstypes can be named */
1746 		return 0;
1747 	}
1748 
1749 	for (;;) {
1750 		p = getstring("mount point",
1751 		    "Where to mount this filesystem (ie: / /var /usr)",
1752 		    mountpoints[partno] ? mountpoints[partno] : "none");
1753 		if (p == NULL)
1754 			return 1;
1755 		if (strcasecmp(p, "none") == 0) {
1756 			free(mountpoints[partno]);
1757 			mountpoints[partno] = NULL;
1758 			break;
1759 		}
1760 		for (i = 0; i < MAXPARTITIONS; i++)
1761 			if (mountpoints[i] != NULL && i != partno &&
1762 			    strcmp(p, mountpoints[i]) == 0)
1763 				break;
1764 		if (i < MAXPARTITIONS) {
1765 			fprintf(stderr, "'%c' already being mounted at "
1766 			    "'%s'\n", 'a'+i, p);
1767 			break;
1768 		}
1769 		if (*p == '/') {
1770 			/* XXX - might as well realloc */
1771 			free(mountpoints[partno]);
1772 			if ((mountpoints[partno] = strdup(p)) == NULL)
1773 				err(1, NULL);
1774 			break;
1775 		}
1776 		fputs("Mount points must start with '/'\n", stderr);
1777 	}
1778 
1779 	return 0;
1780 }
1781 
1782 int
micmp(const void * a1,const void * a2)1783 micmp(const void *a1, const void *a2)
1784 {
1785 	struct mountinfo *mi1 = (struct mountinfo *)a1;
1786 	struct mountinfo *mi2 = (struct mountinfo *)a2;
1787 
1788 	/* We want all the NULLs at the end... */
1789 	if (mi1->mountpoint == NULL && mi2->mountpoint == NULL)
1790 		return 0;
1791 	else if (mi1->mountpoint == NULL)
1792 		return 1;
1793 	else if (mi2->mountpoint == NULL)
1794 		return -1;
1795 	else
1796 		return strcmp(mi1->mountpoint, mi2->mountpoint);
1797 }
1798 
1799 void
zero_partitions(struct disklabel * lp)1800 zero_partitions(struct disklabel *lp)
1801 {
1802 	memset(lp->d_partitions, 0, sizeof(lp->d_partitions));
1803 	DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
1804 
1805 	mpfree(mountpoints, KEEP);
1806 }
1807 
1808 u_int64_t
max_partition_size(const struct disklabel * lp,int partno)1809 max_partition_size(const struct disklabel *lp, int partno)
1810 {
1811 	const struct diskchunk *chunk;
1812 	u_int64_t maxsize = 0, offset;
1813 
1814 	chunk = free_chunks(lp, partno);
1815 
1816 	offset = DL_GETPOFFSET(&lp->d_partitions[partno]);
1817 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
1818 		if (offset < chunk->start || offset >= chunk->stop)
1819 			continue;
1820 		maxsize = chunk->stop - offset;
1821 		break;
1822 	}
1823 	return maxsize;
1824 }
1825 
1826 void
psize(u_int64_t sz,char unit,const struct disklabel * lp)1827 psize(u_int64_t sz, char unit, const struct disklabel *lp)
1828 {
1829 	double d = scale(sz, unit, lp);
1830 	if (d < 0)
1831 		printf("%llu", sz);
1832 	else
1833 		printf("%.*f%c", unit == 'B' ? 0 : 1, d, unit);
1834 }
1835 
1836 void
display_edit(const struct disklabel * lp,char unit)1837 display_edit(const struct disklabel *lp, char unit)
1838 {
1839 	u_int64_t fr;
1840 	int i;
1841 
1842 	fr = editor_countfree(lp);
1843 	unit = canonical_unit(lp, unit);
1844 
1845 	printf("OpenBSD area: ");
1846 	psize(starting_sector, 0, lp);
1847 	printf("-");
1848 	psize(ending_sector, 0, lp);
1849 	printf("; size: ");
1850 	psize(ending_sector - starting_sector, unit, lp);
1851 	printf("; free: ");
1852 	psize(fr, unit, lp);
1853 
1854 	printf("\n#    %16.16s %16.16s  fstype [fsize bsize   cpg]\n",
1855 	    "size", "offset");
1856 	for (i = 0; i < lp->d_npartitions; i++)
1857 		display_partition(stdout, lp, i, unit);
1858 }
1859 
1860 void
parse_autotable(char * filename)1861 parse_autotable(char *filename)
1862 {
1863 	FILE	*cfile;
1864 	size_t	 linesize = 0;
1865 	char	*line = NULL, *buf, *t;
1866 	uint	 idx = 0, pctsum = 0;
1867 	struct space_allocation *sa;
1868 
1869 	if (strcmp(filename, "-") == 0)
1870 		cfile = stdin;
1871 	else if ((cfile = fopen(filename, "r")) == NULL)
1872 		err(1, "%s", filename);
1873 	if ((alloc_table = calloc(1, sizeof(struct alloc_table))) == NULL)
1874 		err(1, NULL);
1875 	alloc_table_nitems = 1;
1876 
1877 	while (getline(&line, &linesize, cfile) != -1) {
1878 		if ((alloc_table[0].table = reallocarray(alloc_table[0].table,
1879 		    idx + 1, sizeof(*sa))) == NULL)
1880 			err(1, NULL);
1881 		sa = &(alloc_table[0].table[idx]);
1882 		memset(sa, 0, sizeof(*sa));
1883 		idx++;
1884 
1885 		buf = line;
1886 		if ((sa->mp = get_token(&buf)) == NULL ||
1887 		    (sa->mp[0] != '/' && strcasecmp(sa->mp, "swap") &&
1888 		    strcasecmp(sa->mp, "raid")))
1889 			errx(1, "%s: parse error on line %u", filename, idx);
1890 		if ((t = get_token(&buf)) == NULL ||
1891 		    parse_sizerange(t, &sa->minsz, &sa->maxsz) == -1)
1892 			errx(1, "%s: parse error on line %u", filename, idx);
1893 		if ((t = get_token(&buf)) != NULL &&
1894 		    parse_pct(t, &sa->rate) == -1)
1895 			errx(1, "%s: parse error on line %u", filename, idx);
1896 		if (sa->minsz > sa->maxsz)
1897 			errx(1, "%s: min size > max size on line %u", filename,
1898 			    idx);
1899 		pctsum += sa->rate;
1900 	}
1901 	if (pctsum > 100)
1902 		errx(1, "%s: sum of extra space allocation > 100%%", filename);
1903 	alloc_table[0].sz = idx;
1904 	free(line);
1905 	fclose(cfile);
1906 }
1907 
1908 char *
get_token(char ** s)1909 get_token(char **s)
1910 {
1911 	char	*p, *r;
1912 	size_t	 tlen = 0;
1913 
1914 	p = *s;
1915 	while (**s != '\0' && !isspace((u_char)**s)) {
1916 		(*s)++;
1917 		tlen++;
1918 	}
1919 	if (tlen == 0)
1920 		return NULL;
1921 
1922 	/* eat whitespace */
1923 	while (isspace((u_char)**s))
1924 		(*s)++;
1925 
1926 	if ((r = strndup(p, tlen)) == NULL)
1927 		err(1, NULL);
1928 	return r;
1929 }
1930 
1931 int
apply_unit(double val,u_char unit,u_int64_t * n)1932 apply_unit(double val, u_char unit, u_int64_t *n)
1933 {
1934 	u_int64_t factor = 1;
1935 
1936 	switch (tolower(unit)) {
1937 	case 't':
1938 		factor *= 1024;
1939 		/* FALLTHROUGH */
1940 	case 'g':
1941 		factor *= 1024;
1942 		/* FALLTHROUGH */
1943 	case 'm':
1944 		factor *= 1024;
1945 		/* FALLTHROUGH */
1946 	case 'k':
1947 		factor *= 1024;
1948 		break;
1949 	default:
1950 		return -1;
1951 	}
1952 
1953 	val *= factor / DEV_BSIZE;
1954 	if (val > (double)ULLONG_MAX)
1955 		return -1;
1956 	*n = val;
1957 	return 0;
1958 }
1959 
1960 int
parse_sizespec(const char * buf,double * val,char ** unit)1961 parse_sizespec(const char *buf, double *val, char **unit)
1962 {
1963 	errno = 0;
1964 	*val = strtod(buf, unit);
1965 	if (errno == ERANGE || *val < 0 || *val > (double)ULLONG_MAX)
1966 		return -1;	/* too big/small */
1967 	if (*val == 0 && *unit == buf)
1968 		return -1;	/* No conversion performed. */
1969 	if (*unit != NULL && *unit[0] == '\0')
1970 		*unit = NULL;
1971 	return 0;
1972 }
1973 
1974 int
parse_sizerange(char * buf,u_int64_t * min,u_int64_t * max)1975 parse_sizerange(char *buf, u_int64_t *min, u_int64_t *max)
1976 {
1977 	char	*p, *unit1 = NULL, *unit2 = NULL;
1978 	double	 val1 = 0, val2 = 0;
1979 
1980 	if (strcmp(buf, "*") == 0) {
1981 		*min = 0;
1982 		*max = UINT64_MAX;
1983 		goto done;
1984 	}
1985 
1986 	if ((p = strchr(buf, '-')) != NULL) {
1987 		p[0] = '\0';
1988 		p++;
1989 	}
1990 	*max = 0;
1991 	if (parse_sizespec(buf, &val1, &unit1) == -1)
1992 		return -1;
1993 	if (p != NULL && p[0] != '\0') {
1994 		if (p[0] == '*')
1995 			*max = UINT64_MAX;
1996 		else
1997 			if (parse_sizespec(p, &val2, &unit2) == -1)
1998 				return -1;
1999 	}
2000 	if (unit1 == NULL && (unit1 = unit2) == NULL)
2001 		return -1;
2002 	if (apply_unit(val1, unit1[0], min) == -1)
2003 		return -1;
2004 	if (val2 > 0) {
2005 		if (apply_unit(val2, unit2[0], max) == -1)
2006 			return -1;
2007 	} else
2008 		if (*max == 0)
2009 			*max = *min;
2010  done:
2011 	free(buf);
2012 	return 0;
2013 }
2014 
2015 int
parse_pct(char * buf,int * n)2016 parse_pct(char *buf, int *n)
2017 {
2018 	const char	*errstr;
2019 
2020 	if (buf[strlen(buf) - 1] == '%')
2021 		buf[strlen(buf) - 1] = '\0';
2022 	*n = strtonum(buf, 0, 100, &errstr);
2023 	if (errstr) {
2024 		warnx("parse percent %s: %s", buf, errstr);
2025 		return -1;
2026 	}
2027 	free(buf);
2028 	return 0;
2029 }
2030 
2031 int
alignpartition(struct disklabel * lp,int partno,u_int64_t startalign,u_int64_t stopalign,int flags)2032 alignpartition(struct disklabel *lp, int partno, u_int64_t startalign,
2033     u_int64_t stopalign, int flags)
2034 {
2035 	struct partition *pp = &lp->d_partitions[partno];
2036 	const struct diskchunk *chunk;
2037 	u_int64_t start, stop, maxstop;
2038 
2039 	start = DL_GETPOFFSET(pp);
2040 	if ((flags & ROUND_OFFSET_UP) == ROUND_OFFSET_UP)
2041 		start = ROUNDUP(start, startalign);
2042 	else if ((flags & ROUND_OFFSET_DOWN) == ROUND_OFFSET_DOWN)
2043 		start = ROUNDDOWN(start, startalign);
2044 
2045 	/* Find the chunk that contains 'start'. */
2046 	chunk = free_chunks(lp, partno);
2047 	for (; chunk->start != 0 || chunk->stop != 0; chunk++) {
2048 		if (start >= chunk->start && start < chunk->stop)
2049 			break;
2050 	}
2051 	if (chunk->stop == 0) {
2052 		fprintf(stderr, "'%c' aligned offset %llu lies outside "
2053 		    "the OpenBSD bounds or inside another partition\n",
2054 		    'a' + partno, start);
2055 		return 1;
2056 	}
2057 
2058 	/* Calculate the new 'stop' sector, the sector after the partition. */
2059 	if ((flags & ROUND_SIZE_OVERLAP) == 0)
2060 		maxstop = ROUNDDOWN(chunk->stop, stopalign);
2061 	else
2062 		maxstop = ROUNDDOWN(ending_sector, stopalign);
2063 
2064 	stop = DL_GETPOFFSET(pp) + DL_GETPSIZE(pp);
2065 	if ((flags & ROUND_SIZE_UP) == ROUND_SIZE_UP)
2066 		stop = ROUNDUP(stop, stopalign);
2067 	else if ((flags & ROUND_SIZE_DOWN) == ROUND_SIZE_DOWN)
2068 		stop = ROUNDDOWN(stop, stopalign);
2069 	if (stop > maxstop)
2070 		stop = maxstop;
2071 
2072 	if (stop <= start) {
2073 		fprintf(stderr, "not enough space\n");
2074 		return 1;
2075 	}
2076 
2077 	if (start != DL_GETPOFFSET(pp))
2078 		DL_SETPOFFSET(pp, start);
2079 	if (stop != DL_GETPOFFSET(pp) + DL_GETPSIZE(pp))
2080 		DL_SETPSIZE(pp, stop - start);
2081 
2082 	return 0;
2083 }
2084