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