搜索exfat资料时,找到一篇老外的反编译exfat的文件,对我们学习代码很有帮助
文章名是reverse-engineering-microsoft-exfat-file-system_33274.pdf。
至于exfat的文件系统代码,可以从github上获取到,github地址是:https://github.com/dorimanx/exfat-nofuse
结合资料和code开启我的exfat之旅
获取到exfat-nofuse代码后,先看看代码中自带的README.md文档,check发现该开源代码是在linux系统中以命令modprobe exfat方式load the driver manually,然后使用标准的mount加载exfat文件系统。
modprobe exfat的控制主要是运行代码exfat_super.c中module_init(init_exfat_fs)的函数。
init_exfat_fs函数主要功能:
1. FsInit------exfat文件系统全局变量初始化以及一些文件系统定义边界检查
2.exfat_init_inodecache------exfat文件系统创建专属的高速缓冲
3.register_filesystem(&exfat_fs_type)------注册exfat文件系统到linux的VFS系统体系中
相关code如下:
static int __init init_exfat_fs(void)
{
int err;
printk(KERN_INFO "exFAT: Core Version %s\n", EXFAT_VERSION);
err = FsInit();
if (err) {
if (err == FFS_MEMORYERR)
return -ENOMEM;
else
return -EIO;
}
err = exfat_init_inodecache();
if (err)
goto out;
err = register_filesystem(&exfat_fs_type);
if (err)
goto out;
return 0;
out:
FsShutdown();
return err;
}
这里需要注意的数据结构如下:
static struct file_system_type exfat_fs_type = {
.owner = THIS_MODULE,
.name = "exfat",
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
.get_sb = exfat_get_sb,
#else
.mount = exfat_fs_mount,//根据目前的kernel版本,我们要使用这个函数probe文件系统(mount命令加载exfat时会使用调用到这个最终实际加载的文件系统函数)
#endif
#if EXFAT_CONFIG_KERNEL_DEBUG
.kill_sb = exfat_debug_kill_sb,
#else
.kill_sb = kill_block_super,
#endif
.fs_flags = FS_REQUIRES_DEV,
};
这个数据结构中
.name = "exfat",-------------标示新添加的文件系统名字,比如ext4,ext3......
.mount = exfat_fs_mount,------------这个函数会注册到文件系统中,在mount时调用加载,后续详细介绍。
上一章强调了exfat_fs_mount在linux mount的时候会调用到,本章节将详细描述这个函数的执行过程。
在安装exFAT(mount)的时候,存放exFAT文件系统的磁盘分区上的大部分数据结构的信息都会被拷贝到RAM(操作系统内存)中,从而使得内核避免了许多后续的操作,变得简单。由于exFAT磁盘数据结构都保存在exFAT磁盘分区的块中,所以当需要经常更新一些数据结构时,内核会利用页高速缓存来实现。
在实际mount一个文件系统的时候,exFAT文件系统依赖于虚拟文件系统的一个标准函数(mount_bdev)来实现文件系统信息的装载。
在我们的exfat-nofuse代码中,函数exfat_fs_mount调用mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super);来实现,在mount_bdev函数中通过exfat_fill_super函数来填充VFS中一个超级块对象,代码实现如下:
static struct dentry *exfat_fs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name,
void *data) {
return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super);
}
现在我们来详细描述exfat_fill_super函数的功能:
1.分配exfat的super_block的私有数据(struct exfat_sb_info *sbi)需要用到的内存,并进行初始化
struct exfat_sb_info *sbi;
sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);
sb->s_fs_info = sbi;
mutex_init(&sbi->s_lock);
................
parse_options(data, silent, &debug, &sbi->options);/*解析mount exfat时传入的参数*/
2.初始化exfat的super_block的数据
sb->s_fs_info = sbi;
sb->s_flags |= MS_NODIRATIME;
sb->s_magic = EXFAT_SUPER_MAGIC;
sb->s_op = &exfat_sops;
sb->s_d_op = &exfat_ci_dentry_ops;
这里需要注意exfat_sops和exfat_ci_dentry_ops的实现内容
const struct super_operations exfat_sops = {
.alloc_inode = exfat_alloc_inode,
.destroy_inode = exfat_destroy_inode,
.write_inode = exfat_write_inode,
.evict_inode = exfat_evict_inode,
.put_super = exfat_put_super,
.sync_fs = exfat_sync_fs,
.statfs = exfat_statfs,
.remount_fs = exfat_remount,
.show_options = exfat_show_options,
};
static const struct dentry_operations exfat_ci_dentry_ops = {
.d_revalidate = exfat_revalidate_ci,
.d_hash = exfat_d_hashi,
.d_compare = exfat_cmpi,
};
3.FsMountVol函数加载exfat分区的Volume Boot Record(VBR)数据以及exfat特定分区数据,然后初始化exfat的super_block数据以及super_block的私有数据以及exfat文件系统必要的数据(后面专门一章再详细描述这个函数的实现)
4.exfat_hash_init函数初始化inode的hash table
5.在exfat文件系统中做codepage的初始化
6.创建exfat的root inode以及初始化,并把root inode挂载到hash table
注意exfat root inode的初始化
inode->i_op = &exfat_dir_inode_operations;
inode->i_fop = &exfat_dir_operations;
const struct inode_operations exfat_dir_inode_operations = {
.create = exfat_create,
.lookup = exfat_lookup,
.unlink = exfat_unlink,
.symlink = exfat_symlink,
.mkdir = exfat_mkdir,
.rmdir = exfat_rmdir,
.rename = exfat_rename,
.setattr = exfat_setattr,
.getattr = exfat_getattr,
#ifdef CONFIG_EXFAT_VIRTUAL_XATTR
.setxattr = exfat_setxattr,
.getxattr = exfat_getxattr,
.listxattr = exfat_listxattr,
.removexattr = exfat_removexattr,
#endif
};
const struct file_operations exfat_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.readdir = exfat_readdir,
.unlocked_ioctl = exfat_generic_ioctl,
.fsync = exfat_file_fsync,
};
7. 通过root inode创建root dentry
8. 完成
code如下:
static int exfat_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *root_inode = NULL;
struct exfat_sb_info *sbi;
int debug, ret;
long error;
char buf[50];
exfat_mnt_msg(sb, 1, 0, "trying to mount...");
/*
* GFP_KERNEL is ok here, because while we do hold the
* supeblock lock, memory pressure can't call back into
* the filesystem, since we're only just about to mount
* it and have no inodes etc active!
*/
sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);/*分配exfat私有的super_block的数据需要用到的内存*/
if (!sbi) {
exfat_mnt_msg(sb, 1, 0, "failed to mount! (ENOMEM)");
return -ENOMEM;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
mutex_init(&sbi->s_lock);
#endif
sb->s_fs_info = sbi;
sb->s_flags |= MS_NODIRATIME;
sb->s_magic = EXFAT_SUPER_MAGIC;
sb->s_op = &exfat_sops;
error = parse_options(data, silent, &debug, &sbi->options);/*解析mount exfat时传入的参数*/
if (error)
goto out_fail;
setup_dops(sb);
error = -EIO;
sb_min_blocksize(sb, 512);
sb->s_maxbytes = 0x7fffffffffffffffLL; /* maximum file size */
ret = FsMountVol(sb);//wangxf14_study
if (ret) {
if (!silent)
printk(KERN_ERR "[EXFAT] FsMountVol failed\n");
goto out_fail;
}
/* set up enough so that it can read an inode */
exfat_hash_init(sb);
/*
* The low byte of FAT's first entry must have same value with
* media-field. But in real world, too many devices is
* writing wrong value. So, removed that validity check.
*
* if (FAT_FIRST_ENT(sb, media) != first)
*/
/* codepage is not meaningful in exfat */
error = -EINVAL;
sprintf(buf, "cp%d", sbi->options.codepage);
sbi->nls_disk = load_nls(buf);
if (!sbi->nls_disk) {
printk(KERN_ERR "[EXFAT] Codepage %s not found\n", buf);
goto out_fail2;
}
sbi->nls_io = load_nls(sbi->options.iocharset);
if (!sbi->nls_io) {
printk(KERN_ERR "[EXFAT] IO charset %s not found\n",
sbi->options.iocharset);
goto out_fail2;
}
error = -ENOMEM;
root_inode = new_inode(sb);/*创建一个inode节点,这个函数就是在fs/inode.c中的new_inode函数*/
if (!root_inode)
goto out_fail2;
root_inode->i_ino = EXFAT_ROOT_INO;
root_inode->i_version = 1;
error = exfat_read_root(root_inode); //wangxf14_study
if (error < 0)
goto out_fail2;
error = -ENOMEM;
exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos);
insert_inode_hash(root_inode);/*将这个新的inode内存节点挂在hashtable中,这个函数在fs/inode.c中的insert_inode_hash函数*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
sb->s_root = d_make_root(root_inode);
#else
sb->s_root = d_alloc_root(root_inode);
#endif
if (!sb->s_root) {
printk(KERN_ERR "[EXFAT] Getting the root inode failed\n");
goto out_fail2;
}
exfat_mnt_msg(sb, 1, 0, "mounted successfully!");
return 0;
out_fail2:
FsUmountVol(sb);
out_fail:
exfat_mnt_msg(sb, 1, 0, "failed to mount!");
if (root_inode)
iput(root_inode);
if (sbi->nls_io)
unload_nls(sbi->nls_io);
if (sbi->nls_disk)
unload_nls(sbi->nls_disk);
if (sbi->options.iocharset != exfat_default_iocharset)
kfree(sbi->options.iocharset);
sb->s_fs_info = NULL;
kfree(sbi);
return error;
}