Linux虚拟文件系统之文件系统安装(sys_mount())

来源:岁月联盟 编辑:exp 时间:2012-02-10
对于前面的根目录文件系统的安装中涉及到了mount系统调用的调用,这里我们考虑一个文件系统将被安装在一个已经安装文件系统之上的情形,即调用mount系统调用实现。mount系统调用被用来安装一个普通文件系统,他的服务例程为sys_mount()。
 
/*sys_mount系统调用*/ 
/*dev_name为待安装设备的路径名;
dir_name为安装点的路径名;
type是表示文件系统类型的字符串;
*/ 
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); 
    if (ret < 0) 
        goto out_type; 
 
    kernel_dir = getname(dir_name); 
    if (IS_ERR(kernel_dir)) { 
        ret = PTR_ERR(kernel_dir); 
        goto out_dir; 
    } 
 
    ret = copy_mount_string(dev_name, &kernel_dev); 
    if (ret < 0) 
        goto out_dev; 
    /*用户空间复制到系统空间,拷贝整个页面*/ 
    ret = copy_mount_options(data, &data_page); 
    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; 

下面是主体实现
 
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; 
 
    /* 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); 
 
    /* ... and get the mountpoint */ 
    /*获得安装点path结构*/ 
    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; 
 
    if (flags & MS_REMOUNT) 
        /*修改已经存在的文件系统参数,即改变超级块对象s_flags
    字段的安装标志*/ 
        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)) 
        /*is responsible for handling shared, slave, and unbindable mounts by changing
        the mount flags or building up the required data structure connections 
        between the vfsmount
        instances involved.*/ 
        retval = do_change_type(&path, flags); 
    else if (flags & MS_MOVE) 
        /*改变已安装文件的安装点*/ 
            /*used to move a mounted filesystem*/ 
        retval = do_move_mount(&path, dev_name); 
    else 
        /*handles normal mount operations. This is the default situation, so no special 
        flags
        are required*/ 
        /*当用户要求安装一个特殊文件系统或存放在磁盘分区
        中的普通文件系统时,调用*/ 
        retval = do_new_mount(&path, type_page, flags, mnt_flags, 
                      dev_name, data_page); 
dput_out: 
    path_put(&path); 
    return retval; 

 
/*
 * create a new mount for userspace and request it to be added into the
 * namespace's tree
 */ 
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(); 
    /*处理实际的安装操作并返回一个新的安装文件系统
    描述符地址,使用get_fs_type扫描已经注册文件系统链表
    找到匹配的file_system_type实例。然后分配或获取sb结构
    并与mnt关联,初始化mnt并返回*/ 
    */ 
    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_kern_mount函数在前面初始化中介绍过了,下面看do_add_mount函数用于将文件系统整合到系统中。
 
 
/*
 * add a mount into a namespace's mount tree
 * - provide the option of adding the new mount to an expiration list
 */ 
int do_add_mount(struct vfsmount *newmnt, struct path *path, 
         int mnt_flags, struct list_head *fslist) 

    int err; 
 
    down_write(&namespace_sem); 
    /* Something was mounted here while we slept */ 
    while (d_mountpoint(path->dentry) && 
           follow_down(path)) 
        ; 
    err = -EINVAL; 
    /*验证再改安装点上最近安装的文件系统是否任然指向
    当前命名空间*/ 
    if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt)) 
        goto unlock; 
 
    /* Refuse the same filesystem on the same mount point */ 
    err = -EBUSY; 
    /*如果要安装的文件系统已经被安装在由系统调用的参数
    所指定的安装点上*/ 
    if (path->mnt->mnt_sb == newmnt->mnt_sb && 
        path->mnt->mnt_root == path->dentry) 
        goto unlock; 
 
    err = -EINVAL; 
    /*该安装点是一个符号链接*/ 
    if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode)) 
        goto unlock; 
 
    newmnt->mnt_flags = mnt_flags; 
    /*把新安装的文件系统对象插入到namespace链表、
    散列表以及父文件系统的子链表中*/ 
    if ((err = graft_tree(newmnt, path))) 
        goto unlock; 
 
    if (fslist) /* add to the specified expiration list */ 
        list_add_tail(&newmnt->mnt_expire, fslist); 
 
    up_write(&namespace_sem); 
    return 0; 
 
unlock: 
    up_write(&namespace_sem); 
    mntput(newmnt); 
    return err; 

新安装的文件系统对象链入系统树。
 
 
static int graft_tree(struct vfsmount *mnt, struct path *path) 

    int err; 
    if (mnt->mnt_sb->s_flags & MS_NOUSER) 
        return -EINVAL; 
 
    if (S_ISDIR(path->dentry->d_inode->i_mode) != 
          S_ISDIR(mnt->mnt_root->d_inode->i_mode)) 
        return -ENOTDIR; 
 
    err = -ENOENT; 
    mutex_lock(&path->dentry->d_inode->i_mutex); 
    if (IS_DEADDIR(path->dentry->d_inode)) 
        goto out_unlock; 
 
    err = security_sb_check_sb(mnt, path); 
    if (err) 
        goto out_unlock; 
 
    err = -ENOENT; 
    if (!d_unlinked(path->dentry))/*加入树*/ 
        err = attach_recursive_mnt(mnt, path, NULL); 
out_unlock: 
    mutex_unlock(&path->dentry->d_inode->i_mutex); 
    if (!err) 
        security_sb_post_addmount(mnt, path); 
    return err; 

 
/*将文件系统添加到父文件系统的命名空间中*/ 
static int attach_recursive_mnt(struct vfsmount *source_mnt, 
            struct path *path, struct path *parent_path) 

    LIST_HEAD(tree_list); 
    struct vfsmount *dest_mnt = path->mnt; 
    struct dentry *dest_dentry = path->dentry; 
    struct vfsmount *child, *p; 
    int err; 
 
    if (IS_MNT_SHARED(dest_mnt)) { 
        err = invent_group_ids(source_mnt, true); 
        if (err) 
            goto out; 
    } 
    err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list); 
    if (err) 
        goto out_cleanup_ids; 
 
    if (IS_MNT_SHARED(dest_mnt)) { 
        for (p = source_mnt; p; p = next_mnt(p, source_mnt)) 
            set_mnt_shared(p); 
    } 
 
    spin_lock(&vfsmount_lock); 
    if (parent_path) { 
        detach_mnt(source_mnt, parent_path); 
        attach_mnt(source_mnt, path); 
        touch_mnt_namespace(parent_path->mnt->mnt_ns); 
    } else { 
        /*确保新的vfsmount实例的mnt_parent成员指向父文件系统
        的vfsmount实例,而mnt_mountpoint成员指向装载点在父文件
        系统中的dentry实例*/ 
        mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt); 
        commit_tree(source_mnt); 
    } 
 
    list_for_each_entry_safe(child, p, &tree_list, mnt_hash) { 
        list_del_init(&child->mnt_hash); 
        /*将新的mnt添加到全局散列表以及父文件系统mnt实例中
        的子文件系统链表*/ 
        commit_tree(child); 
    } 
    spin_unlock(&vfsmount_lock); 
    return 0; 
 
 out_cleanup_ids: 
    if (IS_MNT_SHARED(dest_mnt)) 
        cleanup_group_ids(source_mnt, NULL); 
 out: 
    return err; 
}   

bullbat的专栏