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

实际根文件系统的挂载是在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_BLOCK
	char b[BDEVNAME_SIZE];
#else
	const 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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值