Edit

IABSD.fr/src/usr.sbin/installboot/powerpc64_installboot.c

Branch :

  • Show log

    Commit

  • Author : kettenis
    Date : 2021-07-20 14:51:56
    Hash : c3e1bf61
    Message : Add -p option to "prepare" (newfs) a filesystem that will be used for the bootloader. This is a no-op on architectures where such a filesystem isn't needed. ok krw@, deraadt@

  • usr.sbin/installboot/powerpc64_installboot.c
  • /*	$OpenBSD: powerpc64_installboot.c,v 1.3 2021/07/20 14:51:56 kettenis Exp $	*/
    
    /*
     * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
     * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org>
     * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
     * Copyright (c) 1997 Michael Shalayeff
     * Copyright (c) 1994 Paul Kranenburg
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. All advertising materials mentioning features or use of this software
     *    must display the following acknowledgement:
     *      This product includes software developed by Paul Kranenburg.
     * 4. The name of the author may not be used to endorse or promote products
     *    derived from this software without specific prior written permission
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <sys/param.h>	/* DEV_BSIZE */
    #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)
    {
    }
    
    void
    md_loadboot(void)
    {
    }
    
    void
    md_prepareboot(int devfd, char *dev)
    {
    	struct disklabel dl;
    	int part;
    
    	/* Get and check disklabel. */
    	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);
    
    	/* Warn on unknown disklabel types. */
    	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;
    
    	/* Get and check disklabel. */
    	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]);
    
    	/* Warn on unknown disklabel types. */
    	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 char *newfsfmt ="/sbin/newfs_msdos %s >/dev/null";
    	struct msdosfs_args args;
    	char cmd[60];
    	int rslt;
    
    	/* Mount <duid>.<part> as msdos filesystem. */
    	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");
    		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);
    			return rslt;
    		}
    	}
    
    	return 0;
    }
    
    static void
    write_filesystem(struct disklabel *dl, char part)
    {
    	static char *fsckfmt = "/sbin/fsck_msdos %s >/dev/null";
    	struct msdosfs_args args;
    	char cmd[60];
    	char dir[PATH_MAX];
    	char dst[PATH_MAX];
    	char *src;
    	size_t mntlen, srclen;
    	int rslt;
    
    	src = NULL;
    
    	/* Create directory for temporary mount point. */
    	strlcpy(dir, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
    	if (mkdtemp(dir) == NULL)
    		err(1, "mkdtemp('%s') failed", dst);
    	mntlen = strlen(dir);
    
    	/* Mount <duid>.<part> as msdos filesystem. */
    	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) {
    		/* Try fsck'ing it. */
    		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) {
    			/* Try newfs'ing it. */
    			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;
    			}
    		}
    	}
    
    	/*
    	 * Copy /usr/mdec/boot to /mnt/boot.
    	 */
    	strlcpy(dst, dir, sizeof dst);
    	if (strlcat(dst, "/boot", sizeof(dst)) >= sizeof(dst)) {
    		rslt = -1;
    		warn("unable to build /boot path");
    		goto umount;
    	}
    	src = fileprefix(root, "/usr/mdec/boot");
    	if (src == NULL) {
    		rslt = -1;
    		goto umount;
    	}
    	srclen = strlen(src);
    	if (verbose)
    		fprintf(stderr, "%s %s to %s\n",
    		    (nowrite ? "would copy" : "copying"), src, dst);
    	if (!nowrite) {
    		rslt = filecopy(src, dst);
    		if (rslt == -1)
    			goto umount;
    	}
    
    	/*
    	 * Create grub.cfg
    	 */
    	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);
    
    	free(src);
    
    	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);
    
    	/* Read MBR. */
    	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 ('a' + i);
    		}
    	}
    
    	return (-1);
    }