Linux VFS 之 mount系统调用(kernel 3.4)

linux version: 3.4.67


kernel 代码

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命令调用关系

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方法)

根据调用关系对各函数注解

1、 do_mount  做一些标识判断、查找目标挂载路径,最后调用do_new_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;
}


2、do_new_mount 创建一个新的挂载点vfsmount对象并加入到一个全局mount tree

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


4、vfs_kern_mount  申请vfsmount结构,并调用mount_fs函数

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

5、mount_fs调用到具体文件系统的mount函数

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;

}

你可能感兴趣的:(Linux VFS 之 mount系统调用(kernel 3.4))