/*
 * \brief  Jitterentropy based random file system
 * \author Josef Soentgen
 * \date   2014-08-19
 */

/*
 * Copyright (C) 2014-2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

#ifndef _JITTERENTROPY_FILE_SYSTEM_H_
#define _JITTERENTROPY_FILE_SYSTEM_H_

/* Genode includes */
#include <vfs/single_file_system.h>

/* jitterentropy includes */
#include <jitterentropy.h>

namespace Vfs_jitterentropy {

	using namespace Genode;
	using namespace Genode::Vfs;

	class File_system;
}


class Vfs_jitterentropy::File_system : public Single_file_system
{
	private:

		struct rand_data *_ec_stir;
		bool              _initialized;

		bool _init_jitterentropy(Allocator &alloc)
		{
			/* initialize private allocator backend */
			jitterentropy_init(alloc);

			int err = jent_entropy_init();
			if (err) {
				error("jitterentropy library could not be initialized!");
				return false;
			}

			/* use the default behaviour as specified in jitterentropy(3) */
			_ec_stir = jent_entropy_collector_alloc(0, 0);
			if (!_ec_stir) {
				error("jitterentropy could not allocate entropy collector!");
				return false;
			}

			return true;
		}

		class Jitterentropy_vfs_handle : public Single_vfs_handle
		{
			private:

				struct rand_data *_ec_stir;
				bool             &_initialized;

			public:

				Jitterentropy_vfs_handle(Directory_service &ds,
				                         File_io_service   &fs,
				                         Allocator         &alloc,
				                         struct rand_data  *ec_stir,
				                         bool              &initialized)
				: Single_vfs_handle(ds, fs, alloc, 0),
				  _ec_stir(ec_stir),
				  _initialized(initialized) { }

				Read_result read(Byte_range_ptr const &dst, size_t &out_count) override
				{
					if (!_initialized)
						return READ_ERR_IO;

					enum { MAX_BUF_LEN = 256UL };
					char buf[MAX_BUF_LEN];

					size_t const len = min(dst.num_bytes, MAX_BUF_LEN);

					if (jent_read_entropy(_ec_stir, buf, len) < 0)
						return READ_ERR_IO;

					memcpy(dst.start, buf, len);

					out_count = len;

					return READ_OK;
				}

				Write_result write(Const_byte_range_ptr const &, size_t &) override
				{
					return WRITE_ERR_IO;
				}

				bool read_ready()  const override { return true; }
				bool write_ready() const override { return false; }
		};

	public:

		File_system(Allocator &alloc, Node const &config)
		:
			Single_file_system(Node_type::CONTINUOUS_FILE, name(),
			                   Node_rwx::ro(), config),
			_ec_stir(0),
			_initialized(_init_jitterentropy(alloc))
		{ }

		~File_system()
		{
			if (_initialized)
				jent_entropy_collector_free(_ec_stir);
		}

		static char const *name()   { return "jitterentropy"; }
		char const *type() override { return "jitterentropy"; }

		/*********************************
		 ** Directory service interface **
		 *********************************/

		Open_result open(char const *path, unsigned,
		                 Vfs::Vfs_handle **out_handle,
		                 Allocator &alloc) override
		{
			if (!_single_file(path))
				return OPEN_ERR_UNACCESSIBLE;

			*out_handle = new (alloc)
				Jitterentropy_vfs_handle(*this, *this, alloc, _ec_stir,
				                         _initialized);
			return OPEN_OK;
		}
};

#endif /* _JITTERENTROPY_FILE_SYSTEM_H_ */
