blob: c9faeef9c2b00a91aad73cf579a9207db2698ae7 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2017, Linaro Limited
*/
#include <assert.h>
#include <bitstring.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tee/fs_dirfile.h>
#include <types_ext.h>
struct tee_fs_dirfile_dirh {
const struct tee_fs_dirfile_operations *fops;
struct tee_file_handle *fh;
int nbits;
bitstr_t *files;
size_t ndents;
};
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
*/
static TEE_Result maybe_grow_files(struct tee_fs_dirfile_dirh *dirh, int idx)
{
void *p;
if (idx < dirh->nbits)
return TEE_SUCCESS;
p = realloc(dirh->files, bitstr_size(idx + 1));
if (!p)
return TEE_ERROR_OUT_OF_MEMORY;
dirh->files = p;
bit_nclear(dirh->files, dirh->nbits, idx);
dirh->nbits = idx + 1;
return TEE_SUCCESS;
}
static TEE_Result set_file(struct tee_fs_dirfile_dirh *dirh, int idx)
{
TEE_Result res = maybe_grow_files(dirh, idx);
if (!res)
bit_set(dirh->files, idx);
return res;
}
static void clear_file(struct tee_fs_dirfile_dirh *dirh, int idx)
{
if (idx < dirh->nbits)
bit_clear(dirh->files, idx);
}
static bool test_file(struct tee_fs_dirfile_dirh *dirh, int idx)
{
if (idx < dirh->nbits)
return bit_test(dirh->files, idx);
return false;
}
static TEE_Result read_dent(struct tee_fs_dirfile_dirh *dirh, int idx,
struct dirfile_entry *dent)
{
TEE_Result res;
size_t l;
l = sizeof(*dent);
res = dirh->fops->read(dirh->fh, sizeof(struct dirfile_entry) * idx,
dent, &l);
if (!res && l != sizeof(*dent))
res = TEE_ERROR_ITEM_NOT_FOUND;
return res;
}
static TEE_Result write_dent(struct tee_fs_dirfile_dirh *dirh, size_t n,
struct dirfile_entry *dent)
{
TEE_Result res;
res = dirh->fops->write(dirh->fh, sizeof(*dent) * n,
dent, sizeof(*dent));
if (!res && n >= dirh->ndents)
dirh->ndents = n + 1;
return res;
}
TEE_Result tee_fs_dirfile_open(bool create, uint8_t *hash,
const struct tee_fs_dirfile_operations *fops,
struct tee_fs_dirfile_dirh **dirh_ret)
{
TEE_Result res;
struct tee_fs_dirfile_dirh *dirh = calloc(1, sizeof(*dirh));
size_t n;
if (!dirh)
return TEE_ERROR_OUT_OF_MEMORY;
dirh->fops = fops;
res = fops->open(create, hash, NULL, NULL, &dirh->fh);
if (res)
goto out;
for (n = 0;; n++) {
struct dirfile_entry dent;
res = read_dent(dirh, n, &dent);
if (res) {
if (res == TEE_ERROR_ITEM_NOT_FOUND)
res = TEE_SUCCESS;
goto out;
}
if (!dent.oidlen)
continue;
if (test_file(dirh, dent.file_number)) {
DMSG("clearing duplicate file number %" PRIu32,
dent.file_number);
memset(&dent, 0, sizeof(dent));
res = write_dent(dirh, n, &dent);
if (res)
goto out;
continue;
}
res = set_file(dirh, dent.file_number);
if (res != TEE_SUCCESS)
goto out;
}
out:
if (!res) {
dirh->ndents = n;
*dirh_ret = dirh;
} else {
tee_fs_dirfile_close(dirh);
}
return res;
}
void tee_fs_dirfile_close(struct tee_fs_dirfile_dirh *dirh)
{
if (dirh) {
dirh->fops->close(dirh->fh);
free(dirh->files);
free(dirh);
}
}
TEE_Result tee_fs_dirfile_commit_writes(struct tee_fs_dirfile_dirh *dirh,
uint8_t *hash)
{
return dirh->fops->commit_writes(dirh->fh, hash);
}
TEE_Result tee_fs_dirfile_get_tmp(struct tee_fs_dirfile_dirh *dirh,
struct tee_fs_dirfile_fileh *dfh)
{
TEE_Result res;
int i = 0;
if (dirh->files) {
bit_ffc(dirh->files, dirh->nbits, &i);
if (i == -1)
i = dirh->nbits;
}
res = set_file(dirh, i);
if (!res)
dfh->file_number = i;
return res;
}
TEE_Result tee_fs_dirfile_find(struct tee_fs_dirfile_dirh *dirh,
const TEE_UUID *uuid, const void *oid,
size_t oidlen, struct tee_fs_dirfile_fileh *dfh)
{
TEE_Result res;
struct dirfile_entry dent;
int n;
int first_free = -1;
for (n = 0;; n++) {
res = read_dent(dirh, n, &dent);
if (res == TEE_ERROR_ITEM_NOT_FOUND && !oidlen) {
memset(&dent, 0, sizeof(dent));
if (first_free != -1)
n = first_free;
break;
}
if (res)
return res;
/* TODO check this loop when oidlen == 0 */
if (!dent.oidlen && first_free == -1)
first_free = n;
if (dent.oidlen != oidlen)
continue;
assert(!oidlen || !dent.oidlen ||
test_file(dirh, dent.file_number));
if (!memcmp(&dent.uuid, uuid, sizeof(dent.uuid)) &&
!memcmp(&dent.oid, oid, oidlen))
break;
}
if (dfh) {
dfh->idx = n;
dfh->file_number = dent.file_number;
memcpy(dfh->hash, dent.hash, sizeof(dent.hash));
}
return TEE_SUCCESS;
}
TEE_Result tee_fs_dirfile_fileh_to_fname(const struct tee_fs_dirfile_fileh *dfh,
char *fname, size_t *fnlen)
{
int r;
size_t l = *fnlen;
if (dfh)
r = snprintf(fname, l, "%" PRIx32, dfh->file_number);
else
r = snprintf(fname, l, "dirf.db");
if (r < 0)
return TEE_ERROR_GENERIC;
*fnlen = r + 1;
if ((size_t)r >= l)
return TEE_ERROR_SHORT_BUFFER;
return TEE_SUCCESS;
}
TEE_Result tee_fs_dirfile_rename(struct tee_fs_dirfile_dirh *dirh,
const TEE_UUID *uuid,
struct tee_fs_dirfile_fileh *dfh,
const void *oid, size_t oidlen)
{
TEE_Result res;
struct dirfile_entry dent;
if (!oidlen || oidlen > sizeof(dent.oid))
return TEE_ERROR_BAD_PARAMETERS;
memset(&dent, 0, sizeof(dent));
dent.uuid = *uuid;
memcpy(dent.oid, oid, oidlen);
dent.oidlen = oidlen;
memcpy(dent.hash, dfh->hash, sizeof(dent.hash));
dent.file_number = dfh->file_number;
if (dfh->idx < 0) {
struct tee_fs_dirfile_fileh dfh2;
res = tee_fs_dirfile_find(dirh, uuid, oid, oidlen, &dfh2);
if (res) {
if (res == TEE_ERROR_ITEM_NOT_FOUND)
res = tee_fs_dirfile_find(dirh, uuid, NULL, 0,
&dfh2);
if (res)
return res;
}
dfh->idx = dfh2.idx;
}
return write_dent(dirh, dfh->idx, &dent);
}
TEE_Result tee_fs_dirfile_remove(struct tee_fs_dirfile_dirh *dirh,
const struct tee_fs_dirfile_fileh *dfh)
{
TEE_Result res;
struct dirfile_entry dent;
uint32_t file_number;
res = read_dent(dirh, dfh->idx, &dent);
if (res)
return res;
if (!dent.oidlen)
return TEE_SUCCESS;
file_number = dent.file_number;
assert(dfh->file_number == file_number);
assert(test_file(dirh, file_number));
memset(&dent, 0, sizeof(dent));
res = write_dent(dirh, dfh->idx, &dent);
if (!res)
clear_file(dirh, file_number);
return res;
}
TEE_Result tee_fs_dirfile_update_hash(struct tee_fs_dirfile_dirh *dirh,
const struct tee_fs_dirfile_fileh *dfh)
{
TEE_Result res;
struct dirfile_entry dent;
res = read_dent(dirh, dfh->idx, &dent);
if (res)
return res;
assert(dent.file_number == dfh->file_number);
assert(test_file(dirh, dent.file_number));
memcpy(&dent.hash, dfh->hash, sizeof(dent.hash));
return write_dent(dirh, dfh->idx, &dent);
}
TEE_Result tee_fs_dirfile_get_next(struct tee_fs_dirfile_dirh *dirh,
const TEE_UUID *uuid, int *idx, void *oid,
size_t *oidlen)
{
TEE_Result res;
int i = *idx + 1;
struct dirfile_entry dent;
if (i < 0)
i = 0;
for (;; i++) {
res = read_dent(dirh, i, &dent);
if (res)
return res;
if (!memcmp(&dent.uuid, uuid, sizeof(dent.uuid)) &&
dent.oidlen)
break;
}
if (*oidlen < dent.oidlen)
return TEE_ERROR_SHORT_BUFFER;
memcpy(oid, dent.oid, dent.oidlen);
*oidlen = dent.oidlen;
*idx = i;
return TEE_SUCCESS;
}