#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define DKTYPENAMES
#include <sys/disklabel32.h>
#include <sys/disklabel64.h>
#include <sys/diskslice.h>
#include <sys/diskmbr.h>
#include <sys/dtype.h>
#include <sys/sysctl.h>
#include <disktab.h>
#include <fcntl.h>
#include <fstab.h>
#include <vfs/ufs/dinode.h>
#include <vfs/ufs/fs.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include "pathnames.h"
#ifndef BBSIZE
#define BBSIZE 8192
#endif
#define DEFAULT_NEWFS_BLOCK 8192U
#define DEFAULT_NEWFS_FRAG 1024U
#define DEFAULT_NEWFS_CPG 16U
#define BIG_NEWFS_BLOCK 16384U
#define BIG_NEWFS_FRAG 2048U
#define BIG_NEWFS_CPG 64U
#define NUMBOOT 2
void makelabel(const char *, const char *, struct disklabel32 *);
int writelabel(int, const char *, struct disklabel32 *);
void l_perror(const char *);
struct disklabel32 *readlabel(int);
struct disklabel32 *makebootarea(char *, struct disklabel32 *, int);
void display(FILE *, const struct disklabel32 *);
int edit(struct disklabel32 *, int);
int editit(void);
char *skip(char *);
char *word(char *);
int getasciilabel(FILE *, struct disklabel32 *);
int getasciipartspec(char *, struct disklabel32 *, int, int);
int checklabel(struct disklabel32 *);
void setbootflag(struct disklabel32 *);
void Warning(const char *, ...) __printflike(1, 2);
void usage(void) __dead2;
int checkoldboot(int, const char *);
const char *fixlabel(int, struct disklabel32 *, int);
struct disklabel32 *getvirginlabel(void);
struct disklabel32 *getdisklabelfromdisktab(const char *name);
#define DEFEDITOR _PATH_VI
#define streq(a,b) (strcmp(a,b) == 0)
char *dkname;
char *specname;
char tmpfil[] = PATH_TMPFILE;
char namebuf[BBSIZE];
struct disklabel32 lab;
char bootarea[BBSIZE];
#define MAX_PART ('z')
#define MAX_NUM_PARTS (1 + MAX_PART - 'a')
char part_size_type[MAX_NUM_PARTS];
char part_offset_type[MAX_NUM_PARTS];
int part_set[MAX_NUM_PARTS];
#if NUMBOOT > 0
int installboot;
char *bootbuf;
int bootsize;
char *xxboot;
char *bootxx;
char boot0[MAXPATHLEN];
char boot1[MAXPATHLEN];
#endif
enum {
UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT
} op = UNSPEC;
int rflag;
int disable_write;
int forceflag;
u_int32_t slice_start_lba;
#ifdef DEBUG
int debug;
#define OPTIONS "BNRWb:def:nrs:w"
#else
#define OPTIONS "BNRWb:ef:nrs:w"
#endif
int
main(int argc, char *argv[])
{
struct disklabel32 *lp;
FILE *t;
int ch, f = 0, flag, error = 0;
char *name = NULL;
while ((ch = getopt(argc, argv, OPTIONS)) != -1)
switch (ch) {
#if NUMBOOT > 0
case 'B':
++installboot;
break;
case 'b':
xxboot = optarg;
break;
case 'f':
forceflag = 1;
slice_start_lba = strtoul(optarg, NULL, 0);
break;
#if NUMBOOT > 1
case 's':
bootxx = optarg;
break;
#endif
#endif
case 'N':
if (op != UNSPEC)
usage();
op = NOWRITE;
break;
case 'n':
disable_write = 1;
break;
case 'R':
if (op != UNSPEC)
usage();
op = RESTORE;
break;
case 'W':
if (op != UNSPEC)
usage();
op = WRITEABLE;
break;
case 'e':
if (op != UNSPEC)
usage();
op = EDIT;
break;
case 'r':
++rflag;
break;
case 'w':
if (op != UNSPEC)
usage();
op = WRITE;
break;
#ifdef DEBUG
case 'd':
debug++;
break;
#endif
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
#if NUMBOOT > 0
if (installboot) {
rflag++;
if (op == UNSPEC)
op = WRITEBOOT;
} else {
if (op == UNSPEC)
op = READ;
xxboot = bootxx = NULL;
}
#else
if (op == UNSPEC)
op = READ;
#endif
if (argc < 1)
usage();
dkname = getdevpath(argv[0], 0);
specname = dkname;
f = open(specname, op == READ ? O_RDONLY : O_RDWR);
if (f < 0)
err(4, "%s", specname);
switch(op) {
case UNSPEC:
break;
case EDIT:
if (argc != 1)
usage();
lp = readlabel(f);
error = edit(lp, f);
break;
case NOWRITE:
flag = 0;
if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
err(4, "ioctl DIOCWLABEL");
break;
case READ:
if (argc != 1)
usage();
lp = readlabel(f);
display(stdout, lp);
error = checklabel(lp);
if (checkoldboot(f, NULL))
warnx("Warning, old bootblocks detected, install new bootblocks & reinstall the disklabel");
break;
case RESTORE:
#if NUMBOOT > 0
if (installboot && argc == 3) {
makelabel(argv[2], 0, &lab);
argc--;
bzero((char *)&lab, sizeof(lab));
}
#endif
if (argc != 2)
usage();
if (!(t = fopen(argv[1], "r")))
err(4, "%s", argv[1]);
if (!getasciilabel(t, &lab))
exit(1);
lp = makebootarea(bootarea, &lab, f);
*lp = lab;
error = writelabel(f, bootarea, lp);
break;
case WRITE:
if (argc == 3) {
name = argv[2];
argc--;
}
if (argc != 2)
usage();
makelabel(argv[1], name, &lab);
lp = makebootarea(bootarea, &lab, f);
*lp = lab;
if (checklabel(lp) == 0)
error = writelabel(f, bootarea, lp);
break;
case WRITEABLE:
flag = 1;
if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
err(4, "ioctl DIOCWLABEL");
break;
#if NUMBOOT > 0
case WRITEBOOT:
{
struct disklabel32 tlab;
lp = readlabel(f);
tlab = *lp;
if (argc == 2)
makelabel(argv[1], 0, &lab);
lp = makebootarea(bootarea, &lab, f);
*lp = tlab;
if (checklabel(lp) == 0)
error = writelabel(f, bootarea, lp);
break;
}
#endif
}
exit(error);
}
void
makelabel(const char *type, const char *name, struct disklabel32 *lp)
{
struct disklabel32 *dp;
if (strcmp(type, "auto") == 0)
dp = getvirginlabel();
else
dp = getdisklabelfromdisktab(type);
if (dp == NULL)
errx(1, "%s: unknown disk type", type);
*lp = *dp;
if (name)
strncpy(lp->d_packname, name, sizeof(lp->d_packname));
}
int
writelabel(int f, const char *boot, struct disklabel32 *lp)
{
const char *msg;
int flag;
int r;
if (disable_write) {
Warning("write to disk label suppressed - label was as follows:");
display(stdout, lp);
return (0);
} else {
if (checkoldboot(f, boot))
errx(4, "Will not overwrite old bootblocks w/ label, install new boot blocks first!");
setbootflag(lp);
lp->d_magic = DISKMAGIC32;
lp->d_magic2 = DISKMAGIC32;
lp->d_checksum = 0;
lp->d_checksum = dkcksum32(lp);
if (rflag) {
if (ioctl(f, DIOCSDINFO32, lp) < 0 &&
errno != ENODEV && errno != ENOTTY) {
l_perror("ioctl DIOCSDINFO32");
return (1);
}
lseek(f, (off_t)0, SEEK_SET);
flag = 1;
if (ioctl(f, DIOCWLABEL, &flag) < 0)
warn("ioctl DIOCWLABEL");
msg = fixlabel(f, lp, 1);
if (msg) {
warn("%s", msg);
return (1);
}
r = write(f, boot, lp->d_bbsize);
fixlabel(f, lp, 0);
if (r != ((ssize_t)lp->d_bbsize)) {
warn("write");
return (1);
}
#if NUMBOOT > 0
if (bootbuf) {
fixlabel(f, lp, 1);
r = write(f, bootbuf, bootsize);
fixlabel(f, lp, 0);
if (r != bootsize) {
warn("write");
return(1);
}
}
#endif
flag = 0;
ioctl(f, DIOCWLABEL, &flag);
} else if (ioctl(f, DIOCWDINFO32, lp) < 0) {
l_perror("ioctl DIOCWDINFO32");
return (1);
}
}
return (0);
}
void
l_perror(const char *s)
{
switch (errno) {
case ESRCH:
warnx("%s: no disk label on disk;", s);
fprintf(stderr, "add \"-r\" to install initial label\n");
break;
case EINVAL:
warnx("%s: label magic number or checksum is wrong!", s);
fprintf(stderr, "(disklabel or kernel is out of date?)\n");
break;
case EBUSY:
warnx("%s: open partition would move or shrink", s);
break;
case EXDEV:
warnx("%s: '%c' partition must start at beginning of disk",
s, 'a' + RAW_PART);
break;
case ENOATTR:
warnx("%s: the disk already has a label of a different type,\n"
"probably a 64 bit disklabel. It must be cleaned out "
"first.\n", s);
break;
default:
warn(NULL);
break;
}
}
struct disklabel32 *
readlabel(int f)
{
const char *msg;
struct disklabel32 *lp;
int r;
if (rflag) {
r = read(f, bootarea, BBSIZE);
if (r < BBSIZE)
err(4, "%s", specname);
for (lp = (struct disklabel32 *)bootarea;
lp <= (struct disklabel32 *)(bootarea + BBSIZE - sizeof(*lp));
lp = (struct disklabel32 *)((char *)lp + 16)) {
if (lp->d_magic == DISKMAGIC32 &&
lp->d_magic2 == DISKMAGIC32)
break;
}
if (lp > (struct disklabel32 *)(bootarea+BBSIZE-sizeof(*lp)) ||
lp->d_magic != DISKMAGIC32 || lp->d_magic2 != DISKMAGIC32 ||
dkcksum32(lp) != 0) {
errx(1, "bad pack magic number (label is damaged, "
"or pack is unlabeled)");
}
if ((msg = fixlabel(f, lp, 0)) != NULL)
errx(1, "%s", msg);
} else {
lp = &lab;
if (ioctl(f, DIOCGDINFO32, lp) < 0) {
l_perror("ioctl DIOCGDINFO32");
exit(4);
}
}
return (lp);
}
struct disklabel32 *
makebootarea(char *boot, struct disklabel32 *dp, int f)
{
struct disklabel32 *lp;
char *p;
int b;
#if NUMBOOT > 0
struct stat sb;
#endif
if (dp->d_secsize == 0) {
dp->d_secsize = DEV_BSIZE;
dp->d_bbsize = BBSIZE;
}
lp = (struct disklabel32 *)
(boot + (LABELSECTOR32 * dp->d_secsize) + LABELOFFSET32);
bzero((char *)lp, sizeof *lp);
#if NUMBOOT > 0
if (!installboot) {
if (rflag) {
if (read(f, boot, BBSIZE) < BBSIZE)
err(4, "%s", specname);
bzero((char *)lp, sizeof *lp);
}
return (lp);
}
if (!xxboot || !bootxx) {
if (!xxboot) {
sprintf(boot0, "%s/boot1", _PATH_BOOTDIR);
xxboot = boot0;
}
#if NUMBOOT > 1
if (!bootxx) {
sprintf(boot1, "%s/boot2", _PATH_BOOTDIR);
bootxx = boot1;
}
#endif
}
#ifdef DEBUG
if (debug)
fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n",
xxboot, bootxx ? bootxx : "NONE");
#endif
b = open(xxboot, O_RDONLY);
if (b < 0)
err(4, "%s", xxboot);
#if NUMBOOT > 1
if (read(b, boot, (int)dp->d_secsize) < 0)
err(4, "%s", xxboot);
close(b);
b = open(bootxx, O_RDONLY);
if (b < 0)
err(4, "%s", bootxx);
if (fstat(b, &sb) != 0)
err(4, "%s", bootxx);
if (dp->d_secsize + sb.st_size > dp->d_bbsize)
errx(4, "%s too large", bootxx);
if (read(b, &boot[dp->d_secsize],
(int)(dp->d_bbsize-dp->d_secsize)) < 0)
err(4, "%s", bootxx);
#else
if (read(b, boot, (int)dp->d_bbsize) < 0)
err(4, "%s", xxboot);
if (fstat(b, &sb) != 0)
err(4, "%s", xxboot);
bootsize = (int)sb.st_size - dp->d_bbsize;
if (bootsize > 0) {
bootsize = roundup2(bootsize, dp->d_secsize);
bootbuf = (char *)malloc((size_t)bootsize);
if (bootbuf == NULL)
err(4, "%s", xxboot);
if (read(b, bootbuf, bootsize) < 0) {
free(bootbuf);
err(4, "%s", xxboot);
}
}
#endif
close(b);
#endif
for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel32); p++)
if (*p)
errx(2, "bootstrap doesn't leave room for disk label");
return (lp);
}
void
display(FILE *f, const struct disklabel32 *lp)
{
int i, j;
const struct partition32 *pp;
fprintf(f, "# %s:\n", specname);
if (lp->d_type < DKMAXTYPES)
fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
else
fprintf(f, "type: %u\n", lp->d_type);
fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
lp->d_typename);
fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
lp->d_packname);
fprintf(f, "flags:");
fprintf(f, "\n");
fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
fprintf(f, "rpm: %u\n", lp->d_rpm);
fprintf(f, "interleave: %u\n", lp->d_interleave);
fprintf(f, "trackskew: %u\n", lp->d_trackskew);
fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
(u_long)lp->d_headswitch);
fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
(u_long)lp->d_trkseek);
fprintf(f, "drivedata: ");
for (i = NDDATA32 - 1; i >= 0; i--) {
if (lp->d_drivedata[i])
break;
}
if (i < 0)
i = 0;
for (j = 0; j <= i; j++)
fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
fprintf(f, "\n\n%u partitions:\n", lp->d_npartitions);
fprintf(f,
"# size offset fstype\n");
pp = lp->d_partitions;
for (i = 0; i < lp->d_npartitions; i++, pp++) {
if (pp->p_size) {
u_long onemeg = 1024 * 1024 / lp->d_secsize;
fprintf(f, " %c: ", 'a' + i);
fprintf(f, "%10lu ", (u_long)pp->p_size);
fprintf(f, "%10lu ", (u_long)pp->p_offset);
if (pp->p_fstype < FSMAXTYPES)
fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
else
fprintf(f, "%8d", pp->p_fstype);
fprintf(f, "\t# %11.3fMB", (double)pp->p_size / onemeg);
fprintf(f, "\n");
}
}
fflush(f);
}
int
edit(struct disklabel32 *lp, int f)
{
int c, fd;
struct disklabel32 label;
FILE *fp;
if ((fd = mkstemp(tmpfil)) == -1 ||
(fp = fdopen(fd, "w")) == NULL) {
warnx("can't create %s", tmpfil);
return (1);
}
display(fp, lp);
fclose(fp);
for (;;) {
if (!editit())
break;
fp = fopen(tmpfil, "r");
if (fp == NULL) {
warnx("can't reopen %s for reading", tmpfil);
break;
}
bzero((char *)&label, sizeof(label));
if (getasciilabel(fp, &label)) {
*lp = label;
if (writelabel(f, bootarea, lp) == 0) {
fclose(fp);
unlink(tmpfil);
return (0);
}
}
fclose(fp);
printf("re-edit the label? [y]: "); fflush(stdout);
c = getchar();
if (c != EOF && c != (int)'\n')
while (getchar() != (int)'\n')
;
if (c == (int)'n')
break;
}
unlink(tmpfil);
return (1);
}
int
editit(void)
{
int pid, xpid;
int status, omask;
const char *ed;
omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
while ((pid = fork()) < 0) {
if (errno == EPROCLIM) {
warnx("you have too many processes");
return(0);
}
if (errno != EAGAIN) {
warn("fork");
return(0);
}
sleep(1);
}
if (pid == 0) {
sigsetmask(omask);
setgid(getgid());
setuid(getuid());
if ((ed = getenv("EDITOR")) == NULL)
ed = DEFEDITOR;
execlp(ed, ed, tmpfil, NULL);
err(1, "%s", ed);
}
while ((xpid = wait(&status)) >= 0)
if (xpid == pid)
break;
sigsetmask(omask);
return(!status);
}
char *
skip(char *cp)
{
while (*cp != '\0' && isspace(*cp))
cp++;
if (*cp == '\0' || *cp == '#')
return (NULL);
return (cp);
}
char *
word(char *cp)
{
char c;
while (*cp != '\0' && !isspace(*cp) && *cp != '#')
cp++;
if ((c = *cp) != '\0') {
*cp++ = '\0';
if (c != '#')
return (skip(cp));
}
return (NULL);
}
int
getasciilabel(FILE *f, struct disklabel32 *lp)
{
char *cp;
const char **cpp;
u_int part;
char *tp, line[BUFSIZ];
u_long v;
int lineno = 0, errors = 0;
int i;
char empty[] = "";
char unknown[] = "unknown";
bzero(&part_set, sizeof(part_set));
bzero(&part_size_type, sizeof(part_size_type));
bzero(&part_offset_type, sizeof(part_offset_type));
lp->d_bbsize = BBSIZE;
lp->d_sbsize = SBSIZE;
while (fgets(line, sizeof(line) - 1, f)) {
lineno++;
if ((cp = strchr(line,'\n')) != NULL)
*cp = '\0';
cp = skip(line);
if (cp == NULL)
continue;
tp = strchr(cp, ':');
if (tp == NULL) {
fprintf(stderr, "line %d: syntax error\n", lineno);
errors++;
continue;
}
*tp++ = '\0', tp = skip(tp);
if (streq(cp, "type")) {
if (tp == NULL)
tp = unknown;
cpp = dktypenames;
for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) {
if (*cpp && strcasecmp(*cpp, tp) == 0) {
lp->d_type = cpp - dktypenames;
break;
}
}
if (cpp < &dktypenames[DKMAXTYPES])
continue;
v = strtoul(tp, NULL, 10);
if (v >= DKMAXTYPES) {
fprintf(stderr, "line %d:%s %lu\n", lineno,
"Warning, unknown disk type", v);
}
lp->d_type = v;
continue;
}
if (streq(cp, "flags")) {
for (v = 0; (cp = tp) && *cp != '\0';) {
tp = word(cp);
if (streq(cp, "removeable"))
v |= 0;
else if (streq(cp, "ecc"))
v |= 0;
else if (streq(cp, "badsect"))
v |= 0;
else {
fprintf(stderr,
"line %d: %s: bad flag\n",
lineno, cp);
errors++;
}
}
lp->d_flags = v;
continue;
}
if (streq(cp, "drivedata")) {
for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA32;) {
lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
tp = word(cp);
}
continue;
}
if (sscanf(cp, "%lu partitions", &v) == 1) {
if (v == 0 || v > MAXPARTITIONS32) {
fprintf(stderr,
"line %d: bad # of partitions\n", lineno);
lp->d_npartitions = MAXPARTITIONS32;
errors++;
} else
lp->d_npartitions = v;
continue;
}
if (tp == NULL)
tp = empty;
if (streq(cp, "disk")) {
strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
continue;
}
if (streq(cp, "label")) {
strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
continue;
}
if (streq(cp, "bytes/sector")) {
v = strtoul(tp, NULL, 10);
if (v == 0 || (v % DEV_BSIZE) != 0) {
fprintf(stderr,
"line %d: %s: bad sector size\n",
lineno, tp);
errors++;
} else
lp->d_secsize = v;
continue;
}
if (streq(cp, "sectors/track")) {
v = strtoul(tp, NULL, 10);
#if (ULONG_MAX != 0xffffffffUL)
if (v == 0 || v > 0xffffffff) {
#else
if (v == 0) {
#endif
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_nsectors = v;
continue;
}
if (streq(cp, "sectors/cylinder")) {
v = strtoul(tp, NULL, 10);
if (v == 0) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_secpercyl = v;
continue;
}
if (streq(cp, "tracks/cylinder")) {
v = strtoul(tp, NULL, 10);
if (v == 0) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_ntracks = v;
continue;
}
if (streq(cp, "cylinders")) {
v = strtoul(tp, NULL, 10);
if (v == 0) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_ncylinders = v;
continue;
}
if (streq(cp, "sectors/unit")) {
v = strtoul(tp, NULL, 10);
if (v == 0) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_secperunit = v;
continue;
}
if (streq(cp, "rpm")) {
v = strtoul(tp, NULL, 10);
if (v == 0 || v > USHRT_MAX) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_rpm = v;
continue;
}
if (streq(cp, "interleave")) {
v = strtoul(tp, NULL, 10);
if (v == 0 || v > USHRT_MAX) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_interleave = v;
continue;
}
if (streq(cp, "trackskew")) {
v = strtoul(tp, NULL, 10);
if (v > USHRT_MAX) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_trackskew = v;
continue;
}
if (streq(cp, "cylinderskew")) {
v = strtoul(tp, NULL, 10);
if (v > USHRT_MAX) {
fprintf(stderr, "line %d: %s: bad %s\n",
lineno, tp, cp);
errors++;
} else
lp->d_cylskew = v;
continue;
}
if (streq(cp, "headswitch")) {
v = strtoul(tp, NULL, 10);
lp->d_headswitch = v;
continue;
}
if (streq(cp, "track-to-track seek")) {
v = strtoul(tp, NULL, 10);
lp->d_trkseek = v;
continue;
}
if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
fprintf(stderr,
"line %d: %s: Unknown disklabel field\n", lineno,
cp);
errors++;
continue;
}
part = *cp - 'a';
if (part >= lp->d_npartitions) {
fprintf(stderr,
"line %d: partition name out of range a-%c: %s\n",
lineno, 'a' + lp->d_npartitions - 1, cp);
errors++;
continue;
}
part_set[part] = 1;
if (getasciipartspec(tp, lp, part, lineno) != 0) {
errors++;
break;
}
}
errors += checklabel(lp);
return (errors == 0);
}
#define NXTNUM(n) do { \
if (tp == NULL) { \
fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
return (1); \
} else { \
cp = tp, tp = word(cp); \
(n) = strtoul(cp, NULL, 10); \
} \
} while (0)
#define NXTWORD(w,n) do { \
if (tp == NULL) { \
fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
return (1); \
} else { \
char *tmp; \
cp = tp, tp = word(cp); \
(n) = strtoul(cp, &tmp, 10); \
if (tmp) (w) = *tmp; \
} \
} while (0)
int
getasciipartspec(char *tp, struct disklabel32 *lp, int part, int lineno)
{
struct partition32 *pp;
char *cp;
const char **cpp;
u_long v;
pp = &lp->d_partitions[part];
cp = NULL;
v = 0;
NXTWORD(part_size_type[part],v);
if (v == 0 && part_size_type[part] != '*') {
fprintf(stderr,
"line %d: %s: bad partition size\n", lineno, cp);
return (1);
}
pp->p_size = v;
v = 0;
NXTWORD(part_offset_type[part],v);
if (v == 0 && part_offset_type[part] != '*' &&
part_offset_type[part] != '\0') {
fprintf(stderr,
"line %d: %s: bad partition offset\n", lineno, cp);
return (1);
}
pp->p_offset = v;
if (tp == NULL) {
fprintf(stderr,
"line %d: no filesystem type was specified\n", lineno);
return(1);
}
cp = tp;
tp = word(cp);
for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++) {
if (*cpp && strcasecmp(*cpp, cp) == 0)
break;
}
if (*cpp != NULL) {
pp->p_fstype = cpp - fstypenames;
} else {
if (isdigit(*cp))
v = strtoul(cp, NULL, 10);
else
v = FSMAXTYPES;
if (v >= FSMAXTYPES) {
fprintf(stderr,
"line %d: Warning, unknown filesystem type %s\n",
lineno, cp);
v = FS_UNUSED;
}
pp->p_fstype = v;
}
pp->p_fsize = 0;
pp->p_frag = 0;
pp->p_cpg = 0;
cp = tp;
if (tp) {
fprintf(stderr, "line %d: Warning, fragment, block, "
"and bps/cpg fields are no\n"
"longer supported and must be specified "
"via newfs options instead.\n",
lineno);
}
return(0);
}
int
checklabel(struct disklabel32 *lp)
{
struct partition32 *pp;
int i, errors = 0;
char part;
u_long base_offset, needed, total_size, total_percent, current_offset;
long free_space;
int seen_default_offset;
int hog_part;
int j;
struct partition32 *pp2;
if (lp->d_secsize == 0) {
fprintf(stderr, "sector size 0\n");
return (1);
}
if (lp->d_nsectors == 0) {
fprintf(stderr, "sectors/track 0\n");
return (1);
}
if (lp->d_ntracks == 0) {
fprintf(stderr, "tracks/cylinder 0\n");
return (1);
}
if (lp->d_ncylinders == 0) {
fprintf(stderr, "cylinders/unit 0\n");
errors++;
}
if (lp->d_rpm == 0)
Warning("revolutions/minute 0");
if (lp->d_secpercyl == 0)
lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
if (lp->d_secperunit == 0)
lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
if (lp->d_bbsize == 0) {
fprintf(stderr, "boot block size 0\n");
errors++;
} else if (lp->d_bbsize % lp->d_secsize)
Warning("boot block size %% sector-size != 0");
if (lp->d_sbsize == 0) {
fprintf(stderr, "super block size 0\n");
errors++;
} else if (lp->d_sbsize % lp->d_secsize)
Warning("super block size %% sector-size != 0");
if (lp->d_npartitions > MAXPARTITIONS32)
Warning("number of partitions (%lu) > MAXPARTITIONS (%d)",
(u_long)lp->d_npartitions, MAXPARTITIONS32);
total_size = 0;
total_percent = 0;
hog_part = -1;
for (i = 0; i < lp->d_npartitions; i++) {
pp = &lp->d_partitions[i];
if (part_set[i]) {
if (part_size_type[i] == '*') {
if (i == RAW_PART) {
pp->p_size = lp->d_secperunit;
} else {
if (part_offset_type[i] != '*') {
if (total_size < pp->p_offset)
total_size = pp->p_offset;
}
if (hog_part != -1)
Warning("Too many '*' partitions (%c and %c)",
hog_part + 'a',i + 'a');
else
hog_part = i;
}
} else {
off_t size;
size = pp->p_size;
switch (part_size_type[i]) {
case '%':
total_percent += size;
break;
case 't':
case 'T':
size *= 1024ULL;
case 'g':
case 'G':
size *= 1024ULL;
case 'm':
case 'M':
size *= 1024ULL;
case 'k':
case 'K':
size *= 1024ULL;
break;
case '\0':
break;
default:
Warning("unknown size specifier '%c' (K/M/G/T are valid)",part_size_type[i]);
break;
}
if (part_size_type[i] != '%') {
if (part_size_type[i] != '\0') {
if (size % lp->d_secsize != 0)
Warning("partition %c not an integer number of sectors",
i + 'a');
size /= lp->d_secsize;
pp->p_size = size;
}
if (i != RAW_PART)
total_size += size;
}
}
}
}
base_offset = BBSIZE / lp->d_secsize;
free_space = 0;
for (i = 0; i < lp->d_npartitions; i++) {
pp = &lp->d_partitions[i];
if (!part_set[i] || i == RAW_PART ||
part_size_type[i] == '%' || part_size_type[i] == '*')
continue;
if (pp->p_offset > base_offset)
free_space += pp->p_offset - base_offset;
if (pp->p_offset + pp->p_size > base_offset)
base_offset = pp->p_offset + pp->p_size;
}
if (base_offset < lp->d_secperunit)
free_space += lp->d_secperunit - base_offset;
if (total_percent != 0) {
if (total_percent > 100) {
fprintf(stderr,"total percentage %lu is greater than 100\n",
total_percent);
errors++;
}
if (free_space > 0) {
for (i = 0; i < lp->d_npartitions; i++) {
pp = &lp->d_partitions[i];
if (part_set[i] && part_size_type[i] == '%') {
pp->p_size = ((double)pp->p_size/100) * free_space;
total_size += pp->p_size;
}
}
} else {
fprintf(stderr,
"%ld sectors available to give to '*' and '%%' partitions\n",
free_space);
errors++;
}
}
if (hog_part != -1) {
needed = 0;
base_offset = BBSIZE / lp->d_secsize;
for (i = hog_part - 1; i >= 0; i--) {
pp = &lp->d_partitions[i];
if (!part_set[i] || i == RAW_PART)
continue;
if (part_offset_type[i] == '*') {
needed += pp->p_size;
continue;
}
base_offset = pp->p_offset + pp->p_size;
break;
}
current_offset = lp->d_secperunit;
for (i = lp->d_npartitions - 1; i > hog_part; i--) {
pp = &lp->d_partitions[i];
if (!part_set[i] || i == RAW_PART)
continue;
if (part_offset_type[i] == '*') {
needed += pp->p_size;
continue;
}
current_offset = pp->p_offset;
}
if (current_offset - base_offset <= needed) {
fprintf(stderr, "Cannot find space for partition %c\n",
hog_part + 'a');
fprintf(stderr,
"Need more than %lu sectors between %lu and %lu\n",
needed, base_offset, current_offset);
errors++;
lp->d_partitions[hog_part].p_size = 0;
} else {
lp->d_partitions[hog_part].p_size = current_offset -
base_offset - needed;
total_size += lp->d_partitions[hog_part].p_size;
}
}
current_offset = BBSIZE / lp->d_secsize;
seen_default_offset = 0;
for (i = 0; i < lp->d_npartitions; i++) {
part = 'a' + i;
pp = &lp->d_partitions[i];
if (part_set[i]) {
if (part_offset_type[i] == '*') {
if (i == RAW_PART) {
pp->p_offset = 0;
} else {
pp->p_offset = current_offset;
seen_default_offset = 1;
}
} else {
if (pp->p_offset < current_offset &&
seen_default_offset && i != RAW_PART &&
pp->p_fstype != FS_VINUM) {
fprintf(stderr,
"Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
(long)pp->p_offset,i+'a',current_offset);
fprintf(stderr,
"Labels with any *'s for offset must be in ascending order by sector\n");
errors++;
} else if (pp->p_offset != current_offset &&
i != RAW_PART && seen_default_offset) {
Warning(
"Offset %ld for partition %c doesn't match expected value %ld",
(long)pp->p_offset, i + 'a', current_offset);
}
}
if (i != RAW_PART)
current_offset = pp->p_offset + pp->p_size;
}
}
for (i = 0; i < lp->d_npartitions; i++) {
part = 'a' + i;
pp = &lp->d_partitions[i];
if (pp->p_size == 0 && pp->p_offset != 0)
Warning("partition %c: size 0, but offset %lu",
part, (u_long)pp->p_offset);
#ifdef notdef
if (pp->p_size % lp->d_secpercyl)
Warning("partition %c: size %% cylinder-size != 0",
part);
if (pp->p_offset % lp->d_secpercyl)
Warning("partition %c: offset %% cylinder-size != 0",
part);
#endif
if (pp->p_offset > lp->d_secperunit) {
fprintf(stderr,
"partition %c: offset past end of unit\n", part);
errors++;
}
if (pp->p_offset + pp->p_size > lp->d_secperunit) {
fprintf(stderr,
"partition %c: partition extends past end of unit\n",
part);
errors++;
}
if (i == RAW_PART)
{
if (pp->p_fstype != FS_UNUSED)
Warning("partition %c is not marked as unused!",part);
if (pp->p_offset != 0)
Warning("partition %c doesn't start at 0!",part);
if (pp->p_size != lp->d_secperunit)
Warning("partition %c doesn't cover the whole unit!",part);
if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
(pp->p_size != lp->d_secperunit)) {
Warning("An incorrect partition %c may cause problems for "
"standard system utilities",part);
}
}
for (j = 0; j < i; j++) {
pp2 = &lp->d_partitions[j];
if (j != RAW_PART && i != RAW_PART &&
pp->p_fstype != FS_VINUM &&
pp2->p_fstype != FS_VINUM &&
part_set[i] && part_set[j]) {
if (pp2->p_offset < pp->p_offset + pp->p_size &&
(pp2->p_offset + pp2->p_size > pp->p_offset ||
pp2->p_offset >= pp->p_offset)) {
fprintf(stderr,"partitions %c and %c overlap!\n",
j + 'a', i + 'a');
errors++;
}
}
}
}
for (; i < 8 || i < lp->d_npartitions; i++) {
part = 'a' + i;
pp = &lp->d_partitions[i];
if (pp->p_size || pp->p_offset)
Warning("unused partition %c: size %d offset %lu",
'a' + i, pp->p_size, (u_long)pp->p_offset);
}
return (errors);
}
static struct disklabel32 dlab;
struct disklabel32 *
getvirginlabel(void)
{
struct partinfo info;
struct disklabel32 *dl = &dlab;
int f;
if ((f = open(dkname, O_RDONLY)) == -1) {
warn("cannot open %s", dkname);
return (NULL);
}
if (ioctl(f, DIOCGPART, &info) == 0) {
if (info.media_size >= 0x100000000ULL * 512) {
warnx("The media is too large for a 32 bit disklabel,"
" please use disklabel64.");
return (NULL);
}
}
if (ioctl(f, DIOCGDVIRGIN32, dl) < 0) {
l_perror("ioctl DIOCGDVIRGIN32");
close(f);
return(NULL);
}
close(f);
return (dl);
}
struct disklabel32 *
getdisklabelfromdisktab(const char *name)
{
struct disktab *dt;
struct disklabel32 *dl = &dlab;
int i;
if ((dt = getdisktabbyname(name)) == NULL)
return(NULL);
dl->d_magic = DISKMAGIC32;
dl->d_type = dt->d_typeid;
dl->d_subtype = 0;
dl->d_secsize = dt->d_media_blksize;
dl->d_nsectors = dt->d_secpertrack;
dl->d_ntracks = dt->d_nheads;
dl->d_ncylinders = dt->d_ncylinders;
dl->d_secpercyl = dt->d_secpercyl;
dl->d_secperunit = dt->d_media_blocks;
dl->d_rpm = dt->d_rpm;
dl->d_interleave = dt->d_interleave;
dl->d_trackskew = dt->d_trackskew;
dl->d_cylskew = dt->d_cylskew;
dl->d_headswitch = dt->d_headswitch;
dl->d_trkseek = dt->d_trkseek;
dl->d_magic2 = DISKMAGIC32;
dl->d_npartitions = dt->d_npartitions;
dl->d_bbsize = dt->d_bbsize;
dl->d_sbsize = dt->d_sbsize;
for (i = 0; i < dt->d_npartitions; ++i) {
struct partition32 *dlp = &dl->d_partitions[i];
struct dt_partition *dtp = &dt->d_partitions[i];
dlp->p_size = dtp->p_size;
dlp->p_offset = dtp->p_offset;
dlp->p_fsize = dtp->p_fsize;
dlp->p_fstype = dtp->p_fstype;
dlp->p_frag = dtp->p_fsize;
}
return(dl);
}
void
setbootflag(struct disklabel32 *lp)
{
struct partition32 *pp;
int i, errors = 0;
char part;
u_long boffset;
if (bootbuf == NULL)
return;
boffset = bootsize / lp->d_secsize;
for (i = 0; i < lp->d_npartitions; i++) {
part = 'a' + i;
pp = &lp->d_partitions[i];
if (pp->p_size == 0)
continue;
if (boffset <= pp->p_offset) {
if (pp->p_fstype == FS_BOOT)
pp->p_fstype = FS_UNUSED;
} else if (pp->p_fstype != FS_BOOT) {
if (pp->p_fstype != FS_UNUSED) {
fprintf(stderr,
"boot overlaps used partition %c\n",
part);
errors++;
} else {
pp->p_fstype = FS_BOOT;
Warning("boot overlaps partition %c, %s",
part, "marked as FS_BOOT");
}
}
}
if (errors)
errx(4, "cannot install boot program");
}
void
Warning(const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "Warning, ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
int
checkoldboot(int f, const char *bootbuffer)
{
char buf[BBSIZE];
if (bootbuffer && strncmp(bootbuffer + 0x402, "BTX", 3) == 0)
return(0);
lseek(f, (off_t)0, SEEK_SET);
if (read(f, buf, sizeof(buf)) != sizeof(buf))
return(0);
if (strncmp(buf + 0x402, "BTX", 3) == 0)
return(0);
if (strncmp(buf + 0x316, "BTX", 3) == 0)
return(1);
return(0);
}
const char *
fixlabel(int f, struct disklabel32 *lp, int writeadj)
{
const char *msg = NULL;
struct partinfo info;
struct partition32 *pp;
u_int64_t start;
u_int64_t end;
u_int64_t offset;
int part;
int rev;
size_t rev_len = sizeof(rev);
if (sysctlbyname("kern.osrevision", &rev, &rev_len, NULL, 0) < 0) {
errx(1, "Cannot use raw mode on non-DragonFly systems\n");
}
if (rev < 200701) {
warnx("Warning running new disklabel on old DragonFly systems,\n"
"assuming the disk layer will fixup the label.\n");
sleep(3);
return(NULL);
}
pp = &lp->d_partitions[RAW_PART];
if (forceflag) {
info.media_offset = slice_start_lba * lp->d_secsize;
info.media_blocks = pp->p_size;
info.media_blksize = lp->d_secsize;
} else if (ioctl(f, DIOCGPART, &info) < 0) {
msg = "Unable to extract the slice starting LBA, "
"you must use the -f <slice_start_lba> option\n"
"to specify it manually, or perhaps try without "
"using -r and let the kernel deal with it\n";
return(msg);
}
if (lp->d_magic != DISKMAGIC32 || lp->d_magic2 != DISKMAGIC32)
return ("fixlabel: invalid magic");
if (dkcksum32(lp) != 0)
return ("fixlabel: invalid checksum");
if (writeadj) {
start = 0;
offset = info.media_offset / info.media_blksize;
} else {
start = info.media_offset / info.media_blksize;
offset = -info.media_offset / info.media_blksize;
}
if (pp->p_offset != start)
return ("fixlabel: raw partition offset != slice offset");
if (pp->p_size != info.media_blocks) {
if (pp->p_size > info.media_blocks)
return ("fixlabel: raw partition size > slice size");
}
end = start + info.media_blocks;
if (start > end)
return ("fixlabel: slice wraps");
if (lp->d_secpercyl <= 0)
return ("fixlabel: d_secpercyl <= 0");
pp -= RAW_PART;
for (part = 0; part < lp->d_npartitions; part++, pp++) {
if (pp->p_offset != 0 || pp->p_size != 0) {
if (pp->p_offset < start
|| pp->p_offset + pp->p_size > end
|| pp->p_offset + pp->p_size < pp->p_offset) {
bzero(pp, sizeof *pp);
} else {
pp->p_offset += offset;
}
}
}
lp->d_checksum = 0;
lp->d_checksum = dkcksum32(lp);
return (NULL);
}
void
usage(void)
{
#if NUMBOOT > 0
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",
"usage: disklabel32 [-r] disk",
"\t\t(to read label)",
" disklabel32 -w [-r] [-n] disk type [packid]",
"\t\t(to write label with existing boot program)",
" disklabel32 -e [-r] [-n] disk",
"\t\t(to edit label)",
" disklabel32 -R [-r] [-n] disk protofile",
"\t\t(to restore label with existing boot program)",
#if NUMBOOT > 1
" disklabel32 -B [-n] [-b boot1 -s boot2] disk [type]",
"\t\t(to install boot program with existing label)",
" disklabel32 -w -B [-n] [-b boot1 -s boot2] disk type [packid]",
"\t\t(to write label and boot program)",
" disklabel32 -R -B [-n] [-b boot1 -s boot2] disk protofile [type]",
"\t\t(to restore label and boot program)",
#else
" disklabel32 -B [-n] [-b bootprog] disk [type]",
"\t\t(to install boot program with existing on-disk label)",
" disklabel32 -w -B [-n] [-b bootprog] disk type [packid]",
"\t\t(to write label and install boot program)",
" disklabel32 -R -B [-n] [-b bootprog] disk protofile [type]",
"\t\t(to restore label and install boot program)",
#endif
" disklabel32 [-NW] disk",
"\t\t(to write disable/enable label)");
#else
fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
"usage: disklabel32 [-r] disk", "(to read label)",
" disklabel32 -w [-r] [-n] disk type [packid]",
"\t\t(to write label)",
" disklabel32 -e [-r] [-n] disk",
"\t\t(to edit label)",
" disklabel32 -R [-r] [-n] disk protofile",
"\t\t(to restore label)",
" disklabel32 [-NW] disk",
"\t\t(to write disable/enable label)");
#endif
fprintf(stderr, "%s\n%s\n",
" disklabel32 [-f slice_start_lba] [options]",
"\t\t(to force using manual offset)");
exit(1);
}