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