初识 WrapFS WrapFS 是一种堆栈式文件系统,堆栈式文件系统的一个基本功能就是把操作和参数转换成底层文件系统的操作和参数。这就意味着我们会在 WrapFS 层上创建一个文件对象后会在底层文件对应着创建一个对象,要说明的是,WrapFS 层的这个文件对象只保存在内存里面,断电后会消失,真正文件里面的数据保存在底层文件系统里面。
WrapFS 的意义 从 WrapFS 的发明者有这样的回答:
WrapFS 是一种理想的小模板,可以修改,逐步改造出新的文件系统功能。
WrapFS 可以当作一种方法,用于测试 Linux VFS 超强的堆叠能力。
WrapFS 可以当作学习 VFS,或学习如何写新的 Linux 文件系统的一个好工具。
在 Android 里面,采用的是 FUSE 文件系统,FUSE 文件系统的最终实现是在用户空间,这样导致一个文件操作会两次跨越用户空间和内核空间,导致效率降低,但是 WrapFS 不会有这个问题,其性能接近底层文件系统的实际性能.
继承角度看 WrapFS 从面向对象的角度来看,WrapFS 继承自底层文件系统,并且新增加了一下类成员,重写了一些 VFS 层的方法。
文件系统 mount WrapFS 的 mount 方法为:
1 mount -t wrapfs /some/lower/path /mnt/wrapfs
这里 /some/lower/path
就是底层的文件系统的路径, /mnt/wrapfs
是挂载点的路径,在内核里面可以通过 kern_path()
获取路径所对应的 sturct path
结构,path
结构里面包含了文件系统 mount 时的 vfsmount 信息和挂载点路径的 dentry 信息,struct path
定义如下:
1 2 3 4 struct path { struct vfsmount *mnt; struct dentry *dentry; };
其中struct vfsmount
里面包含了底层文件系统的挂载点的 dentry 和 super block。通过 super block 和 dentry 结构,我们能获得操作底层文件系统的方法。这里来关注下填充 struct super_block
的方法,只列出了主要的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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。
1 2 3 4 5 6 7 8 9 10 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()
创建自已的文件系统对象。
1 2 3 4 5 6 7 8 9 10 11 12 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,下面是该函数的实现
1 2 3 4 static inline struct inode *wrapfs_lower_inode(const struct inode *i) { return wrapfs_I(i)->lower_inode; }
wrapfs_I(i)
是一个宏,定义如下
1 2 3 4 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
的定义:
1 2 3 4 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()
就是做这个事情的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 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,文件数据里面存放着被链接文件的路径。
参考文献
This is copyright.