/*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; }