SDK_PF_Write

 April 9, 2021 at 11:03 am

See also: C++ fwrite

Note that write only do memory cope but doesn't actually write to the disk.

  1. check data to write within enclave
  2. check if the destination can be written
  3. check if there is empty place left in the meta data region. If so then write
  4. write data to the data node(file_data_node_t) iteratively, see below
  5. return count

Write data

  1. create a new data node if needed
  2. write to file_data_node->plain.data[offset_in_node]
  3. update file size
  4. 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;
}