FAT 文件系统代码分析--文件系统挂载篇

1 篇文章 1 订阅

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.cmsdos文件系统咯
  • namei_vfat.cvfat文件系统
  • nfs.c:网络的nfs文件系统

猜测完前面几个源文件的作用后,我们先到每个源文件下查看一下一些重要的结构体,应该会有不错的收获。

源文件下重要的结构体(实例)

cache.c

cache.c这个文件下,只有两个结构体,一个是fat_cache,另一个是fat_cache_idfat_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_opsfat16_opsfat32_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_opsvfat_dentry_ops结构体应该是用来加速寻找目录项的;vfat_dir_inode_operations结构体则是关于vfat文件系统的文件创建、查找、删除、目录的创建、删除和重命名(文件以及目录)、属性的设置与获取;vfat_fs_type则是用于注册文件系统的一个结构体。

nfs.c

nfs.c中,就明显与前面两个不同,只提供了两个结构体fat_export_opsfat_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()函数关注下面的赋值与回写:

在这里插入图片描述

到这里整个文件系统算是注册完成了。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值