]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - module/spl/spl-vnode.c
Set cwd to '/' for the process executing insmod.
[FreeBSD/FreeBSD.git] / module / spl / spl-vnode.c
1 /*
2  *  This file is part of the SPL: Solaris Porting Layer.
3  *
4  *  Copyright (c) 2008 Lawrence Livermore National Security, LLC.
5  *  Produced at Lawrence Livermore National Laboratory
6  *  Written by:
7  *          Brian Behlendorf <behlendorf1@llnl.gov>,
8  *          Herb Wartens <wartens2@llnl.gov>,
9  *          Jim Garlick <garlick@llnl.gov>
10  *  UCRL-CODE-235197
11  *
12  *  This is free software; you can redistribute it and/or modify it
13  *  under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This is distributed in the hope that it will be useful, but WITHOUT
18  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  *  for more details.
21  *
22  *  You should have received a copy of the GNU General Public License along
23  *  with this program; if not, write to the Free Software Foundation, Inc.,
24  *  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  */
26
27 #include <sys/sysmacros.h>
28 #include <sys/vnode.h>
29
30
31 #ifdef DEBUG_SUBSYSTEM
32 #undef DEBUG_SUBSYSTEM
33 #endif
34
35 #define DEBUG_SUBSYSTEM S_VNODE
36
37 vnode_t *rootdir = (vnode_t *)0xabcd1234;
38 EXPORT_SYMBOL(rootdir);
39
40 static spl_kmem_cache_t *vn_cache;
41 static spl_kmem_cache_t *vn_file_cache;
42
43 static spinlock_t vn_file_lock = SPIN_LOCK_UNLOCKED;
44 static LIST_HEAD(vn_file_list);
45
46 static vtype_t
47 vn_get_sol_type(umode_t mode)
48 {
49         if (S_ISREG(mode))
50                 return VREG;
51
52         if (S_ISDIR(mode))
53                 return VDIR;
54
55         if (S_ISCHR(mode))
56                 return VCHR;
57
58         if (S_ISBLK(mode))
59                 return VBLK;
60
61         if (S_ISFIFO(mode))
62                 return VFIFO;
63
64         if (S_ISLNK(mode))
65                 return VLNK;
66
67         if (S_ISSOCK(mode))
68                 return VSOCK;
69
70         if (S_ISCHR(mode))
71                 return VCHR;
72
73         return VNON;
74 } /* vn_get_sol_type() */
75
76 vnode_t *
77 vn_alloc(int flag)
78 {
79         vnode_t *vp;
80         ENTRY;
81
82         vp = kmem_cache_alloc(vn_cache, flag);
83         if (vp != NULL) {
84                 vp->v_file = NULL;
85                 vp->v_type = 0;
86         }
87
88         RETURN(vp);
89 } /* vn_alloc() */
90 EXPORT_SYMBOL(vn_alloc);
91
92 void
93 vn_free(vnode_t *vp)
94 {
95         ENTRY;
96         kmem_cache_free(vn_cache, vp);
97         EXIT;
98 } /* vn_free() */
99 EXPORT_SYMBOL(vn_free);
100
101 int
102 vn_open(const char *path, uio_seg_t seg, int flags, int mode,
103         vnode_t **vpp, int x1, void *x2)
104 {
105         struct file *fp;
106         struct kstat stat;
107         int rc, saved_umask = 0;
108         vnode_t *vp;
109         ENTRY;
110
111         ASSERT(flags & (FWRITE | FREAD));
112         ASSERT(seg == UIO_SYSSPACE);
113         ASSERT(vpp);
114         *vpp = NULL;
115
116         if (!(flags & FCREAT) && (flags & FWRITE))
117                 flags |= FEXCL;
118
119         /* Note for filp_open() the two low bits must be remapped to mean:
120          * 01 - read-only  -> 00 read-only
121          * 10 - write-only -> 01 write-only
122          * 11 - read-write -> 10 read-write
123          */
124         flags--;
125
126         if (flags & FCREAT)
127                 saved_umask = xchg(&current->fs->umask, 0);
128
129         fp = filp_open(path, flags, mode);
130
131         if (flags & FCREAT)
132                 (void)xchg(&current->fs->umask, saved_umask);
133
134         if (IS_ERR(fp))
135                 RETURN(-PTR_ERR(fp));
136
137         rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
138         if (rc) {
139                 filp_close(fp, 0);
140                 RETURN(-rc);
141         }
142
143         vp = vn_alloc(KM_SLEEP);
144         if (!vp) {
145                 filp_close(fp, 0);
146                 RETURN(ENOMEM);
147         }
148
149         mutex_enter(&vp->v_lock);
150         vp->v_type = vn_get_sol_type(stat.mode);
151         vp->v_file = fp;
152         *vpp = vp;
153         mutex_exit(&vp->v_lock);
154
155         RETURN(0);
156 } /* vn_open() */
157 EXPORT_SYMBOL(vn_open);
158
159 int
160 vn_openat(const char *path, uio_seg_t seg, int flags, int mode,
161           vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd)
162 {
163         char *realpath;
164         int len, rc;
165         ENTRY;
166
167         ASSERT(vp == rootdir);
168
169         len = strlen(path) + 2;
170         realpath = kmalloc(len, GFP_KERNEL);
171         if (!realpath)
172                 RETURN(ENOMEM);
173
174         (void)snprintf(realpath, len, "/%s", path);
175         rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2);
176         kfree(realpath);
177
178         RETURN(rc);
179 } /* vn_openat() */
180 EXPORT_SYMBOL(vn_openat);
181
182 int
183 vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off,
184         uio_seg_t seg, int x1, rlim64_t x2, void *x3, ssize_t *residp)
185 {
186         loff_t offset;
187         mm_segment_t saved_fs;
188         struct file *fp;
189         int rc;
190         ENTRY;
191
192         ASSERT(uio == UIO_WRITE || uio == UIO_READ);
193         ASSERT(vp);
194         ASSERT(vp->v_file);
195         ASSERT(seg == UIO_SYSSPACE);
196         ASSERT(x1 == 0);
197         ASSERT(x2 == RLIM64_INFINITY);
198
199         offset = off;
200         fp = vp->v_file;
201
202         /* Writable user data segment must be briefly increased for this
203          * process so we can use the user space read call paths to write
204          * in to memory allocated by the kernel. */
205         saved_fs = get_fs();
206         set_fs(get_ds());
207
208         if (uio & UIO_WRITE)
209                 rc = vfs_write(fp, addr, len, &offset);
210         else
211                 rc = vfs_read(fp, addr, len, &offset);
212
213         set_fs(saved_fs);
214
215         if (rc < 0)
216                 RETURN(-rc);
217
218         if (residp) {
219                 *residp = len - rc;
220         } else {
221                 if (rc != len)
222                         RETURN(EIO);
223         }
224
225         RETURN(0);
226 } /* vn_rdwr() */
227 EXPORT_SYMBOL(vn_rdwr);
228
229 int
230 vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4)
231 {
232         int rc;
233         ENTRY;
234
235         ASSERT(vp);
236         ASSERT(vp->v_file);
237
238         rc = filp_close(vp->v_file, 0);
239         vn_free(vp);
240
241         RETURN(-rc);
242 } /* vn_close() */
243 EXPORT_SYMBOL(vn_close);
244
245 /* vn_seek() does not actually seek it only performs bounds checking on the
246  * proposed seek.  We perform minimal checking and allow vn_rdwr() to catch
247  * anything more serious. */
248 int
249 vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
250 {
251         return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
252 }
253 EXPORT_SYMBOL(vn_seek);
254
255 static struct dentry *
256 vn_lookup_hash(struct nameidata *nd)
257 {
258         return lookup_one_len(nd->last.name, nd->nd_dentry, nd->last.len);
259 } /* lookup_hash() */
260
261 static void
262 vn_path_release(struct nameidata *nd)
263 {
264         dput(nd->nd_dentry);
265         mntput(nd->nd_mnt);
266 }
267
268 /* Modified do_unlinkat() from linux/fs/namei.c, only uses exported symbols */
269 int
270 vn_remove(const char *path, uio_seg_t seg, int flags)
271 {
272         struct dentry *dentry;
273         struct nameidata nd;
274         struct inode *inode = NULL;
275         int rc = 0;
276         ENTRY;
277
278         ASSERT(seg == UIO_SYSSPACE);
279         ASSERT(flags == RMFILE);
280
281         rc = path_lookup(path, LOOKUP_PARENT, &nd);
282         if (rc)
283                 GOTO(exit, rc);
284
285         rc = -EISDIR;
286         if (nd.last_type != LAST_NORM)
287                 GOTO(exit1, rc);
288
289 #ifdef HAVE_INODE_I_MUTEX
290         mutex_lock_nested(&nd.nd_dentry->d_inode->i_mutex, I_MUTEX_PARENT);
291 #else
292         down(&nd.nd_dentry->d_inode->i_sem);
293 #endif /* HAVE_INODE_I_MUTEX */
294         dentry = vn_lookup_hash(&nd);
295         rc = PTR_ERR(dentry);
296         if (!IS_ERR(dentry)) {
297                 /* Why not before? Because we want correct rc value */
298                 if (nd.last.name[nd.last.len])
299                         GOTO(slashes, rc);
300
301                 inode = dentry->d_inode;
302                 if (inode)
303                         atomic_inc(&inode->i_count);
304 #ifdef HAVE_2ARGS_VFS_UNLINK
305                 rc = vfs_unlink(nd.nd_dentry->d_inode, dentry);
306 #else
307                 rc = vfs_unlink(nd.nd_dentry->d_inode, dentry, nd.nd_mnt);
308 #endif /* HAVE_2ARGS_VFS_UNLINK */
309 exit2:
310                 dput(dentry);
311         }
312 #ifdef HAVE_INODE_I_MUTEX
313         mutex_unlock(&nd.nd_dentry->d_inode->i_mutex);
314 #else
315         up(&nd.nd_dentry->d_inode->i_sem);
316 #endif /* HAVE_INODE_I_MUTEX */
317         if (inode)
318                 iput(inode);    /* truncate the inode here */
319 exit1:
320         vn_path_release(&nd);
321 exit:
322         RETURN(-rc);
323
324 slashes:
325         rc = !dentry->d_inode ? -ENOENT :
326                 S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
327         GOTO(exit2, rc);
328 } /* vn_remove() */
329 EXPORT_SYMBOL(vn_remove);
330
331 /* Modified do_rename() from linux/fs/namei.c, only uses exported symbols */
332 int
333 vn_rename(const char *oldname, const char *newname, int x1)
334 {
335         struct dentry *old_dir, *new_dir;
336         struct dentry *old_dentry, *new_dentry;
337         struct dentry *trap;
338         struct nameidata oldnd, newnd;
339         int rc = 0;
340         ENTRY;
341
342         rc = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
343         if (rc)
344                 GOTO(exit, rc);
345
346         rc = path_lookup(newname, LOOKUP_PARENT, &newnd);
347         if (rc)
348                 GOTO(exit1, rc);
349
350         rc = -EXDEV;
351         if (oldnd.nd_mnt != newnd.nd_mnt)
352                 GOTO(exit2, rc);
353
354         old_dir = oldnd.nd_dentry;
355         rc = -EBUSY;
356         if (oldnd.last_type != LAST_NORM)
357                 GOTO(exit2, rc);
358
359         new_dir = newnd.nd_dentry;
360         if (newnd.last_type != LAST_NORM)
361                 GOTO(exit2, rc);
362
363         trap = lock_rename(new_dir, old_dir);
364
365         old_dentry = vn_lookup_hash(&oldnd);
366
367         rc = PTR_ERR(old_dentry);
368         if (IS_ERR(old_dentry))
369                 GOTO(exit3, rc);
370
371         /* source must exist */
372         rc = -ENOENT;
373         if (!old_dentry->d_inode)
374                 GOTO(exit4, rc);
375
376         /* unless the source is a directory trailing slashes give -ENOTDIR */
377         if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
378                 rc = -ENOTDIR;
379                 if (oldnd.last.name[oldnd.last.len])
380                         GOTO(exit4, rc);
381                 if (newnd.last.name[newnd.last.len])
382                         GOTO(exit4, rc);
383         }
384
385         /* source should not be ancestor of target */
386         rc = -EINVAL;
387         if (old_dentry == trap)
388                 GOTO(exit4, rc);
389
390         new_dentry = vn_lookup_hash(&newnd);
391         rc = PTR_ERR(new_dentry);
392         if (IS_ERR(new_dentry))
393                 GOTO(exit4, rc);
394
395         /* target should not be an ancestor of source */
396         rc = -ENOTEMPTY;
397         if (new_dentry == trap)
398                 GOTO(exit5, rc);
399
400 #ifdef HAVE_4ARGS_VFS_RENAME
401         rc = vfs_rename(old_dir->d_inode, old_dentry,
402                         new_dir->d_inode, new_dentry);
403 #else
404         rc = vfs_rename(old_dir->d_inode, old_dentry, oldnd.nd_mnt,
405                         new_dir->d_inode, new_dentry, newnd.nd_mnt);
406 #endif /* HAVE_4ARGS_VFS_RENAME */
407 exit5:
408         dput(new_dentry);
409 exit4:
410         dput(old_dentry);
411 exit3:
412         unlock_rename(new_dir, old_dir);
413 exit2:
414         vn_path_release(&newnd);
415 exit1:
416         vn_path_release(&oldnd);
417 exit:
418         RETURN(-rc);
419 }
420 EXPORT_SYMBOL(vn_rename);
421
422 int
423 vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
424 {
425         struct file *fp;
426         struct kstat stat;
427         int rc;
428         ENTRY;
429
430         ASSERT(vp);
431         ASSERT(vp->v_file);
432         ASSERT(vap);
433
434         fp = vp->v_file;
435
436         rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
437         if (rc)
438                 RETURN(-rc);
439
440         vap->va_type          = vn_get_sol_type(stat.mode);
441         vap->va_mode          = stat.mode;
442         vap->va_uid           = stat.uid;
443         vap->va_gid           = stat.gid;
444         vap->va_fsid          = 0;
445         vap->va_nodeid        = stat.ino;
446         vap->va_nlink         = stat.nlink;
447         vap->va_size          = stat.size;
448         vap->va_blocksize     = stat.blksize;
449         vap->va_atime.tv_sec  = stat.atime.tv_sec;
450         vap->va_atime.tv_usec = stat.atime.tv_nsec / NSEC_PER_USEC;
451         vap->va_mtime.tv_sec  = stat.mtime.tv_sec;
452         vap->va_mtime.tv_usec = stat.mtime.tv_nsec / NSEC_PER_USEC;
453         vap->va_ctime.tv_sec  = stat.ctime.tv_sec;
454         vap->va_ctime.tv_usec = stat.ctime.tv_nsec / NSEC_PER_USEC;
455         vap->va_rdev          = stat.rdev;
456         vap->va_blocks        = stat.blocks;
457
458         RETURN(0);
459 }
460 EXPORT_SYMBOL(vn_getattr);
461
462 int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
463 {
464         int datasync = 0;
465         ENTRY;
466
467         ASSERT(vp);
468         ASSERT(vp->v_file);
469
470         if (flags & FDSYNC)
471                 datasync = 1;
472
473         RETURN(-file_fsync(vp->v_file, vp->v_file->f_dentry, datasync));
474 } /* vn_fsync() */
475 EXPORT_SYMBOL(vn_fsync);
476
477 /* Function must be called while holding the vn_file_lock */
478 static file_t *
479 file_find(int fd)
480 {
481         file_t *fp;
482
483         ASSERT(spin_is_locked(&vn_file_lock));
484
485         list_for_each_entry(fp, &vn_file_list,  f_list) {
486                 if (fd == fp->f_fd) {
487                         ASSERT(atomic_read(&fp->f_ref) != 0);
488                         return fp;
489                 }
490         }
491
492         return NULL;
493 } /* file_find() */
494
495 file_t *
496 vn_getf(int fd)
497 {
498         struct kstat stat;
499         struct file *lfp;
500         file_t *fp;
501         vnode_t *vp;
502         int rc = 0;
503         ENTRY;
504
505         /* Already open just take an extra reference */
506         spin_lock(&vn_file_lock);
507
508         fp = file_find(fd);
509         if (fp) {
510                 atomic_inc(&fp->f_ref);
511                 spin_unlock(&vn_file_lock);
512                 RETURN(fp);
513         }
514
515         spin_unlock(&vn_file_lock);
516
517         /* File was not yet opened create the object and setup */
518         fp = kmem_cache_alloc(vn_file_cache, KM_SLEEP);
519         if (fp == NULL)
520                 GOTO(out, rc);
521
522         mutex_enter(&fp->f_lock);
523
524         fp->f_fd = fd;
525         fp->f_offset = 0;
526         atomic_inc(&fp->f_ref);
527
528         lfp = fget(fd);
529         if (lfp == NULL)
530                 GOTO(out_mutex, rc);
531
532         vp = vn_alloc(KM_SLEEP);
533         if (vp == NULL)
534                 GOTO(out_fget, rc);
535
536         if (vfs_getattr(lfp->f_vfsmnt, lfp->f_dentry, &stat))
537                 GOTO(out_vnode, rc);
538
539         mutex_enter(&vp->v_lock);
540         vp->v_type = vn_get_sol_type(stat.mode);
541         vp->v_file = lfp;
542         mutex_exit(&vp->v_lock);
543
544         fp->f_vnode = vp;
545         fp->f_file = lfp;
546
547         /* Put it on the tracking list */
548         spin_lock(&vn_file_lock);
549         list_add(&fp->f_list, &vn_file_list);
550         spin_unlock(&vn_file_lock);
551
552         mutex_exit(&fp->f_lock);
553         RETURN(fp);
554
555 out_vnode:
556         vn_free(vp);
557 out_fget:
558         fput(lfp);
559 out_mutex:
560         mutex_exit(&fp->f_lock);
561         kmem_cache_free(vn_file_cache, fp);
562 out:
563         RETURN(NULL);
564 } /* getf() */
565 EXPORT_SYMBOL(getf);
566
567 static void releasef_locked(file_t *fp)
568 {
569         ASSERT(fp->f_file);
570         ASSERT(fp->f_vnode);
571
572         /* Unlinked from list, no refs, safe to free outside mutex */
573         fput(fp->f_file);
574         vn_free(fp->f_vnode);
575
576         kmem_cache_free(vn_file_cache, fp);
577 }
578
579 void
580 vn_releasef(int fd)
581 {
582         file_t *fp;
583         ENTRY;
584
585         spin_lock(&vn_file_lock);
586         fp = file_find(fd);
587         if (fp) {
588                 atomic_dec(&fp->f_ref);
589                 if (atomic_read(&fp->f_ref) > 0) {
590                         spin_unlock(&vn_file_lock);
591                         EXIT;
592                         return;
593                 }
594
595                 list_del(&fp->f_list);
596                 releasef_locked(fp);
597         }
598         spin_unlock(&vn_file_lock);
599
600         EXIT;
601         return;
602 } /* releasef() */
603 EXPORT_SYMBOL(releasef);
604
605 #ifndef HAVE_SET_FS_PWD
606 # ifdef HAVE_2ARGS_SET_FS_PWD
607 /* Used from 2.6.25 - 2.6.31+ */
608 void
609 set_fs_pwd(struct fs_struct *fs, struct path *path)
610 {
611         struct path old_pwd;
612
613         write_lock(&fs->lock);
614         old_pwd = fs->pwd;
615         fs->pwd = *path;
616         path_get(path);
617         write_unlock(&fs->lock);
618
619         if (old_pwd.dentry)
620                 path_put(&old_pwd);
621 }
622 # else
623 /* Used from 2.6.11 - 2.6.24 */
624 void
625 set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, struct dentry *dentry)
626 {
627         struct dentry *old_pwd;
628         struct vfsmount *old_pwdmnt;
629
630         write_lock(&fs->lock);
631         old_pwd = fs->pwd;
632         old_pwdmnt = fs->pwdmnt;
633         fs->pwdmnt = mntget(mnt);
634         fs->pwd = dget(dentry);
635         write_unlock(&fs->lock);
636
637         if (old_pwd) {
638                 dput(old_pwd);
639                 mntput(old_pwdmnt);
640         }
641 }
642 # endif /* HAVE_2ARGS_SET_FS_PWD */
643 #endif /* HAVE_SET_FS_PWD */
644
645 int
646 vn_set_pwd(const char *filename)
647 {
648 #ifdef HAVE_2ARGS_SET_FS_PWD
649         struct path path;
650         int rc;
651         ENTRY;
652
653         rc = user_path_dir(filename, &path);
654         if (rc)
655                 GOTO(out, rc);
656
657         rc = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
658         if (rc)
659                 GOTO(dput_and_out, rc);
660
661         set_fs_pwd(current->fs, &path);
662
663 dput_and_out:
664         path_put(&path);
665 #else
666         struct nameidata nd;
667         int rc;
668         ENTRY;
669
670         rc = __user_walk(filename,
671                          LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
672         if (rc)
673                 GOTO(out, rc);
674
675         rc = vfs_permission(&nd, MAY_EXEC);
676         if (rc)
677                 GOTO(dput_and_out, rc);
678
679         set_fs_pwd(current->fs, nd.nd_mnt, nd.nd_dentry);
680
681 dput_and_out:
682         vn_path_release(&nd);
683 #endif /* HAVE_2ARGS_SET_FS_PWD */
684 out:
685         RETURN(-rc);
686 } /* vn_set_pwd() */
687 EXPORT_SYMBOL(vn_set_pwd);
688
689 static int
690 vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
691 {
692         struct vnode *vp = buf;
693
694         mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
695
696         return (0);
697 } /* vn_cache_constructor() */
698
699 static void
700 vn_cache_destructor(void *buf, void *cdrarg)
701 {
702         struct vnode *vp = buf;
703
704         mutex_destroy(&vp->v_lock);
705 } /* vn_cache_destructor() */
706
707 static int
708 vn_file_cache_constructor(void *buf, void *cdrarg, int kmflags)
709 {
710         file_t *fp = buf;
711
712         atomic_set(&fp->f_ref, 0);
713         mutex_init(&fp->f_lock, NULL, MUTEX_DEFAULT, NULL);
714         INIT_LIST_HEAD(&fp->f_list);
715
716         return (0);
717 } /* file_cache_constructor() */
718
719 static void
720 vn_file_cache_destructor(void *buf, void *cdrarg)
721 {
722         file_t *fp = buf;
723
724         mutex_destroy(&fp->f_lock);
725 } /* vn_file_cache_destructor() */
726
727 int
728 vn_init(void)
729 {
730         ENTRY;
731         vn_cache = kmem_cache_create("spl_vn_cache",
732                                      sizeof(struct vnode), 64,
733                                      vn_cache_constructor,
734                                      vn_cache_destructor,
735                                      NULL, NULL, NULL, 0);
736
737         vn_file_cache = kmem_cache_create("spl_vn_file_cache",
738                                           sizeof(file_t), 64,
739                                           vn_file_cache_constructor,
740                                           vn_file_cache_destructor,
741                                           NULL, NULL, NULL, 0);
742         RETURN(0);
743 } /* vn_init() */
744
745 void
746 vn_fini(void)
747 {
748         file_t *fp, *next_fp;
749         int leaked = 0;
750         ENTRY;
751
752         spin_lock(&vn_file_lock);
753
754         list_for_each_entry_safe(fp, next_fp, &vn_file_list,  f_list) {
755                 list_del(&fp->f_list);
756                 releasef_locked(fp);
757                 leaked++;
758         }
759
760         kmem_cache_destroy(vn_file_cache);
761         vn_file_cache = NULL;
762         spin_unlock(&vn_file_lock);
763
764         if (leaked > 0)
765                 CWARN("Warning %d files leaked\n", leaked);
766
767         kmem_cache_destroy(vn_cache);
768
769         EXIT;
770         return;
771 } /* vn_fini() */