/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- vfs_bootstrap
- vfs_biglock_acquire
- vfs_biglock_release
- vfs_biglock_do_i_hold
- vfs_sync
- vfs_getroot
- vfs_getdevname
- mkrawname
- samestring
- samestring3
- badnames
- vfs_doadd
- vfs_adddev
- vfs_addfs
- findmount
- vfs_mount
- vfs_unmount
- 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 }