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