Merge branch 'vendor/OPENSSL'
[dragonfly.git] / sbin / disklabel64 / disklabel64.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Copyright (c) 1987, 1993
36  *      The Regents of the University of California.  All rights reserved.
37  *
38  * This code is derived from software contributed to Berkeley by
39  * Symmetric Computer Systems.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  *
65  * @(#)disklabel.c      1.2 (Symmetric) 11/28/85
66  * @(#)disklabel.c      8.2 (Berkeley) 1/7/94
67  * $FreeBSD: src/sbin/disklabel/disklabel.c,v 1.28.2.15 2003/01/24 16:18:16 des Exp $
68  */
69
70 #include <sys/param.h>
71 #include <sys/file.h>
72 #include <sys/stat.h>
73 #include <sys/wait.h>
74 #define DKTYPENAMES
75 #include <sys/disklabel64.h>
76 #include <sys/diskslice.h>
77 #include <sys/diskmbr.h>
78 #include <sys/dtype.h>
79 #include <sys/sysctl.h>
80 #include <disktab.h>
81 #include <fstab.h>
82
83 #include <vfs/ufs/dinode.h>
84 #include <vfs/ufs/fs.h>
85
86 #include <unistd.h>
87 #include <string.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <signal.h>
91 #include <stdarg.h>
92 #include <stddef.h>
93 #include <ctype.h>
94 #include <err.h>
95 #include <errno.h>
96 #include <uuid.h>
97 #include "pathnames.h"
98
99 extern uint32_t crc32(const void *buf, size_t size);
100
101 /*
102  * Disklabel64: read and write 64 bit disklabels.
103  * The label is usually placed on one of the first sectors of the disk.
104  * Many machines also place a bootstrap in the same area,
105  * in which case the label is embedded in the bootstrap.
106  * The bootstrap source must leave space at the proper offset
107  * for the label on such machines.
108  */
109
110 #define LABELSIZE       ((sizeof(struct disklabel64) + 4095) & ~4095)
111 #define BOOTSIZE        32768
112
113 /* FIX!  These are too low, but are traditional */
114 #define DEFAULT_NEWFS_BLOCK  8192U
115 #define DEFAULT_NEWFS_FRAG   1024U
116 #define DEFAULT_NEWFS_CPG    16U
117
118 #define BIG_NEWFS_BLOCK  16384U
119 #define BIG_NEWFS_FRAG   2048U
120 #define BIG_NEWFS_CPG    64U
121
122 void    makelabel(const char *, const char *, struct disklabel64 *);
123 int     writelabel(int, struct disklabel64 *);
124 void    l_perror(const char *);
125 struct disklabel64 *readlabel(int);
126 struct disklabel64 *makebootarea(int);
127 void    display(FILE *, const struct disklabel64 *);
128 int     edit(struct disklabel64 *, int);
129 int     editit(void);
130 char    *skip(char *);
131 char    *word(char *);
132 int     getasciilabel(FILE *, struct disklabel64 *);
133 int     getasciipartspec(char *, struct disklabel64 *, int, int, uint32_t);
134 int     getasciipartuuid(char *, struct disklabel64 *, int, int, uint32_t);
135 int     checklabel(struct disklabel64 *);
136 void    Warning(const char *, ...) __printflike(1, 2);
137 void    usage(void);
138 struct disklabel64 *getvirginlabel(void);
139
140 #define DEFEDITOR       _PATH_VI
141 #define streq(a,b)      (strcmp(a,b) == 0)
142
143 char    *dkname;
144 char    *specname;
145 char    tmpfil[] = PATH_TMPFILE;
146
147 struct  disklabel64 lab;
148
149 #define MAX_PART ('z')
150 #define MAX_NUM_PARTS (1 + MAX_PART - 'a')
151 char    part_size_type[MAX_NUM_PARTS];
152 char    part_offset_type[MAX_NUM_PARTS];
153 int     part_set[MAX_NUM_PARTS];
154
155 int     installboot;    /* non-zero if we should install a boot program */
156 int     boot1size;
157 int     boot1lsize;
158 int     boot2size;
159 char    *boot1buf;
160 char    *boot2buf;
161 char    *boot1path;
162 char    *boot2path;
163
164 enum    {
165         UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT
166 } op = UNSPEC;
167
168 int     rflag;
169 int     Vflag;
170 int     disable_write;   /* set to disable writing to disk label */
171 u_int32_t slice_start_lba;
172
173 #ifdef DEBUG
174 int     debug;
175 #define OPTIONS "BNRWb:denrs:Vw"
176 #else
177 #define OPTIONS "BNRWb:enrs:Vw"
178 #endif
179
180 int
181 main(int argc, char *argv[])
182 {
183         struct disklabel64 *lp;
184         FILE *t;
185         int ch, f = 0, flag, error = 0;
186         char *name = NULL;
187
188         while ((ch = getopt(argc, argv, OPTIONS)) != -1)
189                 switch (ch) {
190                         case 'B':
191                                 ++installboot;
192                                 break;
193                         case 'b':
194                                 boot1path = optarg;
195                                 break;
196
197                         case 's':
198                                 boot2path = optarg;
199                                 break;
200                         case 'N':
201                                 if (op != UNSPEC)
202                                         usage();
203                                 op = NOWRITE;
204                                 break;
205                         case 'n':
206                                 disable_write = 1;
207                                 break;
208                         case 'R':
209                                 if (op != UNSPEC)
210                                         usage();
211                                 op = RESTORE;
212                                 break;
213                         case 'W':
214                                 if (op != UNSPEC)
215                                         usage();
216                                 op = WRITEABLE;
217                                 break;
218                         case 'e':
219                                 if (op != UNSPEC)
220                                         usage();
221                                 op = EDIT;
222                                 break;
223                         case 'V':
224                                 ++Vflag;
225                                 break;
226                         case 'r':
227                                 ++rflag;
228                                 break;
229                         case 'w':
230                                 if (op != UNSPEC)
231                                         usage();
232                                 op = WRITE;
233                                 break;
234 #ifdef DEBUG
235                         case 'd':
236                                 debug++;
237                                 break;
238 #endif
239                         case '?':
240                         default:
241                                 usage();
242                 }
243         argc -= optind;
244         argv += optind;
245         if (installboot) {
246                 rflag++;
247                 if (op == UNSPEC)
248                         op = WRITEBOOT;
249         } else {
250                 if (op == UNSPEC)
251                         op = READ;
252                 boot1path = NULL;
253                 boot2path = NULL;
254         }
255         if (argc < 1)
256                 usage();
257
258         dkname = getdevpath(argv[0], 0);
259         specname = dkname;
260         f = open(specname, op == READ ? O_RDONLY : O_RDWR);
261         if (f < 0)
262                 err(4, "%s", specname);
263
264         switch(op) {
265
266         case UNSPEC:
267                 break;
268
269         case EDIT:
270                 if (argc != 1)
271                         usage();
272                 lp = readlabel(f);
273                 error = edit(lp, f);
274                 break;
275
276         case NOWRITE:
277                 flag = 0;
278                 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
279                         err(4, "ioctl DIOCWLABEL");
280                 break;
281
282         case READ:
283                 if (argc != 1)
284                         usage();
285                 lp = readlabel(f);
286                 display(stdout, lp);
287                 error = checklabel(lp);
288                 break;
289
290         case RESTORE:
291                 if (installboot && argc == 3) {
292                         makelabel(argv[2], 0, &lab);
293                         argc--;
294
295                         /*
296                          * We only called makelabel() for its side effect
297                          * of setting the bootstrap file names.  Discard
298                          * all changes to `lab' so that all values in the
299                          * final label come from the ASCII label.
300                          */
301                         bzero((char *)&lab, sizeof(lab));
302                 }
303                 if (argc != 2)
304                         usage();
305                 if (!(t = fopen(argv[1], "r")))
306                         err(4, "%s", argv[1]);
307                 if (!getasciilabel(t, &lab))
308                         exit(1);
309                 lp = makebootarea(f);
310                 bcopy(&lab.d_magic, &lp->d_magic,
311                       sizeof(lab) - offsetof(struct disklabel64, d_magic));
312                 error = writelabel(f, lp);
313                 break;
314
315         case WRITE:
316                 if (argc == 3) {
317                         name = argv[2];
318                         argc--;
319                 }
320                 if (argc != 2)
321                         usage();
322                 makelabel(argv[1], name, &lab);
323                 lp = makebootarea(f);
324                 bcopy(&lab.d_magic, &lp->d_magic,
325                       sizeof(lab) - offsetof(struct disklabel64, d_magic));
326                 if (checklabel(lp) == 0)
327                         error = writelabel(f, lp);
328                 break;
329
330         case WRITEABLE:
331                 flag = 1;
332                 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
333                         err(4, "ioctl DIOCWLABEL");
334                 break;
335
336         case WRITEBOOT:
337         {
338                 struct disklabel64 tlab;
339
340                 lp = readlabel(f);
341                 tlab = *lp;
342                 if (argc == 2)
343                         makelabel(argv[1], 0, &lab);
344                 lp = makebootarea(f);
345                 bcopy(&tlab.d_magic, &lp->d_magic,
346                       sizeof(tlab) - offsetof(struct disklabel64, d_magic));
347                 if (checklabel(lp) == 0)
348                         error = writelabel(f, lp);
349                 break;
350         }
351         }
352         exit(error);
353 }
354
355 /*
356  * Construct a prototype disklabel from /etc/disktab.  As a side
357  * effect, set the names of the primary and secondary boot files
358  * if specified.
359  */
360 void
361 makelabel(const char *type, const char *name, struct disklabel64 *lp)
362 {
363         struct disklabel64 *dp;
364
365         if (strcmp(type, "auto") == 0)
366                 dp = getvirginlabel();
367         else
368                 dp = NULL;
369         if (dp == NULL)
370                 errx(1, "%s: unknown disk type", type);
371         *lp = *dp;
372
373         /*
374          * NOTE: boot control files may no longer be specified in disktab.
375          */
376         if (name)
377                 strncpy(lp->d_packname, name, sizeof(lp->d_packname));
378 }
379
380 int
381 writelabel(int f, struct disklabel64 *lp)
382 {
383         struct disklabel64 *blp;
384         int flag;
385         int r;
386         size_t lpsize;
387         size_t lpcrcsize;
388
389         lpsize = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]);
390         lpcrcsize = lpsize - offsetof(struct disklabel64, d_magic);
391
392         if (disable_write) {
393                 Warning("write to disk label suppressed - label was as follows:");
394                 display(stdout, lp);
395                 return (0);
396         } else {
397                 lp->d_magic = DISKMAGIC64;
398                 lp->d_crc = 0;
399                 lp->d_crc = crc32(&lp->d_magic, lpcrcsize);
400                 if (rflag) {
401                         /*
402                          * Make sure the boot area is not too large
403                          */
404                         if (boot2buf) {
405                                 int lpbsize = (int)(lp->d_pbase - lp->d_bbase);
406                                 if (lp->d_pbase == 0) {
407                                         errx(1, "no space was set aside in "
408                                                 "the disklabel for boot2!");
409                                 }
410                                 if (boot2size > lpbsize) {
411                                         errx(1, "label did not reserve enough "
412                                                 "space for boot!  %d/%d",
413                                              boot2size, lpbsize);
414                                 }
415                         }
416
417                         /*
418                          * First set the kernel disk label,
419                          * then write a label to the raw disk.
420                          * If the SDINFO ioctl fails because it is
421                          * unimplemented, keep going; otherwise, the kernel
422                          * consistency checks may prevent us from changing
423                          * the current (in-core) label.
424                          */
425                         if (ioctl(f, DIOCSDINFO64, lp) < 0 &&
426                                 errno != ENODEV && errno != ENOTTY) {
427                                 l_perror("ioctl DIOCSDINFO");
428                                 return (1);
429                         }
430                         lseek(f, (off_t)0, SEEK_SET);
431
432                         /*
433                          * The disklabel embeds areas which we may not
434                          * have wanted to change.  Merge those areas in
435                          * from disk.
436                          */
437                         blp = makebootarea(f);
438                         if (blp != lp) {
439                                 bcopy(&lp->d_magic, &blp->d_magic,
440                                       sizeof(*lp) -
441                                       offsetof(struct disklabel64, d_magic));
442                         }
443                         
444                         /*
445                          * write enable label sector before write
446                          * (if necessary), disable after writing.
447                          */
448                         flag = 1;
449                         if (ioctl(f, DIOCWLABEL, &flag) < 0)
450                                 warn("ioctl DIOCWLABEL");
451
452                         r = write(f, boot1buf, boot1lsize);
453                         if (r != (ssize_t)boot1lsize) {
454                                 warn("write");
455                                 return (1);
456                         }
457                         /*
458                          * Output the remainder of the disklabel
459                          */
460                         if (boot2buf) {
461                                 lseek(f, lp->d_bbase, 0);
462                                 r = write(f, boot2buf, boot2size);
463                                 if (r != boot2size) {
464                                         warn("write");
465                                         return(1);
466                                 }
467                         }
468                         flag = 0;
469                         ioctl(f, DIOCWLABEL, &flag);
470                 } else if (ioctl(f, DIOCWDINFO64, lp) < 0) {
471                         l_perror("ioctl DIOCWDINFO64");
472                         return (1);
473                 }
474         }
475         return (0);
476 }
477
478 void
479 l_perror(const char *s)
480 {
481         switch (errno) {
482
483         case ESRCH:
484                 warnx("%s: no disk label on disk;", s);
485                 fprintf(stderr, "add \"-r\" to install initial label\n");
486                 break;
487
488         case EINVAL:
489                 warnx("%s: label magic number or checksum is wrong!", s);
490                 fprintf(stderr, "(disklabel or kernel is out of date?)\n");
491                 break;
492
493         case EBUSY:
494                 warnx("%s: open partition would move or shrink", s);
495                 break;
496
497         case ENOATTR:
498                 warnx("%s: the disk already has a label of a different type,\n"
499                       "probably a 32 bit disklabel.  It must be cleaned out "
500                       "first.\n", s);
501                 break;
502
503         default:
504                 warn(NULL);
505                 break;
506         }
507 }
508
509 /*
510  * Fetch disklabel for disk.
511  * Use ioctl to get label unless -r flag is given.
512  */
513 struct disklabel64 *
514 readlabel(int f)
515 {
516         struct disklabel64 *lp;
517         u_int32_t savecrc;
518         size_t lpcrcsize;
519
520         if (rflag) {
521                 /*
522                  * Allocate space for the label.  The boot1 code, if any,
523                  * is embedded in the label.  The label overlaps the boot1
524                  * code.
525                  */
526                 lp = makebootarea(f);
527                 lpcrcsize = offsetof(struct disklabel64,
528                                      d_partitions[lp->d_npartitions]) -
529                             offsetof(struct disklabel64, d_magic);
530                 savecrc = lp->d_crc;
531                 lp->d_crc = 0;
532                 if (lp->d_magic != DISKMAGIC64)
533                         errx(1, "bad pack magic number");
534                 if (lp->d_npartitions > MAXPARTITIONS64 ||
535                     savecrc != crc32(&lp->d_magic, lpcrcsize)
536                 ) {
537                         errx(1, "corrupted disklabel64");
538                 }
539                 lp->d_crc = savecrc;
540         } else {
541                 /*
542                  * Just use a static structure to hold the label.  Note
543                  * that DIOCSDINFO64 does not overwrite the boot1 area
544                  * even though it is part of the disklabel64 structure.
545                  */
546                 lp = &lab;
547                 if (Vflag) {
548                         if (ioctl(f, DIOCGDVIRGIN64, lp) < 0) {
549                                 l_perror("ioctl DIOCGDVIRGIN64");
550                                 exit(4);
551                         }
552                 } else {
553                         if (ioctl(f, DIOCGDINFO64, lp) < 0) {
554                                 l_perror("ioctl DIOCGDINFO64");
555                                 exit(4);
556                         }
557                 }
558         }
559         return (lp);
560 }
561
562 /*
563  * Construct a boot area for boot1 and boot2 and return the location of
564  * the label within the area.  The caller will overwrite the label so
565  * we don't actually have to read it.
566  */
567 struct disklabel64 *
568 makebootarea(int f)
569 {
570         struct disklabel64 *lp;
571         struct partinfo info;
572         u_int32_t secsize;
573         struct stat st;
574         int fd;
575         int r;
576
577         if (ioctl(f, DIOCGPART, &info) == 0)
578                 secsize = info.media_blksize;
579         else
580                 secsize = 512;
581
582         if (boot1buf == NULL) {
583                 size_t rsize;
584
585                 rsize = roundup2(sizeof(struct disklabel64), secsize);
586                 boot1size = offsetof(struct disklabel64, d_magic);
587                 boot1lsize = rsize;
588                 boot1buf = malloc(rsize);
589                 bzero(boot1buf, rsize);
590                 r = read(f, boot1buf, rsize);
591                 if (r != (int)rsize)
592                         err(4, "%s", specname);
593         }
594         lp = (void *)boot1buf;
595
596         if (installboot == 0)
597                 return(lp);
598
599         if (boot2buf == NULL) {
600                 boot2size = 32768;
601                 boot2buf = malloc(boot2size);
602                 bzero(boot2buf, boot2size);
603         }
604
605         /*
606          * If installing the boot code, read it into the appropriate portions
607          * of the buffer(s)
608          */
609         if (boot1path == NULL)
610                 asprintf(&boot1path, "%s/boot1_64", _PATH_BOOTDIR);
611         if (boot2path == NULL)
612                 asprintf(&boot2path, "%s/boot2_64", _PATH_BOOTDIR);
613
614         if ((fd = open(boot1path, O_RDONLY)) < 0)
615                 err(4, "%s", boot1path);
616         if (fstat(fd, &st) < 0)
617                 err(4, "%s", boot1path);
618         if (st.st_size > boot1size)
619                 err(4, "%s must be exactly %d bytes!", boot1path, boot1size);
620         if (read(fd, boot1buf, boot1size) != boot1size)
621                 err(4, "%s must be exactly %d bytes!", boot1path, boot1size);
622         close(fd);
623
624         if ((fd = open(boot2path, O_RDONLY)) < 0)
625                 err(4, "%s", boot2path);
626         if (fstat(fd, &st) < 0)
627                 err(4, "%s", boot2path);
628         if (st.st_size > boot2size)
629                 err(4, "%s must be <= %d bytes!", boot2path, boot2size);
630         if ((r = read(fd, boot2buf, boot2size)) < 1)
631                 err(4, "%s is empty!", boot2path);
632         boot2size = roundup2(r, secsize);
633         close(fd);
634
635         /*
636          * XXX dangerously dedicated support goes here XXX
637          */
638         return (lp);
639 }
640
641 void
642 display(FILE *f, const struct disklabel64 *lp)
643 {
644         const struct partition64 *pp;
645         char *str;
646         unsigned int part;
647         int didany;
648         uint32_t blksize;
649
650         /*
651          * Use a human readable block size if possible.  This is for
652          * display and editing purposes only.
653          */
654         if (lp->d_align > 1024)
655                 blksize = 1024;
656         else
657                 blksize = lp->d_align;
658
659         fprintf(f, "# %s:\n", specname);
660         fprintf(f, "#\n");
661         fprintf(f, "# Informational fields calculated from the above\n");
662         fprintf(f, "# All byte equivalent offsets must be aligned\n");
663         fprintf(f, "#\n");
664         fprintf(f, "# boot space: %10ju bytes\n",
665                 (intmax_t)(lp->d_pbase - lp->d_bbase));
666         fprintf(f, "# data space: %10ju blocks\t# %6.2f MB (%ju bytes)\n",
667                         (intmax_t)(lp->d_pstop - lp->d_pbase) / blksize,
668                         (double)(lp->d_pstop - lp->d_pbase) / 1024.0 / 1024.0,
669                         (intmax_t)(lp->d_pstop - lp->d_pbase));
670         fprintf(f, "#\n");
671         fprintf(f, "# NOTE: If the partition data base looks odd it may be\n");
672         fprintf(f, "#       physically aligned instead of slice-aligned\n");
673         fprintf(f, "#\n");
674
675         uuid_to_string(&lp->d_stor_uuid, &str, NULL);
676         fprintf(f, "diskid: %s\n", str ? str : "<unknown>");
677         free(str);
678
679         fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
680                 lp->d_packname);
681         fprintf(f, "boot2 data base:      0x%012jx\n", (intmax_t)lp->d_bbase);
682         fprintf(f, "partitions data base: 0x%012jx\n", (intmax_t)lp->d_pbase);
683         fprintf(f, "partitions data stop: 0x%012jx\n", (intmax_t)lp->d_pstop);
684         fprintf(f, "backup label:         0x%012jx\n", (intmax_t)lp->d_abase);
685         fprintf(f, "total size:           0x%012jx\t# %6.2f MB\n",
686                 (intmax_t)lp->d_total_size,
687                 (double)lp->d_total_size / 1024.0 / 1024.0);
688         fprintf(f, "alignment: %u\n", lp->d_align);
689         fprintf(f, "display block size: %u\t# for partition display only\n",
690                 blksize);
691
692         fprintf(f, "\n");
693         fprintf(f, "%u partitions:\n", lp->d_npartitions);
694         fprintf(f, "#          size     offset    fstype   fsuuid\n");
695         didany = 0;
696         for (part = 0; part < lp->d_npartitions; part++) {
697                 pp = &lp->d_partitions[part];
698                 const u_long onemeg = 1024 * 1024;
699
700                 if (pp->p_bsize == 0)
701                         continue;
702                 didany = 1;
703                 fprintf(f, "  %c: ", 'a' + part);
704
705                 if (pp->p_bsize % lp->d_align)
706                     fprintf(f, "%10s  ", "ILLEGAL");
707                 else
708                     fprintf(f, "%10ju ", (intmax_t)pp->p_bsize / blksize);
709
710                 if ((pp->p_boffset - lp->d_pbase) % lp->d_align)
711                     fprintf(f, "%10s  ", "ILLEGAL");
712                 else
713                     fprintf(f, "%10ju  ",
714                             (intmax_t)(pp->p_boffset - lp->d_pbase) / blksize);
715
716                 if (pp->p_fstype < FSMAXTYPES)
717                         fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
718                 else
719                         fprintf(f, "%8d", pp->p_fstype);
720                 fprintf(f, "\t# %11.3fMB", (double)pp->p_bsize / onemeg);
721                 fprintf(f, "\n");
722         }
723         for (part = 0; part < lp->d_npartitions; part++) {
724                 pp = &lp->d_partitions[part];
725
726                 if (pp->p_bsize == 0)
727                         continue;
728
729                 if (uuid_is_nil(&lp->d_stor_uuid, NULL) == 0) {
730                         fprintf(f, "  %c-stor_uuid: ", 'a' + part);
731                         str = NULL;
732                         uuid_to_string(&pp->p_stor_uuid, &str, NULL);
733                         if (str) {
734                                 fprintf(f, "%s", str);
735                                 free(str);
736                         }
737                         fprintf(f, "\n");
738                 }
739         }
740         if (didany == 0) {
741                 fprintf(f, "# EXAMPLE\n");
742                 fprintf(f, "#a:          4g          0    4.2BSD\n");
743                 fprintf(f, "#a:          *           *    4.2BSD\n");
744
745         }
746         fflush(f);
747 }
748
749 int
750 edit(struct disklabel64 *lp, int f)
751 {
752         int c, fd;
753         struct disklabel64 label;
754         FILE *fp;
755
756         if ((fd = mkstemp(tmpfil)) == -1 ||
757             (fp = fdopen(fd, "w")) == NULL) {
758                 warnx("can't create %s", tmpfil);
759                 return (1);
760         }
761         display(fp, lp);
762         fclose(fp);
763         for (;;) {
764                 if (!editit())
765                         break;
766                 fp = fopen(tmpfil, "r");
767                 if (fp == NULL) {
768                         warnx("can't reopen %s for reading", tmpfil);
769                         break;
770                 }
771                 bzero((char *)&label, sizeof(label));
772                 if (getasciilabel(fp, &label)) {
773                         *lp = label;
774                         if (writelabel(f, lp) == 0) {
775                                 fclose(fp);
776                                 unlink(tmpfil);
777                                 return (0);
778                         }
779                 }
780                 fclose(fp);
781                 printf("re-edit the label? [y]: "); fflush(stdout);
782                 c = getchar();
783                 if (c != EOF && c != (int)'\n')
784                         while (getchar() != (int)'\n')
785                                 ;
786                 if  (c == (int)'n')
787                         break;
788         }
789         unlink(tmpfil);
790         return (1);
791 }
792
793 int
794 editit(void)
795 {
796         int pid, xpid;
797         int status, omask;
798         const char *ed;
799
800         omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
801         while ((pid = fork()) < 0) {
802                 if (errno == EPROCLIM) {
803                         warnx("you have too many processes");
804                         return(0);
805                 }
806                 if (errno != EAGAIN) {
807                         warn("fork");
808                         return(0);
809                 }
810                 sleep(1);
811         }
812         if (pid == 0) {
813                 sigsetmask(omask);
814                 setgid(getgid());
815                 setuid(getuid());
816                 if ((ed = getenv("EDITOR")) == NULL)
817                         ed = DEFEDITOR;
818                 execlp(ed, ed, tmpfil, NULL);
819                 err(1, "%s", ed);
820         }
821         while ((xpid = wait(&status)) >= 0)
822                 if (xpid == pid)
823                         break;
824         sigsetmask(omask);
825         return(!status);
826 }
827
828 char *
829 skip(char *cp)
830 {
831
832         while (*cp != '\0' && isspace(*cp))
833                 cp++;
834         if (*cp == '\0' || *cp == '#')
835                 return (NULL);
836         return (cp);
837 }
838
839 char *
840 word(char *cp)
841 {
842         char c;
843
844         while (*cp != '\0' && !isspace(*cp) && *cp != '#')
845                 cp++;
846         if ((c = *cp) != '\0') {
847                 *cp++ = '\0';
848                 if (c != '#')
849                         return (skip(cp));
850         }
851         return (NULL);
852 }
853
854 /*
855  * Read an ascii label in from fd f,
856  * in the same format as that put out by display(),
857  * and fill in lp.
858  */
859 int
860 getasciilabel(FILE *f, struct disklabel64 *lp)
861 {
862         char *cp;
863         u_int part;
864         char *tp, line[BUFSIZ];
865         u_long v;
866         uint32_t blksize = 0;
867         uint64_t vv;
868         int lineno = 0, errors = 0;
869         char empty[] = "";
870
871         bzero(&part_set, sizeof(part_set));
872         bzero(&part_size_type, sizeof(part_size_type));
873         bzero(&part_offset_type, sizeof(part_offset_type));
874         while (fgets(line, sizeof(line) - 1, f)) {
875                 lineno++;
876                 if ((cp = strchr(line,'\n')) != NULL)
877                         *cp = '\0';
878                 cp = skip(line);
879                 if (cp == NULL)
880                         continue;
881                 tp = strchr(cp, ':');
882                 if (tp == NULL) {
883                         fprintf(stderr, "line %d: syntax error\n", lineno);
884                         errors++;
885                         continue;
886                 }
887                 *tp++ = '\0', tp = skip(tp);
888                 if (sscanf(cp, "%lu partitions", &v) == 1) {
889                         if (v == 0 || v > MAXPARTITIONS64) {
890                                 fprintf(stderr,
891                                     "line %d: bad # of partitions\n", lineno);
892                                 lp->d_npartitions = MAXPARTITIONS64;
893                                 errors++;
894                         } else
895                                 lp->d_npartitions = v;
896                         continue;
897                 }
898                 if (tp == NULL)
899                         tp = empty;
900
901                 if (streq(cp, "diskid")) {
902                         uint32_t status = 0;
903                         uuid_from_string(tp, &lp->d_stor_uuid, &status);
904                         if (status != uuid_s_ok) {
905                                 fprintf(stderr,
906                                     "line %d: %s: illegal UUID\n",
907                                     lineno, tp);
908                                 errors++;
909                         }
910                         continue;
911                 }
912                 if (streq(cp, "label")) {
913                         strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
914                         continue;
915                 }
916
917                 if (streq(cp, "alignment")) {
918                         v = strtoul(tp, NULL, 0);
919                         if (v <= 0 || (v & DEV_BMASK) != 0 || v > 1024*1024) {
920                                 fprintf(stderr,
921                                     "line %d: %s: bad alignment\n",
922                                     lineno, tp);
923                                 errors++;
924                         } else {
925                                 lp->d_align = v;
926                         }
927                         continue;
928                 }
929                 if (streq(cp, "total size")) {
930                         vv = strtoull(tp, NULL, 0);
931                         if (vv == 0 || vv == (uint64_t)-1) {
932                                 fprintf(stderr, "line %d: %s: bad %s\n",
933                                     lineno, tp, cp);
934                                 errors++;
935                         } else {
936                                 lp->d_total_size = vv;
937                         }
938                         continue;
939                 }
940                 if (streq(cp, "boot2 data base")) {
941                         vv = strtoull(tp, NULL, 0);
942                         if (vv == 0 || vv == (uint64_t)-1) {
943                                 fprintf(stderr, "line %d: %s: bad %s\n",
944                                     lineno, tp, cp);
945                                 errors++;
946                         } else {
947                                 lp->d_bbase = vv;
948                         }
949                         continue;
950                 }
951                 if (streq(cp, "partitions data base")) {
952                         vv = strtoull(tp, NULL, 0);
953                         if (vv == 0 || vv == (uint64_t)-1) {
954                                 fprintf(stderr, "line %d: %s: bad %s\n",
955                                     lineno, tp, cp);
956                                 errors++;
957                         } else {
958                                 lp->d_pbase = vv;
959                         }
960                         continue;
961                 }
962                 if (streq(cp, "partitions data stop")) {
963                         vv = strtoull(tp, NULL, 0);
964                         if (vv == 0 || vv == (uint64_t)-1) {
965                                 fprintf(stderr, "line %d: %s: bad %s\n",
966                                     lineno, tp, cp);
967                                 errors++;
968                         } else {
969                                 lp->d_pstop = vv;
970                         }
971                         continue;
972                 }
973                 if (streq(cp, "backup label")) {
974                         vv = strtoull(tp, NULL, 0);
975                         if (vv == 0 || vv == (uint64_t)-1) {
976                                 fprintf(stderr, "line %d: %s: bad %s\n",
977                                     lineno, tp, cp);
978                                 errors++;
979                         } else {
980                                 lp->d_abase = vv;
981                         }
982                         continue;
983                 }
984                 if (streq(cp, "display block size")) {
985                         v = strtoul(tp, NULL, 0);
986                         if (v <= 0 || (v & DEV_BMASK) != 0 || v > 1024*1024) {
987                                 fprintf(stderr,
988                                     "line %d: %s: bad alignment\n",
989                                     lineno, tp);
990                                 errors++;
991                         } else {
992                                 blksize = v;
993                         }
994                         continue;
995                 }
996
997                 /* the ':' was removed above */
998
999                 /*
1000                  * Handle main partition data, e.g. a:, b:, etc.
1001                  */
1002                 if (*cp < 'a' || *cp > MAX_PART) {
1003                         fprintf(stderr,
1004                             "line %d: %s: Unknown disklabel field\n", lineno,
1005                             cp);
1006                         errors++;
1007                         continue;
1008                 }
1009
1010                 /* Process a partition specification line. */
1011                 part = *cp - 'a';
1012                 if (part >= lp->d_npartitions) {
1013                         fprintf(stderr,
1014                             "line %d: partition name out of range a-%c: %s\n",
1015                             lineno, 'a' + lp->d_npartitions - 1, cp);
1016                         errors++;
1017                         continue;
1018                 }
1019
1020                 if (blksize == 0) {
1021                         fprintf(stderr, "block size to use for partition "
1022                                         "display was not specified!\n");
1023                         errors++;
1024                         continue;
1025                 }
1026
1027                 if (strcmp(cp + 1, "-stor_uuid") == 0) {
1028                         if (getasciipartuuid(tp, lp, part, lineno, blksize)) {
1029                                 errors++;
1030                                 break;
1031                         }
1032                         continue;
1033                 } else if (cp[1] == 0) {
1034                         part_set[part] = 1;
1035                         if (getasciipartspec(tp, lp, part, lineno, blksize)) {
1036                                 errors++;
1037                                 break;
1038                         }
1039                         continue;
1040                 }
1041                 fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
1042                         lineno, cp);
1043                 errors++;
1044                 continue;
1045         }
1046         errors += checklabel(lp);
1047         return (errors == 0);
1048 }
1049
1050 static
1051 int
1052 parse_field_val(char **tp, char **cp, u_int64_t *vv, int lineno)
1053 {
1054         char *tmp;
1055
1056         if (*tp == NULL || **tp == 0) {
1057                 fprintf(stderr, "line %d: too few numeric fields\n", lineno);
1058                 return(-1);
1059         }
1060         *cp = *tp;
1061         *tp = word(*cp);
1062         *vv = strtoull(*cp, &tmp, 0);
1063         if (*vv == ULLONG_MAX) {
1064                 fprintf(stderr, "line %d: illegal number\n", lineno);
1065                 return(-1);
1066         }
1067         if (tmp)
1068                 return(*tmp);
1069         else
1070                 return(0);
1071 }
1072
1073 /*
1074  * Read a partition line into partition `part' in the specified disklabel.
1075  * Return 0 on success, 1 on failure.
1076  */
1077 int
1078 getasciipartspec(char *tp, struct disklabel64 *lp, int part,
1079                  int lineno, uint32_t blksize)
1080 {
1081         struct partition64 *pp;
1082         char *cp;
1083         const char **cpp;
1084         int r;
1085         u_long v;
1086         uint64_t vv;
1087         uint64_t mpx;
1088
1089         pp = &lp->d_partitions[part];
1090         cp = NULL;
1091
1092         /*
1093          * size
1094          */
1095         r = parse_field_val(&tp, &cp, &vv, lineno);
1096         if (r < 0)
1097                 return (1);
1098
1099         mpx = 1;
1100         switch(r) {
1101         case 0:
1102                 mpx = blksize;
1103                 break;
1104         case '%':
1105                 /* mpx = 1; */
1106                 break;
1107         case '*':
1108                 mpx = 0;
1109                 break;
1110         case 't':
1111         case 'T':
1112                 mpx *= 1024ULL;
1113                 /* fall through */
1114         case 'g':
1115         case 'G':
1116                 mpx *= 1024ULL;
1117                 /* fall through */
1118         case 'm':
1119         case 'M':
1120                 mpx *= 1024ULL;
1121                 /* fall through */
1122         case 'k':
1123         case 'K':
1124                 mpx *= 1024ULL;
1125                 r = 0;                  /* eat the suffix */
1126                 break;
1127         default:
1128                 Warning("unknown size specifier '%c' (*/%%/K/M/G/T are valid)",
1129                         r);
1130                 return(1);
1131         }
1132
1133         part_size_type[part] = r;
1134         if (vv == 0 && r != '*') {
1135                 fprintf(stderr,
1136                     "line %d: %s: bad partition size (0)\n", lineno, cp);
1137                 return (1);
1138         }
1139         pp->p_bsize = vv * mpx;
1140
1141         /*
1142          * offset
1143          */
1144         r = parse_field_val(&tp, &cp, &vv, lineno);
1145         if (r < 0)
1146                 return (1);
1147         part_offset_type[part] = r;
1148         switch(r) {
1149         case '*':
1150                 pp->p_boffset = 0;
1151                 break;
1152         case 0:
1153                 pp->p_boffset = vv * blksize + lp->d_pbase;
1154                 break;
1155         default:
1156                 fprintf(stderr,
1157                     "line %d: %s: bad suffix on partition offset (%c)\n",
1158                     lineno, cp, r);
1159                 return (1);
1160         }
1161
1162         /*
1163          * fstype
1164          */
1165         if (tp == NULL) {
1166                 fprintf(stderr,
1167                     "line %d: no filesystem type was specified\n", lineno);
1168                 return(1);
1169         }
1170         cp = tp;
1171         tp = word(cp);
1172         for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++) {
1173                 if (*cpp && strcasecmp(*cpp, cp) == 0)
1174                         break;
1175         }
1176         if (*cpp != NULL) {
1177                 pp->p_fstype = cpp - fstypenames;
1178         } else {
1179                 if (isdigit(*cp))
1180                         v = strtoul(cp, NULL, 0);
1181                 else
1182                         v = FSMAXTYPES;
1183                 if (v >= FSMAXTYPES) {
1184                         fprintf(stderr,
1185                             "line %d: Warning, unknown filesystem type %s\n",
1186                             lineno, cp);
1187                         v = FS_UNUSED;
1188                 }
1189                 pp->p_fstype = v;
1190         }
1191
1192         cp = tp;
1193         if (tp) {
1194                 fprintf(stderr, "line %d: Warning, extra data on line\n",
1195                         lineno);
1196         }
1197         return(0);
1198 }
1199
1200 int
1201 getasciipartuuid(char *tp, struct disklabel64 *lp, int part,
1202                  int lineno, uint32_t blksize __unused)
1203 {
1204         struct partition64 *pp;
1205         uint32_t status;
1206         char *cp;
1207
1208         pp = &lp->d_partitions[part];
1209
1210         cp = tp;
1211         tp = word(cp);
1212         uuid_from_string(cp, &pp->p_stor_uuid, &status);
1213         if (status != uuid_s_ok) {
1214                 fprintf(stderr, "line %d: Illegal storage uuid specification\n",
1215                         lineno);
1216                 return(1);
1217         }
1218         return(0);
1219 }
1220
1221 /*
1222  * Check disklabel for errors and fill in
1223  * derived fields according to supplied values.
1224  */
1225 int
1226 checklabel(struct disklabel64 *lp)
1227 {
1228         struct partition64 *pp;
1229         int errors = 0;
1230         char part;
1231         u_int64_t total_size;
1232         u_int64_t current_offset;
1233         u_long total_percent;
1234         int seen_default_offset;
1235         int hog_part;
1236         int i, j;
1237         struct partition64 *pp2;
1238         u_int64_t off;
1239
1240         if (lp->d_align < 512 ||
1241             (lp->d_align ^ (lp->d_align - 1)) != lp->d_align * 2 - 1) {
1242                 Warning("Illegal alignment specified: %u\n", lp->d_align);
1243                 return (1);
1244         }
1245         if (lp->d_npartitions > MAXPARTITIONS64) {
1246                 Warning("number of partitions (%u) > MAXPARTITIONS (%d)",
1247                         lp->d_npartitions, MAXPARTITIONS64);
1248                 return (1);
1249         }
1250         off = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]);
1251         off = (off + lp->d_align - 1) & ~(int64_t)(lp->d_align - 1);
1252
1253         if (lp->d_bbase < off || lp->d_bbase % lp->d_align) {
1254                 Warning("illegal boot2 data base ");
1255                 return (1);
1256         }
1257
1258         /*
1259          * pbase can be unaligned slice-relative but will be
1260          * aligned physically.
1261          */
1262         if (lp->d_pbase < lp->d_bbase) {
1263                 Warning("illegal partition data base");
1264                 return (1);
1265         }
1266         if (lp->d_pstop < lp->d_pbase) {
1267                 Warning("illegal partition data stop");
1268                 return (1);
1269         }
1270         if (lp->d_pstop > lp->d_total_size) {
1271                 printf("%012jx\n%012jx\n",
1272                         (intmax_t)lp->d_pstop, (intmax_t)lp->d_total_size);
1273                 Warning("disklabel control info is beyond the total size");
1274                 return (1);
1275         }
1276         if (lp->d_abase &&
1277             (lp->d_abase < lp->d_pstop ||
1278              lp->d_abase > lp->d_total_size - off)) {
1279                 Warning("illegal backup label location");
1280                 return (1);
1281         }
1282
1283         /* first allocate space to the partitions, then offsets */
1284         total_size = 0;         /* in bytes */
1285         total_percent = 0;      /* in percent */
1286         hog_part = -1;
1287         /* find all fixed partitions */
1288         for (i = 0; i < (int)lp->d_npartitions; i++) {
1289                 pp = &lp->d_partitions[i];
1290                 if (part_set[i]) {
1291                         if (part_size_type[i] == '*') {
1292                                 if (part_offset_type[i] != '*') {
1293                                         if (total_size < pp->p_boffset)
1294                                                 total_size = pp->p_boffset;
1295                                 }
1296                                 if (hog_part != -1) {
1297                                         Warning("Too many '*' partitions (%c and %c)",
1298                                             hog_part + 'a',i + 'a');
1299                                 } else {
1300                                         hog_part = i;
1301                                 }
1302                         } else {
1303                                 off_t size;
1304
1305                                 size = pp->p_bsize;
1306                                 if (part_size_type[i] == '%') {
1307                                         /* 
1308                                          * don't count %'s yet
1309                                          */
1310                                         total_percent += size;
1311                                 } else {
1312                                         /*
1313                                          * Value has already been converted
1314                                          * to bytes.
1315                                          */
1316                                         if (size % lp->d_align != 0) {
1317                                                 Warning("partition %c's size is not properly aligned",
1318                                                         i + 'a');
1319                                         }
1320                                         total_size += size;
1321                                 }
1322                         }
1323                 }
1324         }
1325         /* handle % partitions - note %'s don't need to add up to 100! */
1326         if (total_percent != 0) {
1327                 int64_t free_space;
1328                 int64_t space_left;
1329
1330                 free_space = (int64_t)(lp->d_pstop - lp->d_pbase - total_size);
1331                 free_space &= ~(u_int64_t)(lp->d_align - 1);
1332
1333                 space_left = free_space;
1334                 if (total_percent > 100) {
1335                         fprintf(stderr,"total percentage %lu is greater than 100\n",
1336                             total_percent);
1337                         errors++;
1338                 }
1339
1340                 if (free_space > 0) {
1341                         for (i = 0; i < (int)lp->d_npartitions; i++) {
1342                                 pp = &lp->d_partitions[i];
1343                                 if (part_set[i] && part_size_type[i] == '%') {
1344                                         /* careful of overflows! and integer roundoff */
1345                                         pp->p_bsize = ((double)pp->p_bsize/100) * free_space;
1346                                         pp->p_bsize = (pp->p_bsize + lp->d_align - 1) & ~(u_int64_t)(lp->d_align - 1);
1347                                         if ((int64_t)pp->p_bsize > space_left)
1348                                                 pp->p_bsize = (u_int64_t)space_left;
1349                                         total_size += pp->p_bsize;
1350                                         space_left -= pp->p_bsize;
1351                                 }
1352                         }
1353                 } else {
1354                         fprintf(stderr, "%jd bytes available to give to "
1355                                         "'*' and '%%' partitions\n",
1356                                 (intmax_t)free_space);
1357                         errors++;
1358                         /* fix?  set all % partitions to size 0? */
1359                 }
1360         }
1361         /* give anything remaining to the hog partition */
1362         if (hog_part != -1) {
1363                 off = lp->d_pstop - lp->d_pbase - total_size;
1364                 off &= ~(u_int64_t)(lp->d_align - 1);
1365                 lp->d_partitions[hog_part].p_bsize = off;
1366                 total_size = lp->d_pstop - lp->d_pbase;
1367         }
1368
1369         /* Now set the offsets for each partition */
1370         current_offset = lp->d_pbase;
1371         seen_default_offset = 0;
1372         for (i = 0; i < (int)lp->d_npartitions; i++) {
1373                 part = 'a' + i;
1374                 pp = &lp->d_partitions[i];
1375                 if (pp->p_bsize == 0)
1376                         continue;
1377                 if (part_set[i]) {
1378                         if (part_offset_type[i] == '*') {
1379                                 pp->p_boffset = current_offset;
1380                                 seen_default_offset = 1;
1381                         } else {
1382                                 /* allow them to be out of order for old-style tables */
1383                                 if (pp->p_boffset < current_offset && 
1384                                     seen_default_offset &&
1385                                     pp->p_fstype != FS_VINUM) {
1386                                         fprintf(stderr,
1387 "Offset 0x%012jx for partition %c overlaps previous partition which ends at 0x%012jx\n",
1388                                             (intmax_t)pp->p_boffset,
1389                                             i + 'a',
1390                                             (intmax_t)current_offset);
1391                                         fprintf(stderr,
1392 "Labels with any *'s for offset must be in ascending order by sector\n");
1393                                         errors++;
1394                                 } else if (pp->p_boffset != current_offset &&
1395                                            seen_default_offset) {
1396                                         /* 
1397                                          * this may give unneeded warnings if 
1398                                          * partitions are out-of-order
1399                                          */
1400                                         Warning(
1401 "Offset 0x%012jx for partition %c doesn't match expected value 0x%012jx",
1402                                             pp->p_boffset, i + 'a',
1403                                             (intmax_t)current_offset);
1404                                 }
1405                         }
1406                         current_offset = pp->p_boffset + pp->p_bsize; 
1407                 }
1408         }
1409
1410         for (i = 0; i < (int)lp->d_npartitions; i++) {
1411                 part = 'a' + i;
1412                 pp = &lp->d_partitions[i];
1413                 if (pp->p_bsize == 0 && pp->p_boffset != 0)
1414                         Warning("partition %c: size 0, but offset 0x%012jx",
1415                                 part, (intmax_t)pp->p_boffset);
1416                 if (pp->p_bsize == 0) {
1417                         pp->p_boffset = 0;
1418                         continue;
1419                 }
1420                 if (uuid_is_nil(&pp->p_stor_uuid, NULL))
1421                         uuid_create(&pp->p_stor_uuid, NULL);
1422
1423                 if (pp->p_boffset < lp->d_pbase) {
1424                         fprintf(stderr,
1425                             "partition %c: offset out of bounds (%jd)\n",
1426                             part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1427                         errors++;
1428                 }
1429                 if (pp->p_boffset > lp->d_pstop) {
1430                         fprintf(stderr,
1431                             "partition %c: offset out of bounds (%jd)\n",
1432                             part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1433                         errors++;
1434                 }
1435                 if (pp->p_boffset + pp->p_bsize > lp->d_pstop) {
1436                         fprintf(stderr,
1437                             "partition %c: size out of bounds (%jd)\n",
1438                             part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1439                         errors++;
1440                 }
1441
1442                 /* check for overlaps */
1443                 /* this will check for all possible overlaps once and only once */
1444                 for (j = 0; j < i; j++) {
1445                         pp2 = &lp->d_partitions[j];
1446                         if (pp->p_fstype != FS_VINUM &&
1447                             pp2->p_fstype != FS_VINUM &&
1448                             part_set[i] && part_set[j]) {
1449                                 if (pp2->p_boffset < pp->p_boffset + pp->p_bsize &&
1450                                     (pp2->p_boffset + pp2->p_bsize > pp->p_boffset ||
1451                                         pp2->p_boffset >= pp->p_boffset)) {
1452                                         fprintf(stderr,"partitions %c and %c overlap!\n",
1453                                             j + 'a', i + 'a');
1454                                         errors++;
1455                                 }
1456                         }
1457                 }
1458         }
1459         for (; i < (int)lp->d_npartitions; i++) {
1460                 part = 'a' + i;
1461                 pp = &lp->d_partitions[i];
1462                 if (pp->p_bsize || pp->p_boffset)
1463                         Warning("unused partition %c: size 0x%012jx "
1464                                 "offset 0x%012jx",
1465                                 'a' + i, (intmax_t)pp->p_bsize,
1466                                 (intmax_t)pp->p_boffset);
1467         }
1468         return (errors);
1469 }
1470
1471 /*
1472  * When operating on a "virgin" disk, try getting an initial label
1473  * from the associated device driver.  This might work for all device
1474  * drivers that are able to fetch some initial device parameters
1475  * without even having access to a (BSD) disklabel, like SCSI disks,
1476  * most IDE drives, or vn devices.
1477  *
1478  * The device name must be given in its "canonical" form.
1479  */
1480 static struct disklabel64 dlab;
1481
1482 struct disklabel64 *
1483 getvirginlabel(void)
1484 {
1485         struct disklabel64 *dl = &dlab;
1486         int f;
1487
1488         if ((f = open(dkname, O_RDONLY)) == -1) {
1489                 warn("cannot open %s", dkname);
1490                 return (NULL);
1491         }
1492
1493         /*
1494          * Try to use the new get-virgin-label ioctl.  If it fails,
1495          * fallback to the old get-disk-info ioctl.
1496          */
1497         if (ioctl(f, DIOCGDVIRGIN64, dl) < 0) {
1498                 l_perror("ioctl DIOCGDVIRGIN64");
1499                 close(f);
1500                 return (NULL);
1501         }
1502         close(f);
1503         return (dl);
1504 }
1505
1506 /*VARARGS1*/
1507 void
1508 Warning(const char *fmt, ...)
1509 {
1510         va_list ap;
1511
1512         fprintf(stderr, "Warning, ");
1513         va_start(ap, fmt);
1514         vfprintf(stderr, fmt, ap);
1515         fprintf(stderr, "\n");
1516         va_end(ap);
1517 }
1518
1519 void
1520 usage(void)
1521 {
1522         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1523                 "usage: disklabel64 [-r] disk",
1524                 "\t\t(to read label)",
1525                 "       disklabel64 -w [-r] [-n] disk type [packid]",
1526                 "\t\t(to write label with existing boot program)",
1527                 "       disklabel64 -e [-r] [-n] disk",
1528                 "\t\t(to edit label)",
1529                 "       disklabel64 -R [-r] [-n] disk protofile",
1530                 "\t\t(to restore label with existing boot program)",
1531                 "       disklabel64 -B [-n] [-b boot1 -s boot2] disk [type]",
1532                 "\t\t(to install boot program with existing label)",
1533                 "       disklabel64 -w -B [-n] [-b boot1 -s boot2] disk type [packid]",
1534                 "\t\t(to write label and boot program)",
1535                 "       disklabel64 -R -B [-n] [-b boot1 -s boot2] disk protofile [type]",
1536                 "\t\t(to restore label and boot program)",
1537                 "       disklabel64 [-NW] disk",
1538                 "\t\t(to write disable/enable label)");
1539         exit(1);
1540 }