Chinaunix首页 | 论坛 | 博客
  • 博客访问: 631495
  • 博文数量: 155
  • 博客积分: 5688
  • 博客等级: 大校
  • 技术积分: 2134
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-15 15:12
文章分类

全部博文(155)

文章存档

2011年(58)

2010年(97)

分类: LINUX

2010-08-05 22:25:13

声明:本文为原创
#####请转贴时保留以下内容######
作者GTT
本文档归属http://oldtown.cublog.cn/.转载请注明出处!
请提出宝贵意见Mail:mtloveft@hotmail.com
Linux Version:2.6.33
提示本文是关于mount命令的实现
 
用过Linux的人基本都用过mount命令,我最初用它,是因为想用光驱,所以得加载cdrom。当时以为
是光驱驱动程序呢,那时傻吧,linux0.11最初用的是minix FileSystem,就提供了mount命令。
文件系统是OS的很重要的一部分,如果不了解FS将对其它的子系统产生一些障碍,可大可小,例如
linux 网路协议栈这块,socket利用sockfs的实现就是文件系统的实现。
mount命令格式如下:
mount [-afFhnrvVw] [-L<标签>] [-o<选项>] [-t<文件系统类型>] [设备名] [加载点]
就不多解释了,可以man mount看看。
 
先看看mount的简单实现Flow
User的命令mount 是在用户空间使用的,所以利用Sys Call来实现对内核的mount的调用。
Sys Call的实现不是本文要讨论的问题,所以绕开它。就直接看内核的实现。

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
        char __user *, type, unsigned long, flags, void __user *, data)


dev_name就是设备文件名称,dir_namemount的安装目录,也可以说是安装节点。type是文件系统的名称。flags是对文件系统笼统的分类,mount处理时可以分类处理了。 data是user自己设置的Option。

SYSCALL mount的代码还是比较简单的,就是从user space 得到dev_name,dir_name,data。

然后就直接调用do_mount()

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
        char __user *, type, unsigned long, flags, void __user *, data)
{
    int ret;
    char *kernel_type;
    char *kernel_dir;
    char *kernel_dev;
    unsigned long data_page;

    ret = copy_mount_string(type, &kernel_type); //从user space 拷贝文件系统类型的名称

    if (ret < 0) goto out_type;

    kernel_dir = getname(dir_name); //从user space 拷贝安装目录的名称

    if (IS_ERR(kernel_dir)) {
        ret = PTR_ERR(kernel_dir);
        goto out_dir;
    }

    ret = copy_mount_string(dev_name, &kernel_dev); //从user space 拷贝设备的名称

    if (ret < 0) goto out_dev;

    ret = copy_mount_options(data, &data_page); //从user space 拷贝设置的参数

    if (ret < 0) goto out_data;

    ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags, (void *) data_page);

    free_page(data_page);
out_data:
    kfree(kernel_dev);
out_dev:
    putname(kernel_dir);
out_dir:
    kfree(kernel_type);
out_type:
    return ret;
}

do_mount只是根据flags进行一些简单的设定。

但是kern_path()是根据给定的目录名进行查找,这个过程很复杂,这里先忽略,以后

会详细介绍。

long do_mount(char *dev_name, char *dir_name, char *type_page,
          unsigned long flags, void *data_page)
{
    struct path path;
    int retval = 0;
    int mnt_flags = 0;

    /* Discard magic */
    if ((flags & MS_MGC_MSK) == MS_MGC_VAL) flags &= ~MS_MGC_MSK;

    /* Basic sanity checks */

    if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
        return -EINVAL;

    if (data_page) ((char *)data_page)[PAGE_SIZE - 1] = 0;

    /* ... and get the mountpoint */
    retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
    if (retval) return retval;

    retval = security_sb_mount(dev_name, &path, type_page, flags, data_page);
    if (retval) goto dput_out;

    /* Default to relatime unless overriden */
    if (!(flags & MS_NOATIME)) mnt_flags |= MNT_RELATIME;

    /* Separate the per-mountpoint flags */
    if (flags & MS_NOSUID) mnt_flags |= MNT_NOSUID;
    if (flags & MS_NODEV) mnt_flags |= MNT_NODEV;
    if (flags & MS_NOEXEC) mnt_flags |= MNT_NOEXEC;
    if (flags & MS_NOATIME) mnt_flags |= MNT_NOATIME;
    if (flags & MS_NODIRATIME) mnt_flags |= MNT_NODIRATIME;
    if (flags & MS_STRICTATIME) mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
    if (flags & MS_RDONLY) mnt_flags |= MNT_READONLY;

    flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
           MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
           MS_STRICTATIME);

    if (flags & MS_REMOUNT)
        retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags, data_page);
    else if (flags & MS_BIND)
        retval = do_loopback(&path, dev_name, flags & MS_REC);
    else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
        retval = do_change_type(&path, flags);
    else if (flags & MS_MOVE)
        retval = do_move_mount(&path, dev_name);
    else
        retval = do_new_mount(&path, type_page, flags, mnt_flags, dev_name, data_page);
dput_out:
    path_put(&path);
    return retval;
}

例如MNT_NODEV的意思就是没有设备文件。还有只读啦等等。可以参考定义文件看看实际意义。

然后就调用do_new_mount()了。

static int do_new_mount(struct path *path, char *type, int flags,
            int mnt_flags, char *name, void *data)
{
    struct vfsmount *mnt;

    if (!type) return -EINVAL;

    /* we need capabilities... */
    if (!capable(CAP_SYS_ADMIN)) return -EPERM;

    lock_kernel();
    mnt = do_kern_mount(type, flags, name, data);
    unlock_kernel();
    if (IS_ERR(mnt)) return PTR_ERR(mnt);

    return do_add_mount(mnt, path, mnt_flags, NULL);
}


do_new_mount 实在是太简单了,等do_kern_mount()mount完了之后,把vfsmount

挂载到vfsmout树上。

继续...

struct vfsmount * do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
    struct file_system_type *type = get_fs_type(fstype);
    struct vfsmount *mnt;
    if (!type) return ERR_PTR(-ENODEV);
    mnt = vfs_kern_mount(type, flags, name, data);
    if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && !mnt->mnt_sb->s_subtype)
        mnt = fs_set_subtype(mnt, fstype);
    put_filesystem(type);
    return mnt;
}

根据文件系统名称fstype得到对应的struct file_system_type。

然后就到vfs_kern_mount()这个过程相对比较复杂,主要分析这部分。

struct vfsmount * vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
    struct vfsmount *mnt;
    char *secdata = NULL;
    int error;

    if (!type) return ERR_PTR(-ENODEV);

    error = -ENOMEM;
    mnt = alloc_vfsmnt(name);
    if (!mnt) goto out;

    if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
        secdata = alloc_secdata();
        if (!secdata) goto out_mnt;

        error = security_sb_copy_data(data, secdata);
        if (error) goto out_free_secdata;
    }

    error = type->get_sb(type, flags, name, data, mnt);
    if (error < 0) goto out_free_secdata;
    BUG_ON(!mnt->mnt_sb);

     error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
     if (error) goto out_sb;

    
/*
     * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
     * but s_maxbytes was an unsigned long long for many releases. Throw
     * this warning for a little while to try and catch filesystems that
     * violate this rule. This warning should be either removed or
     * converted to a BUG() in 2.6.34.
     */

    WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
        "negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);

    mnt->mnt_mountpoint = mnt->mnt_root;
    mnt->mnt_parent = mnt;
    up_write(&mnt->mnt_sb->s_umount);
    free_secdata(secdata);
    return mnt;
out_sb:
    dput(mnt->mnt_root);
    deactivate_locked_super(mnt->mnt_sb);
out_free_secdata:
    free_secdata(secdata);
out_mnt:
    free_vfsmnt(mnt);
out:
    return ERR_PTR(error);
}

这个过程太漫长,需要有一定的耐心才能看下去,代码的可读性稍微有那么点.......

自己体会吧!series(2)继续......



阅读(3093) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~