解读filesystem.c
http://space.doit.com.cn/?uid-45811-action-viewspace-itemid-2059
===================================================
作者:ietf AT doit.com.cn
filesystem.c源文件来自于linux kernel 2.6.20
引用请注明出处。
===================================================
Linux VFS在挂载点通过mount和umount挂载和卸载相应的文件系统。
mount和umount文件系统工作由register_filesystem和unregister_filesystem完成。为了清晰的理解文件系统的实际加载过程,对filesystem.c做一个简单分析。为了分析方便,分析过程将嵌套在源代码中,需要原始的源文件代码,可以在指示的linux内核源文件中查找。
所有的注释由如下标记标识:
——-ietf add start——-
——-ietf add end ——-
/*
* linux/fs/filesystems.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* table of configured filesystems
*/
——-ietf add start——-
这个文件ms是由Torvalds本人独立完成的
——-ietf add end ——-
#include <linux/syscalls.h>
——-ietf add start——-
需要用到某些系统调用的定义,如sys_sysfs()
——-ietf add end ——-
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h> /* for ‘current’ */
#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.
*/
——-ietf add start——-
文件系统类型结构,在src/include/linux/fs.h中描述,《Linux虚拟文件系统概述》中有详细描述
——-ietf add end ——-
static struct file_system_type *file_systems;
——-ietf add start——-
文件系统锁,该宏在src/include/linux/spinlock_types.h中定义
——-ietf add end ——-
static DEFINE_RWLOCK(file_systems_lock);
——-ietf add start——-
加载文件系统(加载文件系统模块),__module_get()在src/include/linux/Modules.h中声明,在当前版本中为空函数
——-ietf add end ——-
/* WARNING: This can be used only if we _already_ own a reference */
void get_filesystem(struct file_system_type *fs)
{
__module_get(fs->owner);
}
——-ietf add start——-
释放文件系统(减少模块引用计数或者卸载模块),__module_put()在src/include/linux/modules.h中声明,在src/kernel/modules.c中实现,代码如下:
void module_put(struct module *module)
{
if (module) {
unsigned int cpu = get_cpu();
local_dec(&module->ref[cpu].count);
/* Maybe they’re waiting for us to drop reference? */
if (unlikely(!module_is_live(module)))
wake_up_process(module->waiter);
put_cpu();
}
}
——-ietf add end ——-
void put_filesystem(struct file_system_type *fs)
{
module_put(fs->owner);
}
——-ietf add start——-
通过文件系统名,遍历文件系统链表,查找指定文件系统,返回该文件系统结构的指针。
——-ietf add end ——-
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.
*/
——-ietf add start——-
根据文件系统结构,注册文件系统,mount时调用
——-ietf add end ——-
int register_filesystem(struct file_system_type * fs)
{
int res = 0;
struct file_system_type ** p;
if (fs->next)
return -EBUSY;
——-ietf add start——-
fs指向的是已经被加载的文件系统结构,直接返回
——-ietf add end ——-
INIT_LIST_HEAD(&fs->fs_supers);
write_lock(&file_systems_lock);
——-ietf add start——-
操作文件系统链表,先获得文件系统锁
——-ietf add end ——-
p = find_filesystem(fs->name);
——-ietf add start——-
通过文件系统名,遍历内核中的文件系统,找到,则*p是指向文件系统链表中该文件系统的指针,否则为空,即最后一项的next
——-ietf add end ——-
if (*p)
res = -EBUSY;
——-ietf add start——-
文件系统链表中存在该文件系统,直接返回
——-ietf add end ——-
else
*p = fs;
——-ietf add start——-
不存在,则*p指向该新加入的文件系统,即将fs加载到了文件系统链表的最后一项
——-ietf add end ——-
write_unlock(&file_systems_lock);
——-ietf add start——-
释放文件系统锁
——-ietf add end ——-
return res;
}
EXPORT_SYMBOL(register_filesystem);
——-ietf add start——-
向内核输出模块名,该宏的含义和功能请参考《Linux Device Driver》edition 1/2/3都可以
——-ietf add end ——-
/**
* 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.
*/
——-ietf add start——-
根据文件系统结构指针,卸载文件系统,umount时调用
——-ietf add end ——-
int unregister_filesystem(struct file_system_type * fs)
{
struct file_system_type ** tmp;
write_lock(&file_systems_lock);
tmp = &file_systems;
——-ietf add start——-
获得文件系统链表头指针,并遍历该链表
——-ietf add end ——-
while (*tmp) {
if (fs == *tmp) {
*tmp = fs->next;
fs->next = NULL;
write_unlock(&file_systems_lock);
return 0;
——-ietf add start——-
找到欲卸载的文件系统,将该项从链表中摘除,并释放锁,返回成功
——-ietf add end ——-
}
tmp = &(*tmp)->next;
}
write_unlock(&file_systems_lock);
return -EINVAL;
——-ietf add start——-
没有找到该文件系统,返回出错
——-ietf add end ——-
}
EXPORT_SYMBOL(unregister_filesystem);
——-ietf add start——-
计算获得指定文件系统名的文件系统在文件系统链表中的index值
——-ietf add end ——-
static int fs_index(const char __user * __name)
{
struct file_system_type * tmp;
char * name;
int err, index;
name = getname(__name);
——-ietf add start——-
将__name所指示得字符串拷入内核空间,并且如果内核定制了audit属性,增加该项进入查找access访问控制表getname()在src/fs/namei.c中实现,源代码如下:
static int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;
if (!segment_eq(get_fs(), KERNEL_DS)) {
if ((unsigned long) filename >= TASK_SIZE)
return -EFAULT;
if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
len = TASK_SIZE - (unsigned long) filename;
}
retval = strncpy_from_user(page, filename, len);
if (retval > 0) {
if (retval < len)
return 0;
return -ENAMETOOLONG;
} else if (!retval)
retval = -ENOENT;
return retval;
}
char * getname(const char __user * filename)
{
char *tmp, *result;
result = ERR_PTR(-ENOMEM);
tmp = __getname();
if (tmp) {
int retval = do_getname(filename, tmp);
result = tmp;
if (retval < 0) {
__putname(tmp);
result = ERR_PTR(retval);
}
}
audit_getname(result);
return result;
}
——-ietf add end ——-
err = PTR_ERR(name);
if (IS_ERR(name))
return err;
err = -EINVAL;
read_lock(&file_systems_lock);
——-ietf add start——-
遍历查找,并通过index值自增计算结果
——-ietf add end ——-
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);
——-ietf add start——-
putname()操作和getname()相反,其代码如下:
void putname(const char *name)
{
if (unlikely(!audit_dummy_context()))
audit_putname(name);
else
__putname(name);
}
——-ietf add end ——-
return err;
}
——-ietf add start——-
返回文件系统链表中第index个表项的文件系统名,值返回到用户空间
——-ietf add end ——-
static int fs_name(unsigned int index, char __user * buf)
{
struct file_system_type * tmp;
int len, res;
read_lock(&file_systems_lock);
——-ietf add start——-
遍历文件系统,通过index自减到指定项,当然,同时该文件系统的模块必须在内核中
——-ietf add end ——-
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;
}
——-ietf add start——-
计算表中项的数目
——-ietf add end ——-
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.
*/
——-ietf add start——-
一系统调用的方式提供,该系统调用有三种功能,通过option区分
——-ietf add end ——-
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;
}
——-ietf add start——-
遍历文件系统链表,获得当前表中支持的文件系统名
——-ietf add end ——-
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) {
——-ietf add start——-
遍历每一项,并表明是否是基于dev的还是nodev的
——-ietf add end ——-
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;
}
——-ietf add start——-
根据给定的文件系统名,获得该类型文件系统在文件系统链表中的指针。
——-ietf add end ——-
struct file_system_type *get_fs_type(const char *name)
{
struct file_system_type *fs;
read_lock(&file_systems_lock);
——-ietf add start——-
找到该文件系统的指针,如果fs为空,则该文件系统不在链表中,就需要重新加载
——-ietf add end ——-
fs = *(find_filesystem(name));
——-ietf add start——-
找到该文件系统,但是该文件系统的模块没有加载在内核空间,则需要将该空间重新加载
——-ietf add end ——-
if (fs && !try_module_get(fs->owner))
fs = NULL;
read_unlock(&file_systems_lock);
——-ietf add start——-
加载文件系统模块
——-ietf add end ——-
if (!fs && (request_module(”%s”, name) == 0)) {
read_lock(&file_systems_lock);
——-ietf add start——-
判断是否加载成功
——-ietf add end ——-
fs = *(find_filesystem(name));
——-ietf add start——-
加载失败,则fs赋值为空。
这里尝试加载次数可以根据个人喜好多试几次,不过不能写成循环的方式,
——-ietf add end ——-
if (fs && !try_module_get(fs->owner))
fs = NULL;
read_unlock(&file_systems_lock);
}
return fs;
}
EXPORT_SYMBOL(get_fs_type);
)告知,即刻删除。