大家好,欢迎来到IT知识分享网。
FAT 文件系统代码分析–文件系统挂载篇
当前内核版本:Linux-5.9。
先上一个整理好的图:关联结构体file、inode、superblock、filesystem type以及各自的操作集合。
代码大纲分析
我们到Linux
内核代码的fs/fat
下查看一下:
tree
.
├── Kconfig
├── Makefile
├── cache.c
├── dir.c
├── fat.h
├── fatent.c
├── file.c
├── inode.c
├── misc.c
├── namei_msdos.c
├── namei_vfat.c
└── nfs.c
0 directories, 12 files
可以看到FAT
文件系统代码组织应该算是比较简单的。我们来看一下Makefile
:
#
# Makefile for the Linux fat filesystem support.
#
obj-$(CONFIG_FAT_FS) += fat.o
obj-$(CONFIG_VFAT_FS) += vfat.o
obj-$(CONFIG_MSDOS_FS) += msdos.o
fat-y := cache.o dir.o fatent.o file.o inode.o misc.o nfs.o
vfat-y := namei_vfat.o
msdos-y := namei_msdos.o
再来看一下内核的config
文件:
#
# DOS/FAT/NT Filesystems
#
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# CONFIG_FAT_DEFAULT_UTF8 is not set
# CONFIG_NTFS_FS is not set
可以看到上面的配置都是有配置的,所以说在fs/fat
下的源码基本都是被编译进内核的。
我们先从文件名上猜测一下每个源文件都是干什么的:
cache.c
:名字很明显,就是cache
,至于什么是cache
,咱也说不清,咱也不敢说,过dir.c
:嗯,这个应该就是和目录相关的操作了fatent.c
:这个应该就是和目录项相关的了,每种文件系统在磁盘上的表现形式不同,这个应该是和磁盘上的存储表现形式比较相关。file.c
:看名字就应该是和文件相关的操作inode.c
:和inode
相关的操作咯misc.c
:看名字就是大杂烩,应该是会提供一些各式各样的公用的借口供其他使用namei_msdos.c
:msdos
文件系统咯namei_vfat.c
:vfat
文件系统nfs.c
:网络的nfs
文件系统
猜测完前面几个源文件的作用后,我们先到每个源文件下查看一下一些重要的结构体,应该会有不错的收获。
源文件下重要的结构体(实例)
cache.c
cache.c
这个文件下,只有两个结构体,一个是fat_cache
,另一个是fat_cache_id
,fat_cache
应该是用来组织cache
的,而fat_cache_id
应该是用来加快寻找的。然后还有一个静态变量fat_cache_cachep
,记录申请的cache
的地址。这个cache.c
搞不懂,多说多错,有机会慢慢学习一下。
/* this must be > 0. */
#define FAT_MAX_CACHE 8
struct fat_cache {
struct list_head cache_list;
int nr_contig; /* number of contiguous clusters */
int fcluster; /* cluster number in the file. */
int dcluster; /* cluster number on disk. */
};
struct fat_cache_id {
unsigned int id;
int nr_contig;
int fcluster;
int dcluster;
};
static struct kmem_cache *fat_cache_cachep;
dir.c
dir.c
:应该就是和目录相关的操作,文件中有一个比较重要的结构体fat_dir_operations
:
const struct file_operations fat_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = fat_readdir,
.unlocked_ioctl = fat_dir_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fat_compat_dir_ioctl,
#endif
.fsync = fat_file_fsync,
};
这个结构体的.llseek
、.read
是指向的通用的接口,而剩余的.iterate_shared
、.unlocked_ioctl
、.compat_ioctl
、.fsync
等接口都是FAT
自身另外实现的。
fatent.c
fatent.c
应该就是和目录项操作相关的了,查看了一下,发现有fat12_ops
、fat16_ops
和fat32_ops
,前面两个已经很少使用了,重点关注fat32_ops
就可以了:
struct fatent_operations {
void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
void (*ent_set_ptr)(struct fat_entry *, int);
int (*ent_bread)(struct super_block *, struct fat_entry *,
int, sector_t);
int (*ent_get)(struct fat_entry *);
void (*ent_put)(struct fat_entry *, int);
int (*ent_next)(struct fat_entry *);
};
static const struct fatent_operations fat32_ops = {
.ent_blocknr = fat_ent_blocknr,
.ent_set_ptr = fat32_ent_set_ptr,
.ent_bread = fat_ent_bread,
.ent_get = fat32_ent_get,
.ent_put = fat32_ent_put,
.ent_next = fat32_ent_next,
};
file.c
file.c
是和文件相关的操作,有以下两个结构体:
const struct file_operations fat_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.mmap = generic_file_mmap,
.release = fat_file_release,
.unlocked_ioctl = fat_generic_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fat_generic_compat_ioctl,
#endif
.fsync = fat_file_fsync,
.splice_read = generic_file_splice_read,
.fallocate = fat_fallocate,
};
const struct inode_operations fat_file_inode_operations = {
.setattr = fat_setattr,
.getattr = fat_getattr,
};
至于这两个结构体什么时候被注册、什么时候被使用我们在后续中再继续分析。
可以看到.llseek
、.read_iter
、.write_iter
、.mmap
、.splice_read
这些接口是使用的通用的接口函数,.release
、.unlocked_ioctl
、.compat_ioctl
、.fsync
、.fallocate
都是FAT
自身实现的。
至于获得和设置属性的,也都是FAT
自己实现的。
inode.c
关于inode
的操作,这里的结构体就大部分是FAT
本身自己实现的了:
static const struct address_space_operations fat_aops = {
.readpage = fat_readpage,
.readpages = fat_readpages,
.writepage = fat_writepage,
.writepages = fat_writepages,
.write_begin = fat_write_begin,
.write_end = fat_write_end,
.direct_IO = fat_direct_IO,
.bmap = _fat_bmap
};
static const struct super_operations fat_sops = {
.alloc_inode = fat_alloc_inode,
.destroy_inode = fat_destroy_inode,
.write_inode = fat_write_inode,
.evict_inode = fat_evict_inode,
.put_super = fat_put_super,
.statfs = fat_statfs,
.remount_fs = fat_remount,
.show_options = fat_show_options,
};
fat_aops
对应的就是地址空间(address_space
)的相关操作了,fat_sops
则是关于超级块的相关操作。
misc.c
这里面没有定义相关的结构体以及结构体实例,后面在讲这个文件时再分析。
namei_msdos.c
namei_msdos.c
文件应该是msdos
文件系统的实现。
这个文件有以下几个结构体:
static const struct dentry_operations msdos_dentry_operations = {
.d_hash = msdos_hash,
.d_compare = msdos_cmp,
};
static const struct inode_operations msdos_dir_inode_operations = {
.create = msdos_create,
.lookup = msdos_lookup,
.unlink = msdos_unlink,
.mkdir = msdos_mkdir,
.rmdir = msdos_rmdir,
.rename = msdos_rename,
.setattr = fat_setattr,
.getattr = fat_getattr,
};
static struct file_system_type msdos_fs_type = {
.owner = THIS_MODULE,
.name = "msdos",
.mount = msdos_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("msdos");
msdos_dentry_operations
结构体应该是用来加速寻找目录项的;msdos_dir_inode_operations
结构体则是关于msdos
文件系统的文件创建、查找、删除、目录的创建、删除和重命名(文件以及目录)、属性的设置与获取;msdos_fs_type
则是用于注册文件系统的一个结构体。
namei_vfat.c
同理,namei_vfat.c
文件应该是vfat
文件系统的实现。
有以下结构体:
static const struct dentry_operations vfat_ci_dentry_ops = {
.d_revalidate = vfat_revalidate_ci,
.d_hash = vfat_hashi,
.d_compare = vfat_cmpi,
};
static const struct dentry_operations vfat_dentry_ops = {
.d_revalidate = vfat_revalidate,
.d_hash = vfat_hash,
.d_compare = vfat_cmp,
};
static const struct inode_operations vfat_dir_inode_operations = {
.create = vfat_create,
.lookup = vfat_lookup,
.unlink = vfat_unlink,
.mkdir = vfat_mkdir,
.rmdir = vfat_rmdir,
.rename = vfat_rename,
.setattr = fat_setattr,
.getattr = fat_getattr,
};
static struct file_system_type vfat_fs_type = {
.owner = THIS_MODULE,
.name = "vfat",
.mount = vfat_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
自然,这些结构体作用与在namei_msdos.c
中的类似,vfat_ci_dentry_ops
和vfat_dentry_ops
结构体应该是用来加速寻找目录项的;vfat_dir_inode_operations
结构体则是关于vfat
文件系统的文件创建、查找、删除、目录的创建、删除和重命名(文件以及目录)、属性的设置与获取;vfat_fs_type
则是用于注册文件系统的一个结构体。
nfs.c
在nfs.c
中,就明显与前面两个不同,只提供了两个结构体fat_export_ops
和fat_export_ops_nostale
,并且里面的成员也只有寥寥几个:
const struct export_operations fat_export_ops = {
.fh_to_dentry = fat_fh_to_dentry,
.fh_to_parent = fat_fh_to_parent,
.get_parent = fat_get_parent,
};
const struct export_operations fat_export_ops_nostale = {
.encode_fh = fat_encode_fh_nostale,
.fh_to_dentry = fat_fh_to_dentry_nostale,
.fh_to_parent = fat_fh_to_parent_nostale,
.get_parent = fat_get_parent,
};
FAT文件系统注册
vfat层
接下来就看一下文件系统的注册过程。
在“namei_vfat.c文件
中,我们可以看到下面的这段代码:
可以看到,注册vfat
文件系统只需要调用register_filesystem
函数并且传入struct file_system_type
类型的vfat_fs_type
参数即可完成注册,至于内核如何完成文件系统的注册,我们后面有机会再讲,这里我们主要关注vfat
文件系统本身的行为即可。反注册则调用unregister_filesystem
函数传入vfat_fs_type
参数。
我们来看一下vfat_fs_type
这个结构体,里面的.mount
成员指向的是vfat_mount()
函数,每个文件系统都有自身的特性,挂载时需要根据自身的特性来实现不同的挂载函数。我们这里来看一下vfat_mount()
函数,这个函数也是调用的内核的一个挂载块设备的接口,但我们注意到最后一个参数是vfat_fill_super
,这个是挂载时用来填充文件系统超级块的一个接口。我们看一下vfat_fill_super()
,这里看到vfat_fill_super()
调用了inode.c
文件里面的fat_fill_super()
函数,同时也传入了一个setup
接口,这个setup
接口在给超级块的dir_ops
以及s_d_op
赋值,即建立当前超级块相关的操作集合索引。
在这里,我们再回头看一下,文件系统结构体struct file_system_type
以及超级块struct super_block
的相关内容。
file_system_type
struct file_system_type
:
super_block
struct super_block
:
inode层
上面注册时最后调用的函数是fat_fill_super()
,这个函数比较长,分为8段解析,在过程中参杂着FAT文件系统协议相关的内容。
fat_fill_super-1
fat_fill_super-1
:涉及一个启动扇区信息,以及操作集的赋值。
这里一开始就定义了一个变量struct fat_bios_param_block bpb
,这个是用来记录启动扇区相关的信息,我们来看一下它里面具体的内容:
fat_bios_param_block
这个信息为什么这样定义,那么就是协议的内容了,协议上,启动扇区长这样:
msdos_sb_info
还有另一个结构体struct msdos_sb_info
:
fat_sops
fat_sops
是关于inode的申请、释放、回显等操作,后面再详细看里面的函数。
fat_export_ops
fat_export_ops
关于网络文件系统的操作暂时没有了解,略过。
fat_fill_super-2
接下来看第二段fat_fill_super-2
:涉及挂载参数解析,读启动扇区并解析记录启动扇区的内容。
fat_boot_sector
在1656
行可以看到,一个结构体struct fat_boot_sector
,长这样:实际是与协议上一一对应的。
fat_read_bpb()
函数就是简单的赋值过程:
fat_fill_super-3
接下来看第三段fat_fill_super-3
:赋值一些fat文件系统相关的参数。
fat_fill_super-4
接下来看第三段fat_fill_super-4
:FAT32根目录簇赋值,读取FS INFO信息。
fat_boot_fsinfo
结构体struct fat_boot_fsinfo
,里面记录着两个主要信息,空闲簇数量以及最近在使用的空闲簇。
在协议中有定义:
fat_fill_super-5
接下来看第三段fat_fill_super-5
:计算目录开始存放的地址以及数据开始存放的地址。
msdos_dir_entry
在上面的1761
行,有一个目录结构体:
fat_fill_super-6
接下来看第三段fat_fill_super-6
:对空闲簇数量进行校验,哈希表初始化,以及根据不同的文件系统赋值操作对应目录项的集合。
fat_ent_access_init()
:对目录项操作集合赋值。
fat32_ops()
:这里就是内部目录项与磁盘数据交互的协助函数,具体函数后续再看。
fat_fill_super-7
接下来看第三段fat_fill_super-7
:主要是申请了三个inode
节点,并初始化root
节点相关的内容。
fat_read_root()
里面主要关注了一个文件操作集合的赋值:
具体的文件操作集合fat_dir_operations
如下:
fat_fill_super-8
接下来看第三段fat_fill_super-8
:设置挂载状态,并回写到磁盘,以及最后的出错处理。
fat_set_state()
函数关注下面的赋值与回写:
到这里整个文件系统算是注册完成了。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/22814.html