See also: C++ fwrite
Note that write only do memory cope but doesn't actually write to the disk.
- check data to write within enclave
- check if the destination can be written
- check if there is empty place left in the meta data region. If so then write
- write data to the data node(
file_data_node_t
) iteratively, see below - return count
Write data
- create a new data node if needed
- write to
file_data_node->plain.data[offset_in_node]
- update file size
- update writing status and mht (Merkle Hash Tree) info
size_t protected_fs_file::write(const void* ptr, size_t size, size_t count)
{
if (ptr == NULL || size == 0 || count == 0)
return 0;
int32_t result32 = sgx_thread_mutex_lock(&mutex);
if (result32 != 0)
{
last_error = result32;
file_status = SGX_FILE_STATUS_MEMORY_CORRUPTED;
return 0;
}
size_t data_left_to_write = size * count;
// prevent overlap...
#if defined(_WIN64) || defined(__x86_64__)
if (size > UINT32_MAX || count > UINT32_MAX)
{
last_error = EINVAL;
sgx_thread_mutex_unlock(&mutex);
return 0;
}
#else
if (((uint64_t)((uint64_t)size * (uint64_t)count)) != (uint64_t)data_left_to_write)
{
last_error = EINVAL;
sgx_thread_mutex_unlock(&mutex);
return 0;
}
#endif
if (sgx_is_outside_enclave(ptr, data_left_to_write))
{
last_error = SGX_ERROR_INVALID_PARAMETER;
sgx_thread_mutex_unlock(&mutex);
return 0;
}
if (file_status != SGX_FILE_STATUS_OK)
{
last_error = SGX_ERROR_FILE_BAD_STATUS;
sgx_thread_mutex_unlock(&mutex);
return 0;
}
if (open_mode.append == 0 && open_mode.update == 0 && open_mode.write == 0)
{
last_error = EACCES;
sgx_thread_mutex_unlock(&mutex);
return 0;
}
if (open_mode.append == 1)
offset = encrypted_part_plain.size; // add at the end of the file
const unsigned char* data_to_write = (const unsigned char*)ptr;
// the first block of user data is written in the meta-data encrypted part
if (offset < MD_USER_DATA_SIZE)
{
size_t empty_place_left_in_md = MD_USER_DATA_SIZE - (size_t)offset; // offset is smaller than MD_USER_DATA_SIZE
if (data_left_to_write <= empty_place_left_in_md)
{
memcpy(&encrypted_part_plain.data[offset], data_to_write, data_left_to_write);
offset += data_left_to_write;
data_to_write += data_left_to_write; // not needed, to prevent future errors
data_left_to_write = 0;
}
else
{
memcpy(&encrypted_part_plain.data[offset], data_to_write, empty_place_left_in_md);
offset += empty_place_left_in_md;
data_to_write += empty_place_left_in_md;
data_left_to_write -= empty_place_left_in_md;
}
if (offset > encrypted_part_plain.size)
encrypted_part_plain.size = offset; // file grew, update the new file size
need_writing = true;
}
while (data_left_to_write > 0)
{
file_data_node_t* file_data_node = NULL;
file_data_node = get_data_node(); // return the data node of the current offset, will read it from disk or create new one if needed (and also the mht node if needed)
if (file_data_node == NULL)
break;
size_t offset_in_node = (size_t)((offset - MD_USER_DATA_SIZE) % NODE_SIZE);
size_t empty_place_left_in_node = NODE_SIZE - offset_in_node;
if (data_left_to_write <= empty_place_left_in_node)
{ // this will be the last write
memcpy(&file_data_node->plain.data[offset_in_node], data_to_write, data_left_to_write);
offset += data_left_to_write;
data_to_write += data_left_to_write; // not needed, to prevent future errors
data_left_to_write = 0;
}
else
{
memcpy(&file_data_node->plain.data[offset_in_node], data_to_write, empty_place_left_in_node);
offset += empty_place_left_in_node;
data_to_write += empty_place_left_in_node;
data_left_to_write -= empty_place_left_in_node;
}
if (offset > encrypted_part_plain.size)
encrypted_part_plain.size = offset; // file grew, update the new file size
if (file_data_node->need_writing == false)
{
file_data_node->need_writing = true;
file_mht_node_t* file_mht_node = file_data_node->parent;
while (file_mht_node->mht_node_number != 0) // set all the mht parent nodes as 'need writing'
{
file_mht_node->need_writing = true;
file_mht_node = file_mht_node->parent;
}
root_mht.need_writing = true;
need_writing = true;
}
}
sgx_thread_mutex_unlock(&mutex);
size_t ret_count = ((size * count) - data_left_to_write) / size;
return ret_count;
}