linux系统中是可以通过mount挂载一个文件系统
mount命令格式:
mount [-t vfstype] [-o options] device dir
1.-t vfstype 指定文件系统的类型,通常不必指定。mount 会自动选择正确的类型。常用类型有:
光盘或光盘镜像:iso9660
DOS fat16文件系统:msdos
Windows 9x fat32文件系统:vfat
Windows NT ntfs文件系统:ntfs
Mount Windows文件网络共享:smbfs
UNIX(LINUX) 文件网络共享:nfs
2.-o options 主要用来描述设备或档案的挂接方式。常用的参数有:
loop:用来把一个文件当成硬盘分区挂接上系统
ro:采用只读方式挂接设备
rw:采用读写方式挂接设备
iocharset:指定访问文件系统所用字符集
3.device 要挂接(mount)的设备。
4.dir设备在系统上的挂接点(mount point)。
系统本身也有一个文件目录结构,如果把aufs创建的dentry树绑定到系统本来的dentry树上并建立链接,就可以从原来的系统树遍历到aufs的dentr树。
其实挂载就是这样的 首先是判断dentry的d_mounted成员,如果该成员不为0,说明该dentry是个挂载点,有文件系统挂载,需要特殊处理。当文件系统被挂载的时候,它的vfsmount结构就被链接到内核的一个全局链表—mount_hashtable数组链表 这个数组的每个成员都是一个hash链表,被链接的文件系统的vfsmount被链接到mount_hashtable。这样当发现mnt目录是个特殊的目录时,就从mount_hashtable数组找到hash链表头,再遍历整个hash链表,就能找到被挂载的文件系统的vfsmount,然后mnt目录的dentry就被替换,置换为新文件系统的根目录。
文件系统的挂载就是将文件系统的dentry结构嫁接到目的文件系统的dentry 并且把vfsmount连接到mount_hashtable里去。
一般而言文件系统挂载通过系统调用sys_mount来执行,sys_mount又调用do_mount,do_mount首先获得挂载点目录的dentry结构以及目的系统的vfsmount结构,这些信息保存在一个nameidata结构中。然后根据mount选项调用不同的函数执行mount。aufs第一次执行Mount 所以调用的是do_new_mount函数。
do_new_mount函数实现代码
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;
/* 创建超级块对象和root dentry和inode */
mnt = do_kern_mount(type, flags, name, data);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
/* 调用do_add_mount函数执行挂载部分 */
return do_add_mount(mnt, nd, mnt_flags, NULL);
}
do_add_mount函数实现代码
int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
int mnt_flags, struct list_head *fslist)
{
int err;
down_write(&namespace_sem);
/* Something was mounted here while we slept */
/* 判断dentry的d_mounted成员 检查目录本身是否是挂载点目录 如果是就调用follow_down来寻找真正的dentry结构和vfsmount结构 */
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;
/* 判断源文件系统的根inode是否符号链接,如果是符号链接 返回错误 */
if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
goto unlock;
newmnt->mnt_flags = mnt_flags;
/* 调用graft_tree把aufs的dentry树和目的文件系统的dentry树嫁接到一起 */
if ((err = graft_tree(newmnt, nd)))
goto unlock;
/* 当前fslist为空 跳过 */
if (fslist) {
/* add to the specified expiration list */
spin_lock(&vfsmount_lock);
list_add_tail(&newmnt->mnt_expire, fslist);
spin_unlock(&vfsmount_lock);
}
up_write(&namespace_sem);
return 0;
unlock:
up_write(&namespace_sem);
mntput(newmnt);
return err;
}
graft_tree函数实现代码
static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
{
int err;
/* 判断源文件系统是否可以被挂载 Linux系统的一些特殊的文件系统是不能被挂载的 */
if (mnt->mnt_sb->s_flags & MS_NOUSER)
return -EINVAL;
/* 检查挂载点目录是否是一个目录文件以及源文件系统的根inode是否目录文件 */
if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
S_ISDIR(mnt->mnt_root->d_inode->i_mode))
return -ENOTDIR;
err = -ENOENT;
mutex_lock(&nd->dentry->d_inode->i_mutex);
/* 检查挂载点目录是否是废弃的目录 */
if (IS_DEADDIR(nd->dentry->d_inode))
goto out_unlock;
err = security_sb_check_sb(mnt, nd);
if (err)
goto out_unlock;
err = -ENOENT;
/* 检查挂载点目录是一个根目录或者挂载点目录被缓存到dentry cache中 */
if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry))
/* 调用attach_recursive_mnt执行挂载操作 mnt是aufs的vfsmount nd是目的文件系统的vfsmount */
err = attach_recursive_mnt(mnt, nd, NULL);
out_unlock:
mutex_unlock(&nd->dentry->d_inode->i_mutex);
if (!err)
security_sb_post_addmount(mnt, nd);
return err;
}
attach_recursive_mnt 函数实现代码如下
static int attach_recursive_mnt(struct vfsmount *source_mnt,
struct nameidata *nd, struct nameidata *parent_nd)
{
LIST_HEAD(tree_list);
struct vfsmount *dest_mnt = nd->mnt;
struct dentry *dest_dentry = nd->dentry;
struct vfsmount *child, *p;
if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list))
return -EINVAL;
/* 处理shared */
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_nd为空 执行else分支 */
if (parent_nd) {
detach_mnt(source_mnt, parent_nd);
attach_mnt(source_mnt, nd);
touch_mnt_namespace(current->nsproxy->mnt_ns);
} else {
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);
}
spin_unlock(&vfsmount_lock);
return 0;
}
mnt_set_mountpoint 函数实现代码分析
void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
struct vfsmount *child_mnt)
{
child_mnt->mnt_parent = mntget(mnt);
/* 源文件系统的vfsmount对象的mnt_mountpoint指向目的dentry */
child_mnt->mnt_mountpoint = dget(dentry);
/* d_mounted+1 判断是否为挂载点有用 */
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);
/* 将源文件系统的vfsmount对象链接到namespace的链表尾部 */
list_add_tail(&head, &mnt->mnt_list);
list_for_each_entry(m, &head, mnt_list)
/* vfsmount对象的namespace为父对象的namespace */
m->mnt_ns = n;
list_splice(&head, n->list.prev);
/* 源vfsmount对象链接到mount_hash表 */
list_add_tail(&mnt->mnt_hash, mount_hashtable +
hash(parent, mnt->mnt_mountpoint));
/* 源vfsmount对象链接到父vfsmount对象的链表 */
list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
touch_mnt_namespace(n);
}