root/kern/vfs/vfslist.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vfs_bootstrap
  2. vfs_biglock_acquire
  3. vfs_biglock_release
  4. vfs_biglock_do_i_hold
  5. vfs_sync
  6. vfs_getroot
  7. vfs_getdevname
  8. mkrawname
  9. samestring
  10. samestring3
  11. badnames
  12. vfs_doadd
  13. vfs_adddev
  14. vfs_addfs
  15. findmount
  16. vfs_mount
  17. vfs_unmount
  18. vfs_unmountall

   1 /*
   2  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
   3  *      The President and Fellows of Harvard College.
   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  * 3. Neither the name of the University nor the names of its contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 /*
  31  * VFS operations that involve the list of VFS (named) devices
  32  * (the "dev" in "dev:path" syntax).
  33  */
  34 
  35 #define VFSINLINE
  36 
  37 #include <types.h>
  38 #include <kern/errno.h>
  39 #include <lib.h>
  40 #include <array.h>
  41 #include <synch.h>
  42 #include <vfs.h>
  43 #include <fs.h>
  44 #include <vnode.h>
  45 #include <device.h>
  46 
  47 /*
  48  * Structure for a single named device.
  49  * 
  50  * kd_name    - Name of device (eg, "lhd0"). Should always be set to
  51  *              a valid string.
  52  *
  53  * kd_rawname - Name of raw device (eg, "lhd0raw"). Is non-NULL if and
  54  *              only if this device can have a filesystem mounted on
  55  *              it.
  56  *
  57  * kd_device  - Device object this name refers to. May be NULL if kd_fs
  58  *              is hardwired.
  59  *
  60  * kd_fs      - Filesystem object mounted on, or associated with, this
  61  *              device. NULL if there is no filesystem. 
  62  *
  63  * A filesystem can be associated with a device without having been
  64  * mounted if the device was created that way. In this case,
  65  * kd_rawname is NULL (prohibiting mount/unmount), and, as there is
  66  * then no way to access kd_device, it will be NULL as well. This is
  67  * intended for devices that are inherently filesystems, like emu0.
  68  *
  69  * Referencing kd_name, or the filesystem volume name, on a device
  70  * with a filesystem mounted returns the root of the filesystem.
  71  * Referencing kd_name on a mountable device with no filesystem
  72  * returns ENXIO. Referencing kd_name on a device that is not
  73  * mountable and has no filesystem, or kd_rawname on a mountable
  74  * device, returns the device itself.
  75  */
  76 
  77 struct knowndev {
  78         char *kd_name;
  79         char *kd_rawname;
  80         struct device *kd_device;
  81         struct vnode *kd_vnode;
  82         struct fs *kd_fs;
  83 };
  84 
  85 DECLARRAY(knowndev);
  86 DEFARRAY(knowndev, /*no inline*/);
  87 
  88 static struct knowndevarray *knowndevs;
  89 
  90 /* The big lock for all FS ops. Remove for filesystem assignment. */
  91 static struct lock *vfs_biglock;
  92 static unsigned vfs_biglock_depth;
  93 
  94 
  95 /*
  96  * Setup function
  97  */
  98 void
  99 vfs_bootstrap(void)
 100 {
 101         knowndevs = knowndevarray_create();
 102         if (knowndevs==NULL) {
 103                 panic("vfs: Could not create knowndevs array\n");
 104         }
 105 
 106         vfs_biglock = lock_create("vfs_biglock");
 107         if (vfs_biglock==NULL) {
 108                 panic("vfs: Could not create vfs big lock\n");
 109         }
 110         vfs_biglock_depth = 0;
 111 
 112         devnull_create();
 113 }
 114 
 115 /*
 116  * Operations on vfs_biglock. We make it recursive to avoid having to
 117  * think about where we do and don't already hold it. This is an
 118  * undesirable hack that's frequently necessary when a lock covers too
 119  * much material. Your solution scheme for FS and VFS locking should
 120  * not require recursive locks.
 121  */
 122 void
 123 vfs_biglock_acquire(void)
 124 {
 125         if (!lock_do_i_hold(vfs_biglock)) {
 126                 lock_acquire(vfs_biglock);
 127         }
 128         vfs_biglock_depth++;
 129 }
 130 
 131 void
 132 vfs_biglock_release(void)
 133 {
 134         KASSERT(lock_do_i_hold(vfs_biglock));
 135         KASSERT(vfs_biglock_depth > 0);
 136         vfs_biglock_depth--;
 137         if (vfs_biglock_depth == 0) {
 138                 lock_release(vfs_biglock);
 139         }
 140 }
 141 
 142 bool
 143 vfs_biglock_do_i_hold(void)
 144 {
 145         return lock_do_i_hold(vfs_biglock);
 146 }
 147 
 148 /*
 149  * Global sync function - call FSOP_SYNC on all devices.
 150  */
 151 int
 152 vfs_sync(void)
 153 {
 154         struct knowndev *dev;
 155         unsigned i, num;
 156 
 157         vfs_biglock_acquire();
 158 
 159         num = knowndevarray_num(knowndevs);
 160         for (i=0; i<num; i++) {
 161                 dev = knowndevarray_get(knowndevs, i);
 162                 if (dev->kd_fs != NULL) {
 163                         /*result =*/ FSOP_SYNC(dev->kd_fs);
 164                 }
 165         }
 166 
 167         vfs_biglock_release();
 168 
 169         return 0;
 170 }
 171 
 172 /*
 173  * Given a device name (lhd0, emu0, somevolname, null, etc.), hand
 174  * back an appropriate vnode.
 175  */
 176 int
 177 vfs_getroot(const char *devname, struct vnode **result)
 178 {
 179         struct knowndev *kd;
 180         unsigned i, num;
 181 
 182         KASSERT(vfs_biglock_do_i_hold());
 183 
 184         num = knowndevarray_num(knowndevs);
 185         for (i=0; i<num; i++) {
 186                 kd = knowndevarray_get(knowndevs, i);
 187 
 188                 /*
 189                  * If this device has a mounted filesystem, and
 190                  * DEVNAME names either the filesystem or the device,
 191                  * return the root of the filesystem.
 192                  *
 193                  * If it has no mounted filesystem, it's mountable,
 194                  * and DEVNAME names the device, return ENXIO.
 195                  */
 196 
 197                 if (kd->kd_fs!=NULL) {
 198                         const char *volname;
 199                         volname = FSOP_GETVOLNAME(kd->kd_fs);
 200 
 201                         if (!strcmp(kd->kd_name, devname) ||
 202                             (volname!=NULL && !strcmp(volname, devname))) {
 203                                 *result = FSOP_GETROOT(kd->kd_fs);
 204                                 return 0;
 205                         }
 206                 }
 207                 else {
 208                         if (kd->kd_rawname!=NULL &&
 209                             !strcmp(kd->kd_name, devname)) {
 210                                 return ENXIO;
 211                         }
 212                 }
 213 
 214                 /*
 215                  * If DEVNAME names the device, and we get here, it
 216                  * must have no fs and not be mountable. In this case,
 217                  * we return the device itself.
 218                  */
 219                 if (!strcmp(kd->kd_name, devname)) {
 220                         KASSERT(kd->kd_fs==NULL);
 221                         KASSERT(kd->kd_rawname==NULL);
 222                         KASSERT(kd->kd_device != NULL);
 223                         VOP_INCREF(kd->kd_vnode);
 224                         *result = kd->kd_vnode;
 225                         return 0;
 226                 }
 227 
 228                 /*
 229                  * If the device has a rawname and DEVNAME names that,
 230                  * return the device itself.
 231                  */
 232                 if (kd->kd_rawname!=NULL && !strcmp(kd->kd_rawname, devname)) {
 233                         KASSERT(kd->kd_device != NULL);
 234                         VOP_INCREF(kd->kd_vnode);
 235                         *result = kd->kd_vnode;
 236                         return 0;
 237                 }
 238 
 239                 /*
 240                  * If none of the above tests matched, we didn't name
 241                  * any of the names of this device, so go on to the
 242                  * next one. 
 243                  */
 244         }
 245 
 246         /*
 247          * If we got here, the device specified by devname doesn't exist.
 248          */
 249 
 250         return ENODEV;
 251 }
 252 
 253 /*
 254  * Given a filesystem, hand back the name of the device it's mounted on.
 255  */
 256 const char *
 257 vfs_getdevname(struct fs *fs)
 258 {
 259         struct knowndev *kd;
 260         unsigned i, num;
 261 
 262         KASSERT(fs != NULL);
 263 
 264         KASSERT(vfs_biglock_do_i_hold());
 265 
 266         num = knowndevarray_num(knowndevs);
 267         for (i=0; i<num; i++) {
 268                 kd = knowndevarray_get(knowndevs, i);
 269 
 270                 if (kd->kd_fs == fs) {
 271                         /*
 272                          * This is not a race condition: as long as the
 273                          * guy calling us holds a reference to the fs,
 274                          * the fs cannot go away, and the device can't
 275                          * go away until the fs goes away.
 276                          */
 277                         return kd->kd_name;
 278                 }
 279         }
 280 
 281         return NULL;
 282 }
 283 
 284 /*
 285  * Assemble the name for a raw device from the name for the regular device.
 286  */
 287 static
 288 char *
 289 mkrawname(const char *name)
 290 {
 291         char *s = kmalloc(strlen(name)+3+1);
 292         if (!s) {
 293                 return NULL;
 294         }
 295         strcpy(s, name);
 296         strcat(s, "raw");
 297         return s;
 298 }
 299 
 300 
 301 /*
 302  * Check if the two strings passed in are the same, if they're both
 303  * not NULL (the latter part being significant).
 304  */
 305 static
 306 inline
 307 int
 308 samestring(const char *a, const char *b)
 309 {
 310         if (a==NULL || b==NULL) {
 311                 return 0;
 312         }
 313         return !strcmp(a, b);
 314 }
 315 
 316 /*
 317  * Check if the first string passed is the same as any of the three others,
 318  * if they're not NULL.
 319  */
 320 static
 321 inline
 322 int
 323 samestring3(const char *a, const char *b, const char *c, const char *d)
 324 {
 325         return samestring(a,b) || samestring(a,c) || samestring(a,d);
 326 }
 327 
 328 /*
 329  * Check if any of the three names passed in already exists as a device
 330  * name.
 331  */
 332 
 333 static
 334 int
 335 badnames(const char *n1, const char *n2, const char *n3)
 336 {
 337         const char *volname;
 338         unsigned i, num;
 339         struct knowndev *kd;
 340 
 341         KASSERT(vfs_biglock_do_i_hold());
 342 
 343         num = knowndevarray_num(knowndevs);
 344         for (i=0; i<num; i++) {
 345                 kd = knowndevarray_get(knowndevs, i);
 346 
 347                 if (kd->kd_fs) {
 348                         volname = FSOP_GETVOLNAME(kd->kd_fs);
 349                         if (samestring3(volname, n1, n2, n3)) {
 350                                 return 1;
 351                         }
 352                 }
 353 
 354                 if (samestring3(kd->kd_rawname, n1, n2, n3) ||
 355                     samestring3(kd->kd_name, n1, n2, n3)) {
 356                         return 1;
 357                 }
 358         }
 359 
 360         return 0;
 361 }
 362 
 363 /*
 364  * Add a new device to the VFS layer's device table.
 365  *
 366  * If "mountable" is set, the device will be treated as one that expects
 367  * to have a filesystem mounted on it, and a raw device will be created
 368  * for direct access.
 369  */
 370 static
 371 int
 372 vfs_doadd(const char *dname, int mountable, struct device *dev, struct fs *fs)
 373 {
 374         char *name=NULL, *rawname=NULL;
 375         struct knowndev *kd=NULL;
 376         struct vnode *vnode=NULL;
 377         const char *volname=NULL;
 378         unsigned index;
 379         int result;
 380 
 381         vfs_biglock_acquire();
 382 
 383         name = kstrdup(dname);
 384         if (name==NULL) {
 385                 goto nomem;
 386         }
 387         if (mountable) {
 388                 rawname = mkrawname(name);
 389                 if (rawname==NULL) {
 390                         goto nomem;
 391                 }
 392         }
 393 
 394         vnode = dev_create_vnode(dev);
 395         if (vnode==NULL) {
 396                 goto nomem;
 397         }
 398 
 399         kd = kmalloc(sizeof(struct knowndev));
 400         if (kd==NULL) {
 401                 goto nomem;
 402         }
 403 
 404         kd->kd_name = name;
 405         kd->kd_rawname = rawname;
 406         kd->kd_device = dev;
 407         kd->kd_vnode = vnode;
 408         kd->kd_fs = fs;
 409 
 410         if (fs!=NULL) {
 411                 volname = FSOP_GETVOLNAME(fs);
 412         }
 413 
 414         if (badnames(name, rawname, volname)) {
 415                 vfs_biglock_release();
 416                 return EEXIST;
 417         }
 418 
 419         result = knowndevarray_add(knowndevs, kd, &index);
 420 
 421         if (result == 0 && dev != NULL) {
 422                 /* use index+1 as the device number, so 0 is reserved */
 423                 dev->d_devnumber = index+1;
 424         }
 425 
 426         vfs_biglock_release();
 427         return result;
 428 
 429  nomem:
 430 
 431         if (name) {
 432                 kfree(name);
 433         }
 434         if (rawname) {
 435                 kfree(rawname);
 436         }
 437         if (vnode) {
 438                 kfree(vnode);
 439         }
 440         if (kd) {
 441                 kfree(kd);
 442         }
 443         
 444         vfs_biglock_release();
 445         return ENOMEM;
 446 }
 447 
 448 /*
 449  * Add a new device, by name. See above for the description of
 450  * mountable.
 451  */
 452 int
 453 vfs_adddev(const char *devname, struct device *dev, int mountable)
 454 {
 455         return vfs_doadd(devname, mountable, dev, NULL);
 456 }
 457 
 458 /*
 459  * Add a filesystem that does not have an underlying device.
 460  * This is used for emufs, but might also be used for network
 461  * filesystems and the like.
 462  */
 463 int
 464 vfs_addfs(const char *devname, struct fs *fs)
 465 {
 466         return vfs_doadd(devname, 0, NULL, fs);
 467 }
 468 
 469 //////////////////////////////////////////////////
 470 
 471 /*
 472  * Look for a mountable device named DEVNAME.
 473  * Should already hold knowndevs_lock.
 474  */
 475 static
 476 int
 477 findmount(const char *devname, struct knowndev **result)
 478 {
 479         struct knowndev *dev;
 480         unsigned i, num;
 481         bool found = false;
 482 
 483         KASSERT(vfs_biglock_do_i_hold());
 484 
 485         num = knowndevarray_num(knowndevs);
 486         for (i=0; !found && i<num; i++) {
 487                 dev = knowndevarray_get(knowndevs, i);
 488                 if (dev->kd_rawname==NULL) {
 489                         /* not mountable/unmountable */
 490                         continue;
 491                 }
 492 
 493                 if (!strcmp(devname, dev->kd_name)) {
 494                         *result = dev;
 495                         found = true;
 496                 }
 497         }
 498 
 499         return found ? 0 : ENODEV;
 500 }
 501 
 502 /*
 503  * Mount a filesystem. Once we've found the device, call MOUNTFUNC to
 504  * set up the filesystem and hand back a struct fs.
 505  *
 506  * The DATA argument is passed through unchanged to MOUNTFUNC.
 507  */
 508 int
 509 vfs_mount(const char *devname, void *data,
 510           int (*mountfunc)(void *data, struct device *, struct fs **ret))
 511 {
 512         const char *volname;
 513         struct knowndev *kd;
 514         struct fs *fs;
 515         int result;
 516 
 517         vfs_biglock_acquire();
 518 
 519         result = findmount(devname, &kd);
 520         if (result) {
 521                 vfs_biglock_release();
 522                 return result;
 523         }
 524 
 525         if (kd->kd_fs != NULL) {
 526                 vfs_biglock_release();
 527                 return EBUSY;
 528         }
 529         KASSERT(kd->kd_rawname != NULL);
 530         KASSERT(kd->kd_device != NULL);
 531 
 532         result = mountfunc(data, kd->kd_device, &fs);
 533         if (result) {
 534                 vfs_biglock_release();
 535                 return result;
 536         }
 537 
 538         KASSERT(fs != NULL);
 539 
 540         kd->kd_fs = fs;
 541 
 542         volname = FSOP_GETVOLNAME(fs);
 543         kprintf("vfs: Mounted %s: on %s\n",
 544                 volname ? volname : kd->kd_name, kd->kd_name);
 545 
 546         vfs_biglock_release();
 547         return 0;
 548 }
 549 
 550 /*
 551  * Unmount a filesystem/device by name.
 552  * First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT.
 553  */
 554 int
 555 vfs_unmount(const char *devname)
 556 {
 557         struct knowndev *kd;
 558         int result;
 559 
 560         vfs_biglock_acquire();
 561 
 562         result = findmount(devname, &kd);
 563         if (result) {
 564                 goto fail;
 565         }
 566 
 567         if (kd->kd_fs == NULL) {
 568                 result = EINVAL;
 569                 goto fail;
 570         }
 571         KASSERT(kd->kd_rawname != NULL);
 572         KASSERT(kd->kd_device != NULL);
 573 
 574         result = FSOP_SYNC(kd->kd_fs);
 575         if (result) {
 576                 goto fail;
 577         }
 578 
 579         result = FSOP_UNMOUNT(kd->kd_fs);
 580         if (result) {
 581                 goto fail;
 582         }
 583 
 584         kprintf("vfs: Unmounted %s:\n", kd->kd_name);
 585 
 586         /* now drop the filesystem */
 587         kd->kd_fs = NULL;
 588 
 589         KASSERT(result==0);
 590 
 591  fail:
 592         vfs_biglock_release();
 593         return result;
 594 }
 595 
 596 /*
 597  * Global unmount function.
 598  */
 599 int
 600 vfs_unmountall(void)
 601 {
 602         struct knowndev *dev;
 603         unsigned i, num;
 604         int result;
 605 
 606         vfs_biglock_acquire();
 607 
 608         num = knowndevarray_num(knowndevs);
 609         for (i=0; i<num; i++) {
 610                 dev = knowndevarray_get(knowndevs, i);
 611                 if (dev->kd_rawname == NULL) {
 612                         /* not mountable/unmountable */
 613                         continue;
 614                 }
 615                 if (dev->kd_fs == NULL) {
 616                         /* not mounted */
 617                         continue;
 618                 }
 619 
 620                 kprintf("vfs: Unmounting %s:\n", dev->kd_name);
 621 
 622                 result = FSOP_SYNC(dev->kd_fs);
 623                 if (result) {
 624                         kprintf("vfs: Warning: sync failed for %s: %s, trying "
 625                                 "again\n", dev->kd_name, strerror(result));
 626 
 627                         result = FSOP_SYNC(dev->kd_fs);
 628                         if (result) {
 629                                 kprintf("vfs: Warning: sync failed second time"
 630                                         " for %s: %s, giving up...\n",
 631                                         dev->kd_name, strerror(result));
 632                                 continue;
 633                         }
 634                 }
 635 
 636                 result = FSOP_UNMOUNT(dev->kd_fs);
 637                 if (result == EBUSY) {
 638                         kprintf("vfs: Cannot unmount %s: (busy)\n", 
 639                                 dev->kd_name);
 640                         continue;
 641                 }
 642                 if (result) {
 643                         kprintf("vfs: Warning: unmount failed for %s:"
 644                                 " %s, already synced, dropping...\n",
 645                                 dev->kd_name, strerror(result));
 646                         continue;
 647                 }
 648 
 649                 /* now drop the filesystem */
 650                 dev->kd_fs = NULL;
 651         }
 652 
 653         vfs_biglock_release();
 654 
 655         return 0;
 656 }

/* [<][>][^][v][top][bottom][index][help] */