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