Implement SO_RCVBUF and SO_SNDBUF for filesystem sockets.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-04-30 17:25:52 +02:00
parent feea0786fc
commit 2ff72426ec
5 changed files with 305 additions and 7 deletions

View File

@ -129,6 +129,7 @@ resource.o \
scheduler.o \
segment.o \
signal.o \
sockopt.o \
string.o \
symbol.o \
syscall.o \

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/kernel/sockopt.h
getsockopt() and setsockopt() utility functions.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_KERNEL_SOCKOPT_H
#define INCLUDE_SORTIX_KERNEL_SOCKOPT_H
#include <stddef.h>
#include <stdint.h>
#include <sortix/kernel/ioctx.h>
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

View File

@ -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 <sys/socket.h>
#include <sys/un.h>
#include <assert.h>
@ -47,15 +48,10 @@
#include <sortix/kernel/poll.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/sockopt.h>
#include "fs.h"
// TODO: This is declared in the <sys/socket.h> 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;

View File

@ -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:

137
kernel/sockopt.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
sockopt.cpp
getsockopt() and setsockopt() utility functions.
*******************************************************************************/
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/sockopt.h>
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