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