/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* VFS operations that involve the list of VFS (named) devices31* (the "dev" in "dev:path" syntax).32*/3334#define VFSINLINE3536#include <types.h>37#include <kern/errno.h>38#include <lib.h>39#include <array.h>40#include <synch.h>41#include <vfs.h>42#include <fs.h>43#include <vnode.h>44#include <device.h>4546/*47* Structure for a single named device.48*49* kd_name - Name of device (eg, "lhd0"). Should always be set to50* a valid string.51*52* kd_rawname - Name of raw device (eg, "lhd0raw"). Is non-NULL if and53* only if this device can have a filesystem mounted on54* it.55*56* kd_device - Device object this name refers to. May be NULL if kd_fs57* is hardwired.58*59* kd_fs - Filesystem object mounted on, or associated with, this60* device. NULL if there is no filesystem.61*62* A filesystem can be associated with a device without having been63* mounted if the device was created that way. In this case,64* kd_rawname is NULL (prohibiting mount/unmount), and, as there is65* then no way to access kd_device, it will be NULL as well. This is66* intended for devices that are inherently filesystems, like emu0.67*68* Referencing kd_name, or the filesystem volume name, on a device69* with a filesystem mounted returns the root of the filesystem.70* Referencing kd_name on a mountable device with no filesystem71* returns ENXIO. Referencing kd_name on a device that is not72* mountable and has no filesystem, or kd_rawname on a mountable73* device, returns the device itself.74*/7576struct knowndev {77char *kd_name;78char *kd_rawname;79struct device *kd_device;80struct vnode *kd_vnode;81struct fs *kd_fs;82};8384DECLARRAY(knowndev);85DEFARRAY(knowndev, /*no inline*/);8687static struct knowndevarray *knowndevs;8889/* The big lock for all FS ops. Remove for filesystem assignment. */90static struct lock *vfs_biglock;91static unsigned vfs_biglock_depth;929394/*95* Setup function96*/97void98vfs_bootstrap(void)99{100knowndevs = knowndevarray_create();101if (knowndevs==NULL) {102panic("vfs: Could not create knowndevs array\n");103}104105vfs_biglock = lock_create("vfs_biglock");106if (vfs_biglock==NULL) {107panic("vfs: Could not create vfs big lock\n");108}109vfs_biglock_depth = 0;110111devnull_create();112}113114/*115* Operations on vfs_biglock. We make it recursive to avoid having to116* think about where we do and don't already hold it. This is an117* undesirable hack that's frequently necessary when a lock covers too118* much material. Your solution scheme for FS and VFS locking should119* not require recursive locks.120*/121void122vfs_biglock_acquire(void)123{124if (!lock_do_i_hold(vfs_biglock)) {125lock_acquire(vfs_biglock);126}127vfs_biglock_depth++;128}129130void131vfs_biglock_release(void)132{133KASSERT(lock_do_i_hold(vfs_biglock));134KASSERT(vfs_biglock_depth > 0);135vfs_biglock_depth--;136if (vfs_biglock_depth == 0) {137lock_release(vfs_biglock);138}139}140141bool142vfs_biglock_do_i_hold(void)143{144return lock_do_i_hold(vfs_biglock);145}146147/*148* Global sync function - call FSOP_SYNC on all devices.149*/150int151vfs_sync(void)152{153struct knowndev *dev;154unsigned i, num;155156vfs_biglock_acquire();157158num = knowndevarray_num(knowndevs);159for (i=0; i<num; i++) {160dev = knowndevarray_get(knowndevs, i);161if (dev->kd_fs != NULL) {162/*result =*/ FSOP_SYNC(dev->kd_fs);163}164}165166vfs_biglock_release();167168return 0;169}170171/*172* Given a device name (lhd0, emu0, somevolname, null, etc.), hand173* back an appropriate vnode.174*/175int176vfs_getroot(const char *devname, struct vnode **result)177{178struct knowndev *kd;179unsigned i, num;180181KASSERT(vfs_biglock_do_i_hold());182183num = knowndevarray_num(knowndevs);184for (i=0; i<num; i++) {185kd = knowndevarray_get(knowndevs, i);186187/*188* If this device has a mounted filesystem, and189* DEVNAME names either the filesystem or the device,190* return the root of the filesystem.191*192* If it has no mounted filesystem, it's mountable,193* and DEVNAME names the device, return ENXIO.194*/195196if (kd->kd_fs!=NULL) {197const char *volname;198volname = FSOP_GETVOLNAME(kd->kd_fs);199200if (!strcmp(kd->kd_name, devname) ||201(volname!=NULL && !strcmp(volname, devname))) {202*result = FSOP_GETROOT(kd->kd_fs);203return 0;204}205}206else {207if (kd->kd_rawname!=NULL &&208!strcmp(kd->kd_name, devname)) {209return ENXIO;210}211}212213/*214* If DEVNAME names the device, and we get here, it215* must have no fs and not be mountable. In this case,216* we return the device itself.217*/218if (!strcmp(kd->kd_name, devname)) {219KASSERT(kd->kd_fs==NULL);220KASSERT(kd->kd_rawname==NULL);221KASSERT(kd->kd_device != NULL);222VOP_INCREF(kd->kd_vnode);223*result = kd->kd_vnode;224return 0;225}226227/*228* If the device has a rawname and DEVNAME names that,229* return the device itself.230*/231if (kd->kd_rawname!=NULL && !strcmp(kd->kd_rawname, devname)) {232KASSERT(kd->kd_device != NULL);233VOP_INCREF(kd->kd_vnode);234*result = kd->kd_vnode;235return 0;236}237238/*239* If none of the above tests matched, we didn't name240* any of the names of this device, so go on to the241* next one.242*/243}244245/*246* If we got here, the device specified by devname doesn't exist.247*/248249return ENODEV;250}251252/*253* Given a filesystem, hand back the name of the device it's mounted on.254*/255const char *256vfs_getdevname(struct fs *fs)257{258struct knowndev *kd;259unsigned i, num;260261KASSERT(fs != NULL);262263KASSERT(vfs_biglock_do_i_hold());264265num = knowndevarray_num(knowndevs);266for (i=0; i<num; i++) {267kd = knowndevarray_get(knowndevs, i);268269if (kd->kd_fs == fs) {270/*271* This is not a race condition: as long as the272* guy calling us holds a reference to the fs,273* the fs cannot go away, and the device can't274* go away until the fs goes away.275*/276return kd->kd_name;277}278}279280return NULL;281}282283/*284* Assemble the name for a raw device from the name for the regular device.285*/286static287char *288mkrawname(const char *name)289{290char *s = kmalloc(strlen(name)+3+1);291if (!s) {292return NULL;293}294strcpy(s, name);295strcat(s, "raw");296return s;297}298299300/*301* Check if the two strings passed in are the same, if they're both302* not NULL (the latter part being significant).303*/304static305inline306int307samestring(const char *a, const char *b)308{309if (a==NULL || b==NULL) {310return 0;311}312return !strcmp(a, b);313}314315/*316* Check if the first string passed is the same as any of the three others,317* if they're not NULL.318*/319static320inline321int322samestring3(const char *a, const char *b, const char *c, const char *d)323{324return samestring(a,b) || samestring(a,c) || samestring(a,d);325}326327/*328* Check if any of the three names passed in already exists as a device329* name.330*/331332static333int334badnames(const char *n1, const char *n2, const char *n3)335{336const char *volname;337unsigned i, num;338struct knowndev *kd;339340KASSERT(vfs_biglock_do_i_hold());341342num = knowndevarray_num(knowndevs);343for (i=0; i<num; i++) {344kd = knowndevarray_get(knowndevs, i);345346if (kd->kd_fs) {347volname = FSOP_GETVOLNAME(kd->kd_fs);348if (samestring3(volname, n1, n2, n3)) {349return 1;350}351}352353if (samestring3(kd->kd_rawname, n1, n2, n3) ||354samestring3(kd->kd_name, n1, n2, n3)) {355return 1;356}357}358359return 0;360}361362/*363* Add a new device to the VFS layer's device table.364*365* If "mountable" is set, the device will be treated as one that expects366* to have a filesystem mounted on it, and a raw device will be created367* for direct access.368*/369static370int371vfs_doadd(const char *dname, int mountable, struct device *dev, struct fs *fs)372{373char *name=NULL, *rawname=NULL;374struct knowndev *kd=NULL;375struct vnode *vnode=NULL;376const char *volname=NULL;377unsigned index;378int result;379380vfs_biglock_acquire();381382name = kstrdup(dname);383if (name==NULL) {384goto nomem;385}386if (mountable) {387rawname = mkrawname(name);388if (rawname==NULL) {389goto nomem;390}391}392393vnode = dev_create_vnode(dev);394if (vnode==NULL) {395goto nomem;396}397398kd = kmalloc(sizeof(struct knowndev));399if (kd==NULL) {400goto nomem;401}402403kd->kd_name = name;404kd->kd_rawname = rawname;405kd->kd_device = dev;406kd->kd_vnode = vnode;407kd->kd_fs = fs;408409if (fs!=NULL) {410volname = FSOP_GETVOLNAME(fs);411}412413if (badnames(name, rawname, volname)) {414vfs_biglock_release();415return EEXIST;416}417418result = knowndevarray_add(knowndevs, kd, &index);419420if (result == 0 && dev != NULL) {421/* use index+1 as the device number, so 0 is reserved */422dev->d_devnumber = index+1;423}424425vfs_biglock_release();426return result;427428nomem:429430if (name) {431kfree(name);432}433if (rawname) {434kfree(rawname);435}436if (vnode) {437kfree(vnode);438}439if (kd) {440kfree(kd);441}442443vfs_biglock_release();444return ENOMEM;445}446447/*448* Add a new device, by name. See above for the description of449* mountable.450*/451int452vfs_adddev(const char *devname, struct device *dev, int mountable)453{454return vfs_doadd(devname, mountable, dev, NULL);455}456457/*458* Add a filesystem that does not have an underlying device.459* This is used for emufs, but might also be used for network460* filesystems and the like.461*/462int463vfs_addfs(const char *devname, struct fs *fs)464{465return vfs_doadd(devname, 0, NULL, fs);466}467468//////////////////////////////////////////////////469470/*471* Look for a mountable device named DEVNAME.472* Should already hold knowndevs_lock.473*/474static475int476findmount(const char *devname, struct knowndev **result)477{478struct knowndev *dev;479unsigned i, num;480bool found = false;481482KASSERT(vfs_biglock_do_i_hold());483484num = knowndevarray_num(knowndevs);485for (i=0; !found && i<num; i++) {486dev = knowndevarray_get(knowndevs, i);487if (dev->kd_rawname==NULL) {488/* not mountable/unmountable */489continue;490}491492if (!strcmp(devname, dev->kd_name)) {493*result = dev;494found = true;495}496}497498return found ? 0 : ENODEV;499}500501/*502* Mount a filesystem. Once we've found the device, call MOUNTFUNC to503* set up the filesystem and hand back a struct fs.504*505* The DATA argument is passed through unchanged to MOUNTFUNC.506*/507int508vfs_mount(const char *devname, void *data,509int (*mountfunc)(void *data, struct device *, struct fs **ret))510{511const char *volname;512struct knowndev *kd;513struct fs *fs;514int result;515516vfs_biglock_acquire();517518result = findmount(devname, &kd);519if (result) {520vfs_biglock_release();521return result;522}523524if (kd->kd_fs != NULL) {525vfs_biglock_release();526return EBUSY;527}528KASSERT(kd->kd_rawname != NULL);529KASSERT(kd->kd_device != NULL);530531result = mountfunc(data, kd->kd_device, &fs);532if (result) {533vfs_biglock_release();534return result;535}536537KASSERT(fs != NULL);538539kd->kd_fs = fs;540541volname = FSOP_GETVOLNAME(fs);542kprintf("vfs: Mounted %s: on %s\n",543volname ? volname : kd->kd_name, kd->kd_name);544545vfs_biglock_release();546return 0;547}548549/*550* Unmount a filesystem/device by name.551* First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT.552*/553int554vfs_unmount(const char *devname)555{556struct knowndev *kd;557int result;558559vfs_biglock_acquire();560561result = findmount(devname, &kd);562if (result) {563goto fail;564}565566if (kd->kd_fs == NULL) {567result = EINVAL;568goto fail;569}570KASSERT(kd->kd_rawname != NULL);571KASSERT(kd->kd_device != NULL);572573result = FSOP_SYNC(kd->kd_fs);574if (result) {575goto fail;576}577578result = FSOP_UNMOUNT(kd->kd_fs);579if (result) {580goto fail;581}582583kprintf("vfs: Unmounted %s:\n", kd->kd_name);584585/* now drop the filesystem */586kd->kd_fs = NULL;587588KASSERT(result==0);589590fail:591vfs_biglock_release();592return result;593}594595/*596* Global unmount function.597*/598int599vfs_unmountall(void)600{601struct knowndev *dev;602unsigned i, num;603int result;604605vfs_biglock_acquire();606607num = knowndevarray_num(knowndevs);608for (i=0; i<num; i++) {609dev = knowndevarray_get(knowndevs, i);610if (dev->kd_rawname == NULL) {611/* not mountable/unmountable */612continue;613}614if (dev->kd_fs == NULL) {615/* not mounted */616continue;617}618619kprintf("vfs: Unmounting %s:\n", dev->kd_name);620621result = FSOP_SYNC(dev->kd_fs);622if (result) {623kprintf("vfs: Warning: sync failed for %s: %s, trying "624"again\n", dev->kd_name, strerror(result));625626result = FSOP_SYNC(dev->kd_fs);627if (result) {628kprintf("vfs: Warning: sync failed second time"629" for %s: %s, giving up...\n",630dev->kd_name, strerror(result));631continue;632}633}634635result = FSOP_UNMOUNT(dev->kd_fs);636if (result == EBUSY) {637kprintf("vfs: Cannot unmount %s: (busy)\n",638dev->kd_name);639continue;640}641if (result) {642kprintf("vfs: Warning: unmount failed for %s:"643" %s, already synced, dropping...\n",644dev->kd_name, strerror(result));645continue;646}647648/* now drop the filesystem */649dev->kd_fs = NULL;650}651652vfs_biglock_release();653654return 0;655}656657658