blob: 6ad2cb6bb282ad1ef8366e79f9eb482b4f8d0ea7 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2015, Linaro Limited
*/
#include <assert.h>
#include <kernel/mutex.h>
#include <kernel/panic.h>
#include <kernel/thread.h>
#include <mempool.h>
#include <mm/core_memprot.h>
#include <mm/tee_pager.h>
#include <optee_rpc_cmd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string_ext.h>
#include <string.h>
#include <sys/queue.h>
#include <tee/fs_dirfile.h>
#include <tee/fs_htree.h>
#include <tee/tee_fs.h>
#include <tee/tee_fs_rpc.h>
#include <tee/tee_pobj.h>
#include <trace.h>
#include <utee_defines.h>
#include <util.h>
#define BLOCK_SHIFT 12
#define BLOCK_SIZE (1 << BLOCK_SHIFT)
struct tee_fs_fd {
struct tee_fs_htree *ht;
int fd;
struct tee_fs_dirfile_fileh dfh;
const TEE_UUID *uuid;
};
struct tee_fs_dir {
struct tee_fs_dirfile_dirh *dirh;
int idx;
struct tee_fs_dirent d;
const TEE_UUID *uuid;
};
static int pos_to_block_num(int position)
{
return position >> BLOCK_SHIFT;
}
static struct mutex ree_fs_mutex = MUTEX_INITIALIZER;
static void *get_tmp_block(void)
{
return mempool_alloc(mempool_default, BLOCK_SIZE);
}
static void put_tmp_block(void *tmp_block)
{
mempool_free(mempool_default, tmp_block);
}
static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, size_t pos,
const void *buf, size_t len)
{
TEE_Result res;
size_t start_block_num = pos_to_block_num(pos);
size_t end_block_num = pos_to_block_num(pos + len - 1);
size_t remain_bytes = len;
uint8_t *data_ptr = (uint8_t *)buf;
uint8_t *block;
struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
/*
* It doesn't make sense to call this function if nothing is to be
* written. This also guards against end_block_num getting an
* unexpected value when pos == 0 and len == 0.
*/
if (!len)
return TEE_ERROR_BAD_PARAMETERS;
block = get_tmp_block();
if (!block)
return TEE_ERROR_OUT_OF_MEMORY;
while (start_block_num <= end_block_num) {
size_t offset = pos % BLOCK_SIZE;
size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE);
if (size_to_write + offset > BLOCK_SIZE)
size_to_write = BLOCK_SIZE - offset;
if (start_block_num * BLOCK_SIZE <
ROUNDUP(meta->length, BLOCK_SIZE)) {
res = tee_fs_htree_read_block(&fdp->ht,
start_block_num, block);
if (res != TEE_SUCCESS)
goto exit;
} else {
memset(block, 0, BLOCK_SIZE);
}
if (data_ptr)
memcpy(block + offset, data_ptr, size_to_write);
else
memset(block + offset, 0, size_to_write);
res = tee_fs_htree_write_block(&fdp->ht, start_block_num,
block);
if (res != TEE_SUCCESS)
goto exit;
if (data_ptr)
data_ptr += size_to_write;
remain_bytes -= size_to_write;
start_block_num++;
pos += size_to_write;
}
if (pos > meta->length) {
meta->length = pos;
tee_fs_htree_meta_set_dirty(fdp->ht);
}
exit:
if (block)
put_tmp_block(block);
return res;
}
static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx,
uint8_t vers, size_t *offs, size_t *size)
{
const size_t node_size = sizeof(struct tee_fs_htree_node_image);
const size_t block_nodes = BLOCK_SIZE / (node_size * 2);
size_t pbn;
size_t bidx;
assert(vers == 0 || vers == 1);
/*
* 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
* ...
*/
switch (type) {
case TEE_FS_HTREE_TYPE_HEAD:
*offs = sizeof(struct tee_fs_htree_image) * vers;
*size = sizeof(struct tee_fs_htree_image);
return TEE_SUCCESS;
case TEE_FS_HTREE_TYPE_NODE:
pbn = 1 + ((idx / block_nodes) * block_nodes * 2);
*offs = pbn * BLOCK_SIZE +
2 * node_size * (idx % block_nodes) +
node_size * vers;
*size = node_size;
return TEE_SUCCESS;
case TEE_FS_HTREE_TYPE_BLOCK:
bidx = 2 * idx + vers;
pbn = 2 + bidx + bidx / (block_nodes * 2 - 1);
*offs = pbn * BLOCK_SIZE;
*size = BLOCK_SIZE;
return TEE_SUCCESS;
default:
return TEE_ERROR_GENERIC;
}
}
static TEE_Result ree_fs_rpc_read_init(void *aux,
struct tee_fs_rpc_operation *op,
enum tee_fs_htree_type type, size_t idx,
uint8_t vers, void **data)
{
struct tee_fs_fd *fdp = aux;
TEE_Result res;
size_t offs;
size_t size;
res = get_offs_size(type, idx, vers, &offs, &size);
if (res != TEE_SUCCESS)
return res;
return tee_fs_rpc_read_init(op, OPTEE_RPC_CMD_FS, fdp->fd,
offs, size, data);
}
static TEE_Result ree_fs_rpc_write_init(void *aux,
struct tee_fs_rpc_operation *op,
enum tee_fs_htree_type type, size_t idx,
uint8_t vers, void **data)
{
struct tee_fs_fd *fdp = aux;
TEE_Result res;
size_t offs;
size_t size;
res = get_offs_size(type, idx, vers, &offs, &size);
if (res != TEE_SUCCESS)
return res;
return tee_fs_rpc_write_init(op, OPTEE_RPC_CMD_FS, fdp->fd,
offs, size, data);
}
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,
};
static TEE_Result ree_fs_ftruncate_internal(struct tee_fs_fd *fdp,
tee_fs_off_t new_file_len)
{
TEE_Result res;
struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
if ((size_t)new_file_len > meta->length) {
size_t ext_len = new_file_len - meta->length;
res = out_of_place_write(fdp, meta->length, NULL, ext_len);
if (res != TEE_SUCCESS)
return res;
} else {
size_t offs;
size_t sz;
res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK,
ROUNDUP(new_file_len, BLOCK_SIZE) /
BLOCK_SIZE, 1, &offs, &sz);
if (res != TEE_SUCCESS)
return res;
res = tee_fs_htree_truncate(&fdp->ht,
new_file_len / BLOCK_SIZE);
if (res != TEE_SUCCESS)
return res;
res = tee_fs_rpc_truncate(OPTEE_RPC_CMD_FS, fdp->fd,
offs + sz);
if (res != TEE_SUCCESS)
return res;
meta->length = new_file_len;
tee_fs_htree_meta_set_dirty(fdp->ht);
}
return TEE_SUCCESS;
}
static TEE_Result ree_fs_read_primitive(struct tee_file_handle *fh, size_t pos,
void *buf, size_t *len)
{
TEE_Result res;
int start_block_num;
int end_block_num;
size_t remain_bytes;
uint8_t *data_ptr = buf;
uint8_t *block = NULL;
struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
remain_bytes = *len;
if ((pos + remain_bytes) < remain_bytes || pos > meta->length)
remain_bytes = 0;
else if (pos + remain_bytes > meta->length)
remain_bytes = meta->length - pos;
*len = remain_bytes;
if (!remain_bytes) {
res = TEE_SUCCESS;
goto exit;
}
start_block_num = pos_to_block_num(pos);
end_block_num = pos_to_block_num(pos + remain_bytes - 1);
block = get_tmp_block();
if (!block) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto exit;
}
while (start_block_num <= end_block_num) {
size_t offset = pos % BLOCK_SIZE;
size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE);
if (size_to_read + offset > BLOCK_SIZE)
size_to_read = BLOCK_SIZE - offset;
res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block);
if (res != TEE_SUCCESS)
goto exit;
memcpy(data_ptr, block + offset, size_to_read);
data_ptr += size_to_read;
remain_bytes -= size_to_read;
pos += size_to_read;
start_block_num++;
}
res = TEE_SUCCESS;
exit:
if (block)
put_tmp_block(block);
return res;
}
static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos,
void *buf, size_t *len)
{
TEE_Result res;
mutex_lock(&ree_fs_mutex);
res = ree_fs_read_primitive(fh, pos, buf, len);
mutex_unlock(&ree_fs_mutex);
return res;
}
static TEE_Result ree_fs_write_primitive(struct tee_file_handle *fh, size_t pos,
const void *buf, size_t len)
{
TEE_Result res;
struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
size_t file_size;
if (!len)
return TEE_SUCCESS;
file_size = tee_fs_htree_get_meta(fdp->ht)->length;
if ((pos + len) < len)
return TEE_ERROR_BAD_PARAMETERS;
if (file_size < pos) {
res = ree_fs_ftruncate_internal(fdp, pos);
if (res != TEE_SUCCESS)
return res;
}
return out_of_place_write(fdp, pos, buf, len);
}
static TEE_Result ree_fs_open_primitive(bool create, uint8_t *hash,
const TEE_UUID *uuid,
struct tee_fs_dirfile_fileh *dfh,
struct tee_file_handle **fh)
{
TEE_Result res;
struct tee_fs_fd *fdp;
fdp = calloc(1, sizeof(struct tee_fs_fd));
if (!fdp)
return TEE_ERROR_OUT_OF_MEMORY;
fdp->fd = -1;
fdp->uuid = uuid;
if (create)
res = tee_fs_rpc_create_dfh(OPTEE_RPC_CMD_FS,
dfh, &fdp->fd);
else
res = tee_fs_rpc_open_dfh(OPTEE_RPC_CMD_FS, dfh, &fdp->fd);
if (res != TEE_SUCCESS)
goto out;
res = tee_fs_htree_open(create, hash, uuid, &ree_fs_storage_ops,
fdp, &fdp->ht);
out:
if (res == TEE_SUCCESS) {
if (dfh)
fdp->dfh = *dfh;
else
fdp->dfh.idx = -1;
*fh = (struct tee_file_handle *)fdp;
} else {
if (fdp->fd != -1)
tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
if (create)
tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, dfh);
free(fdp);
}
return res;
}
static void ree_fs_close_primitive(struct tee_file_handle *fh)
{
struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
if (fdp) {
tee_fs_htree_close(&fdp->ht);
tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
free(fdp);
}
}
static TEE_Result ree_dirf_commit_writes(struct tee_file_handle *fh,
uint8_t *hash)
{
TEE_Result res;
struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
if (!res && hash)
memcpy(hash, fdp->dfh.hash, sizeof(fdp->dfh.hash));
return res;
}
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,
};
static struct tee_fs_dirfile_dirh *ree_fs_dirh;
static size_t ree_fs_dirh_refcount;
#ifdef CFG_RPMB_FS
static struct tee_file_handle *ree_fs_rpmb_fh;
static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
{
TEE_Result res;
uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
uint8_t *hashp = NULL;
const char fname[] = "dirfile.db.hash";
res = tee_rpmb_fs_raw_open(fname, false, &ree_fs_rpmb_fh);
if (!res) {
size_t l = sizeof(hash);
res = rpmb_fs_ops.read(ree_fs_rpmb_fh, 0, hash, &l);
if (res)
return res;
if (l == sizeof(hash))
hashp = hash;
} else if (res == TEE_ERROR_ITEM_NOT_FOUND) {
res = tee_rpmb_fs_raw_open(fname, true, &ree_fs_rpmb_fh);
}
if (res)
return res;
res = tee_fs_dirfile_open(false, hashp, &ree_dirf_ops, dirh);
if (res == TEE_ERROR_ITEM_NOT_FOUND)
res = tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
if (res)
rpmb_fs_ops.close(&ree_fs_rpmb_fh);
return res;
}
static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
{
TEE_Result res;
uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
res = tee_fs_dirfile_commit_writes(dirh, hash);
if (res)
return res;
return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, sizeof(hash));
}
static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
{
tee_fs_dirfile_close(*dirh);
*dirh = NULL;
rpmb_fs_ops.close(&ree_fs_rpmb_fh);
}
#else /*!CFG_RPMB_FS*/
static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
{
TEE_Result res;
res = tee_fs_dirfile_open(false, NULL, &ree_dirf_ops, dirh);
if (res == TEE_ERROR_ITEM_NOT_FOUND)
return tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
return res;
}
static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
{
return tee_fs_dirfile_commit_writes(dirh, NULL);
}
static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
{
tee_fs_dirfile_close(*dirh);
*dirh = NULL;
}
#endif /*!CFG_RPMB_FS*/
static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh)
{
if (!ree_fs_dirh) {
TEE_Result res = open_dirh(&ree_fs_dirh);
if (res) {
*dirh = NULL;
return res;
}
}
ree_fs_dirh_refcount++;
assert(ree_fs_dirh);
assert(ree_fs_dirh_refcount);
*dirh = ree_fs_dirh;
return TEE_SUCCESS;
}
static void put_dirh_primitive(bool close)
{
assert(ree_fs_dirh_refcount);
/*
* During the execution of one of the ree_fs_ops ree_fs_dirh is
* guareteed to be a valid pointer. But when the fop has returned
* another thread may get an error or something causing that fop
* to do a put with close=1.
*
* For all fops but ree_fs_close() there's a call to get_dirh() to
* get a new dirh which will open it again if it was closed before.
* But in the ree_fs_close() case there's no call to get_dirh()
* only to this function, put_dirh_primitive(), and in this case
* ree_fs_dirh may actually be NULL.
*/
ree_fs_dirh_refcount--;
if (ree_fs_dirh && (!ree_fs_dirh_refcount || close))
close_dirh(&ree_fs_dirh);
}
static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close)
{
if (dirh) {
assert(dirh == ree_fs_dirh);
put_dirh_primitive(close);
}
}
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 set_name(struct tee_fs_dirfile_dirh *dirh,
struct tee_fs_fd *fdp, struct tee_pobj *po,
bool overwrite)
{
TEE_Result res;
bool have_old_dfh = false;
struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 };
res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
&old_dfh);
if (!overwrite && !res)
return TEE_ERROR_ACCESS_CONFLICT;
if (!res)
have_old_dfh = true;
/*
* If old_dfh wasn't found, the idx will be -1 and
* tee_fs_dirfile_rename() will allocate a new index.
*/
fdp->dfh.idx = old_dfh.idx;
old_dfh.idx = -1;
res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh,
po->obj_id, po->obj_id_len);
if (res)
return res;
res = commit_dirh_writes(dirh);
if (res)
return res;
if (have_old_dfh)
tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh);
return TEE_SUCCESS;
}
static void ree_fs_close(struct tee_file_handle **fh)
{
if (*fh) {
mutex_lock(&ree_fs_mutex);
put_dirh_primitive(false);
ree_fs_close_primitive(*fh);
*fh = NULL;
mutex_unlock(&ree_fs_mutex);
}
}
static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite,
const void *head, size_t head_size,
const void *attr, size_t attr_size,
const void *data, size_t data_size,
struct tee_file_handle **fh)
{
struct tee_fs_fd *fdp;
struct tee_fs_dirfile_dirh *dirh = NULL;
struct tee_fs_dirfile_fileh dfh;
TEE_Result res;
size_t pos = 0;
*fh = NULL;
mutex_lock(&ree_fs_mutex);
res = get_dirh(&dirh);
if (res)
goto out;
res = tee_fs_dirfile_get_tmp(dirh, &dfh);
if (res)
goto out;
res = ree_fs_open_primitive(true, dfh.hash, &po->uuid, &dfh, fh);
if (res)
goto out;
if (head && head_size) {
res = ree_fs_write_primitive(*fh, pos, head, head_size);
if (res)
goto out;
pos += head_size;
}
if (attr && attr_size) {
res = ree_fs_write_primitive(*fh, pos, attr, attr_size);
if (res)
goto out;
pos += attr_size;
}
if (data && data_size) {
res = ree_fs_write_primitive(*fh, pos, data, data_size);
if (res)
goto out;
}
fdp = (struct tee_fs_fd *)*fh;
res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
if (res)
goto out;
res = set_name(dirh, fdp, po, overwrite);
out:
if (res) {
put_dirh(dirh, true);
if (*fh) {
ree_fs_close_primitive(*fh);
*fh = NULL;
tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
}
}
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);
if (res)
goto out;
res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
if (res)
goto out;
res = commit_dirh_writes(dirh);
out:
put_dirh(dirh, res);
mutex_unlock(&ree_fs_mutex);
return res;
}
static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
bool overwrite)
{
TEE_Result res;
struct tee_fs_dirfile_dirh *dirh = NULL;
struct tee_fs_dirfile_fileh dfh;
struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 };
if (!new)
return TEE_ERROR_BAD_PARAMETERS;
mutex_lock(&ree_fs_mutex);
res = get_dirh(&dirh);
if (res)
goto out;
res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id,
new->obj_id_len, &remove_dfh);
if (!res && !overwrite) {
res = TEE_ERROR_ACCESS_CONFLICT;
goto out;
}
res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id,
old->obj_id_len, &dfh);
if (res)
goto out;
res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id,
new->obj_id_len);
if (res)
goto out;
if (remove_dfh.idx != -1) {
res = tee_fs_dirfile_remove(dirh, &remove_dfh);
if (res)
goto out;
}
res = commit_dirh_writes(dirh);
if (res)
goto out;
if (remove_dfh.idx != -1)
tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh);
out:
put_dirh(dirh, res);
mutex_unlock(&ree_fs_mutex);
return res;
}
static TEE_Result ree_fs_remove(struct tee_pobj *po)
{
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)
goto out;
res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
&dfh);
if (res)
goto out;
res = tee_fs_dirfile_remove(dirh, &dfh);
if (res)
goto out;
res = commit_dirh_writes(dirh);
if (res)
goto out;
tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
&dfh));
out:
put_dirh(dirh, res);
mutex_unlock(&ree_fs_mutex);
return res;
}
static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, 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_ftruncate_internal(fdp, len);
if (res)
goto out;
res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
if (res)
goto out;
res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
if (res)
goto out;
res = commit_dirh_writes(dirh);
out:
put_dirh(dirh, res);
mutex_unlock(&ree_fs_mutex);
return res;
}
static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid,
struct tee_fs_dir **dir)
{
TEE_Result res;
struct tee_fs_dir *d = calloc(1, sizeof(*d));
if (!d)
return TEE_ERROR_OUT_OF_MEMORY;
d->uuid = uuid;
mutex_lock(&ree_fs_mutex);
res = get_dirh(&d->dirh);
if (res)
goto out;
/* See that there's at least one file */
d->idx = -1;
d->d.oidlen = sizeof(d->d.oid);
res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
&d->d.oidlen);
d->idx = -1;
out:
if (!res) {
*dir = d;
} else {
if (d)
put_dirh(d->dirh, false);
free(d);
}
mutex_unlock(&ree_fs_mutex);
return res;
}
static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
{
if (d) {
mutex_lock(&ree_fs_mutex);
put_dirh(d->dirh, false);
free(d);
mutex_unlock(&ree_fs_mutex);
}
}
static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
struct tee_fs_dirent **ent)
{
TEE_Result res;
mutex_lock(&ree_fs_mutex);
d->d.oidlen = sizeof(d->d.oid);
res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
&d->d.oidlen);
if (res == TEE_SUCCESS)
*ent = &d->d;
mutex_unlock(&ree_fs_mutex);
return res;
}
const struct tee_file_operations ree_fs_ops = {
.open = ree_fs_open,
.create = ree_fs_create,
.close = ree_fs_close,
.read = ree_fs_read,
.write = ree_fs_write,
.truncate = ree_fs_truncate,
.rename = ree_fs_rename,
.remove = ree_fs_remove,
.opendir = ree_fs_opendir_rpc,
.closedir = ree_fs_closedir_rpc,
.readdir = ree_fs_readdir_rpc,
};