main.c revision 1.3 1 /* $NetBSD: main.c,v 1.3 2005/06/15 20:49:41 dsl Exp $ */
2
3 /*
4 * Copyright (c) 1987, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Symmetric Computer Systems.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\
42 The Regents of the University of California. All rights reserved.\n");
43 #endif /* not lint */
44
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)disklabel.c 8.4 (Berkeley) 5/4/95";
48 /* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */
49 #else
50 __RCSID("$NetBSD: main.c,v 1.3 2005/06/15 20:49:41 dsl Exp $");
51 #endif
52 #endif /* not lint */
53
54 #include <sys/param.h>
55 #include <sys/file.h>
56 #include <sys/stat.h>
57 #include <sys/wait.h>
58 #define DKTYPENAMES
59 #define FSTYPENAMES
60
61 #include <ctype.h>
62 #include <err.h>
63 #include <errno.h>
64 #include <unistd.h>
65 #include <signal.h>
66 #include <string.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <unistd.h>
70
71 #include <ufs/ufs/dinode.h>
72 #include <ufs/ffs/fs.h>
73
74 #if HAVE_NBTOOL_CONFIG_H
75 #include <nbinclude/sys/disklabel.h>
76 #include <nbinclude/sys/bootblock.h>
77 #include "../../include/util.h"
78 #include "../../include/disktab.h"
79 #else
80 #include <sys/ioctl.h>
81 #include <sys/disklabel.h>
82 #include <sys/bootblock.h>
83 #include <util.h>
84 #include <disktab.h>
85 #endif /* HAVE_NBTOOL_CONFIG_H */
86
87 #include "pathnames.h"
88 #include "extern.h"
89 #include "dkcksum.h"
90
91 /*
92 * Disklabel: read and write disklabels.
93 * The label is usually placed on one of the first sectors of the disk.
94 * Many machines also place a bootstrap in the same area,
95 * in which case the label is embedded in the bootstrap.
96 * The bootstrap source must leave space at the proper offset
97 * for the label on such machines.
98 */
99
100 #ifndef BBSIZE
101 #define BBSIZE 8192 /* size of boot area, with label */
102 #endif
103
104 #ifndef NUMBOOT
105 #define NUMBOOT 0
106 #endif
107
108 #define DEFEDITOR _PATH_VI
109
110 static char *dkname;
111 static char tmpfil[MAXPATHLEN];
112
113 static char namebuf[BBSIZE], *np = namebuf;
114 static struct disklabel lab;
115
116 char bootarea[BBSIZE];
117 char *specname;
118
119
120 #if NUMBOOT > 0
121 static int installboot; /* non-zero if we should install a boot program */
122 static char *bootbuf; /* pointer to buffer with remainder of boot prog */
123 static int bootsize; /* size of remaining boot program */
124 static char *xxboot; /* primary boot */
125 static char boot0[MAXPATHLEN];
126 #endif /* NUMBOOT > 0 */
127
128 static enum {
129 UNSPEC, EDIT, READ, RESTORE, SETWRITABLE, WRITE, WRITEBOOT, INTERACT
130 } op = UNSPEC;
131
132 static int Fflag;
133 static int rflag;
134 static int tflag;
135 int Cflag;
136 static int Iflag;
137
138 #define COMMON_OPTIONS "BCFINRWb:ef:irs:tw"
139
140 #ifdef DEBUG
141 static int debug;
142 #define OPTIONS COMMON_OPTIONS "d"
143 #else /* ! DEBUG */
144 #define OPTIONS COMMON_OPTIONS
145 #endif /* ! DEBUG */
146
147 #ifdef USE_MBR
148 static struct mbr_partition *dosdp; /* i386 DOS partition, if found */
149 static struct mbr_partition *readmbr(int);
150 #endif /* USE_MBR */
151
152 #ifdef USE_ACORN
153 static u_int filecore_partition_offset;
154 static u_int get_filecore_partition(int);
155 static int filecore_checksum(u_char *);
156 #endif /* USE_ACORN */
157
158 #if defined(USE_MBR) || (defined(USE_ACORN) && defined(notyet))
159 static void confirm(const char *);
160 #endif
161
162 int main(int, char *[]);
163
164 static void makedisktab(FILE *, struct disklabel *);
165 static void makelabel(const char *, const char *,
166 struct disklabel *);
167 static void l_perror(const char *);
168 static struct disklabel *readlabel(int);
169 static struct disklabel *makebootarea(char *, struct disklabel *, int);
170 static int edit(struct disklabel *, int);
171 static int editit(void);
172 static char *skip(char *);
173 static char *word(char *);
174 static int getasciilabel(FILE *, struct disklabel *);
175 #if NUMBOOT > 0
176 static void setbootflag(struct disklabel *);
177 #endif
178 static void usage(void);
179 static int getulong(const char *, char, char **,
180 unsigned long *, unsigned long);
181 #define GETNUM32(a, v) getulong(a, '\0', NULL, v, UINT32_MAX)
182 #define GETNUM16(a, v) getulong(a, '\0', NULL, v, UINT16_MAX)
183 #define GETNUM8(a, v) getulong(a, '\0', NULL, v, UINT8_MAX)
184
185 #if HAVE_NBTOOL_CONFIG_H
186 #define GETLABELOFFSET() LABELOFFSET
187 #define GETLABELSECTOR() LABELSECTOR
188 #else /* HAVE_NBTOOL_CONFIG_H */
189 #define GETLABELOFFSET() getlabeloffset()
190 #define GETLABELSECTOR() getlabelsector()
191 #endif
192
193 int
194 main(int argc, char *argv[])
195 {
196 struct disklabel *lp;
197 FILE *t;
198 int ch, f, writable, error;
199
200 error = 0;
201 while ((ch = getopt(argc, argv, OPTIONS)) != -1)
202 switch (ch) {
203 #if NUMBOOT > 0
204 case 'B':
205 ++installboot;
206 break;
207 case 'b':
208 xxboot = optarg;
209 break;
210 #endif /* NUMBOOT > 0 */
211 case 'C':
212 ++Cflag;
213 break;
214 case 'F':
215 ++Fflag;
216 break;
217 case 'I':
218 ++Iflag;
219 break;
220 case 'R':
221 if (op != UNSPEC)
222 usage();
223 op = RESTORE;
224 break;
225 #if !HAVE_NBTOOL_CONFIG_H
226 case 'N':
227 if (op != UNSPEC)
228 usage();
229 writable = 0;
230 op = SETWRITABLE;
231 break;
232 case 'W':
233 if (op != UNSPEC)
234 usage();
235 writable = 1;
236 op = SETWRITABLE;
237 break;
238 #endif /* !HAVE_NBTOOL_CONFIG_H */
239 case 'e':
240 if (op != UNSPEC)
241 usage();
242 op = EDIT;
243 break;
244 case 'f':
245 if (setdisktab(optarg) == -1)
246 usage();
247 break;
248 case 'i':
249 if (op != UNSPEC)
250 usage();
251 op = INTERACT;
252 break;
253 case 't':
254 ++tflag;
255 break;
256 case 'r':
257 ++rflag;
258 break;
259 case 'w':
260 if (op != UNSPEC)
261 usage();
262 op = WRITE;
263 break;
264 #ifdef DEBUG
265 case 'd':
266 debug++;
267 break;
268 #endif
269 case '?':
270 default:
271 usage();
272 }
273 argc -= optind;
274 argv += optind;
275
276 #if NUMBOOT > 0
277 if (installboot) {
278 rflag++;
279 if (op == UNSPEC)
280 op = WRITEBOOT;
281 } else {
282 if (op == UNSPEC)
283 op = READ;
284 }
285 #else /* NUMBOOT <= 0 */
286 if (op == UNSPEC)
287 op = READ;
288 #endif /* NUMBOOT <= 0 */
289
290 if (argc < 1)
291 usage();
292
293 if (Iflag && op != EDIT && op != INTERACT)
294 usage();
295
296 dkname = argv[0];
297 #if HAVE_NBTOOL_CONFIG_H
298 f = open(dkname, op == READ ? O_RDONLY : O_RDWR, 0);
299 strlcpy(np, dkname, MAXPATHLEN);
300 #else
301 f = opendisk(dkname, op == READ ? O_RDONLY : O_RDWR, np, MAXPATHLEN, 0);
302 #endif /* HAVE_NBTOOL_CONFIG_H */
303 specname = np;
304 np += strlen(specname) + 1;
305 if (f < 0)
306 err(4, "%s", specname);
307
308 #ifdef USE_MBR
309 /*
310 * Check for presence of DOS partition table in
311 * master boot record. Return pointer to NetBSD/i386
312 * partition, if present.
313 */
314 dosdp = readmbr(f);
315 #endif /* USE_MBR */
316
317 #ifdef USE_ACORN
318 /*
319 * Check for the presence of a RiscOS filecore boot block
320 * indicating an ADFS file system on the disc.
321 * Return the offset to the NetBSD part of the disc if
322 * this can be determined.
323 * This routine will terminate disklabel if the disc
324 * is found to be ADFS only.
325 */
326 filecore_partition_offset = get_filecore_partition(f);
327 #endif /* USE_ACORN */
328
329 switch (op) {
330
331 case EDIT:
332 if (argc != 1)
333 usage();
334 lp = readlabel(f);
335 error = edit(lp, f);
336 break;
337
338 case INTERACT:
339 if (argc != 1)
340 usage();
341 lp = readlabel(f);
342 /*
343 * XXX: Fill some default values so checklabel does not fail
344 */
345 if (lp->d_bbsize == 0)
346 lp->d_bbsize = BBSIZE;
347 if (lp->d_sbsize == 0)
348 lp->d_sbsize = SBLOCKSIZE;
349 interact(lp, f);
350 break;
351
352 case READ:
353 if (argc != 1)
354 usage();
355 lp = readlabel(f);
356 if (tflag)
357 makedisktab(stdout, lp);
358 else {
359 showinfo(stdout, lp, specname);
360 showpartitions(stdout, lp, Cflag);
361 }
362 error = checklabel(lp);
363 if (error)
364 error += 100;
365 break;
366
367 case RESTORE:
368 if (argc < 2 || argc > 3)
369 usage();
370 #if NUMBOOT > 0
371 if (installboot && argc == 3)
372 makelabel(argv[2], (char *)0, &lab);
373 #endif
374 lp = makebootarea(bootarea, &lab, f);
375 if (!(t = fopen(argv[1], "r")))
376 err(4, "%s", argv[1]);
377 if (getasciilabel(t, lp))
378 error = writelabel(f, bootarea, lp);
379 else
380 error = 1;
381 break;
382
383 #if !HAVE_NBTOOL_CONFIG_H
384 case SETWRITABLE:
385 if (ioctl(f, DIOCWLABEL, (char *)&writable) < 0)
386 err(4, "ioctl DIOCWLABEL");
387 break;
388 #endif /* !HAVE_NBTOOL_CONFIG_H */
389
390 case WRITE:
391 if (argc < 2 || argc > 3)
392 usage();
393 makelabel(argv[1], argc == 3 ? argv[2] : (char *)0, &lab);
394 lp = makebootarea(bootarea, &lab, f);
395 *lp = lab;
396 if (checklabel(lp) == 0)
397 error = writelabel(f, bootarea, lp);
398 else
399 error = 1;
400 break;
401
402 case WRITEBOOT:
403 #if NUMBOOT > 0
404 {
405 struct disklabel tlab;
406
407 lp = readlabel(f);
408 tlab = *lp;
409 if (argc == 2)
410 makelabel(argv[1], (char *)0, &lab);
411 lp = makebootarea(bootarea, &lab, f);
412 *lp = tlab;
413 if (checklabel(lp) == 0)
414 error = writelabel(f, bootarea, lp);
415 else
416 error = 1;
417 break;
418 }
419 #endif /* NUMBOOT > 0 */
420
421 case UNSPEC:
422 usage();
423
424 }
425 exit(error);
426 }
427
428 /*
429 * Construct a prototype disklabel from /etc/disktab. As a side
430 * effect, set the names of the primary and secondary boot files
431 * if specified.
432 */
433 static void
434 makelabel(const char *type, const char *name, struct disklabel *lp)
435 {
436 struct disklabel *dp;
437
438 dp = getdiskbyname(type);
439 if (dp == NULL)
440 errx(1, "unknown disk type: %s", type);
441 *lp = *dp;
442
443 #if NUMBOOT > 0
444 /*
445 * Set bootstrap name(s).
446 * 1. If set from command line, use those,
447 * 2. otherwise, check if disktab specifies them (b0 or b1),
448 * 3. otherwise, makebootarea() will choose ones based on the name
449 * of the disk special file. E.g. /dev/ra0 -> raboot, bootra
450 */
451 if (!xxboot && lp->d_boot0) {
452 if (*lp->d_boot0 != '/')
453 (void)snprintf(boot0, sizeof(boot0), "%s/%s",
454 _PATH_BOOTDIR, lp->d_boot0);
455 else
456 (void)strlcpy(boot0, lp->d_boot0, sizeof(boot0));
457 xxboot = boot0;
458 }
459 #endif /* NUMBOOT > 0 */
460
461 /* d_packname is union d_boot[01], so zero */
462 (void) memset(lp->d_packname, 0, sizeof(lp->d_packname));
463 if (name)
464 (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
465 }
466
467 #if defined(USE_MBR) || (defined(USE_ACORN) && defined(notyet))
468 static void
469 confirm(const char *txt)
470 {
471 int first, ch;
472
473 (void) printf("%s? [n]: ", txt);
474 (void) fflush(stdout);
475 first = ch = getchar();
476 while (ch != '\n' && ch != EOF)
477 ch = getchar();
478 if (first != 'y' && first != 'Y')
479 exit(0);
480 }
481 #endif /* USE_MBR || USE_ACORN && notyet */
482
483 int
484 writelabel(int f, char *boot, struct disklabel *lp)
485 {
486 int writable;
487 off_t sectoffset;
488
489 sectoffset = 0;
490 #if NUMBOOT > 0
491 setbootflag(lp);
492 #endif
493 lp->d_magic = DISKMAGIC;
494 lp->d_magic2 = DISKMAGIC;
495 lp->d_checksum = 0;
496 lp->d_checksum = dkcksum(lp);
497
498 if (Fflag || rflag || Iflag)
499 {
500 #ifdef USE_MBR
501 struct partition *pp = &lp->d_partitions[2];
502
503 /*
504 * If NetBSD/i386 DOS partition is missing, or if
505 * the label to be written is not within partition,
506 * prompt first. Need to allow this in case operator
507 * wants to convert the drive for dedicated use.
508 */
509 if (dosdp) {
510 if (dosdp->mbrp_start != pp->p_offset) {
511 printf("NetBSD slice at %u, "
512 "partition C at %u\n", dosdp->mbrp_start,
513 pp->p_offset);
514 confirm("Write outside MBR partition");
515 }
516 sectoffset = (off_t)pp->p_offset * lp->d_secsize;
517 } else {
518 sectoffset = 0;
519 }
520 #endif /* USE_MBR */
521
522 #ifdef USE_ACORN
523 /* XXX */
524 sectoffset = (off_t)filecore_partition_offset * DEV_BSIZE;
525 #endif /* USE_ACORN */
526
527 /*
528 * First set the kernel disk label,
529 * then write a label to the raw disk.
530 * If the SDINFO ioctl fails because it is unimplemented,
531 * keep going; otherwise, the kernel consistency checks
532 * may prevent us from changing the current (in-core)
533 * label.
534 */
535 #if !HAVE_NBTOOL_CONFIG_H
536 if (!Fflag && ioctl(f, DIOCSDINFO, lp) < 0 &&
537 errno != ENODEV && errno != ENOTTY) {
538 l_perror("ioctl DIOCSDINFO");
539 return (1);
540 }
541 #endif /* HAVE_NBTOOL_CONFIG_H */
542 if (lseek(f, sectoffset, SEEK_SET) < 0) {
543 perror("lseek");
544 return (1);
545 }
546 /*
547 * write enable label sector before write (if necessary),
548 * disable after writing.
549 */
550 writable = 1;
551 #if !HAVE_NBTOOL_CONFIG_H
552 if (!Fflag && ioctl(f, DIOCWLABEL, &writable) < 0)
553 perror("ioctl DIOCWLABEL");
554 #endif /* HAVE_NBTOOL_CONFIG_H */
555
556 #ifdef __alpha__
557 /*
558 * The Alpha requires that the boot block be checksummed.
559 * The NetBSD/alpha disklabel.h provides a macro to do it.
560 */
561 {
562 struct alpha_boot_block *bb;
563
564 bb = (struct alpha_boot_block *)boot;
565 ALPHA_BOOT_BLOCK_CKSUM(bb, &bb->bb_cksum);
566 }
567 #endif /* __alpha__ */
568
569 if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
570 perror("write");
571 return (1);
572 }
573
574 #if NUMBOOT > 0
575 /*
576 * Output the remainder of the disklabel
577 */
578 if (bootbuf && write(f, bootbuf, bootsize) != bootsize) {
579 perror("write");
580 return (1);
581 }
582 #endif /* NUMBOOT > 0 */
583
584 writable = 0;
585 #if !HAVE_NBTOOL_CONFIG_H
586 if (!Fflag && ioctl(f, DIOCWLABEL, &writable) < 0)
587 perror("ioctl DIOCWLABEL");
588 /*
589 * Now issue a DIOCWDINFO. This will let the kernel convert the
590 * disklabel to some machdep format if needed.
591 */
592 if (!Fflag && ioctl(f, DIOCWDINFO, lp) < 0) {
593 l_perror("ioctl DIOCWDINFO");
594 return (1);
595 }
596 #endif /* !HAVE_NBTOOL_CONFIG_H */
597 } else {
598 #if !HAVE_NBTOOL_CONFIG_H
599 if (ioctl(f, DIOCWDINFO, lp) < 0) {
600 l_perror("ioctl DIOCWDINFO");
601 return (1);
602 }
603 #else
604 errx(1, "use -F, -r, or -I");
605 #endif /* HAVE_NBTOOL_CONFIG_H */
606 }
607
608 #ifdef __vax__
609 if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
610 daddr_t alt;
611 int i;
612
613 alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
614 for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
615 (void)lseek(f, (off_t)(alt + i) * lp->d_secsize,
616 SEEK_SET);
617 if (write(f, boot, lp->d_secsize) < lp->d_secsize)
618 warn("alternate label %d write", i/2);
619 }
620 }
621 #endif /* __vax__ */
622
623 return (0);
624 }
625
626 static void
627 l_perror(const char *s)
628 {
629
630 switch (errno) {
631
632 case ESRCH:
633 warnx("%s: No disk label on disk;\n"
634 "use \"disklabel -I\" to install initial label", s);
635 break;
636
637 case EINVAL:
638 warnx("%s: Label magic number or checksum is wrong!\n"
639 "(disklabel or kernel is out of date?)", s);
640 break;
641
642 case EBUSY:
643 warnx("%s: Open partition would move or shrink", s);
644 break;
645
646 case EXDEV:
647 warnx("%s: Labeled partition or 'a' partition must start"
648 " at beginning of disk", s);
649 break;
650
651 default:
652 warn("%s", s);
653 break;
654 }
655 }
656
657 #ifdef USE_MBR
658 /*
659 * Fetch DOS partition table from disk.
660 */
661 static struct mbr_partition *
662 readmbr(int f)
663 {
664 struct mbr_partition *dp;
665 struct mbr_sector mbr;
666 int part;
667 u_int ext_base, next_ext, this_ext;
668 static struct mbr_partition netbsd_part;
669
670 /*
671 * Don't (yet) know disk geometry (BIOS), use
672 * partition table to find NetBSD/i386 partition, and obtain
673 * disklabel from there.
674 */
675
676 ext_base = 0;
677 next_ext = 0;
678 for (;;) {
679 this_ext = next_ext;
680 next_ext = 0;
681 if (pread(f, &mbr, sizeof mbr, this_ext * (off_t)DEV_BSIZE)
682 != sizeof(mbr)) {
683 warn("Can't read master boot record %d", this_ext);
684 return 0;
685 }
686
687 /* Check if table is valid. */
688 if (mbr.mbr_magic != htole16(MBR_MAGIC)) {
689 warnx("Invalid signature in mbr record %d", this_ext);
690 return 0;
691 }
692
693 dp = &mbr.mbr_parts[0];
694 #if defined(_no_longer_needed) && !defined(__i386__) && !defined(__x86_64__)
695 /* avoid alignment error */
696 memcpy(mbr, dp, MBR_PART_COUNT * sizeof(*dp));
697 dp = (struct mbr_partition *)mbr;
698 #endif /* ! __i386__ */
699
700 /* Find NetBSD partition. */
701 for (part = 0; part < MBR_PART_COUNT; dp++, part++) {
702 dp->mbrp_start = le32toh(dp->mbrp_start);
703 dp->mbrp_size = le32toh(dp->mbrp_size);
704 switch (dp->mbrp_type) {
705 case MBR_PTYPE_NETBSD:
706 netbsd_part = *dp;
707 break;
708 case MBR_PTYPE_EXT:
709 case MBR_PTYPE_EXT_LBA:
710 case MBR_PTYPE_EXT_LNX:
711 next_ext = dp->mbrp_start;
712 continue;
713 #ifdef COMPAT_386BSD_MBRPART
714 case MBR_PTYPE_386BSD:
715 if (ext_base == 0)
716 netbsd_part = *dp;
717 continue;
718 #endif /* COMPAT_386BSD_MBRPART */
719 default:
720 continue;
721 }
722 break;
723 }
724 if (part < MBR_PART_COUNT)
725 /* We found a netbsd partition */
726 break;
727 if (next_ext == 0)
728 /* No more extended partitions */
729 break;
730 next_ext += ext_base;
731 if (ext_base == 0)
732 ext_base = next_ext;
733
734 if (next_ext <= this_ext) {
735 warnx("Invalid extended chain %x <= %x",
736 next_ext, this_ext);
737 break;
738 }
739 }
740
741 if (netbsd_part.mbrp_type == 0)
742 return 0;
743
744 netbsd_part.mbrp_start += this_ext;
745 return &netbsd_part;
746 }
747 #endif /* USE_MBR */
748
749 #ifdef USE_ACORN
750 /*
751 * static int filecore_checksum(u_char *bootblock)
752 *
753 * Calculates the filecore boot block checksum. This is used to validate
754 * a filecore boot block on the disk. If a boot block is validated then
755 * it is used to locate the partition table. If the boot block is not
756 * validated, it is assumed that the whole disk is NetBSD.
757 *
758 * The basic algorithm is:
759 *
760 * for (each byte in block, excluding checksum) {
761 * sum += byte;
762 * if (sum > 255)
763 * sum -= 255;
764 * }
765 *
766 * That's equivalent to summing all of the bytes in the block
767 * (excluding the checksum byte, of course), then calculating the
768 * checksum as "cksum = sum - ((sum - 1) / 255) * 255)". That
769 * expression may or may not yield a faster checksum function,
770 * but it's easier to reason about.
771 *
772 * Note that if you have a block filled with bytes of a single
773 * value "X" (regardless of that value!) and calculate the cksum
774 * of the block (excluding the checksum byte), you will _always_
775 * end up with a checksum of X. (Do the math; that can be derived
776 * from the checksum calculation function!) That means that
777 * blocks which contain bytes which all have the same value will
778 * always checksum properly. That's a _very_ unlikely occurence
779 * (probably impossible, actually) for a valid filecore boot block,
780 * so we treat such blocks as invalid.
781 */
782 static int
783 filecore_checksum(u_char *bootblock)
784 {
785 u_char byte0, accum_diff;
786 u_int sum;
787 int i;
788
789 sum = 0;
790 accum_diff = 0;
791 byte0 = bootblock[0];
792
793 /*
794 * Sum the contents of the block, keeping track of whether
795 * or not all bytes are the same. If 'accum_diff' ends up
796 * being zero, all of the bytes are, in fact, the same.
797 */
798 for (i = 0; i < 511; ++i) {
799 sum += bootblock[i];
800 accum_diff |= bootblock[i] ^ byte0;
801 }
802
803 /*
804 * Check to see if the checksum byte is the same as the
805 * rest of the bytes, too. (Note that if all of the bytes
806 * are the same except the checksum, a checksum compare
807 * won't succeed, but that's not our problem.)
808 */
809 accum_diff |= bootblock[i] ^ byte0;
810
811 /* All bytes in block are the same; call it invalid. */
812 if (accum_diff == 0)
813 return (-1);
814
815 return (sum - ((sum - 1) / 255) * 255);
816 }
817
818 /*
819 * Fetch filecore bootblock from disk and analyse it
820 */
821 static u_int
822 get_filecore_partition(int f)
823 {
824 struct filecore_bootblock *fcbb;
825 static char bb[DEV_BSIZE];
826 u_int offset;
827
828 if (lseek(f, (off_t)FILECORE_BOOT_SECTOR * DEV_BSIZE, SEEK_SET) < 0 ||
829 read(f, bb, sizeof(bb)) != sizeof(bb))
830 err(4, "can't read filecore boot block");
831 fcbb = (struct filecore_bootblock *)bb;
832
833 /* Check if table is valid. */
834 if (filecore_checksum(bb) != fcbb->checksum)
835 return (0);
836
837 /*
838 * Check for NetBSD/arm32 (RiscBSD) partition marker.
839 * If found the NetBSD disklabel location is easy.
840 */
841 offset = (fcbb->partition_cyl_low + (fcbb->partition_cyl_high << 8))
842 * fcbb->heads * fcbb->secspertrack;
843 if (fcbb->partition_type == PARTITION_FORMAT_RISCBSD)
844 return (offset);
845 else if (fcbb->partition_type == PARTITION_FORMAT_RISCIX) {
846 struct riscix_partition_table *riscix_part;
847 int loop;
848
849 /*
850 * Read the RISCiX partition table and search for the
851 * first partition named "RiscBSD", "NetBSD", or "Empty:"
852 *
853 * XXX is use of 'Empty:' really desirable?! -- cgd
854 */
855
856 if (lseek(f, (off_t)offset * DEV_BSIZE, SEEK_SET) < 0 ||
857 read(f, bb, sizeof(bb)) != sizeof(bb))
858 err(4, "can't read riscix partition table");
859 riscix_part = (struct riscix_partition_table *)bb;
860
861 for (loop = 0; loop < NRISCIX_PARTITIONS; ++loop) {
862 if (strcmp(riscix_part->partitions[loop].rp_name,
863 "RiscBSD") == 0 ||
864 strcmp(riscix_part->partitions[loop].rp_name,
865 "NetBSD") == 0 ||
866 strcmp(riscix_part->partitions[loop].rp_name,
867 "Empty:") == 0) {
868 offset = riscix_part->partitions[loop].rp_start;
869 break;
870 }
871 }
872 if (loop == NRISCIX_PARTITIONS) {
873 /*
874 * Valid filecore boot block, RISCiX partition table
875 * but no NetBSD partition. We should leave this
876 * disc alone.
877 */
878 errx(4, "cannot label: no NetBSD partition found"
879 " in RISCiX partition table");
880 }
881 return (offset);
882 } else {
883 /*
884 * Valid filecore boot block and no non-ADFS partition.
885 * This means that the whole disc is allocated for ADFS
886 * so do not trash ! If the user really wants to put a
887 * NetBSD disklabel on the disc then they should remove
888 * the filecore boot block first with dd.
889 */
890 errx(4, "cannot label: filecore-only disk"
891 " (no non-ADFS partition)");
892 }
893 return (0);
894 }
895 #endif /* USE_ACORN */
896
897 /*
898 * Fetch disklabel for disk.
899 * Use ioctl to get label unless -r flag is given.
900 */
901 static struct disklabel *
902 readlabel(int f)
903 {
904 struct disklabel *lp;
905
906 if (Fflag || rflag || Iflag) {
907 const char *msg;
908 off_t sectoffset;
909
910 msg = NULL;
911 sectoffset = 0;
912
913 #ifdef USE_MBR
914 if (dosdp)
915 sectoffset = (off_t)dosdp->mbrp_start * DEV_BSIZE;
916 #endif /* USE_MBR */
917
918 #ifdef USE_ACORN
919 /* XXX */
920 sectoffset = (off_t)filecore_partition_offset * DEV_BSIZE;
921 #endif /* USE_ACORN */
922
923 if (lseek(f, sectoffset, SEEK_SET) < 0 ||
924 read(f, bootarea, BBSIZE) != BBSIZE)
925 err(4, "%s", specname);
926
927 msg = "no disklabel";
928 for (lp = (struct disklabel *)bootarea;
929 lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
930 lp = (struct disklabel *)((char *)lp + sizeof(long))) {
931 if (lp->d_magic == DISKMAGIC &&
932 lp->d_magic2 == DISKMAGIC) {
933 if (lp->d_npartitions <= MAXPARTITIONS &&
934 dkcksum(lp) == 0)
935 return (lp);
936 msg = "disk label corrupted";
937 }
938 }
939 if (msg != NULL && !Iflag)
940 errx(1, "%s", msg);
941 #if HAVE_NBTOOL_CONFIG_H
942 goto err;
943 #else
944 /*
945 * There was no label on the disk. Get the fictious one
946 * as a basis for initialisation.
947 */
948 lp = makebootarea(bootarea, &lab, f);
949 if (ioctl(f, DIOCGDINFO, lp) < 0 &&
950 ioctl(f, DIOCGDEFLABEL, lp) < 0)
951 goto err;
952 #endif /* HAVE_NBTOOL_CONFIG_H */
953 } else {
954 #ifdef HAVE_NBTOOL_CONFIG_H
955 goto err;
956 #else
957 lp = &lab;
958 if (ioctl(f, DIOCGDINFO, lp) < 0)
959 err(4, "ioctl DIOCGDINFO");
960 #endif /* HAVE_NBTOOL_CONFIG_H */
961 }
962 return (lp);
963 err:
964 errx(1, "could not get initial label");
965 }
966
967 /*
968 * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
969 * Returns a pointer to the disklabel portion of the bootarea.
970 */
971 static struct disklabel *
972 makebootarea(char *boot, struct disklabel *dp, int f)
973 {
974 struct disklabel *lp;
975 char *p;
976 daddr_t lsec;
977 off_t loff;
978 #if NUMBOOT > 0
979 int b;
980 char *dkbasename;
981 struct stat sb;
982 #endif /* NUMBOOT > 0 */
983
984 if ((lsec = GETLABELSECTOR()) < 0)
985 err(4, "getlabelsector()");
986 if ((loff = GETLABELOFFSET()) < 0)
987 err(4, "getlabeloffset()");
988
989 /* XXX */
990 if (dp->d_secsize == 0) {
991 dp->d_secsize = DEV_BSIZE;
992 dp->d_bbsize = BBSIZE;
993 }
994 lp = (struct disklabel *) (boot + (lsec * dp->d_secsize) + loff);
995 (void) memset(lp, 0, sizeof(*lp));
996
997 #ifdef SAVEBOOTAREA
998 /*
999 * We must read the current bootarea so we don't clobber the
1000 * existing boot block, if any.
1001 */
1002 if (rflag || Iflag) {
1003 off_t sectoffset;
1004
1005 sectoffset = 0;
1006 if (lseek(f, sectoffset, SEEK_SET) < 0 ||
1007 read(f, boot, BBSIZE) != BBSIZE)
1008 err(4, "%s", specname);
1009 (void) memset(lp, 0, sizeof(*lp));
1010 }
1011 #endif /* SAVEBOOTAREA */
1012
1013 #if NUMBOOT > 0
1014 /*
1015 * If we are not installing a boot program but we are installing a
1016 * label on disk then we must read the current bootarea so we don't
1017 * clobber the existing boot.
1018 */
1019 if (!installboot) {
1020 if (rflag || Iflag) {
1021 off_t sectoffset;
1022
1023 sectoffset = 0;
1024 #ifdef USE_MBR
1025 if (dosdp)
1026 sectoffset = (off_t)dosdp->mbrp_start * DEV_BSIZE;
1027 #endif /* USE_MBR */
1028
1029 #ifdef USE_ACORN
1030 /* XXX */
1031 sectoffset = (off_t)filecore_partition_offset
1032 * DEV_BSIZE;
1033 #endif /* USE_ACORN */
1034
1035 if (lseek(f, sectoffset, SEEK_SET) < 0 ||
1036 read(f, boot, BBSIZE) != BBSIZE)
1037 err(4, "%s", specname);
1038 (void) memset(lp, 0, sizeof(*lp));
1039 }
1040 return (lp);
1041 }
1042 /*
1043 * We are installing a boot program. Determine the name(s) and
1044 * read them into the appropriate places in the boot area.
1045 */
1046 if (!xxboot) {
1047 dkbasename = np;
1048 if ((p = strrchr(dkname, '/')) == NULL)
1049 p = dkname;
1050 else
1051 p++;
1052 while (*p && !isdigit(*p & 0xff))
1053 *np++ = *p++;
1054 *np++ = '\0';
1055
1056 if (!xxboot) {
1057 (void)sprintf(np, "%s/%sboot",
1058 _PATH_BOOTDIR, dkbasename);
1059 if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
1060 dkbasename++;
1061 xxboot = np;
1062 (void)sprintf(xxboot, "%s/%sboot",
1063 _PATH_BOOTDIR, dkbasename);
1064 np += strlen(xxboot) + 1;
1065 }
1066 }
1067
1068 #ifdef DEBUG
1069 if (debug)
1070 warnx("bootstrap: xxboot = %s", xxboot);
1071 #endif
1072
1073 /*
1074 * Strange rules:
1075 * 1. One-piece bootstrap (hp300/hp800)
1076 * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest
1077 * is remembered and written later following the bootarea.
1078 */
1079 b = open(xxboot, O_RDONLY);
1080 if (b < 0)
1081 err(4, "%s", xxboot);
1082 if (read(b, boot, (int)dp->d_bbsize) < 0)
1083 err(4, "%s", xxboot);
1084 (void)fstat(b, &sb);
1085 bootsize = (int)sb.st_size - dp->d_bbsize;
1086 if (bootsize > 0) {
1087 /* XXX assume d_secsize is a power of two */
1088 bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
1089 bootbuf = (char *)malloc((size_t)bootsize);
1090 if (bootbuf == 0)
1091 err(4, "%s", xxboot);
1092 if (read(b, bootbuf, bootsize) < 0) {
1093 free(bootbuf);
1094 err(4, "%s", xxboot);
1095 }
1096 }
1097 (void)close(b);
1098 #endif /* NUMBOOT > 0 */
1099
1100 /*
1101 * Make sure no part of the bootstrap is written in the area
1102 * reserved for the label.
1103 */
1104 for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
1105 if (*p)
1106 errx(2, "Bootstrap doesn't leave room for disk label");
1107 return (lp);
1108 }
1109
1110 static void
1111 makedisktab(FILE *f, struct disklabel *lp)
1112 {
1113 int i;
1114 const char *did;
1115 struct partition *pp;
1116
1117 did = "\\\n\t:";
1118 (void) fprintf(f, "%.*s|Automatically generated label:\\\n\t:dt=",
1119 (int) sizeof(lp->d_typename), lp->d_typename);
1120 if ((unsigned) lp->d_type < DKMAXTYPES)
1121 (void) fprintf(f, "%s:", dktypenames[lp->d_type]);
1122 else
1123 (void) fprintf(f, "unknown%d:", lp->d_type);
1124
1125 (void) fprintf(f, "se#%d:", lp->d_secsize);
1126 (void) fprintf(f, "ns#%d:", lp->d_nsectors);
1127 (void) fprintf(f, "nt#%d:", lp->d_ntracks);
1128 (void) fprintf(f, "sc#%d:", lp->d_secpercyl);
1129 (void) fprintf(f, "nc#%d:", lp->d_ncylinders);
1130
1131 if ((lp->d_secpercyl * lp->d_ncylinders) != lp->d_secperunit) {
1132 (void) fprintf(f, "%ssu#%d:", did, lp->d_secperunit);
1133 did = "";
1134 }
1135 if (lp->d_rpm != 3600) {
1136 (void) fprintf(f, "%srm#%d:", did, lp->d_rpm);
1137 did = "";
1138 }
1139 if (lp->d_interleave != 1) {
1140 (void) fprintf(f, "%sil#%d:", did, lp->d_interleave);
1141 did = "";
1142 }
1143 if (lp->d_trackskew != 0) {
1144 (void) fprintf(f, "%ssk#%d:", did, lp->d_trackskew);
1145 did = "";
1146 }
1147 if (lp->d_cylskew != 0) {
1148 (void) fprintf(f, "%scs#%d:", did, lp->d_cylskew);
1149 did = "";
1150 }
1151 if (lp->d_headswitch != 0) {
1152 (void) fprintf(f, "%shs#%d:", did, lp->d_headswitch);
1153 did = "";
1154 }
1155 if (lp->d_trkseek != 0) {
1156 (void) fprintf(f, "%sts#%d:", did, lp->d_trkseek);
1157 did = "";
1158 }
1159 #ifdef notyet
1160 (void) fprintf(f, "drivedata: ");
1161 for (i = NDDATA - 1; i >= 0; i--)
1162 if (lp->d_drivedata[i])
1163 break;
1164 if (i < 0)
1165 i = 0;
1166 for (j = 0; j <= i; j++)
1167 (void) fprintf(f, "%d ", lp->d_drivedata[j]);
1168 #endif /* notyet */
1169 pp = lp->d_partitions;
1170 for (i = 0; i < lp->d_npartitions; i++, pp++) {
1171 if (pp->p_size) {
1172 char c = 'a' + i;
1173 (void) fprintf(f, "\\\n\t:");
1174 (void) fprintf(f, "p%c#%d:", c, pp->p_size);
1175 (void) fprintf(f, "o%c#%d:", c, pp->p_offset);
1176 if (pp->p_fstype != FS_UNUSED) {
1177 if ((unsigned) pp->p_fstype < FSMAXTYPES)
1178 (void) fprintf(f, "t%c=%s:", c,
1179 fstypenames[pp->p_fstype]);
1180 else
1181 (void) fprintf(f, "t%c=unknown%d:",
1182 c, pp->p_fstype);
1183 }
1184 switch (pp->p_fstype) {
1185
1186 case FS_UNUSED:
1187 break;
1188
1189 case FS_BSDFFS:
1190 case FS_BSDLFS:
1191 case FS_EX2FS:
1192 case FS_ADOS:
1193 case FS_APPLEUFS:
1194 (void) fprintf(f, "b%c#%d:", c,
1195 pp->p_fsize * pp->p_frag);
1196 (void) fprintf(f, "f%c#%d:", c, pp->p_fsize);
1197 break;
1198 default:
1199 break;
1200 }
1201 }
1202 }
1203 (void) fprintf(f, "\n");
1204 (void) fflush(f);
1205 }
1206
1207 static int
1208 edit(struct disklabel *lp, int f)
1209 {
1210 struct disklabel label;
1211 const char *tmpdir;
1212 int first, ch, fd;
1213 FILE *fp;
1214
1215 if ((tmpdir = getenv("TMPDIR")) == NULL)
1216 tmpdir = _PATH_TMP;
1217 (void)snprintf(tmpfil, sizeof(tmpfil), "%s/%s", tmpdir, TMPFILE);
1218 if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
1219 warn("%s", tmpfil);
1220 return (1);
1221 }
1222 (void)fchmod(fd, 0600);
1223 showinfo(fp, lp, specname);
1224 showpartitions(fp, lp, Cflag);
1225 (void) fclose(fp);
1226 for (;;) {
1227 if (!editit())
1228 break;
1229 fp = fopen(tmpfil, "r");
1230 if (fp == NULL) {
1231 warn("%s", tmpfil);
1232 break;
1233 }
1234 (void) memset(&label, 0, sizeof(label));
1235 if (getasciilabel(fp, &label)) {
1236 *lp = label;
1237 if (writelabel(f, bootarea, lp) == 0) {
1238 (void) unlink(tmpfil);
1239 return (0);
1240 }
1241 }
1242 (void) printf("re-edit the label? [y]: ");
1243 (void) fflush(stdout);
1244 first = ch = getchar();
1245 while (ch != '\n' && ch != EOF)
1246 ch = getchar();
1247 if (first == 'n' || first == 'N')
1248 break;
1249 }
1250 (void)unlink(tmpfil);
1251 return (1);
1252 }
1253
1254 static int
1255 editit(void)
1256 {
1257 int pid, xpid;
1258 int status;
1259 sigset_t nsigset, osigset;
1260
1261 sigemptyset(&nsigset);
1262 sigaddset(&nsigset, SIGINT);
1263 sigaddset(&nsigset, SIGQUIT);
1264 sigaddset(&nsigset, SIGHUP);
1265 sigprocmask(SIG_BLOCK, &nsigset, &osigset);
1266 while ((pid = fork()) < 0) {
1267 if (errno != EAGAIN) {
1268 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1269 warn("fork");
1270 return (0);
1271 }
1272 sleep(1);
1273 }
1274 if (pid == 0) {
1275 const char *ed;
1276 char *buf;
1277 int retval;
1278
1279 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1280 setgid(getgid());
1281 setuid(getuid());
1282 if ((ed = getenv("EDITOR")) == (char *)0)
1283 ed = DEFEDITOR;
1284 /*
1285 * Jump through a few extra hoops in case someone's editor
1286 * is "editor arg1 arg2".
1287 */
1288 asprintf(&buf, "%s %s", ed, tmpfil);
1289 if (!buf)
1290 err(1, "malloc");
1291 retval = execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", buf, NULL);
1292 if (retval == -1)
1293 perror(ed);
1294 exit(retval);
1295 }
1296 while ((xpid = wait(&status)) >= 0)
1297 if (xpid == pid)
1298 break;
1299 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1300 return (!status);
1301 }
1302
1303 static char *
1304 skip(char *cp)
1305 {
1306
1307 cp += strspn(cp, " \t");
1308 if (*cp == '\0')
1309 return (NULL);
1310 return (cp);
1311 }
1312
1313 static char *
1314 word(char *cp)
1315 {
1316
1317 if (cp == NULL || *cp == '\0')
1318 return (NULL);
1319
1320 cp += strcspn(cp, " \t");
1321 if (*cp == '\0')
1322 return (NULL);
1323 *cp++ = '\0';
1324 cp += strspn(cp, " \t");
1325 if (*cp == '\0')
1326 return (NULL);
1327 return (cp);
1328 }
1329
1330 #define _CHECKLINE \
1331 if (tp == NULL || *tp == '\0') { \
1332 warnx("line %d: too few fields", lineno); \
1333 errors++; \
1334 break; \
1335 }
1336
1337 #define __CHECKLINE \
1338 if (*tp == NULL || **tp == '\0') { \
1339 warnx("line %d: too few fields", lineno); \
1340 *tp = _error_; \
1341 return 0; \
1342 }
1343
1344 static char _error_[] = "";
1345 #define NXTNUM(n) if ((n = nxtnum(&tp, lineno),0) + tp != _error_) \
1346 ; else goto error
1347 #define NXTXNUM(n) if ((n = nxtxnum(&tp, lp, lineno),0) + tp != _error_) \
1348 ; else goto error
1349
1350 static unsigned long
1351 nxtnum(char **tp, int lineno)
1352 {
1353 char *cp;
1354 unsigned long v;
1355
1356 __CHECKLINE
1357 if (getulong(*tp, '\0', &cp, &v, UINT32_MAX) != 0) {
1358 warnx("line %d: syntax error", lineno);
1359 *tp = _error_;
1360 return 0;
1361 }
1362 *tp = cp;
1363 return v;
1364 }
1365
1366 static unsigned long
1367 nxtxnum(char **tp, struct disklabel *lp, int lineno)
1368 {
1369 char *cp, *ncp;
1370 unsigned long n, v;
1371
1372 __CHECKLINE
1373 cp = *tp;
1374 if (getulong(cp, '/', &ncp, &n, UINT32_MAX) != 0)
1375 goto bad;
1376
1377 if (*ncp == '/') {
1378 n *= lp->d_secpercyl;
1379 cp = ncp + 1;
1380 if (getulong(cp, '/', &ncp, &v, UINT32_MAX) != 0)
1381 goto bad;
1382 n += v * lp->d_nsectors;
1383 cp = ncp + 1;
1384 if (getulong(cp, '\0', &ncp, &v, UINT32_MAX) != 0)
1385 goto bad;
1386 n += v;
1387 }
1388 *tp = ncp;
1389 return n;
1390 bad:
1391 warnx("line %d: invalid format", lineno);
1392 *tp = _error_;
1393 return 0;
1394 }
1395
1396 /*
1397 * Read an ascii label in from fd f,
1398 * in the same format as that put out by showinfo() and showpartitions(),
1399 * and fill in lp.
1400 */
1401 static int
1402 getasciilabel(FILE *f, struct disklabel *lp)
1403 {
1404 const char *const *cpp, *s;
1405 struct partition *pp;
1406 char *cp, *tp, line[BUFSIZ], tbuf[15];
1407 int lineno, errors;
1408 unsigned long v;
1409 unsigned int part;
1410
1411 lineno = 0;
1412 errors = 0;
1413 lp->d_bbsize = BBSIZE; /* XXX */
1414 lp->d_sbsize = SBLOCKSIZE; /* XXX */
1415 while (fgets(line, sizeof(line) - 1, f)) {
1416 lineno++;
1417 if ((cp = strpbrk(line, "#\r\n")) != NULL)
1418 *cp = '\0';
1419 cp = skip(line);
1420 if (cp == NULL) /* blank line or comment line */
1421 continue;
1422 tp = strchr(cp, ':'); /* everything has a colon in it */
1423 if (tp == NULL) {
1424 warnx("line %d: syntax error", lineno);
1425 errors++;
1426 continue;
1427 }
1428 *tp++ = '\0', tp = skip(tp);
1429 if (!strcmp(cp, "type")) {
1430 if (tp == NULL) {
1431 strlcpy(tbuf, "unknown", sizeof(tbuf));
1432 tp = tbuf;
1433 }
1434 cpp = dktypenames;
1435 for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
1436 if ((s = *cpp) && !strcasecmp(s, tp)) {
1437 lp->d_type = cpp - dktypenames;
1438 goto next;
1439 }
1440 if (GETNUM16(tp, &v) != 0) {
1441 warnx("line %d: syntax error", lineno);
1442 errors++;
1443 continue;
1444 }
1445 if (v >= DKMAXTYPES)
1446 warnx("line %d: warning, unknown disk type: %s",
1447 lineno, tp);
1448 lp->d_type = v;
1449 continue;
1450 }
1451 if (!strcmp(cp, "flags")) {
1452 for (v = 0; (cp = tp) && *cp != '\0';) {
1453 tp = word(cp);
1454 if (!strcasecmp(cp, "removable"))
1455 v |= D_REMOVABLE;
1456 else if (!strcasecmp(cp, "ecc"))
1457 v |= D_ECC;
1458 else if (!strcasecmp(cp, "badsect"))
1459 v |= D_BADSECT;
1460 else {
1461 warnx("line %d: bad flag: %s",
1462 lineno, cp);
1463 errors++;
1464 }
1465 }
1466 lp->d_flags = v;
1467 continue;
1468 }
1469 if (!strcmp(cp, "drivedata")) {
1470 int i;
1471
1472 for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
1473 if (GETNUM32(cp, &v) != 0) {
1474 warnx("line %d: bad drive data",
1475 lineno);
1476 errors++;
1477 } else
1478 lp->d_drivedata[i] = v;
1479 i++;
1480 tp = word(cp);
1481 }
1482 continue;
1483 }
1484 if (sscanf(cp, "%lu partitions", &v) == 1) {
1485 if (v == 0 || v > MAXPARTITIONS) {
1486 warnx("line %d: bad # of partitions", lineno);
1487 lp->d_npartitions = MAXPARTITIONS;
1488 errors++;
1489 } else
1490 lp->d_npartitions = v;
1491 continue;
1492 }
1493 if (tp == NULL) {
1494 tbuf[0] = '\0';
1495 tp = tbuf;
1496 }
1497 if (!strcmp(cp, "disk")) {
1498 strncpy(lp->d_typename, tp, sizeof(lp->d_typename));
1499 continue;
1500 }
1501 if (!strcmp(cp, "label")) {
1502 strncpy(lp->d_packname, tp, sizeof(lp->d_packname));
1503 continue;
1504 }
1505 if (!strcmp(cp, "bytes/sector")) {
1506 if (GETNUM32(tp, &v) != 0 || v <= 0 || (v % 512) != 0) {
1507 warnx("line %d: bad %s: %s", lineno, cp, tp);
1508 errors++;
1509 } else
1510 lp->d_secsize = v;
1511 continue;
1512 }
1513 if (!strcmp(cp, "sectors/track")) {
1514 if (GETNUM32(tp, &v) != 0) {
1515 warnx("line %d: bad %s: %s", lineno, cp, tp);
1516 errors++;
1517 } else
1518 lp->d_nsectors = v;
1519 continue;
1520 }
1521 if (!strcmp(cp, "sectors/cylinder")) {
1522 if (GETNUM32(tp, &v) != 0) {
1523 warnx("line %d: bad %s: %s", lineno, cp, tp);
1524 errors++;
1525 } else
1526 lp->d_secpercyl = v;
1527 continue;
1528 }
1529 if (!strcmp(cp, "tracks/cylinder")) {
1530 if (GETNUM32(tp, &v) != 0) {
1531 warnx("line %d: bad %s: %s", lineno, cp, tp);
1532 errors++;
1533 } else
1534 lp->d_ntracks = v;
1535 continue;
1536 }
1537 if (!strcmp(cp, "cylinders")) {
1538 if (GETNUM32(tp, &v) != 0) {
1539 warnx("line %d: bad %s: %s", lineno, cp, tp);
1540 errors++;
1541 } else
1542 lp->d_ncylinders = v;
1543 continue;
1544 }
1545 if (!strcmp(cp, "total sectors")) {
1546 if (GETNUM32(tp, &v) != 0) {
1547 warnx("line %d: bad %s: %s", lineno, cp, tp);
1548 errors++;
1549 } else
1550 lp->d_secperunit = v;
1551 continue;
1552 }
1553 if (!strcmp(cp, "rpm")) {
1554 if (GETNUM16(tp, &v) != 0) {
1555 warnx("line %d: bad %s: %s", lineno, cp, tp);
1556 errors++;
1557 } else
1558 lp->d_rpm = v;
1559 continue;
1560 }
1561 if (!strcmp(cp, "interleave")) {
1562 if (GETNUM16(tp, &v) != 0) {
1563 warnx("line %d: bad %s: %s", lineno, cp, tp);
1564 errors++;
1565 } else
1566 lp->d_interleave = v;
1567 continue;
1568 }
1569 if (!strcmp(cp, "trackskew")) {
1570 if (GETNUM16(tp, &v) != 0) {
1571 warnx("line %d: bad %s: %s", lineno, cp, tp);
1572 errors++;
1573 } else
1574 lp->d_trackskew = v;
1575 continue;
1576 }
1577 if (!strcmp(cp, "cylinderskew")) {
1578 if (GETNUM16(tp, &v) != 0) {
1579 warnx("line %d: bad %s: %s", lineno, cp, tp);
1580 errors++;
1581 } else
1582 lp->d_cylskew = v;
1583 continue;
1584 }
1585 if (!strcmp(cp, "headswitch")) {
1586 if (GETNUM32(tp, &v) != 0) {
1587 warnx("line %d: bad %s: %s", lineno, cp, tp);
1588 errors++;
1589 } else
1590 lp->d_headswitch = v;
1591 continue;
1592 }
1593 if (!strcmp(cp, "track-to-track seek")) {
1594 if (GETNUM32(tp, &v) != 0) {
1595 warnx("line %d: bad %s: %s", lineno, cp, tp);
1596 errors++;
1597 } else
1598 lp->d_trkseek = v;
1599 continue;
1600 }
1601 if ('a' > *cp || *cp > 'z' || cp[1] != '\0') {
1602 warnx("line %d: unknown field: %s", lineno, cp);
1603 errors++;
1604 continue;
1605 }
1606
1607 /* We have a partition entry */
1608 part = *cp - 'a';
1609
1610 if (part >= MAXPARTITIONS) {
1611 warnx("line %d: bad partition name: %s", lineno, cp);
1612 errors++;
1613 continue;
1614 }
1615 pp = &lp->d_partitions[part];
1616
1617 NXTXNUM(pp->p_size);
1618 NXTXNUM(pp->p_offset);
1619 /* can't use word() here because of blanks in fstypenames[] */
1620 tp += strspn(tp, " \t");
1621 _CHECKLINE
1622 cp = tp;
1623 cpp = fstypenames;
1624 for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) {
1625 s = *cpp;
1626 if (s == NULL ||
1627 (cp[strlen(s)] != ' ' &&
1628 cp[strlen(s)] != '\t' &&
1629 cp[strlen(s)] != '\0'))
1630 continue;
1631 if (!memcmp(s, cp, strlen(s))) {
1632 pp->p_fstype = cpp - fstypenames;
1633 tp += strlen(s);
1634 if (*tp == '\0')
1635 tp = NULL;
1636 else {
1637 tp += strspn(tp, " \t");
1638 if (*tp == '\0')
1639 tp = NULL;
1640 }
1641 goto gottype;
1642 }
1643 }
1644 tp = word(cp);
1645 if (isdigit(*cp & 0xff)) {
1646 if (GETNUM8(cp, &v) != 0) {
1647 warnx("line %d: syntax error", lineno);
1648 errors++;
1649 }
1650 } else
1651 v = FSMAXTYPES;
1652 if ((unsigned)v >= FSMAXTYPES) {
1653 warnx("line %d: warning, unknown filesystem type: %s",
1654 lineno, cp);
1655 v = FS_UNUSED;
1656 }
1657 pp->p_fstype = v;
1658 gottype:
1659 switch (pp->p_fstype) {
1660
1661 case FS_UNUSED: /* XXX */
1662 NXTNUM(pp->p_fsize);
1663 if (pp->p_fsize == 0)
1664 break;
1665 NXTNUM(v);
1666 pp->p_frag = v / pp->p_fsize;
1667 break;
1668
1669 case FS_BSDFFS:
1670 case FS_ADOS:
1671 case FS_APPLEUFS:
1672 NXTNUM(pp->p_fsize);
1673 if (pp->p_fsize == 0)
1674 break;
1675 NXTNUM(v);
1676 pp->p_frag = v / pp->p_fsize;
1677 NXTNUM(pp->p_cpg);
1678 break;
1679 case FS_BSDLFS:
1680 NXTNUM(pp->p_fsize);
1681 if (pp->p_fsize == 0)
1682 break;
1683 NXTNUM(v);
1684 pp->p_frag = v / pp->p_fsize;
1685 NXTNUM(pp->p_sgs);
1686 break;
1687 case FS_EX2FS:
1688 NXTNUM(pp->p_fsize);
1689 if (pp->p_fsize == 0)
1690 break;
1691 NXTNUM(v);
1692 pp->p_frag = v / pp->p_fsize;
1693 break;
1694 case FS_ISO9660:
1695 NXTNUM(pp->p_cdsession);
1696 break;
1697 default:
1698 break;
1699 }
1700 continue;
1701 error:
1702 errors++;
1703 next:
1704 ;
1705 }
1706 errors += checklabel(lp);
1707 return (errors == 0);
1708 }
1709
1710 /*
1711 * Check disklabel for errors and fill in
1712 * derived fields according to supplied values.
1713 */
1714 int
1715 checklabel(struct disklabel *lp)
1716 {
1717 struct partition *pp, *qp;
1718 int i, j, errors;
1719 char part;
1720
1721 errors = 0;
1722 if (lp->d_secsize == 0) {
1723 warnx("sector size %d", lp->d_secsize);
1724 return (1);
1725 }
1726 if (lp->d_nsectors == 0) {
1727 warnx("sectors/track %d", lp->d_nsectors);
1728 return (1);
1729 }
1730 if (lp->d_ntracks == 0) {
1731 warnx("tracks/cylinder %d", lp->d_ntracks);
1732 return (1);
1733 }
1734 if (lp->d_ncylinders == 0) {
1735 warnx("cylinders/unit %d", lp->d_ncylinders);
1736 errors++;
1737 }
1738 if (lp->d_rpm == 0)
1739 warnx("warning, revolutions/minute %d", lp->d_rpm);
1740 if (lp->d_secpercyl == 0)
1741 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1742 if (lp->d_secperunit == 0)
1743 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1744 #ifdef __i386__notyet__
1745 if (dosdp && lp->d_secperunit > dosdp->mbrp_start + dosdp->mbrp_size) {
1746 warnx("exceeds DOS partition size");
1747 errors++;
1748 lp->d_secperunit = dosdp->mbrp_start + dosdp->mbrp_size;
1749 }
1750 /* XXX should also check geometry against BIOS's idea */
1751 #endif /* __i386__notyet__ */
1752 #ifdef __arm32__notyet__
1753 /* XXX similar code as for i386 */
1754 #endif /* __arm32__notyet__ */
1755 if (lp->d_bbsize == 0) {
1756 warnx("boot block size %d", lp->d_bbsize);
1757 errors++;
1758 } else if (lp->d_bbsize % lp->d_secsize)
1759 warnx("warning, boot block size %% sector-size != 0");
1760 if (lp->d_sbsize == 0) {
1761 warnx("super block size %d", lp->d_sbsize);
1762 errors++;
1763 } else if (lp->d_sbsize % lp->d_secsize)
1764 warnx("warning, super block size %% sector-size != 0");
1765 if (lp->d_npartitions > MAXPARTITIONS)
1766 warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
1767 lp->d_npartitions, MAXPARTITIONS);
1768 else
1769 for (i = MAXPARTITIONS - 1; i >= lp->d_npartitions; i--) {
1770 part = 'a' + i;
1771 pp = &lp->d_partitions[i];
1772 if (pp->p_size || pp->p_offset) {
1773 warnx("warning, partition %c increased "
1774 "number of partitions from %d to %d",
1775 part, lp->d_npartitions, i + 1);
1776 lp->d_npartitions = i + 1;
1777 break;
1778 }
1779 }
1780 for (i = 0; i < lp->d_npartitions; i++) {
1781 part = 'a' + i;
1782 pp = &lp->d_partitions[i];
1783 if (pp->p_size == 0 && pp->p_offset != 0)
1784 warnx("warning, partition %c: size 0, but offset %d",
1785 part, pp->p_offset);
1786 #ifdef STRICT_CYLINDER_ALIGNMENT
1787 if (pp->p_offset % lp->d_secpercyl) {
1788 warnx("warning, partition %c:"
1789 " not starting on cylinder boundary",
1790 part);
1791 errors++;
1792 }
1793 #endif /* STRICT_CYLINDER_ALIGNMENT */
1794 if (pp->p_offset > lp->d_secperunit) {
1795 warnx("partition %c: offset past end of unit", part);
1796 errors++;
1797 }
1798 if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1799 warnx("partition %c: partition extends"
1800 " past end of unit",
1801 part);
1802 errors++;
1803 }
1804 if (pp->p_fstype != FS_UNUSED)
1805 for (j = i + 1; j < lp->d_npartitions; j++) {
1806 qp = &lp->d_partitions[j];
1807 if (qp->p_fstype == FS_UNUSED)
1808 continue;
1809 if (pp->p_offset < qp->p_offset + qp->p_size &&
1810 qp->p_offset < pp->p_offset + pp->p_size)
1811 warnx("partitions %c and %c overlap",
1812 part, 'a' + j);
1813 }
1814 }
1815 return (errors);
1816 }
1817
1818 #if NUMBOOT > 0
1819 /*
1820 * If we are installing a boot program that doesn't fit in d_bbsize
1821 * we need to mark those partitions that the boot overflows into.
1822 * This allows newfs to prevent creation of a filesystem where it might
1823 * clobber bootstrap code.
1824 */
1825 static void
1826 setbootflag(struct disklabel *lp)
1827 {
1828 struct partition *pp;
1829 int i, errors;
1830 char part;
1831 u_long boffset;
1832
1833 errors = 0;
1834 if (bootbuf == 0)
1835 return;
1836 boffset = bootsize / lp->d_secsize;
1837 for (i = 0; i < lp->d_npartitions; i++) {
1838 part = 'a' + i;
1839 pp = &lp->d_partitions[i];
1840 if (pp->p_size == 0)
1841 continue;
1842 if (boffset <= pp->p_offset) {
1843 if (pp->p_fstype == FS_BOOT)
1844 pp->p_fstype = FS_UNUSED;
1845 } else if (pp->p_fstype != FS_BOOT) {
1846 if (pp->p_fstype != FS_UNUSED) {
1847 warnx("boot overlaps used partition %c",
1848 part);
1849 errors++;
1850 } else {
1851 pp->p_fstype = FS_BOOT;
1852 warnx("warning, boot overlaps partition %c, %s",
1853 part, "marked as FS_BOOT");
1854 }
1855 }
1856 }
1857 if (errors)
1858 errx(4, "cannot install boot program");
1859 }
1860 #endif /* NUMBOOT > 0 */
1861
1862 static void
1863 usage(void)
1864 {
1865 static const struct {
1866 const char *name;
1867 const char *expn;
1868 } usages[] = {
1869 { "[-rt] [-C] [-F] disk",
1870 "(to read label)" },
1871 { "-w [-r] [-F] [-f disktab] disk type [ packid ]",
1872 #if NUMBOOT > 0
1873 "(to write label with existing boot program)"
1874 #else
1875 "(to write label)"
1876 #endif
1877 },
1878 { "-e [-r] [-I] [-C] [-F] disk",
1879 "(to edit label)" },
1880 { "-i [-I] [-r] [-F] disk",
1881 "(to create a label interactively)" },
1882 { "-R [-r] [-F] disk protofile",
1883 #if NUMBOOT > 0
1884 "(to restore label with existing boot program)"
1885 #else
1886 "(to restore label)"
1887 #endif
1888 },
1889 #if NUMBOOT > 0
1890 { "-B [-F] [-f disktab] [ -b bootprog ] disk [ type ]",
1891 "(to install boot program with existing on-disk label)" },
1892 { "-w -B [-F] [-f disktab] [ -b bootprog ] disk type [ packid ]",
1893 "(to write label and install boot program)" },
1894 { "-R -B [-F] [-f disktab] [ -b bootprog ] disk protofile [ type ]",
1895 "(to restore label and install boot program)" },
1896 #endif
1897 { "[-NW] disk",
1898 "(to write disable/enable label)" },
1899 { NULL,
1900 NULL }
1901 };
1902 int i;
1903
1904 for (i = 0; usages[i].name; i++) {
1905 (void) fputs(i ? "or " : "usage: ", stderr);
1906 (void) fprintf(stderr, "%s %s", getprogname(), usages[i].name);
1907 (void) fputs("\n\t", stderr);
1908 (void) fprintf(stderr, "%s %s", getprogname(), usages[i].expn);
1909 (void) fputs("\n", stderr);
1910 }
1911 exit(1);
1912 }
1913
1914 static int
1915 getulong(const char *str, char sep, char **epp, unsigned long *ul,
1916 unsigned long max)
1917 {
1918 char *ep;
1919
1920 if (epp == NULL)
1921 epp = &ep;
1922
1923 *ul = strtoul(str, epp, 10);
1924
1925 if ((*ul == ULONG_MAX && errno == ERANGE) || *ul > max)
1926 return ERANGE;
1927
1928 if (*str == '\0' || (**epp != '\0' && **epp != sep &&
1929 !isspace((unsigned char)**epp)))
1930 return EFTYPE;
1931
1932 return 0;
1933 }
1934