xref: /freebsd/stand/userboot/userboot/devicename.c (revision ca987d4641cdcd7f27e153db17c5bf064934faf5)
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