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