]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/vfs_mount.c
This is a complete rewrite of vfs_conf.c, which changes the way the root
[FreeBSD/FreeBSD.git] / sys / kern / vfs_mount.c
1 /*-
2  * Copyright (c) 1999 Michael Smith
3  * All rights reserved.
4  * Copyright (c) 1999 Poul-Henning Kamp
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  *      $FreeBSD$
29  */
30
31 /*
32  * Locate and mount the root filesystem.
33  *
34  * The root filesystem is detailed in the kernel environment variable
35  * vfs.root.mountfrom, which is expected to be in the general format
36  *
37  * <vfsname>:[<path>]
38  * vfsname   := the name of a VFS known to the kernel and capable
39  *              of being mounted as root
40  * path      := disk device name or other data used by the filesystem
41  *              to locate its physical store
42  *
43  */
44
45 #include <sys/param.h>
46 #include <sys/kernel.h>
47 #include <sys/systm.h>
48 #include <sys/proc.h>
49 #include <sys/vnode.h>
50 #include <sys/mount.h>
51 #include <sys/malloc.h>
52 #include <sys/reboot.h>
53 #include <sys/diskslice.h>
54 #include <sys/disklabel.h>
55 #include <sys/conf.h>
56 #include <sys/cons.h>
57
58 MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
59
60 #define ROOTNAME        "root_device"
61
62 struct vnode    *rootvnode;
63
64 static void     vfs_mountroot(void *junk);
65 static int      vfs_mountroot_try(char *mountfrom);
66 static int      vfs_mountroot_ask(void);
67 static void     gets(char *cp);
68
69 /* legacy find-root code */
70 char            *rootdevnames[2] = {NULL, NULL};
71 static int      setrootbyname(char *name);
72
73 SYSINIT(mountroot, SI_SUB_MOUNT_ROOT, SI_ORDER_FIRST, vfs_mountroot, NULL);
74         
75 /*
76  * Find and mount the root filesystem
77  */
78 static void
79 vfs_mountroot(void *junk)
80 {
81         /* 
82          * The root filesystem information is compiled in, and we are
83          * booted with instructions to use it.
84          */
85 #ifdef ROOTDEVNAME
86         if ((boothowto & RB_DFLTROOT) && 
87             !vfs_mountroot_try(ROOTDEVNAME))
88                 return;
89 #endif
90         /* 
91          * We are booted with instructions to prompt for the root filesystem,
92          * or to use the compiled-in default when it doesn't exist.
93          */
94         if (boothowto & (RB_DFLTROOT | RB_ASKNAME)) {
95                 if (!vfs_mountroot_ask())
96                         return;
97         }
98
99         /*
100          * Try to use the value read by the loader from /etc/fstab, or
101          * supplied via some other means.  This is the preferred 
102          * mechanism.
103          */
104         if (!vfs_mountroot_try(getenv("vfs.root.mountfrom")))
105                 return;
106
107         /* 
108          * Try values that may have been computed by the machine-dependant
109          * legacy code.
110          */
111         if (!vfs_mountroot_try(rootdevnames[0]))
112                 return;
113         if (!vfs_mountroot_try(rootdevnames[1]))
114                 return;
115
116         /*
117          * If we have a compiled-in default, and haven't already tried it, try
118          * it now.
119          */
120 #ifdef ROOTDEVNAME
121         if (!(boothowto & RB_DFLTROOT))
122             !vfs_mountroot_try(ROOTDEVNAME))
123                 return;
124 #endif
125
126         /* 
127          * Everything so far has failed, prompt on the console if we haven't
128          * already tried that.
129          */
130         if (!(boothowto & (RB_DFLTROOT | RB_ASKNAME)) && !vfs_mountroot_ask())
131                 return;
132         panic("Root mount failed, startup aborted.");
133 }
134
135 /*
136  * Mount (mountfrom) as the root filesystem.
137  */
138 static int
139 vfs_mountroot_try(char *mountfrom)
140 {
141         struct mount    *mp;
142         char            *vfsname, *path;
143         int             error;
144         char            patt[16];
145
146         vfsname = path = NULL;
147         error = EINVAL;
148
149         if (mountfrom == NULL)
150                 goto done;
151
152         printf("Mounting root from %s\n", mountfrom);
153
154         /* parse vfs name and path */
155         vfsname = malloc(MFSNAMELEN, M_MOUNT, M_WAITOK);
156         path = malloc(MNAMELEN, M_MOUNT, M_WAITOK);
157         sprintf(patt, "%%%d[a-z]:%%%ds", MFSNAMELEN, MNAMELEN);
158         if (sscanf(mountfrom, patt, vfsname, path) != 2)
159                 goto done;
160
161         /* allocate a root mount */
162         if ((error = vfs_rootmountalloc(vfsname, ROOTNAME, &mp))) {
163                 printf("Can't allocate root mount for filesystem '%s': %d\n",
164                        vfsname, error);
165                 goto done;
166         }
167         mp->mnt_flag |= MNT_ROOTFS;
168
169         /* do our best to set rootdev */
170         if (setrootbyname(path))
171                 printf("setrootbyname failed\n");
172
173         strcpy(mp->mnt_stat.f_mntfromname, path);
174         error = VFS_MOUNT(mp, NULL, NULL, NULL, curproc);
175
176 done:
177         free(vfsname, M_MOUNT);
178         free(path, M_MOUNT);
179         if (error != 0) {
180                 if (mp != NULL)
181                         free(mp, M_MOUNT);
182                 printf("Root mount failed: %d\n", error);
183         } else {
184
185                 /* register with list of mounted filesystems */
186                 simple_lock(&mountlist_slock);
187                 CIRCLEQ_INSERT_HEAD(&mountlist, mp, mnt_list);
188                 simple_unlock(&mountlist_slock);
189
190                 /* sanity check system clock against root filesystem timestamp */
191                 inittodr(mp->mnt_time);
192         }
193         vfs_unbusy(mp, curproc);
194         return(error);
195 }
196
197 /*
198  * Spin prompting on the console for a suitable root filesystem
199  */
200 static int
201 vfs_mountroot_ask(void)
202 {
203         char name[128];
204         int i;
205         dev_t dev;
206
207         for(;;) {
208                 printf("\nManual root filesystem specification:\n");
209                 printf("  <fstype>:<device>  Mount <device> using filesystem <fstype>\n");
210                 printf("                       eg. ufs:/dev/da0s1a\n");
211                 printf("  ?                  List valid disk boot devices\n");
212                 printf("  <empty line>       Abort manual input\n");
213                 printf("\n>>> ");
214                 gets(name);
215                 if (name[0] == 0)
216                         return(1);
217                 if (name[0] == '?') {
218                         printf("Possibly valid devices for 'ufs' root:\n");
219                         for (i = 0; i < NUMCDEVSW; i++) {
220                                 dev = makebdev(i, 0);
221                                 if (devsw(dev) != NULL)
222                                         printf(" \"%s\"", devsw(dev)->d_name);
223                         }
224                         printf("\n");
225                         continue;
226                 }
227                 if (!vfs_mountroot_try(name))
228                         return(0);
229         }
230 }
231
232 static void
233 gets(char *cp)
234 {
235         char *lp;
236         int c;
237
238         lp = cp;
239         for (;;) {
240                 printf("%c", c = cngetc() & 0177);
241                 switch (c) {
242                 case -1:
243                 case '\n':
244                 case '\r':
245                         *lp++ = '\0';
246                         return;
247                 case '\b':
248                 case '\177':
249                         if (lp > cp) {
250                                 printf(" \b");
251                                 lp--;
252                         }
253                         continue;
254                 case '#':
255                         lp--;
256                         if (lp < cp)
257                                 lp = cp;
258                         continue;
259                 case '@':
260                 case 'u' & 037:
261                         lp = cp;
262                         printf("%c", '\n');
263                         continue;
264                 default:
265                         *lp++ = c;
266                 }
267         }
268 }
269
270 /*
271  * Set rootdev to match (name), given that we expect it to
272  * refer to a disk-like device.
273  */
274 static int
275 setrootbyname(char *name)
276 {
277         char *cp;
278         int bd, unit, slice, part;
279         dev_t dev;
280
281         slice = 0;
282         part = 0;
283         cp = rindex(name, '/');
284         if (cp != NULL) {
285                 name = cp + 1;
286         }
287         cp = name;
288         while (cp != '\0' && (*cp < '0' || *cp > '9'))
289                 cp++;
290         if (cp == name) {
291                 printf("missing device name\n");
292                 return(1);
293         }
294         if (*cp == '\0') {
295                 printf("missing unit number\n");
296                 return(1);
297         }
298         unit = *cp - '0';
299         *cp++ = '\0';
300         for (bd = 0; bd < NUMCDEVSW; bd++) {
301                 dev = makebdev(bd, 0);
302                 if (devsw(dev) != NULL &&
303                     strcmp(devsw(dev)->d_name, name) == 0)
304                         goto gotit;
305         }
306         printf("no such device '%s'\n", name);
307         return (2);
308 gotit:
309         while (*cp >= '0' && *cp <= '9')
310                 unit += 10 * unit + *cp++ - '0';
311         if (*cp == 's' && cp[1] >= '0' && cp[1] <= '9') {
312                 slice = cp[1] - '0' + 1;
313                 cp += 2;
314         }
315         if (*cp >= 'a' && *cp <= 'h') {
316                 part = *cp - 'a';
317                 cp++;
318         }
319         if (*cp != '\0') {
320                 printf("junk after name\n");
321                 return (1);
322         }
323         rootdev = makebdev(bd, dkmakeminor(unit, slice, part));
324         return 0;
325 }
326