文件系统的装载
【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实例中的子文件系统链表中。
转载于:https://blog.51cto.com/csdyabc/856184