diff --git a/repos/dde_linux/include/lxip/lxip.h b/repos/dde_linux/include/lxip/lxip.h
index 3eb9b2c06..1b85c9c2c 100644
--- a/repos/dde_linux/include/lxip/lxip.h
+++ b/repos/dde_linux/include/lxip/lxip.h
@@ -78,7 +78,18 @@ namespace Lxip {
};
enum Ioctl_cmd {
- LINUX_FIONREAD = 0x541b /* == SIOCINQ */
+ LINUX_FIONREAD = 0x541b, /* == SIOCINQ */
+ LINUX_IFADDR = 0x8915, /* == SIOCGIFADDR */
+ };
+
+ /*
+ * Must match errno values from lx_emul.h
+ */
+ enum Io_result {
+ LINUX_EAGAIN = -35,
+ LINUX_EINPROGRESS = -36,
+ LINUX_EALREADY = -37,
+ LINUX_EISCONN = -56,
};
}
diff --git a/repos/dde_linux/lib/mk/vfs_lxip.mk b/repos/dde_linux/lib/mk/vfs_lxip.mk
new file mode 100644
index 000000000..af31baa9e
--- /dev/null
+++ b/repos/dde_linux/lib/mk/vfs_lxip.mk
@@ -0,0 +1,21 @@
+SHARED_LIB = yes
+
+VFS_DIR = $(REP_DIR)/src/lib/vfs/lxip
+LXIP_DIR = $(REP_DIR)/src/lib/lxip
+
+LIBS += lxip lxip_include
+INC_DIR += $(VFS_DIR)
+LD_OPT += --version-script=$(VFS_DIR)/symbol.map
+SRC_CC = vfs.cc
+
+vpath %.cc $(REP_DIR)/src/lib/vfs/lxip
+
+SETUP_SUFFIX =
+CC_OPT += -DSETUP_SUFFIX=$(SETUP_SUFFIX)
+
+CC_OPT += -U__linux__ -D__KERNEL__
+CC_OPT += -DCONFIG_INET -DCONFIG_BASE_SMALL=0 -DCONFIG_DEBUG_LOCK_ALLOC \
+ -DCONFIG_IP_PNP_DHCP
+
+CC_C_OPT += -include $(LXIP_DIR)/include/lx_emul.h
+CC_CXX_OPT = -fpermissive
diff --git a/repos/dde_linux/run/vfs_cfg.run b/repos/dde_linux/run/vfs_cfg.run
new file mode 100644
index 000000000..090b9a56d
--- /dev/null
+++ b/repos/dde_linux/run/vfs_cfg.run
@@ -0,0 +1,103 @@
+set build_components {
+ core init
+ drivers/nic
+ drivers/timer
+ lib/vfs/lxip
+ server/vfs
+ server/dynamic_rom
+}
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+append_platform_drv_config
+
+append config {
+
+}
+
+install_config $config
+
+set boot_modules {
+ core init timer nic_drv vfs dynamic_rom
+ ld.lib.so vfs_lxip.lib.so lxip.lib.so
+}
+
+append_platform_drv_boot_modules
+
+build_boot_image $boot_modules
+
+puts "#################################################################"
+puts "## run simultaneous ping to 10.0.2.55 and ##"
+puts "#################################################################"
+sleep 1
+
+append qemu_args " -nographic -net nic,model=e1000 -net tap,ifname=tap0,downscript=no,script=no "
+
+run_genode_until forever
+
+# vi: set ft=tcl :
diff --git a/repos/dde_linux/run/vfs_lxip.run b/repos/dde_linux/run/vfs_lxip.run
new file mode 100644
index 000000000..290f84361
--- /dev/null
+++ b/repos/dde_linux/run/vfs_lxip.run
@@ -0,0 +1,108 @@
+assert_spec linux
+
+set build_components {
+ core init
+ drivers/timer drivers/nic
+ server/tcp_terminal
+ test/terminal_echo
+ lib/vfs/lxip
+ test/vfs_lxip
+}
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+append_platform_drv_config
+
+append config {
+
+}
+
+install_config $config
+
+set boot_modules {
+ core init timer
+ nic_drv
+ ld.lib.so libc.lib.so libc_lock_pipe.lib.so
+ libm.lib.so
+ vfs_lxip.lib.so lxip.lib.so
+ tcp_terminal
+ test-terminal_echo
+ test-vfs_lxip
+}
+
+append_platform_drv_boot_modules
+
+build_boot_image $boot_modules
+
+run_genode_until forever
+
+# vi: set ft=tcl :
diff --git a/repos/dde_linux/src/lib/vfs/lxip/symbol.map b/repos/dde_linux/src/lib/vfs/lxip/symbol.map
new file mode 100644
index 000000000..58e4ff57a
--- /dev/null
+++ b/repos/dde_linux/src/lib/vfs/lxip/symbol.map
@@ -0,0 +1,9 @@
+{
+ global:
+
+ vfs_file_system_factory;
+
+ local:
+
+ *;
+};
diff --git a/repos/dde_linux/src/lib/vfs/lxip/target.mk b/repos/dde_linux/src/lib/vfs/lxip/target.mk
new file mode 100644
index 000000000..49d9c1210
--- /dev/null
+++ b/repos/dde_linux/src/lib/vfs/lxip/target.mk
@@ -0,0 +1,2 @@
+TARGET = dummy-vfs_lxip
+LIBS = vfs_lxip
diff --git a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc
new file mode 100644
index 000000000..819f7b414
--- /dev/null
+++ b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc
@@ -0,0 +1,1653 @@
+/*
+ * \brief lxip-based socket file system
+ * \author Christian Helmuth
+ * \author Josef Soentgen
+ * \author Emery Hemingway
+ * \date 2016-02-01
+ */
+
+/*
+ * Copyright (C) 2015-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Lxip includes */
+#include
+#include
+
+/* Lx_kit */
+#include
+#include
+
+
+namespace Linux {
+ #include
+
+ #include
+ #include
+ #include
+ #include
+ extern int sock_setsockopt(socket *sock, int level,
+ int op, char __user *optval,
+ unsigned int optlen);
+ extern int sock_getsockopt(socket *sock, int level,
+ int op, char __user *optval,
+ int __user *optlen);
+ socket *sock_alloc(void);
+ #include
+
+ enum {
+ POLLIN_SET = (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR),
+ POLLOUT_SET = (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR),
+ POLLEX_SET = (POLLPRI)
+ };
+}
+
+
+namespace {
+
+long get_port(char const *p)
+{
+ long tmp = -1;
+
+ while (*++p) {
+ if (*p == ':') {
+ Genode::ascii_to_unsigned(++p, tmp, 10);
+ break;
+ }
+ }
+ return tmp;
+}
+
+
+unsigned get_addr(char const *p)
+{
+ unsigned char to[4] = { 0, 0, 0, 0};
+
+ for (unsigned char &c : to) {
+
+ unsigned long result = 0;
+ p += Genode::ascii_to_unsigned(p, result, 10);
+
+ c = result;
+
+ if (*p == '.') ++p;
+ if (*p == 0) break;
+ };
+
+ return (to[0]<<0)|(to[1]<<8)|(to[2]<<16)|(to[3]<<24);
+}
+
+}
+
+
+namespace Lxip {
+
+ using namespace Linux;
+
+ struct Protocol_dir;
+ struct Socket_dir;
+
+ class Protocol_dir_impl;
+
+ enum {
+ MAX_SOCKETS = 128, /* 3 */
+ MAX_SOCKET_NAME_LEN = 3 + 1, /* + \0 */
+ MAX_DATA_LEN = 32, /* 255.255.255.255:65536 + something */
+ };
+}
+
+
+struct Lxip::Protocol_dir
+{
+ enum Type { TYPE_STREAM, TYPE_DGRAM };
+
+ virtual char const *top_dir() = 0;
+ virtual Type type() = 0;
+ virtual unsigned adopt_socket(socket &, bool) = 0;
+ virtual bool lookup_port(long) = 0;
+};
+
+
+struct Lxip::Socket_dir
+{
+ virtual Protocol_dir &parent() = 0;
+ virtual char const *top_dir() = 0;
+ virtual unsigned accept(socket&) = 0;
+ virtual void bind(bool) = 0; /* bind to port */
+ virtual long bind() = 0; /* return bound port */
+ virtual bool lookup_port(long) = 0;
+ virtual void connect(bool) = 0;
+ virtual void listen(bool) = 0;
+ virtual sockaddr_storage &remote_addr() = 0;
+ virtual void close() = 0;
+ virtual bool closed() const = 0;
+ virtual void trigger_io_response(Vfs::Vfs_handle::Context *) = 0;
+};
+
+
+namespace Vfs {
+
+ using namespace Genode;
+
+ struct Node;
+ struct Directory;
+ struct File;
+
+ class Lxip_file;
+ class Lxip_data_file;
+ class Lxip_bind_file;
+ class Lxip_accept_file;
+ class Lxip_connect_file;
+ class Lxip_listen_file;
+ class Lxip_local_file;
+ class Lxip_remote_file;
+ class Lxip_socket_dir;
+
+ class Lxip_new_socket_file;
+
+ class Lxip_address_file;
+
+ class Lxip_vfs_handle;
+ class Lxip_file_system;
+
+ typedef Genode::List > Lxip_vfs_handles;
+}
+
+
+/***************
+ ** Vfs nodes **
+ ***************/
+
+struct Vfs::Lxip_vfs_handle final : Vfs::Vfs_handle
+{
+ Vfs::File &file;
+
+ List_element file_le { this };
+ List_element polling_le { this };
+
+ Lxip_vfs_handle(Vfs::File_system &fs, Allocator &alloc, int status_flags,
+ Vfs::File &file);
+
+ ~Lxip_vfs_handle();
+};
+
+
+struct Vfs::Node
+{
+ char const *_name;
+
+ Node(char const *name) : _name(name) { }
+
+ virtual ~Node() { }
+
+ virtual char const *name() { return _name; }
+};
+
+
+struct Vfs::File : Vfs::Node
+{
+ Lxip_vfs_handles handles;
+
+ File(char const *name) : Node(name) { }
+
+ virtual ~File() { }
+
+ /**
+ * Read or write operation would block exception
+ */
+ struct Would_block { };
+
+ /**
+ * Pass len data to handle read callback
+ */
+ virtual Lxip::ssize_t read(char *, Genode::size_t, file_size)
+ {
+ Genode::error("lxip: read from write-only handle");
+ return -1;
+ }
+
+ /**
+ * Pass len data to handle write callback
+ */
+ virtual Lxip::ssize_t write(char const *, Genode::size_t, file_size)
+ {
+ Genode::error("lxip: write to read-only handle");
+ return -1;
+ }
+
+ /**
+ * Check for data to read or write
+ */
+ virtual bool poll(bool trigger_io_response = false,
+ Vfs::Vfs_handle::Context *context = nullptr) { return false; }
+};
+
+
+Vfs::Lxip_vfs_handle::Lxip_vfs_handle(Vfs::File_system &fs,
+ Allocator &alloc,
+ int status_flags,
+ Vfs::File &file)
+:
+ Vfs_handle(fs, fs, alloc, status_flags), file(file)
+{
+ file.handles.insert(&file_le);
+}
+
+
+Vfs::Lxip_vfs_handle::~Lxip_vfs_handle()
+{
+ file.handles.remove(&file_le);
+}
+
+
+/**
+ * List of open handles to potentially poll
+ *
+ * Could be a dynamic queue, but this works for now.
+ */
+static Vfs::Lxip_vfs_handles _polling_handles;
+
+static void poll_all()
+{
+ using namespace Linux;
+
+ for (Genode::List_element *le = _polling_handles.first();
+ le; le = le->next())
+ {
+ Vfs::Lxip_vfs_handle *handle = le->object();
+ handle->file.poll(true, handle->context);
+ }
+}
+
+
+struct Vfs::Directory : Vfs::Node
+{
+ Directory(char const *name) : Node(name) { }
+
+ virtual ~Directory() { };
+
+ virtual Vfs::Node *child(char const *) = 0;
+ virtual void dirent(file_offset, Directory_service::Dirent &) = 0;
+ virtual file_size num_dirent() = 0;
+};
+
+
+/*****************************
+ ** Lxip vfs specific nodes **
+ *****************************/
+
+class Vfs::Lxip_file : public Vfs::File
+{
+ protected:
+
+ Lxip::Socket_dir &_parent;
+ Linux::socket &_sock;
+
+ char _content_buffer[Lxip::MAX_DATA_LEN];
+
+ public:
+
+ Lxip_file(Lxip::Socket_dir &p, Linux::socket &s, char const *name)
+ : Vfs::File(name), _parent(p), _sock(s) { }
+
+ virtual ~Lxip_file() { }
+
+ void dissolve_handles()
+ {
+ for (Genode::List_element *le = handles.first();
+ le; le = le->next())
+ {
+ _polling_handles.remove(&le->object()->polling_le);
+ }
+ }
+};
+
+
+class Vfs::Lxip_data_file : public Vfs::Lxip_file
+{
+ public:
+
+ Lxip_data_file(Lxip::Socket_dir &p, Linux::socket &s)
+ : Lxip_file(p, s, "data") { }
+
+ /********************
+ ** File interface **
+ ********************/
+
+ bool poll(bool trigger_io_response,
+ Vfs::Vfs_handle::Context *context) override
+ {
+ using namespace Linux;
+
+ file f;
+ f.f_flags = 0;
+ if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN_SET)) {
+ if (trigger_io_response)
+ _parent.trigger_io_response(context);
+ return true;
+ }
+ return false;
+ }
+
+ Lxip::ssize_t write(char const *src, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ msghdr msg;
+ iovec iov;
+
+ msg.msg_name = &_parent.remote_addr();
+ msg.msg_namelen = sizeof(sockaddr_in);
+ msg.msg_iter.iov_offset = 0;
+ msg.msg_iter.count = len;
+ msg.msg_iter.iov = &iov;
+ msg.msg_iter.nr_segs = 1;
+ msg.msg_control = nullptr;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ msg.msg_iocb = nullptr;
+
+ iov.iov_base = const_cast(src);
+ iov.iov_len = len;
+
+ return _sock.ops->sendmsg(&_sock, &msg, len);
+ }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ msghdr msg;
+ iovec iov;
+
+ msg.msg_name = nullptr;
+ msg.msg_namelen = 0;
+ msg.msg_iter.iov_offset = 0;
+ msg.msg_iter.count = len;
+ msg.msg_iter.iov = &iov;
+ msg.msg_iter.nr_segs = 1;
+ msg.msg_control = nullptr;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ msg.msg_iocb = nullptr;
+
+ iov.iov_base = dst;
+ iov.iov_len = len;
+
+ /* FIXME this read does not block */
+ return _sock.ops->recvmsg(&_sock, &msg, len, MSG_DONTWAIT);
+ }
+};
+
+
+class Vfs::Lxip_bind_file : public Vfs::Lxip_file
+{
+ private:
+
+ long _port = -1;
+
+ public:
+
+ Lxip_bind_file(Lxip::Socket_dir &p, Linux::socket &s)
+ : Lxip_file(p, s, "bind") { }
+
+ long port() { return _port; }
+
+ /********************
+ ** File interface **
+ ********************/
+
+ Lxip::ssize_t write(char const *src, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ if (len > (sizeof(_content_buffer) - 2))
+ return -1;
+
+ /* already bound to port */
+ if (_port >= 0) return -1;
+
+ Genode::memcpy(_content_buffer, src, len);
+ _content_buffer[len+1] = '\n';
+ _content_buffer[len+2] = '\0';
+
+ /* check if port is already used by other socket */
+ long port = get_port(_content_buffer);
+ if (port == -1) return -1;
+ if (_parent.lookup_port(port)) return -1;
+
+ /* port is free, try to bind it */
+ sockaddr_storage addr_storage;
+
+ sockaddr_in *addr = (sockaddr_in *)&addr_storage;
+ addr->sin_port = htons(port);
+ addr->sin_addr.s_addr = get_addr(_content_buffer);
+ addr->sin_family = AF_INET;
+
+ int res = _sock.ops->bind(&_sock, (sockaddr*)addr, sizeof(addr_storage));
+ if (res != 0) return -1;
+
+ _port = port;
+
+ _parent.bind(true);
+
+ return len;
+ }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ if (len < sizeof(_content_buffer))
+ return -1;
+
+ Genode::size_t const n = Genode::strlen(_content_buffer);
+ Genode::memcpy(dst, _content_buffer, n);
+
+ return n;
+ }
+};
+
+
+class Vfs::Lxip_listen_file : public Vfs::Lxip_file
+{
+ public:
+
+ Lxip_listen_file(Lxip::Socket_dir &p, Linux::socket &s)
+ : Lxip_file(p, s, "listen") { }
+
+ /********************
+ ** File interface **
+ ********************/
+
+ Lxip::ssize_t write(char const *src, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ if (len > (sizeof(_content_buffer) - 2))
+ return -1;
+
+ Lxip::ssize_t res;
+
+ unsigned long backlog = 5; /* default */
+ Genode::ascii_to_unsigned(src, backlog, 10);
+
+ res = _sock.ops->listen(&_sock, backlog);
+ if (res != 0) return -1;
+
+ Genode::memcpy(_content_buffer, src, len);
+ _content_buffer[len+1] = '\n';
+ _content_buffer[len+2] = '\0';
+
+ _parent.listen(true);
+
+ return len;
+ }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ if (len < sizeof(_content_buffer))
+ return -1;
+
+ Genode::size_t const n = Genode::strlen(_content_buffer);
+ Genode::memcpy(dst, _content_buffer, n);
+
+ return n;
+ }
+};
+
+
+class Vfs::Lxip_connect_file : public Vfs::Lxip_file
+{
+ private:
+
+ bool _connecting = false;
+ bool _is_connected = false;
+
+ public:
+
+ Lxip_connect_file(Lxip::Socket_dir &p, Linux::socket &s)
+ : Lxip_file(p, s, "connect") { }
+
+ /********************
+ ** File interface **
+ ********************/
+
+ Lxip::ssize_t write(char const *src, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ if (len > (sizeof(_content_buffer) - 2))
+ return -1;
+
+ Genode::memcpy(_content_buffer, src, len);
+ _content_buffer[len+1] = '\n';
+ _content_buffer[len+2] = '\0';
+
+ long const port = get_port(_content_buffer);
+ if (port == -1) return -1;
+
+ sockaddr_storage addr_storage;
+
+ sockaddr_in *addr = (sockaddr_in *)&addr_storage;
+ addr->sin_port = htons(port);
+ addr->sin_addr.s_addr = get_addr(_content_buffer);
+ addr->sin_family = AF_INET;
+
+ int res = _sock.ops->connect(&_sock, (sockaddr *)addr, sizeof(addr_storage), 0);
+
+ switch (res) {
+ case Lxip::Io_result::LINUX_EINPROGRESS:
+ _connecting = true;
+ return -1;
+
+ case Lxip::Io_result::LINUX_EALREADY:
+ return -1;
+
+ case Lxip::Io_result::LINUX_EISCONN:
+ /*
+ * Connecting on an already connected socket is an error.
+ * If we get this error after we got EINPROGRESS it is
+ * fine.
+ */
+ if (_is_connected || !_connecting) return -1;
+ _is_connected = true;
+ break;
+
+ default:
+ if (res != 0) return -1;
+ break;
+ }
+
+ sockaddr_in *remote_addr = (sockaddr_in *)&_parent.remote_addr();
+ remote_addr->sin_port = htons(port);
+ remote_addr->sin_addr.s_addr = get_addr(_content_buffer);
+ remote_addr->sin_family = AF_INET;
+
+ _parent.connect(true);
+
+ return len;
+ }
+};
+
+
+class Vfs::Lxip_local_file : public Vfs::Lxip_file
+{
+ public:
+
+ Lxip_local_file(Lxip::Socket_dir &p, Linux::socket &s)
+ : Lxip_file(p, s, "local") { }
+
+ /********************
+ ** File interface **
+ ********************/
+
+ bool poll(bool trigger_io_response,
+ Vfs::Vfs_handle::Context *context) override
+ {
+ using namespace Linux;
+
+ file f;
+ f.f_flags = 0;
+
+ switch (_parent.parent().type()) {
+ case Lxip::Protocol_dir::TYPE_DGRAM:
+ if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN_SET)) {
+ if (trigger_io_response)
+ _parent.trigger_io_response(context);
+ return true;
+ }
+ case Lxip::Protocol_dir::TYPE_STREAM:
+ return true;
+ }
+
+ return false;
+ }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ if (len < sizeof(_content_buffer))
+ return -1;
+
+ sockaddr_storage addr_storage;
+ sockaddr_in *addr = (sockaddr_in *)&addr_storage;
+
+ int out_len = sizeof(addr_storage);
+ int const res = _sock.ops->getname(&_sock, (sockaddr *)addr, &out_len, 0);
+ if (res < 0) return -1;
+
+ in_addr const i_addr = addr->sin_addr;
+ unsigned char const *a = (unsigned char *)&i_addr.s_addr;
+ unsigned char const *p = (unsigned char *)&addr->sin_port;
+ return Genode::snprintf(dst, len,
+ "%d.%d.%d.%d:%u\n",
+ a[0], a[1], a[2], a[3], (p[0]<<8)|(p[1]<<0));
+ }
+};
+
+
+class Vfs::Lxip_remote_file : public Vfs::Lxip_file
+{
+ public:
+
+ Lxip_remote_file(Lxip::Socket_dir &p, Linux::socket &s)
+ : Lxip_file(p, s, "remote") { }
+
+ /********************
+ ** File interface **
+ ********************/
+
+ bool poll(bool trigger_io_response,
+ Vfs::Vfs_handle::Context *context) override
+ {
+ using namespace Linux;
+
+ file f;
+ f.f_flags = 0;
+
+ switch (_parent.parent().type()) {
+ case Lxip::Protocol_dir::TYPE_DGRAM:
+ if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN_SET)) {
+ if (trigger_io_response)
+ _parent.trigger_io_response(context);
+ return true;
+ }
+ case Lxip::Protocol_dir::TYPE_STREAM:
+ return true;
+ }
+
+ return false;
+ }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ sockaddr_storage addr_storage;
+ sockaddr_in *addr = (sockaddr_in *)&addr_storage;
+
+ switch (_parent.parent().type()) {
+ case Lxip::Protocol_dir::TYPE_DGRAM:
+ {
+ /* peek the sender address of the next packet */
+ msghdr msg;
+ iovec iov;
+
+ msg.msg_name = addr;
+ msg.msg_namelen = sizeof(addr_storage);
+ msg.msg_iter.type = 0;
+ msg.msg_iter.iov_offset = 0;
+ msg.msg_iter.iov = &iov;
+ msg.msg_iter.nr_segs = 1;
+ msg.msg_control = nullptr;
+ msg.msg_controllen = 0;
+ msg.msg_iocb = nullptr;
+
+ /* buffer not used */
+ iov.iov_base = _content_buffer;
+ iov.iov_len = sizeof(_content_buffer);
+
+ int const res = _sock.ops->recvmsg(&_sock, &msg, 0, MSG_DONTWAIT|MSG_PEEK);
+ if (res < 0) return -1;
+ }
+ break;
+ case Lxip::Protocol_dir::TYPE_STREAM:
+ {
+ int out_len = sizeof(addr_storage);
+ int const res = _sock.ops->getname(&_sock, (sockaddr *)addr, &out_len, 1);
+ if (res < 0) return -1;
+ }
+ break;
+ }
+
+ in_addr const i_addr = addr->sin_addr;
+ unsigned char const *a = (unsigned char *)&i_addr.s_addr;
+ unsigned char const *p = (unsigned char *)&addr->sin_port;
+ return Genode::snprintf(dst, len,
+ "%d.%d.%d.%d:%u\n",
+ a[0], a[1], a[2], a[3], (p[0]<<8)|(p[1]<<0));
+ }
+
+ Lxip::ssize_t write(char const *src, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ if (len > (sizeof(_content_buffer) - 2))
+ return -1;
+
+ Genode::memcpy(_content_buffer, src, len);
+ _content_buffer[len+1] = '\n';
+ _content_buffer[len+2] = '\0';
+
+ long const port = get_port(_content_buffer);
+ if (port == -1) return -1;
+
+ sockaddr_in *remote_addr = (sockaddr_in *)&_parent.remote_addr();
+ remote_addr->sin_port = htons(port);
+ remote_addr->sin_addr.s_addr = get_addr(_content_buffer);
+ remote_addr->sin_family = AF_INET;
+
+ return len;
+ }
+};
+
+
+class Vfs::Lxip_accept_file : public Vfs::Lxip_file
+{
+ public:
+
+ Lxip_accept_file(Lxip::Socket_dir &p, Linux::socket &s)
+ : Lxip_file(p, s, "accept") { }
+
+ /********************
+ ** File interface **
+ ********************/
+
+ bool poll(bool trigger_io_response,
+ Vfs::Vfs_handle::Context *context) override
+ {
+ using namespace Linux;
+
+ file f;
+ f.f_flags = 0;
+
+ if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN)) {
+ if (trigger_io_response)
+ _parent.trigger_io_response(context);
+ return true;
+ }
+ return false;
+ }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ /* FIXME this read does not block */
+ if (!poll(false, nullptr))
+ return -1;
+
+ socket *new_sock = sock_alloc();
+
+ if (int res = _sock.ops->accept(&_sock, new_sock, O_NONBLOCK)) {
+ kfree(new_sock);
+ if (res == -EAGAIN)
+ throw Would_block();
+ else
+ return -1;
+ }
+ set_sock_wait(new_sock, 0);
+
+ new_sock->type = _sock.type;
+ new_sock->ops = _sock.ops;
+
+ try {
+ unsigned const id = _parent.accept(*new_sock);
+ return Genode::snprintf(dst, len, "%s/%u\n", _parent.top_dir(), id);
+ } catch (...) {
+ Genode::error("lxip: could not adopt new client socket");
+ }
+
+ kfree(new_sock);
+ return -1;
+ }
+};
+
+
+class Vfs::Lxip_socket_dir final : public Vfs::Directory,
+ public Lxip::Socket_dir
+{
+ public:
+
+ enum {
+ ACCEPT_NODE, BIND_NODE, CONNECT_NODE,
+ DATA_NODE, LOCAL_NODE, LISTEN_NODE, REMOTE_NODE,
+ MAX_NODES
+ };
+
+ private:
+
+ Genode::Allocator &_alloc;
+ Lxip::Protocol_dir &_parent;
+ Vfs::Io_response_handler &_io_response_handler;
+ Linux::socket &_sock;
+
+ Vfs::Node *_nodes[MAX_NODES];
+
+ Linux::sockaddr_storage _remote_addr;
+
+ unsigned _num_nodes()
+ {
+ unsigned num = 0;
+ for (Vfs::Node *n : _nodes) num += (n != nullptr);
+ return num;
+ }
+
+ Lxip_accept_file _accept_file { *this, _sock };
+ Lxip_bind_file _bind_file { *this, _sock };
+ Lxip_connect_file _connect_file { *this, _sock };
+ Lxip_data_file _data_file { *this, _sock };
+ Lxip_listen_file _listen_file { *this, _sock };
+ Lxip_local_file _local_file { *this, _sock };
+ Lxip_remote_file _remote_file { *this, _sock };
+
+ char _name[Lxip::MAX_SOCKET_NAME_LEN];
+
+ public:
+
+ Lxip_socket_dir(Genode::Allocator &alloc, unsigned id,
+ Lxip::Protocol_dir &parent,
+ Vfs::Io_response_handler &io_response_handler,
+ Linux::socket &sock,
+ bool from_accept)
+ :
+ Directory(_name),
+ _alloc(alloc), _parent(parent), _io_response_handler(io_response_handler),
+ _sock(sock)
+ {
+ Genode::snprintf(_name, sizeof(_name), "%u", id);
+
+ for (Vfs::Node * &node : _nodes) node = nullptr;
+
+ _nodes[BIND_NODE] = &_bind_file;
+ _nodes[CONNECT_NODE] = &_connect_file;
+ _nodes[DATA_NODE] = &_data_file;
+ _nodes[LOCAL_NODE] = &_local_file;
+ _nodes[REMOTE_NODE] = &_remote_file;
+ }
+
+ ~Lxip_socket_dir()
+ {
+ _accept_file.dissolve_handles();
+ _bind_file.dissolve_handles();
+ _connect_file.dissolve_handles();
+ _data_file.dissolve_handles();
+ _listen_file.dissolve_handles();
+ _local_file.dissolve_handles();
+ _remote_file.dissolve_handles();
+
+ Linux::socket *sock = &_sock;
+
+ if (sock->ops)
+ sock->ops->release(sock);
+
+ kfree(sock->wq);
+ kfree(sock);
+ }
+
+ /**************************
+ ** Socket_dir interface **
+ **************************/
+
+ Lxip::Protocol_dir &parent() override { return _parent; }
+
+ Linux::sockaddr_storage &remote_addr() override { return _remote_addr; }
+
+ char const *top_dir() override { return _parent.top_dir(); }
+
+ unsigned accept(Linux::socket &sock) override
+ {
+ return _parent.adopt_socket(sock, true);
+ }
+
+ void bind(bool v) override
+ {
+ _nodes[LISTEN_NODE] = v ? &_listen_file : nullptr;
+ }
+
+ long bind() override { return _bind_file.port(); }
+
+ bool lookup_port(long port) override { return _parent.lookup_port(port); }
+
+ void connect(bool v) override
+ {
+ _nodes[REMOTE_NODE] = v ? &_remote_file : nullptr;
+ }
+
+ void listen(bool v) override
+ {
+ _nodes[ACCEPT_NODE] = v ? &_accept_file : nullptr;
+ }
+
+ bool _closed = false;
+
+ void close() override { _closed = true; }
+ bool closed() const override { return _closed; }
+
+ void trigger_io_response(Vfs::Vfs_handle::Context *context) override
+ {
+ _io_response_handler.handle_io_response(context);
+ }
+
+ /*************************
+ ** Directory interface **
+ *************************/
+
+ Vfs::Node *child(char const *name) override
+ {
+ for (Vfs::Node *n : _nodes)
+ if (n && Genode::strcmp(n->name(), name) == 0)
+ return n;
+
+ return nullptr;
+ }
+
+ void dirent(file_offset index, Directory_service::Dirent &out) override
+ {
+ out.fileno = index+1;
+ out.type = Directory_service::DIRENT_TYPE_END;
+ out.name[0] = '\0';
+
+ Vfs::Node *node = nullptr;
+ for (Vfs::Node *n : _nodes) {
+ if (n) {
+ if (index == 0) {
+ node = n;
+ break;
+ }
+ --index;
+ }
+ }
+ if (!node) return;
+
+ out.type = Directory_service::DIRENT_TYPE_FILE;
+
+ strncpy(out.name, node->name(), sizeof(out.name));
+ }
+
+ file_size num_dirent() override { return _num_nodes(); }
+};
+
+
+class Vfs::Lxip_new_socket_file : public Vfs::File
+{
+ private:
+
+ Lxip::Protocol_dir &_parent;
+
+ public:
+
+ Lxip_new_socket_file(Lxip::Protocol_dir &parent)
+ : Vfs::File("new_socket"), _parent(parent) { }
+
+ bool poll(bool, Vfs::Vfs_handle::Context *) override { return true; }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ using namespace Linux;
+
+ socket *sock = nullptr;
+
+ int type = (_parent.type() == Lxip::Protocol_dir::TYPE_STREAM)
+ ? SOCK_STREAM : SOCK_DGRAM;
+
+ if (sock_create_kern(nullptr, AF_INET, type, 0, &sock)) {
+ kfree(sock);
+ return -1;
+ }
+ set_sock_wait(sock, 0);
+
+ try {
+ unsigned const id = _parent.adopt_socket(*sock, false);
+ return Genode::snprintf(dst, len, "%s/%u\n", _parent.top_dir(), id);
+ } catch (...) {
+ Genode::error("lxip: could not adopt socket");
+ }
+
+ return -1;
+ }
+};
+
+
+class Lxip::Protocol_dir_impl : public Protocol_dir,
+ public Vfs::Directory
+{
+ private:
+
+ Genode::Allocator &_alloc;
+ Vfs::File_system &_parent;
+ Vfs::Io_response_handler &_io_response_handler;
+
+ Type const _type;
+
+ /**************************
+ ** Simple node registry **
+ **************************/
+
+ enum { MAX_NODES = Lxip::MAX_SOCKETS + 1 };
+ Vfs::Node *_nodes[MAX_NODES];
+
+ unsigned _num_nodes()
+ {
+ unsigned n = 0;
+ for (Genode::size_t i = 0; i < MAX_NODES; i++)
+ n += (_nodes[i] != nullptr);
+ return n;
+ }
+
+ Vfs::Node **_unused_node()
+ {
+ for (Genode::size_t i = 0; i < MAX_NODES; i++)
+ if (_nodes[i] == nullptr) return &_nodes[i];
+ throw -1;
+ }
+
+ void _free_node(Vfs::Node *node)
+ {
+ for (Genode::size_t i = 0; i < MAX_NODES; i++)
+ if (_nodes[i] == node) {
+ _nodes[i] = nullptr;
+ break;
+ }
+ }
+
+ bool _is_root(const char *path)
+ {
+ return (Genode::strcmp(path, "") == 0) || (Genode::strcmp(path, "/") == 0);
+ }
+
+ Vfs::Lxip_new_socket_file _new_socket_file { *this };
+
+ public:
+
+ Protocol_dir_impl(Genode::Allocator &alloc,
+ Vfs::File_system &parent,
+ Vfs::Io_response_handler &io_response_handler,
+ char const *name,
+ Lxip::Protocol_dir::Type type)
+ :
+ Directory(name),
+ _alloc(alloc), _parent(parent), _io_response_handler(io_response_handler),
+ _type(type)
+ {
+ for (Genode::size_t i = 0; i < MAX_NODES; i++) {
+ _nodes[i] = nullptr;
+ }
+
+ _nodes[0] = &_new_socket_file;
+ }
+
+ ~Protocol_dir_impl() { }
+
+ Vfs::Node *lookup(char const *path)
+ {
+ if (*path == '/') path++;
+ if (*path == '\0') return this;
+
+ char const *p = path;
+ while (*++p && *p != '/');
+
+ for (Genode::size_t i = 0; i < MAX_NODES; i++) {
+ if (!_nodes[i]) continue;
+
+ if (Genode::strcmp(_nodes[i]->name(), path, (p - path)) == 0) {
+ Vfs::Directory *dir = dynamic_cast(_nodes[i]);
+ if (!dir) return _nodes[i];
+
+ Socket_dir *socket = dynamic_cast(_nodes[i]);
+ if (socket && socket->closed())
+ return nullptr;
+
+ if (*p == '/') return dir->child(p+1);
+ else return dir;
+ }
+ }
+
+ return nullptr;
+ }
+
+ Vfs::Directory_service::Unlink_result unlink(char const *path)
+ {
+ Vfs::Node *node = lookup(path);
+ if (!node) return Vfs::Directory_service::UNLINK_ERR_NO_ENTRY;
+
+ Vfs::Directory *dir = dynamic_cast(node);
+ if (!dir) return Vfs::Directory_service::UNLINK_ERR_NO_ENTRY;
+
+ _free_node(node);
+
+ Genode::destroy(&_alloc, dir);
+
+ return Vfs::Directory_service::UNLINK_OK;
+ }
+
+ /****************************
+ ** Protocol_dir interface **
+ ****************************/
+
+ char const *top_dir() override { return name(); }
+
+ Type type() { return _type; }
+
+ unsigned adopt_socket(Linux::socket &sock, bool from_accept)
+ {
+ Vfs::Node **node = _unused_node();
+ if (!node) throw -1;
+
+ unsigned const id = ((unsigned char*)node - (unsigned char*)_nodes)/sizeof(*_nodes);
+
+ Vfs::Lxip_socket_dir *dir = new (&_alloc)
+ Vfs::Lxip_socket_dir(_alloc, id, *this, _io_response_handler,
+ sock, from_accept);
+
+ *node = dir;
+ return id;
+ }
+
+ bool lookup_port(long port)
+ {
+ for (Genode::size_t i = 0; i < MAX_NODES; i++) {
+ if (_nodes[i] == nullptr) continue;
+
+ Lxip::Socket_dir *dir = dynamic_cast(_nodes[i]);
+ if (dir && dir->bind() == port) return true;
+ }
+ return false;
+ }
+
+ /*************************
+ ** Directory interface **
+ *************************/
+
+ void dirent(Vfs::file_offset index, Vfs::Directory_service::Dirent &out) override
+ {
+ out.fileno = index+1;
+ out.type = Vfs::Directory_service::DIRENT_TYPE_END;
+ out.name[0] = '\0';
+
+ Vfs::Node *node = nullptr;
+ for (Vfs::Node *n : _nodes) {
+ if (n) {
+ if (index == 0) {
+ node = n;
+ break;
+ }
+ --index;
+ }
+ }
+ if (!node) return;
+
+ if (dynamic_cast(node))
+ out.type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY;
+
+ if (dynamic_cast(node))
+ out.type = Vfs::Directory_service::DIRENT_TYPE_FILE;
+
+ Genode::strncpy(out.name, node->name(), sizeof(out.name));
+ }
+
+ Vfs::file_size num_dirent() override { return _num_nodes(); }
+
+ Vfs::Node *child(char const *name) override { return nullptr; }
+};
+
+
+class Vfs::Lxip_address_file : public Vfs::File
+{
+ private:
+
+ unsigned int &_numeric_address;
+
+ public:
+
+ Lxip_address_file(char const *name, unsigned int &numeric_address)
+ : Vfs::File(name), _numeric_address(numeric_address) { }
+
+ Lxip::ssize_t read(char *dst, Genode::size_t len,
+ file_size /* ignored */) override
+ {
+ enum {
+ MAX_ADDRESS_STRING_SIZE = sizeof("000.000.000.000\n")
+ };
+
+ Genode::String address {
+ Net::Ipv4_address(&_numeric_address)
+ };
+
+ Lxip::size_t n = min(len, strlen(address.string()));
+ memcpy(dst, address.string(), n);
+ if (n < len)
+ dst[n++] = '\n';
+
+ return n;
+ }
+};
+
+
+extern "C" unsigned int ic_myaddr;
+extern "C" unsigned int ic_netmask;
+extern "C" unsigned int ic_gateway;
+extern "C" unsigned int ic_nameservers[1];
+
+
+/*******************************
+ ** Filesystem implementation **
+ *******************************/
+
+class Vfs::Lxip_file_system : public Vfs::File_system,
+ public Vfs::Directory
+{
+ private:
+
+ Genode::Entrypoint &_ep;
+ Genode::Allocator &_alloc;
+ Vfs::Io_response_handler &_io_response_handler;
+
+ Lxip::Protocol_dir_impl _tcp_dir {
+ _alloc, *this, _io_response_handler, "tcp", Lxip::Protocol_dir::TYPE_STREAM };
+ Lxip::Protocol_dir_impl _udp_dir {
+ _alloc, *this, _io_response_handler, "udp", Lxip::Protocol_dir::TYPE_DGRAM };
+
+ Lxip_address_file _address { "address", ic_myaddr };
+ Lxip_address_file _netmask { "netmask", ic_netmask };
+ Lxip_address_file _gateway { "gateway", ic_gateway };
+ Lxip_address_file _nameserver { "nameserver", ic_nameservers[0] };
+
+ Vfs::Node *_lookup(char const *path)
+ {
+ if (*path == '/') path++;
+ if (*path == '\0') return this;
+
+ if (Genode::strcmp(path, "tcp", 3) == 0)
+ return _tcp_dir.lookup(&path[3]);
+
+ if (Genode::strcmp(path, "udp", 3) == 0)
+ return _udp_dir.lookup(&path[3]);
+
+ if (Genode::strcmp(path, _address.name(),
+ strlen(_address.name()) + 1) == 0)
+ return &_address;
+
+ if (Genode::strcmp(path, _netmask.name(),
+ strlen(_netmask.name()) + 1) == 0)
+ return &_netmask;
+
+ if (Genode::strcmp(path, _gateway.name(),
+ strlen(_gateway.name()) + 1) == 0)
+ return &_gateway;
+
+ if (Genode::strcmp(path, _nameserver.name(),
+ strlen(_nameserver.name()) + 1) == 0)
+ return &_nameserver;
+
+ return nullptr;
+ }
+
+ bool _is_root(const char *path)
+ {
+ return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
+ }
+
+ public:
+
+ Lxip_file_system(Genode::Env &env, Genode::Allocator &alloc,
+ Genode::Xml_node config,
+ Vfs::Io_response_handler &io_response_handler)
+ :
+ Directory(""),
+ _ep(env.ep()), _alloc(alloc), _io_response_handler(io_response_handler)
+ {
+ apply_config(config);
+ }
+
+ ~Lxip_file_system() { }
+
+ char const *name() { return "lxip"; }
+ char const *type() override { return "lxip"; }
+
+ /***************************
+ ** File_system interface **
+ ***************************/
+
+ void apply_config(Genode::Xml_node const &config) override
+ {
+ typedef String<16> Addr;
+
+ try {
+
+ if (config.attribute_value("dhcp", false)) {
+ log("Using DHCP for interface configuration.");
+ lxip_configure_dhcp();
+ return;
+ }
+
+ } catch (...) { }
+
+ try {
+
+ Addr ip_addr = config.attribute_value("ip_addr", Addr());
+ Addr netmask = config.attribute_value("netmask", Addr());
+ Addr gateway = config.attribute_value("gateway", Addr());
+ Addr nameserver = config.attribute_value("nameserver", Addr());
+
+ /* either none or all 4 interface attributes must exist */
+ if (ip_addr == "") {
+ warning("Missing \"ip_addr\" attribute. Ignoring network interface config.");
+ throw Genode::Xml_node::Nonexistent_attribute();
+ } else if (netmask == "") {
+ warning("Missing \"netmask\" attribute. Ignoring network interface config.");
+ throw Genode::Xml_node::Nonexistent_attribute();
+ } else if (gateway == "") {
+ warning("Missing \"gateway\" attribute. Ignoring network interface config.");
+ throw Genode::Xml_node::Nonexistent_attribute();
+ } else if (nameserver == "") {
+ warning("Missing \"nameserver\" attribute. Ignoring network interface config.");
+ throw Genode::Xml_node::Nonexistent_attribute();
+ }
+
+ log("static network interface: ip_addr=",ip_addr," netmask=",netmask," gateway=",gateway);
+
+ lxip_configure_static(ip_addr.string(), netmask.string(),
+ gateway.string(), nameserver.string());
+ } catch (...) { }
+ }
+
+
+ /*************************
+ ** Directory interface **
+ *************************/
+
+ void dirent(file_offset index, Directory_service::Dirent &out) override
+ {
+ if (index == 0) {
+ out.fileno = (Genode::addr_t)&_tcp_dir;
+ out.type = Directory_service::DIRENT_TYPE_DIRECTORY;
+ Genode::strncpy(out.name, "tcp", sizeof(out.name));
+ } else if (index == 1) {
+ out.fileno = (Genode::addr_t)&_udp_dir;
+ out.type = Directory_service::DIRENT_TYPE_DIRECTORY;
+ Genode::strncpy(out.name, "udp", sizeof(out.name));
+ } else if (index == 2) {
+ out.fileno = (Genode::addr_t)&_address;
+ out.type = Directory_service::DIRENT_TYPE_FILE;
+ Genode::strncpy(out.name, "address", sizeof(out.name));
+ } else if (index == 3) {
+ out.fileno = (Genode::addr_t)&_netmask;
+ out.type = Directory_service::DIRENT_TYPE_FILE;
+ Genode::strncpy(out.name, "netmask", sizeof(out.name));
+ } else if (index == 4) {
+ out.fileno = (Genode::addr_t)&_gateway;
+ out.type = Directory_service::DIRENT_TYPE_FILE;
+ Genode::strncpy(out.name, "gateway", sizeof(out.name));
+ } else if (index == 5) {
+ out.fileno = (Genode::addr_t)&_nameserver;
+ out.type = Directory_service::DIRENT_TYPE_FILE;
+ Genode::strncpy(out.name, "nameserver", sizeof(out.name));
+ } else {
+ out.fileno = 0;
+ out.type = Directory_service::DIRENT_TYPE_END;
+ out.name[0] = '\0';
+ }
+ }
+
+ file_size num_dirent() override { return 6; }
+
+ Vfs::Node *child(char const *name) override { return nullptr; }
+
+ /*********************************
+ ** Directory-service interface **
+ *********************************/
+
+ Dataspace_capability dataspace(char const *path) override {
+ return Dataspace_capability(); }
+
+ void release(char const *path, Dataspace_capability ds_cap) override { }
+
+ Stat_result stat(char const *path, Stat &out) override
+ {
+ Vfs::Node *node = _lookup(path);
+ if (!node) return STAT_ERR_NO_ENTRY;
+
+ Vfs::Directory *dir = dynamic_cast(node);
+ if (dir) {
+ out.mode = STAT_MODE_DIRECTORY | 0777;
+ return STAT_OK;
+ }
+
+ if (dynamic_cast(node)) {
+ out.mode = STAT_MODE_FILE | 0666;
+ out.size = 0;
+ return STAT_OK;
+ }
+
+ if (dynamic_cast(node)) {
+ out.mode = STAT_MODE_FILE | 0666;
+ out.size = 0x1000; /* there may be something to read */
+ return STAT_OK;
+ }
+
+ return STAT_ERR_NO_ENTRY;
+ }
+
+ Dirent_result dirent(char const *path, file_offset index, Dirent &out) override
+ {
+ Vfs::Node *node = _lookup(path);
+ if (!node) return DIRENT_ERR_INVALID_PATH;
+
+ Vfs::Directory *dir = dynamic_cast(node);
+ if (dir) {
+ dir->dirent(index, out);
+ return DIRENT_OK;
+ }
+
+ return DIRENT_ERR_INVALID_PATH;
+ }
+
+ file_size num_dirent(char const *path) override
+ {
+ if (_is_root(path)) return num_dirent();
+
+ Vfs::Node *node = _lookup(path);
+ if (!node) return 0;
+
+ Vfs::Directory *dir = dynamic_cast(node);
+ if (!dir) return 0;
+
+ return dir->num_dirent();
+ }
+
+ bool directory(char const *path) override
+ {
+ Vfs::Node *node = _lookup(path);
+ return node ? dynamic_cast(node) : 0;
+ }
+
+ char const *leaf_path(char const *path) override
+ {
+ return path;
+ }
+
+ Open_result open(char const *path, unsigned mode,
+ Vfs_handle **out_handle,
+ Genode::Allocator &alloc) override
+ {
+ if (mode & OPEN_MODE_CREATE) return OPEN_ERR_NO_PERM;
+
+ Vfs::Node *node = _lookup(path);
+ if (!node) return OPEN_ERR_UNACCESSIBLE;
+
+ Vfs::File *file = dynamic_cast(node);
+ if (file) {
+ Lxip_vfs_handle *handle =
+ new (alloc) Vfs::Lxip_vfs_handle(*this, alloc, 0, *file);
+ *out_handle = handle;
+
+ return OPEN_OK;
+ }
+
+ return OPEN_ERR_UNACCESSIBLE;
+ }
+
+ void close(Vfs_handle *vfs_handle) override
+ {
+ Lxip_vfs_handle *handle =
+ static_cast(vfs_handle);
+ if (handle) {
+ _polling_handles.remove(&handle->polling_le);
+ Genode::destroy(handle->alloc(), handle);
+ }
+ }
+
+ Unlink_result unlink(char const *path) override
+ {
+ if (*path == '/') path++;
+
+ if (Genode::strcmp(path, "tcp", 3) == 0)
+ return _tcp_dir.unlink(&path[3]);
+ if (Genode::strcmp(path, "udp", 3) == 0)
+ return _udp_dir.unlink(&path[3]);
+ return UNLINK_ERR_NO_ENTRY;
+ }
+
+ Readlink_result readlink(char const *, char *, file_size,
+ file_size &) override {
+ return READLINK_ERR_NO_ENTRY; }
+
+ Rename_result rename(char const *, char const *) override {
+ return RENAME_ERR_NO_PERM; }
+
+ Mkdir_result mkdir(char const *, unsigned) override {
+ return MKDIR_ERR_NO_PERM; }
+
+ Symlink_result symlink(char const *, char const *) override {
+ return SYMLINK_ERR_NO_ENTRY; }
+
+ /*************************************
+ ** Lxip_file I/O service interface **
+ *************************************/
+
+ Write_result write(Vfs_handle *vfs_handle, char const *src,
+ file_size count,
+ file_size &out_count) override
+ {
+ Vfs::File &file =
+ static_cast(vfs_handle)->file;
+
+ if (!count)
+ Genode::error("zero write of ",file.name());
+
+ if (count) try {
+ Lxip::ssize_t res = file.write(src, count, vfs_handle->seek());
+ if (res < 0) return WRITE_ERR_IO;
+
+ out_count = res;
+
+ } catch (File::Would_block) { return WRITE_ERR_WOULD_BLOCK; }
+ return WRITE_OK;
+ }
+
+ Read_result read(Vfs::Vfs_handle *vfs_handle, char *dst,
+ Vfs::file_size count,
+ Vfs::file_size &out_count) override
+ {
+ Vfs::File &file =
+ static_cast(vfs_handle)->file;
+
+ if (!count)
+ Genode::error("zero read of ",file.name());
+
+ if (count) try {
+ Lxip::ssize_t res = file.read(dst, count, vfs_handle->seek());
+ if (res < 0) return READ_ERR_IO;
+
+ out_count = res;
+
+ } catch (File::Would_block) { return READ_ERR_WOULD_BLOCK; }
+ return READ_OK;
+ }
+
+ /* XXX check if queue_read / complete_read semantics fit better */
+
+ Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size) override
+ {
+ /* report ok because libc always executes ftruncate() when opening rw */
+ return FTRUNCATE_OK;
+ }
+
+ bool notify_read_ready(Vfs_handle *vfs_handle) override
+ {
+ Lxip_vfs_handle *handle =
+ static_cast(vfs_handle);
+
+ if (dynamic_cast(&handle->file)) {
+ _polling_handles.remove(&handle->polling_le);
+ _polling_handles.insert(&handle->polling_le);
+ }
+
+ return true;
+ }
+
+ bool read_ready(Vfs_handle *vfs_handle) override
+ {
+ Lxip_vfs_handle *handle =
+ static_cast(vfs_handle);
+
+ /* TODO when to _polling_handles.remove(handle); ? */
+ return handle->file.poll();
+ }
+};
+
+
+struct Lxip_factory : Vfs::File_system_factory
+{
+ struct Init
+ {
+ char _config_buf[128];
+
+ char *_parse_config(Genode::Xml_node);
+
+ Init(Genode::Env &env,
+ Genode::Allocator &alloc)
+ {
+ Lx_kit::Env &lx_env = Lx_kit::construct_env(env);
+
+ Lx::lxcc_emul_init(lx_env);
+ Lx::malloc_init(env, lx_env.heap());
+ Lx::timer_init(env, lx_env.env().ep(), lx_env.heap(), &poll_all);
+ Lx::event_init(env, lx_env.env().ep(), &poll_all);
+ Lx::nic_client_init(env, lx_env.env().ep(), lx_env.heap(), &poll_all);
+
+ lxip_init();
+ }
+ };
+
+ Vfs::File_system *create(Genode::Env &env,
+ Genode::Allocator &alloc,
+ Genode::Xml_node config,
+ Vfs::Io_response_handler &io_handler) override
+ {
+ static Init inst(env, alloc);
+ return new (alloc) Vfs::Lxip_file_system(env, alloc, config, io_handler);
+ }
+};
+
+
+extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
+{
+ static Lxip_factory factory;
+ return &factory;
+}
diff --git a/repos/dde_linux/src/test/vfs_lxip/main.cc b/repos/dde_linux/src/test/vfs_lxip/main.cc
new file mode 100644
index 000000000..3b3dc566a
--- /dev/null
+++ b/repos/dde_linux/src/test/vfs_lxip/main.cc
@@ -0,0 +1,271 @@
+/*
+ * \brief Simple test for lxip VFS plugin
+ * \author Emery Hemingway
+ * \author Josef Söntgen
+ * \author Christian Helmuth
+ * \date 2016-10-17
+ */
+
+/*
+ * Copyright (C) 2016-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+
+static void ls_socket_fs(char const *path, bool top = true)
+{
+ if (top)
+ printf("recursive listing of %s:\n", path);
+
+ DIR *dp = opendir(path);
+ if (dp == NULL) {
+ perror("opendir");
+ abort();
+ }
+
+ struct dirent *dent = NULL;
+ while ((dent = readdir(dp))) {
+ int d = dent->d_type == DT_DIR;
+ printf(" %s %s/%s\n", d ? "d" : "f", path, dent->d_name);
+
+ if (d) {
+ char subdir[128];
+ snprintf(subdir, sizeof(subdir), "%s/%s", path, dent->d_name);
+ ls_socket_fs(subdir, false);
+ }
+ }
+ closedir(dp);
+}
+
+
+static int remove_sock_dir(char const *sock_root, char const *sock_fd)
+{
+ char sock_dir[64];
+ snprintf(sock_dir, sizeof(sock_dir), "%s/%s", sock_root, sock_fd);
+ return unlink(sock_dir);
+}
+
+
+static int recv_client(char const *sock_root, char const *sock_fd)
+{
+ char sock_data[96];
+ snprintf(sock_data, sizeof(sock_data), "%s/%s/data", sock_root, sock_fd);
+
+ int fd = open(sock_data, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ return -1;
+ }
+
+ char dst;
+ ssize_t n = read(fd, &dst, 1);
+ printf("receiving data from client %s\n",
+ n > 0 ? "successful" : "failed");
+
+ close(fd);
+ return 0;
+}
+
+
+static int send_client(char const *sock_root, char const *sock_fd,
+ char const *src, size_t len)
+{
+ char sock_data[96];
+ snprintf(sock_data, sizeof(sock_data), "%s/%s/data", sock_root, sock_fd);
+
+ int fd = open(sock_data, O_WRONLY);
+ if (fd == -1) {
+ perror("open");
+ return -1;
+ }
+
+ ssize_t n = write(fd, src, len);
+ printf("sending data to client %s\n",
+ n > 0 && (size_t)n == len ? "successful" : "failed");
+
+ close(fd);
+ return 0;
+}
+
+
+static void sock_info(char const *sock_root, char const *sock_fd)
+{
+ size_t n;
+
+ char local[96];
+ snprintf(local, sizeof(local), "%s/%s/local", sock_root, sock_fd);
+
+ int fdl = open(local, O_RDONLY);
+ if (fdl == -1) {
+ perror("open");
+ return;
+ }
+
+ n = read(fdl, local, sizeof(local));
+ local[n-1] = '\0';
+ printf("local: %s\n", local);
+ close(fdl);
+
+ char remote[96];
+ snprintf(remote, sizeof(remote), "%s/%s/remote", sock_root, sock_fd);
+
+ int fdr = open(remote, O_RDONLY);
+ if (fdr == -1) {
+ perror("open");
+ return;
+ }
+
+ n = read(fdr, remote, sizeof(remote));
+ remote[n-1] = '\0';
+ printf("remote: %s\n", remote);
+ close(fdr);
+}
+
+
+static int test_bind_accept(char const *sock_root, char const *sock_fd)
+{
+ ssize_t n;
+
+ /* bind */
+ char sock_bind[96];
+ snprintf(sock_bind, sizeof(sock_bind), "%s/%s/bind", sock_root, sock_fd);
+
+ int fdb = open(sock_bind, O_RDWR);
+ if (fdb == -1) {
+ perror("open");
+ return -1;
+ }
+
+ char addr[] = "0.0.0.0:80";
+ n = write(fdb, addr, sizeof(addr));
+ printf("binding to: %s %s\n", addr, n > 0 ? "success" : "failed");
+ close(fdb);
+
+ char sock_listen[96];
+ snprintf(sock_listen, sizeof(sock_listen), "%s/%s/listen", sock_root, sock_fd);
+
+ int fdl = open(sock_listen, O_RDWR);
+ if (fdl == -1) {
+ perror("open");
+ return -1;
+ }
+
+ char backlog[] = "5";
+ n = write(fdl, backlog, sizeof(backlog));
+ printf("listen backlog: %s %s\n", backlog, n > 0 ? "success" : "failed");
+ close(fdl);
+
+
+ /* accept */
+ int res = 0;
+ while (1) {
+ char sock_accept[96];
+ snprintf(sock_accept, sizeof(sock_accept), "%s/%s/accept", sock_root, sock_fd);
+
+ ls_socket_fs(sock_root);
+
+ int fda = open(sock_accept, O_RDWR);
+ if (fda == -1) {
+ perror("open");
+ res = -1;
+ break;
+ }
+
+ char client_fd[8] = { 0 };
+ res = read(fda, client_fd, sizeof(client_fd));
+ close(fda);
+
+ if (res < 0) break;
+ else if (res == 0) continue;
+
+ client_fd[res-1] = '\0';
+
+ printf("accept socket: %s\n", client_fd);
+
+ sock_info(sock_root, client_fd);
+
+ char hello[] = "hello w0rld!\n";
+ recv_client(sock_root, client_fd);
+ send_client(sock_root, client_fd, hello, sizeof(hello));
+ remove_sock_dir(sock_root, client_fd);
+ }
+
+ return res;
+}
+
+
+static int test_connect_recv(char const *sock_root, char const *sock_fd)
+{
+ char sock_connect[96];
+ snprintf(sock_connect, sizeof(sock_connect), "%s/%s/connect", sock_root, sock_fd);
+
+ int fd = open(sock_connect, O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ return -1;
+ }
+
+ char host[] = "10.0.2.1:80";
+ ssize_t n = write(fd, host, sizeof(host));
+ (void)n;
+ close(fd);
+}
+
+
+static void test_proto(char const *sock_root, char const *proto)
+{
+ char proto_root[64];
+ snprintf(proto_root, sizeof(proto_root), "%s/%s", sock_root, proto);
+
+ ls_socket_fs(proto_root);
+
+ char new_socket_path[64];
+ snprintf(new_socket_path, sizeof(new_socket_path), "%s/new_socket", proto_root);
+
+ int fd = open(new_socket_path, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ abort();
+ }
+
+ char sock_path[16];
+ size_t n = read(fd, sock_path, sizeof(sock_path));
+ sock_path[n-1] = '\0';
+ close(fd);
+
+ ls_socket_fs(proto_root);
+
+ char sock_dir[64];
+ snprintf(sock_dir, sizeof(sock_dir), "%s/%s", sock_root, sock_path);
+
+ ls_socket_fs(sock_dir);
+
+ test_bind_accept(sock_root, sock_path);
+ // test_connect_recv(proto_root, sock_fd);
+
+ ls_socket_fs(sock_dir);
+
+ remove_sock_dir(sock_root, sock_path);
+}
+
+
+int main()
+{
+ char const *socket_fs = "/socket";
+
+ ls_socket_fs(socket_fs);
+ test_proto(socket_fs, "tcp");
+ test_proto(socket_fs, "udp");
+ ls_socket_fs(socket_fs);
+}
diff --git a/repos/dde_linux/src/test/vfs_lxip/target.mk b/repos/dde_linux/src/test/vfs_lxip/target.mk
new file mode 100644
index 000000000..13e9feb57
--- /dev/null
+++ b/repos/dde_linux/src/test/vfs_lxip/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-vfs_lxip
+SRC_CC = main.cc
+LIBS = posix