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