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