From d21bc8268b0d597fad7ca24f84cb9cabfb68dc16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= <josef.soentgen@genode-labs.com>
Date: Fri, 14 Aug 2020 15:44:22 +0200
Subject: [PATCH] vfs: add raw option to terminal plugin

When the 'raw' attribute is set to 'yes' the terminal VFS plugin will
ignore control characters. This in necessary for terminal connections
that transport data that contain such characters as part of message.

Fixes #3860.
---
 repos/os/src/lib/vfs/terminal_file_system.h | 42 ++++++++++++++-------
 1 file changed, 28 insertions(+), 14 deletions(-)

diff --git a/repos/os/src/lib/vfs/terminal_file_system.h b/repos/os/src/lib/vfs/terminal_file_system.h
index a776891a67..dc0fa909c0 100644
--- a/repos/os/src/lib/vfs/terminal_file_system.h
+++ b/repos/os/src/lib/vfs/terminal_file_system.h
@@ -62,6 +62,8 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 
 		Interrupt_handler &_interrupt_handler;
 
+		bool const _raw;
+
 		enum { READ_BUFFER_SIZE = 4000 };
 
 		typedef Genode::Ring_buffer<char, READ_BUFFER_SIZE + 1,
@@ -71,7 +73,8 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 
 		static void _fetch_data_from_terminal(Terminal::Connection &terminal,
 		                                      Read_buffer          &read_buffer,
-		                                      Interrupt_handler    &interrupt_handler)
+		                                      Interrupt_handler    &interrupt_handler,
+		                                      bool                  raw)
 		{
 			while (terminal.avail()) {
 
@@ -93,7 +96,7 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 
 					enum { INTERRUPT = 3 };
 
-					if (c == INTERRUPT) {
+					if (c == INTERRUPT && !raw) {
 						interrupt_handler.handle_interrupt();
 					} else {
 						read_buffer.add(c);
@@ -108,6 +111,8 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 			Read_buffer          &_read_buffer;
 			Interrupt_handler    &_interrupt_handler;
 
+			bool _raw;
+
 			bool notifying = false;
 			bool blocked   = false;
 
@@ -117,12 +122,14 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 			                    Directory_service    &ds,
 			                    File_io_service      &fs,
 			                    Genode::Allocator    &alloc,
-			                    int                   flags)
+			                    int                   flags,
+			                    bool                  raw)
 			:
 				Single_vfs_handle(ds, fs, alloc, flags),
 				_terminal(terminal),
 				_read_buffer(read_buffer),
-				_interrupt_handler(interrupt_handler)
+				_interrupt_handler(interrupt_handler),
+				_raw(raw)
 			{ }
 
 			bool read_ready() override {
@@ -133,7 +140,7 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 			{
 				if (_read_buffer.empty())
 					_fetch_data_from_terminal(_terminal, _read_buffer,
-					                          _interrupt_handler);
+					                          _interrupt_handler, _raw);
 
 				if (_read_buffer.empty()) {
 					blocked = true;
@@ -168,9 +175,9 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 		void _handle_read_avail()
 		{
 			/*
-			 * Fetch as much data from the terminal as possible to detect
-			 * user-interrupt characters (control-c), even before the VFS
-			 * client attempts to read from the terminal.
+			 * On non-raw sessions, fetch as much data from the terminal as
+			 * possible to detect user-interrupt characters (control-c), even
+			 * before the VFS client attempts to read from the terminal.
 			 *
 			 * Note that a user interrupt that follows a large chunk of data
 			 * (exceeding the capacity of the read buffer) cannot be detected
@@ -180,7 +187,8 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 			 * situation occurs. This can be provoked by pasting a large amount
 			 * of text into the terminal.
 			 */
-			_fetch_data_from_terminal(_terminal, _read_buffer, _interrupt_handler);
+			_fetch_data_from_terminal(_terminal, _read_buffer, _interrupt_handler,
+			                          _raw);
 
 			_handle_registry.for_each([this] (Registered_handle &handle) {
 				if (handle.blocked) {
@@ -200,12 +208,14 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 		Data_file_system(Genode::Entrypoint   &ep,
 		                 Terminal::Connection &terminal,
 		                 Name           const &name,
-		                 Interrupt_handler    &interrupt_handler)
+		                 Interrupt_handler    &interrupt_handler,
+		                 bool                  raw)
 		:
 			Single_file_system(Node_type::TRANSACTIONAL_FILE, name.string(),
 			                   Node_rwx::rw(), Genode::Xml_node("<data/>")),
 			_name(name), _ep(ep), _terminal(terminal),
-			_interrupt_handler(interrupt_handler)
+			_interrupt_handler(interrupt_handler),
+			_raw(raw)
 		{
 			/* register for read-avail notification */
 			_terminal.read_avail_sigh(_read_avail_handler);
@@ -224,7 +234,8 @@ class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
 			try {
 				*out_handle = new (alloc)
 					Registered_handle(_handle_registry, _terminal, _read_buffer,
-					                  _interrupt_handler, *this, *this, alloc, flags);
+					                  _interrupt_handler, *this, *this, alloc, flags,
+					                  _raw);
 				return OPEN_OK;
 			}
 			catch (Genode::Out_of_ram)  { return OPEN_ERR_OUT_OF_RAM; }
@@ -271,7 +282,9 @@ struct Vfs::Terminal_file_system::Local_factory : File_system_factory,
 
 	Terminal::Connection _terminal { _env, _label.string() };
 
-	Data_file_system _data_fs { _env.ep(), _terminal, _name, *this };
+	bool const _raw;
+
+	Data_file_system _data_fs { _env.ep(), _terminal, _name, *this, _raw };
 
 	struct Info
 	{
@@ -328,7 +341,8 @@ struct Vfs::Terminal_file_system::Local_factory : File_system_factory,
 	:
 		_label(config.attribute_value("label", Label(""))),
 		_name(name(config)),
-		_env(env.env())
+		_env(env.env()),
+		_raw(config.attribute_value("raw", false))
 	{
 		_terminal.size_changed_sigh(_size_changed_handler);
 		_handle_size_changed();