#include <sys/param.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#include <endian.h>
#include "installboot.h"
static int create_filesystem(struct disklabel *, char);
static void write_filesystem(struct disklabel *, char);
static int findmbrfat(int, struct disklabel *);
char duid[20];
void
md_init(void)
{
stages = 1;
stage1 = "/usr/mdec/boot";
}
void
md_loadboot(void)
{
}
void
md_prepareboot(int devfd, char *dev)
{
struct disklabel dl;
int part;
if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
err(1, "disklabel: %s", dev);
if (dl.d_magic != DISKMAGIC)
errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
if (dl.d_type == 0)
warnx("disklabel type unknown");
part = findmbrfat(devfd, &dl);
if (part != -1) {
create_filesystem(&dl, (char)part);
return;
}
}
void
md_installboot(int devfd, char *dev)
{
struct disklabel dl;
int part;
if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
err(1, "disklabel: %s", dev);
if (dl.d_magic != DISKMAGIC)
errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
snprintf(duid, sizeof duid,
"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
dl.d_uid[0], dl.d_uid[1], dl.d_uid[2], dl.d_uid[3],
dl.d_uid[4], dl.d_uid[5], dl.d_uid[6], dl.d_uid[7]);
if (dl.d_type == 0)
warnx("disklabel type unknown");
part = findmbrfat(devfd, &dl);
if (part != -1) {
write_filesystem(&dl, (char)part);
return;
}
}
static int
create_filesystem(struct disklabel *dl, char part)
{
static const char *newfsfmt = "/sbin/newfs -t msdos %s >/dev/null";
struct msdosfs_args args;
char cmd[60];
int rslt;
memset(&args, 0, sizeof(args));
rslt = asprintf(&args.fspec,
"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
part);
if (rslt == -1) {
warn("bad special device");
return rslt;
}
rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec);
if (rslt >= sizeof(cmd)) {
warnx("can't build newfs command");
free(args.fspec);
rslt = -1;
return rslt;
}
if (verbose)
fprintf(stderr, "%s %s\n",
(nowrite ? "would newfs" : "newfsing"), args.fspec);
if (!nowrite) {
rslt = system(cmd);
if (rslt == -1) {
warn("system('%s') failed", cmd);
free(args.fspec);
return rslt;
}
}
free(args.fspec);
return 0;
}
static void
write_filesystem(struct disklabel *dl, char part)
{
static const char *fsckfmt = "/sbin/fsck -t msdos %s >/dev/null";
struct msdosfs_args args;
char cmd[60];
char dir[PATH_MAX];
char dst[PATH_MAX];
size_t mntlen;
int rslt;
strlcpy(dir, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
if (mkdtemp(dir) == NULL)
err(1, "mkdtemp('%s') failed", dst);
mntlen = strlen(dir);
memset(&args, 0, sizeof(args));
rslt = asprintf(&args.fspec,
"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
part);
if (rslt == -1) {
warn("bad special device");
goto rmdir;
}
args.export_info.ex_root = -2;
args.export_info.ex_flags = 0;
args.flags = MSDOSFSMNT_LONGNAME;
if (mount(MOUNT_MSDOS, dir, 0, &args) == -1) {
rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
if (rslt >= sizeof(cmd)) {
warnx("can't build fsck command");
rslt = -1;
goto rmdir;
}
rslt = system(cmd);
if (rslt == -1) {
warn("system('%s') failed", cmd);
goto rmdir;
}
if (mount(MOUNT_MSDOS, dir, 0, &args) == -1) {
rslt = create_filesystem(dl, part);
if (rslt == -1)
goto rmdir;
rslt = mount(MOUNT_MSDOS, dir, 0, &args);
if (rslt == -1) {
warn("unable to mount MSDOS partition");
goto rmdir;
}
}
}
strlcpy(dst, dir, sizeof dst);
if (strlcat(dst, "/boot", sizeof(dst)) >= sizeof(dst)) {
rslt = -1;
warn("unable to build /boot path");
goto umount;
}
if (verbose)
fprintf(stderr, "%s %s to %s\n",
(nowrite ? "would copy" : "copying"), stage1, dst);
if (!nowrite) {
rslt = filecopy(stage1, dst);
if (rslt == -1)
goto umount;
}
strlcpy(dst, dir, sizeof dst);
if (strlcat(dst, "/grub.cfg", sizeof(dst)) >= sizeof(dst)) {
rslt = -1;
warn("unable to build /grub.cfg path");
goto umount;
}
if (verbose)
fprintf(stderr, "%s %s\n",
(nowrite ? "would create" : "creating"), dst);
if (!nowrite) {
FILE *f;
f = fopen(dst, "w+");
if (f == NULL)
goto umount;
fprintf(f,
"menuentry \"OpenBSD\" {\n"
"\tlinux /boot bootduid=%s\n"
"\tinitrd /boot\n"
"}\n", duid);
fclose(f);
}
rslt = 0;
umount:
dir[mntlen] = '\0';
if (unmount(dir, MNT_FORCE) == -1)
err(1, "unmount('%s') failed", dir);
rmdir:
free(args.fspec);
dst[mntlen] = '\0';
if (rmdir(dir) == -1)
err(1, "rmdir('%s') failed", dir);
if (rslt == -1)
exit(1);
}
int
findmbrfat(int devfd, struct disklabel *dl)
{
struct dos_partition dp[NDOSPART];
ssize_t len;
u_int64_t start = 0;
int i;
u_int8_t *secbuf;
if ((secbuf = malloc(dl->d_secsize)) == NULL)
err(1, NULL);
len = pread(devfd, secbuf, dl->d_secsize, 0);
if (len != dl->d_secsize)
err(4, "can't read mbr");
memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
for (i = 0; i < NDOSPART; i++) {
if (dp[i].dp_typ == DOSPTYP_UNUSED)
continue;
if (dp[i].dp_typ == DOSPTYP_FAT16L ||
dp[i].dp_typ == DOSPTYP_FAT32L ||
dp[i].dp_typ == DOSPTYP_FAT16B)
start = letoh32(dp[i].dp_start);
}
free(secbuf);
if (start) {
for (i = 0; i < MAXPARTITIONS; i++) {
if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
DL_GETPOFFSET(&dl->d_partitions[i]) == start)
return (DL_PARTNUM2NAME(i));
}
}
return (-1);
}