summaryrefslogtreecommitdiff
path: root/core/tee/tee_sql_fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/tee/tee_sql_fs.c')
-rw-r--r--core/tee/tee_sql_fs.c419
1 files changed, 168 insertions, 251 deletions
diff --git a/core/tee/tee_sql_fs.c b/core/tee/tee_sql_fs.c
index e38e1bc..bfc04f6 100644
--- a/core/tee/tee_sql_fs.c
+++ b/core/tee/tee_sql_fs.c
@@ -29,15 +29,6 @@
* This file implements the tee_file_operations structure for a secure
* filesystem based on an SQLite database in normal world.
* The atomicity of each operation is ensured by using SQL transactions.
- * The main purpose of the code below is to perform block encryption and
- * authentication of the file data, and properly handle seeking through the
- * file. One file (in the sense of struct tee_file_operations) maps to one
- * file in the SQL filesystem, and has the following structure:
- *
- * [ File meta-data ][ Block #0 ][Block #1]...
- * [meta_header|sql_fs_file_meta][block_header|user data][ ]...
- *
- * meta_header and block_header are defined in tee_fs_key_manager.h.
*/
#include <assert.h>
@@ -45,13 +36,12 @@
#include <optee_msg_supplicant.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <string_ext.h>
+#include <string.h>
#include <sys/queue.h>
+#include <tee/fs_htree.h>
#include <tee/tee_cryp_provider.h>
#include <tee/tee_fs.h>
-#include <tee/tee_fs_defs.h>
-#include <tee/tee_fs_key_manager.h>
#include <tee/tee_fs_rpc.h>
#include <trace.h>
#include <utee_defines.h>
@@ -61,22 +51,10 @@
#define BLOCK_SHIFT 12
#define BLOCK_SIZE (1 << BLOCK_SHIFT)
-struct sql_fs_file_meta {
- size_t length;
-};
-
/* File descriptor */
struct sql_fs_fd {
- struct sql_fs_file_meta meta;
- uint8_t encrypted_fek[TEE_FS_KM_FEK_SIZE];
- tee_fs_off_t pos;
+ struct tee_fs_htree *ht;
int fd; /* returned by normal world */
- int flags; /* open flags */
-};
-
-struct tee_fs_dir {
- int nw_dir;
- struct tee_fs_dirent d;
};
static struct mutex sql_fs_mutex = MUTEX_INITIALIZER;
@@ -96,9 +74,10 @@ static TEE_Result sql_fs_end_transaction_rpc(bool rollback)
rollback);
}
-static TEE_Result sql_fs_opendir_rpc(const char *name, struct tee_fs_dir **d)
+static TEE_Result sql_fs_opendir_rpc(const TEE_UUID *uuid,
+ struct tee_fs_dir **d)
{
- return tee_fs_rpc_opendir(OPTEE_MSG_RPC_CMD_SQL_FS, name, d);
+ return tee_fs_rpc_opendir(OPTEE_MSG_RPC_CMD_SQL_FS, uuid, d);
}
static TEE_Result sql_fs_readdir_rpc(struct tee_fs_dir *d,
@@ -107,15 +86,15 @@ static TEE_Result sql_fs_readdir_rpc(struct tee_fs_dir *d,
return tee_fs_rpc_readdir(OPTEE_MSG_RPC_CMD_SQL_FS, d, ent);
}
-static TEE_Result sql_fs_remove_rpc(const char *file)
+static TEE_Result sql_fs_remove_rpc(struct tee_pobj *po)
{
- return tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, file);
+ return tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, po);
}
-static TEE_Result sql_fs_rename_rpc(const char *old, const char *nw,
+static TEE_Result sql_fs_rename_rpc(struct tee_pobj *old, struct tee_pobj *new,
bool overwrite)
{
- return tee_fs_rpc_rename(OPTEE_MSG_RPC_CMD_SQL_FS, old, nw, overwrite);
+ return tee_fs_rpc_rename(OPTEE_MSG_RPC_CMD_SQL_FS, old, new, overwrite);
}
static void sql_fs_closedir_rpc(struct tee_fs_dir *d)
@@ -128,21 +107,6 @@ static void sql_fs_closedir_rpc(struct tee_fs_dir *d)
* End of interface with tee-supplicant
*/
-static size_t meta_size(void)
-{
- return tee_fs_get_header_size(META_FILE) +
- sizeof(struct sql_fs_file_meta);
-}
-
-static size_t block_header_size(void)
-{
- return tee_fs_get_header_size(BLOCK_FILE);
-}
-
-static size_t block_size_raw(void)
-{
- return block_header_size() + BLOCK_SIZE;
-}
/* Return the block number from a position in the user data */
static ssize_t block_num(tee_fs_off_t pos)
@@ -150,131 +114,108 @@ static ssize_t block_num(tee_fs_off_t pos)
return pos / BLOCK_SIZE;
}
-/* Return the position of a block in the DB file */
-static ssize_t block_pos_raw(size_t block_num)
+static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx,
+ size_t *offs, size_t *size)
{
- return meta_size() + block_num * block_size_raw();
-}
-
-static TEE_Result write_meta(struct sql_fs_fd *fdp)
-{
- TEE_Result res;
- size_t ct_size = meta_size();
- void *ct;
- struct tee_fs_rpc_operation op;
-
- res = tee_fs_rpc_write_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, 0,
- ct_size, &ct);
- if (res != TEE_SUCCESS)
- return res;
-
-
- res = tee_fs_encrypt_file(META_FILE,
- (const uint8_t *)&fdp->meta,
- sizeof(fdp->meta), ct, &ct_size,
- fdp->encrypted_fek);
- if (res != TEE_SUCCESS)
- return res;
-
- return tee_fs_rpc_write_final(&op);
+ const size_t node_size = sizeof(struct tee_fs_htree_node_image);
+ const size_t block_nodes = BLOCK_SIZE / node_size;
+ size_t pbn;
+ size_t bidx;
+
+
+ /*
+ * File layout
+ *
+ * phys block 0:
+ * tee_fs_htree_image @ offs = 0
+ *
+ * phys block 1:
+ * tee_fs_htree_node_image 0 @ offs = 0
+ * tee_fs_htree_node_image 1 @ offs = node_size * 2
+ * ...
+ * tee_fs_htree_node_image 61 @ offs = node_size * 122
+ *
+ * phys block 2:
+ * data block 0
+ *
+ * ...
+ *
+ * phys block 64:
+ * data block 61
+ *
+ * phys block 65:
+ * tee_fs_htree_node_image 62 @ offs = 0
+ * tee_fs_htree_node_image 63 @ offs = node_size * 2
+ * ...
+ * tee_fs_htree_node_image 121 @ offs = node_size * 123
+ *
+ * ...
+ */
+
+ switch (type) {
+ case TEE_FS_HTREE_TYPE_HEAD:
+ *offs = 0;
+ *size = sizeof(struct tee_fs_htree_image);
+ return TEE_SUCCESS;
+ case TEE_FS_HTREE_TYPE_NODE:
+ pbn = 1 + ((idx / block_nodes) * block_nodes);
+ *offs = pbn * BLOCK_SIZE + node_size * (idx % block_nodes);
+ *size = node_size;
+ return TEE_SUCCESS;
+ case TEE_FS_HTREE_TYPE_BLOCK:
+ bidx = idx;
+ pbn = 2 + bidx + bidx / (block_nodes - 1);
+ *offs = pbn * BLOCK_SIZE;
+ *size = BLOCK_SIZE;
+ return TEE_SUCCESS;
+ default:
+ return TEE_ERROR_GENERIC;
+ }
}
-static TEE_Result create_meta(struct sql_fs_fd *fdp, const char *fname)
+static TEE_Result sql_fs_rpc_read_init(void *aux,
+ struct tee_fs_rpc_operation *op,
+ enum tee_fs_htree_type type, size_t idx,
+ uint8_t vers __unused, void **data)
{
+ struct sql_fs_fd *fdp = aux;
TEE_Result res;
+ size_t offs;
+ size_t size;
- memset(&fdp->meta, 0, sizeof(fdp->meta));
-
- res = tee_fs_generate_fek(fdp->encrypted_fek, TEE_FS_KM_FEK_SIZE);
- if (res != TEE_SUCCESS)
- return res;
-
- res = tee_fs_rpc_create(OPTEE_MSG_RPC_CMD_SQL_FS, fname, &fdp->fd);
+ res = get_offs_size(type, idx, &offs, &size);
if (res != TEE_SUCCESS)
return res;
- return write_meta(fdp);
+ return tee_fs_rpc_read_init(op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd,
+ offs, size, data);
}
-static TEE_Result read_meta(struct sql_fs_fd *fdp, const char *fname)
+static TEE_Result sql_fs_rpc_write_init(void *aux,
+ struct tee_fs_rpc_operation *op,
+ enum tee_fs_htree_type type, size_t idx,
+ uint8_t vers __unused, void **data)
{
+ struct sql_fs_fd *fdp = aux;
TEE_Result res;
- size_t msize = meta_size();
- size_t out_size = sizeof(fdp->meta);
- void *meta;
- size_t bytes;
- struct tee_fs_rpc_operation op;
-
- res = tee_fs_rpc_open(OPTEE_MSG_RPC_CMD_SQL_FS, fname, &fdp->fd);
- if (res != TEE_SUCCESS)
- return res;
-
- res = tee_fs_rpc_read_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, 0,
- msize, &meta);
- if (res != TEE_SUCCESS)
- return res;
-
- res = tee_fs_rpc_read_final(&op, &bytes);
- if (res != TEE_SUCCESS)
- return res;
+ size_t offs;
+ size_t size;
- return tee_fs_decrypt_file(META_FILE, meta, msize,
- (uint8_t *)&fdp->meta, &out_size,
- fdp->encrypted_fek);
-}
-
-/*
- * Read one block of user data.
- * Returns:
- * < 0: read error
- * 0: block does not exist (reading past last block)
- * > 0: success
- */
-static TEE_Result read_block(struct sql_fs_fd *fdp, size_t bnum, uint8_t *data)
-{
- TEE_Result res;
- size_t ct_size = block_size_raw();
- size_t out_size = BLOCK_SIZE;
- ssize_t pos = block_pos_raw(bnum);
- size_t bytes;
- void *ct;
- struct tee_fs_rpc_operation op;
-
- res = tee_fs_rpc_read_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, pos,
- ct_size, &ct);
+ res = get_offs_size(type, idx, &offs, &size);
if (res != TEE_SUCCESS)
return res;
- res = tee_fs_rpc_read_final(&op, &bytes);
- if (res != TEE_SUCCESS)
- return res;
- if (!bytes)
- return TEE_SUCCESS; /* Block does not exist */
- return tee_fs_decrypt_file(BLOCK_FILE, ct, bytes, data,
- &out_size, fdp->encrypted_fek);
+ return tee_fs_rpc_write_init(op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd,
+ offs, size, data);
}
-/* Write one block of user data */
-static TEE_Result write_block(struct sql_fs_fd *fdp, size_t bnum, uint8_t *data)
-{
- TEE_Result res;
- size_t ct_size = block_size_raw();
- ssize_t pos = block_pos_raw(bnum);
- void *ct;
- struct tee_fs_rpc_operation op;
-
- res = tee_fs_rpc_write_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, pos,
- ct_size, &ct);
- if (res != TEE_SUCCESS)
- return res;
-
- res = tee_fs_encrypt_file(BLOCK_FILE, data, BLOCK_SIZE, ct,
- &ct_size, fdp->encrypted_fek);
- if (res != TEE_SUCCESS)
- return res;
-
- return tee_fs_rpc_write_final(&op);
-}
+static const struct tee_fs_htree_storage sql_fs_storage_ops = {
+ .block_size = BLOCK_SIZE,
+ .rpc_read_init = sql_fs_rpc_read_init,
+ .rpc_read_final = tee_fs_rpc_read_final,
+ .rpc_write_init = sql_fs_rpc_write_init,
+ .rpc_write_final = tee_fs_rpc_write_final,
+};
/*
* Partial write (< BLOCK_SIZE) into a block: read/update/write
@@ -296,16 +237,21 @@ static TEE_Result write_block_partial(struct sql_fs_fd *fdp, size_t bnum,
if (!buf)
return TEE_ERROR_OUT_OF_MEMORY;
- res = read_block(fdp, bnum, buf);
- if (res != TEE_SUCCESS)
- goto exit;
+ if (bnum * BLOCK_SIZE <
+ ROUNDUP(tee_fs_htree_get_meta(fdp->ht)->length, BLOCK_SIZE)) {
+ res = tee_fs_htree_read_block(&fdp->ht, bnum, buf);
+ if (res != TEE_SUCCESS)
+ goto exit;
+ } else {
+ memset(buf, 0, BLOCK_SIZE);
+ }
if (data)
memcpy(buf + offset, data, len);
else
memset(buf + offset, 0, len);
- res = write_block(fdp, bnum, buf);
+ res = tee_fs_htree_write_block(&fdp->ht, bnum, buf);
exit:
free(buf);
return res;
@@ -315,32 +261,42 @@ static TEE_Result sql_fs_ftruncate_internal(struct sql_fs_fd *fdp,
tee_fs_off_t new_length)
{
TEE_Result res;
- tee_fs_off_t old_length;
-
- old_length = (tee_fs_off_t)fdp->meta.length;
+ struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
- if (new_length == old_length)
+ if ((size_t)new_length == meta->length)
return TEE_SUCCESS;
sql_fs_begin_transaction_rpc();
- if (new_length < old_length) {
+ if ((size_t)new_length < meta->length) {
/* Trim unused blocks */
- int old_last_block = block_num(old_length);
+ int old_last_block = block_num(meta->length);
int last_block = block_num(new_length);
- tee_fs_off_t off;
if (last_block < old_last_block) {
- off = block_pos_raw(last_block);
+ size_t offs;
+ size_t sz;
+
+ res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK,
+ ROUNDUP(new_length, BLOCK_SIZE) /
+ BLOCK_SIZE, &offs, &sz);
+ if (res != TEE_SUCCESS)
+ goto exit;
+
+ res = tee_fs_htree_truncate(&fdp->ht,
+ new_length / BLOCK_SIZE);
+ if (res != TEE_SUCCESS)
+ goto exit;
+
res = tee_fs_rpc_truncate(OPTEE_MSG_RPC_CMD_SQL_FS,
- fdp->fd, off);
+ fdp->fd, offs + sz);
if (res != TEE_SUCCESS)
goto exit;
}
} else {
/* Extend file with zeroes */
- tee_fs_off_t off = old_length % BLOCK_SIZE;
- size_t bnum = block_num(old_length);
+ tee_fs_off_t off = meta->length % BLOCK_SIZE;
+ size_t bnum = block_num(meta->length);
size_t end_bnum = block_num(new_length);
while (bnum <= end_bnum) {
@@ -354,70 +310,28 @@ static TEE_Result sql_fs_ftruncate_internal(struct sql_fs_fd *fdp,
}
}
- fdp->meta.length = new_length;
- res = write_meta(fdp);
+ meta->length = new_length;
+ res = TEE_SUCCESS;
exit:
+ if (res == TEE_SUCCESS)
+ res = tee_fs_htree_sync_to_storage(&fdp->ht);
sql_fs_end_transaction_rpc(res != TEE_SUCCESS);
return res;
}
-static TEE_Result sql_fs_seek(struct tee_file_handle *fh, int32_t offset,
- TEE_Whence whence, int32_t *new_offs)
-{
- TEE_Result res;
- struct sql_fs_fd *fdp = (struct sql_fs_fd *)fh;
- tee_fs_off_t pos;
-
- mutex_lock(&sql_fs_mutex);
-
- switch (whence) {
- case TEE_DATA_SEEK_SET:
- pos = offset;
- break;
-
- case TEE_DATA_SEEK_CUR:
- pos = fdp->pos + offset;
- break;
-
- case TEE_DATA_SEEK_END:
- pos = fdp->meta.length + offset;
- break;
-
- default:
- res = TEE_ERROR_BAD_PARAMETERS;
- goto exit_ret;
- }
-
- if (pos > TEE_DATA_MAX_POSITION) {
- EMSG("Position is beyond TEE_DATA_MAX_POSITION");
- res = TEE_ERROR_BAD_PARAMETERS;
- goto exit_ret;
- }
-
- if (pos < 0)
- pos = 0;
-
- fdp->pos = pos;
- if (new_offs)
- *new_offs = pos;
- res = TEE_SUCCESS;
-exit_ret:
- mutex_unlock(&sql_fs_mutex);
- return res;
-}
-
static void sql_fs_close(struct tee_file_handle **fh)
{
struct sql_fs_fd *fdp = (struct sql_fs_fd *)*fh;
if (fdp) {
- tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd);
+ tee_fs_htree_close(&fdp->ht);
+ tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd);
free(fdp);
*fh = NULL;
}
}
-static TEE_Result open_internal(const char *file, bool create,
+static TEE_Result open_internal(struct tee_pobj *po, bool create,
struct tee_file_handle **fh)
{
TEE_Result res;
@@ -432,36 +346,41 @@ static TEE_Result open_internal(const char *file, bool create,
mutex_lock(&sql_fs_mutex);
if (create)
- res = create_meta(fdp, file);
+ res = tee_fs_rpc_create(OPTEE_MSG_RPC_CMD_SQL_FS, po, &fdp->fd);
else
- res = read_meta(fdp, file);
+ res = tee_fs_rpc_open(OPTEE_MSG_RPC_CMD_SQL_FS, po, &fdp->fd);
+ if (res != TEE_SUCCESS)
+ goto out;
+ res = tee_fs_htree_open(create, &sql_fs_storage_ops, fdp, &fdp->ht);
+out:
if (res == TEE_SUCCESS) {
*fh = (struct tee_file_handle *)fdp;
} else {
if (fdp && fdp->fd != -1)
tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd);
if (created)
- tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, file);
+ tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, po);
free(fdp);
}
mutex_unlock(&sql_fs_mutex);
return res;
}
-static TEE_Result sql_fs_open(const char *file, struct tee_file_handle **fh)
+static TEE_Result sql_fs_open(struct tee_pobj *po, struct tee_file_handle **fh)
{
- return open_internal(file, false, fh);
+ return open_internal(po, false, fh);
}
-static TEE_Result sql_fs_create(const char *file, struct tee_file_handle **fh)
+static TEE_Result sql_fs_create(struct tee_pobj *po,
+ struct tee_file_handle **fh)
{
- return open_internal(file, true, fh);
+ return open_internal(po, true, fh);
}
-static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf,
- size_t *len)
+static TEE_Result sql_fs_read(struct tee_file_handle *fh, size_t pos,
+ void *buf, size_t *len)
{
TEE_Result res;
struct sql_fs_fd *fdp = (struct sql_fs_fd *)fh;
@@ -470,14 +389,15 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf,
uint8_t *block = NULL;
int start_block_num;
int end_block_num;
+ size_t file_size;
mutex_lock(&sql_fs_mutex);
- if ((fdp->pos + remain_bytes) < remain_bytes ||
- fdp->pos > (tee_fs_off_t)fdp->meta.length)
+ file_size = tee_fs_htree_get_meta(fdp->ht)->length;
+ if ((pos + remain_bytes) < remain_bytes || pos > file_size)
remain_bytes = 0;
- else if (fdp->pos + remain_bytes > fdp->meta.length)
- remain_bytes = fdp->meta.length - fdp->pos;
+ else if (pos + remain_bytes > file_size)
+ remain_bytes = file_size - pos;
*len = remain_bytes;
@@ -486,8 +406,8 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf,
goto exit;
}
- start_block_num = block_num(fdp->pos);
- end_block_num = block_num(fdp->pos + remain_bytes - 1);
+ start_block_num = block_num(pos);
+ end_block_num = block_num(pos + remain_bytes - 1);
block = malloc(BLOCK_SIZE);
if (!block) {
@@ -496,17 +416,13 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf,
}
while (start_block_num <= end_block_num) {
- tee_fs_off_t offset = fdp->pos % BLOCK_SIZE;
+ 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;
- /*
- * REVISIT: implement read_block_partial() since we have
- * write_block_partial()?
- */
- res = read_block(fdp, start_block_num, block);
+ res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block);
if (res != TEE_SUCCESS)
goto exit;
@@ -514,7 +430,7 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf,
data_ptr += size_to_read;
remain_bytes -= size_to_read;
- fdp->pos += size_to_read;
+ pos += size_to_read;
start_block_num++;
}
@@ -525,11 +441,12 @@ exit:
return res;
}
-static TEE_Result sql_fs_write(struct tee_file_handle *fh, const void *buf,
- size_t len)
+static TEE_Result sql_fs_write(struct tee_file_handle *fh, size_t pos,
+ const void *buf, size_t len)
{
TEE_Result res;
struct sql_fs_fd *fdp = (struct sql_fs_fd *)fh;
+ struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
size_t remain_bytes = len;
const uint8_t *data_ptr = buf;
int start_block_num;
@@ -542,18 +459,18 @@ static TEE_Result sql_fs_write(struct tee_file_handle *fh, const void *buf,
sql_fs_begin_transaction_rpc();
- if (fdp->meta.length < (size_t)fdp->pos) {
+ if (meta->length < pos) {
/* Fill hole */
- res = sql_fs_ftruncate_internal(fdp, fdp->pos);
+ res = sql_fs_ftruncate_internal(fdp, pos);
if (res != TEE_SUCCESS)
goto exit;
}
- start_block_num = block_num(fdp->pos);
- end_block_num = block_num(fdp->pos + len - 1);
+ start_block_num = block_num(pos);
+ end_block_num = block_num(pos + len - 1);
while (start_block_num <= end_block_num) {
- tee_fs_off_t offset = fdp->pos % BLOCK_SIZE;
+ 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)
@@ -566,16 +483,17 @@ static TEE_Result sql_fs_write(struct tee_file_handle *fh, const void *buf,
data_ptr += size_to_write;
remain_bytes -= size_to_write;
- fdp->pos += size_to_write;
+ pos += size_to_write;
start_block_num++;
}
- if (fdp->meta.length < (size_t)fdp->pos) {
- fdp->meta.length = fdp->pos;
- res = write_meta(fdp);
- }
+ if (pos > meta->length)
+ meta->length = pos;
+
exit:
+ if (res == TEE_SUCCESS)
+ res = tee_fs_htree_sync_to_storage(&fdp->ht);
sql_fs_end_transaction_rpc(res != TEE_SUCCESS);
mutex_unlock(&sql_fs_mutex);
return res;
@@ -599,7 +517,6 @@ const struct tee_file_operations sql_fs_ops = {
.close = sql_fs_close,
.read = sql_fs_read,
.write = sql_fs_write,
- .seek = sql_fs_seek,
.truncate = sql_fs_truncate,
.opendir = sql_fs_opendir_rpc,