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