Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
This commit is contained in:
commit
d0f7acf9eb
275 changed files with 51417 additions and 0 deletions
346
kernel/sys/sys_net.c
Normal file
346
kernel/sys/sys_net.c
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
// Socket syscalls.
|
||||
//
|
||||
// The kernel side is in kernel/net/unix.c; this file is only the translation
|
||||
// between a syscall's user-supplied `struct sockaddr` and the path string the
|
||||
// socket layer actually wants, plus the file-descriptor bookkeeping.
|
||||
//
|
||||
// Only AF_UNIX is answered. AF_INET is recognised and refused with
|
||||
// EAFNOSUPPORT rather than EINVAL, because a program that probes for IPv6 and
|
||||
// falls back to IPv4 needs to be able to tell "this address family does not
|
||||
// exist here" from "you passed nonsense".
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "fs.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "file.h"
|
||||
#include "posix.h"
|
||||
#include "syscall.h"
|
||||
#include "uaccess.h"
|
||||
#include "errno.h"
|
||||
#include "socket.h"
|
||||
|
||||
// Pull the sun_path out of a user `struct sockaddr_un`, NUL-terminated.
|
||||
//
|
||||
// The length the caller gives is checked rather than trusted: musl passes
|
||||
// `offsetof(sockaddr_un, sun_path) + strlen(path) + 1`, which is shorter than
|
||||
// the full struct, so reading sizeof(struct sockaddr_un) from user memory
|
||||
// would fault on a path that sits near the end of a page.
|
||||
static int
|
||||
fetch_sun_path(addr_t uaddr, int ulen, char *out)
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
int plen;
|
||||
|
||||
if (ulen < (int)sizeof(sa.sun_family) || ulen > (int)sizeof(sa))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
if (copy_from_user(&sa, uaddr, (uint64)ulen) < 0)
|
||||
return -EFAULT;
|
||||
|
||||
if (sa.sun_family == AF_INET)
|
||||
return -EAFNOSUPPORT;
|
||||
if (sa.sun_family != AF_UNIX)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
plen = ulen - (int)sizeof(sa.sun_family);
|
||||
if (plen <= 0)
|
||||
return -EINVAL;
|
||||
if (plen > UNIX_PATH_MAX - 1)
|
||||
plen = UNIX_PATH_MAX - 1;
|
||||
|
||||
memmove(out, sa.sun_path, (uint)plen);
|
||||
out[plen] = '\0';
|
||||
|
||||
// A NUL inside the copied region is fine -- it just ends the path early,
|
||||
// which is what a caller passing the full struct length does.
|
||||
out[UNIX_PATH_MAX - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Wrap an established socket in a file and a descriptor. Consumes the
|
||||
// socket's reference on success; on failure the caller still owns it.
|
||||
static int
|
||||
sock_to_fd(struct socket *s)
|
||||
{
|
||||
struct file *f;
|
||||
int fd;
|
||||
|
||||
if ((f = filealloc()) == 0)
|
||||
return -ENFILE;
|
||||
if ((fd = fdalloc(f)) < 0) {
|
||||
f->ref = 0;
|
||||
return -EMFILE;
|
||||
}
|
||||
f->type = FD_SOCKET;
|
||||
f->sock = s;
|
||||
f->readable = 1;
|
||||
f->writable = 1;
|
||||
f->off = 0;
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Resolve a descriptor to the socket behind it.
|
||||
static int
|
||||
argsock(int n, struct socket **ps)
|
||||
{
|
||||
int fd;
|
||||
struct file *f;
|
||||
|
||||
if (argint(n, &fd) < 0)
|
||||
return -EBADF;
|
||||
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd]))
|
||||
return -EBADF;
|
||||
if (f->type != FD_SOCKET)
|
||||
return -ENOTSOCK;
|
||||
*ps = f->sock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE(socket)
|
||||
{
|
||||
int domain, type, protocol;
|
||||
struct socket *s;
|
||||
int fd;
|
||||
|
||||
argint(0, &domain);
|
||||
argint(1, &type);
|
||||
argint(2, &protocol);
|
||||
|
||||
if (domain != AF_UNIX)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
// SOCK_CLOEXEC / SOCK_NONBLOCK ride in the high bits of `type`. Neither
|
||||
// is honoured yet, but masking them off means a musl program that always
|
||||
// sets SOCK_CLOEXEC still gets a socket instead of EINVAL.
|
||||
type &= 0xFF;
|
||||
if (type != SOCK_STREAM)
|
||||
return -EPROTOTYPE;
|
||||
if (protocol != 0)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if ((s = socknew(domain, type)) == 0)
|
||||
return -ENFILE;
|
||||
|
||||
if ((fd = sock_to_fd(s)) < 0) {
|
||||
sockclose(s);
|
||||
return fd;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE(bind)
|
||||
{
|
||||
struct socket *s;
|
||||
addr_t uaddr;
|
||||
int ulen, r;
|
||||
char path[UNIX_PATH_MAX];
|
||||
|
||||
if ((r = argsock(0, &s)) < 0)
|
||||
return r;
|
||||
argaddr(1, &uaddr);
|
||||
argint(2, &ulen);
|
||||
|
||||
if ((r = fetch_sun_path(uaddr, ulen, path)) < 0)
|
||||
return r;
|
||||
return sockbind(s, path);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE(listen)
|
||||
{
|
||||
struct socket *s;
|
||||
int backlog, r;
|
||||
|
||||
if ((r = argsock(0, &s)) < 0)
|
||||
return r;
|
||||
argint(1, &backlog);
|
||||
return socklisten(s, backlog);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE(connect)
|
||||
{
|
||||
struct socket *s;
|
||||
addr_t uaddr;
|
||||
int ulen, r;
|
||||
char path[UNIX_PATH_MAX];
|
||||
|
||||
if ((r = argsock(0, &s)) < 0)
|
||||
return r;
|
||||
argaddr(1, &uaddr);
|
||||
argint(2, &ulen);
|
||||
|
||||
if ((r = fetch_sun_path(uaddr, ulen, path)) < 0)
|
||||
return r;
|
||||
return sockconnect(s, path);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE(accept)
|
||||
{
|
||||
struct socket *s, *c;
|
||||
addr_t uaddr, ulenp;
|
||||
int r, fd;
|
||||
|
||||
if ((r = argsock(0, &s)) < 0)
|
||||
return r;
|
||||
argaddr(1, &uaddr);
|
||||
argaddr(2, &ulenp);
|
||||
|
||||
if ((c = sockaccept(s)) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if ((fd = sock_to_fd(c)) < 0) {
|
||||
sockclose(c);
|
||||
return fd;
|
||||
}
|
||||
|
||||
// The peer address of a Unix socket that never bound a name is the empty
|
||||
// one: family AF_UNIX and a zero-length path. Reporting it is optional
|
||||
// (both pointers may be null) but a caller that asks must not be left
|
||||
// with an uninitialised struct it then prints.
|
||||
if (uaddr && ulenp) {
|
||||
struct sockaddr_un sa;
|
||||
int len = (int)sizeof(sa.sun_family);
|
||||
int want = 0;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
safestrcpy(sa.sun_path, sockname(c), UNIX_PATH_MAX);
|
||||
len += (int)strlen(sa.sun_path) + 1;
|
||||
|
||||
if (get_user(want, ulenp) < 0)
|
||||
return fd; // the fd is already live; do not fail it
|
||||
if (want > len)
|
||||
want = len;
|
||||
if (want > 0)
|
||||
copy_to_user(uaddr, &sa, (uint64)want);
|
||||
put_user(len, ulenp);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE(socketpair)
|
||||
{
|
||||
int domain, type, protocol;
|
||||
addr_t usv;
|
||||
struct socket *a, *b;
|
||||
int fd0, fd1, r;
|
||||
|
||||
argint(0, &domain);
|
||||
argint(1, &type);
|
||||
argint(2, &protocol);
|
||||
argaddr(3, &usv);
|
||||
|
||||
if (domain != AF_UNIX)
|
||||
return -EAFNOSUPPORT;
|
||||
type &= 0xFF;
|
||||
if (type != SOCK_STREAM)
|
||||
return -EPROTOTYPE;
|
||||
|
||||
if ((r = sockpair(&a, &b)) < 0)
|
||||
return r;
|
||||
|
||||
if ((fd0 = sock_to_fd(a)) < 0) {
|
||||
sockclose(a);
|
||||
sockclose(b);
|
||||
return fd0;
|
||||
}
|
||||
if ((fd1 = sock_to_fd(b)) < 0) {
|
||||
myproc()->ofile[fd0] = 0;
|
||||
sockclose(a);
|
||||
sockclose(b);
|
||||
return fd1;
|
||||
}
|
||||
|
||||
int sv[2] = { fd0, fd1 };
|
||||
if (copy_to_user(usv, sv, sizeof(sv)) < 0) {
|
||||
myproc()->ofile[fd0] = 0;
|
||||
myproc()->ofile[fd1] = 0;
|
||||
sockclose(a);
|
||||
sockclose(b);
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// send/recv with no flags are read/write. Implementing them here rather than
|
||||
// leaving them ENOSYS costs four lines and saves every ported program from
|
||||
// having to be rewritten to use write().
|
||||
SYSCALL_DEFINE(sendto)
|
||||
{
|
||||
struct socket *s;
|
||||
addr_t ubuf;
|
||||
int len, flags, r;
|
||||
|
||||
if ((r = argsock(0, &s)) < 0)
|
||||
return r;
|
||||
argaddr(1, &ubuf);
|
||||
argint(2, &len);
|
||||
argint(3, &flags);
|
||||
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
char *p = (char *)user_ptr(ubuf, (uint64)len);
|
||||
if (len && !p)
|
||||
return -EFAULT;
|
||||
return sockwrite(s, p, len);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE(recvfrom)
|
||||
{
|
||||
struct socket *s;
|
||||
addr_t ubuf;
|
||||
int len, flags, r;
|
||||
|
||||
if ((r = argsock(0, &s)) < 0)
|
||||
return r;
|
||||
argaddr(1, &ubuf);
|
||||
argint(2, &len);
|
||||
argint(3, &flags);
|
||||
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
char *p = (char *)user_ptr(ubuf, (uint64)len);
|
||||
if (len && !p)
|
||||
return -EFAULT;
|
||||
return sockread(s, p, len);
|
||||
}
|
||||
|
||||
// shutdown(2). Half-close is not modelled -- there is one buffer per
|
||||
// direction and no per-direction flag -- so SHUT_WR and SHUT_RDWR both mean
|
||||
// "the peer sees end-of-file", which is the behaviour programs depend on.
|
||||
SYSCALL_DEFINE(sockshutdown)
|
||||
{
|
||||
struct socket *s;
|
||||
int how, r;
|
||||
|
||||
if ((r = argsock(0, &s)) < 0)
|
||||
return r;
|
||||
argint(1, &how);
|
||||
return sock_shutdown(s, how);
|
||||
}
|
||||
|
||||
// getsockopt/setsockopt: accepted and ignored. A server that sets SO_REUSEADDR
|
||||
// and treats failure as fatal is common enough to be worth not failing.
|
||||
SYSCALL_DEFINE(setsockopt) { return 0; }
|
||||
|
||||
SYSCALL_DEFINE(getsockopt)
|
||||
{
|
||||
addr_t uval, ulen;
|
||||
int zero = 0, len = 0;
|
||||
|
||||
argaddr(3, &uval);
|
||||
argaddr(4, &ulen);
|
||||
if (uval && ulen) {
|
||||
if (get_user(len, ulen) == 0 && len >= (int)sizeof(int)) {
|
||||
put_user(zero, uval);
|
||||
len = sizeof(int);
|
||||
put_user(len, ulen);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in a new issue