kernel+libc: Add basic Unix sockets (creation and binding)

This commit is contained in:
apio 2023-07-27 16:37:10 +02:00
parent c819a0cfc1
commit ca3f3fbe16
Signed by: apio
GPG Key ID: B8A7D06E42258954
11 changed files with 403 additions and 1 deletions

View File

@ -41,11 +41,13 @@ set(SOURCES
src/sys/mount.cpp src/sys/mount.cpp
src/sys/resource.cpp src/sys/resource.cpp
src/sys/signal.cpp src/sys/signal.cpp
src/sys/socket.cpp
src/fs/VFS.cpp src/fs/VFS.cpp
src/fs/Pipe.cpp src/fs/Pipe.cpp
src/fs/Mount.cpp src/fs/Mount.cpp
src/fs/MBR.cpp src/fs/MBR.cpp
src/fs/GPT.cpp src/fs/GPT.cpp
src/net/UnixSocket.cpp
src/fs/tmpfs/FileSystem.cpp src/fs/tmpfs/FileSystem.cpp
src/fs/tmpfs/Inode.cpp src/fs/tmpfs/Inode.cpp
src/fs/ext2/FileSystem.cpp src/fs/ext2/FileSystem.cpp

114
kernel/src/net/Socket.h Normal file
View File

@ -0,0 +1,114 @@
#pragma once
#include "fs/VFS.h"
#include <bits/socket.h>
class Socket : public VFS::FileInode
{
public:
Socket() = default;
VFS::InodeType type() const override
{
return VFS::InodeType::Socket;
}
void set_fs(VFS::FileSystem* fs)
{
m_fs = fs;
}
void set_inode_number(usize inum)
{
m_inode_number = inum;
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
usize inode_number() const override
{
return m_inode_number;
}
Result<usize> read(u8* buf, usize, usize length) const override
{
return recv(buf, length, 0);
}
Result<usize> write(const u8* buf, usize, usize length) override
{
return send(buf, length, 0);
}
virtual Result<usize> send(const u8*, usize, int) = 0;
virtual Result<usize> recv(u8*, usize, int) const = 0;
virtual Result<void> bind(SharedPtr<Socket>, struct sockaddr*, socklen_t) = 0;
virtual Result<void> connect(struct sockaddr*, socklen_t) = 0;
Result<void> truncate(usize) override
{
return err(EINVAL);
}
usize size() const override
{
return 0;
}
mode_t mode() const override
{
return m_mode;
}
u32 uid() const override
{
return m_uid;
}
u32 gid() const override
{
return m_gid;
}
nlink_t nlinks() const override
{
return (nlink_t)m_nlinks;
}
Result<void> chmod(mode_t mode) override
{
m_mode = mode;
return {};
}
Result<void> chown(u32 uid, u32 gid) override
{
m_uid = uid;
m_gid = gid;
return {};
}
void did_link() override
{
m_nlinks++;
}
void did_unlink() override
{
m_nlinks--;
}
virtual ~Socket() = default;
protected:
VFS::FileSystem* m_fs;
usize m_inode_number { 0 };
mode_t m_mode;
u32 m_uid { 0 };
u32 m_gid { 0 };
u32 m_nlinks { 0 };
};

View File

@ -0,0 +1,104 @@
#include "net/UnixSocket.h"
#include <luna/PathParser.h>
#include <thread/Scheduler.h>
UnixSocket::UnixSocket()
{
}
UnixSocket::UnixSocket(UnixSocket* peer) : m_state(State::Connected), m_peer(peer)
{
}
UnixSocket::~UnixSocket()
{
if (m_peer)
{
m_peer->m_peer = nullptr;
m_peer->m_state = State::Reset;
}
}
void UnixSocket::connect_to_peer(UnixSocket* peer)
{
m_peer = peer;
m_state = State::Connected;
}
Result<usize> UnixSocket::send(const u8* buf, usize length, int)
{
if (m_state == State::Reset) return err(ECONNRESET);
if (m_state != State::Connected) return err(ENOTCONN);
check(m_peer);
TRY(m_peer->m_data.append_data(buf, length));
return length;
}
Result<usize> UnixSocket::recv(u8* buf, usize length, int) const
{
if (m_state == State::Reset) return err(ECONNRESET);
if (m_state != State::Connected) return err(ENOTCONN);
return m_data.dequeue_data(buf, length);
}
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
SharedPtr<UnixSocket> socket)
{
auto parent_path = TRY(PathParser::dirname(path));
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, working_directory));
if (!VFS::can_write(parent_inode, auth)) return err(EACCES);
auto child_name = TRY(PathParser::basename(path));
TRY(VFS::validate_filename(child_name.view()));
socket->set_inode_number(TRY(parent_inode->fs()->allocate_inode_number()));
socket->set_fs(parent_inode->fs());
return parent_inode->add_entry(socket, child_name.chars());
}
Result<void> UnixSocket::bind(SharedPtr<Socket> socket, struct sockaddr* addr, socklen_t addrlen)
{
if (!addr) return err(EDESTADDRREQ);
if (addr->sa_family != AF_UNIX) return err(EAFNOSUPPORT);
if ((usize)addrlen > sizeof(sockaddr_un)) return err(EINVAL);
if (m_state == State::Connected) return err(EISCONN);
if (m_state != State::Inactive) return err(EINVAL);
struct sockaddr_un* un_address = (struct sockaddr_un*)addr;
String path = TRY(String::from_string_view(
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
auto* current = Scheduler::current();
socket->chmod(0777 & ~current->umask);
socket->chown(current->auth.euid, current->auth.egid);
auto rc = bind_socket_to_fs(path.chars(), current->auth, current->current_directory, socket);
if (rc.has_error())
{
if (rc.error() == EEXIST) return err(EADDRINUSE);
return rc.release_error();
}
memcpy(&m_addr, un_address, addrlen);
m_addrlen = addrlen;
m_state = State::Bound;
return {};
}
Result<void> UnixSocket::connect(struct sockaddr*, socklen_t)
{
return err(ENOSYS);
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "net/Socket.h"
#include <luna/Buffer.h>
#include <luna/String.h>
#include <sys/un.h>
class UnixSocket : public Socket
{
public:
UnixSocket();
UnixSocket(UnixSocket* peer);
bool blocking() const override
{
return !m_data.size();
}
Result<usize> send(const u8*, usize, int) override;
Result<usize> recv(u8*, usize, int) const override;
Result<void> bind(SharedPtr<Socket>, struct sockaddr*, socklen_t) override;
Result<void> connect(struct sockaddr*, socklen_t) override;
void connect_to_peer(UnixSocket* peer);
virtual ~UnixSocket();
private:
enum State
{
Inactive,
Bound,
Listening,
Connecting,
Connected,
Reset,
};
State m_state = State::Inactive;
UnixSocket* m_peer;
mutable Buffer m_data;
struct sockaddr_un m_addr = { .sun_family = AF_UNIX, .sun_path = {} };
socklen_t m_addrlen = sizeof(sa_family_t);
};

49
kernel/src/sys/socket.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "net/Socket.h"
#include "memory/MemoryManager.h"
#include "net/UnixSocket.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/open-flags.h>
Result<u64> sys_socket(Registers*, SyscallArgs args)
{
int domain = (int)args[0];
int type = (int)args[1];
// protocol is not used for now.
if (type != SOCK_STREAM) return err(EPROTOTYPE);
if (domain != AF_UNIX) return err(EAFNOSUPPORT);
auto socket = TRY(make_shared<UnixSocket>());
auto* current = Scheduler::current();
int fd = TRY(current->allocate_fd(0));
current->fd_table[fd] = FileDescriptor { socket, 0, O_RDWR };
return fd;
}
Result<u64> sys_bind(Registers*, SyscallArgs args)
{
int sockfd = (int)args[0];
struct sockaddr* addr = (struct sockaddr*)args[1];
socklen_t addrlen = (socklen_t)args[2];
struct sockaddr_storage storage;
if ((usize)addrlen > sizeof(storage)) return err(EINVAL);
if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT);
auto* current = Scheduler::current();
auto inode = TRY(current->resolve_fd(sockfd))->inode;
if (inode->type() != VFS::InodeType::Socket) return err(ENOTSOCK);
auto socket = (SharedPtr<Socket>)inode;
TRY(socket->bind(socket, (struct sockaddr*)&storage, addrlen));
return 0;
}

View File

@ -31,6 +31,7 @@ set(SOURCES
src/sys/mount.cpp src/sys/mount.cpp
src/sys/pstat.cpp src/sys/pstat.cpp
src/sys/resource.cpp src/sys/resource.cpp
src/sys/socket.cpp
) )
if(${LUNA_ARCH} STREQUAL "x86_64") if(${LUNA_ARCH} STREQUAL "x86_64")

View File

@ -0,0 +1,30 @@
/* bits/socket.h: Socket-related types and structures. */
#ifndef _BITS_SOCKET_H
#define _BITS_SOCKET_H
typedef int socklen_t;
typedef unsigned sa_family_t;
struct sockaddr
{
sa_family_t sa_family;
char sa_data[4];
};
struct sockaddr_storage
{
sa_family_t ss_family;
union {
char _sun_path[108]; // AF_UNIX
};
};
#define SOCK_STREAM 0
#define AF_UNSPEC 0
#define AF_UNIX 1
#define AF_INET 2
#define AF_INET6 3
#endif

23
libc/include/sys/socket.h Normal file
View File

@ -0,0 +1,23 @@
/* sys/socket.h: Communication sockets. */
#ifndef _SYS_SOCKET_H
#define _SYS_SOCKET_H
#include <bits/socket.h>
#ifdef __cplusplus
extern "C"
{
#endif
/* Create a new socket and return a file descriptor pointing to it. */
int socket(int domain, int type, int protocol);
/* Bind a socket to an address. */
int bind(int sockfd, struct sockaddr* addr, socklen_t addrlen);
#ifdef __cplusplus
}
#endif
#endif

14
libc/include/sys/un.h Normal file
View File

@ -0,0 +1,14 @@
/* sys/un.h: The sockaddr_un structure for AF_UNIX sockets. */
#ifndef _SYS_UN_H
#define _SYS_UN_H
#include <bits/socket.h>
struct sockaddr_un
{
sa_family_t sun_family;
char sun_path[108];
};
#endif

19
libc/src/sys/socket.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <bits/errno-return.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <unistd.h>
extern "C"
{
int socket(int domain, int type, int protocol)
{
long rc = syscall(SYS_socket, domain, type, protocol);
__errno_return(rc, int);
}
int bind(int sockfd, struct sockaddr* addr, socklen_t addrlen)
{
long rc = syscall(SYS_bind, sockfd, addr, addrlen);
__errno_return(rc, int);
}
}

View File

@ -7,7 +7,7 @@
_e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \ _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \
_e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \
_e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \
_e(getpgid) _e(getpgid) _e(socket) _e(bind)
enum Syscalls enum Syscalls
{ {