super.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:21k
- /*
- * linux/fs/super.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * super.c contains code to handle: - mount structures
- * - super-block tables
- * - filesystem drivers list
- * - mount system call
- * - umount system call
- * - ustat system call
- *
- * GK 2/5/95 - Changed to support mounting the root fs via NFS
- *
- * Added kerneld support: Jacques Gelinas and Bjorn Ekwall
- * Added change_root: Werner Almesberger & Hans Lermen, Feb '96
- * Added options to /proc/mounts:
- * Torbj鰎n Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
- * Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998
- * Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000
- */
- #include <linux/config.h>
- #include <linux/slab.h>
- #include <linux/locks.h>
- #include <linux/smp_lock.h>
- #include <linux/devfs_fs_kernel.h>
- #include <linux/major.h>
- #include <linux/acct.h>
- #include <asm/uaccess.h>
- #include <linux/kmod.h>
- #define __NO_VERSION__
- #include <linux/module.h>
- LIST_HEAD(super_blocks);
- spinlock_t sb_lock = SPIN_LOCK_UNLOCKED;
- /*
- * Handling of filesystem drivers list.
- * Rules:
- * Inclusion to/removals from/scanning of list are protected by spinlock.
- * During the unload module must call unregister_filesystem().
- * We can access the fields of list element if:
- * 1) spinlock is held or
- * 2) we hold the reference to the module.
- * The latter can be guaranteed by call of try_inc_mod_count(); if it
- * returned 0 we must skip the element, otherwise we got the reference.
- * Once the reference is obtained we can drop the spinlock.
- */
- static struct file_system_type *file_systems;
- static rwlock_t file_systems_lock = RW_LOCK_UNLOCKED;
- /* WARNING: This can be used only if we _already_ own a reference */
- static void get_filesystem(struct file_system_type *fs)
- {
- if (fs->owner)
- __MOD_INC_USE_COUNT(fs->owner);
- }
- static void put_filesystem(struct file_system_type *fs)
- {
- if (fs->owner)
- __MOD_DEC_USE_COUNT(fs->owner);
- }
- static struct file_system_type **find_filesystem(const char *name)
- {
- struct file_system_type **p;
- for (p=&file_systems; *p; p=&(*p)->next)
- if (strcmp((*p)->name,name) == 0)
- break;
- return p;
- }
- /**
- * register_filesystem - register a new filesystem
- * @fs: the file system structure
- *
- * Adds the file system passed to the list of file systems the kernel
- * is aware of for mount and other syscalls. Returns 0 on success,
- * or a negative errno code on an error.
- *
- * The &struct file_system_type that is passed is linked into the kernel
- * structures and must not be freed until the file system has been
- * unregistered.
- */
-
- int register_filesystem(struct file_system_type * fs)
- {
- int res = 0;
- struct file_system_type ** p;
- if (!fs)
- return -EINVAL;
- if (fs->next)
- return -EBUSY;
- INIT_LIST_HEAD(&fs->fs_supers);
- write_lock(&file_systems_lock);
- p = find_filesystem(fs->name);
- if (*p)
- res = -EBUSY;
- else
- *p = fs;
- write_unlock(&file_systems_lock);
- return res;
- }
- /**
- * unregister_filesystem - unregister a file system
- * @fs: filesystem to unregister
- *
- * Remove a file system that was previously successfully registered
- * with the kernel. An error is returned if the file system is not found.
- * Zero is returned on a success.
- *
- * Once this function has returned the &struct file_system_type structure
- * may be freed or reused.
- */
-
- int unregister_filesystem(struct file_system_type * fs)
- {
- struct file_system_type ** tmp;
- write_lock(&file_systems_lock);
- tmp = &file_systems;
- while (*tmp) {
- if (fs == *tmp) {
- *tmp = fs->next;
- fs->next = NULL;
- write_unlock(&file_systems_lock);
- return 0;
- }
- tmp = &(*tmp)->next;
- }
- write_unlock(&file_systems_lock);
- return -EINVAL;
- }
- static int fs_index(const char * __name)
- {
- struct file_system_type * tmp;
- char * name;
- int err, index;
- name = getname(__name);
- err = PTR_ERR(name);
- if (IS_ERR(name))
- return err;
- err = -EINVAL;
- read_lock(&file_systems_lock);
- for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {
- if (strcmp(tmp->name,name) == 0) {
- err = index;
- break;
- }
- }
- read_unlock(&file_systems_lock);
- putname(name);
- return err;
- }
- static int fs_name(unsigned int index, char * buf)
- {
- struct file_system_type * tmp;
- int len, res;
- read_lock(&file_systems_lock);
- for (tmp = file_systems; tmp; tmp = tmp->next, index--)
- if (index <= 0 && try_inc_mod_count(tmp->owner))
- break;
- read_unlock(&file_systems_lock);
- if (!tmp)
- return -EINVAL;
- /* OK, we got the reference, so we can safely block */
- len = strlen(tmp->name) + 1;
- res = copy_to_user(buf, tmp->name, len) ? -EFAULT : 0;
- put_filesystem(tmp);
- return res;
- }
- static int fs_maxindex(void)
- {
- struct file_system_type * tmp;
- int index;
- read_lock(&file_systems_lock);
- for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next, index++)
- ;
- read_unlock(&file_systems_lock);
- return index;
- }
- /*
- * Whee.. Weird sysv syscall.
- */
- asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2)
- {
- int retval = -EINVAL;
- switch (option) {
- case 1:
- retval = fs_index((const char *) arg1);
- break;
- case 2:
- retval = fs_name(arg1, (char *) arg2);
- break;
- case 3:
- retval = fs_maxindex();
- break;
- }
- return retval;
- }
- int get_filesystem_list(char * buf)
- {
- int len = 0;
- struct file_system_type * tmp;
- read_lock(&file_systems_lock);
- tmp = file_systems;
- while (tmp && len < PAGE_SIZE - 80) {
- len += sprintf(buf+len, "%st%sn",
- (tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
- tmp->name);
- tmp = tmp->next;
- }
- read_unlock(&file_systems_lock);
- return len;
- }
- struct file_system_type *get_fs_type(const char *name)
- {
- struct file_system_type *fs;
-
- read_lock(&file_systems_lock);
- fs = *(find_filesystem(name));
- if (fs && !try_inc_mod_count(fs->owner))
- fs = NULL;
- read_unlock(&file_systems_lock);
- if (!fs && (request_module(name) == 0)) {
- read_lock(&file_systems_lock);
- fs = *(find_filesystem(name));
- if (fs && !try_inc_mod_count(fs->owner))
- fs = NULL;
- read_unlock(&file_systems_lock);
- }
- return fs;
- }
- /**
- * alloc_super - create new superblock
- *
- * Allocates and initializes a new &struct super_block. alloc_super()
- * returns a pointer new superblock or %NULL if allocation had failed.
- */
- static struct super_block *alloc_super(void)
- {
- struct super_block *s = kmalloc(sizeof(struct super_block), GFP_USER);
- if (s) {
- memset(s, 0, sizeof(struct super_block));
- INIT_LIST_HEAD(&s->s_dirty);
- INIT_LIST_HEAD(&s->s_locked_inodes);
- INIT_LIST_HEAD(&s->s_files);
- INIT_LIST_HEAD(&s->s_instances);
- init_rwsem(&s->s_umount);
- sema_init(&s->s_lock, 1);
- down_write(&s->s_umount);
- s->s_count = S_BIAS;
- atomic_set(&s->s_active, 1);
- sema_init(&s->s_vfs_rename_sem,1);
- sema_init(&s->s_nfsd_free_path_sem,1);
- sema_init(&s->s_dquot.dqio_sem, 1);
- sema_init(&s->s_dquot.dqoff_sem, 1);
- s->s_maxbytes = MAX_NON_LFS;
- }
- return s;
- }
- /**
- * destroy_super - frees a superblock
- * @s: superblock to free
- *
- * Frees a superblock.
- */
- static inline void destroy_super(struct super_block *s)
- {
- kfree(s);
- }
- /* Superblock refcounting */
- /**
- * deactivate_super - turn an active reference into temporary
- * @s: superblock to deactivate
- *
- * Turns an active reference into temporary one. Returns 0 if there are
- * other active references, 1 if we had deactivated the last one.
- */
- static inline int deactivate_super(struct super_block *s)
- {
- if (!atomic_dec_and_lock(&s->s_active, &sb_lock))
- return 0;
- s->s_count -= S_BIAS-1;
- spin_unlock(&sb_lock);
- return 1;
- }
- /**
- * put_super - drop a temporary reference to superblock
- * @s: superblock in question
- *
- * Drops a temporary reference, frees superblock if there's no
- * references left.
- */
- static inline void put_super(struct super_block *s)
- {
- spin_lock(&sb_lock);
- if (!--s->s_count)
- destroy_super(s);
- spin_unlock(&sb_lock);
- }
- /**
- * grab_super - acquire an active reference
- * @s - reference we are trying to make active
- *
- * Tries to acquire an active reference. grab_super() is used when we
- * had just found a superblock in super_blocks or fs_type->fs_supers
- * and want to turn it into a full-blown active reference. grab_super()
- * is called with sb_lock held and drops it. Returns 1 in case of
- * success, 0 if we had failed (superblock contents was already dead or
- * dying when grab_super() had been called).
- */
- static int grab_super(struct super_block *s)
- {
- s->s_count++;
- spin_unlock(&sb_lock);
- down_write(&s->s_umount);
- if (s->s_root) {
- spin_lock(&sb_lock);
- if (s->s_count > S_BIAS) {
- atomic_inc(&s->s_active);
- s->s_count--;
- spin_unlock(&sb_lock);
- return 1;
- }
- spin_unlock(&sb_lock);
- }
- up_write(&s->s_umount);
- put_super(s);
- return 0;
- }
-
- /**
- * insert_super - put superblock on the lists
- * @s: superblock in question
- * @type: filesystem type it will belong to
- *
- * Associates superblock with fs type and puts it on per-type and global
- * superblocks' lists. Should be called with sb_lock held; drops it.
- */
- static void insert_super(struct super_block *s, struct file_system_type *type)
- {
- s->s_type = type;
- list_add(&s->s_list, super_blocks.prev);
- list_add(&s->s_instances, &type->fs_supers);
- spin_unlock(&sb_lock);
- get_filesystem(type);
- }
- static void put_anon_dev(kdev_t dev);
- /**
- * remove_super - makes superblock unreachable
- * @s: superblock in question
- *
- * Removes superblock from the lists, unlocks it, drop the reference
- * and releases the hosting device. @s should have no active
- * references by that time and after remove_super() it's essentially
- * in rundown mode - all remaining references are temporary, no new
- * reference of any sort are going to appear and all holders of
- * temporary ones will eventually drop them. At that point superblock
- * itself will be destroyed; all its contents is already gone.
- */
- static void remove_super(struct super_block *s)
- {
- kdev_t dev = s->s_dev;
- struct block_device *bdev = s->s_bdev;
- struct file_system_type *fs = s->s_type;
- spin_lock(&sb_lock);
- list_del(&s->s_list);
- list_del(&s->s_instances);
- spin_unlock(&sb_lock);
- up_write(&s->s_umount);
- put_super(s);
- put_filesystem(fs);
- if (bdev)
- blkdev_put(bdev, BDEV_FS);
- else
- put_anon_dev(dev);
- }
- struct vfsmount *alloc_vfsmnt(char *name);
- void free_vfsmnt(struct vfsmount *mnt);
- static inline struct super_block * find_super(kdev_t dev)
- {
- struct list_head *p;
- list_for_each(p, &super_blocks) {
- struct super_block * s = sb_entry(p);
- if (s->s_dev == dev) {
- s->s_count++;
- return s;
- }
- }
- return NULL;
- }
- void drop_super(struct super_block *sb)
- {
- up_read(&sb->s_umount);
- put_super(sb);
- }
- static inline void write_super(struct super_block *sb)
- {
- lock_super(sb);
- if (sb->s_root && sb->s_dirt)
- if (sb->s_op && sb->s_op->write_super)
- sb->s_op->write_super(sb);
- unlock_super(sb);
- }
- /*
- * Note: check the dirty flag before waiting, so we don't
- * hold up the sync while mounting a device. (The newly
- * mounted device won't need syncing.)
- */
- void sync_supers(kdev_t dev)
- {
- struct super_block * sb;
- if (dev) {
- sb = get_super(dev);
- if (sb) {
- if (sb->s_dirt)
- write_super(sb);
- drop_super(sb);
- }
- return;
- }
- restart:
- spin_lock(&sb_lock);
- sb = sb_entry(super_blocks.next);
- while (sb != sb_entry(&super_blocks))
- if (sb->s_dirt) {
- sb->s_count++;
- spin_unlock(&sb_lock);
- down_read(&sb->s_umount);
- write_super(sb);
- drop_super(sb);
- goto restart;
- } else
- sb = sb_entry(sb->s_list.next);
- spin_unlock(&sb_lock);
- }
- /**
- * get_super - get the superblock of a device
- * @dev: device to get the superblock for
- *
- * Scans the superblock list and finds the superblock of the file system
- * mounted on the device given. %NULL is returned if no match is found.
- */
-
- struct super_block * get_super(kdev_t dev)
- {
- struct super_block * s;
- if (!dev)
- return NULL;
- restart:
- spin_lock(&sb_lock);
- s = find_super(dev);
- if (s) {
- spin_unlock(&sb_lock);
- down_read(&s->s_umount);
- if (s->s_root)
- return s;
- drop_super(s);
- goto restart;
- }
- spin_unlock(&sb_lock);
- return NULL;
- }
- asmlinkage long sys_ustat(dev_t dev, struct ustat * ubuf)
- {
- struct super_block *s;
- struct ustat tmp;
- struct statfs sbuf;
- int err = -EINVAL;
- s = get_super(to_kdev_t(dev));
- if (s == NULL)
- goto out;
- err = vfs_statfs(s, &sbuf);
- drop_super(s);
- if (err)
- goto out;
- memset(&tmp,0,sizeof(struct ustat));
- tmp.f_tfree = sbuf.f_bfree;
- tmp.f_tinode = sbuf.f_ffree;
- err = copy_to_user(ubuf,&tmp,sizeof(struct ustat)) ? -EFAULT : 0;
- out:
- return err;
- }
- /**
- * do_remount_sb - asks filesystem to change mount options.
- * @sb: superblock in question
- * @flags: numeric part of options
- * @data: the rest of options
- *
- * Alters the mount options of a mounted file system.
- */
- int do_remount_sb(struct super_block *sb, int flags, void *data)
- {
- int retval;
-
- if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
- return -EACCES;
- /*flags |= MS_RDONLY;*/
- if (flags & MS_RDONLY)
- acct_auto_close(sb->s_dev);
- shrink_dcache_sb(sb);
- fsync_super(sb);
- /* If we are remounting RDONLY, make sure there are no rw files open */
- if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY))
- if (!fs_may_remount_ro(sb))
- return -EBUSY;
- if (sb->s_op && sb->s_op->remount_fs) {
- lock_super(sb);
- retval = sb->s_op->remount_fs(sb, &flags, data);
- unlock_super(sb);
- if (retval)
- return retval;
- }
- sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
- return 0;
- }
- /*
- * Unnamed block devices are dummy devices used by virtual
- * filesystems which don't use real block-devices. -- jrs
- */
- enum {Max_anon = 256};
- static unsigned long unnamed_dev_in_use[Max_anon/(8*sizeof(unsigned long))];
- static spinlock_t unnamed_dev_lock = SPIN_LOCK_UNLOCKED;/* protects the above */
- /**
- * put_anon_dev - release anonymous device number.
- * @dev: device in question
- */
- static void put_anon_dev(kdev_t dev)
- {
- spin_lock(&unnamed_dev_lock);
- clear_bit(MINOR(dev), unnamed_dev_in_use);
- spin_unlock(&unnamed_dev_lock);
- }
- /**
- * get_anon_super - allocate a superblock for non-device fs
- * @type: filesystem type
- * @compare: check if existing superblock is what we want
- * @data: argument for @compare.
- *
- * get_anon_super is a helper for non-blockdevice filesystems.
- * It either finds and returns one of the superblocks of given type
- * (if it can find one that would satisfy caller) or creates a new
- * one. In the either case we return an active reference to superblock
- * with ->s_umount locked. If superblock is new it gets a new
- * anonymous device allocated for it and is inserted into lists -
- * other initialization is left to caller.
- *
- * Rather than duplicating all that logics every time when
- * we want something that doesn't fit "nodev" and "single" we pull
- * the relevant code into common helper and let get_sb_...() call
- * it.
- *
- * NB: get_sb_...() is going to become an fs type method, with
- * current ->read_super() becoming a callback used by common instances.
- */
- struct super_block *get_anon_super(struct file_system_type *type,
- int (*compare)(struct super_block *,void *), void *data)
- {
- struct super_block *s = alloc_super();
- kdev_t dev;
- struct list_head *p;
- if (!s)
- return ERR_PTR(-ENOMEM);
- retry:
- spin_lock(&sb_lock);
- if (compare) list_for_each(p, &type->fs_supers) {
- struct super_block *old;
- old = list_entry(p, struct super_block, s_instances);
- if (!compare(old, data))
- continue;
- if (!grab_super(old))
- goto retry;
- destroy_super(s);
- return old;
- }
- spin_lock(&unnamed_dev_lock);
- dev = find_first_zero_bit(unnamed_dev_in_use, Max_anon);
- if (dev == Max_anon) {
- spin_unlock(&unnamed_dev_lock);
- spin_unlock(&sb_lock);
- destroy_super(s);
- return ERR_PTR(-EMFILE);
- }
- set_bit(dev, unnamed_dev_in_use);
- spin_unlock(&unnamed_dev_lock);
- s->s_dev = dev;
- insert_super(s, type);
- return s;
- }
- static struct super_block *get_sb_bdev(struct file_system_type *fs_type,
- int flags, char *dev_name, void * data)
- {
- struct inode *inode;
- struct block_device *bdev;
- struct block_device_operations *bdops;
- devfs_handle_t de;
- struct super_block * s;
- struct nameidata nd;
- struct list_head *p;
- kdev_t dev;
- int error = 0;
- mode_t mode = FMODE_READ; /* we always need it ;-) */
- /* What device it is? */
- if (!dev_name || !*dev_name)
- return ERR_PTR(-EINVAL);
- error = path_lookup(dev_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd);
- if (error)
- return ERR_PTR(error);
- inode = nd.dentry->d_inode;
- error = -ENOTBLK;
- if (!S_ISBLK(inode->i_mode))
- goto out;
- error = -EACCES;
- if (nd.mnt->mnt_flags & MNT_NODEV)
- goto out;
- bd_acquire(inode);
- bdev = inode->i_bdev;
- de = devfs_get_handle_from_inode (inode);
- bdops = devfs_get_ops (de); /* Increments module use count */
- if (bdops) bdev->bd_op = bdops;
- /* Done with lookups, semaphore down */
- dev = to_kdev_t(bdev->bd_dev);
- if (!(flags & MS_RDONLY))
- mode |= FMODE_WRITE;
- error = blkdev_get(bdev, mode, 0, BDEV_FS);
- devfs_put_ops (de); /* Decrement module use count now we're safe */
- if (error)
- goto out;
- check_disk_change(dev);
- error = -EACCES;
- if (!(flags & MS_RDONLY) && is_read_only(dev))
- goto out1;
- error = -ENOMEM;
- s = alloc_super();
- if (!s)
- goto out1;
- error = -EBUSY;
- restart:
- spin_lock(&sb_lock);
- list_for_each(p, &super_blocks) {
- struct super_block *old = sb_entry(p);
- if (old->s_dev != dev)
- continue;
- if (old->s_type != fs_type ||
- ((flags ^ old->s_flags) & MS_RDONLY)) {
- spin_unlock(&sb_lock);
- destroy_super(s);
- goto out1;
- }
- if (!grab_super(old))
- goto restart;
- destroy_super(s);
- blkdev_put(bdev, BDEV_FS);
- path_release(&nd);
- return old;
- }
- s->s_dev = dev;
- s->s_bdev = bdev;
- s->s_flags = flags;
- insert_super(s, fs_type);
- if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
- goto Einval;
- s->s_flags |= MS_ACTIVE;
- path_release(&nd);
- return s;
- Einval:
- deactivate_super(s);
- remove_super(s);
- error = -EINVAL;
- goto out;
- out1:
- blkdev_put(bdev, BDEV_FS);
- out:
- path_release(&nd);
- return ERR_PTR(error);
- }
- static struct super_block *get_sb_nodev(struct file_system_type *fs_type,
- int flags, char *dev_name, void *data)
- {
- struct super_block *s = get_anon_super(fs_type, NULL, NULL);
- if (IS_ERR(s))
- return s;
- s->s_flags = flags;
- if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0)) {
- deactivate_super(s);
- remove_super(s);
- return ERR_PTR(-EINVAL);
- }
- s->s_flags |= MS_ACTIVE;
- return s;
- }
- static int compare_single(struct super_block *s, void *p)
- {
- return 1;
- }
- static struct super_block *get_sb_single(struct file_system_type *fs_type,
- int flags, char *dev_name, void *data)
- {
- struct super_block *s = get_anon_super(fs_type, compare_single, NULL);
- if (IS_ERR(s))
- return s;
- if (!s->s_root) {
- s->s_flags = flags;
- if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0)) {
- deactivate_super(s);
- remove_super(s);
- return ERR_PTR(-EINVAL);
- }
- s->s_flags |= MS_ACTIVE;
- }
- do_remount_sb(s, flags, data);
- return s;
- }
- struct vfsmount *
- do_kern_mount(const char *fstype, int flags, char *name, void *data)
- {
- struct file_system_type *type = get_fs_type(fstype);
- struct super_block *sb = ERR_PTR(-ENOMEM);
- struct vfsmount *mnt;
- if (!type)
- return ERR_PTR(-ENODEV);
- mnt = alloc_vfsmnt(name);
- if (!mnt)
- goto out;
- if (type->fs_flags & FS_REQUIRES_DEV)
- sb = get_sb_bdev(type, flags, name, data);
- else if (type->fs_flags & FS_SINGLE)
- sb = get_sb_single(type, flags, name, data);
- else
- sb = get_sb_nodev(type, flags, name, data);
- if (IS_ERR(sb))
- goto out_mnt;
- if (type->fs_flags & FS_NOMOUNT)
- sb->s_flags |= MS_NOUSER;
- mnt->mnt_sb = sb;
- mnt->mnt_root = dget(sb->s_root);
- mnt->mnt_mountpoint = sb->s_root;
- mnt->mnt_parent = mnt;
- up_write(&sb->s_umount);
- put_filesystem(type);
- return mnt;
- out_mnt:
- free_vfsmnt(mnt);
- out:
- put_filesystem(type);
- return (struct vfsmount *)sb;
- }
- void kill_super(struct super_block *sb)
- {
- struct dentry *root = sb->s_root;
- struct file_system_type *fs = sb->s_type;
- struct super_operations *sop = sb->s_op;
- if (!deactivate_super(sb))
- return;
- down_write(&sb->s_umount);
- sb->s_root = NULL;
- /* Need to clean after the sucker */
- if (fs->fs_flags & FS_LITTER)
- d_genocide(root);
- shrink_dcache_parent(root);
- dput(root);
- fsync_super(sb);
- lock_super(sb);
- lock_kernel();
- sb->s_flags &= ~MS_ACTIVE;
- invalidate_inodes(sb); /* bad name - it should be evict_inodes() */
- if (sop) {
- if (sop->write_super && sb->s_dirt)
- sop->write_super(sb);
- if (sop->put_super)
- sop->put_super(sb);
- }
- /* Forget any remaining inodes */
- if (invalidate_inodes(sb)) {
- printk(KERN_ERR "VFS: Busy inodes after unmount. "
- "Self-destruct in 5 seconds. Have a nice day...n");
- }
- unlock_kernel();
- unlock_super(sb);
- remove_super(sb);
- }
- struct vfsmount *kern_mount(struct file_system_type *type)
- {
- return do_kern_mount(type->name, 0, (char *)type->name, NULL);
- }