继承角度看 WrapFS从面向对象的角度来看,WrapFS 继承自底层文件系统,并且新增加了一下类成员,重写了一些 VFS 层的方法。
文件系统 mountWrapFS 的 mount 方法为:
- mount -t wrapfs /some/lower/path /mnt/wrapfs
复制代码
这里 /some/lower/path 就是底层的文件系统的路径, /mnt/wrapfs 是挂载点的路径,在内核里面可以通过 kern_path() 获取路径所对应的 sturct path 结构,path 结构里面包含了文件系统 mount 时的 vfsmount 信息和挂载点路径的 dentry 信息,struct path 定义如下:
- struct path {
- struct vfsmount *mnt;
- struct dentry *dentry;
- };
复制代码
其中struct vfsmount 里面包含了底层文件系统的挂载点的 dentry 和 super block。通过 super block 和 dentry 结构,我们能获得操作底层文件系统的方法。这里来关注下填充 struct super_block 的方法,只列出了主要的代码。
- static int wrapfs_read_super(struct super_block *sb, void *raw_data, int silent)
- {
- int err = 0;
- struct super_block *lower_sb;
- struct path lower_path;
-
- /* raw_data 就是挂载点的路径 */
- char *dev_name = (char *) raw_data;
- struct inode *inode;
-
- ...
-
- /* 通过kern_path获取挂载点的 path 结构 */
- err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
- &lower_path);
-
- /* 获取底层文件系统的超级块,并绑定到 wrapfs 的超级块里面 */
- lower_sb = lower_path.dentry->d_sb;
- wrapfs_set_lower_super(sb, lower_sb);
-
- /* 设置超级块的方法 */
- sb->s_op = &wrapfs_sops;
-
- ...
-
- /* 获取根 inode 和 dentry,并绑定对应底层目录的 inode ,后面会详细介绍 */
- inode = wrapfs_iget(sb, lower_path.dentry->d_inode);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out_sput;
- }
- sb->s_root = d_alloc_root(inode);
- if (!sb->s_root) {
- err = -ENOMEM;
- goto out_iput;
- }
-
- ...
- }
复制代码
WrapFS VFS 操作的一般方法堆栈操作有两种类型:创建新 VFS 对象和不创建新 VFS 对象。
不创建 VFS 文件对象的方法仅仅传递 VFS 对象到底层,并返回可能的错误信息给 VFS ,比如 link 和 ulink 方法,下面是不创建新对象的代码,这个代码比较简单,就是调用 get_lower_dentry() 获取底层目录的 inode 对象,然后调用底层目录的 inode 对象的 unlink 方法解除 link。
- int wrapfs_unlink(struct inode *dir, struct dentry *dentry)
- {
- int err;
- struct inode *lower_dir;
- struct dentry *lower_dentry;
- lower_dir = get_lower_inode(dir);
- lower_dentry = get_lower_dentry(dentry);
- err = lower_dir->i_op->unlink(lower_dir, lower_dentry);
- return err;
- }
复制代码
- 下面代码展现的是重新创建新 VFS 对象的方法 create。
主要流程就是先调用 vfs_create 创建底层文件系统对象,再调用 wrapfs_interpose() 创建自已的文件系统对象。
- int wrapfs_create(struct inode *dir, struct dentry *dentry, int mode)
- {
- int err;
- struct dentry *lower_dentry;
- struct inode *lower_dir;
- lower_dir = wrapfs_lower_inode(dir);
- lower_dentry = wrapfs_lower_dentry(dentry);
- err = vfs_create(lower_dir, lower_dentry, mode);
- if (!err)
- err = wrapfs_interpose(dentry, dir->i_sb);
- return err;
- }
复制代码
这个函数调用 wrapfs_lower_inode() 来获取底层 dir 的 inode,下面是该函数的实现
- static inline struct inode *wrapfs_lower_inode(const struct inode *i)
- {
- return wrapfs_I(i)->lower_inode;
- }
复制代码
wrapfs_I(i) 是一个宏,定义如下
- static inline struct wrapfs_inode_info *wrapfs_I(const struct inode *inode)
- {
- return container_of(inode, struct wrapfs_inode_info, vfs_inode);
- }
复制代码
再来看看 struct wrapfs_inode_info 的定义:
- struct wrapfs_inode_info {
- struct inode *lower_inode;
- struct inode vfs_inode;
- };
复制代码
lower_inode 是 WrapFS 层文件对应的底层文件的 inode,vfs_inode 是 WrapFS 层的 inode。那 lower_inode 是什么时候设置的?在后面会看到 WrapFS 在创建文件的时候会设置文件对应的 lower_inode。那么 WrapFS 的根目录的 lower_inode 是在什么时候设置的呢?前面有提到过 mount 的时候会获取 mount 点的 dentry 方法,其实根目录的 lower_inode 也是在 mount 时设定的。获取到底层文件系统的 inode 结构后,我们就可以在底层文件系统上创建文件了。这个时候底层的文件时创建好了,但是 WrapFS 层的文件还没有创建好,wrapfs_interpose() 就是做这个事情的:
- int wrapfs_interpose(struct dentry *dentry, struct super_block *sb,
- struct path *lower_path)
- {
- int err = 0;
- struct inode *inode;
- struct inode *lower_inode;
- struct super_block *lower_sb;
-
- lower_inode = lower_path->dentry->d_inode;
- lower_sb = wrapfs_lower_super(sb);
-
- ...
-
- /* 这个函数创建 WrapFS 的 inode */
- inode = wrapfs_iget(sb, lower_inode);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out;
- }
-
- ...
-
- /* 把 inode 和 dentry 绑定 */
- d_add(dentry, inode);
-
- out:
- return err;
- }
复制代码
wrapfs_iget() 创建 WrapFS 层的 inode,并绑定对应的底层文件的 inode;
- struct inode *wrapfs_iget(struct super_block *sb, struct inode *lower_inode)
- {
- struct wrapfs_inode_info *info;
- struct inode *inode; /* the new inode to return */
- int err;
-
- /*
- * 创建 WrapFS 的 inode,该函数会调用 super_ops 的 alloc_inode 方法即
- * wrapfs_alloc_in() 生成 struct wrapfs_inode_info 结构。
- */
- inode = iget5_locked(sb, /* our superblock */
- lower_inode->i_ino, /* hashval */
- wrapfs_inode_test, /* inode comparison function */
- wrapfs_inode_set, /* inode init function */
- lower_inode); /* data passed to test+set fxns */
- ...
-
- /* 设定底层文件的 inode 到 struct WrapFS_inode_info 结构 */
- wrapfs_set_lower_inode(inode, lower_inode);
-
- inode->i_version++;
-
- /* 根据不同的文件类型设定不同的 inode 方法和 file_operation */
- if (S_ISDIR(lower_inode->i_mode))
- inode->i_op = &wrapfs_dir_iops;
- else if (S_ISLNK(lower_inode->i_mode))
- inode->i_op = &wrapfs_symlink_iops;
- else
- inode->i_op = &wrapfs_main_iops;
-
- /* use different set of file ops for directories */
- if (S_ISDIR(lower_inode->i_mode))
- inode->i_fop = &wrapfs_dir_fops;
- else
- inode->i_fop = &wrapfs_main_fops;
-
- inode->i_mapping->a_ops = &wrapfs_aops;
-
- ...
-
- /*
- * 初始化特殊的文件,比如 fifo,块设备文件,字符设备文件,socket 文件等,
- * 这些文件对应子模块有相关接口,所以直接调用内核提高的接口初始化就好了
- */
- if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
- S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
- init_special_inode(inode, lower_inode->i_mode,
- lower_inode->i_rdev);
-
- /* 拷贝底层文件的属性到 WrapFS 层文件,主要是创建时间,访问时间,文件大小等 */
- fsstack_copy_attr_all(inode, lower_inode);
- fsstack_copy_inode_size(inode, lower_inode);
-
- unlock_new_inode(inode);
- return inode;
- }
复制代码
WrapFS 创建文件的过程就是分别在底层文件系统和本层创建一个文件,然后重新设置一下本层文件的 inode 属性以及 inode 的方法和 file_operation 等方法,其它文件操作的方法和上面介绍的两种方法类似,就不一一介绍了。
WrapFS 和链接的区别WrapFS 和链接是有很大区别的,WrapFS 是一种文件系统,有自己的 VFS 数据结构,他的数据结构保存在底层文件系统的某个路径,他把底层的文件系统改造一番后提供给用户,把用户创建的文件改造一番后写到底层文件系统上面。而硬链接是两个文件对于着同一个 inode 以及数据 block;软链接则只是一个文件,有着自己的数据块和 inode,文件数据里面存放着被链接文件的路径。
参考文献