vfs_mount.c revision 75858
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: head/sys/kern/vfs_mount.c 75858 2001-04-23 09:05:15Z grog $ 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 "opt_rootdevname.h" 46 47#include <sys/param.h> 48#include <sys/kernel.h> 49#include <sys/systm.h> 50#include <sys/vnode.h> 51#include <net/radix.h> 52#include <sys/socket.h> 53#include <sys/mount.h> 54#include <sys/malloc.h> 55#include <sys/reboot.h> 56#include <sys/diskslice.h> 57#include <sys/disklabel.h> 58#include <sys/conf.h> 59#include <sys/cons.h> 60#include <paths.h> 61 62#include "opt_ddb.h" 63#ifdef DDB 64#include <ddb/ddb.h> 65#endif 66 67MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure"); 68 69#define ROOTNAME "root_device" 70 71struct vnode *rootvnode; 72 73/* 74 * The root specifiers we will try if RB_CDROM is specified. 75 */ 76static char *cdrom_rootdevnames[] = { 77 "cd9660:cd0a", 78 "cd9660:acd0a", 79 "cd9660:wcd0a", 80 NULL 81}; 82 83static void vfs_mountroot(void *junk); 84static int vfs_mountroot_try(char *mountfrom); 85static int vfs_mountroot_ask(void); 86static void gets(char *cp); 87 88/* legacy find-root code */ 89char *rootdevnames[2] = {NULL, NULL}; 90static int setrootbyname(char *name); 91 92SYSINIT(mountroot, SI_SUB_MOUNT_ROOT, SI_ORDER_SECOND, vfs_mountroot, NULL); 93 94/* 95 * Find and mount the root filesystem 96 */ 97static void 98vfs_mountroot(void *junk) 99{ 100 int i; 101 102 /* 103 * The root filesystem information is compiled in, and we are 104 * booted with instructions to use it. 105 */ 106#ifdef ROOTDEVNAME 107 if ((boothowto & RB_DFLTROOT) && 108 !vfs_mountroot_try(ROOTDEVNAME)) 109 return; 110#endif 111 /* 112 * We are booted with instructions to prompt for the root filesystem, 113 * or to use the compiled-in default when it doesn't exist. 114 */ 115 if (boothowto & (RB_DFLTROOT | RB_ASKNAME)) { 116 if (!vfs_mountroot_ask()) 117 return; 118 } 119 120 /* 121 * We've been given the generic "use CDROM as root" flag. This is 122 * necessary because one media may be used in many different 123 * devices, so we need to search for them. 124 */ 125 if (boothowto & RB_CDROM) { 126 for (i = 0; cdrom_rootdevnames[i] != NULL; i++) { 127 if (!vfs_mountroot_try(cdrom_rootdevnames[i])) 128 return; 129 } 130 } 131 132 /* 133 * Try to use the value read by the loader from /etc/fstab, or 134 * supplied via some other means. This is the preferred 135 * mechanism. 136 */ 137 if (!vfs_mountroot_try(getenv("vfs.root.mountfrom"))) 138 return; 139 140 /* 141 * Try values that may have been computed by the machine-dependant 142 * legacy code. 143 */ 144 if (!vfs_mountroot_try(rootdevnames[0])) 145 return; 146 if (!vfs_mountroot_try(rootdevnames[1])) 147 return; 148 149 /* 150 * If we have a compiled-in default, and haven't already tried it, try 151 * it now. 152 */ 153#ifdef ROOTDEVNAME 154 if (!(boothowto & RB_DFLTROOT)) 155 if (!vfs_mountroot_try(ROOTDEVNAME)) 156 return; 157#endif 158 159 /* 160 * Everything so far has failed, prompt on the console if we haven't 161 * already tried that. 162 */ 163 if (!(boothowto & (RB_DFLTROOT | RB_ASKNAME)) && !vfs_mountroot_ask()) 164 return; 165 panic("Root mount failed, startup aborted."); 166} 167 168/* 169 * Mount (mountfrom) as the root filesystem. 170 */ 171static int 172vfs_mountroot_try(char *mountfrom) 173{ 174 struct mount *mp; 175 char *vfsname, *path; 176 int error; 177 char patt[32]; 178 int s; 179 180 vfsname = NULL; 181 path = NULL; 182 mp = NULL; 183 error = EINVAL; 184 185 if (mountfrom == NULL) 186 return(error); /* don't complain */ 187 188 s = splcam(); /* Overkill, but annoying without it */ 189 printf("Mounting root from %s\n", mountfrom); 190 splx(s); 191 192 /* parse vfs name and path */ 193 vfsname = malloc(MFSNAMELEN, M_MOUNT, M_WAITOK); 194 path = malloc(MNAMELEN, M_MOUNT, M_WAITOK); 195 vfsname[0] = path[0] = 0; 196 sprintf(patt, "%%%d[a-z0-9]:%%%ds", MFSNAMELEN, MNAMELEN); 197 if (sscanf(mountfrom, patt, vfsname, path) < 1) 198 goto done; 199 200 /* allocate a root mount */ 201 error = vfs_rootmountalloc(vfsname, path[0] != 0 ? path : ROOTNAME, 202 &mp); 203 if (error != 0) { 204 printf("Can't allocate root mount for filesystem '%s': %d\n", 205 vfsname, error); 206 goto done; 207 } 208 mp->mnt_flag |= MNT_ROOTFS; 209 210 /* do our best to set rootdev */ 211 if ((path[0] != 0) && setrootbyname(path)) 212 printf("setrootbyname failed\n"); 213 214 /* If the root device is a type "memory disk", mount RW */ 215 if (rootdev != NODEV && devsw(rootdev) && 216 (devsw(rootdev)->d_flags & D_MEMDISK)) 217 mp->mnt_flag &= ~MNT_RDONLY; 218 219 /* 220 * Set the mount path to be something useful, because the 221 * filesystem code isn't responsible now for initialising 222 * f_mntonname unless they want to override the default 223 * (which is `path'.) 224 */ 225 strncpy(mp->mnt_stat.f_mntonname, "/", MNAMELEN); 226 227 error = VFS_MOUNT(mp, NULL, NULL, NULL, curproc); 228 229done: 230 if (vfsname != NULL) 231 free(vfsname, M_MOUNT); 232 if (path != NULL) 233 free(path, M_MOUNT); 234 if (error != 0) { 235 if (mp != NULL) { 236 vfs_unbusy(mp, curproc); 237 free(mp, M_MOUNT); 238 } 239 printf("Root mount failed: %d\n", error); 240 } else { 241 242 /* register with list of mounted filesystems */ 243 mtx_lock(&mountlist_mtx); 244 TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list); 245 mtx_unlock(&mountlist_mtx); 246 247 /* sanity check system clock against root filesystem timestamp */ 248 inittodr(mp->mnt_time); 249 vfs_unbusy(mp, curproc); 250 } 251 return(error); 252} 253 254/* 255 * Spin prompting on the console for a suitable root filesystem 256 */ 257static int 258vfs_mountroot_ask(void) 259{ 260 char name[128]; 261 int i; 262 dev_t dev; 263 264 for(;;) { 265 printf("\nManual root filesystem specification:\n"); 266 printf(" <fstype>:<device> Mount <device> using filesystem <fstype>\n"); 267 printf(" eg. ufs:%sda0s1a\n", _PATH_DEV); 268 printf(" ? List valid disk boot devices\n"); 269 printf(" <empty line> Abort manual input\n"); 270 printf("\nmountroot> "); 271 gets(name); 272 if (name[0] == 0) 273 return(1); 274 if (name[0] == '?') { 275 printf("Possibly valid devices for 'ufs' root:\n"); 276 for (i = 0; i < NUMCDEVSW; i++) { 277 dev = makedev(i, 0); 278 if (devsw(dev) != NULL) 279 printf(" \"%s\"", devsw(dev)->d_name); 280 } 281 printf("\n"); 282 continue; 283 } 284 if (!vfs_mountroot_try(name)) 285 return(0); 286 } 287} 288 289static void 290gets(char *cp) 291{ 292 char *lp; 293 int c; 294 295 lp = cp; 296 for (;;) { 297 printf("%c", c = cngetc() & 0177); 298 switch (c) { 299 case -1: 300 case '\n': 301 case '\r': 302 *lp++ = '\0'; 303 return; 304 case '\b': 305 case '\177': 306 if (lp > cp) { 307 printf(" \b"); 308 lp--; 309 } 310 continue; 311 case '#': 312 lp--; 313 if (lp < cp) 314 lp = cp; 315 continue; 316 case '@': 317 case 'u' & 037: 318 lp = cp; 319 printf("%c", '\n'); 320 continue; 321 default: 322 *lp++ = c; 323 } 324 } 325} 326 327/* 328 * Convert a given name to the dev_t of the disk-like device 329 * it refers to. 330 */ 331dev_t 332getdiskbyname(char *name) { 333 char *cp; 334 dev_t dev; 335 336 cp = name; 337 if (!bcmp(cp, "/dev/", 5)) 338 cp += 5; 339 340 dev = NODEV; 341 EVENTHANDLER_INVOKE(dev_clone, cp, strlen(cp), &dev); 342 return (dev); 343} 344 345/* 346 * Set rootdev to match (name), given that we expect it to 347 * refer to a disk-like device. 348 */ 349static int 350setrootbyname(char *name) 351{ 352 dev_t diskdev; 353 354 diskdev = getdiskbyname(name); 355 if (diskdev != NODEV) { 356 rootdev = diskdev; 357 return (0); 358 } 359 360 return (1); 361} 362 363#ifdef DDB 364DB_SHOW_COMMAND(disk, db_getdiskbyname) 365{ 366 dev_t dev; 367 368 if (modif[0] == '\0') { 369 db_error("usage: show disk/devicename"); 370 return; 371 } 372 dev = getdiskbyname(modif); 373 if (dev != NODEV) 374 db_printf("dev_t = %p\n", dev); 375 else 376 db_printf("No disk device matched.\n"); 377} 378#endif 379