exFAT文件系统2

搜索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;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值