实际根文件系统的挂载-sys_mount()

来源:互联网 发布:银行业大数据应用 编辑:程序博客网 时间:2024/04/28 02:05

实际根文件系统的挂载是在prepare_namespace()中实现的。
init/do_mounts.c

void __init prepare_namespace(void){...mount_root();out:...sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");...}

首先调用mount_root()将实际的根文件系统挂载在rootfs文件系统的"/root"目录下,并将当前目录切换
到实际根文件系统的根目录下;接着调用sys_mount(".", "/", NULL, MS_MOVE, NULL)将实际文件系统
的挂载点移到(MS_MOVE)rootfs的"/"根目录下。最后将实际文件系统的根目录作为初始进程的根目录。
下面分析下挂载的流程,里面涉及到的MTD、jffs2知识,只简单提下,以后再具体分析。

void __init mount_root(void){.../*创建设备文件*/create_dev("/dev/root", ROOT_DEV);mount_block_root("/dev/root", root_mountflags);}void __init mount_block_root(char *name, int flags){char *fs_names = __getname_gfp(GFP_KERNEL| __GFP_NOTRACK_FALSE_POSITIVE);char *p;#ifdef CONFIG_BLOCKchar b[BDEVNAME_SIZE];#elseconst char *b = name;#endif/*从命令行获取"rootfstype=根文件系统的类型,本例是jffs2*/get_fs_names(fs_names);retry:for (p = fs_names; *p; p += strlen(p)+1) {/*挂载*/int err = do_mount_root(name, p, flags, root_mount_data);switch (err) {case 0:goto out;case -EACCES:flags |= MS_RDONLY;goto retry;case -EINVAL:continue;}...out:putname(fs_names);}static int __init do_mount_root(char *name, char *fs, int flags, void *data){/*将"/dev/root"设备挂载到"/root"目录下*/int err = sys_mount(name, "/root", fs, flags, data);if (err)return err;/*将当前目录切换到"/root"目录下,因为"/root"挂载有实际的根文件系统,*所以当前目录被切换到实际的根文件系统的根目录下*/sys_chdir("/root");ROOT_DEV = current->fs->pwd.mnt->mnt_sb->s_dev;printk("VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",       current->fs->pwd.mnt->mnt_sb->s_type->name,       current->fs->pwd.mnt->mnt_sb->s_flags & MS_RDONLY ?       " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV));return 0;}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 *//*获取挂载点的信息即mnt结构和dentry结构*/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)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;}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();/*创建新文件系统的vfsmnt,superblock,dentry,inode等结构*/mnt = do_kern_mount(type, flags, name, data);unlock_kernel();if (IS_ERR(mnt))return PTR_ERR(mnt);/*将上述创建的结构,添加到"/root"目录结构中,从而实现挂载*/return do_add_mount(mnt, path, mnt_flags, NULL);}struct vfsmount *do_kern_mount(const char *fstype, int flags, const char *name, void *data){/*根据文件系统类型jffs2,查找对应的file_system_type结构*jffs2文件系统之前在fs/jffs2/super.c中的init_jffs2_fs()函数*完成注册,所以此时可以找到。*/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;}

这个函数之前介绍过,首先分配一个vfsmount结构,然后调用具体文件系统的get_sb()函数。
get_sb()函数会创建super block,根目录dentry,根索引节点inode等结构。
此处需要注意的是,之前的get_sb()函数,是ram类型的,不会去操作flash等存储IC,而jffs2
的get_sb()函数,会从flash中读取实际文件系统的内容,用来初始化super block,根目录
dentry,根索引节点inode等结构。所以挂载完成后,flash里的相关内容(不是全部)就会在内存
中,这也是挂载的实质用意。

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;printk(KERN_WARNING "get_sb0\n");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;}printk(KERN_WARNING "get_sb1\n");error = type->get_sb(type, flags, name, data, mnt);printk(KERN_WARNING "get_sb2\n");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);}


具体的get_sb()函数涉及jffs2的知识,先省略。
程序运行到此处,可以认为代表实际根文件系统的vfsmount,super block,根目录dentry,根索引节点inode
等结构都已初始化完毕。现在要做的就是将这些结构关联到挂载点上。关联过程是由do_add_mount()函数实现。

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


实际操作由graft_tree()中attach_recursive_mnt()的完成

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;}/*此例中propagation_next为NULL,所以下面的函数时空函数。*该函数不知道做什么作用,先不管了*/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);/*parent_path为NULL*/if (parent_path) {detach_mnt(source_mnt, parent_path);attach_mnt(source_mnt, path);touch_mnt_namespace(parent_path->mnt->mnt_ns);} else {mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);commit_tree(source_mnt);}/*tree_list为NULL*/list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {list_del_init(&child->mnt_hash);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;}



就剩下mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);和commit_tree(source_mnt);

void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,struct vfsmount *child_mnt){/*此处就是建立新文件系统和挂载点之间关联的地方*主要设置mnt_parent、mnt_mountpoint以及dentry->d_mounted*dentry->d_mounted就表示dentry目录是否挂载有文件系统*/child_mnt->mnt_parent = mntget(mnt);/**/child_mnt->mnt_mountpoint = dget(dentry);dentry->d_mounted++;}static void commit_tree(struct vfsmount *mnt){struct vfsmount *parent = mnt->mnt_parent;struct vfsmount *m;LIST_HEAD(head);struct mnt_namespace *n = parent->mnt_ns;BUG_ON(parent == mnt);list_add_tail(&head, &mnt->mnt_list);list_for_each_entry(m, &head, mnt_list){/*设置新文件系统的命名空间*/m->mnt_ns = n;}/*合并链表*/list_splice(&head, n->list.prev);/*将mnt->mnt_hash添加到mount_hashtable哈希表中*/list_add_tail(&mnt->mnt_hash, mount_hashtable +hash(parent, mnt->mnt_mountpoint));/*将mnt_child添加到mnt_mounts*/list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);touch_mnt_namespace(n);}


挂载后的主要数据结构图如下:

另外,挂载后,会调用sys_mount(".", "/", NULL, MS_MOVE, NULL)函数,将挂载点移到(MS_MOVE)"/"目录下.
因为设置了MS_MOVE,所以sys_mount()会调用do_move_mount()函数。do_move_mount()仍会调用attach_recursive_mnt();
只不过参数parent_path不再会NULL,所以会执行以下语句

if (parent_path) {detach_mnt(source_mnt, parent_path);attach_mnt(source_mnt, path);touch_mnt_namespace(parent_path->mnt->mnt_ns);} static void detach_mnt(struct vfsmount *mnt, struct path *old_path){/*保存原始挂载点"/root"的信息*/old_path->dentry = mnt->mnt_mountpoint;old_path->mnt = mnt->mnt_parent;/*重新初始化新文件系统的挂载信息*/mnt->mnt_parent = mnt;mnt->mnt_mountpoint = mnt->mnt_root;list_del_init(&mnt->mnt_child);list_del_init(&mnt->mnt_hash);/*原始挂载点的d_mounted减一,表示去掉了一个文件系统*/old_path->dentry->d_mounted--;}static void attach_mnt(struct vfsmount *mnt, struct path *path){/*设置新的挂载点"/",上面分析过*/mnt_set_mountpoint(path->mnt, path->dentry, mnt);/*将mnt->mnt_hash添加到mount_hashtable哈希表中*/list_add_tail(&mnt->mnt_hash, mount_hashtable +hash(path->mnt, path->dentry));/*将mnt_child添加到mnt_mounts*/list_add_tail(&mnt->mnt_child, &path->mnt->mnt_mounts);}


还有一点,因为先后执行了sys_chdir("/root")和sys_chroot("."),所以当前目录和根目录都变成了实际文件系统的根目录。

0 0
原创粉丝点击