diff options
Diffstat (limited to 'fs/filesystems.c')
-rw-r--r-- | fs/filesystems.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/fs/filesystems.c b/fs/filesystems.c new file mode 100644 index 0000000..44082bf --- /dev/null +++ b/fs/filesystems.c @@ -0,0 +1,236 @@ +/* + * linux/fs/filesystems.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * table of configured filesystems + */ + +#include <linux/syscalls.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/kmod.h> +#include <linux/init.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +/* + * 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_module_get(); 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 DEFINE_RWLOCK(file_systems_lock); + +/* WARNING: This can be used only if we _already_ own a reference */ +void get_filesystem(struct file_system_type *fs) +{ + __module_get(fs->owner); +} + +void put_filesystem(struct file_system_type *fs) +{ + module_put(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; +} + +EXPORT_SYMBOL(register_filesystem); + +/** + * 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; +} + +EXPORT_SYMBOL(unregister_filesystem); + +static int fs_index(const char __user * __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 __user * 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_module_get(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 __user *) arg1); + break; + + case 2: + retval = fs_name(arg1, (char __user *) 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, "%s\t%s\n", + (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_module_get(fs->owner)) + fs = NULL; + read_unlock(&file_systems_lock); + if (!fs && (request_module("%s", name) == 0)) { + read_lock(&file_systems_lock); + fs = *(find_filesystem(name)); + if (fs && !try_module_get(fs->owner)) + fs = NULL; + read_unlock(&file_systems_lock); + } + return fs; +} + +EXPORT_SYMBOL(get_fs_type); |