相关代码涉及主要涉及3个c文件,我们从底层开始
1. tee_ree_fs.c文件中
static const struct tee_fs_htree_storage ree_fs_storage_ops = {
.block_size = BLOCK_SIZE,
.rpc_read_init = ree_fs_rpc_read_init,
.rpc_read_final = tee_fs_rpc_read_final,
.rpc_write_init = ree_fs_rpc_write_init,
.rpc_write_final = tee_fs_rpc_write_final,
};
这几个接口就是通过rpc调用非安全世界(linux/android)的读写接口,数据一般都是在tee端加密后调用这些接口传递给非安全端,由非安全端进行存储。
2. fs_htree.c文件是REE FS文件系统的核心代码,其核心思想是通过数据备份以达到断电保护的作用,代码依赖上诉rpc读写接口
文件的结构:
/*
* File layout
* [demo with input:
* BLOCK_SIZE = 4096,
* node_size = 66,
* block_nodes = 4096/(66*2) = 31 ]
*
* phys block 0:
* tee_fs_htree_image vers 0 @ offs = 0
* tee_fs_htree_image vers 1 @ offs = sizeof(tee_fs_htree_image)
*
* phys block 1:
* tee_fs_htree_node_image 0 vers 0 @ offs = 0
* tee_fs_htree_node_image 0 vers 1 @ offs = node_size
* tee_fs_htree_node_image 1 vers 0 @ offs = node_size * 2
* tee_fs_htree_node_image 1 vers 1 @ offs = node_size * 3
* ...
* tee_fs_htree_node_image 30 vers 0 @ offs = node_size * 60
* tee_fs_htree_node_image 30 vers 1 @ offs = node_size * 61
*
* phys block 2:
* data block 0 vers 0
*
* phys block 3:
* data block 0 vers 1
*
* ...
* phys block 62:
* data block 30 vers 0
*
* phys block 63:
* data block 30 vers 1
*
* phys block 64:
* tee_fs_htree_node_image 31 vers 0 @ offs = 0
* tee_fs_htree_node_image 31 vers 1 @ offs = node_size
* tee_fs_htree_node_image 32 vers 0 @ offs = node_size * 2
* tee_fs_htree_node_image 32 vers 1 @ offs = node_size * 3
* ...
* tee_fs_htree_node_image 61 vers 0 @ offs = node_size * 60
* tee_fs_htree_node_image 61 vers 1 @ offs = node_size * 61
*
* phys block 65:
* data block 31 vers 0
*
* phys block 66:
* data block 31 vers 1
* ...
*/
node在存储的时候看似依次存储,实际在打开文件时会把node转成二叉树!node0是根节点,node1和node2是node0的子节点,node3和node4是node1的子节点,以此类推。假如有这么一个文件,他的所有数据都是存在vers0中的,现在我们更改其中的data block 6数据,我们会把数据加密然后存在data block 6 vers1中,同时修改对应node->node.flags = 1(表示使用data block 6 vers1而不是data block 6 vers0);若此时发生断电,再次打开改文件时依然使用的是data block 6 vers0中的数据,所有没有影响;若没有断电正常执行,程序会遍历htree中所有node,把更改的的node写入,这里也就是node6 vers1,node6发生改变他的父节点node2也要改变以指明子节点使用vers 1而不是使用vers 0,node2发生改变他的父节点node0也要改变,同时head中的计数器加一,这时数据完全写入成功,下次打开该文件时,通过计数器判断使用vers 1数据而不是vers 0数据。在计数器没有加一并写入之前发生断电,文件任然使用旧数据,文件数据不会发生更改,这就是ree fs文件系统断电保护大致原理。
主要函数
tee_fs_htree_open 读取文件,初始化htree
tee_fs_htree_write_block 加密数据并写入,同时设置node->node.flags ^= HTREE_NODE_COMMITTED_BLOCK;
node->block_updated = true; node->dirty = true; ht->dirty = true;
tee_fs_htree_read_block 读取并解密数据
tee_fs_htree_sync_to_storage 写入信息有更改的node,其父节点也会跟着修改并写入,计数器ht->head.counter++;写入head
3. tee_ree_fs.c文件中
static const struct tee_fs_dirfile_operations ree_dirf_ops = {
.open = ree_fs_open_primitive,
.close = ree_fs_close_primitive,
.read = ree_fs_read_primitive,
.write = ree_fs_write_primitive,
.commit_writes = ree_dirf_commit_writes,
};
这几个接口只是对htree接口的进一步封装而已
4. fs_dirfile.c
struct dirfile_entry {
TEE_UUID uuid;
uint8_t oid[TEE_OBJECT_ID_MAX_LEN];
uint32_t oidlen;
uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
uint32_t file_number;
};
/*
* File layout
*
* dirfile_entry.0
* ...
* dirfile_entry.n
*
* where n the index is disconnected from file_number in struct dirfile_entry
*/
目录文件,通过uuid和obj_id查找对应文件
5.
static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size,
struct tee_file_handle **fh)
{
TEE_Result res;
struct tee_fs_dirfile_dirh *dirh = NULL;
struct tee_fs_dirfile_fileh dfh;
mutex_lock(&ree_fs_mutex);
res = get_dirh(&dirh);//打开目录文件
if (res != TEE_SUCCESS)
goto out;
res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,//查找指定文件
&dfh);
if (res != TEE_SUCCESS)
goto out;
res = ree_fs_open_primitive(false, dfh.hash, &po->uuid, &dfh, fh);//打卡指定文件
if (res == TEE_ERROR_ITEM_NOT_FOUND) {
/*
* If the object isn't found someone has tampered with it,
* treat it as corrupt.
*/
res = TEE_ERROR_CORRUPT_OBJECT;
} else if (!res && size) {
struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
*size = tee_fs_htree_get_meta(fdp->ht)->length;
}
out:
if (res)
put_dirh(dirh, false);
mutex_unlock(&ree_fs_mutex);
return res;
}
static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
const void *buf, size_t len)
{
TEE_Result res;
struct tee_fs_dirfile_dirh *dirh = NULL;
struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
mutex_lock(&ree_fs_mutex);
res = get_dirh(&dirh);//打卡目录文件
if (res)
goto out;
res = ree_fs_write_primitive(fh, pos, buf, len);//写入指定文件数据,数据未生效
if (res)
goto out;
res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);//同步node和head信息,写入的数据生效
if (res)
goto out;
res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);//更新目录文件中指定文件的hash值,写入未生效
if (res)
goto out;
res = commit_dirh_writes(dirh);//同步目录文件的node及head信息,写入生效,ps:目录文件通过计数器来选择vers,数据文件 //通过hash来选择,所以此时断电,目录文件还是使用旧的hash,数据文件任然使用旧的数据
out:
put_dirh(dirh, res);
mutex_unlock(&ree_fs_mutex);
return res;
}