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

FAT 文件系统代码分析–文件系统挂载篇FAT文件系统代码分析–文件系统挂载篇当前内核版本:Linux-5.9。文章目录FAT文件系统代码分析文件系统挂载篇代码大纲分析源文件下重要的结构体(实例)cache.cdir.cfatent.cfile.cinode.cmisc.cnamei_msdos.cnamei_vfat.cnfs.cFAT文件系统注册vfat层file_system_typesuper_blockinode层fat_fill_super-1fat_bios_param_blockmsdos_sb_infofat_sop

大家好,欢迎来到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.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()函数关注下面的赋值与回写:

在这里插入图片描述

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

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/22814.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信