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