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