GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/disklabel/editor.c Lines: 5 1192 0.4 %
Date: 2017-11-07 Branches: 1 956 0.1 %

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