52fa108bc8bedc857db5203d183c2bbc9f26f06b
[dragonfly.git] / sbin / disklabel / disklabel.c
1 /*
2  * Copyright (c) 1987, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Symmetric Computer Systems.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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 the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#) Copyright (c) 1987, 1993 The Regents of the University of California.  All rights reserved.
37  * @(#)disklabel.c      1.2 (Symmetric) 11/28/85
38  * @(#)disklabel.c      8.2 (Berkeley) 1/7/94
39  * $FreeBSD: src/sbin/disklabel/disklabel.c,v 1.28.2.15 2003/01/24 16:18:16 des Exp $
40  * $DragonFly: src/sbin/disklabel/disklabel.c,v 1.28 2008/08/21 21:22:36 thomas Exp $
41  */
42
43 #include <sys/param.h>
44 #include <sys/file.h>
45 #include <sys/stat.h>
46 #include <sys/wait.h>
47 #define DKTYPENAMES
48 #include <sys/disklabel32.h>
49 #include <sys/disklabel64.h>
50 #include <sys/diskslice.h>
51 #include <sys/diskmbr.h>
52 #include <sys/dtype.h>
53 #include <sys/sysctl.h>
54 #include <disktab.h>
55
56 #include <vfs/ufs/dinode.h>
57 #include <vfs/ufs/fs.h>
58
59 #include <unistd.h>
60 #include <string.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <signal.h>
64 #include <stdarg.h>
65 #include <ctype.h>
66 #include <err.h>
67 #include <errno.h>
68 #include <disktab.h>
69 #include "pathnames.h"
70
71 /*
72  * Disklabel: read and write disklabels.
73  * The label is usually placed on one of the first sectors of the disk.
74  * Many machines also place a bootstrap in the same area,
75  * in which case the label is embedded in the bootstrap.
76  * The bootstrap source must leave space at the proper offset
77  * for the label on such machines.
78  */
79
80 #ifndef BBSIZE
81 #define BBSIZE  8192                    /* size of boot area, with label */
82 #endif
83
84 /* FIX!  These are too low, but are traditional */
85 #define DEFAULT_NEWFS_BLOCK  8192U
86 #define DEFAULT_NEWFS_FRAG   1024U
87 #define DEFAULT_NEWFS_CPG    16U
88
89 #define BIG_NEWFS_BLOCK  16384U
90 #define BIG_NEWFS_FRAG   2048U
91 #define BIG_NEWFS_CPG    64U
92
93 #define NUMBOOT 2
94
95 void    makelabel(const char *, const char *, struct disklabel32 *);
96 int     writelabel(int, const char *, struct disklabel32 *);
97 void    l_perror(const char *);
98 struct disklabel32 *readlabel(int);
99 struct disklabel32 *makebootarea(char *, struct disklabel32 *, int);
100 void    display(FILE *, const struct disklabel32 *);
101 int     edit(struct disklabel32 *, int);
102 int     editit(void);
103 char    *skip(char *);
104 char    *word(char *);
105 int     getasciilabel(FILE *, struct disklabel32 *);
106 int     getasciipartspec(char *, struct disklabel32 *, int, int);
107 int     checklabel(struct disklabel32 *);
108 void    setbootflag(struct disklabel32 *);
109 void    Warning(const char *, ...) __printflike(1, 2);
110 void    usage(void);
111 int     checkoldboot(int, const char *);
112 const char *fixlabel(int, struct disklabel32 *, int);
113 struct disklabel32 *getvirginlabel(void);
114 struct disklabel32 *getdisklabelfromdisktab(const char *name);
115
116 #define DEFEDITOR       _PATH_VI
117 #define streq(a,b)      (strcmp(a,b) == 0)
118
119 char    *dkname;
120 char    *specname;
121 char    tmpfil[] = PATH_TMPFILE;
122
123 char    namebuf[BBSIZE], *np = namebuf;
124 struct  disklabel32 lab;
125 char    bootarea[BBSIZE];
126
127 #define MAX_PART ('z')
128 #define MAX_NUM_PARTS (1 + MAX_PART - 'a')
129 char    part_size_type[MAX_NUM_PARTS];
130 char    part_offset_type[MAX_NUM_PARTS];
131 int     part_set[MAX_NUM_PARTS];
132
133 #if NUMBOOT > 0
134 int     installboot;    /* non-zero if we should install a boot program */
135 char    *bootbuf;       /* pointer to buffer with remainder of boot prog */
136 int     bootsize;       /* size of remaining boot program */
137 char    *xxboot;        /* primary boot */
138 char    *bootxx;        /* secondary boot */
139 char    boot0[MAXPATHLEN];
140 char    boot1[MAXPATHLEN];
141 #endif
142
143 enum    {
144         UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT
145 } op = UNSPEC;
146
147 int     rflag;
148 int     disable_write;   /* set to disable writing to disk label */
149 int     forceflag;
150 u_int32_t slice_start_lba;
151
152 #ifdef DEBUG
153 int     debug;
154 #define OPTIONS "BNRWb:def:nrs:w"
155 #else
156 #define OPTIONS "BNRWb:ef:nrs:w"
157 #endif
158
159 int
160 main(int argc, char *argv[])
161 {
162         struct disklabel32 *lp;
163         FILE *t;
164         int ch, f = 0, flag, error = 0;
165         char *name = 0;
166
167         while ((ch = getopt(argc, argv, OPTIONS)) != -1)
168                 switch (ch) {
169 #if NUMBOOT > 0
170                         case 'B':
171                                 ++installboot;
172                                 break;
173                         case 'b':
174                                 xxboot = optarg;
175                                 break;
176
177                         case 'f':
178                                 forceflag = 1;
179                                 slice_start_lba = strtoul(optarg, NULL, 0);
180                                 break;
181 #if NUMBOOT > 1
182                         case 's':
183                                 bootxx = optarg;
184                                 break;
185 #endif
186 #endif
187                         case 'N':
188                                 if (op != UNSPEC)
189                                         usage();
190                                 op = NOWRITE;
191                                 break;
192                         case 'n':
193                                 disable_write = 1;
194                                 break;
195                         case 'R':
196                                 if (op != UNSPEC)
197                                         usage();
198                                 op = RESTORE;
199                                 break;
200                         case 'W':
201                                 if (op != UNSPEC)
202                                         usage();
203                                 op = WRITEABLE;
204                                 break;
205                         case 'e':
206                                 if (op != UNSPEC)
207                                         usage();
208                                 op = EDIT;
209                                 break;
210                         case 'r':
211                                 ++rflag;
212                                 break;
213                         case 'w':
214                                 if (op != UNSPEC)
215                                         usage();
216                                 op = WRITE;
217                                 break;
218 #ifdef DEBUG
219                         case 'd':
220                                 debug++;
221                                 break;
222 #endif
223                         case '?':
224                         default:
225                                 usage();
226                 }
227         argc -= optind;
228         argv += optind;
229 #if NUMBOOT > 0
230         if (installboot) {
231                 rflag++;
232                 if (op == UNSPEC)
233                         op = WRITEBOOT;
234         } else {
235                 if (op == UNSPEC)
236                         op = READ;
237                 xxboot = bootxx = 0;
238         }
239 #else
240         if (op == UNSPEC)
241                 op = READ;
242 #endif
243         if (argc < 1)
244                 usage();
245
246         dkname = argv[0];
247         if (dkname[0] != '/') {
248                 sprintf(np, "%s%s", _PATH_DEV, dkname);
249                 specname = np;
250                 np += strlen(specname) + 1;
251         } else {
252                 specname = dkname;
253         }
254         f = open(specname, op == READ ? O_RDONLY : O_RDWR);
255         if (f < 0 && errno == ENOENT && dkname[0] != '/') {
256                 sprintf(specname, "%s%s", _PATH_DEV, dkname);
257                 np = namebuf + strlen(specname) + 1;
258                 f = open(specname, op == READ ? O_RDONLY : O_RDWR);
259         }
260         if (f < 0)
261                 err(4, "%s", specname);
262
263         switch(op) {
264
265         case UNSPEC:
266                 break;
267
268         case EDIT:
269                 if (argc != 1)
270                         usage();
271                 lp = readlabel(f);
272                 error = edit(lp, f);
273                 break;
274
275         case NOWRITE:
276                 flag = 0;
277                 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
278                         err(4, "ioctl DIOCWLABEL");
279                 break;
280
281         case READ:
282                 if (argc != 1)
283                         usage();
284                 lp = readlabel(f);
285                 display(stdout, lp);
286                 error = checklabel(lp);
287                 if (checkoldboot(f, NULL))
288                         warnx("Warning, old bootblocks detected, install new bootblocks & reinstall the disklabel");
289                 break;
290
291         case RESTORE:
292 #if NUMBOOT > 0
293                 if (installboot && argc == 3) {
294                         makelabel(argv[2], 0, &lab);
295                         argc--;
296
297                         /*
298                          * We only called makelabel() for its side effect
299                          * of setting the bootstrap file names.  Discard
300                          * all changes to `lab' so that all values in the
301                          * final label come from the ASCII label.
302                          */
303                         bzero((char *)&lab, sizeof(lab));
304                 }
305 #endif
306                 if (argc != 2)
307                         usage();
308                 if (!(t = fopen(argv[1], "r")))
309                         err(4, "%s", argv[1]);
310                 if (!getasciilabel(t, &lab))
311                         exit(1);
312                 lp = makebootarea(bootarea, &lab, f);
313                 *lp = lab;
314                 error = writelabel(f, bootarea, lp);
315                 break;
316
317         case WRITE:
318                 if (argc == 3) {
319                         name = argv[2];
320                         argc--;
321                 }
322                 if (argc != 2)
323                         usage();
324                 makelabel(argv[1], name, &lab);
325                 lp = makebootarea(bootarea, &lab, f);
326                 *lp = lab;
327                 if (checklabel(lp) == 0)
328                         error = writelabel(f, bootarea, lp);
329                 break;
330
331         case WRITEABLE:
332                 flag = 1;
333                 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
334                         err(4, "ioctl DIOCWLABEL");
335                 break;
336
337 #if NUMBOOT > 0
338         case WRITEBOOT:
339         {
340                 struct disklabel32 tlab;
341
342                 lp = readlabel(f);
343                 tlab = *lp;
344                 if (argc == 2)
345                         makelabel(argv[1], 0, &lab);
346                 lp = makebootarea(bootarea, &lab, f);
347                 *lp = tlab;
348                 if (checklabel(lp) == 0)
349                         error = writelabel(f, bootarea, lp);
350                 break;
351         }
352 #endif
353         }
354         exit(error);
355 }
356
357 /*
358  * Construct a prototype disklabel from /etc/disktab.  As a side
359  * effect, set the names of the primary and secondary boot files
360  * if specified.
361  */
362 void
363 makelabel(const char *type, const char *name, struct disklabel32 *lp)
364 {
365         struct disklabel32 *dp;
366
367         if (strcmp(type, "auto") == 0)
368                 dp = getvirginlabel();
369         else
370                 dp = getdisklabelfromdisktab(type);
371         if (dp == NULL)
372                 errx(1, "%s: unknown disk type", type);
373         *lp = *dp;
374
375         /*
376          * NOTE: boot control files may no longer be specified in disktab.
377          */
378         if (name)
379                 strncpy(lp->d_packname, name, sizeof(lp->d_packname));
380 }
381
382 int
383 writelabel(int f, const char *boot, struct disklabel32 *lp)
384 {
385         const char *msg;
386         int flag;
387         int r;
388
389         if (disable_write) {
390                 Warning("write to disk label suppressed - label was as follows:");
391                 display(stdout, lp);
392                 return (0);
393         } else {
394                 /* make sure we are not overwriting our boot code */
395                 if (checkoldboot(f, boot))
396                         errx(4, "Will not overwrite old bootblocks w/ label, install new boot blocks first!");
397                 setbootflag(lp);
398                 lp->d_magic = DISKMAGIC32;
399                 lp->d_magic2 = DISKMAGIC32;
400                 lp->d_checksum = 0;
401                 lp->d_checksum = dkcksum32(lp);
402                 if (rflag) {
403                         /*
404                          * First set the kernel disk label,
405                          * then write a label to the raw disk.
406                          * If the SDINFO ioctl fails because it is unimplemented,
407                          * keep going; otherwise, the kernel consistency checks
408                          * may prevent us from changing the current (in-core)
409                          * label.
410                          */
411                         if (ioctl(f, DIOCSDINFO32, lp) < 0 &&
412                                 errno != ENODEV && errno != ENOTTY) {
413                                 l_perror("ioctl DIOCSDINFO32");
414                                 return (1);
415                         }
416                         lseek(f, (off_t)0, SEEK_SET);
417                         
418                         /*
419                          * write enable label sector before write
420                          * (if necessary), disable after writing.
421                          */
422                         flag = 1;
423                         if (ioctl(f, DIOCWLABEL, &flag) < 0)
424                                 warn("ioctl DIOCWLABEL");
425                         msg = fixlabel(f, lp, 1);
426                         if (msg) {
427                                 warn(msg);
428                                 return (1);
429                         }
430                         r = write(f, boot, lp->d_bbsize);
431                         fixlabel(f, lp, 0);
432                         if (r != ((ssize_t)lp->d_bbsize)) {
433                                 warn("write");
434                                 return (1);
435                         }
436 #if NUMBOOT > 0
437                         /*
438                          * Output the remainder of the disklabel
439                          */
440                         if (bootbuf) {
441                                 fixlabel(f, lp, 1);
442                                 r = write(f, bootbuf, bootsize);
443                                 fixlabel(f, lp, 0);
444                                 if (r != bootsize) {
445                                         warn("write");
446                                         return(1);
447                                 }
448                         }
449 #endif
450                         flag = 0;
451                         ioctl(f, DIOCWLABEL, &flag);
452                 } else if (ioctl(f, DIOCWDINFO32, lp) < 0) {
453                         l_perror("ioctl DIOCWDINFO32");
454                         return (1);
455                 }
456         }
457         return (0);
458 }
459
460 void
461 l_perror(const char *s)
462 {
463         switch (errno) {
464
465         case ESRCH:
466                 warnx("%s: no disk label on disk;", s);
467                 fprintf(stderr, "add \"-r\" to install initial label\n");
468                 break;
469
470         case EINVAL:
471                 warnx("%s: label magic number or checksum is wrong!", s);
472                 fprintf(stderr, "(disklabel or kernel is out of date?)\n");
473                 break;
474
475         case EBUSY:
476                 warnx("%s: open partition would move or shrink", s);
477                 break;
478
479         case EXDEV:
480                 warnx("%s: '%c' partition must start at beginning of disk",
481                     s, 'a' + RAW_PART);
482                 break;
483
484         case ENOATTR:
485                 warnx("%s: the disk already has a label of a different type,\n"
486                       "probably a 64 bit disklabel.  It must be cleaned out "
487                       "first.\n", s);
488                 break;
489
490         default:
491                 warn((char *)NULL);
492                 break;
493         }
494 }
495
496 /*
497  * Fetch disklabel for disk.
498  * Use ioctl to get label unless -r flag is given.
499  */
500 struct disklabel32 *
501 readlabel(int f)
502 {
503         const char *msg;
504         struct disklabel32 *lp;
505         int r;
506
507         if (rflag) {
508                 r = read(f, bootarea, BBSIZE);
509                 if (r < BBSIZE)
510                         err(4, "%s", specname);
511                 for (lp = (struct disklabel32 *)bootarea;
512                     lp <= (struct disklabel32 *)(bootarea + BBSIZE - sizeof(*lp));
513                     lp = (struct disklabel32 *)((char *)lp + 16)) {
514                         if (lp->d_magic == DISKMAGIC32 &&
515                             lp->d_magic2 == DISKMAGIC32)
516                                 break;
517                 }
518                 if (lp > (struct disklabel32 *)(bootarea+BBSIZE-sizeof(*lp)) ||
519                     lp->d_magic != DISKMAGIC32 || lp->d_magic2 != DISKMAGIC32 ||
520                     dkcksum32(lp) != 0) {
521                         errx(1, "bad pack magic number (label is damaged, "
522                                 "or pack is unlabeled)");
523                 }
524                 if ((msg = fixlabel(f, lp, 0)) != NULL)
525                         errx(1, msg);
526         } else {
527                 lp = &lab;
528                 if (ioctl(f, DIOCGDINFO32, lp) < 0) {
529                         l_perror("ioctl DIOCGDINFO32");
530                         exit(4);
531                 }
532         }
533         return (lp);
534 }
535
536 /*
537  * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
538  * Returns a pointer to the disklabel portion of the bootarea.
539  */
540 struct disklabel32 *
541 makebootarea(char *boot, struct disklabel32 *dp, int f)
542 {
543         struct disklabel32 *lp;
544         char *p;
545         int b;
546 #if NUMBOOT > 0
547         char *dkbasename;
548         struct stat sb;
549 #endif
550 #ifdef __i386__
551         char *tmpbuf;
552         unsigned int i, found;
553 #endif
554
555         /* XXX */
556         if (dp->d_secsize == 0) {
557                 dp->d_secsize = DEV_BSIZE;
558                 dp->d_bbsize = BBSIZE;
559         }
560         lp = (struct disklabel32 *)
561                 (boot + (LABELSECTOR32 * dp->d_secsize) + LABELOFFSET32);
562         bzero((char *)lp, sizeof *lp);
563 #if NUMBOOT > 0
564         /*
565          * If we are not installing a boot program but we are installing a
566          * label on disk then we must read the current bootarea so we don't
567          * clobber the existing boot.
568          */
569         if (!installboot) {
570                 if (rflag) {
571                         if (read(f, boot, BBSIZE) < BBSIZE)
572                                 err(4, "%s", specname);
573                         bzero((char *)lp, sizeof *lp);
574                 }
575                 return (lp);
576         }
577         /*
578          * We are installing a boot program.  Determine the name(s) and
579          * read them into the appropriate places in the boot area.
580          */
581         if (!xxboot || !bootxx) {
582                 dkbasename = np;
583                 if ((p = strrchr(dkname, '/')) == NULL)
584                         p = dkname;
585                 else
586                         p++;
587                 while (*p && !isdigit(*p))
588                         *np++ = *p++;
589                 *np++ = '\0';
590
591                 if (!xxboot) {
592                         sprintf(boot0, "%s/boot1", _PATH_BOOTDIR);
593                         xxboot = boot0;
594                 }
595 #if NUMBOOT > 1
596                 if (!bootxx) {
597                         sprintf(boot1, "%s/boot2", _PATH_BOOTDIR);
598                         bootxx = boot1;
599                 }
600 #endif
601         }
602 #ifdef DEBUG
603         if (debug)
604                 fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n",
605                         xxboot, bootxx ? bootxx : "NONE");
606 #endif
607
608         /*
609          * Strange rules:
610          * 1. One-piece bootstrap (hp300/hp800)
611          *      up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest
612          *      is remembered and written later following the bootarea.
613          * 2. Two-piece bootstraps (vax/i386?/mips?)
614          *      up to d_secsize bytes of ``xxboot'' go in first d_secsize
615          *      bytes of bootarea, remaining d_bbsize-d_secsize filled
616          *      from ``bootxx''.
617          */
618         b = open(xxboot, O_RDONLY);
619         if (b < 0)
620                 err(4, "%s", xxboot);
621 #if NUMBOOT > 1
622 #ifdef __i386__
623         /*
624          * XXX Botch alert.
625          * The i386 has the so-called fdisk table embedded into the
626          * primary bootstrap.  We take care to not clobber it, but
627          * only if it does already contain some data.  (Otherwise,
628          * the xxboot provides a template.)
629          */
630         if ((tmpbuf = (char *)malloc((int)dp->d_secsize)) == 0)
631                 err(4, "%s", xxboot);
632         memcpy((void *)tmpbuf, (void *)boot, (int)dp->d_secsize);
633 #endif /* i386 */
634         if (read(b, boot, (int)dp->d_secsize) < 0)
635                 err(4, "%s", xxboot);
636         close(b);
637 #ifdef __i386__
638         for (i = DOSPARTOFF, found = 0;
639              !found && i < DOSPARTOFF + NDOSPART*sizeof(struct dos_partition);
640              i++)
641                 found = tmpbuf[i] != 0;
642         if (found)
643                 memcpy((void *)&boot[DOSPARTOFF],
644                        (void *)&tmpbuf[DOSPARTOFF],
645                        NDOSPART * sizeof(struct dos_partition));
646         free(tmpbuf);
647 #endif /* i386 */
648         b = open(bootxx, O_RDONLY);
649         if (b < 0)
650                 err(4, "%s", bootxx);
651         if (fstat(b, &sb) != 0)
652                 err(4, "%s", bootxx);
653         if (dp->d_secsize + sb.st_size > dp->d_bbsize)
654                 errx(4, "%s too large", bootxx);
655         if (read(b, &boot[dp->d_secsize],
656                  (int)(dp->d_bbsize-dp->d_secsize)) < 0)
657                 err(4, "%s", bootxx);
658 #else /* !(NUMBOOT > 1) */
659         if (read(b, boot, (int)dp->d_bbsize) < 0)
660                 err(4, "%s", xxboot);
661         if (fstat(b, &sb) != 0)
662                 err(4, "%s", xxboot);
663         bootsize = (int)sb.st_size - dp->d_bbsize;
664         if (bootsize > 0) {
665                 /* XXX assume d_secsize is a power of two */
666                 bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
667                 bootbuf = (char *)malloc((size_t)bootsize);
668                 if (bootbuf == 0)
669                         err(4, "%s", xxboot);
670                 if (read(b, bootbuf, bootsize) < 0) {
671                         free(bootbuf);
672                         err(4, "%s", xxboot);
673                 }
674         }
675 #endif /* NUMBOOT > 1 */
676         close(b);
677 #endif /* NUMBOOT > 0 */
678         /*
679          * Make sure no part of the bootstrap is written in the area
680          * reserved for the label.
681          */
682         for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel32); p++)
683                 if (*p)
684                         errx(2, "bootstrap doesn't leave room for disk label");
685         return (lp);
686 }
687
688 void
689 display(FILE *f, const struct disklabel32 *lp)
690 {
691         int i, j;
692         const struct partition32 *pp;
693
694         fprintf(f, "# %s:\n", specname);
695         if (lp->d_type < DKMAXTYPES)
696                 fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
697         else
698                 fprintf(f, "type: %u\n", lp->d_type);
699         fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
700                 lp->d_typename);
701         fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
702                 lp->d_packname);
703         fprintf(f, "flags:");
704         fprintf(f, "\n");
705         fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
706         fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
707         fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
708         fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
709         fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
710         fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
711         fprintf(f, "rpm: %u\n", lp->d_rpm);
712         fprintf(f, "interleave: %u\n", lp->d_interleave);
713         fprintf(f, "trackskew: %u\n", lp->d_trackskew);
714         fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
715         fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
716             (u_long)lp->d_headswitch);
717         fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
718             (u_long)lp->d_trkseek);
719         fprintf(f, "drivedata: ");
720         for (i = NDDATA32 - 1; i >= 0; i--) {
721                 if (lp->d_drivedata[i])
722                         break;
723         }
724         if (i < 0)
725                 i = 0;
726         for (j = 0; j <= i; j++)
727                 fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
728         fprintf(f, "\n\n%u partitions:\n", lp->d_npartitions);
729         fprintf(f,
730             "#          size     offset    fstype\n");
731         pp = lp->d_partitions;
732         for (i = 0; i < lp->d_npartitions; i++, pp++) {
733                 if (pp->p_size) {
734                         u_long onemeg = 1024 * 1024 / lp->d_secsize;
735                         fprintf(f, "  %c: ", 'a' + i);
736
737                         fprintf(f, "%10lu ", (u_long)pp->p_size);
738                         fprintf(f, "%10lu  ", (u_long)pp->p_offset);
739                         if (pp->p_fstype < FSMAXTYPES)
740                                 fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
741                         else
742                                 fprintf(f, "%8d", pp->p_fstype);
743
744                         fprintf(f, "\t# %11.3fMB", (double)pp->p_size / onemeg);
745                         fprintf(f, "\n");
746                 }
747         }
748         fflush(f);
749 }
750
751 int
752 edit(struct disklabel32 *lp, int f)
753 {
754         int c, fd;
755         struct disklabel32 label;
756         FILE *fp;
757
758         if ((fd = mkstemp(tmpfil)) == -1 ||
759             (fp = fdopen(fd, "w")) == NULL) {
760                 warnx("can't create %s", tmpfil);
761                 return (1);
762         }
763         display(fp, lp);
764         fclose(fp);
765         for (;;) {
766                 if (!editit())
767                         break;
768                 fp = fopen(tmpfil, "r");
769                 if (fp == NULL) {
770                         warnx("can't reopen %s for reading", tmpfil);
771                         break;
772                 }
773                 bzero((char *)&label, sizeof(label));
774                 if (getasciilabel(fp, &label)) {
775                         *lp = label;
776                         if (writelabel(f, bootarea, lp) == 0) {
777                                 fclose(fp);
778                                 unlink(tmpfil);
779                                 return (0);
780                         }
781                 }
782                 fclose(fp);
783                 printf("re-edit the label? [y]: "); fflush(stdout);
784                 c = getchar();
785                 if (c != EOF && c != (int)'\n')
786                         while (getchar() != (int)'\n')
787                                 ;
788                 if  (c == (int)'n')
789                         break;
790         }
791         unlink(tmpfil);
792         return (1);
793 }
794
795 int
796 editit(void)
797 {
798         int pid, xpid;
799         int status, omask;
800         const char *ed;
801
802         omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
803         while ((pid = fork()) < 0) {
804                 if (errno == EPROCLIM) {
805                         warnx("you have too many processes");
806                         return(0);
807                 }
808                 if (errno != EAGAIN) {
809                         warn("fork");
810                         return(0);
811                 }
812                 sleep(1);
813         }
814         if (pid == 0) {
815                 sigsetmask(omask);
816                 setgid(getgid());
817                 setuid(getuid());
818                 if ((ed = getenv("EDITOR")) == (char *)0)
819                         ed = DEFEDITOR;
820                 execlp(ed, ed, tmpfil, (char *)0);
821                 err(1, "%s", ed);
822         }
823         while ((xpid = wait(&status)) >= 0)
824                 if (xpid == pid)
825                         break;
826         sigsetmask(omask);
827         return(!status);
828 }
829
830 char *
831 skip(char *cp)
832 {
833
834         while (*cp != '\0' && isspace(*cp))
835                 cp++;
836         if (*cp == '\0' || *cp == '#')
837                 return (NULL);
838         return (cp);
839 }
840
841 char *
842 word(char *cp)
843 {
844         char c;
845
846         while (*cp != '\0' && !isspace(*cp) && *cp != '#')
847                 cp++;
848         if ((c = *cp) != '\0') {
849                 *cp++ = '\0';
850                 if (c != '#')
851                         return (skip(cp));
852         }
853         return (NULL);
854 }
855
856 /*
857  * Read an ascii label in from fd f,
858  * in the same format as that put out by display(),
859  * and fill in lp.
860  */
861 int
862 getasciilabel(FILE *f, struct disklabel32 *lp)
863 {
864         char *cp;
865         const char **cpp;
866         u_int part;
867         char *tp, line[BUFSIZ];
868         u_long v;
869         int lineno = 0, errors = 0;
870         int i;
871         char empty[] = "";
872         char unknown[] = "unknown";
873
874         bzero(&part_set, sizeof(part_set));
875         bzero(&part_size_type, sizeof(part_size_type));
876         bzero(&part_offset_type, sizeof(part_offset_type));
877         lp->d_bbsize = BBSIZE;                          /* XXX */
878         lp->d_sbsize = SBSIZE;                          /* XXX */
879         while (fgets(line, sizeof(line) - 1, f)) {
880                 lineno++;
881                 if ((cp = strchr(line,'\n')) != 0)
882                         *cp = '\0';
883                 cp = skip(line);
884                 if (cp == NULL)
885                         continue;
886                 tp = strchr(cp, ':');
887                 if (tp == NULL) {
888                         fprintf(stderr, "line %d: syntax error\n", lineno);
889                         errors++;
890                         continue;
891                 }
892                 *tp++ = '\0', tp = skip(tp);
893                 if (streq(cp, "type")) {
894                         if (tp == NULL)
895                                 tp = unknown;
896                         cpp = dktypenames;
897                         for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) {
898                                 if (*cpp && streq(*cpp, tp)) {
899                                         lp->d_type = cpp - dktypenames;
900                                         break;
901                                 }
902                         }
903                         if (cpp < &dktypenames[DKMAXTYPES])
904                                 continue;
905                         v = strtoul(tp, NULL, 10);
906                         if (v >= DKMAXTYPES) {
907                                 fprintf(stderr, "line %d:%s %lu\n", lineno,
908                                     "Warning, unknown disk type", v);
909                         }
910                         lp->d_type = v;
911                         continue;
912                 }
913                 if (streq(cp, "flags")) {
914                         for (v = 0; (cp = tp) && *cp != '\0';) {
915                                 tp = word(cp);
916                                 if (streq(cp, "removeable"))
917                                         v |= 0; /* obsolete */
918                                 else if (streq(cp, "ecc"))
919                                         v |= 0; /* obsolete */
920                                 else if (streq(cp, "badsect"))
921                                         v |= 0; /* obsolete */
922                                 else {
923                                         fprintf(stderr,
924                                             "line %d: %s: bad flag\n",
925                                             lineno, cp);
926                                         errors++;
927                                 }
928                         }
929                         lp->d_flags = v;
930                         continue;
931                 }
932                 if (streq(cp, "drivedata")) {
933                         for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA32;) {
934                                 lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
935                                 tp = word(cp);
936                         }
937                         continue;
938                 }
939                 if (sscanf(cp, "%lu partitions", &v) == 1) {
940                         if (v == 0 || v > MAXPARTITIONS32) {
941                                 fprintf(stderr,
942                                     "line %d: bad # of partitions\n", lineno);
943                                 lp->d_npartitions = MAXPARTITIONS32;
944                                 errors++;
945                         } else
946                                 lp->d_npartitions = v;
947                         continue;
948                 }
949                 if (tp == NULL)
950                         tp = empty;
951                 if (streq(cp, "disk")) {
952                         strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
953                         continue;
954                 }
955                 if (streq(cp, "label")) {
956                         strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
957                         continue;
958                 }
959                 if (streq(cp, "bytes/sector")) {
960                         v = strtoul(tp, NULL, 10);
961                         if (v == 0 || (v % DEV_BSIZE) != 0) {
962                                 fprintf(stderr,
963                                     "line %d: %s: bad sector size\n",
964                                     lineno, tp);
965                                 errors++;
966                         } else
967                                 lp->d_secsize = v;
968                         continue;
969                 }
970                 if (streq(cp, "sectors/track")) {
971                         v = strtoul(tp, NULL, 10);
972 #if (ULONG_MAX != 0xffffffffUL)
973                         if (v == 0 || v > 0xffffffff) {
974 #else
975                         if (v == 0) {
976 #endif
977                                 fprintf(stderr, "line %d: %s: bad %s\n",
978                                     lineno, tp, cp);
979                                 errors++;
980                         } else
981                                 lp->d_nsectors = v;
982                         continue;
983                 }
984                 if (streq(cp, "sectors/cylinder")) {
985                         v = strtoul(tp, NULL, 10);
986                         if (v == 0) {
987                                 fprintf(stderr, "line %d: %s: bad %s\n",
988                                     lineno, tp, cp);
989                                 errors++;
990                         } else
991                                 lp->d_secpercyl = v;
992                         continue;
993                 }
994                 if (streq(cp, "tracks/cylinder")) {
995                         v = strtoul(tp, NULL, 10);
996                         if (v == 0) {
997                                 fprintf(stderr, "line %d: %s: bad %s\n",
998                                     lineno, tp, cp);
999                                 errors++;
1000                         } else
1001                                 lp->d_ntracks = v;
1002                         continue;
1003                 }
1004                 if (streq(cp, "cylinders")) {
1005                         v = strtoul(tp, NULL, 10);
1006                         if (v == 0) {
1007                                 fprintf(stderr, "line %d: %s: bad %s\n",
1008                                     lineno, tp, cp);
1009                                 errors++;
1010                         } else
1011                                 lp->d_ncylinders = v;
1012                         continue;
1013                 }
1014                 if (streq(cp, "sectors/unit")) {
1015                         v = strtoul(tp, NULL, 10);
1016                         if (v == 0) {
1017                                 fprintf(stderr, "line %d: %s: bad %s\n",
1018                                     lineno, tp, cp);
1019                                 errors++;
1020                         } else
1021                                 lp->d_secperunit = v;
1022                         continue;
1023                 }
1024                 if (streq(cp, "rpm")) {
1025                         v = strtoul(tp, NULL, 10);
1026                         if (v == 0 || v > USHRT_MAX) {
1027                                 fprintf(stderr, "line %d: %s: bad %s\n",
1028                                     lineno, tp, cp);
1029                                 errors++;
1030                         } else
1031                                 lp->d_rpm = v;
1032                         continue;
1033                 }
1034                 if (streq(cp, "interleave")) {
1035                         v = strtoul(tp, NULL, 10);
1036                         if (v == 0 || v > USHRT_MAX) {
1037                                 fprintf(stderr, "line %d: %s: bad %s\n",
1038                                     lineno, tp, cp);
1039                                 errors++;
1040                         } else
1041                                 lp->d_interleave = v;
1042                         continue;
1043                 }
1044                 if (streq(cp, "trackskew")) {
1045                         v = strtoul(tp, NULL, 10);
1046                         if (v > USHRT_MAX) {
1047                                 fprintf(stderr, "line %d: %s: bad %s\n",
1048                                     lineno, tp, cp);
1049                                 errors++;
1050                         } else
1051                                 lp->d_trackskew = v;
1052                         continue;
1053                 }
1054                 if (streq(cp, "cylinderskew")) {
1055                         v = strtoul(tp, NULL, 10);
1056                         if (v > USHRT_MAX) {
1057                                 fprintf(stderr, "line %d: %s: bad %s\n",
1058                                     lineno, tp, cp);
1059                                 errors++;
1060                         } else
1061                                 lp->d_cylskew = v;
1062                         continue;
1063                 }
1064                 if (streq(cp, "headswitch")) {
1065                         v = strtoul(tp, NULL, 10);
1066                         lp->d_headswitch = v;
1067                         continue;
1068                 }
1069                 if (streq(cp, "track-to-track seek")) {
1070                         v = strtoul(tp, NULL, 10);
1071                         lp->d_trkseek = v;
1072                         continue;
1073                 }
1074                 /* the ':' was removed above */
1075                 if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
1076                         fprintf(stderr,
1077                             "line %d: %s: Unknown disklabel field\n", lineno,
1078                             cp);
1079                         errors++;
1080                         continue;
1081                 }
1082
1083                 /* Process a partition specification line. */
1084                 part = *cp - 'a';
1085                 if (part >= lp->d_npartitions) {
1086                         fprintf(stderr,
1087                             "line %d: partition name out of range a-%c: %s\n",
1088                             lineno, 'a' + lp->d_npartitions - 1, cp);
1089                         errors++;
1090                         continue;
1091                 }
1092                 part_set[part] = 1;
1093
1094                 if (getasciipartspec(tp, lp, part, lineno) != 0) {
1095                         errors++;
1096                         break;
1097                 }
1098         }
1099         errors += checklabel(lp);
1100         return (errors == 0);
1101 }
1102
1103 #define NXTNUM(n) do { \
1104         if (tp == NULL) { \
1105                 fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
1106                 return (1); \
1107         } else { \
1108                 cp = tp, tp = word(cp); \
1109                 (n) = strtoul(cp, NULL, 10); \
1110         } \
1111 } while (0)
1112
1113 /* retain 1 character following number */
1114 #define NXTWORD(w,n) do { \
1115         if (tp == NULL) { \
1116                 fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
1117                 return (1); \
1118         } else { \
1119                 char *tmp; \
1120                 cp = tp, tp = word(cp); \
1121                 (n) = strtoul(cp, &tmp, 10); \
1122                 if (tmp) (w) = *tmp; \
1123         } \
1124 } while (0)
1125
1126 /*
1127  * Read a partition line into partition `part' in the specified disklabel.
1128  * Return 0 on success, 1 on failure.
1129  */
1130 int
1131 getasciipartspec(char *tp, struct disklabel32 *lp, int part, int lineno)
1132 {
1133         struct partition32 *pp;
1134         char *cp;
1135         const char **cpp;
1136         u_long v;
1137
1138         pp = &lp->d_partitions[part];
1139         cp = NULL;
1140
1141         /*
1142          * size
1143          */
1144         v = 0;
1145         NXTWORD(part_size_type[part],v);
1146         if (v == 0 && part_size_type[part] != '*') {
1147                 fprintf(stderr,
1148                     "line %d: %s: bad partition size\n", lineno, cp);
1149                 return (1);
1150         }
1151         pp->p_size = v;
1152
1153         /*
1154          * offset
1155          */
1156         v = 0;
1157         NXTWORD(part_offset_type[part],v);
1158         if (v == 0 && part_offset_type[part] != '*' &&
1159             part_offset_type[part] != '\0') {
1160                 fprintf(stderr,
1161                     "line %d: %s: bad partition offset\n", lineno, cp);
1162                 return (1);
1163         }
1164         pp->p_offset = v;
1165
1166         /*
1167          * fstype
1168          */
1169         if (tp == NULL) {
1170                 fprintf(stderr,
1171                     "line %d: no filesystem type was specified\n", lineno);
1172                return(1);
1173         }
1174         cp = tp;
1175         tp = word(cp);
1176         for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1177                 if (*cpp && streq(*cpp, cp))
1178                         break;
1179         if (*cpp != NULL) {
1180                 pp->p_fstype = cpp - fstypenames;
1181         } else {
1182                 if (isdigit(*cp))
1183                         v = strtoul(cp, NULL, 10);
1184                 else
1185                         v = FSMAXTYPES;
1186                 if (v >= FSMAXTYPES) {
1187                         fprintf(stderr,
1188                             "line %d: Warning, unknown filesystem type %s\n",
1189                             lineno, cp);
1190                         v = FS_UNUSED;
1191                 }
1192                 pp->p_fstype = v;
1193         }
1194
1195         pp->p_fsize = 0;
1196         pp->p_frag = 0;
1197         pp->p_cpg = 0;
1198
1199         cp = tp;
1200         if (tp) {
1201                 fprintf(stderr, "line %d: Warning, fragment, block, "
1202                                 "and bps/cpg fields are no\n"
1203                                 "longer supported and must be specified "
1204                                 "via newfs options instead.\n",
1205                         lineno);
1206         }
1207         return(0);
1208 }
1209
1210 /*
1211  * Check disklabel for errors and fill in
1212  * derived fields according to supplied values.
1213  */
1214 int
1215 checklabel(struct disklabel32 *lp)
1216 {
1217         struct partition32 *pp;
1218         int i, errors = 0;
1219         char part;
1220         u_long base_offset, needed, total_size, total_percent, current_offset;
1221         long free_space;
1222         int seen_default_offset;
1223         int hog_part;
1224         int j;
1225         struct partition32 *pp2;
1226
1227         if (lp->d_secsize == 0) {
1228                 fprintf(stderr, "sector size 0\n");
1229                 return (1);
1230         }
1231         if (lp->d_nsectors == 0) {
1232                 fprintf(stderr, "sectors/track 0\n");
1233                 return (1);
1234         }
1235         if (lp->d_ntracks == 0) {
1236                 fprintf(stderr, "tracks/cylinder 0\n");
1237                 return (1);
1238         }
1239         if  (lp->d_ncylinders == 0) {
1240                 fprintf(stderr, "cylinders/unit 0\n");
1241                 errors++;
1242         }
1243         if (lp->d_rpm == 0)
1244                 Warning("revolutions/minute 0");
1245         if (lp->d_secpercyl == 0)
1246                 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1247         if (lp->d_secperunit == 0)
1248                 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1249         if (lp->d_bbsize == 0) {
1250                 fprintf(stderr, "boot block size 0\n");
1251                 errors++;
1252         } else if (lp->d_bbsize % lp->d_secsize)
1253                 Warning("boot block size %% sector-size != 0");
1254         if (lp->d_sbsize == 0) {
1255                 fprintf(stderr, "super block size 0\n");
1256                 errors++;
1257         } else if (lp->d_sbsize % lp->d_secsize)
1258                 Warning("super block size %% sector-size != 0");
1259         if (lp->d_npartitions > MAXPARTITIONS32)
1260                 Warning("number of partitions (%lu) > MAXPARTITIONS (%d)",
1261                     (u_long)lp->d_npartitions, MAXPARTITIONS32);
1262
1263         /* first allocate space to the partitions, then offsets */
1264         total_size = 0; /* in sectors */
1265         total_percent = 0; /* in percent */
1266         hog_part = -1;
1267         /* find all fixed partitions */
1268         for (i = 0; i < lp->d_npartitions; i++) {
1269                 pp = &lp->d_partitions[i];
1270                 if (part_set[i]) {
1271
1272                         if (part_size_type[i] == '*') {
1273                                 if (i == RAW_PART) {
1274                                         pp->p_size = lp->d_secperunit;
1275                                 } else {
1276                                         if (part_offset_type[i] != '*') {
1277                                                 if (total_size < pp->p_offset)
1278                                                         total_size = pp->p_offset;
1279                                         }
1280                                         if (hog_part != -1)
1281                                                 Warning("Too many '*' partitions (%c and %c)",
1282                                                     hog_part + 'a',i + 'a');
1283                                         else
1284                                                 hog_part = i;
1285                                 }
1286                         } else {
1287                                 off_t size;
1288
1289                                 size = pp->p_size;
1290                                 switch (part_size_type[i]) {
1291                                 case '%':
1292                                         total_percent += size;
1293                                         break;
1294                                 case 'k':
1295                                 case 'K':
1296                                         size *= 1024ULL;
1297                                         break;
1298                                 case 'm':
1299                                 case 'M':
1300                                         size *= 1024ULL * 1024ULL;
1301                                         break;
1302                                 case 'g':
1303                                 case 'G':
1304                                         size *= 1024ULL * 1024ULL * 1024ULL;
1305                                         break;
1306                                 case '\0':
1307                                         break;
1308                                 default:
1309                                         Warning("unknown size specifier '%c' (K/M/G are valid)",part_size_type[i]);
1310                                         break;
1311                                 }
1312                                 /* don't count %'s yet */
1313                                 if (part_size_type[i] != '%') {
1314                                         /*
1315                                          * for all not in sectors, convert to
1316                                          * sectors
1317                                          */
1318                                         if (part_size_type[i] != '\0') {
1319                                                 if (size % lp->d_secsize != 0)
1320                                                         Warning("partition %c not an integer number of sectors",
1321                                                             i + 'a');
1322                                                 size /= lp->d_secsize;
1323                                                 pp->p_size = size;
1324                                         }
1325                                         /* else already in sectors */
1326                                         if (i != RAW_PART)
1327                                                 total_size += size;
1328                                 }
1329                         }
1330                 }
1331         }
1332
1333         /* Find out the total free space, excluding the boot block area. */
1334         base_offset = BBSIZE / lp->d_secsize;
1335         free_space = 0;
1336         for (i = 0; i < lp->d_npartitions; i++) {
1337                 pp = &lp->d_partitions[i];
1338                 if (!part_set[i] || i == RAW_PART ||
1339                     part_size_type[i] == '%' || part_size_type[i] == '*')
1340                         continue;
1341                 if (pp->p_offset > base_offset)
1342                         free_space += pp->p_offset - base_offset;
1343                 if (pp->p_offset + pp->p_size > base_offset)
1344                         base_offset = pp->p_offset + pp->p_size;
1345         }
1346         if (base_offset < lp->d_secperunit)
1347                 free_space += lp->d_secperunit - base_offset;
1348
1349         /* handle % partitions - note %'s don't need to add up to 100! */
1350         if (total_percent != 0) {
1351                 if (total_percent > 100) {
1352                         fprintf(stderr,"total percentage %lu is greater than 100\n",
1353                             total_percent);
1354                         errors++;
1355                 }
1356
1357                 if (free_space > 0) {
1358                         for (i = 0; i < lp->d_npartitions; i++) {
1359                                 pp = &lp->d_partitions[i];
1360                                 if (part_set[i] && part_size_type[i] == '%') {
1361                                         /* careful of overflows! and integer roundoff */
1362                                         pp->p_size = ((double)pp->p_size/100) * free_space;
1363                                         total_size += pp->p_size;
1364
1365                                         /* FIX we can lose a sector or so due to roundoff per
1366                                            partition.  A more complex algorithm could avoid that */
1367                                 }
1368                         }
1369                 } else {
1370                         fprintf(stderr,
1371                             "%ld sectors available to give to '*' and '%%' partitions\n",
1372                             free_space);
1373                         errors++;
1374                         /* fix?  set all % partitions to size 0? */
1375                 }
1376         }
1377         /* give anything remaining to the hog partition */
1378         if (hog_part != -1) {
1379                 /*
1380                  * Find the range of offsets usable by '*' partitions around
1381                  * the hog partition and how much space they need.
1382                  */
1383                 needed = 0;
1384                 base_offset = BBSIZE / lp->d_secsize;
1385                 for (i = hog_part - 1; i >= 0; i--) {
1386                         pp = &lp->d_partitions[i];
1387                         if (!part_set[i] || i == RAW_PART)
1388                                 continue;
1389                         if (part_offset_type[i] == '*') {
1390                                 needed += pp->p_size;
1391                                 continue;
1392                         }
1393                         base_offset = pp->p_offset + pp->p_size;
1394                         break;
1395                 }
1396                 current_offset = lp->d_secperunit;
1397                 for (i = lp->d_npartitions - 1; i > hog_part; i--) {
1398                         pp = &lp->d_partitions[i];
1399                         if (!part_set[i] || i == RAW_PART)
1400                                 continue;
1401                         if (part_offset_type[i] == '*') {
1402                                 needed += pp->p_size;
1403                                 continue;
1404                         }
1405                         current_offset = pp->p_offset;
1406                 }
1407
1408                 if (current_offset - base_offset <= needed) {
1409                         fprintf(stderr, "Cannot find space for partition %c\n",
1410                             hog_part + 'a');
1411                         fprintf(stderr,
1412                             "Need more than %lu sectors between %lu and %lu\n",
1413                             needed, base_offset, current_offset);
1414                         errors++;
1415                         lp->d_partitions[hog_part].p_size = 0;
1416                 } else {
1417                         lp->d_partitions[hog_part].p_size = current_offset -
1418                             base_offset - needed;
1419                         total_size += lp->d_partitions[hog_part].p_size;
1420                 }
1421         }
1422
1423         /* Now set the offsets for each partition */
1424         current_offset = BBSIZE / lp->d_secsize; /* in sectors */
1425         seen_default_offset = 0;
1426         for (i = 0; i < lp->d_npartitions; i++) {
1427                 part = 'a' + i;
1428                 pp = &lp->d_partitions[i];
1429                 if (part_set[i]) {
1430                         if (part_offset_type[i] == '*') {
1431                                 if (i == RAW_PART) {
1432                                         pp->p_offset = 0;
1433                                 } else {
1434                                         pp->p_offset = current_offset;
1435                                         seen_default_offset = 1;
1436                                 }
1437                         } else {
1438                                 /* allow them to be out of order for old-style tables */
1439                                 if (pp->p_offset < current_offset && 
1440                                     seen_default_offset && i != RAW_PART &&
1441                                     pp->p_fstype != FS_VINUM) {
1442                                         fprintf(stderr,
1443 "Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
1444                                             (long)pp->p_offset,i+'a',current_offset);
1445                                         fprintf(stderr,
1446 "Labels with any *'s for offset must be in ascending order by sector\n");
1447                                         errors++;
1448                                 } else if (pp->p_offset != current_offset &&
1449                                     i != RAW_PART && seen_default_offset) {
1450                                         /* 
1451                                          * this may give unneeded warnings if 
1452                                          * partitions are out-of-order
1453                                          */
1454                                         Warning(
1455 "Offset %ld for partition %c doesn't match expected value %ld",
1456                                             (long)pp->p_offset, i + 'a', current_offset);
1457                                 }
1458                         }
1459                         if (i != RAW_PART)
1460                                 current_offset = pp->p_offset + pp->p_size; 
1461                 }
1462         }
1463
1464         for (i = 0; i < lp->d_npartitions; i++) {
1465                 part = 'a' + i;
1466                 pp = &lp->d_partitions[i];
1467                 if (pp->p_size == 0 && pp->p_offset != 0)
1468                         Warning("partition %c: size 0, but offset %lu",
1469                             part, (u_long)pp->p_offset);
1470 #ifdef notdef
1471                 if (pp->p_size % lp->d_secpercyl)
1472                         Warning("partition %c: size %% cylinder-size != 0",
1473                             part);
1474                 if (pp->p_offset % lp->d_secpercyl)
1475                         Warning("partition %c: offset %% cylinder-size != 0",
1476                             part);
1477 #endif
1478                 if (pp->p_offset > lp->d_secperunit) {
1479                         fprintf(stderr,
1480                             "partition %c: offset past end of unit\n", part);
1481                         errors++;
1482                 }
1483                 if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1484                         fprintf(stderr,
1485                         "partition %c: partition extends past end of unit\n",
1486                             part);
1487                         errors++;
1488                 }
1489                 if (i == RAW_PART)
1490                 {
1491                         if (pp->p_fstype != FS_UNUSED)
1492                                 Warning("partition %c is not marked as unused!",part);
1493                         if (pp->p_offset != 0)
1494                                 Warning("partition %c doesn't start at 0!",part);
1495                         if (pp->p_size != lp->d_secperunit)
1496                                 Warning("partition %c doesn't cover the whole unit!",part);
1497
1498                         if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
1499                             (pp->p_size != lp->d_secperunit)) {
1500                                 Warning("An incorrect partition %c may cause problems for "
1501                                     "standard system utilities",part);
1502                         }
1503                 }
1504
1505                 /* check for overlaps */
1506                 /* this will check for all possible overlaps once and only once */
1507                 for (j = 0; j < i; j++) {
1508                         pp2 = &lp->d_partitions[j];
1509                         if (j != RAW_PART && i != RAW_PART &&   
1510                             pp->p_fstype != FS_VINUM &&
1511                             pp2->p_fstype != FS_VINUM &&
1512                             part_set[i] && part_set[j]) {
1513                                 if (pp2->p_offset < pp->p_offset + pp->p_size &&
1514                                     (pp2->p_offset + pp2->p_size > pp->p_offset ||
1515                                         pp2->p_offset >= pp->p_offset)) {
1516                                         fprintf(stderr,"partitions %c and %c overlap!\n",
1517                                             j + 'a', i + 'a');
1518                                         errors++;
1519                                 }
1520                         }
1521                 }
1522         }
1523         for (; i < 8 || i < lp->d_npartitions; i++) {
1524                 part = 'a' + i;
1525                 pp = &lp->d_partitions[i];
1526                 if (pp->p_size || pp->p_offset)
1527                         Warning("unused partition %c: size %d offset %lu",
1528                             'a' + i, pp->p_size, (u_long)pp->p_offset);
1529         }
1530         return (errors);
1531 }
1532
1533 /*
1534  * When operating on a "virgin" disk, try getting an initial label
1535  * from the associated device driver.  This might work for all device
1536  * drivers that are able to fetch some initial device parameters
1537  * without even having access to a (BSD) disklabel, like SCSI disks,
1538  * most IDE drives, or vn devices.
1539  *
1540  * The device name must be given in its "canonical" form.
1541  */
1542 static struct disklabel32 dlab;
1543
1544 struct disklabel32 *
1545 getvirginlabel(void)
1546 {
1547         struct partinfo info;
1548         struct disklabel32 *dl = &dlab;
1549         char nambuf[BBSIZE];
1550         int f;
1551
1552         if (dkname[0] == '/') {
1553                 warnx("\"auto\" requires the usage of a canonical disk name");
1554                 return (NULL);
1555         }
1556         snprintf(nambuf, BBSIZE, "%s%s", _PATH_DEV, dkname);
1557         if ((f = open(nambuf, O_RDONLY)) == -1) {
1558                 warn("cannot open %s", nambuf);
1559                 return (NULL);
1560         }
1561
1562         /*
1563          * Check to see if the media is too big for a 32 bit disklabel.
1564          */
1565         if (ioctl(f, DIOCGPART, &info) == 0) {
1566                  if (info.media_size >= 0x100000000ULL * 512) {
1567                         warnx("The media is too large for a 32 bit disklabel");
1568                         return (NULL);
1569                  }
1570         }
1571
1572         /*
1573          * Generate a virgin disklabel via ioctl
1574          */
1575         if (ioctl(f, DIOCGDVIRGIN32, dl) < 0) {
1576                 l_perror("ioctl DIOCGDVIRGIN32");
1577                 close(f);
1578                 return(NULL);
1579         }
1580         close(f);
1581         return (dl);
1582 }
1583
1584 struct disklabel32 *
1585 getdisklabelfromdisktab(const char *name)
1586 {
1587         struct disktab *dt;
1588         struct disklabel32 *dl = &dlab;
1589         int i;
1590
1591         if ((dt = getdisktabbyname(name)) == NULL)
1592                 return(NULL);
1593         dl->d_magic = DISKMAGIC32;
1594         dl->d_type = dt->d_typeid;
1595         dl->d_subtype = 0;
1596         dl->d_secsize = dt->d_media_blksize;
1597         dl->d_nsectors = dt->d_secpertrack;
1598         dl->d_ntracks = dt->d_nheads;
1599         dl->d_ncylinders = dt->d_ncylinders;
1600         dl->d_secpercyl = dt->d_secpercyl;
1601         dl->d_secperunit = dt->d_media_blocks;
1602         dl->d_rpm = dt->d_rpm;
1603         dl->d_interleave = dt->d_interleave;
1604         dl->d_trackskew = dt->d_trackskew;
1605         dl->d_cylskew = dt->d_cylskew;
1606         dl->d_headswitch = dt->d_headswitch;
1607         dl->d_trkseek = dt->d_trkseek;
1608         dl->d_magic2 = DISKMAGIC32;
1609         dl->d_npartitions = dt->d_npartitions;
1610         dl->d_bbsize = dt->d_bbsize;
1611         dl->d_sbsize = dt->d_sbsize;
1612         for (i = 0; i < dt->d_npartitions; ++i) {
1613                 struct partition32 *dlp = &dl->d_partitions[i];
1614                 struct dt_partition *dtp = &dt->d_partitions[i];
1615
1616                 dlp->p_size     = dtp->p_size;
1617                 dlp->p_offset   = dtp->p_offset;
1618                 dlp->p_fsize    = dtp->p_fsize;
1619                 dlp->p_fstype   = dtp->p_fstype;
1620                 dlp->p_frag     = dtp->p_fsize;
1621         }
1622         return(dl);
1623 }
1624
1625 /*
1626  * If we are installing a boot program that doesn't fit in d_bbsize
1627  * we need to mark those partitions that the boot overflows into.
1628  * This allows newfs to prevent creation of a filesystem where it might
1629  * clobber bootstrap code.
1630  */
1631 void
1632 setbootflag(struct disklabel32 *lp)
1633 {
1634         struct partition32 *pp;
1635         int i, errors = 0;
1636         char part;
1637         u_long boffset;
1638
1639         if (bootbuf == 0)
1640                 return;
1641         boffset = bootsize / lp->d_secsize;
1642         for (i = 0; i < lp->d_npartitions; i++) {
1643                 part = 'a' + i;
1644                 pp = &lp->d_partitions[i];
1645                 if (pp->p_size == 0)
1646                         continue;
1647                 if (boffset <= pp->p_offset) {
1648                         if (pp->p_fstype == FS_BOOT)
1649                                 pp->p_fstype = FS_UNUSED;
1650                 } else if (pp->p_fstype != FS_BOOT) {
1651                         if (pp->p_fstype != FS_UNUSED) {
1652                                 fprintf(stderr,
1653                                         "boot overlaps used partition %c\n",
1654                                         part);
1655                                 errors++;
1656                         } else {
1657                                 pp->p_fstype = FS_BOOT;
1658                                 Warning("boot overlaps partition %c, %s",
1659                                         part, "marked as FS_BOOT");
1660                         }
1661                 }
1662         }
1663         if (errors)
1664                 errx(4, "cannot install boot program");
1665 }
1666
1667 /*VARARGS1*/
1668 void
1669 Warning(const char *fmt, ...)
1670 {
1671         va_list ap;
1672
1673         fprintf(stderr, "Warning, ");
1674         va_start(ap, fmt);
1675         vfprintf(stderr, fmt, ap);
1676         fprintf(stderr, "\n");
1677         va_end(ap);
1678 }
1679
1680 /*
1681  * Check to see if the bootblocks are in the wrong place.  FBsd5 bootblocks
1682  * and earlier DFly bb's are packed against the old disklabel and a new
1683  * disklabel would blow them up.  This is a hack that should be removed
1684  * in 2006 sometime (if ever).
1685  */
1686
1687 int
1688 checkoldboot(int f, const char *bootbuffer)
1689 {
1690         char buf[BBSIZE];
1691
1692         if (bootbuffer && strncmp(bootbuffer + 0x402, "BTX", 3) == 0)
1693                 return(0);
1694         lseek(f, (off_t)0, SEEK_SET);
1695         if (read(f, buf, sizeof(buf)) != sizeof(buf))
1696                 return(0);
1697         if (strncmp(buf + 0x402, "BTX", 3) == 0)  /* new location */
1698                 return(0);
1699         if (strncmp(buf + 0x316, "BTX", 3) == 0)  /* old location */
1700                 return(1);
1701         return(0);
1702 }
1703
1704 /*
1705  * Traditional 32 bit disklabels actually use absolute sector numbers on
1706  * disk, NOT slice relative sector numbres.   The OS hides this from us
1707  * when we use DIOC ioctls to access the label, but newer versions of
1708  * Dragonfly no longer adjusts the disklabel when snooping reads or writes
1709  * so we have to figure it out ourselves.
1710  */
1711 const char *
1712 fixlabel(int f, struct disklabel32 *lp, int writeadj)
1713 {
1714         const char *msg = NULL;
1715         struct partinfo info;
1716         struct partition32 *pp;
1717         u_int64_t start;
1718         u_int64_t end;
1719         u_int64_t offset;
1720         int part;
1721         int rev;
1722         int rev_len = sizeof(rev);
1723
1724         if (sysctlbyname("kern.osrevision", &rev, &rev_len, NULL, 0) < 0) {
1725                 errx(1, "Cannot use raw mode on non-DragonFly systems\n");
1726         }
1727         if (rev < 200701) {
1728                 warnx("Warning running new disklabel on old DragonFly systems,\n"
1729                       "assuming the disk layer will fixup the label.\n");
1730                 sleep(3);
1731                 return(NULL);
1732         }
1733
1734         pp = &lp->d_partitions[RAW_PART];
1735
1736         if (forceflag) {
1737                 info.media_offset = slice_start_lba * lp->d_secsize;
1738                 info.media_blocks = pp->p_size;
1739                 info.media_blksize = lp->d_secsize;
1740         } else if (ioctl(f, DIOCGPART, &info) < 0) {
1741                 msg = "Unable to extract the slice starting LBA, "
1742                       "you must use the -f <slice_start_lba> option\n"
1743                       "to specify it manually, or perhaps try without "
1744                       "using -r and let the kernel deal with it\n";
1745                 return(msg);
1746         }
1747
1748         if (lp->d_magic != DISKMAGIC32 || lp->d_magic2 != DISKMAGIC32)
1749                 return ("fixlabel: invalid magic");
1750         if (dkcksum32(lp) != 0)
1751                 return ("fixlabel: invalid checksum");
1752
1753         /*
1754          * What a mess.  For ages old backwards compatibility the disklabel
1755          * on-disk stores absolute offsets instead of slice-relative offsets.
1756          * So fix it up when reading, writing, or snooping.
1757          *
1758          * The in-core label is always slice-relative.
1759          */
1760         if (writeadj) {
1761                 /*
1762                  * incore -> disk
1763                  */
1764                 start = 0;
1765                 offset = info.media_offset / info.media_blksize;
1766         } else {
1767                 /*
1768                  * disk -> incore
1769                  */
1770                 start = info.media_offset / info.media_blksize;
1771                 offset = -info.media_offset / info.media_blksize;
1772         }
1773         if (pp->p_offset != start)
1774                 return ("fixlabel: raw partition offset != slice offset");
1775         if (pp->p_size != info.media_blocks) {
1776                 if (pp->p_size > info.media_blocks)
1777                         return ("fixlabel: raw partition size > slice size");
1778         }
1779         end = start + info.media_blocks;
1780         if (start > end)
1781                 return ("fixlabel: slice wraps");
1782         if (lp->d_secpercyl <= 0)
1783                 return ("fixlabel: d_secpercyl <= 0");
1784         pp -= RAW_PART;
1785         for (part = 0; part < lp->d_npartitions; part++, pp++) {
1786                 if (pp->p_offset != 0 || pp->p_size != 0) {
1787                         if (pp->p_offset < start
1788                             || pp->p_offset + pp->p_size > end
1789                             || pp->p_offset + pp->p_size < pp->p_offset) {
1790                                 /* XXX else silently discard junk. */
1791                                 bzero(pp, sizeof *pp);
1792                         } else {
1793                                 pp->p_offset += offset;
1794                         }
1795                 }
1796         }
1797         lp->d_checksum = 0;
1798         lp->d_checksum = dkcksum32(lp);
1799         return (NULL);
1800 }
1801
1802 void
1803 usage(void)
1804 {
1805 #if NUMBOOT > 0
1806         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",
1807                 "usage: disklabel [-r] disk",
1808                 "\t\t(to read label)",
1809                 "       disklabel -w [-r] [-n] disk type [ packid ]",
1810                 "\t\t(to write label with existing boot program)",
1811                 "       disklabel -e [-r] [-n] disk",
1812                 "\t\t(to edit label)",
1813                 "       disklabel -R [-r] [-n] disk protofile",
1814                 "\t\t(to restore label with existing boot program)",
1815 #if NUMBOOT > 1
1816                 "       disklabel -B [-n] [ -b boot1 [ -s boot2 ] ] disk [ type ]",
1817                 "\t\t(to install boot program with existing label)",
1818                 "       disklabel -w -B [-n] [ -b boot1 [ -s boot2 ] ] disk type [ packid ]",
1819                 "\t\t(to write label and boot program)",
1820                 "       disklabel -R -B [-n] [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]",
1821                 "\t\t(to restore label and boot program)",
1822 #else
1823                 "       disklabel -B [-n] [ -b bootprog ] disk [ type ]",
1824                 "\t\t(to install boot program with existing on-disk label)",
1825                 "       disklabel -w -B [-n] [ -b bootprog ] disk type [ packid ]",
1826                 "\t\t(to write label and install boot program)",
1827                 "       disklabel -R -B [-n] [ -b bootprog ] disk protofile [ type ]",
1828                 "\t\t(to restore label and install boot program)",
1829 #endif
1830                 "       disklabel [-NW] disk",
1831                 "\t\t(to write disable/enable label)");
1832 #else
1833         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1834                 "usage: disklabel [-r] disk", "(to read label)",
1835                 "       disklabel -w [-r] [-n] disk type [ packid ]",
1836                 "\t\t(to write label)",
1837                 "       disklabel -e [-r] [-n] disk",
1838                 "\t\t(to edit label)",
1839                 "       disklabel -R [-r] [-n] disk protofile",
1840                 "\t\t(to restore label)",
1841                 "       disklabel [-NW] disk",
1842                 "\t\t(to write disable/enable label)");
1843 #endif
1844         fprintf(stderr, "%s\n%s\n",
1845                 "       disklabel [-f slice_start_lba] [options]",
1846                 "\t\t(to force using manual offset)");
1847         exit(1);
1848 }