GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/disklabel/editor.c Lines: 0 1094 0.0 %
Date: 2016-12-06 Branches: 0 924 0.0 %

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