linux version: 3.4.67
fs/namespace.c , mount.h
struct vfsmount {
struct dentry *mnt_root;/*挂载点根目录的dentry */
struct super_block *mnt_sb;/* 挂载点批向superblock的指针*/
int mnt_flags; //挂载标识
};
struct mount {
struct list_head mnt_hash;
struct mount *mnt_parent;
struct dentry *mnt_mountpoint;
struct vfsmount mnt;
........
};
mount- > /fs/namespace.c/SYSCALL_DEFINE5(mount,....); -> do_mount -> do_new_mount->
->do_kern_mount->vfs_kern_mount->mount_fs->
->/fs/super.c (struct file_system_type *)type->mount(具体文件系统的mount方法)
long do_mount(const char *dev_name, const char *dir_name,
const char *type_page, unsigned long flags, void *data_page)
{
struct path path;
int retval = 0;
int mnt_flags = 0;
//查询路径,存储在namidata结构体对象,存放了挂载点目录项对象和挂载点对象
retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
if (retval)
return retval;
。。。。。。
/* Separate the per-mountpoint flags */
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
if (flags & MS_NODEV)
mnt_flags |= MNT_NODEV; // 该文件系统类型不访问块设备
//注:根据这个标识最后具体文件系统的mount方法会调用super.c的mount_nodev或者mount_bdev。
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);
if (flags & MS_REMOUNT) //重新挂载,通常改变文件挂载的标志,如将只读的文件系统变为可写,一般不改变挂载点 ,比如:adb remount
retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags, data_page)
//将文件系统的某个目录挂载到另一个目录,这样两个目录可以共同访问同一个目标设备或目录。
//比如: sprintf( emulatedPoint, "/mnt/shell/emulated/%d", uid);
//mount(emulatedPoint, "/storage/sdcard0/", "", MS_BIND, NULL);
//通过这种mount bind后,"/mnt/shell/emulated/0"和"/storage/sdcard0/" 都可以访问内置sdcard
else if (flags & MS_BIND)
retval = do_loopback(&path, dev_name, flags & MS_REC);
//改变mount点的type,实际有这种需求吗?没用过!
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
retval = do_change_type(&path, flags);
//移动mo unt点,不清楚用于什么场景,没实际使用过!
else if (flags & MS_MOVE)
retval = do_move_mount(&path, dev_name);
else //通常情况下调用这个函数,mount一个新的挂载点
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;
int err;
/* we need capabilities... */
if (!capable(CAP_SYS_ADMIN)) //mount的权限检查,只有root用户才有权mount
{
return -EPERM;
}
//创建一个新的挂载点vfsmount对象,包括建立一个超级块对象和根目录项
mnt = do_kern_mount(type, flags, name, data);
if (IS_ERR(mnt))
{
return PTR_ERR(mnt);
}
//将挂载点添加到挂载目录树,这个tree是全局的(不清楚具体算法),后面使用就在这边找吧。
err = do_add_mount(real_mount(mnt), path, mnt_flags);
if (err)
{
mntput(mnt);
}
return err;
}
3、do_kern_mount调用vfs_kern_mount
static struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
//根据type从全局file_systems链表(filesystems.c)查找已经注册的文件系统类型。比如ext4, fuse ,
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); //创建挂载点,并返回vfsmount挂载点对象。
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
{
xlog_printk(ANDROID_LOG_DEBUG, "MNT_TAG", "do_kern_mount, fs_set_subtype\n");
mnt = fs_set_subtype(mnt, fstype); //更新superblock fstype变量
}
put_filesystem(type);
return mnt;
}
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct mount *mnt;
struct dentry *root;
mnt = alloc_vfsmnt(name); //申请vfsmount结构内存,并初始化
if (!mnt)
{
return ERR_PTR(-ENOMEM);
}
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
root = mount_fs(type, flags, name, data); //调用具体的fs来mount,并得到根目的dentry和文件系统superblock等。
if (IS_ERR(root)) {
free_vfsmnt(mnt);
return ERR_CAST(root);
}
mnt->mnt.mnt_root = root; //根目的dentry和文件系统superblock初始化vfsmount挂载点对象
mnt->mnt.mnt_sb = root->d_sb;
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
br_write_lock(vfsmount_lock);
list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
br_write_unlock(vfsmount_lock);
return &mnt->mnt;
}
struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
struct dentry *root;
struct super_block *sb;
char *secdata = NULL;
int error = -ENOMEM;
。。。。。。
root = type->mount(type, flags, name, data); // 调用具体文件系系统由register_filesystem注册的file_system_type->mount方法。
//如前面提到,从fs/super.c 中可以看到根据不同文件系统的类型mount方法最后会调用两个通用函数:访问块设备mount_bdev,和不访问块设备主mount_nodev。
sb = root->d_sb;
BUG_ON(!sb);
WARN_ON(!sb->s_bdi);
WARN_ON(sb->s_bdi == &default_backing_dev_info);
sb->s_flags |= MS_BORN;
。。。。。。。
WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
"negative value (%lld)\n", type->name, sb->s_maxbytes);
return root;
}