有没有一种网站做拍卖厂的/章鱼磁力链接引擎
1. 概述
2. 实现
2.1 相关数据结构
2.2 sys_mount()流程分析
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;/* 以下几个函数将用户态参数拷贝至内核态,包括:** 1. kernel_type:挂载文件系统类型,如ext3** 2. kernel_dir: 挂载点路径** 3. dev_name: 设备名称** 4. data_pages: 选项信息*/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;/* 调用do_mount 完成主要挂载工作 */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;
}
总体上来说,该函数还算比较简单,主要就作了两件事:
/* 参数:
** dev_name : 挂载设备名称
** dir_name : 挂载点名称
** type_page: 保存挂载的文件系统类型,如"ext3"
** flags : 挂载标志
** data_page: 大部分情况下为NULL
*/
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;/* 调用kern_path(),根据挂载点名称查找其dentry等信息** 参数:@dir_name : 挂载点路径** :@LOOKUP_FOLLOW: 查找标志,遇到链接继续查找** :@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;/* 对于挂载标志的检查和初始化,忽略 *//* 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_BORN |MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |MS_STRICTATIME);/* 我们不关心别的,只关注do_new_mount */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);elseretval = do_new_mount(&path, type_page, flags, mnt_flags,dev_name, data_page);
dput_out:path_put(&path);return retval;
}
在do_mount()函数中,作了一些标志位的检查,安全性检查等附加工作,然后根据不同的flag来调用不同的挂载函数,我们这里主要关注普通块设备的挂载,其他的以后遇到再分析吧,这这里面调用了下面两个主要的函数:
/* 参数:
** @path: 保存挂载点信息,包括dentry结构等
** @type: 挂载文件系统类型,如ext3
** @flags & mnt_flags:挂载标志
** @name:设备名称
** @data: 保存额外的数据,一般是NULL
*/
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... *//* 必须是root权限*/if (!capable(CAP_SYS_ADMIN))return -EPERM;lock_kernel();/* 调用do_kern_mount()来完成挂载第一步,主要是创建一个vfsmount结构并对其初始化** 并读设备的superblock并初始化文件系统在内存中的结构等*/mnt = do_kern_mount(type, flags, name, data);unlock_kernel();if (IS_ERR(mnt))return PTR_ERR(mnt);/* 将上面创建的vfsmount结构添加到全局结构中** 形成目录树结构*/return do_add_mount(mnt, path, mnt_flags, NULL);
}
/* 参数: fstype: 文件系统名称,如"ext3"
** flags : 挂载标志
** name : 设备名称,如"/dev/sda"
** data : 其他挂载数据,暂时忽略
*/
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{/* 首先根据文件系统名称获取文件系统结构file_system_type** 内核中所有支持的文件系统的该结构通过链表保存*/struct file_system_type *type = get_fs_type(fstype);struct vfsmount *mnt;if (!type)return ERR_PTR(-ENODEV);/* 调用vfs_kern_mount()完成主要挂载*/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;
}
该函数主要流程是:1. 调用get_fs_type()通过文件系统名查找到文件系统的struct file_system_type结构;2.通过vfs_kern_mount()来进行主要的挂载。
/* 主要挂载工作
** 参数: type : 文件系统内存结构
** : flags: 挂载标志
** : name : 设备名称
** : data : 额外挂载参数,忽略
*/
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;//首先,分配一个代表挂载结构的struct vfs_mount结构mnt = alloc_vfsmnt(name);if (!mnt)goto out;if (flags & MS_KERNMOUNT)mnt->mnt_flags = MNT_INTERNAL;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;}//调用具体文件系统的get_sb方法//从name代表的设备上读出超级块信息error = type->get_sb(type, flags, name, data, mnt);if (error < 0)goto out_free_secdata;BUG_ON(!mnt->mnt_sb);WARN_ON(!mnt->mnt_sb->s_bdi);mnt->mnt_sb->s_flags |= MS_BORN;//与安全相关,暂时忽略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);//设置挂载点的dentry结构为刚读出的设备的根目录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);
}
我们追踪到这里对于第一个部分的函数调用do_kern_mount()就已经基本结束,在这个相当底层的函数中,它主要调用了具体文件系统的get_sb()方法来从设备上读出超级块,这个我们会在具体文件系统剖析的时候去作深入地分析。其实就是读出超级块,作有效性检查,内存构造根目录项等工作。
至此,我们第一部分的主要工作就完成了,在该部分的核心工作就是创建一个struct vfsmount,并读出文件系统超级块来初始化该结构。接下来就是将该结构添加到全局结构中,这就是do_add_mount()的主要工作。
int do_add_mount(struct vfsmount *newmnt, struct path *path,int mnt_flags, struct list_head *fslist)
{int err;mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);//这里为什么需要对namespace_sem加锁呢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;//主要调用graft_tree()来实现文件系统目录树结构//其中newmnt是本次创建的vfsmount结构//path是挂载点信息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;
}
do_add_mount()主要调用了函数graft_tree()来实现linux文件系统的目录树结构。
/*参数: mnt: 本次挂载新建的vfsmount结构
** : path: 挂载点的结构
*/
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 (cant_mount(path->dentry))goto out_unlock;if (!d_unlinked(path->dentry))err = attach_recursive_mnt(mnt, path, NULL);
out_unlock:mutex_unlock(&path->dentry->d_inode->i_mutex);return err;
}
该函数的处理也是首先检查挂载的有效性(如挂载点是否是目录)等,而真正的核心函数是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;struct vfsmount *child, *p;int err;//这个与sharemount相关,暂时忽略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;br_write_lock(vfsmount_lock);if (IS_MNT_SHARED(dest_mnt)) {for (p = source_mnt; p; p = next_mnt(p, source_mnt))set_mnt_shared(p);}//因为传入的parent_path是NULL,所以我们//对else分支比较感兴趣if (parent_path) {detach_mnt(source_mnt, parent_path);attach_mnt(source_mnt, path);touch_mnt_namespace(parent_path->mnt->mnt_ns);} else {//这里主要是将新建的source_mnt与挂载点(dest_dentry) 挂钩//并和挂载点所在的dest_mnt建立起父子关系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);commit_tree(child);}br_write_unlock(vfsmount_lock);return 0;out_cleanup_ids:if (IS_MNT_SHARED(dest_mnt))cleanup_group_ids(source_mnt, NULL);out:return err;
}
该函数中很多的代码可能与sharemount机制相关,关于该机制我会在另外一篇博客中专门阐述,这里就忽略了,因为参数parent_path主要为NULL,所以我们比较关注的代码分支是else,即mnt_set_mountpoint和commit_tree,其实两个函数干的事情都比较简单,主要是将当前新建的vfsmount结构与挂载点挂钩,并和挂载点所在的vfsmount形成一种父子关系结构。形成的结构图如下所示: