内核中实现的一套加密文件系统。
在VFS层和实际底层文件系统(如ext4、vfat)之间,嵌入一层ecryptfs。在该层完成文件的加解密处理。
从而在通用文件系统的基础上,叠加该层实现文件系统的加密,而不是单纯去做一套加密文件系统,这样更加灵活,但堆叠式的文件系统会对性能带来一定影响。
- root@xxzh:~/src/kernel/fs/ecryptfs# ls
- built-in.o debug.c dentry.o ecryptfs.mod.c file.c inode.o keystore.o main.c messaging.c miscdev.o modules.order super.c
- crypto.c debug.o ecryptfs_kernel.h ecryptfs.mod.o file.o Kconfig kthread.c main.o messaging.o mmap.c read_write.c super.o
- crypto.o dentry.c ecryptfs.ko ecryptfs.o inode.c keystore.c kthread.o Makefile miscdev.c mmap.o read_write.o
- root@xxzh:~/src/kernel/fs/ecryptfs#
本文不再分析如何在ubunut上如何使用ecryptfs,网上教程比较多,在ubunut上基本安装后配置一下就可以达到效果。
在内核安装ecryptfs模块之后,通过mount的时候指定为ecryptfs类型。如mmcblk0p1卡以ext4
类型挂载在sd下,在sd下创建ecryptfs目录。然后挂载
mount -t ecryptfs /sd/ecryptfs /sd/ecryptfs ...
...中可以是一些加密参数。之后在/sd/ecryptfs目录下创建文件,读取文件都将经过加解密处理。最终磁盘上的文件都是经过加密处理的。访问时候需要先挂载。
本文重点说明ecryptfs的实现机制,以及如何通过mount嵌入ecryptfs层,并最终分析文件的读写过程。其中,并不重点分析加密过程,ecryptfs也依赖内核的加密模块进行加密。
ecryptfs_alloc_inode的时候,通过kmem_cache_alloc创建,并返回inode_info->vfs_inode。
- struct ecryptfs_inode_info {
- struct inode vfs_inode;
- struct inode *wii_inode;
- struct mutex lower_file_mutex;
- atomic_t lower_file_count;
- struct file *lower_file;
- struct ecryptfs_crypt_stat crypt_stat;
- };
kmem_cache_zalloc创建,是sb的私有数据。sb->s_fs_info = sb_info
- /* superblock private data. */
- struct ecryptfs_sb_info {
- struct super_block *wsi_sb;
- struct ecryptfs_mount_crypt_stat mount_crypt_stat;
- struct backing_dev_info bdi;
- };
核心函数调用如下:
- static int __init ecryptfs_init(void)
- {
- int rc;
-
- rc = ecryptfs_init_kmem_caches();
- #if 0
- rc = do_sysfs_registration();
- #endif
- rc = ecryptfs_init_kthread();
- #if 0
- rc = ecryptfs_init_messaging();
- #endif
- rc = ecryptfs_init_crypto();
- rc = register_filesystem(&ecryptfs_fs_type);
-
- return rc;
- }
ecryptfs_init_kmem_caches中,使用kmem_cache_create创建了各种slab缓存,如inode的缓存,用户在文件产生、读写过程中使用,毕竟文件访问要讲究效率,所以用了slab。
do_sysfs_registration注册sys下的辅助节点,不做分析
ecryptfs_init_kthread创建了一个内核线程,后期用于绕过文件读写权限
ecryptfs_init_messaging创建了一个辅助字符设备,不做分析
ecryptfs_init_crypto 加密key链表初始化,不做分析
register_filesystem注册ecryptfs文件系统,因为挂载的时候-t 指定了ecryptfs,所有一定有register_filesystem来注册这个文件系统,只是这个文件系统和ext4等不是并列关系,而是上下关系,最终在内核是VFS-ECRYPTFS-EXT4。
因此,驱动入口函数,主要任务是创建slab缓存,并注册文件系统。
注册的ecryptfs文件系统如下,可见创建一个文件系统,最重要的指定其名字,以及该名字的文件系统mount时候的调用函数ecryptfs_mount。
- static struct file_system_type ecryptfs_fs_type = {
- .owner = THIS_MODULE,
- .name = "ecryptfs",
- .mount = ecryptfs_mount,
- .kill_sb = ecryptfs_kill_block_super,
- .fs_flags = 0
- };
- MODULE_ALIAS_FS("ecryptfs");
mount函数调用如下
- static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *raw_data)
- {
- struct super_block *s;
- struct ecryptfs_sb_info *sbi;
- struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
- struct ecryptfs_dentry_info *root_info;
- const char *err = "Getting sb failed";
- struct inode *inode;
- struct path path;
- uid_t check_ruid;
- int rc;
-
- //创建超级块缓存
- sbi = kmem_cache_zalloc(ecryptfs_sb_info_cache, GFP_KERNEL);
-
- //解析参数
- rc = ecryptfs_parse_options(sbi, raw_data, &check_ruid);
-
- //分配一个super_block
- s = sget(fs_type, NULL, set_anon_super, flags, NULL);
-
- //回写相关
- rc = bdi_setup_and_register(&sbi->bdi, "ecryptfs");
-
- ecryptfs_set_superblock_private(s, sbi);
- s->s_bdi = &sbi->bdi;
-
- /* ->kill_sb() will take care of sbi after that point */
- sbi = NULL;
- //挂载文件系统的操作函数
- s->s_op = &ecryptfs_sops;
- s->s_d_op = &ecryptfs_dops;
-
- rc = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
-
- //防止重复mount
- if (path.dentry->d_sb->s_type == &ecryptfs_fs_type) {
- goto out_free;
- }
-
- //挂载权限检查
- if (check_ruid && !uid_eq(d_inode(path.dentry)->i_uid, current_uid())) {
- goto out_free;
- }
-
- //底下一层文件系统的sb
- ecryptfs_set_superblock_lower(s, path.dentry->d_sb);
-
- s->s_maxbytes = path.dentry->d_sb->s_maxbytes;
- s->s_blocksize = path.dentry->d_sb->s_blocksize;
- s->s_magic = ECRYPTFS_SUPER_MAGIC;
- s->s_stack_depth = path.dentry->d_sb->s_stack_depth + 1;
-
- inode = ecryptfs_get_inode(d_inode(path.dentry), s);
-
- //设置全局根目录/
- s->s_root = d_make_root(inode);
- if (!s->s_root) {
- rc = -ENOMEM;
- goto out_free;
- }
-
- root_info = kmem_cache_zalloc(ecryptfs_dentry_info_cache, GFP_KERNEL);
-
- /* ->kill_sb() will take care of root_info */
- //设置文件系统的私有数据
- ecryptfs_set_dentry_private(s->s_root, root_info);
- root_info->lower_path = path;
- s->s_flags |= MS_ACTIVE;
-
- return dget(s->s_root);
- }
kmem_cache_zalloc创建超级块ecryptfs_sb_info,作为超级块的信息sb->s_fs_info = sb_info。
ecryptfs_parse_options解析mount时候传入的加密信息,不做分析
sget创建一个超级块,每个文件系统都需要有的,存在文件系统相关信息。
bdi_setup_and_register,bdi回写相关设置初始化
kern_path获取挂载路径信息,存储在path
ecryptfs_set_superblock_lower将ecryptfs和底层文件系统关联。
ecryptfs_get_inode获取挂载目录的ecryptfs层的inode。
执行touch text.txt的时候,会创建这个文件,从上层看会调用VFS层的vfs_create,之后进入ecryptfs层,之后再到具体的文件系统层。
VFS层对下层的调用:
如上,我们在根目录下创建新文件,因此是根据挂载目录的inode里面的方法进行create。那么挂载目录的inode在哪里设置呢。在mount的时候设置的,如下:
通过上面,在vfs_create的时候,调用了ecryptfs_dir_iops中的ecryptfs_create函数创建。
创建文件的主要工作:
根据挂载时候给mount点inode的设置,调用其设置好的create函数,产生新文件的inode,并在新文件开始的前8K空间中存储加解密信息。并为新文件初始化并设置了inode,为后续读写准好了准备。
还是定位到VFS层到ecryptfs的入口。写之前肯定还有open操作。
写加密文件总结:
以page为单位进行加密处理,分配一个page进行加密,加密后写入底层文件。全程依赖VFS层inode,并通过该inode找到底层inode后进行最后写入。
存储介质中ext4的文件排布_【星星之火】的博客-CSDN博客
在获取inode的时候,通过ecryptfs_inode_set设置了indo中的操作函数。