文件系统的装载

compat_sys_mount

函数compat_sys_mount是系统调用mount在内核中的实现。Dev_name为待安装设备的路径名;dir_name是安装点的路径名;type是表示文件系统类型的字符串,如“ext2”。Flags为安装模式。

asmlinkage long compat_sys_mount(const char __user * dev_name,

 const char __user * dir_name,

 const char __user * type, unsigned long flags,

 const void __user * data)

{

char *kernel_type;

unsigned long data_page;

char *kernel_dev;

char *dir_page;

int retval;

 

retval = copy_mount_string(type, &kernel_type);

if (retval < 0)

goto out;

 

dir_page = getname(dir_name);

retval = PTR_ERR(dir_page);

if (IS_ERR(dir_page))

goto out1;

 

retval = copy_mount_string(dev_name, &kernel_dev);

if (retval < 0)

goto out2;

 

retval = copy_mount_options(data, &data_page);

if (retval < 0)

goto out3;

 

retval = -EINVAL;

 

if (kernel_type && data_page) {

if (!strcmp(kernel_type, NCPFS_NAME)) {

do_ncp_super_data_conv((void *)data_page);

} else if (!strcmp(kernel_type, NFS4_NAME)) {

if (do_nfs4_super_data_conv((void *) data_page))

goto out4;

}

}

 

retval = do_mount(kernel_dev, dir_page, kernel_type,

flags, (void*)data_page);

.......

}

 

将用户空间数据拷贝到系统空间,然后调用函数do_mount做具体的挂载工作。

 

compat_sys_mount--->do_mount

 

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;

 

if ((flags & MS_MGC_MSK) == MS_MGC_VAL)

flags &= ~MS_MGC_MSK;

 

if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))

return -EINVAL;

 

if (data_page)

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

 

对安装点路径名的检查:路径名不能为空,不能超过一个页面长度。

 

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;

 

找到安装点路径的目录条目存放在path结构中。

 

if (!(flags & MS_NOATIME))

mnt_flags |= MNT_RELATIME;

 

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_BORN | 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;

}

函数do_mount充当一个多路分解器,将各类型的装载委派给对应的函数。

函数do_remount,修改已经装载的文件系统的选项;函数do_loopback用于通过环回接口装载一个文件系统;函数do_change_type负责处理共享、从属和不可绑定装载,它可改变装载标志或在涉及的各个vfsmount实例之间建立所需的数据结构的关联;do_move_mount用来移动一个已经装载的文件系统;函数do_new_mount处理普通装载操作。

 

compat_sys_mount--->do_mount--->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;

int err;

 

if (!type)

return -EINVAL;

 

if (!capable(CAP_SYS_ADMIN))

return -EPERM;

 

mnt = do_kern_mount(type, flags, name, data);

if (IS_ERR(mnt))

return PTR_ERR(mnt);

 

err = do_add_mount(mnt, path, mnt_flags);

if (err)

mntput(mnt);

return err;

}

在函数中do_kern_mount先在注册的文件系统链表上找到正确的文件系统,然后调用特定文件系统的mount函数,初始化相关结构。

函数do_add_mount处理一些必须的锁定操作,确保一个文件系统不会重复装载到同一位置。然后将工作委托给graft_tree

 

compat_sys_mount--->do_mount--->do_new_mount--->do_kern_mount

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;

}

通过函数get_fs_type找到匹配的文件系统file_system_type实例,如果没找到匹配的文件系统自动加载对应的模块。函数vfs_kern_mount读取对应文件系统的超级块,分配并初始化一个vfsmount实例。

 

compat_sys_mount--->do_mount--->do_new_mount--->do_kern_mount--->

vfs_kern_mount

vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)

{

struct vfsmount *mnt;

struct dentry *root;

 

if (!type)

return ERR_PTR(-ENODEV);

 

mnt = alloc_vfsmnt(name);

if (!mnt)

return ERR_PTR(-ENOMEM);

 

if (flags & MS_KERNMOUNT)

mnt->mnt_flags = MNT_INTERNAL;

 

root = mount_fs(type, flags, name, data);

if (IS_ERR(root)) {

free_vfsmnt(mnt);

return ERR_CAST(root);

}

 

mnt->mnt_root = root;

mnt->mnt_sb = root->d_sb;

mnt->mnt_mountpoint = mnt->mnt_root;

mnt->mnt_parent = mnt;

return mnt;

}

分配一个vfsmount实例,在函数mount_fs中调用具体文件系统的mount函数读取文件系统的超级块。

compat_sys_mount--->do_mount--->do_new_mount--->do_kern_mount--->

do_add_mount--->graft_tree--->attach_recursive_mnt

将新装载的文件系统添加到父文件系统的命名空间。

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;

......

mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);

commit_tree(source_mnt);

......

}

函数mnt_set_mountpoint设置文件系统的装载点和父文件系统。函数commit_tree将新的vfsmount实例添加到全局散列表以及父文件系统vfsmount实例中的子文件系统链表中。