文件系统系列学习笔记(3)


挂载和卸载

1 文件系统启动之初建立“/”,根文件系统的根目录挂载在“/”下,该文件系统所在的设备即是根设备

2 其他文件系统挂载在根文件系统的某些目录下,特定设备文件系统在挂载前以设备文件形式存在于系统上特定目录下如/dev/hda1。设备文件可以确定驱动程序,此时为原始的流设备。可以字节的读写。挂载后以文件系统的形式读写。

3 根据系统不同状态,内核有三个挂载函数:sys_mount(), mount_root(), kern_mount()。下面分别介绍如下:

sys_mount()----系统调用mount的内核实现

//参数:设备路径名,挂载处的目录路径名, 挂载文件系统类型,挂载标志, 挂载数据
**1**asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name, char __user * type, unsigned long flags, void __user * data)
{
     ...
retval = do_mount((char*)dev_page, dir_page, (char*)type_page,
			  flags, (void*)data_page);
         unlock_kernel();
	free_page(data_page);
    ...      
}
//可见主要操作在do_mount()中完成,一起看下:
*
 * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to
 * be given to the mount() call (ie: read-only, no-dev, no-suid etc).
 *
 * data is a (void *) that can point to any structure up to
 * PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent
 * information (or be NULL).
 *
 * Pre-0.97 versions of mount() didn't have a flags word.
 * When the flags word was introduced its top half was required
 * to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.
 * Therefore, if this magic number is present, it carries no information
 * and must be discarded.
 */
**2**long do_mount(char * dev_name, char * dir_name, char *type_page,
		  unsigned long flags, void *data_page)
{
	struct nameidata nd;
	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 (dev_name && !memchr(dev_name, 0, PAGE_SIZE))
		return -EINVAL;

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

	/* 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;
	flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_ACTIVE);

	/* ... and get the mountpoint */
	retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd);//nd中存储挂载目录的dentry信息
	if (retval)
		return retval;
//下面做安全检查和挂载操作检查,直接看主流程do_new_moun()
	retval = security_sb_mount(dev_name, &nd, type_page, flags, data_page);
	if (retval)
		goto dput_out;

	if (flags & MS_REMOUNT)
		retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
				    data_page);
	else if (flags & MS_BIND)
		retval = do_loopback(&nd, dev_name, flags & MS_REC);
	else if (flags & MS_MOVE)
		retval = do_move_mount(&nd, dev_name);
	else
//挂载主流程
		retval = do_new_mount(&nd, type_page, flags, mnt_flags,
				      dev_name, data_page);
dput_out:
	path_release(&nd);
	return retval;
}

**3**static int do_new_mount(struct nameidata *nd, char *type, int flags,
			int mnt_flags, char *name, void *data)
{
	struct vfsmount *mnt;

	if (!type || !memchr(type, 0, PAGE_SIZE))
		return -EINVAL;
        //一般需要系统管理员权限
	/* we need capabilities... */
	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	mnt = do_kern_mount(type, flags, name, data);//主流程
	if (IS_ERR(mnt))
		return PTR_ERR(mnt);

	return do_add_mount(mnt, nd, mnt_flags, NULL);//最后注释************
}

struct vfsmount * do_kern_mount(const char *fstype, int flags, const char *name, void *data) 
 //根据文件系统类型在系统中查找对应的file_system_type信息,该结构中包含有读取超级块的函数指针、VFSmount、具体文件系统flag、module owner、文件系统type名等信息。 
{
	struct file_system_type *type = get_fs_type(fstype);
	struct super_block *sb = ERR_PTR(-ENOMEM);
	struct vfsmount *mnt;
	int error;
	char *secdata = NULL;

	if (!type)
		return ERR_PTR(-ENODEV);

	mnt = alloc_vfsmnt(name);//According the device path name to allocate vfsmount
	if (!mnt)
		goto out;

	if (data) {
		secdata = alloc_secdata();
		if (!secdata) {
			sb = ERR_PTR(-ENOMEM);
			goto out_mnt;
		}

		error = security_sb_copy_data(type, data, secdata);
		if (error) {
			sb = ERR_PTR(error);
			goto out_free_secdata;
		}
	}

	sb = type->get_sb(type, flags, name, data);//Use the get_sb() in structure file_system_type to read super block.
	if (IS_ERR(sb))
		goto out_free_secdata;
 	error = security_sb_kern_mount(sb, secdata);
 	if (error)
 		goto out_sb;
	mnt->mnt_sb = sb;//Super block
	mnt->mnt_root = dget(sb->s_root);//root dentry
	mnt->mnt_mountpoint = sb->s_root;
	mnt->mnt_parent = mnt;
	mnt->mnt_namespace = current->namespace;
	up_write(&sb->s_umount);
	put_filesystem(type);
	return mnt;
out_sb:
	up_write(&sb->s_umount);
	deactivate_super(sb);
	sb = ERR_PTR(error);
out_free_secdata:
	free_secdata(secdata);
out_mnt:
	free_vfsmnt(mnt);
out:
	put_filesystem(type);
	return (struct vfsmount *)sb;
}

最后:

/*
 * 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 nameidata *nd,
		 int mnt_flags, struct list_head *fslist)
{
	int err;

	down_write(&current->namespace->sem);
	/* Something was mounted here while we slept */
	while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
		;
	err = -EINVAL;
	if (!check_mnt(nd->mnt))
		goto unlock;

	/* Refuse the same filesystem on the same mount point */
	err = -EBUSY;
	if (nd->mnt->mnt_sb == newmnt->mnt_sb &&
	    nd->mnt->mnt_root == nd->dentry)
		goto unlock;

	err = -EINVAL;
	if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
		goto unlock;

	newmnt->mnt_flags = mnt_flags;
	err = graft_tree(newmnt, nd);

	if (err == 0 && fslist) {
		/* add to the specified expiration list */
		spin_lock(&vfsmount_lock);
		list_add_tail(&newmnt->mnt_fslink, fslist);
		spin_unlock(&vfsmount_lock);
	}

unlock:
	up_write(&current->namespace->sem);
	mntput(newmnt);
	return err;
}

应用的到的重要数据结构及其意义:

- file_system_type:系统初始化时将内核支持的各种文件系统的file_system_type数据结构用register_filesystem挂到一个队列当中,成为文件系统的注册。有些文件系统支持可安装的模块,在装入这种模块时也会将相应的数据结构挂入该队列中。

- vfsmount:作为挂载待挂载的设备与挂载点的连接器。

struct vfsmount
{
	struct list_head mnt_hash;
	struct vfsmount *mnt_parent;	/* fs we are mounted on */
	struct dentry *mnt_mountpoint;	/* dentry of mountpoint */
	struct dentry *mnt_root;	/* root of the mounted tree */
	struct super_block *mnt_sb;	/* pointer to superblock */
	struct list_head mnt_mounts;	/* list of children, anchored here */
	struct list_head mnt_child;	/* and going through their mnt_child */
	atomic_t mnt_count;
	int mnt_flags;
	int mnt_expiry_mark;		/* true if marked for expiry */
	char *mnt_devname;		/* Name of device e.g. /dev/dsk/hda1 */
	struct list_head mnt_list;
	struct list_head mnt_fslink;	/* link in fs-specific expiry list */
	struct namespace *mnt_namespace; /* containing namespace */
};

优质内容筛选与推荐>>
1、安卓LogCat中无用信息过滤
2、linux字符串操作
3、Unity pdb2mdb错误
4、Visual Studio与Eclipse与IntelliJ快捷键对比
5、mvn install 打包


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn