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