disklabel* - Make disk and filesystem types case insensitive.
[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
89 #include <vfs/ufs/dinode.h>
90 #include <vfs/ufs/fs.h>
91
92 #include <unistd.h>
93 #include <string.h>
94 #include <stdio.h>
95 #include <stdlib.h>
96 #include <signal.h>
97 #include <stdarg.h>
98 #include <stddef.h>
99 #include <ctype.h>
100 #include <err.h>
101 #include <errno.h>
102 #include <disktab.h>
103 #include <uuid.h>
104 #include "pathnames.h"
105
106 extern uint32_t crc32(const void *buf, size_t size);
107
108 /*
109  * Disklabel: read and write disklabels.
110  * The label is usually placed on one of the first sectors of the disk.
111  * Many machines also place a bootstrap in the same area,
112  * in which case the label is embedded in the bootstrap.
113  * The bootstrap source must leave space at the proper offset
114  * for the label on such machines.
115  */
116
117 #define LABELSIZE       ((sizeof(struct disklabel64) + 4095) & ~4095)
118 #define BOOTSIZE        32768
119
120 /* FIX!  These are too low, but are traditional */
121 #define DEFAULT_NEWFS_BLOCK  8192U
122 #define DEFAULT_NEWFS_FRAG   1024U
123 #define DEFAULT_NEWFS_CPG    16U
124
125 #define BIG_NEWFS_BLOCK  16384U
126 #define BIG_NEWFS_FRAG   2048U
127 #define BIG_NEWFS_CPG    64U
128
129 void    makelabel(const char *, const char *, struct disklabel64 *);
130 int     writelabel(int, struct disklabel64 *);
131 void    l_perror(const char *);
132 struct disklabel64 *readlabel(int);
133 struct disklabel64 *makebootarea(int);
134 void    display(FILE *, const struct disklabel64 *);
135 int     edit(struct disklabel64 *, int);
136 int     editit(void);
137 char    *skip(char *);
138 char    *word(char *);
139 int     getasciilabel(FILE *, struct disklabel64 *);
140 int     getasciipartspec(char *, struct disklabel64 *, int, int, uint32_t);
141 int     getasciipartuuid(char *, struct disklabel64 *, int, int, uint32_t);
142 int     checklabel(struct disklabel64 *);
143 void    Warning(const char *, ...) __printflike(1, 2);
144 void    usage(void);
145 struct disklabel64 *getvirginlabel(void);
146
147 #define DEFEDITOR       _PATH_VI
148 #define streq(a,b)      (strcmp(a,b) == 0)
149
150 char    *dkname;
151 char    *specname;
152 char    tmpfil[] = PATH_TMPFILE;
153
154 struct  disklabel64 lab;
155
156 #define MAX_PART ('z')
157 #define MAX_NUM_PARTS (1 + MAX_PART - 'a')
158 char    part_size_type[MAX_NUM_PARTS];
159 char    part_offset_type[MAX_NUM_PARTS];
160 int     part_set[MAX_NUM_PARTS];
161
162 int     installboot;    /* non-zero if we should install a boot program */
163 int     boot1size;
164 int     boot1lsize;
165 int     boot2size;
166 char    *boot1buf;
167 char    *boot2buf;
168 char    *boot1path;
169 char    *boot2path;
170
171 enum    {
172         UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT
173 } op = UNSPEC;
174
175 int     rflag;
176 int     Vflag;
177 int     disable_write;   /* set to disable writing to disk label */
178 u_int32_t slice_start_lba;
179
180 #ifdef DEBUG
181 int     debug;
182 #define OPTIONS "BNRWb:denrs:Vw"
183 #else
184 #define OPTIONS "BNRWb:enrs:Vw"
185 #endif
186
187 int
188 main(int argc, char *argv[])
189 {
190         struct disklabel64 *lp;
191         FILE *t;
192         int ch, f = 0, flag, error = 0;
193         char *name = 0;
194
195         while ((ch = getopt(argc, argv, OPTIONS)) != -1)
196                 switch (ch) {
197                         case 'B':
198                                 ++installboot;
199                                 break;
200                         case 'b':
201                                 boot1path = optarg;
202                                 break;
203
204                         case 's':
205                                 boot2path = optarg;
206                                 break;
207                         case 'N':
208                                 if (op != UNSPEC)
209                                         usage();
210                                 op = NOWRITE;
211                                 break;
212                         case 'n':
213                                 disable_write = 1;
214                                 break;
215                         case 'R':
216                                 if (op != UNSPEC)
217                                         usage();
218                                 op = RESTORE;
219                                 break;
220                         case 'W':
221                                 if (op != UNSPEC)
222                                         usage();
223                                 op = WRITEABLE;
224                                 break;
225                         case 'e':
226                                 if (op != UNSPEC)
227                                         usage();
228                                 op = EDIT;
229                                 break;
230                         case 'V':
231                                 ++Vflag;
232                                 break;
233                         case 'r':
234                                 ++rflag;
235                                 break;
236                         case 'w':
237                                 if (op != UNSPEC)
238                                         usage();
239                                 op = WRITE;
240                                 break;
241 #ifdef DEBUG
242                         case 'd':
243                                 debug++;
244                                 break;
245 #endif
246                         case '?':
247                         default:
248                                 usage();
249                 }
250         argc -= optind;
251         argv += optind;
252         if (installboot) {
253                 rflag++;
254                 if (op == UNSPEC)
255                         op = WRITEBOOT;
256         } else {
257                 if (op == UNSPEC)
258                         op = READ;
259                 boot1path = NULL;
260                 boot2path = NULL;
261         }
262         if (argc < 1)
263                 usage();
264
265         dkname = argv[0];
266         if (dkname[0] != '/') {
267                 asprintf(&specname, "%s%s", _PATH_DEV, dkname);
268         } else {
269                 specname = dkname;
270         }
271         f = open(specname, op == READ ? O_RDONLY : O_RDWR);
272         if (f < 0 && errno == ENOENT && dkname[0] != '/') {
273                 asprintf(&specname, "%s%s", _PATH_DEV, dkname);
274                 f = open(specname, op == READ ? O_RDONLY : O_RDWR);
275         }
276         if (f < 0)
277                 err(4, "%s", specname);
278
279         switch(op) {
280
281         case UNSPEC:
282                 break;
283
284         case EDIT:
285                 if (argc != 1)
286                         usage();
287                 lp = readlabel(f);
288                 error = edit(lp, f);
289                 break;
290
291         case NOWRITE:
292                 flag = 0;
293                 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
294                         err(4, "ioctl DIOCWLABEL");
295                 break;
296
297         case READ:
298                 if (argc != 1)
299                         usage();
300                 lp = readlabel(f);
301                 display(stdout, lp);
302                 error = checklabel(lp);
303                 break;
304
305         case RESTORE:
306                 if (installboot && argc == 3) {
307                         makelabel(argv[2], 0, &lab);
308                         argc--;
309
310                         /*
311                          * We only called makelabel() for its side effect
312                          * of setting the bootstrap file names.  Discard
313                          * all changes to `lab' so that all values in the
314                          * final label come from the ASCII label.
315                          */
316                         bzero((char *)&lab, sizeof(lab));
317                 }
318                 if (argc != 2)
319                         usage();
320                 if (!(t = fopen(argv[1], "r")))
321                         err(4, "%s", argv[1]);
322                 if (!getasciilabel(t, &lab))
323                         exit(1);
324                 lp = makebootarea(f);
325                 bcopy(&lab.d_magic, &lp->d_magic,
326                       sizeof(lab) - offsetof(struct disklabel64, d_magic));
327                 error = writelabel(f, lp);
328                 break;
329
330         case WRITE:
331                 if (argc == 3) {
332                         name = argv[2];
333                         argc--;
334                 }
335                 if (argc != 2)
336                         usage();
337                 makelabel(argv[1], name, &lab);
338                 lp = makebootarea(f);
339                 bcopy(&lab.d_magic, &lp->d_magic,
340                       sizeof(lab) - offsetof(struct disklabel64, d_magic));
341                 if (checklabel(lp) == 0)
342                         error = writelabel(f, lp);
343                 break;
344
345         case WRITEABLE:
346                 flag = 1;
347                 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
348                         err(4, "ioctl DIOCWLABEL");
349                 break;
350
351         case WRITEBOOT:
352         {
353                 struct disklabel64 tlab;
354
355                 lp = readlabel(f);
356                 tlab = *lp;
357                 if (argc == 2)
358                         makelabel(argv[1], 0, &lab);
359                 lp = makebootarea(f);
360                 bcopy(&tlab.d_magic, &lp->d_magic,
361                       sizeof(tlab) - offsetof(struct disklabel64, d_magic));
362                 if (checklabel(lp) == 0)
363                         error = writelabel(f, lp);
364                 break;
365         }
366         }
367         exit(error);
368 }
369
370 /*
371  * Construct a prototype disklabel from /etc/disktab.  As a side
372  * effect, set the names of the primary and secondary boot files
373  * if specified.
374  */
375 void
376 makelabel(const char *type, const char *name, struct disklabel64 *lp)
377 {
378         struct disklabel64 *dp;
379
380         if (strcmp(type, "auto") == 0)
381                 dp = getvirginlabel();
382         else
383                 dp = NULL;
384         if (dp == NULL)
385                 errx(1, "%s: unknown disk type", type);
386         *lp = *dp;
387
388         /*
389          * NOTE: boot control files may no longer be specified in disktab.
390          */
391         if (name)
392                 strncpy(lp->d_packname, name, sizeof(lp->d_packname));
393 }
394
395 int
396 writelabel(int f, struct disklabel64 *lp)
397 {
398         struct disklabel64 *blp;
399         int flag;
400         int r;
401         size_t lpsize;
402         size_t lpcrcsize;
403
404         lpsize = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]);
405         lpcrcsize = lpsize - offsetof(struct disklabel64, d_magic);
406
407         if (disable_write) {
408                 Warning("write to disk label suppressed - label was as follows:");
409                 display(stdout, lp);
410                 return (0);
411         } else {
412                 lp->d_magic = DISKMAGIC64;
413                 lp->d_crc = 0;
414                 lp->d_crc = crc32(&lp->d_magic, lpcrcsize);
415                 if (rflag) {
416                         /*
417                          * Make sure the boot area is not too large
418                          */
419                         if (boot2buf) {
420                                 int lpbsize = (int)(lp->d_pbase - lp->d_bbase);
421                                 if (lp->d_pbase == 0) {
422                                         errx(1, "no space was set aside in "
423                                                 "the disklabel for boot2!");
424                                 }
425                                 if (boot2size > lpbsize) {
426                                         errx(1, "label did not reserve enough "
427                                                 "space for boot!  %d/%d",
428                                              boot2size, lpbsize);
429                                 }
430                         }
431
432                         /*
433                          * First set the kernel disk label,
434                          * then write a label to the raw disk.
435                          * If the SDINFO ioctl fails because it is
436                          * unimplemented, keep going; otherwise, the kernel
437                          * consistency checks may prevent us from changing
438                          * the current (in-core) label.
439                          */
440                         if (ioctl(f, DIOCSDINFO64, lp) < 0 &&
441                                 errno != ENODEV && errno != ENOTTY) {
442                                 l_perror("ioctl DIOCSDINFO");
443                                 return (1);
444                         }
445                         lseek(f, (off_t)0, SEEK_SET);
446
447                         /*
448                          * The disklabel embeds areas which we may not
449                          * have wanted to change.  Merge those areas in
450                          * from disk.
451                          */
452                         blp = makebootarea(f);
453                         if (blp != lp) {
454                                 bcopy(&lp->d_magic, &blp->d_magic,
455                                       sizeof(*lp) -
456                                       offsetof(struct disklabel64, d_magic));
457                         }
458                         
459                         /*
460                          * write enable label sector before write
461                          * (if necessary), disable after writing.
462                          */
463                         flag = 1;
464                         if (ioctl(f, DIOCWLABEL, &flag) < 0)
465                                 warn("ioctl DIOCWLABEL");
466
467                         r = write(f, boot1buf, boot1lsize);
468                         if (r != (ssize_t)boot1lsize) {
469                                 warn("write");
470                                 return (1);
471                         }
472                         /*
473                          * Output the remainder of the disklabel
474                          */
475                         if (boot2buf) {
476                                 lseek(f, lp->d_bbase, 0);
477                                 r = write(f, boot2buf, boot2size);
478                                 if (r != boot2size) {
479                                         warn("write");
480                                         return(1);
481                                 }
482                         }
483                         flag = 0;
484                         ioctl(f, DIOCWLABEL, &flag);
485                 } else if (ioctl(f, DIOCWDINFO64, lp) < 0) {
486                         l_perror("ioctl DIOCWDINFO64");
487                         return (1);
488                 }
489         }
490         return (0);
491 }
492
493 void
494 l_perror(const char *s)
495 {
496         switch (errno) {
497
498         case ESRCH:
499                 warnx("%s: no disk label on disk;", s);
500                 fprintf(stderr, "add \"-r\" to install initial label\n");
501                 break;
502
503         case EINVAL:
504                 warnx("%s: label magic number or checksum is wrong!", s);
505                 fprintf(stderr, "(disklabel or kernel is out of date?)\n");
506                 break;
507
508         case EBUSY:
509                 warnx("%s: open partition would move or shrink", s);
510                 break;
511
512         case ENOATTR:
513                 warnx("%s: the disk already has a label of a different type,\n"
514                       "probably a 32 bit disklabel.  It must be cleaned out "
515                       "first.\n", s);
516                 break;
517
518         default:
519                 warn(NULL);
520                 break;
521         }
522 }
523
524 /*
525  * Fetch disklabel for disk.
526  * Use ioctl to get label unless -r flag is given.
527  */
528 struct disklabel64 *
529 readlabel(int f)
530 {
531         struct disklabel64 *lp;
532         u_int32_t savecrc;
533         size_t lpcrcsize;
534
535         if (rflag) {
536                 /*
537                  * Allocate space for the label.  The boot1 code, if any,
538                  * is embedded in the label.  The label overlaps the boot1
539                  * code.
540                  */
541                 lp = makebootarea(f);
542                 lpcrcsize = offsetof(struct disklabel64,
543                                      d_partitions[lp->d_npartitions]) -
544                             offsetof(struct disklabel64, d_magic);
545                 savecrc = lp->d_crc;
546                 lp->d_crc = 0;
547                 if (lp->d_magic != DISKMAGIC64)
548                         errx(1, "bad pack magic number");
549                 if (lp->d_npartitions > MAXPARTITIONS64 ||
550                     savecrc != crc32(&lp->d_magic, lpcrcsize)
551                 ) {
552                         errx(1, "corrupted disklabel64");
553                 }
554                 lp->d_crc = savecrc;
555         } else {
556                 /*
557                  * Just use a static structure to hold the label.  Note
558                  * that DIOCSDINFO64 does not overwrite the boot1 area
559                  * even though it is part of the disklabel64 structure.
560                  */
561                 lp = &lab;
562                 if (Vflag) {
563                         if (ioctl(f, DIOCGDVIRGIN64, lp) < 0) {
564                                 l_perror("ioctl DIOCGDVIRGIN64");
565                                 exit(4);
566                         }
567                 } else {
568                         if (ioctl(f, DIOCGDINFO64, lp) < 0) {
569                                 l_perror("ioctl DIOCGDINFO64");
570                                 exit(4);
571                         }
572                 }
573         }
574         return (lp);
575 }
576
577 /*
578  * Construct a boot area for boot1 and boot2 and return the location of
579  * the label within the area.  The caller will overwrite the label so
580  * we don't actually have to read it.
581  */
582 struct disklabel64 *
583 makebootarea(int f)
584 {
585         struct disklabel64 *lp;
586         struct partinfo info;
587         u_int32_t secsize;
588         struct stat st;
589         int fd;
590         int r;
591
592         if (ioctl(f, DIOCGPART, &info) == 0)
593                 secsize = info.media_blksize;
594         else
595                 secsize = 512;
596
597         if (boot1buf == NULL) {
598                 size_t rsize;
599
600                 rsize = (sizeof(struct disklabel64) + secsize - 1) &
601                         ~(secsize - 1);
602                 boot1size = offsetof(struct disklabel64, d_magic);
603                 boot1lsize = rsize;
604                 boot1buf = malloc(rsize);
605                 bzero(boot1buf, rsize);
606                 r = read(f, boot1buf, rsize);
607                 if (r != (int)rsize)
608                         err(4, "%s", specname);
609         }
610         lp = (void *)boot1buf;
611
612         if (installboot == 0)
613                 return(lp);
614
615         if (boot2buf == NULL) {
616                 boot2size = 32768;
617                 boot2buf = malloc(boot2size);
618                 bzero(boot2buf, boot2size);
619         }
620
621         /*
622          * If installing the boot code, read it into the appropriate portions
623          * of the buffer(s)
624          */
625         if (boot1path == NULL)
626                 asprintf(&boot1path, "%s/boot1_64", _PATH_BOOTDIR);
627         if (boot2path == NULL)
628                 asprintf(&boot2path, "%s/boot2_64", _PATH_BOOTDIR);
629
630         if ((fd = open(boot1path, O_RDONLY)) < 0)
631                 err(4, "%s", boot1path);
632         if (fstat(fd, &st) < 0)
633                 err(4, "%s", boot1path);
634         if (st.st_size > boot1size)
635                 err(4, "%s must be exactly %d bytes!", boot1path, boot1size);
636         if (read(fd, boot1buf, boot1size) != boot1size)
637                 err(4, "%s must be exactly %d bytes!", boot1path, boot1size);
638         close(fd);
639
640         if ((fd = open(boot2path, O_RDONLY)) < 0)
641                 err(4, "%s", boot2path);
642         if (fstat(fd, &st) < 0)
643                 err(4, "%s", boot2path);
644         if (st.st_size > boot2size)
645                 err(4, "%s must be <= %d bytes!", boot2path, boot2size);
646         if ((r = read(fd, boot2buf, boot2size)) < 1)
647                 err(4, "%s is empty!", boot2path);
648         boot2size = (r + secsize - 1) & ~(secsize - 1);
649         close(fd);
650
651         /*
652          * XXX dangerously dedicated support goes here XXX
653          */
654         return (lp);
655 }
656
657 void
658 display(FILE *f, const struct disklabel64 *lp)
659 {
660         const struct partition64 *pp;
661         char *str;
662         unsigned int part;
663         int didany;
664         uint32_t blksize;
665
666         /*
667          * Use a human readable block size if possible.  This is for
668          * display and editing purposes only.
669          */
670         if (lp->d_align > 1024)
671                 blksize = 1024;
672         else
673                 blksize = lp->d_align;
674
675         fprintf(f, "# %s:\n", specname);
676         fprintf(f, "#\n");
677         fprintf(f, "# Informational fields calculated from the above\n");
678         fprintf(f, "# All byte equivalent offsets must be aligned\n");
679         fprintf(f, "#\n");
680         fprintf(f, "# boot space: %10llu bytes\n", lp->d_pbase - lp->d_bbase);
681         fprintf(f, "# data space: %10llu blocks\t# %6.2f MB (%llu bytes)\n",
682                         (lp->d_pstop - lp->d_pbase) / blksize,
683                         (double)(lp->d_pstop - lp->d_pbase) / 1024.0 / 1024.0,
684                         lp->d_pstop - lp->d_pbase);
685         fprintf(f, "#\n");
686
687         uuid_to_string(&lp->d_stor_uuid, &str, NULL);
688         fprintf(f, "diskid: %s\n", str ? str : "<unknown>");
689         free(str);
690
691         fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
692                 lp->d_packname);
693         fprintf(f, "boot2 data base:      0x%012llx\n", lp->d_bbase);
694         fprintf(f, "partitions data base: 0x%012llx\n", lp->d_pbase);
695         fprintf(f, "partitions data stop: 0x%012llx\n", lp->d_pstop);
696         fprintf(f, "backup label:         0x%012llx\n", lp->d_abase);
697         fprintf(f, "total size:           0x%012llx\t# %6.2f MB\n",
698                 lp->d_total_size,
699                 (double)lp->d_total_size / 1024.0 / 1024.0);
700         fprintf(f, "alignment: %u\n", lp->d_align);
701         fprintf(f, "display block size: %u\t# for partition display only\n",
702                 blksize);
703
704         fprintf(f, "\n");
705         fprintf(f, "%u partitions:\n", lp->d_npartitions);
706         fprintf(f, "#          size     offset    fstype   fsuuid\n");
707         didany = 0;
708         for (part = 0; part < lp->d_npartitions; part++) {
709                 pp = &lp->d_partitions[part];
710                 const u_long onemeg = 1024 * 1024;
711
712                 if (pp->p_bsize == 0)
713                         continue;
714                 didany = 1;
715                 fprintf(f, "  %c: ", 'a' + part);
716
717                 if (pp->p_bsize % lp->d_align)
718                     fprintf(f, "%10s  ", "ILLEGAL");
719                 else
720                     fprintf(f, "%10llu ", pp->p_bsize / blksize);
721                 if (pp->p_boffset % lp->d_align)
722                     fprintf(f, "%10s  ", "ILLEGAL");
723                 else
724                     fprintf(f, "%10llu  ", (pp->p_boffset - lp->d_pbase) / blksize);
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         if (lp->d_pbase < lp->d_bbase || lp->d_pbase % lp->d_align) {
1258                 Warning("illegal partition data base");
1259                 return (1);
1260         }
1261         if (lp->d_pstop < lp->d_pbase || lp->d_pstop % lp->d_align) {
1262                 Warning("illegal partition data stop");
1263                 return (1);
1264         }
1265         if (lp->d_pstop > lp->d_total_size) {
1266                 printf("%012llx\n%012llx\n", lp->d_pstop, lp->d_total_size);
1267                 Warning("disklabel control info is beyond the total size");
1268                 return (1);
1269         }
1270         if (lp->d_abase &&
1271             (lp->d_abase < lp->d_pstop || lp->d_pstop % lp->d_align ||
1272              lp->d_abase > lp->d_total_size - off)) {
1273                 Warning("illegal backup label location");
1274                 return (1);
1275         }
1276
1277         /* first allocate space to the partitions, then offsets */
1278         total_size = 0;         /* in bytes */
1279         total_percent = 0;      /* in percent */
1280         hog_part = -1;
1281         /* find all fixed partitions */
1282         for (i = 0; i < (int)lp->d_npartitions; i++) {
1283                 pp = &lp->d_partitions[i];
1284                 if (part_set[i]) {
1285                         if (part_size_type[i] == '*') {
1286                                 if (part_offset_type[i] != '*') {
1287                                         if (total_size < pp->p_boffset)
1288                                                 total_size = pp->p_boffset;
1289                                 }
1290                                 if (hog_part != -1) {
1291                                         Warning("Too many '*' partitions (%c and %c)",
1292                                             hog_part + 'a',i + 'a');
1293                                 } else {
1294                                         hog_part = i;
1295                                 }
1296                         } else {
1297                                 off_t size;
1298
1299                                 size = pp->p_bsize;
1300                                 if (part_size_type[i] == '%') {
1301                                         /* 
1302                                          * don't count %'s yet
1303                                          */
1304                                         total_percent += size;
1305                                 } else {
1306                                         /*
1307                                          * Value has already been converted
1308                                          * to bytes.
1309                                          */
1310                                         if (size % lp->d_align != 0) {
1311                                                 Warning("partition %c's size is not properly aligned",
1312                                                         i + 'a');
1313                                         }
1314                                         total_size += size;
1315                                 }
1316                         }
1317                 }
1318         }
1319         /* handle % partitions - note %'s don't need to add up to 100! */
1320         if (total_percent != 0) {
1321                 int64_t free_space;
1322                 int64_t space_left;
1323
1324                 free_space = (int64_t)(lp->d_pstop - lp->d_pbase - total_size);
1325                 space_left = free_space;
1326                 if (total_percent > 100) {
1327                         fprintf(stderr,"total percentage %lu is greater than 100\n",
1328                             total_percent);
1329                         errors++;
1330                 }
1331
1332                 if (free_space > 0) {
1333                         for (i = 0; i < (int)lp->d_npartitions; i++) {
1334                                 pp = &lp->d_partitions[i];
1335                                 if (part_set[i] && part_size_type[i] == '%') {
1336                                         /* careful of overflows! and integer roundoff */
1337                                         pp->p_bsize = ((double)pp->p_bsize/100) * free_space;
1338                                         pp->p_bsize = (pp->p_bsize + lp->d_align - 1) & ~(u_int64_t)(lp->d_align - 1);
1339                                         if ((int64_t)pp->p_bsize > space_left)
1340                                                 pp->p_bsize = (u_int64_t)space_left;
1341                                         total_size += pp->p_bsize;
1342                                         space_left -= pp->p_bsize;
1343                                 }
1344                         }
1345                 } else {
1346                         fprintf(stderr,
1347                             "%lld bytes available to give to '*' and '%%' partitions\n",
1348                             free_space);
1349                         errors++;
1350                         /* fix?  set all % partitions to size 0? */
1351                 }
1352         }
1353         /* give anything remaining to the hog partition */
1354         if (hog_part != -1) {
1355                 lp->d_partitions[hog_part].p_bsize = lp->d_pstop - lp->d_pbase - total_size;
1356                 total_size = lp->d_pstop - lp->d_pbase;
1357         }
1358
1359         /* Now set the offsets for each partition */
1360         current_offset = lp->d_pbase;
1361         seen_default_offset = 0;
1362         for (i = 0; i < (int)lp->d_npartitions; i++) {
1363                 part = 'a' + i;
1364                 pp = &lp->d_partitions[i];
1365                 if (pp->p_bsize == 0)
1366                         continue;
1367                 if (part_set[i]) {
1368                         if (part_offset_type[i] == '*') {
1369                                 pp->p_boffset = current_offset;
1370                                 seen_default_offset = 1;
1371                         } else {
1372                                 /* allow them to be out of order for old-style tables */
1373                                 if (pp->p_boffset < current_offset && 
1374                                     seen_default_offset &&
1375                                     pp->p_fstype != FS_VINUM) {
1376                                         fprintf(stderr,
1377 "Offset 0x%012llx for partition %c overlaps previous partition which ends at 0x%012llx\n",
1378                                             pp->p_boffset, i + 'a',
1379                                             current_offset);
1380                                         fprintf(stderr,
1381 "Labels with any *'s for offset must be in ascending order by sector\n");
1382                                         errors++;
1383                                 } else if (pp->p_boffset != current_offset &&
1384                                            seen_default_offset) {
1385                                         /* 
1386                                          * this may give unneeded warnings if 
1387                                          * partitions are out-of-order
1388                                          */
1389                                         Warning(
1390 "Offset 0x%012llx for partition %c doesn't match expected value 0x%012llx",
1391                                             pp->p_boffset, i + 'a',
1392                                             current_offset);
1393                                 }
1394                         }
1395                         current_offset = pp->p_boffset + pp->p_bsize; 
1396                 }
1397         }
1398
1399         for (i = 0; i < (int)lp->d_npartitions; i++) {
1400                 part = 'a' + i;
1401                 pp = &lp->d_partitions[i];
1402                 if (pp->p_bsize == 0 && pp->p_boffset != 0)
1403                         Warning("partition %c: size 0, but offset 0x%012llx",
1404                             part, pp->p_boffset);
1405                 if (pp->p_bsize == 0) {
1406                         pp->p_boffset = 0;
1407                         continue;
1408                 }
1409                 if (uuid_is_nil(&pp->p_stor_uuid, NULL))
1410                         uuid_create(&pp->p_stor_uuid, NULL);
1411
1412                 if (pp->p_boffset < lp->d_pbase) {
1413                         fprintf(stderr,
1414                             "partition %c: offset out of bounds (%lld)\n",
1415                             part, pp->p_boffset - lp->d_pbase);
1416                         errors++;
1417                 }
1418                 if (pp->p_boffset > lp->d_pstop) {
1419                         fprintf(stderr,
1420                             "partition %c: offset out of bounds (%lld)\n",
1421                             part, pp->p_boffset - lp->d_pbase);
1422                         errors++;
1423                 }
1424                 if (pp->p_boffset + pp->p_bsize > lp->d_pstop) {
1425                         fprintf(stderr,
1426                             "partition %c: size out of bounds (%lld)\n",
1427                             part, pp->p_boffset - lp->d_pbase);
1428                         errors++;
1429                 }
1430
1431                 /* check for overlaps */
1432                 /* this will check for all possible overlaps once and only once */
1433                 for (j = 0; j < i; j++) {
1434                         pp2 = &lp->d_partitions[j];
1435                         if (pp->p_fstype != FS_VINUM &&
1436                             pp2->p_fstype != FS_VINUM &&
1437                             part_set[i] && part_set[j]) {
1438                                 if (pp2->p_boffset < pp->p_boffset + pp->p_bsize &&
1439                                     (pp2->p_boffset + pp2->p_bsize > pp->p_boffset ||
1440                                         pp2->p_boffset >= pp->p_boffset)) {
1441                                         fprintf(stderr,"partitions %c and %c overlap!\n",
1442                                             j + 'a', i + 'a');
1443                                         errors++;
1444                                 }
1445                         }
1446                 }
1447         }
1448         for (; i < (int)lp->d_npartitions; i++) {
1449                 part = 'a' + i;
1450                 pp = &lp->d_partitions[i];
1451                 if (pp->p_bsize || pp->p_boffset)
1452                         Warning("unused partition %c: size 0x%012llx offset 0x%012llx",
1453                             'a' + i, pp->p_bsize, pp->p_boffset);
1454         }
1455         return (errors);
1456 }
1457
1458 /*
1459  * When operating on a "virgin" disk, try getting an initial label
1460  * from the associated device driver.  This might work for all device
1461  * drivers that are able to fetch some initial device parameters
1462  * without even having access to a (BSD) disklabel, like SCSI disks,
1463  * most IDE drives, or vn devices.
1464  *
1465  * The device name must be given in its "canonical" form.
1466  */
1467 static struct disklabel64 dlab;
1468
1469 struct disklabel64 *
1470 getvirginlabel(void)
1471 {
1472         struct disklabel64 *dl = &dlab;
1473         char *path;
1474         int f;
1475
1476         if (dkname[0] == '/') {
1477                 warnx("\"auto\" requires the usage of a canonical disk name");
1478                 return (NULL);
1479         }
1480         asprintf(&path, "%s%s", _PATH_DEV, dkname);
1481         if ((f = open(path, O_RDONLY)) == -1) {
1482                 warn("cannot open %s", path);
1483                 return (NULL);
1484         }
1485
1486         /*
1487          * Try to use the new get-virgin-label ioctl.  If it fails,
1488          * fallback to the old get-disk-info ioctl.
1489          */
1490         if (ioctl(f, DIOCGDVIRGIN64, dl) < 0) {
1491                 l_perror("ioctl DIOCGDVIRGIN64");
1492                 close(f);
1493                 return (NULL);
1494         }
1495         close(f);
1496         return (dl);
1497 }
1498
1499 /*VARARGS1*/
1500 void
1501 Warning(const char *fmt, ...)
1502 {
1503         va_list ap;
1504
1505         fprintf(stderr, "Warning, ");
1506         va_start(ap, fmt);
1507         vfprintf(stderr, fmt, ap);
1508         fprintf(stderr, "\n");
1509         va_end(ap);
1510 }
1511
1512 void
1513 usage(void)
1514 {
1515         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1516                 "usage: disklabel64 [-r] disk",
1517                 "\t\t(to read label)",
1518                 "       disklabel64 -w [-r] [-n] disk type [ packid ]",
1519                 "\t\t(to write label with existing boot program)",
1520                 "       disklabel64 -e [-r] [-n] disk",
1521                 "\t\t(to edit label)",
1522                 "       disklabel64 -R [-r] [-n] disk protofile",
1523                 "\t\t(to restore label with existing boot program)",
1524 #if 0
1525                 "       disklabel64 -B [-n] [ -b boot1 [ -s boot2 ] ] disk [ type ]",
1526                 "\t\t(to install boot program with existing label)",
1527                 "       disklabel64 -w -B [-n] [ -b boot1 [ -s boot2 ] ] disk type [ packid ]",
1528                 "\t\t(to write label and boot program)",
1529                 "       disklabel64 -R -B [-n] [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]",
1530                 "\t\t(to restore label and boot program)",
1531 #endif
1532                 "       disklabel64 [-NW] disk",
1533                 "\t\t(to write disable/enable label)");
1534         exit(1);
1535 }