]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sbin/sunlabel/sunlabel.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sbin / sunlabel / sunlabel.c
1 /*-
2  * Copyright (c) 2003 Jake Burkholder.
3  * Copyright (c) 2004,2005 Joerg Wunsch.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 /*
28  * Copyright (c) 1994, 1995 Gordon W. Ross
29  * Copyright (c) 1994 Theo de Raadt
30  * All rights reserved.
31  * Copyright (c) 1987, 1993
32  *      The Regents of the University of California.  All rights reserved.
33  *
34  * This code is derived from software contributed to Berkeley by
35  * Symmetric Computer Systems.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. All advertising materials mentioning features or use of this software
46  *    must display the following acknowledgement:
47  *      This product includes software developed by the University of
48  *      California, Berkeley and its contributors.
49  *      This product includes software developed by Theo de Raadt.
50  * 4. Neither the name of the University nor the names of its contributors
51  *    may be used to endorse or promote products derived from this software
52  *    without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64  * SUCH DAMAGE.
65  *
66  *      from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
67  */
68
69 #include <sys/cdefs.h>
70 __FBSDID("$FreeBSD$");
71
72 #include <sys/types.h>
73 #include <sys/param.h>
74 #include <sys/disk.h>
75 #include <sys/ioctl.h>
76 #include <sys/sun_disklabel.h>
77 #include <sys/wait.h>
78
79 #include <ctype.h>
80 #include <err.h>
81 #include <fcntl.h>
82 #include <inttypes.h>
83 #include <libgeom.h>
84 #include <paths.h>
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88 #include <unistd.h>
89
90 #define _PATH_TMPFILE   "/tmp/EdDk.XXXXXXXXXX"
91 #define _PATH_BOOT      "/boot/boot1"
92
93 static int bflag;
94 static int Bflag;
95 static int cflag;
96 static int eflag;
97 static int hflag;
98 static int nflag;
99 static int Rflag;
100 static int wflag;
101
102 static off_t mediasize;
103 static uint32_t sectorsize;
104
105 struct tags {
106         const char *name;
107         unsigned int id;
108 };
109
110 static int check_label(struct sun_disklabel *sl);
111 static void read_label(struct sun_disklabel *sl, const char *disk);
112 static void write_label(struct sun_disklabel *sl, const char *disk,
113     const char *bootpath);
114 static void edit_label(struct sun_disklabel *sl, const char *disk,
115     const char *bootpath);
116 static int parse_label(struct sun_disklabel *sl, const char *file);
117 static void print_label(struct sun_disklabel *sl, const char *disk, FILE *out);
118
119 static int parse_size(struct sun_disklabel *sl, int part, char *size);
120 static int parse_offset(struct sun_disklabel *sl, int part, char *offset);
121
122 static const char *flagname(unsigned int tag);
123 static const char *tagname(unsigned int tag);
124 static unsigned int parse_flag(struct sun_disklabel *sl, int part,
125                                const char *flag);
126 static unsigned int parse_tag(struct sun_disklabel *sl, int part,
127                               const char *tag);
128 static const char *make_h_number(uintmax_t u);
129
130 static void usage(void);
131
132 extern char *__progname;
133
134 static struct tags knowntags[] = {
135         { "unassigned", VTOC_UNASSIGNED },
136         { "boot",       VTOC_BOOT },
137         { "root",       VTOC_ROOT },
138         { "swap",       VTOC_SWAP },
139         { "usr",        VTOC_USR },
140         { "backup",     VTOC_BACKUP },
141         { "stand",      VTOC_STAND },
142         { "var",        VTOC_VAR },
143         { "home",       VTOC_HOME },
144         { "altsctr",    VTOC_ALTSCTR },
145         { "cache",      VTOC_CACHE },
146         { "VxVM_pub",   VTOC_VXVM_PUB },
147         { "VxVM_priv",  VTOC_VXVM_PRIV },
148 };
149
150 static struct tags knownflags[] = {
151         { "wm", 0 },
152         { "wu", VTOC_UNMNT },
153         { "rm", VTOC_RONLY },
154         { "ru", VTOC_UNMNT | VTOC_RONLY },
155 };
156
157 /*
158  * Disk label editor for sun disklabels.
159  */
160 int
161 main(int ac, char **av)
162 {
163         struct sun_disklabel sl;
164         const char *bootpath;
165         const char *proto;
166         const char *disk;
167         int ch;
168
169         bootpath = _PATH_BOOT; 
170         while ((ch = getopt(ac, av, "b:BcehnrRw")) != -1)
171                 switch (ch) {
172                 case 'b':
173                         bflag = 1;
174                         bootpath = optarg;
175                         break;
176                 case 'B':
177                         Bflag = 1;
178                         break;
179                 case 'c':
180                         cflag = 1;
181                         break;
182                 case 'e':
183                         eflag = 1;
184                         break;
185                 case 'h':
186                         hflag = 1;
187                         break;
188                 case 'n':
189                         nflag = 1;
190                         break;
191                 case 'r':
192                         fprintf(stderr, "Obsolete -r flag ignored\n");
193                         break;
194                 case 'R':
195                         Rflag = 1;
196                         break;
197                 case 'w':
198                         wflag = 1;
199                         break;
200                 default:
201                         usage();
202                         break;
203                 }
204         if (bflag && !Bflag)
205                 usage();
206         if (nflag && !(Bflag || eflag || Rflag || wflag))
207                 usage();
208         if (eflag && (Rflag || wflag))
209                 usage();
210         if (eflag)
211                 hflag = 0;
212         ac -= optind;
213         av += optind;
214         if (ac == 0)
215                 usage();
216         bzero(&sl, sizeof(sl));
217         disk = av[0];
218         if (wflag) {
219                 if (ac != 2 || strcmp(av[1], "auto") != 0)
220                         usage();
221                 read_label(&sl, disk);
222                 bzero(sl.sl_part, sizeof(sl.sl_part));
223                 sl.sl_part[SUN_RAWPART].sdkp_cyloffset = 0;
224                 sl.sl_part[SUN_RAWPART].sdkp_nsectors = sl.sl_ncylinders *
225                     sl.sl_ntracks * sl.sl_nsectors;
226                 write_label(&sl, disk, bootpath);
227         } else if (eflag) {
228                 if (ac != 1)
229                         usage();
230                 read_label(&sl, disk);
231                 if (sl.sl_magic != SUN_DKMAGIC)
232                         errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
233                 edit_label(&sl, disk, bootpath);
234         } else if (Rflag) {
235                 if (ac != 2)
236                         usage();
237                 proto = av[1];
238                 read_label(&sl, disk);
239                 if (parse_label(&sl, proto) != 0)
240                         errx(1, "%s: invalid label", proto);
241                 write_label(&sl, disk, bootpath);
242         } else if (Bflag) {
243                 read_label(&sl, disk);
244                 if (sl.sl_magic != SUN_DKMAGIC)
245                         errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
246                 write_label(&sl, disk, bootpath);
247         } else {
248                 read_label(&sl, disk);
249                 if (sl.sl_magic != SUN_DKMAGIC)
250                         errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
251                 print_label(&sl, disk, stdout);
252         }
253         return (0);
254 }
255
256 static int
257 check_label(struct sun_disklabel *sl)
258 {
259         uint64_t nsectors;
260         uint64_t ostart;
261         uint64_t start;
262         uint64_t oend;
263         uint64_t end;
264         int havevtoc;
265         int warnonly;
266         int i;
267         int j;
268
269         havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE;
270
271         nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors;
272         if (sl->sl_part[SUN_RAWPART].sdkp_cyloffset != 0 ||
273             sl->sl_part[SUN_RAWPART].sdkp_nsectors != nsectors) {
274                 warnx("partition c is incorrect, must start at 0 and cover "
275                     "whole disk");
276                 return (1);
277         }
278         if (havevtoc && sl->sl_vtoc_map[2].svtoc_tag != VTOC_BACKUP) {
279                 warnx("partition c must have tag \"backup\"");
280                 return (1);
281         }
282         for (i = 0; i < SUN_NPART; i++) {
283                 if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0)
284                         continue;
285                 start = (uint64_t)sl->sl_part[i].sdkp_cyloffset *
286                     sl->sl_ntracks * sl->sl_nsectors;
287                 end = start + sl->sl_part[i].sdkp_nsectors;
288                 if (end > nsectors) {
289                         warnx("partition %c extends past end of disk",
290                             'a' + i);
291                         return (1);
292                 }
293                 if (havevtoc) {
294                         if (sl->sl_vtoc_map[i].svtoc_tag == VTOC_BACKUP) {
295                                 warnx("only partition c is allowed to have "
296                                     "tag \"backup\"");
297                                 return (1);
298                         }
299                 }
300                 for (j = 0; j < SUN_NPART; j++) {
301                         /* 
302                          * Overlaps for unmountable partitions are
303                          * non-fatal but will be warned anyway.
304                          */
305                         warnonly = havevtoc &&
306                                 ((sl->sl_vtoc_map[i].svtoc_flag & VTOC_UNMNT) != 0 ||
307                                  (sl->sl_vtoc_map[j].svtoc_flag & VTOC_UNMNT) != 0);
308
309                         if (j == 2 || j == i ||
310                             sl->sl_part[j].sdkp_nsectors == 0)
311                                 continue;
312                         ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset *
313                             sl->sl_ntracks * sl->sl_nsectors;
314                         oend = ostart + sl->sl_part[j].sdkp_nsectors;
315                         if ((start <= ostart && end >= oend) ||
316                             (start > ostart && start < oend) ||
317                             (end > ostart && end < oend)) {
318                                 warnx("partition %c overlaps partition %c",
319                                     'a' + i, 'a' + j);
320                                 if (!warnonly)
321                                         return (1);
322                         }
323                 }
324         }
325         return (0);
326 }
327
328 static void
329 read_label(struct sun_disklabel *sl, const char *disk)
330 {
331         char path[MAXPATHLEN];
332         uint32_t fwsectors;
333         uint32_t fwheads;
334         char buf[SUN_SIZE];
335         int fd, error;
336
337         snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk);
338         if ((fd = open(path, O_RDONLY)) < 0)
339                 err(1, "open %s", path);
340         if (read(fd, buf, sizeof(buf)) != sizeof(buf))
341                 err(1, "read");
342         error = sunlabel_dec(buf, sl);
343         if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0)
344                 if (error)
345                         err(1, "%s: ioctl(DIOCGMEDIASIZE) failed", disk);
346         if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) != 0) {
347                 if (error)
348                         err(1, "%s: DIOCGSECTORSIZE failed", disk);
349                 else
350                         sectorsize = 512;
351         }
352         if (error) {
353                 bzero(sl, sizeof(*sl));
354                 if (ioctl(fd, DIOCGFWSECTORS, &fwsectors) != 0)
355                         fwsectors = 63;
356                 if (ioctl(fd, DIOCGFWHEADS, &fwheads) != 0) {
357                         if (mediasize <= 63 * 1024 * sectorsize)
358                                 fwheads = 1;
359                         else if (mediasize <= 63 * 16 * 1024 * sectorsize)
360                                 fwheads = 16;
361                         else
362                                 fwheads = 255;
363                 }
364                 sl->sl_rpm = 3600;
365                 sl->sl_pcylinders = mediasize / (fwsectors * fwheads *
366                     sectorsize);
367                 sl->sl_sparespercyl = 0;
368                 sl->sl_interleave = 1;
369                 sl->sl_ncylinders = sl->sl_pcylinders - 2;
370                 sl->sl_acylinders = 2;
371                 sl->sl_nsectors = fwsectors;
372                 sl->sl_ntracks = fwheads;
373                 sl->sl_part[SUN_RAWPART].sdkp_cyloffset = 0;
374                 sl->sl_part[SUN_RAWPART].sdkp_nsectors = sl->sl_ncylinders *
375                     sl->sl_ntracks * sl->sl_nsectors;
376                 if (mediasize > (off_t)4999L * 1024L * 1024L) {
377                         sprintf(sl->sl_text,
378                             "FreeBSD%jdG cyl %u alt %u hd %u sec %u",
379                             (intmax_t)(mediasize + 512 * 1024 * 1024) /
380                                 (1024 * 1024 * 1024),
381                             sl->sl_ncylinders, sl->sl_acylinders,
382                             sl->sl_ntracks, sl->sl_nsectors);
383                 } else {
384                         sprintf(sl->sl_text,
385                             "FreeBSD%jdM cyl %u alt %u hd %u sec %u",
386                             (intmax_t)(mediasize + 512 * 1024) / (1024 * 1024),
387                             sl->sl_ncylinders, sl->sl_acylinders,
388                             sl->sl_ntracks, sl->sl_nsectors);
389                 }
390         }
391         close(fd);
392 }
393
394 static void
395 write_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
396 {
397         char path[MAXPATHLEN];
398         char boot[SUN_BOOTSIZE];
399         char buf[SUN_SIZE];
400         const char *errstr;
401         off_t off;
402         int bfd;
403         int fd;
404         int i;
405         struct gctl_req *grq;
406
407         sl->sl_magic = SUN_DKMAGIC;
408
409         if (check_label(sl) != 0)
410                 errx(1, "invalid label");
411
412         bzero(buf, sizeof(buf));
413         sunlabel_enc(buf, sl);
414
415         if (nflag) {
416                 print_label(sl, disk, stdout);
417                 return;
418         }
419         if (Bflag) {
420                 if ((bfd = open(bootpath, O_RDONLY)) < 0)
421                         err(1, "open %s", bootpath);
422                 i = read(bfd, boot, sizeof(boot));
423                 if (i < 0)
424                         err(1, "read");
425                 else if (i != sizeof (boot))
426                         errx(1, "read wrong size boot code (%d)", i);
427                 close(bfd);
428         }
429         snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk);
430         fd = open(path, O_RDWR);
431         if (fd < 0) {
432                 grq = gctl_get_handle();
433                 gctl_ro_param(grq, "verb", -1, "write label");
434                 gctl_ro_param(grq, "class", -1, "SUN");
435                 gctl_ro_param(grq, "geom", -1, disk);
436                 gctl_ro_param(grq, "label", sizeof buf, buf);
437                 errstr = gctl_issue(grq);
438                 if (errstr != NULL)
439                         errx(1, "%s", errstr);
440                 gctl_free(grq);
441                 if (Bflag) {
442                         grq = gctl_get_handle();
443                         gctl_ro_param(grq, "verb", -1, "write bootcode");
444                         gctl_ro_param(grq, "class", -1, "SUN");
445                         gctl_ro_param(grq, "geom", -1, disk);
446                         gctl_ro_param(grq, "bootcode", sizeof boot, boot);
447                         errstr = gctl_issue(grq);
448                         if (errstr != NULL)
449                                 errx(1, "%s", errstr);
450                         gctl_free(grq);
451                 }
452         } else {
453                 if (lseek(fd, 0, SEEK_SET) < 0)
454                         err(1, "lseek");
455                 if (write(fd, buf, sizeof(buf)) != sizeof(buf))
456                         err (1, "write");
457                 if (Bflag) {
458                         for (i = 0; i < SUN_NPART; i++) {
459                                 if (sl->sl_part[i].sdkp_nsectors == 0)
460                                         continue;
461                                 off = sl->sl_part[i].sdkp_cyloffset *
462                                     sl->sl_ntracks * sl->sl_nsectors * 512;
463                                 /*
464                                  * Ignore first SUN_SIZE bytes of boot code to
465                                  * avoid overwriting the label.
466                                  */
467                                 if (lseek(fd, off + SUN_SIZE, SEEK_SET) < 0)
468                                         err(1, "lseek");
469                                 if (write(fd, boot + SUN_SIZE,
470                                     sizeof(boot) - SUN_SIZE) !=
471                                     sizeof(boot) - SUN_SIZE)
472                                         err(1, "write");
473                         }
474                 }
475                 close(fd);
476         }
477         exit(0);
478 }
479
480 static void
481 edit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
482 {
483         char tmpfil[] = _PATH_TMPFILE;
484         const char *editor;
485         int status;
486         FILE *fp;
487         pid_t pid;
488         pid_t r;
489         int fd;
490         int c;
491
492         if ((fd = mkstemp(tmpfil)) < 0)
493                 err(1, "mkstemp");
494         if ((fp = fdopen(fd, "w")) == NULL)
495                 err(1, "fdopen");
496         print_label(sl, disk, fp);
497         fflush(fp);
498         for (;;) {
499                 if ((pid = fork()) < 0)
500                         err(1, "fork");
501                 if (pid == 0) {
502                         if ((editor = getenv("EDITOR")) == NULL)
503                                 editor = _PATH_VI;
504                         execlp(editor, editor, tmpfil, (char *)NULL);
505                         err(1, "execlp %s", editor);
506                 }
507                 status = 0;
508                 while ((r = wait(&status)) > 0 && r != pid)
509                         ;
510                 if (WIFEXITED(status)) {
511                         if (parse_label(sl, tmpfil) == 0) {
512                                 fclose(fp);
513                                 unlink(tmpfil);
514                                 write_label(sl, disk, bootpath);
515                                 return;
516                         }
517                         printf("re-edit the label? [y]: ");
518                         fflush(stdout);
519                         c = getchar();
520                         if (c != EOF && c != '\n')
521                                 while (getchar() != '\n')
522                                         ;
523                         if  (c == 'n') {
524                                 fclose(fp);
525                                 unlink(tmpfil);
526                                 return;
527                         }
528                 }
529         }
530         fclose(fp);
531         unlink(tmpfil);
532         return;
533 }
534
535 static int
536 parse_label(struct sun_disklabel *sl, const char *file)
537 {
538         char offset[32];
539         char size[32];
540         char flag[32];
541         char tag[32];
542         char buf[128];
543         char text[128];
544         char volname[SUN_VOLNAME_LEN + 1];
545         struct sun_disklabel sl1;
546         char *bp;
547         const char *what;
548         uint8_t part;
549         FILE *fp;
550         int line;
551         int rv;
552         int wantvtoc;
553         unsigned alt, cyl, hd, nr, sec;
554
555         line = wantvtoc = 0;
556         if ((fp = fopen(file, "r")) == NULL)
557                 err(1, "fopen");
558         sl1 = *sl;
559         bzero(&sl1.sl_part, sizeof(sl1.sl_part));
560         while (fgets(buf, sizeof(buf), fp) != NULL) {
561                 /*
562                  * In order to recognize a partition entry, we search
563                  * for lines starting with a single letter followed by
564                  * a colon as their first non-white characters.  We
565                  * silently ignore any other lines, so any comment etc.
566                  * lines in the label template will be ignored.
567                  *
568                  * XXX We should probably also recognize the geometry
569                  * fields on top, and allow changing the geometry
570                  * emulated by this disk.
571                  */
572                 for (bp = buf; isspace(*bp); bp++)
573                         ;
574                 if (strncmp(bp, "text:", strlen("text:")) == 0) {
575                         bp += strlen("text:");
576                         rv = sscanf(bp,
577                             " %s cyl %u alt %u hd %u sec %u",
578                             text, &cyl, &alt, &hd, &sec);
579                         if (rv != 5) {
580                                 warnx("%s, line %d: text label does not "
581                                     "contain required fields",
582                                     file, line + 1);
583                                 fclose(fp);
584                                 return (1);
585                         }
586                         if (alt != 2) {
587                                 warnx("%s, line %d: # alt must be equal 2",
588                                     file, line + 1);
589                                 fclose(fp);
590                                 return (1);
591                         }
592                         if (cyl == 0 || cyl > USHRT_MAX) {
593                                 what = "cyl";
594                                 nr = cyl;
595                         unreasonable:
596                                 warnx("%s, line %d: # %s %d unreasonable",
597                                     file, line + 1, what, nr);
598                                 fclose(fp);
599                                 return (1);
600                         }
601                         if (hd == 0 || hd > USHRT_MAX) {
602                                 what = "hd";
603                                 nr = hd;
604                                 goto unreasonable;
605                         }
606                         if (sec == 0 || sec > USHRT_MAX) {
607                                 what = "sec";
608                                 nr = sec;
609                                 goto unreasonable;
610                         }
611                         if (mediasize == 0)
612                                 warnx("unit size unknown, no sector count "
613                                     "check could be done");
614                         else if ((uintmax_t)(cyl + alt) * sec * hd >
615                                  (uintmax_t)mediasize / sectorsize) {
616                                 warnx("%s, line %d: sector count %ju exceeds "
617                                     "unit size %ju",
618                                     file, line + 1,
619                                     (uintmax_t)(cyl + alt) * sec * hd,
620                                     (uintmax_t)mediasize / sectorsize);
621                                 fclose(fp);
622                                 return (1);
623                         }
624                         sl1.sl_pcylinders = cyl + alt;
625                         sl1.sl_ncylinders = cyl;
626                         sl1.sl_acylinders = alt;
627                         sl1.sl_nsectors = sec;
628                         sl1.sl_ntracks = hd;
629                         memset(sl1.sl_text, 0, sizeof(sl1.sl_text));
630                         snprintf(sl1.sl_text, sizeof(sl1.sl_text),
631                             "%s cyl %u alt %u hd %u sec %u",
632                             text, cyl, alt, hd, sec);
633                         continue;
634                 }
635                 if (strncmp(bp, "volume name:", strlen("volume name:")) == 0) {
636                         wantvtoc = 1; /* Volume name requires VTOC. */
637                         bp += strlen("volume name:");
638 #if SUN_VOLNAME_LEN != 8
639 # error "scanf field width does not match SUN_VOLNAME_LEN"
640 #endif
641                         /*
642                          * We set the field length to one more than
643                          * SUN_VOLNAME_LEN to allow detecting an
644                          * overflow.
645                          */
646                         memset(volname, 0, sizeof volname);
647                         rv = sscanf(bp, " %9[^\n]", volname);
648                         if (rv != 1) {
649                                 /* Clear the volume name. */
650                                 memset(sl1.sl_vtoc_volname, 0,
651                                     SUN_VOLNAME_LEN);
652                         } else {
653                                 memcpy(sl1.sl_vtoc_volname, volname,
654                                     SUN_VOLNAME_LEN);
655                                 if (volname[SUN_VOLNAME_LEN] != '\0')
656                                         warnx(
657 "%s, line %d: volume name longer than %d characters, truncating",
658                                             file, line + 1, SUN_VOLNAME_LEN);
659                         }
660                         continue;
661                 }
662                 if (strlen(bp) < 2 || bp[1] != ':') {
663                         line++;
664                         continue;
665                 }
666                 rv = sscanf(bp, "%c: %30s %30s %30s %30s",
667                     &part, size, offset, tag, flag);
668                 if (rv < 3) {
669                 syntaxerr:
670                         warnx("%s: syntax error on line %d",
671                             file, line + 1);
672                         fclose(fp);
673                         return (1);
674                 }
675                 if (parse_size(&sl1, part - 'a', size) ||
676                     parse_offset(&sl1, part - 'a', offset))
677                         goto syntaxerr;
678                 if (rv > 3) {
679                         wantvtoc = 1;
680                         if (rv == 5 && parse_flag(&sl1, part - 'a', flag))
681                                 goto syntaxerr;
682                         if (parse_tag(&sl1, part - 'a', tag))
683                                 goto syntaxerr;
684                 }
685                 line++;
686         }
687         fclose(fp);
688         if (wantvtoc) {
689                 sl1.sl_vtoc_sane = SUN_VTOC_SANE;
690                 sl1.sl_vtoc_vers = SUN_VTOC_VERSION;
691                 sl1.sl_vtoc_nparts = SUN_NPART;
692         } else {
693                 sl1.sl_vtoc_sane = 0;
694                 sl1.sl_vtoc_vers = 0;
695                 sl1.sl_vtoc_nparts = 0;
696                 bzero(&sl1.sl_vtoc_map, sizeof(sl1.sl_vtoc_map));
697         }
698         *sl = sl1;
699         return (check_label(sl));
700 }
701
702 static int
703 parse_size(struct sun_disklabel *sl, int part, char *size)
704 {
705         uintmax_t nsectors;
706         uintmax_t total;
707         uintmax_t n;
708         char *p;
709         int i;
710
711         nsectors = 0;
712         n = strtoumax(size, &p, 10);
713         if (*p != '\0') {
714                 if (strcmp(size, "*") == 0) {
715                         total = sl->sl_ncylinders * sl->sl_ntracks *
716                             sl->sl_nsectors;
717                         for (i = 0; i < part; i++) {
718                                 if (i == 2)
719                                         continue;
720                                 nsectors += sl->sl_part[i].sdkp_nsectors;
721                         }
722                         n = total - nsectors;
723                 } else if (p[1] == '\0' && (p[0] == 'C' || p[0] == 'c')) {
724                         n = n * sl->sl_ntracks * sl->sl_nsectors;
725                 } else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) {
726                         n = roundup((n * 1024) / 512,
727                             sl->sl_ntracks * sl->sl_nsectors);
728                 } else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) {
729                         n = roundup((n * 1024 * 1024) / 512,
730                             sl->sl_ntracks * sl->sl_nsectors);
731                 } else if (p[1] == '\0' && (p[0] == 'S' || p[0] == 's')) {
732                         /* size in sectors, no action neded */
733                 } else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) {
734                         n = roundup((n * 1024 * 1024 * 1024) / 512,
735                             sl->sl_ntracks * sl->sl_nsectors);
736                 } else
737                         return (-1);
738         } else if (cflag) {
739                 n = n * sl->sl_ntracks * sl->sl_nsectors;
740         }
741         sl->sl_part[part].sdkp_nsectors = n;
742         return (0);
743 }
744
745 static int
746 parse_offset(struct sun_disklabel *sl, int part, char *offset)
747 {
748         uintmax_t nsectors;
749         uintmax_t n;
750         char *p;
751         int i;
752
753         nsectors = 0;
754         n = strtoumax(offset, &p, 10);
755         if (*p != '\0') {
756                 if (strcmp(offset, "*") == 0) {
757                         for (i = 0; i < part; i++) {
758                                 if (i == 2)
759                                         continue;
760                                 nsectors += sl->sl_part[i].sdkp_nsectors;
761                         }
762                         n = nsectors / (sl->sl_nsectors * sl->sl_ntracks);
763                 } else
764                         return (-1);
765         }
766         sl->sl_part[part].sdkp_cyloffset = n;
767         return (0);
768 }
769
770 static void
771 print_label(struct sun_disklabel *sl, const char *disk, FILE *out)
772 {
773         int i, j;
774         int havevtoc;
775         uintmax_t secpercyl;
776         /* Long enough to hex-encode each character. */
777         char volname[4 * SUN_VOLNAME_LEN + 1];
778
779         havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE;
780         secpercyl = sl->sl_nsectors * sl->sl_ntracks;
781
782         fprintf(out,
783 "# /dev/%s:\n"
784 "text: %s\n"
785 "bytes/sector: %d\n"
786 "sectors/cylinder: %ju\n",
787             disk,
788             sl->sl_text,
789             sectorsize,
790             secpercyl);
791         if (eflag)
792                 fprintf(out,
793                     "# max sectors/unit (including alt cylinders): %ju\n",
794                     (uintmax_t)mediasize / sectorsize);
795         fprintf(out,
796 "sectors/unit: %ju\n",
797             secpercyl * sl->sl_ncylinders);
798         if (havevtoc && sl->sl_vtoc_volname[0] != '\0') {
799                 for (i = j = 0; i < SUN_VOLNAME_LEN; i++) {
800                         if (sl->sl_vtoc_volname[i] == '\0')
801                                 break;
802                         if (isprint(sl->sl_vtoc_volname[i]))
803                                 volname[j++] = sl->sl_vtoc_volname[i];
804                         else
805                                 j += sprintf(volname + j, "\\x%02X",
806                                     sl->sl_vtoc_volname[i]);
807                 }
808                 volname[j] = '\0';
809                 fprintf(out, "volume name: %s\n", volname);
810         }
811         fprintf(out,
812 "\n"
813 "%d partitions:\n"
814 "#\n",
815             SUN_NPART);
816         if (!hflag) {
817                 fprintf(out, "# Size is in %s.", cflag? "cylinders": "sectors");
818                 if (eflag)
819                         fprintf(out,
820 "  Use %%d%c, %%dK, %%dM or %%dG to specify in %s,\n"
821 "# kilobytes, megabytes or gigabytes respectively, or '*' to specify rest of\n"
822 "# disk.\n",
823                             cflag? 's': 'c',
824                             cflag? "sectors": "cylinders");
825                 else
826                         putc('\n', out);
827                 fprintf(out, "# Offset is in cylinders.");
828                 if (eflag)
829                         fprintf(out,
830 "  Use '*' to calculate offsets automatically.\n"
831 "#\n");
832                 else
833                         putc('\n', out);
834         }
835         if (havevtoc)
836                 fprintf(out,
837 "#    size       offset      tag         flag\n"
838 "#    ---------- ----------  ----------  ----\n"
839                         );
840         else
841                 fprintf(out,
842 "#    size       offset\n"
843 "#    ---------- ----------\n"
844                         );
845
846         for (i = 0; i < SUN_NPART; i++) {
847                 if (sl->sl_part[i].sdkp_nsectors == 0)
848                         continue;
849                 if (hflag) {
850                         fprintf(out, "  %c: %10s",
851                             'a' + i,
852                             make_h_number((uintmax_t)
853                                 sl->sl_part[i].sdkp_nsectors * 512));
854                         fprintf(out, " %10s",
855                             make_h_number((uintmax_t)
856                                 sl->sl_part[i].sdkp_cyloffset * 512
857                                 * secpercyl));
858                 } else {
859                         fprintf(out, "  %c: %10ju %10u",
860                             'a' + i,
861                             sl->sl_part[i].sdkp_nsectors / (cflag? secpercyl: 1),
862                             sl->sl_part[i].sdkp_cyloffset);
863                 }
864                 if (havevtoc)
865                         fprintf(out, " %11s %5s",
866                             tagname(sl->sl_vtoc_map[i].svtoc_tag),
867                             flagname(sl->sl_vtoc_map[i].svtoc_flag));
868                 putc('\n', out);
869         }
870 }
871
872 static void
873 usage(void)
874 {
875
876         fprintf(stderr, "usage:"
877 "\t%s [-r] [-c | -h] disk\n"
878 "\t\t(to read label)\n"
879 "\t%s -B [-b boot1] [-n] disk\n"
880 "\t\t(to install boot program only)\n"
881 "\t%s -R [-B [-b boot1]] [-r] [-n] [-c] disk protofile\n"
882 "\t\t(to restore label)\n"
883 "\t%s -e [-B [-b boot1]] [-r] [-n] [-c] disk\n"
884 "\t\t(to edit label)\n"
885 "\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n"
886 "\t\t(to write default label)\n",
887              __progname,
888              __progname,
889              __progname,
890              __progname,
891              __progname);
892         exit(1);
893 }
894
895 /*
896  * Return VTOC tag and flag names for tag or flag ID, resp.
897  */
898 static const char *
899 tagname(unsigned int tag)
900 {
901         static char buf[32];
902         size_t i;
903         struct tags *tp;
904
905         for (i = 0, tp = knowntags;
906              i < sizeof(knowntags) / sizeof(struct tags);
907              i++, tp++)
908                 if (tp->id == tag)
909                         return (tp->name);
910
911         sprintf(buf, "%u", tag);
912
913         return (buf);
914 }
915
916 static const char *
917 flagname(unsigned int flag)
918 {
919         static char buf[32];
920         size_t i;
921         struct tags *tp;
922
923         for (i = 0, tp = knownflags;
924              i < sizeof(knownflags) / sizeof(struct tags);
925              i++, tp++)
926                 if (tp->id == flag)
927                         return (tp->name);
928
929         sprintf(buf, "%u", flag);
930
931         return (buf);
932 }
933
934 static unsigned int
935 parse_tag(struct sun_disklabel *sl, int part, const char *tag)
936 {
937         struct tags *tp;
938         char *endp;
939         size_t i;
940         unsigned long l;
941
942         for (i = 0, tp = knowntags;
943              i < sizeof(knowntags) / sizeof(struct tags);
944              i++, tp++)
945                 if (strcmp(tp->name, tag) == 0) {
946                         sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)tp->id;
947                         return (0);
948                 }
949
950         l = strtoul(tag, &endp, 0);
951         if (*tag != '\0' && *endp == '\0') {
952                 sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)l;
953                 return (0);
954         }
955
956         return (-1);
957 }
958
959 static unsigned int
960 parse_flag(struct sun_disklabel *sl, int part, const char *flag)
961 {
962         struct tags *tp;
963         char *endp;
964         size_t i;
965         unsigned long l;
966
967         for (i = 0, tp = knownflags;
968              i < sizeof(knownflags) / sizeof(struct tags);
969              i++, tp++)
970                 if (strcmp(tp->name, flag) == 0) {
971                         sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)tp->id;
972                         return (0);
973                 }
974
975         l = strtoul(flag, &endp, 0);
976         if (*flag != '\0' && *endp == '\0') {
977                 sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)l;
978                 return (0);
979         }
980
981         return (-1);
982 }
983
984 /*
985  * Convert argument into `human readable' byte number form.
986  */
987 static const char *
988 make_h_number(uintmax_t u)
989 {
990         static char buf[32];
991         double d;
992
993         if (u == 0) {
994                 strcpy(buf, "0B");
995         } else if (u > 2000000000UL) {
996                 d = (double)u / 1e9;
997                 sprintf(buf, "%.1fG", d);
998         } else if (u > 2000000UL) {
999                 d = (double)u / 1e6;
1000                 sprintf(buf, "%.1fM", d);
1001         } else {
1002                 d = (double)u / 1e3;
1003                 sprintf(buf, "%.1fK", d);
1004         }
1005
1006         return (buf);
1007 }