|
![]() | 作者: softlib [softlib]
![]() |
登录 |
AIX 内核的虚拟文件系统框架 Author: sinister Email: sinister@whitecell.org Homepage:http://www.whitecell.org Date: 2005-11-16 2005-10-25 虚拟文件系统(Virtual File System)作为上层系统调用(System call)接口以 及下层实际文件系统的中间层支持框架被广泛应用再各 UNIX 操作系统中。不论是 System V 系列的 AIX,SOLARIS 还是 BSD 系列的 Free/Net/Open BSD 操作系统都 支持虚拟文件系统(Virutal File System 以下简称 VFS)框架,为什么要在 System call 与 File System 之间抽象出一层 VFS?其主要目的是为了支持多种类型的下层 实际文件系统,并对上层系统调用有一个统一完整的接口。为了更好的达到目的,VFS 是按照面向对象的方法设计实现。 本文主要以AIX 内核为基础介绍 VFS框架,如 AIX 内核特有的 GFS。象文件系统缓 存及相关的内存映射机制与内核文件读/写流程则不在本文的介绍范围之内。我会另写 篇文章来和大家探讨。 AIX 内核在启动时只有一个根文件系统的 vfs 对象,由 rootvfs 指向这个根 vfs 对象。rootvfs 是一个链头,之后安装的下层实际文件系统所对应的 vfs 结构都会添 加到由 rootvfs 所指向的 vfs 链中。每个vfs 结构都代表一个下层实际文件系统, 如 AIX 的 JFS 文件系统。每个下层实际文件系统至少有一个或可能多个(一个文件 系统被多次安装在不同目录上) vfs 结构对应。那么系统如何来连接每个 vfs 对象 与下层实际文件系统?又是如何区分每个 vfs 对象对应哪个下层实际文件系统的呢? 解决这个问题是靠内核维护的一个gfs 来实现的。结构描述如下: struct gfs { struct vfsops *gfs_ops; struct vnodeops *gn_ops; int gfs_type; /* type of gfs (from ) */ char gfs_name[16]; /* name of vfs (eg. "jfs","nfs", ...)*/ int (*gfs_init)(); /* ( gfsp ) - if ! NULL, */ /* called once to init gfs */ int gfs_flags; /* flags for gfs capabilities */ caddr_t gfs_data; /* ptr to gfs's private config data */ int (*gfs_rinit)(); int gfs_hold; /* count of mounts of this gfs type */ }; 系统上安装的所有下层实际文件系统都可以在 gfs 结构中找到。也就是说下层实际文 件系统驱动要想在 AIX 内核顺利运行,都必须在第一次安装时通过调用一个 gfsadd() 函数来填充 gfs 结构中的相关信息。系统维护了一个 gfs 数组,根据下层实际文件系 统的类型作为数组的下标,来选择填充哪个 gfs 结构。这样一来,一个 gfs 结构就代 表一个下层实际文件系统,也就是说,gfs 与下层实际文件系统是一一对应的,有几个 gfs 就有几个下层实际文件系统,不会存在多个 vfs 代表一个下层实际文件系统的情 况。我们来看一下 gfs 中的重要数据结构。gfs 结构中的 gfs_type 是下层实际文件 系统在 gfs 结构中的一个索引,用来指定下层实际文件系统的类型,相关定义在 vmount .h 中的,定义如下: #define MNT_AIX 0 /* AIX physical fs "oaix" */ #define MNT_NFS 2 /* SUN Network File System "nfs" */ #define MNT_JFS 3 /* AIX R3 physical fs "jfs" */ #define MNT_CDROM 5 /* CDROM File System "cdrom" */ #define MNT_SFS 16 /* AIX Special FS (STREAM mounts) */ #define MNT_CACHEFS 17 /* Cachefs file system */ #define MNT_NFS3 18 /* NFSv3 file system */ #define MNT_AUTOFS 19 /* Automount file system */ gfs_name 指示了下层实际文件系统的名称,如下层实际文件系统是 JFS 那么 gfs->gfs _name 则为 jfs。而 gfs_init 函数指针指向下层实际文件系统的初始化列程 如 jfs_i nit()。gfs_flags 指定下层实际文件系统可否被强行卸载,和是否为远程文件系统,还 有 gfs 所支持的当前内核版本。列:gfs-> gfs_flags = GFS_FUMNT | GFS_VERSION4; gfs_data应该是指下层实际文件系统专有的特性,如分区及元数据(meta data)。对于 gfs_data 我也不是很确定,可能理解有误,还希望得到你的指正。gfs_rinit() 函数指 针指向下层实际文件系统的,根节点初始化列程,如 jfs_rootinit()。还有两个重要的 结构就是 struct vfsops *gfs_ops, 和 struct vnodeops *gn_ops, 它们是vfs/vnode 用来连接下层实际文件系统相关操作函数的。这样就系统就可以很容易通过 vfs-> gfs<-jfs这种映射关系找到每个 vfs 对应的下层实际文件系统,并调用 vfs 相关函数 对其进行操作。下面我们来看下 AIX 内核定义的 struct vfs 结构,结构描述如下: struct vfs { struct vfs *vfs_next; /* vfs's are a linked list */ struct gfs *vfs_gfs; /* ptr to gfs of vfs */ struct vnode *vfs_mntd; /* pointer to mounted vnode, */ /* the root of this vfs */ struct vnode *vfs_mntdover; /* pointer to mounted-over */ /* vnode */ struct vnode *vfs_vnodes; /* all vnodes in this vfs */ int vfs_count; /* number of users of this vfs */ caddr_t vfs_data; /* private data area pointer */ unsigned int vfs_number; /* serial number to help */ /* distinguish between */ /* different mounts of the */ /* same object */ int vfs_bsize; /* native block size */ #ifdef _SUN short vfs_exflags; /* for SUN, exported fs flags */ unsigned short vfs_exroot; /* for SUN, " fs uid 0 mapping */ #else short vfs_rsvd1; /* Reserved */ unsigned short vfs_rsvd2; /* Reserved */ #endif /* _SUN */ struct vmount *vfs_mdata; /* record of mount arguments */ Simple_lock vfs_lock; /* lock to serialize vnode list */ }; typedef struct vfs vfs_t; /* these defines are for backwards compatibility */ #define vfs_fsid vfs_mdata->vmt_fsid #define vfs_date vfs_mdata->vmt_time #define vfs_flag vfs_mdata->vmt_flags #define vfs_type vfs_gfs->gfs_type #define vfs_ops vfs_gfs->gfs_ops 从上面结构中可以看出 struct vfs 是一个链表。由 struct vfs *vfs_next 来指向 下一个vfs 结构,由上面提到的 gfs 结构中的 struct gfs *vfs_gfs 来找到下层实 际文件系统。以此来连接系统中的所有下层实际文件系统,并对应到其相关的 vfs。 为了更好的理解 VFS 实际操作过程及每个vfs->gfs<-physical file system 的对应 关系,我们用 AIX 的 kdb 来观察一下。 (0)> vfs GFS MNTD MNTDOVER VNODES DATA TYPE FLAGS 1 30A0783C 001C9F30 1341E3F8 00000000 131F1E68 30A11C28 JFS DEVMOUNT ... /dev/hd4 mounted over / 2 30A078A4 001C9F30 13FDF9F8 1354B2F8 137D2968 30A11BC0 JFS DEVMOUNT ... /dev/hd2 mounted over /usr 3 30A07870 001C9F30 137A5260 132F1660 13902838 30A118E8 JFS DEVMOUNT ... /dev/hd9var mounted over /var 4 30A07808 001C9F30 1383BCB0 14239960 14111A88 30A119B8 JFS DEVMOUNT ... /dev/hd3 mounted over /tmp 5 30A078D8 001C9F30 13A01388 1354D788 13D0B678 30A11880 JFS DEVMOUNT ... /dev/hd1 mounted over /home 在 kdb 下输入 vfs 显示系统中所有的 vfs 对象。我们可以看到,系统中一共有 5 个 vfs 对象,每个 vfs 对象的第一项代表 vfs 地址,第二项代表 gfs 对象地址, 第三项是根 vnode。 第四项是所安装目录的 vnode,第五个地址是下层实际文件系 统相关的数据地址。第六项表示,下层实际文件系统的类型。第七项表示下层实际文件 系统的标志,DEVMOUNT 表示是本地文件系统。如果是网络文件系统,则标志为 REMOTE 如,nfs 文件系统。从上面所列出的信息可以看到,第一项 vfs 地址中应该是指向系 统的下一个 vfs 地址,即 vfs->vfs_next。我们看到每项 vfs 地址都是不同的。但它 所对应的第六项(TYPE)下层实际文件系统却是相同的 jfs。这也就证明了上面提到的, 每个 vfs 对象对应一个下层实际文件系统,而下层实际文件系统可能对应多个 vfs 对 象。第二项 gfs 对象地址则都是一样的,这也证明上面提到的每个 gfs 对象和下层实 际文件系统是一一对应的,有几个下层实际文件系统就有几个 gfs 对象。上面所列出的 下层实际文件系统只有 jfs,那么也就只有一个 gfs 对象。我们输入 vfs 30A0783C 来 看一下第一个 vfs 对象的内容。 (0)> vfs 30A0783C GFS MNTD MNTDOVER VNODES DATA TYPE FLAGS 0 30A0783C 001C9F30 1341E3F8 00000000 131F1E68 30A11C28 JFS DEVMOUNT ... /dev/hd4 mounted over / vfs_next..... 30A078A4 vfs_gfs...... 001C9F30 vfs_mntd..... 1341E3F8 vfs_mntdover. 00000000 vfs_vnodes... 131F1E68 vfs_count.... 00000001 vfs_number... 00000005 vfs_bsize.... 00001000 vfs_mdata.... 30A10200 vmt_revision. 00000001 vmt_length... 0000006C vfs_fsid..... 000A0004 00000003 vmt_vfsnumber 00000005 vfs_date..... 4357D873 vfs_flag..... 00000004 vmt_gfstype.. 00000003 @vmt_data.... 30A10224 vfs_lock..... 00000000 vfs_lock@.... 30A07868 vfs_type..... 00000003 vfs_ops...... jfs_vfsops (因篇幅所限,只截取 vfs 对象内容) 我们可以看到 vfs_next 所指的内容正是用 vfs 命令列出来的第二个 vfs 对象的地址。 vfs_gfs 指向的 gfs 对象地址也都是一个地址。vfs_type 为 3 正是 vmount.h 中定 义的 MNT_JFS,jfs 文件系统。这时可以更明确的看到 vfs_ops 与 jfs_vfsops 的对应 关系,我们可以再来看下一个 vfs 对象。输入 vfs 30A078A4 (0)> vfs 30A078A4 GFS MNTD MNTDOVER VNODES DATA TYPE FLAGS 0 30A078A4 001C9F30 13FDF9F8 1354B2F8 137D2968 30A11BC0 JFS DEVMOUNT ... /dev/hd2 mounted over /usr vfs_next..... 30A07870 vfs_gfs...... 001C9F30 vfs_mntd..... 13FDF9F8 vfs_mntdover. 1354B2F8 vfs_vnodes... 137D2968 vfs_count.... 00000001 vfs_number... 00000006 vfs_bsize.... 00001000 vfs_mdata.... 30A10380 vmt_revision. 00000001 vmt_length... 00000070 vfs_fsid..... 000A0005 00000003 vmt_vfsnumber 00000006 vfs_date..... 4357D874 vfs_flag..... 00000004 vmt_gfstype.. 00000003 @vmt_data.... 30A103A4 vfs_lock..... 00000000 vfs_lock@.... 30A078D0 vfs_type..... 00000003 vfs_ops...... jfs_vfsops 可以看到 vfs->vfs_gfs,vfs->vfs_type, vfs->jfs_vfsops 值和上面所显示的是一样 的。我们一直按照 vfs->vfs_next 所显示的地址输入,直到 vfs->vfs_next 为 0。这样 即遍历了所有 vfs 对象。 (0)> vfs 30A078D8 GFS MNTD MNTDOVER VNODES DATA TYPE FLAGS 0 30A078D8 001C9F30 13A01388 1354D788 13D0B678 30A11880 JFS DEVMOUNT ... /dev/hd1 mounted over /home vfs_next..... 00000000 vfs_gfs...... 001C9F30 vfs_mntd..... 13A01388 vfs_mntdover. 1354D788 vfs_vnodes... 13D0B678 vfs_count.... 00000001 vfs_number... 00000009 vfs_bsize.... 00001000 vfs_mdata.... 30A10700 vmt_revision. 00000001 vmt_length... 00000070 vfs_fsid..... 000A0008 00000003 vmt_vfsnumber 00000009 vfs_date..... 4357D898 vfs_flag..... 00000004 vmt_gfstype.. 00000003 @vmt_data.... 30A10724 vfs_lock..... 00000000 vfs_lock@.... 30A07904 vfs_type..... 00000003 vfs_ops...... jfs_vfsops 上面的 vfs_next 值为0,表示这是系统中最后一个 vfs 对象。这时再来看一下 gfs 对象,通过观察 gfs 对象在每个 vfs 对象 vfs->vfs_gfs 中的值,它们所指的地址是 一样的,我们通过 gfs 001C9F30命令来查看 gfs 对象。 (0)> gfs 001C9F30 gfs_data. 00000000 gfs_flag. INIT VERSION4 VERSION42 VERSION421 VERSION43 gfs_ops.. jfs_vfsops gn_ops... jfs_vops gfs_name. jfs gfs_init. jfs_init gfs_rinit jfs_rootinit gfs_type. JFS gfs_hold. 00000005 我们可以看到,gfs 结构中每项都已对应到了下层实际文件系统 jfs 中的各项。上面 看到的 vfs 对象正是通过 vfs->vfs_gfs 连接到 gfs 对象中得到的 jfs 各项。 如 vfs_ops 与 jfs_vfsops 的映射关系,正是通过 gfs->gfs_ops 来连接的。这也就 证明了文章开始提到的 vfs->gfs<-jfs 那种通过 gfs 对象建立起来的对应关系。再看 来下一个 gfs 对象,通过 gfs 001C9F30 + 30 获得。 (0)> gfs 001C9F30 + 30 gfs_data. 00000000 gfs_flag. INIT VERSION4 VERSION42 VERSION421 VERSION43 gfs_ops.. spec_vfsops gn_ops... spec_vnops gfs_name. sfs gfs_init. spec_init gfs_rinit nodev gfs_type. SFS gfs_hold. 00000000 在第二个 gfs 对象中可以看到下层实际文件系统为 sfs。我们再向下找 gfs 对象。 (0)> gfs 001C9F30 + 60 gfs_data. 00000000 gfs_flag. gfs_ops.. 00000000 gn_ops... 00000000 gfs_name. gfs_init. 00000000 gfs_rinit 00000000 gfs_type. AIX gfs_hold. 00000000 当 gfs 各项为 0 时则表明已经没有下层实际文件系统存在。 (0)> q 退出 kdb。 介绍完 vfs/gfs 对象与下层实际文件系统关系后,再来看看 vfs/gfs 对象中与文件 系统相关函数。在上面介绍 gfs 对象时提到的struct vfsops 结构是 vfs 用来对应下 层实际文件系统与文件系统相关操作函数的,这样上层系统调用操作与文件系统相关函 数时,只需要调用 vfs->vfsops 里指定的函数,而不必关心具体的文件系统。vfsops 结构如下: struct vfsops { int (*vfs_mount)(vfs_t *, vnode_t *, struct mounta *, cred_t *); int (*vfs_unmount)(vfs_t *, int, cred_t *); int (*vfs_root)(vfs_t *, vnode_t **); int (*vfs_statvfs)(vfs_t *, statvfs64_t *); int (*vfs_sync)(vfs_t *, short, cred_t *); int (*vfs_vget)(vfs_t *, vnode_t **, fid_t *); int (*vfs_mountroot)(vfs_t *, enum whymountroot); int (*vfs_freevfs)(vfs_t *); int (*vfs_vnstate)(vfs_t *, vnode_t *, vntrans_t); }; 为了更好的说明 vfsops 结构中的函数作用,我们举一个实际的例子。如当有一个实际文 件系安装时,如 JFS,系统调用 mount() 首先分配一个新的 vfs 结构来对应 JFS 文件 系统,上面讲到一个下层实际文件系统驱动是如何与 gfs 对应起来,那么一个新分配的 vfs又是如何找到相关 gfs 并关联下层实际文件系统的相关函数呢?上面提过要用到 vfs 结构中的 vfs->vfs_gfs,那么它又是被谁赋值的呢?这个值是由系统调用 mount() 的 vmount 参数所提供的,vmount 中的 vmt_gfstype 其实就是在 gfs 数组中的一个索引, 用来对应某个 vfs 结构在 gfs 中的位置。如 vfs->vfs_gfs = gfsindex[vmount->vmt_ gfstype] 这样便定位到了相应的 gfs, 以便来初始化新vfs 中的虚拟文件系统相关函数 与 JFS 文件系统相关函数连接,从上面的 vfs 结构中可以看到,每个 vfs 结构中并不 是直接的存有虚拟文件系统相关函数,如 vfs_mount() 是通过 vfs->vfs_gfs 定位到相 关的 gfs 结构,在gfs 结构中由 struct vfsops gfs_ops 所指定的。这样来完成 vfs-> vfs_gfs->gfs_ops->vfs_mount 与 jfs_mount 关联。设置好新 vfs 的相关项后,接着调 用 lookupname() 来得到安装目录的 vnode,(我们知道当 mount 一个文件系统时,需 要一个目录做为安装节点)。并将返回的目录 vnode 保存到vfs->vfs_mntdover 项中, 并调用 vn_access() 检查是否对此目录vnode 有访问权限。最后调用 vfs->vfs_gfs结构 中 gfs_ops 所指向的 vfs_mount() 函数,在 vfs_mount() 函数开始首先调用 vfs_root () 得到根接点,再向下调用 jfs_mount() 函数来完成实际的安装工作。当调用返回成功 时,会将其 vfs 加到系统 rootvfs 所指的vfs链中并设置 vfs->vfs_flag 标志通知系统 已经安装成功。再如 NFS 文件系统安装时流程如上,只不过 vfs_mount() 调用 的是 nf s_mount() 来完成安装工作。卸载一个文件系统 umount() 系统调用的流程基本上是moun t() 系统调用的一个反向过程,它首先从 rootvfs 链头开始遍历,通过比较 vfsp->vfs_ number 找到需要卸栽的 vfs 并设置 vfsp->vfs_flag 为 VFS_UNMOUNTING。接着调用 vn _access() 判断 vfs->vfs_mntdover 确保对安装文件系统的目录有访问权限。如果可以 访问,则设置 vfs->vfs_mntdover->v_mvfsp 为 NULL,以免多个进程来卸载。最后调用 vfs_umount() -> jfs_umount() 来完成卸栽。卸栽完成后设置 vfs->vfs_flag 为 VFS_ UNMOUNTED 并检查 vfsp->vfs_count 是否为 0,如果为 0 则将此 vfs 从 rootvfs 链中 删掉,并释放掉 vfs 中所有资源。 本文在编写过程中不断修改调整,所以显的有些凌乱。发出来是为了抛砖引玉,好让 熟悉 AIX 内核的高手们多发表些文章。我也好从中学习。 参考资源: Kernel Extensions and Device Support Programming Concepts http://publib.boulder.ibm.com/infocenter/pseries/topic/com.ibm.aix.doc/aixprggd/kernextc/kernextc.pdf KDB Kernel debugger and kdb command http://publib.boulder.ibm.com/infocenter/pseries/topic/com.ibm.aix.doc/aixprggd/kdb/kdb.pdf 感谢 lgx 与我探讨。 WSS(Whitecell Security Systems),一个非营利性民间技术组织,致力于各种系统安全技术的研究。坚持传统的hacker精神,追求技术的精纯。 WSS 主页:http://www.whitecell.org/ WSS 论坛:http://www.whitecell.org/forums/ |
地主 发表时间: 05-11-30 12:43 |
![]() | 回复: TecZm [teczm] ![]() |
登录 |
Author: sinister 莫非楼猪是MJ |
B1层 发表时间: 05-11-30 13:42 |
|
20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon
粤ICP备05087286号