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