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