Merge branch 'vendor/OPENSSH'
[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  * Disklabel64: read and write 64 bit 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: %10ju bytes\n",
674                 (intmax_t)(lp->d_pbase - lp->d_bbase));
675         fprintf(f, "# data space: %10ju blocks\t# %6.2f MB (%ju bytes)\n",
676                         (intmax_t)(lp->d_pstop - lp->d_pbase) / blksize,
677                         (double)(lp->d_pstop - lp->d_pbase) / 1024.0 / 1024.0,
678                         (intmax_t)(lp->d_pstop - lp->d_pbase));
679         fprintf(f, "#\n");
680         fprintf(f, "# NOTE: If the partition data base looks odd it may be\n");
681         fprintf(f, "#       physically aligned instead of slice-aligned\n");
682         fprintf(f, "#\n");
683
684         uuid_to_string(&lp->d_stor_uuid, &str, NULL);
685         fprintf(f, "diskid: %s\n", str ? str : "<unknown>");
686         free(str);
687
688         fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
689                 lp->d_packname);
690         fprintf(f, "boot2 data base:      0x%012jx\n", (intmax_t)lp->d_bbase);
691         fprintf(f, "partitions data base: 0x%012jx\n", (intmax_t)lp->d_pbase);
692         fprintf(f, "partitions data stop: 0x%012jx\n", (intmax_t)lp->d_pstop);
693         fprintf(f, "backup label:         0x%012jx\n", (intmax_t)lp->d_abase);
694         fprintf(f, "total size:           0x%012jx\t# %6.2f MB\n",
695                 (intmax_t)lp->d_total_size,
696                 (double)lp->d_total_size / 1024.0 / 1024.0);
697         fprintf(f, "alignment: %u\n", lp->d_align);
698         fprintf(f, "display block size: %u\t# for partition display only\n",
699                 blksize);
700
701         fprintf(f, "\n");
702         fprintf(f, "%u partitions:\n", lp->d_npartitions);
703         fprintf(f, "#          size     offset    fstype   fsuuid\n");
704         didany = 0;
705         for (part = 0; part < lp->d_npartitions; part++) {
706                 pp = &lp->d_partitions[part];
707                 const u_long onemeg = 1024 * 1024;
708
709                 if (pp->p_bsize == 0)
710                         continue;
711                 didany = 1;
712                 fprintf(f, "  %c: ", 'a' + part);
713
714                 if (pp->p_bsize % lp->d_align)
715                     fprintf(f, "%10s  ", "ILLEGAL");
716                 else
717                     fprintf(f, "%10ju ", (intmax_t)pp->p_bsize / blksize);
718
719                 if ((pp->p_boffset - lp->d_pbase) % lp->d_align)
720                     fprintf(f, "%10s  ", "ILLEGAL");
721                 else
722                     fprintf(f, "%10ju  ",
723                             (intmax_t)(pp->p_boffset - lp->d_pbase) / blksize);
724
725                 if (pp->p_fstype < FSMAXTYPES)
726                         fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
727                 else
728                         fprintf(f, "%8d", pp->p_fstype);
729                 fprintf(f, "\t# %11.3fMB", (double)pp->p_bsize / onemeg);
730                 fprintf(f, "\n");
731         }
732         for (part = 0; part < lp->d_npartitions; part++) {
733                 pp = &lp->d_partitions[part];
734
735                 if (pp->p_bsize == 0)
736                         continue;
737
738                 if (uuid_is_nil(&lp->d_stor_uuid, NULL) == 0) {
739                         fprintf(f, "  %c-stor_uuid: ", 'a' + part);
740                         str = NULL;
741                         uuid_to_string(&pp->p_stor_uuid, &str, NULL);
742                         if (str) {
743                                 fprintf(f, "%s", str);
744                                 free(str);
745                         }
746                         fprintf(f, "\n");
747                 }
748         }
749         if (didany == 0) {
750                 fprintf(f, "# EXAMPLE\n");
751                 fprintf(f, "#a:          4g          0    4.2BSD\n");
752                 fprintf(f, "#a:          *           *    4.2BSD\n");
753
754         }
755         fflush(f);
756 }
757
758 int
759 edit(struct disklabel64 *lp, int f)
760 {
761         int c, fd;
762         struct disklabel64 label;
763         FILE *fp;
764
765         if ((fd = mkstemp(tmpfil)) == -1 ||
766             (fp = fdopen(fd, "w")) == NULL) {
767                 warnx("can't create %s", tmpfil);
768                 return (1);
769         }
770         display(fp, lp);
771         fclose(fp);
772         for (;;) {
773                 if (!editit())
774                         break;
775                 fp = fopen(tmpfil, "r");
776                 if (fp == NULL) {
777                         warnx("can't reopen %s for reading", tmpfil);
778                         break;
779                 }
780                 bzero((char *)&label, sizeof(label));
781                 if (getasciilabel(fp, &label)) {
782                         *lp = label;
783                         if (writelabel(f, lp) == 0) {
784                                 fclose(fp);
785                                 unlink(tmpfil);
786                                 return (0);
787                         }
788                 }
789                 fclose(fp);
790                 printf("re-edit the label? [y]: "); fflush(stdout);
791                 c = getchar();
792                 if (c != EOF && c != (int)'\n')
793                         while (getchar() != (int)'\n')
794                                 ;
795                 if  (c == (int)'n')
796                         break;
797         }
798         unlink(tmpfil);
799         return (1);
800 }
801
802 int
803 editit(void)
804 {
805         int pid, xpid;
806         int status, omask;
807         const char *ed;
808
809         omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
810         while ((pid = fork()) < 0) {
811                 if (errno == EPROCLIM) {
812                         warnx("you have too many processes");
813                         return(0);
814                 }
815                 if (errno != EAGAIN) {
816                         warn("fork");
817                         return(0);
818                 }
819                 sleep(1);
820         }
821         if (pid == 0) {
822                 sigsetmask(omask);
823                 setgid(getgid());
824                 setuid(getuid());
825                 if ((ed = getenv("EDITOR")) == NULL)
826                         ed = DEFEDITOR;
827                 execlp(ed, ed, tmpfil, NULL);
828                 err(1, "%s", ed);
829         }
830         while ((xpid = wait(&status)) >= 0)
831                 if (xpid == pid)
832                         break;
833         sigsetmask(omask);
834         return(!status);
835 }
836
837 char *
838 skip(char *cp)
839 {
840
841         while (*cp != '\0' && isspace(*cp))
842                 cp++;
843         if (*cp == '\0' || *cp == '#')
844                 return (NULL);
845         return (cp);
846 }
847
848 char *
849 word(char *cp)
850 {
851         char c;
852
853         while (*cp != '\0' && !isspace(*cp) && *cp != '#')
854                 cp++;
855         if ((c = *cp) != '\0') {
856                 *cp++ = '\0';
857                 if (c != '#')
858                         return (skip(cp));
859         }
860         return (NULL);
861 }
862
863 /*
864  * Read an ascii label in from fd f,
865  * in the same format as that put out by display(),
866  * and fill in lp.
867  */
868 int
869 getasciilabel(FILE *f, struct disklabel64 *lp)
870 {
871         char *cp;
872         u_int part;
873         char *tp, line[BUFSIZ];
874         u_long v;
875         uint32_t blksize = 0;
876         uint64_t vv;
877         int lineno = 0, errors = 0;
878         char empty[] = "";
879
880         bzero(&part_set, sizeof(part_set));
881         bzero(&part_size_type, sizeof(part_size_type));
882         bzero(&part_offset_type, sizeof(part_offset_type));
883         while (fgets(line, sizeof(line) - 1, f)) {
884                 lineno++;
885                 if ((cp = strchr(line,'\n')) != 0)
886                         *cp = '\0';
887                 cp = skip(line);
888                 if (cp == NULL)
889                         continue;
890                 tp = strchr(cp, ':');
891                 if (tp == NULL) {
892                         fprintf(stderr, "line %d: syntax error\n", lineno);
893                         errors++;
894                         continue;
895                 }
896                 *tp++ = '\0', tp = skip(tp);
897                 if (sscanf(cp, "%lu partitions", &v) == 1) {
898                         if (v == 0 || v > MAXPARTITIONS64) {
899                                 fprintf(stderr,
900                                     "line %d: bad # of partitions\n", lineno);
901                                 lp->d_npartitions = MAXPARTITIONS64;
902                                 errors++;
903                         } else
904                                 lp->d_npartitions = v;
905                         continue;
906                 }
907                 if (tp == NULL)
908                         tp = empty;
909
910                 if (streq(cp, "diskid")) {
911                         uint32_t status = 0;
912                         uuid_from_string(tp, &lp->d_stor_uuid, &status);
913                         if (status != uuid_s_ok) {
914                                 fprintf(stderr,
915                                     "line %d: %s: illegal UUID\n",
916                                     lineno, tp);
917                                 errors++;
918                         }
919                         continue;
920                 }
921                 if (streq(cp, "label")) {
922                         strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
923                         continue;
924                 }
925
926                 if (streq(cp, "alignment")) {
927                         v = strtoul(tp, NULL, 0);
928                         if (v <= 0 || (v & DEV_BMASK) != 0 || v > 1024*1024) {
929                                 fprintf(stderr,
930                                     "line %d: %s: bad alignment\n",
931                                     lineno, tp);
932                                 errors++;
933                         } else {
934                                 lp->d_align = v;
935                         }
936                         continue;
937                 }
938                 if (streq(cp, "total size")) {
939                         vv = strtoull(tp, NULL, 0);
940                         if (vv == 0 || vv == (uint64_t)-1) {
941                                 fprintf(stderr, "line %d: %s: bad %s\n",
942                                     lineno, tp, cp);
943                                 errors++;
944                         } else {
945                                 lp->d_total_size = vv;
946                         }
947                         continue;
948                 }
949                 if (streq(cp, "boot2 data base")) {
950                         vv = strtoull(tp, NULL, 0);
951                         if (vv == 0 || vv == (uint64_t)-1) {
952                                 fprintf(stderr, "line %d: %s: bad %s\n",
953                                     lineno, tp, cp);
954                                 errors++;
955                         } else {
956                                 lp->d_bbase = vv;
957                         }
958                         continue;
959                 }
960                 if (streq(cp, "partitions data base")) {
961                         vv = strtoull(tp, NULL, 0);
962                         if (vv == 0 || vv == (uint64_t)-1) {
963                                 fprintf(stderr, "line %d: %s: bad %s\n",
964                                     lineno, tp, cp);
965                                 errors++;
966                         } else {
967                                 lp->d_pbase = vv;
968                         }
969                         continue;
970                 }
971                 if (streq(cp, "partitions data stop")) {
972                         vv = strtoull(tp, NULL, 0);
973                         if (vv == 0 || vv == (uint64_t)-1) {
974                                 fprintf(stderr, "line %d: %s: bad %s\n",
975                                     lineno, tp, cp);
976                                 errors++;
977                         } else {
978                                 lp->d_pstop = vv;
979                         }
980                         continue;
981                 }
982                 if (streq(cp, "backup label")) {
983                         vv = strtoull(tp, NULL, 0);
984                         if (vv == 0 || vv == (uint64_t)-1) {
985                                 fprintf(stderr, "line %d: %s: bad %s\n",
986                                     lineno, tp, cp);
987                                 errors++;
988                         } else {
989                                 lp->d_abase = vv;
990                         }
991                         continue;
992                 }
993                 if (streq(cp, "display block size")) {
994                         v = strtoul(tp, NULL, 0);
995                         if (v <= 0 || (v & DEV_BMASK) != 0 || v > 1024*1024) {
996                                 fprintf(stderr,
997                                     "line %d: %s: bad alignment\n",
998                                     lineno, tp);
999                                 errors++;
1000                         } else {
1001                                 blksize = v;
1002                         }
1003                         continue;
1004                 }
1005
1006                 /* the ':' was removed above */
1007
1008                 /*
1009                  * Handle main partition data, e.g. a:, b:, etc.
1010                  */
1011                 if (*cp < 'a' || *cp > MAX_PART) {
1012                         fprintf(stderr,
1013                             "line %d: %s: Unknown disklabel field\n", lineno,
1014                             cp);
1015                         errors++;
1016                         continue;
1017                 }
1018
1019                 /* Process a partition specification line. */
1020                 part = *cp - 'a';
1021                 if (part >= lp->d_npartitions) {
1022                         fprintf(stderr,
1023                             "line %d: partition name out of range a-%c: %s\n",
1024                             lineno, 'a' + lp->d_npartitions - 1, cp);
1025                         errors++;
1026                         continue;
1027                 }
1028
1029                 if (blksize == 0) {
1030                         fprintf(stderr, "block size to use for partition "
1031                                         "display was not specified!\n");
1032                         errors++;
1033                         continue;
1034                 }
1035
1036                 if (strcmp(cp + 1, "-stor_uuid") == 0) {
1037                         if (getasciipartuuid(tp, lp, part, lineno, blksize)) {
1038                                 errors++;
1039                                 break;
1040                         }
1041                         continue;
1042                 } else if (cp[1] == 0) {
1043                         part_set[part] = 1;
1044                         if (getasciipartspec(tp, lp, part, lineno, blksize)) {
1045                                 errors++;
1046                                 break;
1047                         }
1048                         continue;
1049                 }
1050                 fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
1051                         lineno, cp);
1052                 errors++;
1053                 continue;
1054         }
1055         errors += checklabel(lp);
1056         return (errors == 0);
1057 }
1058
1059 static
1060 int
1061 parse_field_val(char **tp, char **cp, u_int64_t *vv, int lineno)
1062 {
1063         char *tmp;
1064
1065         if (*tp == NULL || **tp == 0) {
1066                 fprintf(stderr, "line %d: too few numeric fields\n", lineno);
1067                 return(-1);
1068         }
1069         *cp = *tp;
1070         *tp = word(*cp);
1071         *vv = strtoull(*cp, &tmp, 0);
1072         if (*vv == ULLONG_MAX) {
1073                 fprintf(stderr, "line %d: illegal number\n", lineno);
1074                 return(-1);
1075         }
1076         if (tmp)
1077                 return(*tmp);
1078         else
1079                 return(0);
1080 }
1081
1082 /*
1083  * Read a partition line into partition `part' in the specified disklabel.
1084  * Return 0 on success, 1 on failure.
1085  */
1086 int
1087 getasciipartspec(char *tp, struct disklabel64 *lp, int part,
1088                  int lineno, uint32_t blksize)
1089 {
1090         struct partition64 *pp;
1091         char *cp;
1092         const char **cpp;
1093         int r;
1094         u_long v;
1095         uint64_t vv;
1096         uint64_t mpx;
1097
1098         pp = &lp->d_partitions[part];
1099         cp = NULL;
1100
1101         /*
1102          * size
1103          */
1104         r = parse_field_val(&tp, &cp, &vv, lineno);
1105         if (r < 0)
1106                 return (1);
1107
1108         mpx = 1;
1109         switch(r) {
1110         case 0:
1111                 mpx = blksize;
1112                 break;
1113         case '%':
1114                 /* mpx = 1; */
1115                 break;
1116         case '*':
1117                 mpx = 0;
1118                 break;
1119         case 'g':
1120         case 'G':
1121                 mpx *= 1024ULL;
1122                 /* fall through */
1123         case 'm':
1124         case 'M':
1125                 mpx *= 1024ULL;
1126                 /* fall through */
1127         case 'k':
1128         case 'K':
1129                 mpx *= 1024ULL;
1130                 r = 0;                  /* eat the suffix */
1131                 break;
1132         default:
1133                 Warning("unknown size specifier '%c' (*/%%/K/M/G are valid)",
1134                         r);
1135                 return(1);
1136         }
1137
1138         part_size_type[part] = r;
1139         if (vv == 0 && r != '*') {
1140                 fprintf(stderr,
1141                     "line %d: %s: bad partition size (0)\n", lineno, cp);
1142                 return (1);
1143         }
1144         pp->p_bsize = vv * mpx;
1145
1146         /*
1147          * offset
1148          */
1149         r = parse_field_val(&tp, &cp, &vv, lineno);
1150         if (r < 0)
1151                 return (1);
1152         part_offset_type[part] = r;
1153         switch(r) {
1154         case '*':
1155                 pp->p_boffset = 0;
1156                 break;
1157         case 0:
1158                 pp->p_boffset = vv * blksize + lp->d_pbase;
1159                 break;
1160         default:
1161                 fprintf(stderr,
1162                     "line %d: %s: bad suffix on partition offset (%c)\n",
1163                     lineno, cp, r);
1164                 return (1);
1165         }
1166
1167         /*
1168          * fstype
1169          */
1170         cp = tp;
1171         tp = word(cp);
1172         for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++) {
1173                 if (*cpp && strcasecmp(*cpp, cp) == 0)
1174                         break;
1175         }
1176         if (*cpp != NULL) {
1177                 pp->p_fstype = cpp - fstypenames;
1178         } else {
1179                 if (isdigit(*cp))
1180                         v = strtoul(cp, NULL, 0);
1181                 else
1182                         v = FSMAXTYPES;
1183                 if (v >= FSMAXTYPES) {
1184                         fprintf(stderr,
1185                             "line %d: Warning, unknown filesystem type %s\n",
1186                             lineno, cp);
1187                         v = FS_UNUSED;
1188                 }
1189                 pp->p_fstype = v;
1190         }
1191
1192         cp = tp;
1193         if (tp) {
1194                 fprintf(stderr, "line %d: Warning, extra data on line\n",
1195                         lineno);
1196         }
1197         return(0);
1198 }
1199
1200 int
1201 getasciipartuuid(char *tp, struct disklabel64 *lp, int part,
1202                  int lineno, uint32_t blksize __unused)
1203 {
1204         struct partition64 *pp;
1205         uint32_t status;
1206         char *cp;
1207
1208         pp = &lp->d_partitions[part];
1209
1210         cp = tp;
1211         tp = word(cp);
1212         uuid_from_string(cp, &pp->p_stor_uuid, &status);
1213         if (status != uuid_s_ok) {
1214                 fprintf(stderr, "line %d: Illegal storage uuid specification\n",
1215                         lineno);
1216                 return(1);
1217         }
1218         return(0);
1219 }
1220
1221 /*
1222  * Check disklabel for errors and fill in
1223  * derived fields according to supplied values.
1224  */
1225 int
1226 checklabel(struct disklabel64 *lp)
1227 {
1228         struct partition64 *pp;
1229         int errors = 0;
1230         char part;
1231         u_int64_t total_size;
1232         u_int64_t current_offset;
1233         u_long total_percent;
1234         int seen_default_offset;
1235         int hog_part;
1236         int i, j;
1237         struct partition64 *pp2;
1238         u_int64_t off;
1239
1240         if (lp->d_align < 512 ||
1241             (lp->d_align ^ (lp->d_align - 1)) != lp->d_align * 2 - 1) {
1242                 Warning("Illegal alignment specified: %u\n", lp->d_align);
1243                 return (1);
1244         }
1245         if (lp->d_npartitions > MAXPARTITIONS64) {
1246                 Warning("number of partitions (%u) > MAXPARTITIONS (%d)",
1247                         lp->d_npartitions, MAXPARTITIONS64);
1248                 return (1);
1249         }
1250         off = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]);
1251         off = (off + lp->d_align - 1) & ~(int64_t)(lp->d_align - 1);
1252
1253         if (lp->d_bbase < off || lp->d_bbase % lp->d_align) {
1254                 Warning("illegal boot2 data base ");
1255                 return (1);
1256         }
1257
1258         /*
1259          * pbase can be unaligned slice-relative but will be
1260          * aligned physically.
1261          */
1262         if (lp->d_pbase < lp->d_bbase) {
1263                 Warning("illegal partition data base");
1264                 return (1);
1265         }
1266         if (lp->d_pstop < lp->d_pbase) {
1267                 Warning("illegal partition data stop");
1268                 return (1);
1269         }
1270         if (lp->d_pstop > lp->d_total_size) {
1271                 printf("%012jx\n%012jx\n",
1272                         (intmax_t)lp->d_pstop, (intmax_t)lp->d_total_size);
1273                 Warning("disklabel control info is beyond the total size");
1274                 return (1);
1275         }
1276         if (lp->d_abase &&
1277             (lp->d_abase < lp->d_pstop ||
1278              lp->d_abase > lp->d_total_size - off)) {
1279                 Warning("illegal backup label location");
1280                 return (1);
1281         }
1282
1283         /* first allocate space to the partitions, then offsets */
1284         total_size = 0;         /* in bytes */
1285         total_percent = 0;      /* in percent */
1286         hog_part = -1;
1287         /* find all fixed partitions */
1288         for (i = 0; i < (int)lp->d_npartitions; i++) {
1289                 pp = &lp->d_partitions[i];
1290                 if (part_set[i]) {
1291                         if (part_size_type[i] == '*') {
1292                                 if (part_offset_type[i] != '*') {
1293                                         if (total_size < pp->p_boffset)
1294                                                 total_size = pp->p_boffset;
1295                                 }
1296                                 if (hog_part != -1) {
1297                                         Warning("Too many '*' partitions (%c and %c)",
1298                                             hog_part + 'a',i + 'a');
1299                                 } else {
1300                                         hog_part = i;
1301                                 }
1302                         } else {
1303                                 off_t size;
1304
1305                                 size = pp->p_bsize;
1306                                 if (part_size_type[i] == '%') {
1307                                         /* 
1308                                          * don't count %'s yet
1309                                          */
1310                                         total_percent += size;
1311                                 } else {
1312                                         /*
1313                                          * Value has already been converted
1314                                          * to bytes.
1315                                          */
1316                                         if (size % lp->d_align != 0) {
1317                                                 Warning("partition %c's size is not properly aligned",
1318                                                         i + 'a');
1319                                         }
1320                                         total_size += size;
1321                                 }
1322                         }
1323                 }
1324         }
1325         /* handle % partitions - note %'s don't need to add up to 100! */
1326         if (total_percent != 0) {
1327                 int64_t free_space;
1328                 int64_t space_left;
1329
1330                 free_space = (int64_t)(lp->d_pstop - lp->d_pbase - total_size);
1331                 free_space &= ~(u_int64_t)(lp->d_align - 1);
1332
1333                 space_left = free_space;
1334                 if (total_percent > 100) {
1335                         fprintf(stderr,"total percentage %lu is greater than 100\n",
1336                             total_percent);
1337                         errors++;
1338                 }
1339
1340                 if (free_space > 0) {
1341                         for (i = 0; i < (int)lp->d_npartitions; i++) {
1342                                 pp = &lp->d_partitions[i];
1343                                 if (part_set[i] && part_size_type[i] == '%') {
1344                                         /* careful of overflows! and integer roundoff */
1345                                         pp->p_bsize = ((double)pp->p_bsize/100) * free_space;
1346                                         pp->p_bsize = (pp->p_bsize + lp->d_align - 1) & ~(u_int64_t)(lp->d_align - 1);
1347                                         if ((int64_t)pp->p_bsize > space_left)
1348                                                 pp->p_bsize = (u_int64_t)space_left;
1349                                         total_size += pp->p_bsize;
1350                                         space_left -= pp->p_bsize;
1351                                 }
1352                         }
1353                 } else {
1354                         fprintf(stderr, "%jd bytes available to give to "
1355                                         "'*' and '%%' partitions\n",
1356                                 (intmax_t)free_space);
1357                         errors++;
1358                         /* fix?  set all % partitions to size 0? */
1359                 }
1360         }
1361         /* give anything remaining to the hog partition */
1362         if (hog_part != -1) {
1363                 off = lp->d_pstop - lp->d_pbase - total_size;
1364                 off &= ~(u_int64_t)(lp->d_align - 1);
1365                 lp->d_partitions[hog_part].p_bsize = off;
1366                 total_size = lp->d_pstop - lp->d_pbase;
1367         }
1368
1369         /* Now set the offsets for each partition */
1370         current_offset = lp->d_pbase;
1371         seen_default_offset = 0;
1372         for (i = 0; i < (int)lp->d_npartitions; i++) {
1373                 part = 'a' + i;
1374                 pp = &lp->d_partitions[i];
1375                 if (pp->p_bsize == 0)
1376                         continue;
1377                 if (part_set[i]) {
1378                         if (part_offset_type[i] == '*') {
1379                                 pp->p_boffset = current_offset;
1380                                 seen_default_offset = 1;
1381                         } else {
1382                                 /* allow them to be out of order for old-style tables */
1383                                 if (pp->p_boffset < current_offset && 
1384                                     seen_default_offset &&
1385                                     pp->p_fstype != FS_VINUM) {
1386                                         fprintf(stderr,
1387 "Offset 0x%012jx for partition %c overlaps previous partition which ends at 0x%012jx\n",
1388                                             (intmax_t)pp->p_boffset,
1389                                             i + 'a',
1390                                             (intmax_t)current_offset);
1391                                         fprintf(stderr,
1392 "Labels with any *'s for offset must be in ascending order by sector\n");
1393                                         errors++;
1394                                 } else if (pp->p_boffset != current_offset &&
1395                                            seen_default_offset) {
1396                                         /* 
1397                                          * this may give unneeded warnings if 
1398                                          * partitions are out-of-order
1399                                          */
1400                                         Warning(
1401 "Offset 0x%012jx for partition %c doesn't match expected value 0x%012jx",
1402                                             pp->p_boffset, i + 'a',
1403                                             (intmax_t)current_offset);
1404                                 }
1405                         }
1406                         current_offset = pp->p_boffset + pp->p_bsize; 
1407                 }
1408         }
1409
1410         for (i = 0; i < (int)lp->d_npartitions; i++) {
1411                 part = 'a' + i;
1412                 pp = &lp->d_partitions[i];
1413                 if (pp->p_bsize == 0 && pp->p_boffset != 0)
1414                         Warning("partition %c: size 0, but offset 0x%012jx",
1415                                 part, (intmax_t)pp->p_boffset);
1416                 if (pp->p_bsize == 0) {
1417                         pp->p_boffset = 0;
1418                         continue;
1419                 }
1420                 if (uuid_is_nil(&pp->p_stor_uuid, NULL))
1421                         uuid_create(&pp->p_stor_uuid, NULL);
1422
1423                 if (pp->p_boffset < lp->d_pbase) {
1424                         fprintf(stderr,
1425                             "partition %c: offset out of bounds (%jd)\n",
1426                             part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1427                         errors++;
1428                 }
1429                 if (pp->p_boffset > lp->d_pstop) {
1430                         fprintf(stderr,
1431                             "partition %c: offset out of bounds (%jd)\n",
1432                             part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1433                         errors++;
1434                 }
1435                 if (pp->p_boffset + pp->p_bsize > lp->d_pstop) {
1436                         fprintf(stderr,
1437                             "partition %c: size out of bounds (%jd)\n",
1438                             part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1439                         errors++;
1440                 }
1441
1442                 /* check for overlaps */
1443                 /* this will check for all possible overlaps once and only once */
1444                 for (j = 0; j < i; j++) {
1445                         pp2 = &lp->d_partitions[j];
1446                         if (pp->p_fstype != FS_VINUM &&
1447                             pp2->p_fstype != FS_VINUM &&
1448                             part_set[i] && part_set[j]) {
1449                                 if (pp2->p_boffset < pp->p_boffset + pp->p_bsize &&
1450                                     (pp2->p_boffset + pp2->p_bsize > pp->p_boffset ||
1451                                         pp2->p_boffset >= pp->p_boffset)) {
1452                                         fprintf(stderr,"partitions %c and %c overlap!\n",
1453                                             j + 'a', i + 'a');
1454                                         errors++;
1455                                 }
1456                         }
1457                 }
1458         }
1459         for (; i < (int)lp->d_npartitions; i++) {
1460                 part = 'a' + i;
1461                 pp = &lp->d_partitions[i];
1462                 if (pp->p_bsize || pp->p_boffset)
1463                         Warning("unused partition %c: size 0x%012jx "
1464                                 "offset 0x%012jx",
1465                                 'a' + i, (intmax_t)pp->p_bsize,
1466                                 (intmax_t)pp->p_boffset);
1467         }
1468         return (errors);
1469 }
1470
1471 /*
1472  * When operating on a "virgin" disk, try getting an initial label
1473  * from the associated device driver.  This might work for all device
1474  * drivers that are able to fetch some initial device parameters
1475  * without even having access to a (BSD) disklabel, like SCSI disks,
1476  * most IDE drives, or vn devices.
1477  *
1478  * The device name must be given in its "canonical" form.
1479  */
1480 static struct disklabel64 dlab;
1481
1482 struct disklabel64 *
1483 getvirginlabel(void)
1484 {
1485         struct disklabel64 *dl = &dlab;
1486         int f;
1487
1488         if ((f = open(dkname, O_RDONLY)) == -1) {
1489                 warn("cannot open %s", dkname);
1490                 return (NULL);
1491         }
1492
1493         /*
1494          * Try to use the new get-virgin-label ioctl.  If it fails,
1495          * fallback to the old get-disk-info ioctl.
1496          */
1497         if (ioctl(f, DIOCGDVIRGIN64, dl) < 0) {
1498                 l_perror("ioctl DIOCGDVIRGIN64");
1499                 close(f);
1500                 return (NULL);
1501         }
1502         close(f);
1503         return (dl);
1504 }
1505
1506 /*VARARGS1*/
1507 void
1508 Warning(const char *fmt, ...)
1509 {
1510         va_list ap;
1511
1512         fprintf(stderr, "Warning, ");
1513         va_start(ap, fmt);
1514         vfprintf(stderr, fmt, ap);
1515         fprintf(stderr, "\n");
1516         va_end(ap);
1517 }
1518
1519 void
1520 usage(void)
1521 {
1522         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1523                 "usage: disklabel64 [-r] disk",
1524                 "\t\t(to read label)",
1525                 "       disklabel64 -w [-r] [-n] disk type [packid]",
1526                 "\t\t(to write label with existing boot program)",
1527                 "       disklabel64 -e [-r] [-n] disk",
1528                 "\t\t(to edit label)",
1529                 "       disklabel64 -R [-r] [-n] disk protofile",
1530                 "\t\t(to restore label with existing boot program)",
1531                 "       disklabel64 -B [-n] [-b boot1 -s boot2] disk [type]",
1532                 "\t\t(to install boot program with existing label)",
1533                 "       disklabel64 -w -B [-n] [-b boot1 -s boot2] disk type [packid]",
1534                 "\t\t(to write label and boot program)",
1535                 "       disklabel64 -R -B [-n] [-b boot1 -s boot2] disk protofile [type]",
1536                 "\t\t(to restore label and boot program)",
1537                 "       disklabel64 [-NW] disk",
1538                 "\t\t(to write disable/enable label)");
1539         exit(1);
1540 }