1*ca987d46SWarner Losh /*- 2*ca987d46SWarner Losh * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3*ca987d46SWarner Losh * All rights reserved. 4*ca987d46SWarner Losh * 5*ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without 6*ca987d46SWarner Losh * modification, are permitted provided that the following conditions 7*ca987d46SWarner Losh * are met: 8*ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright 9*ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer. 10*ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 11*ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the 12*ca987d46SWarner Losh * documentation and/or other materials provided with the distribution. 13*ca987d46SWarner Losh * 14*ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*ca987d46SWarner Losh * SUCH DAMAGE. 25*ca987d46SWarner Losh */ 26*ca987d46SWarner Losh 27*ca987d46SWarner Losh #include <sys/cdefs.h> 28*ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 29*ca987d46SWarner Losh 30*ca987d46SWarner Losh #include <stand.h> 31*ca987d46SWarner Losh #include <string.h> 32*ca987d46SWarner Losh 33*ca987d46SWarner Losh #include "bootstrap.h" 34*ca987d46SWarner Losh #include "disk.h" 35*ca987d46SWarner Losh #include "libuserboot.h" 36*ca987d46SWarner Losh 37*ca987d46SWarner Losh #if defined(USERBOOT_ZFS_SUPPORT) 38*ca987d46SWarner Losh #include "../zfs/libzfs.h" 39*ca987d46SWarner Losh #endif 40*ca987d46SWarner Losh 41*ca987d46SWarner Losh static int userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path); 42*ca987d46SWarner Losh 43*ca987d46SWarner Losh /* 44*ca987d46SWarner Losh * Point (dev) at an allocated device specifier for the device matching the 45*ca987d46SWarner Losh * path in (devspec). If it contains an explicit device specification, 46*ca987d46SWarner Losh * use that. If not, use the default device. 47*ca987d46SWarner Losh */ 48*ca987d46SWarner Losh int 49*ca987d46SWarner Losh userboot_getdev(void **vdev, const char *devspec, const char **path) 50*ca987d46SWarner Losh { 51*ca987d46SWarner Losh struct disk_devdesc **dev = (struct disk_devdesc **)vdev; 52*ca987d46SWarner Losh int rv; 53*ca987d46SWarner Losh 54*ca987d46SWarner Losh /* 55*ca987d46SWarner Losh * If it looks like this is just a path and no 56*ca987d46SWarner Losh * device, go with the current device. 57*ca987d46SWarner Losh */ 58*ca987d46SWarner Losh if ((devspec == NULL) || 59*ca987d46SWarner Losh (devspec[0] == '/') || 60*ca987d46SWarner Losh (strchr(devspec, ':') == NULL)) { 61*ca987d46SWarner Losh 62*ca987d46SWarner Losh if (((rv = userboot_parsedev(dev, getenv("currdev"), NULL)) == 0) && 63*ca987d46SWarner Losh (path != NULL)) 64*ca987d46SWarner Losh *path = devspec; 65*ca987d46SWarner Losh return(rv); 66*ca987d46SWarner Losh } 67*ca987d46SWarner Losh 68*ca987d46SWarner Losh /* 69*ca987d46SWarner Losh * Try to parse the device name off the beginning of the devspec 70*ca987d46SWarner Losh */ 71*ca987d46SWarner Losh return(userboot_parsedev(dev, devspec, path)); 72*ca987d46SWarner Losh } 73*ca987d46SWarner Losh 74*ca987d46SWarner Losh /* 75*ca987d46SWarner Losh * Point (dev) at an allocated device specifier matching the string version 76*ca987d46SWarner Losh * at the beginning of (devspec). Return a pointer to the remaining 77*ca987d46SWarner Losh * text in (path). 78*ca987d46SWarner Losh * 79*ca987d46SWarner Losh * In all cases, the beginning of (devspec) is compared to the names 80*ca987d46SWarner Losh * of known devices in the device switch, and then any following text 81*ca987d46SWarner Losh * is parsed according to the rules applied to the device type. 82*ca987d46SWarner Losh * 83*ca987d46SWarner Losh * For disk-type devices, the syntax is: 84*ca987d46SWarner Losh * 85*ca987d46SWarner Losh * disk<unit>[s<slice>][<partition>]: 86*ca987d46SWarner Losh * 87*ca987d46SWarner Losh */ 88*ca987d46SWarner Losh static int 89*ca987d46SWarner Losh userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path) 90*ca987d46SWarner Losh { 91*ca987d46SWarner Losh struct disk_devdesc *idev; 92*ca987d46SWarner Losh struct devsw *dv; 93*ca987d46SWarner Losh int i, unit, err; 94*ca987d46SWarner Losh const char *cp; 95*ca987d46SWarner Losh const char *np; 96*ca987d46SWarner Losh 97*ca987d46SWarner Losh /* minimum length check */ 98*ca987d46SWarner Losh if (strlen(devspec) < 2) 99*ca987d46SWarner Losh return(EINVAL); 100*ca987d46SWarner Losh 101*ca987d46SWarner Losh /* look for a device that matches */ 102*ca987d46SWarner Losh for (i = 0, dv = NULL; devsw[i] != NULL; i++) { 103*ca987d46SWarner Losh if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) { 104*ca987d46SWarner Losh dv = devsw[i]; 105*ca987d46SWarner Losh break; 106*ca987d46SWarner Losh } 107*ca987d46SWarner Losh } 108*ca987d46SWarner Losh if (dv == NULL) 109*ca987d46SWarner Losh return(ENOENT); 110*ca987d46SWarner Losh idev = malloc(sizeof(struct disk_devdesc)); 111*ca987d46SWarner Losh err = 0; 112*ca987d46SWarner Losh np = (devspec + strlen(dv->dv_name)); 113*ca987d46SWarner Losh 114*ca987d46SWarner Losh switch(dv->dv_type) { 115*ca987d46SWarner Losh case DEVT_NONE: /* XXX what to do here? Do we care? */ 116*ca987d46SWarner Losh break; 117*ca987d46SWarner Losh 118*ca987d46SWarner Losh case DEVT_DISK: 119*ca987d46SWarner Losh err = disk_parsedev(idev, np, path); 120*ca987d46SWarner Losh if (err != 0) 121*ca987d46SWarner Losh goto fail; 122*ca987d46SWarner Losh break; 123*ca987d46SWarner Losh 124*ca987d46SWarner Losh case DEVT_CD: 125*ca987d46SWarner Losh case DEVT_NET: 126*ca987d46SWarner Losh unit = 0; 127*ca987d46SWarner Losh 128*ca987d46SWarner Losh if (*np && (*np != ':')) { 129*ca987d46SWarner Losh unit = strtol(np, (char **)&cp, 0); /* get unit number if present */ 130*ca987d46SWarner Losh if (cp == np) { 131*ca987d46SWarner Losh err = EUNIT; 132*ca987d46SWarner Losh goto fail; 133*ca987d46SWarner Losh } 134*ca987d46SWarner Losh } else { 135*ca987d46SWarner Losh cp = np; 136*ca987d46SWarner Losh } 137*ca987d46SWarner Losh if (*cp && (*cp != ':')) { 138*ca987d46SWarner Losh err = EINVAL; 139*ca987d46SWarner Losh goto fail; 140*ca987d46SWarner Losh } 141*ca987d46SWarner Losh 142*ca987d46SWarner Losh idev->d_unit = unit; 143*ca987d46SWarner Losh if (path != NULL) 144*ca987d46SWarner Losh *path = (*cp == 0) ? cp : cp + 1; 145*ca987d46SWarner Losh break; 146*ca987d46SWarner Losh 147*ca987d46SWarner Losh case DEVT_ZFS: 148*ca987d46SWarner Losh #if defined(USERBOOT_ZFS_SUPPORT) 149*ca987d46SWarner Losh err = zfs_parsedev((struct zfs_devdesc *)idev, np, path); 150*ca987d46SWarner Losh if (err != 0) 151*ca987d46SWarner Losh goto fail; 152*ca987d46SWarner Losh break; 153*ca987d46SWarner Losh #else 154*ca987d46SWarner Losh /* FALLTHROUGH */ 155*ca987d46SWarner Losh #endif 156*ca987d46SWarner Losh 157*ca987d46SWarner Losh default: 158*ca987d46SWarner Losh err = EINVAL; 159*ca987d46SWarner Losh goto fail; 160*ca987d46SWarner Losh } 161*ca987d46SWarner Losh idev->d_dev = dv; 162*ca987d46SWarner Losh idev->d_type = dv->dv_type; 163*ca987d46SWarner Losh if (dev == NULL) { 164*ca987d46SWarner Losh free(idev); 165*ca987d46SWarner Losh } else { 166*ca987d46SWarner Losh *dev = idev; 167*ca987d46SWarner Losh } 168*ca987d46SWarner Losh return(0); 169*ca987d46SWarner Losh 170*ca987d46SWarner Losh fail: 171*ca987d46SWarner Losh free(idev); 172*ca987d46SWarner Losh return(err); 173*ca987d46SWarner Losh } 174*ca987d46SWarner Losh 175*ca987d46SWarner Losh 176*ca987d46SWarner Losh char * 177*ca987d46SWarner Losh userboot_fmtdev(void *vdev) 178*ca987d46SWarner Losh { 179*ca987d46SWarner Losh struct disk_devdesc *dev = (struct disk_devdesc *)vdev; 180*ca987d46SWarner Losh static char buf[128]; /* XXX device length constant? */ 181*ca987d46SWarner Losh 182*ca987d46SWarner Losh switch(dev->d_type) { 183*ca987d46SWarner Losh case DEVT_NONE: 184*ca987d46SWarner Losh strcpy(buf, "(no device)"); 185*ca987d46SWarner Losh break; 186*ca987d46SWarner Losh 187*ca987d46SWarner Losh case DEVT_CD: 188*ca987d46SWarner Losh sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 189*ca987d46SWarner Losh break; 190*ca987d46SWarner Losh 191*ca987d46SWarner Losh case DEVT_DISK: 192*ca987d46SWarner Losh return (disk_fmtdev(vdev)); 193*ca987d46SWarner Losh 194*ca987d46SWarner Losh case DEVT_NET: 195*ca987d46SWarner Losh sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 196*ca987d46SWarner Losh break; 197*ca987d46SWarner Losh 198*ca987d46SWarner Losh case DEVT_ZFS: 199*ca987d46SWarner Losh #if defined(USERBOOT_ZFS_SUPPORT) 200*ca987d46SWarner Losh return (zfs_fmtdev(vdev)); 201*ca987d46SWarner Losh #else 202*ca987d46SWarner Losh sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 203*ca987d46SWarner Losh #endif 204*ca987d46SWarner Losh break; 205*ca987d46SWarner Losh } 206*ca987d46SWarner Losh return(buf); 207*ca987d46SWarner Losh } 208*ca987d46SWarner Losh 209*ca987d46SWarner Losh 210*ca987d46SWarner Losh /* 211*ca987d46SWarner Losh * Set currdev to suit the value being supplied in (value) 212*ca987d46SWarner Losh */ 213*ca987d46SWarner Losh int 214*ca987d46SWarner Losh userboot_setcurrdev(struct env_var *ev, int flags, const void *value) 215*ca987d46SWarner Losh { 216*ca987d46SWarner Losh struct disk_devdesc *ncurr; 217*ca987d46SWarner Losh int rv; 218*ca987d46SWarner Losh 219*ca987d46SWarner Losh if ((rv = userboot_parsedev(&ncurr, value, NULL)) != 0) 220*ca987d46SWarner Losh return(rv); 221*ca987d46SWarner Losh free(ncurr); 222*ca987d46SWarner Losh env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 223*ca987d46SWarner Losh return(0); 224*ca987d46SWarner Losh } 225