os161-1.99
 All Data Structures
vfslist.c
00001 /*
00002  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
00003  *      The President and Fellows of Harvard College.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the University nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  */
00029 
00030 /*
00031  * VFS operations that involve the list of VFS (named) devices
00032  * (the "dev" in "dev:path" syntax).
00033  */
00034 
00035 #define VFSINLINE
00036 
00037 #include <types.h>
00038 #include <kern/errno.h>
00039 #include <lib.h>
00040 #include <array.h>
00041 #include <synch.h>
00042 #include <vfs.h>
00043 #include <fs.h>
00044 #include <vnode.h>
00045 #include <device.h>
00046 
00047 /*
00048  * Structure for a single named device.
00049  * 
00050  * kd_name    - Name of device (eg, "lhd0"). Should always be set to
00051  *              a valid string.
00052  *
00053  * kd_rawname - Name of raw device (eg, "lhd0raw"). Is non-NULL if and
00054  *              only if this device can have a filesystem mounted on
00055  *              it.
00056  *
00057  * kd_device  - Device object this name refers to. May be NULL if kd_fs
00058  *              is hardwired.
00059  *
00060  * kd_fs      - Filesystem object mounted on, or associated with, this
00061  *              device. NULL if there is no filesystem. 
00062  *
00063  * A filesystem can be associated with a device without having been
00064  * mounted if the device was created that way. In this case,
00065  * kd_rawname is NULL (prohibiting mount/unmount), and, as there is
00066  * then no way to access kd_device, it will be NULL as well. This is
00067  * intended for devices that are inherently filesystems, like emu0.
00068  *
00069  * Referencing kd_name, or the filesystem volume name, on a device
00070  * with a filesystem mounted returns the root of the filesystem.
00071  * Referencing kd_name on a mountable device with no filesystem
00072  * returns ENXIO. Referencing kd_name on a device that is not
00073  * mountable and has no filesystem, or kd_rawname on a mountable
00074  * device, returns the device itself.
00075  */
00076 
00077 struct knowndev {
00078         char *kd_name;
00079         char *kd_rawname;
00080         struct device *kd_device;
00081         struct vnode *kd_vnode;
00082         struct fs *kd_fs;
00083 };
00084 
00085 DECLARRAY(knowndev);
00086 DEFARRAY(knowndev, /*no inline*/);
00087 
00088 static struct knowndevarray *knowndevs;
00089 
00090 /* The big lock for all FS ops. Remove for filesystem assignment. */
00091 static struct lock *vfs_biglock;
00092 static unsigned vfs_biglock_depth;
00093 
00094 
00095 /*
00096  * Setup function
00097  */
00098 void
00099 vfs_bootstrap(void)
00100 {
00101         knowndevs = knowndevarray_create();
00102         if (knowndevs==NULL) {
00103                 panic("vfs: Could not create knowndevs array\n");
00104         }
00105 
00106         vfs_biglock = lock_create("vfs_biglock");
00107         if (vfs_biglock==NULL) {
00108                 panic("vfs: Could not create vfs big lock\n");
00109         }
00110         vfs_biglock_depth = 0;
00111 
00112         devnull_create();
00113 }
00114 
00115 /*
00116  * Operations on vfs_biglock. We make it recursive to avoid having to
00117  * think about where we do and don't already hold it. This is an
00118  * undesirable hack that's frequently necessary when a lock covers too
00119  * much material. Your solution scheme for FS and VFS locking should
00120  * not require recursive locks.
00121  */
00122 void
00123 vfs_biglock_acquire(void)
00124 {
00125         if (!lock_do_i_hold(vfs_biglock)) {
00126                 lock_acquire(vfs_biglock);
00127         }
00128         vfs_biglock_depth++;
00129 }
00130 
00131 void
00132 vfs_biglock_release(void)
00133 {
00134         KASSERT(lock_do_i_hold(vfs_biglock));
00135         KASSERT(vfs_biglock_depth > 0);
00136         vfs_biglock_depth--;
00137         if (vfs_biglock_depth == 0) {
00138                 lock_release(vfs_biglock);
00139         }
00140 }
00141 
00142 bool
00143 vfs_biglock_do_i_hold(void)
00144 {
00145         return lock_do_i_hold(vfs_biglock);
00146 }
00147 
00148 /*
00149  * Global sync function - call FSOP_SYNC on all devices.
00150  */
00151 int
00152 vfs_sync(void)
00153 {
00154         struct knowndev *dev;
00155         unsigned i, num;
00156 
00157         vfs_biglock_acquire();
00158 
00159         num = knowndevarray_num(knowndevs);
00160         for (i=0; i<num; i++) {
00161                 dev = knowndevarray_get(knowndevs, i);
00162                 if (dev->kd_fs != NULL) {
00163                         /*result =*/ FSOP_SYNC(dev->kd_fs);
00164                 }
00165         }
00166 
00167         vfs_biglock_release();
00168 
00169         return 0;
00170 }
00171 
00172 /*
00173  * Given a device name (lhd0, emu0, somevolname, null, etc.), hand
00174  * back an appropriate vnode.
00175  */
00176 int
00177 vfs_getroot(const char *devname, struct vnode **result)
00178 {
00179         struct knowndev *kd;
00180         unsigned i, num;
00181 
00182         KASSERT(vfs_biglock_do_i_hold());
00183 
00184         num = knowndevarray_num(knowndevs);
00185         for (i=0; i<num; i++) {
00186                 kd = knowndevarray_get(knowndevs, i);
00187 
00188                 /*
00189                  * If this device has a mounted filesystem, and
00190                  * DEVNAME names either the filesystem or the device,
00191                  * return the root of the filesystem.
00192                  *
00193                  * If it has no mounted filesystem, it's mountable,
00194                  * and DEVNAME names the device, return ENXIO.
00195                  */
00196 
00197                 if (kd->kd_fs!=NULL) {
00198                         const char *volname;
00199                         volname = FSOP_GETVOLNAME(kd->kd_fs);
00200 
00201                         if (!strcmp(kd->kd_name, devname) ||
00202                             (volname!=NULL && !strcmp(volname, devname))) {
00203                                 *result = FSOP_GETROOT(kd->kd_fs);
00204                                 return 0;
00205                         }
00206                 }
00207                 else {
00208                         if (kd->kd_rawname!=NULL &&
00209                             !strcmp(kd->kd_name, devname)) {
00210                                 return ENXIO;
00211                         }
00212                 }
00213 
00214                 /*
00215                  * If DEVNAME names the device, and we get here, it
00216                  * must have no fs and not be mountable. In this case,
00217                  * we return the device itself.
00218                  */
00219                 if (!strcmp(kd->kd_name, devname)) {
00220                         KASSERT(kd->kd_fs==NULL);
00221                         KASSERT(kd->kd_rawname==NULL);
00222                         KASSERT(kd->kd_device != NULL);
00223                         VOP_INCREF(kd->kd_vnode);
00224                         *result = kd->kd_vnode;
00225                         return 0;
00226                 }
00227 
00228                 /*
00229                  * If the device has a rawname and DEVNAME names that,
00230                  * return the device itself.
00231                  */
00232                 if (kd->kd_rawname!=NULL && !strcmp(kd->kd_rawname, devname)) {
00233                         KASSERT(kd->kd_device != NULL);
00234                         VOP_INCREF(kd->kd_vnode);
00235                         *result = kd->kd_vnode;
00236                         return 0;
00237                 }
00238 
00239                 /*
00240                  * If none of the above tests matched, we didn't name
00241                  * any of the names of this device, so go on to the
00242                  * next one. 
00243                  */
00244         }
00245 
00246         /*
00247          * If we got here, the device specified by devname doesn't exist.
00248          */
00249 
00250         return ENODEV;
00251 }
00252 
00253 /*
00254  * Given a filesystem, hand back the name of the device it's mounted on.
00255  */
00256 const char *
00257 vfs_getdevname(struct fs *fs)
00258 {
00259         struct knowndev *kd;
00260         unsigned i, num;
00261 
00262         KASSERT(fs != NULL);
00263 
00264         KASSERT(vfs_biglock_do_i_hold());
00265 
00266         num = knowndevarray_num(knowndevs);
00267         for (i=0; i<num; i++) {
00268                 kd = knowndevarray_get(knowndevs, i);
00269 
00270                 if (kd->kd_fs == fs) {
00271                         /*
00272                          * This is not a race condition: as long as the
00273                          * guy calling us holds a reference to the fs,
00274                          * the fs cannot go away, and the device can't
00275                          * go away until the fs goes away.
00276                          */
00277                         return kd->kd_name;
00278                 }
00279         }
00280 
00281         return NULL;
00282 }
00283 
00284 /*
00285  * Assemble the name for a raw device from the name for the regular device.
00286  */
00287 static
00288 char *
00289 mkrawname(const char *name)
00290 {
00291         char *s = kmalloc(strlen(name)+3+1);
00292         if (!s) {
00293                 return NULL;
00294         }
00295         strcpy(s, name);
00296         strcat(s, "raw");
00297         return s;
00298 }
00299 
00300 
00301 /*
00302  * Check if the two strings passed in are the same, if they're both
00303  * not NULL (the latter part being significant).
00304  */
00305 static
00306 inline
00307 int
00308 samestring(const char *a, const char *b)
00309 {
00310         if (a==NULL || b==NULL) {
00311                 return 0;
00312         }
00313         return !strcmp(a, b);
00314 }
00315 
00316 /*
00317  * Check if the first string passed is the same as any of the three others,
00318  * if they're not NULL.
00319  */
00320 static
00321 inline
00322 int
00323 samestring3(const char *a, const char *b, const char *c, const char *d)
00324 {
00325         return samestring(a,b) || samestring(a,c) || samestring(a,d);
00326 }
00327 
00328 /*
00329  * Check if any of the three names passed in already exists as a device
00330  * name.
00331  */
00332 
00333 static
00334 int
00335 badnames(const char *n1, const char *n2, const char *n3)
00336 {
00337         const char *volname;
00338         unsigned i, num;
00339         struct knowndev *kd;
00340 
00341         KASSERT(vfs_biglock_do_i_hold());
00342 
00343         num = knowndevarray_num(knowndevs);
00344         for (i=0; i<num; i++) {
00345                 kd = knowndevarray_get(knowndevs, i);
00346 
00347                 if (kd->kd_fs) {
00348                         volname = FSOP_GETVOLNAME(kd->kd_fs);
00349                         if (samestring3(volname, n1, n2, n3)) {
00350                                 return 1;
00351                         }
00352                 }
00353 
00354                 if (samestring3(kd->kd_rawname, n1, n2, n3) ||
00355                     samestring3(kd->kd_name, n1, n2, n3)) {
00356                         return 1;
00357                 }
00358         }
00359 
00360         return 0;
00361 }
00362 
00363 /*
00364  * Add a new device to the VFS layer's device table.
00365  *
00366  * If "mountable" is set, the device will be treated as one that expects
00367  * to have a filesystem mounted on it, and a raw device will be created
00368  * for direct access.
00369  */
00370 static
00371 int
00372 vfs_doadd(const char *dname, int mountable, struct device *dev, struct fs *fs)
00373 {
00374         char *name=NULL, *rawname=NULL;
00375         struct knowndev *kd=NULL;
00376         struct vnode *vnode=NULL;
00377         const char *volname=NULL;
00378         unsigned index;
00379         int result;
00380 
00381         vfs_biglock_acquire();
00382 
00383         name = kstrdup(dname);
00384         if (name==NULL) {
00385                 goto nomem;
00386         }
00387         if (mountable) {
00388                 rawname = mkrawname(name);
00389                 if (rawname==NULL) {
00390                         goto nomem;
00391                 }
00392         }
00393 
00394         vnode = dev_create_vnode(dev);
00395         if (vnode==NULL) {
00396                 goto nomem;
00397         }
00398 
00399         kd = kmalloc(sizeof(struct knowndev));
00400         if (kd==NULL) {
00401                 goto nomem;
00402         }
00403 
00404         kd->kd_name = name;
00405         kd->kd_rawname = rawname;
00406         kd->kd_device = dev;
00407         kd->kd_vnode = vnode;
00408         kd->kd_fs = fs;
00409 
00410         if (fs!=NULL) {
00411                 volname = FSOP_GETVOLNAME(fs);
00412         }
00413 
00414         if (badnames(name, rawname, volname)) {
00415                 vfs_biglock_release();
00416                 return EEXIST;
00417         }
00418 
00419         result = knowndevarray_add(knowndevs, kd, &index);
00420 
00421         if (result == 0 && dev != NULL) {
00422                 /* use index+1 as the device number, so 0 is reserved */
00423                 dev->d_devnumber = index+1;
00424         }
00425 
00426         vfs_biglock_release();
00427         return result;
00428 
00429  nomem:
00430 
00431         if (name) {
00432                 kfree(name);
00433         }
00434         if (rawname) {
00435                 kfree(rawname);
00436         }
00437         if (vnode) {
00438                 kfree(vnode);
00439         }
00440         if (kd) {
00441                 kfree(kd);
00442         }
00443         
00444         vfs_biglock_release();
00445         return ENOMEM;
00446 }
00447 
00448 /*
00449  * Add a new device, by name. See above for the description of
00450  * mountable.
00451  */
00452 int
00453 vfs_adddev(const char *devname, struct device *dev, int mountable)
00454 {
00455         return vfs_doadd(devname, mountable, dev, NULL);
00456 }
00457 
00458 /*
00459  * Add a filesystem that does not have an underlying device.
00460  * This is used for emufs, but might also be used for network
00461  * filesystems and the like.
00462  */
00463 int
00464 vfs_addfs(const char *devname, struct fs *fs)
00465 {
00466         return vfs_doadd(devname, 0, NULL, fs);
00467 }
00468 
00469 //////////////////////////////////////////////////
00470 
00471 /*
00472  * Look for a mountable device named DEVNAME.
00473  * Should already hold knowndevs_lock.
00474  */
00475 static
00476 int
00477 findmount(const char *devname, struct knowndev **result)
00478 {
00479         struct knowndev *dev;
00480         unsigned i, num;
00481         bool found = false;
00482 
00483         KASSERT(vfs_biglock_do_i_hold());
00484 
00485         num = knowndevarray_num(knowndevs);
00486         for (i=0; !found && i<num; i++) {
00487                 dev = knowndevarray_get(knowndevs, i);
00488                 if (dev->kd_rawname==NULL) {
00489                         /* not mountable/unmountable */
00490                         continue;
00491                 }
00492 
00493                 if (!strcmp(devname, dev->kd_name)) {
00494                         *result = dev;
00495                         found = true;
00496                 }
00497         }
00498 
00499         return found ? 0 : ENODEV;
00500 }
00501 
00502 /*
00503  * Mount a filesystem. Once we've found the device, call MOUNTFUNC to
00504  * set up the filesystem and hand back a struct fs.
00505  *
00506  * The DATA argument is passed through unchanged to MOUNTFUNC.
00507  */
00508 int
00509 vfs_mount(const char *devname, void *data,
00510           int (*mountfunc)(void *data, struct device *, struct fs **ret))
00511 {
00512         const char *volname;
00513         struct knowndev *kd;
00514         struct fs *fs;
00515         int result;
00516 
00517         vfs_biglock_acquire();
00518 
00519         result = findmount(devname, &kd);
00520         if (result) {
00521                 vfs_biglock_release();
00522                 return result;
00523         }
00524 
00525         if (kd->kd_fs != NULL) {
00526                 vfs_biglock_release();
00527                 return EBUSY;
00528         }
00529         KASSERT(kd->kd_rawname != NULL);
00530         KASSERT(kd->kd_device != NULL);
00531 
00532         result = mountfunc(data, kd->kd_device, &fs);
00533         if (result) {
00534                 vfs_biglock_release();
00535                 return result;
00536         }
00537 
00538         KASSERT(fs != NULL);
00539 
00540         kd->kd_fs = fs;
00541 
00542         volname = FSOP_GETVOLNAME(fs);
00543         kprintf("vfs: Mounted %s: on %s\n",
00544                 volname ? volname : kd->kd_name, kd->kd_name);
00545 
00546         vfs_biglock_release();
00547         return 0;
00548 }
00549 
00550 /*
00551  * Unmount a filesystem/device by name.
00552  * First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT.
00553  */
00554 int
00555 vfs_unmount(const char *devname)
00556 {
00557         struct knowndev *kd;
00558         int result;
00559 
00560         vfs_biglock_acquire();
00561 
00562         result = findmount(devname, &kd);
00563         if (result) {
00564                 goto fail;
00565         }
00566 
00567         if (kd->kd_fs == NULL) {
00568                 result = EINVAL;
00569                 goto fail;
00570         }
00571         KASSERT(kd->kd_rawname != NULL);
00572         KASSERT(kd->kd_device != NULL);
00573 
00574         result = FSOP_SYNC(kd->kd_fs);
00575         if (result) {
00576                 goto fail;
00577         }
00578 
00579         result = FSOP_UNMOUNT(kd->kd_fs);
00580         if (result) {
00581                 goto fail;
00582         }
00583 
00584         kprintf("vfs: Unmounted %s:\n", kd->kd_name);
00585 
00586         /* now drop the filesystem */
00587         kd->kd_fs = NULL;
00588 
00589         KASSERT(result==0);
00590 
00591  fail:
00592         vfs_biglock_release();
00593         return result;
00594 }
00595 
00596 /*
00597  * Global unmount function.
00598  */
00599 int
00600 vfs_unmountall(void)
00601 {
00602         struct knowndev *dev;
00603         unsigned i, num;
00604         int result;
00605 
00606         vfs_biglock_acquire();
00607 
00608         num = knowndevarray_num(knowndevs);
00609         for (i=0; i<num; i++) {
00610                 dev = knowndevarray_get(knowndevs, i);
00611                 if (dev->kd_rawname == NULL) {
00612                         /* not mountable/unmountable */
00613                         continue;
00614                 }
00615                 if (dev->kd_fs == NULL) {
00616                         /* not mounted */
00617                         continue;
00618                 }
00619 
00620                 kprintf("vfs: Unmounting %s:\n", dev->kd_name);
00621 
00622                 result = FSOP_SYNC(dev->kd_fs);
00623                 if (result) {
00624                         kprintf("vfs: Warning: sync failed for %s: %s, trying "
00625                                 "again\n", dev->kd_name, strerror(result));
00626 
00627                         result = FSOP_SYNC(dev->kd_fs);
00628                         if (result) {
00629                                 kprintf("vfs: Warning: sync failed second time"
00630                                         " for %s: %s, giving up...\n",
00631                                         dev->kd_name, strerror(result));
00632                                 continue;
00633                         }
00634                 }
00635 
00636                 result = FSOP_UNMOUNT(dev->kd_fs);
00637                 if (result == EBUSY) {
00638                         kprintf("vfs: Cannot unmount %s: (busy)\n", 
00639                                 dev->kd_name);
00640                         continue;
00641                 }
00642                 if (result) {
00643                         kprintf("vfs: Warning: unmount failed for %s:"
00644                                 " %s, already synced, dropping...\n",
00645                                 dev->kd_name, strerror(result));
00646                         continue;
00647                 }
00648 
00649                 /* now drop the filesystem */
00650                 dev->kd_fs = NULL;
00651         }
00652 
00653         vfs_biglock_release();
00654 
00655         return 0;
00656 }
 All Data Structures