From 2ff72426ecbaf34d16d3475bafdf5fe8c8a699ca Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Wed, 30 Apr 2014 17:25:52 +0200 Subject: [PATCH] Implement SO_RCVBUF and SO_SNDBUF for filesystem sockets. --- kernel/Makefile | 1 + kernel/include/sortix/kernel/sockopt.h | 42 ++++++++ kernel/net/fs.cpp | 65 ++++++++++-- kernel/pipe.cpp | 67 ++++++++++++ kernel/sockopt.cpp | 137 +++++++++++++++++++++++++ 5 files changed, 305 insertions(+), 7 deletions(-) create mode 100644 kernel/include/sortix/kernel/sockopt.h create mode 100644 kernel/sockopt.cpp diff --git a/kernel/Makefile b/kernel/Makefile index c3783c9c..3308b54b 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -129,6 +129,7 @@ resource.o \ scheduler.o \ segment.o \ signal.o \ +sockopt.o \ string.o \ symbol.o \ syscall.o \ diff --git a/kernel/include/sortix/kernel/sockopt.h b/kernel/include/sortix/kernel/sockopt.h new file mode 100644 index 00000000..65dfb728 --- /dev/null +++ b/kernel/include/sortix/kernel/sockopt.h @@ -0,0 +1,42 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/sockopt.h + getsockopt() and setsockopt() utility functions. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_SOCKOPT_H +#define INCLUDE_SORTIX_KERNEL_SOCKOPT_H + +#include +#include + +#include + +namespace Sortix { + +bool sockopt_fetch_uintmax(uintmax_t* optval_ptr, ioctx_t* ctx, + const void* option_value, size_t option_size); +bool sockopt_return_uintmax(uintmax_t optval, ioctx_t* ctx, + void* option_value, size_t* option_size_ptr); + +} // namespace Sortix + +#endif diff --git a/kernel/net/fs.cpp b/kernel/net/fs.cpp index 592a28d7..95c3ee31 100644 --- a/kernel/net/fs.cpp +++ b/kernel/net/fs.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013. + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. This file is part of Sortix. @@ -24,6 +24,7 @@ // TODO: Should this be moved into user-space? +#include #include #include @@ -47,15 +48,10 @@ #include #include #include +#include #include "fs.h" -// TODO: This is declared in the header that isn't ready for -// kernel usage, so we declare it here for now. -#ifndef AF_UNIX -#define AF_UNIX 3 -#endif - namespace Sortix { namespace NetFS { @@ -104,6 +100,10 @@ public: virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); virtual int poll(ioctx_t* ctx, PollNode* node); + virtual int getsockopt(ioctx_t* ctx, int level, int option_name, + void* option_value, size_t* option_size_ptr); + virtual int setsockopt(ioctx_t* ctx, int level, int option_name, + const void* option_value, size_t option_size); private: int do_bind(ioctx_t* ctx, const uint8_t* addr, size_t addrsize); @@ -302,6 +302,57 @@ int StreamSocket::poll(ioctx_t* ctx, PollNode* node) return errno = ENOTCONN, -1; } +int StreamSocket::getsockopt(ioctx_t* ctx, int level, int option_name, + void* option_value, size_t* option_size_ptr) +{ + if ( level != SOL_SOCKET ) + return errno = EINVAL, -1; + + uintmax_t result = 0; + switch ( option_name ) + { + case SO_RCVBUF: result = incoming.Size(); break; + case SO_SNDBUF: result = outgoing.Size(); break; + default: return errno = ENOPROTOOPT, -1; break; + } + + if ( !sockopt_return_uintmax(result, ctx, option_value, option_size_ptr) ) + return -1; + + return 0; +} + +int StreamSocket::setsockopt(ioctx_t* ctx, int level, int option_name, + const void* option_value, size_t option_size) +{ + if ( level != SOL_SOCKET ) + return errno = EINVAL, -1; + + uintmax_t value; + if ( !sockopt_fetch_uintmax(&value, ctx, option_value, option_size) ) + return -1; + + switch ( option_name ) + { + case SO_RCVBUF: + if ( SIZE_MAX < value ) + return errno = EINVAL, -1; + if ( !incoming.Resize((size_t) value) ) + return -1; + break; + case SO_SNDBUF: + if ( SIZE_MAX < value ) + return errno = EINVAL, -1; + if ( !outgoing.Resize((size_t) value) ) + return -1; + break; + default: + return errno = ENOPROTOOPT, -1; + } + + return 0; +} + Manager::Manager(uid_t owner, gid_t group, mode_t mode) { inode_type = INODE_TYPE_UNKNOWN; diff --git a/kernel/pipe.cpp b/kernel/pipe.cpp index 7f5520a2..a59959fa 100644 --- a/kernel/pipe.cpp +++ b/kernel/pipe.cpp @@ -62,6 +62,10 @@ public: void PerhapsShutdown(); bool GetSIGPIPEDelivery(); void SetSIGPIPEDelivery(bool deliver_sigpipe); + size_t ReadSize(); + size_t WriteSize(); + bool ReadResize(size_t new_size); + bool WriteResize(size_t new_size); ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); int read_poll(ioctx_t* ctx, PollNode* node); @@ -81,6 +85,7 @@ private: size_t bufferoffset; size_t bufferused; size_t buffersize; + size_t pretended_read_buffer_size; bool anyreading; bool anywriting; bool is_sigpipe_enabled; @@ -244,6 +249,56 @@ void PipeChannel::SetSIGPIPEDelivery(bool deliver_sigpipe) is_sigpipe_enabled = deliver_sigpipe; } +size_t PipeChannel::ReadSize() +{ + ScopedLockSignal lock(&pipelock); + return pretended_read_buffer_size; +} + +size_t PipeChannel::WriteSize() +{ + ScopedLockSignal lock(&pipelock); + return buffersize; +} + +bool PipeChannel::ReadResize(size_t new_size) +{ + ScopedLockSignal lock(&pipelock); + if ( !new_size ) + return errno = EINVAL, false; + // The read and write end share the same buffer, so let the write end decide + // how big a buffer it wants and pretend the read end can decide too. + pretended_read_buffer_size = new_size; + return true; +} + +bool PipeChannel::WriteResize(size_t new_size) +{ + ScopedLockSignal lock(&pipelock); + if ( !new_size ) + return errno = EINVAL, false; + + size_t MAX_PIPE_SIZE = 2 * 1024 * 1024; + if ( MAX_PIPE_SIZE < new_size ) + new_size = MAX_PIPE_SIZE; + + // Refuse to lose data if the the new size would cause truncation. + if ( new_size < bufferused ) + new_size = bufferused; + + uint8_t* new_buffer = new uint8_t[new_size]; + if ( !new_buffer ) + return false; + + for ( size_t i = 0; i < bufferused; i++ ) + new_buffer[i] = buffer[(bufferoffset + i) % buffersize]; + delete[] buffer; + buffer = new_buffer; + buffersize = new_size; + + return true; +} + PipeEndpoint::PipeEndpoint() { channel = NULL; @@ -316,6 +371,18 @@ bool PipeEndpoint::SetSIGPIPEDelivery(bool deliver_sigpipe) return true; } +size_t PipeEndpoint::Size() +{ + return reading ? channel->ReadSize() + : channel->WriteSize(); +} + +bool PipeEndpoint::Resize(size_t new_size) +{ + return reading ? channel->ReadResize(new_size) + : channel->WriteResize(new_size); +} + class PipeNode : public AbstractInode { public: diff --git a/kernel/sockopt.cpp b/kernel/sockopt.cpp new file mode 100644 index 00000000..a82f9206 --- /dev/null +++ b/kernel/sockopt.cpp @@ -0,0 +1,137 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sockopt.cpp + getsockopt() and setsockopt() utility functions. + +*******************************************************************************/ + +#include +#include +#include + +#include +#include + +namespace Sortix { + +bool sockopt_fetch_uintmax(uintmax_t* optval_ptr, ioctx_t* ctx, + const void* option_value, size_t option_size) +{ + uintmax_t optval; + + if ( option_size == 0 ) + { + optval = 0; + } + else if ( option_size == 1 ) + { + uint8_t truncated; + if ( !ctx->copy_from_src(&truncated, option_value, sizeof(truncated)) ) + return false; + optval = truncated; + } + else if ( option_size == 2 ) + { + uint16_t truncated; + if ( !ctx->copy_from_src(&truncated, option_value, sizeof(truncated)) ) + return false; + optval = truncated; + } + else if ( option_size == 4 ) + { + uint32_t truncated; + if ( !ctx->copy_from_src(&truncated, option_value, sizeof(truncated)) ) + return false; + optval = truncated; + } + else if ( option_size == 8 ) + { + uint64_t truncated; + if ( !ctx->copy_from_src(&truncated, option_value, sizeof(truncated)) ) + return false; + optval = truncated; + } +#if UINT64_MAX < UINTMAX_MAX + #error "Add support for your large uintmax_t" +#endif + else + { + return errno = EINVAL, false; + } + + return *optval_ptr = optval, true; +} + +bool sockopt_return_uintmax(uintmax_t optval, ioctx_t* ctx, + void* option_value, size_t* option_size_ptr) +{ + size_t option_size; + if ( !ctx->copy_from_src(&option_size, option_size_ptr, sizeof(option_size)) ) + return false; + + if ( /*0 <= option_size &&*/ option_size < 1 ) + { + option_size = 0; + } + else if ( 1 <= option_size && option_size < 2 ) + { + if ( UINT8_MAX < optval ) + return errno = ERANGE, false; + uint8_t value = optval; + option_size = sizeof(value); + if ( !ctx->copy_to_dest(option_value, &value, sizeof(value)) ) + return false; + } + else if ( 2 <= option_size && option_size < 4 ) + { + if ( UINT16_MAX < optval ) + return errno = ERANGE, false; + uint16_t value = optval; + option_size = sizeof(value); + if ( !ctx->copy_to_dest(option_value, &value, sizeof(value)) ) + return false; + } + else if ( 4 <= option_size && option_size < 8 ) + { + if ( UINT32_MAX < optval ) + return errno = ERANGE, false; + uint32_t value = optval; + option_size = sizeof(value); + if ( !ctx->copy_to_dest(option_value, &value, sizeof(value)) ) + return false; + } + else if ( 8 <= option_size /* && option_size < 16 */ ) + { +#if UINT64_MAX < UINTMAX_MAX + #error "Add support for your large uintmax_t" +#endif + uint64_t value = optval; + option_size = sizeof(value); + if ( !ctx->copy_to_dest(option_value, &value, sizeof(value)) ) + return false; + } + + if ( !ctx->copy_to_dest(option_size_ptr, &option_size, sizeof(option_size)) ) + return false; + + return true; +} + +} // namespace Sortix