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