/* * Copyright (c) 2011-2017, 2021 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * io.cpp * Provides system calls for input and output. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "partition.h" namespace Sortix { static Ref PrepareLookup(const char* path, int dirfd) { if ( path[0] == '/' ) return CurrentProcess()->GetRoot(); if ( dirfd == AT_FDCWD ) return CurrentProcess()->GetCWD(); return CurrentProcess()->GetDescriptor(dirfd); } ssize_t sys_write(int fd, const void* buffer, size_t count) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); ssize_t ret = desc->write(&ctx, (const uint8_t*) buffer, count); return ret; } ssize_t sys_pwrite(int fd, const void* buffer, size_t count, off_t off) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); ssize_t ret = desc->pwrite(&ctx, (const uint8_t*) buffer, count, off); return ret; } ssize_t sys_read(int fd, void* buffer, size_t count) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); ssize_t ret = desc->read(&ctx, (uint8_t*) buffer, count); return ret; } ssize_t sys_pread(int fd, void* buffer, size_t count, off_t off) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); ssize_t ret = desc->pread(&ctx, (uint8_t*) buffer, count, off); return ret; } off_t sys_lseek(int fd, off_t offset, int whence) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); off_t ret = desc->lseek(&ctx, offset, whence); return ret; } int sys_close(int fd) { ioctx_t ctx; SetupUserIOCtx(&ctx); Ref dtable = CurrentProcess()->GetDTable(); Ref desc = dtable->FreeKeep(fd); dtable.Reset(); if ( !desc ) return -1; // TODO: This can be a significant bottleneck. All we need to do is ask the // filesystem for whether any errors occurred that should be reported // at close time. //return desc->sync(&ctx); return 0; } int sys_closefrom(int fd) { ioctx_t ctx; SetupUserIOCtx(&ctx); Ref dtable = CurrentProcess()->GetDTable(); int ret = dtable->CloseFrom(fd); dtable.Reset(); return ret; } int sys_dup(int fd) { Ref dtable = CurrentProcess()->GetDTable(); Ref desc = dtable->Get(fd); return dtable->Allocate(desc, 0); } int sys_dup3(int oldfd, int newfd, int flags) { if ( flags & ~(O_CLOEXEC | O_CLOFORK) ) return errno = EINVAL, -1; int fd_flags = 0; fd_flags |= flags & O_CLOEXEC ? FD_CLOEXEC : 0; fd_flags |= flags & O_CLOFORK ? FD_CLOFORK : 0; Ref dtable = CurrentProcess()->GetDTable(); return dtable->Copy(oldfd, newfd, fd_flags); } int sys_dup2(int oldfd, int newfd) { if ( oldfd < 0 || newfd < 0 ) return errno = EINVAL, -1; int ret = sys_dup3(oldfd, newfd, 0); if ( ret < 0 && errno == EINVAL ) return errno = 0, newfd; return ret; } // TODO: If this function fails the file may still have been created. Does a // standard prohibit this and is that the wrong thing? int sys_openat(int dirfd, const char* path, int flags, mode_t mode) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); int fdflags = 0; if ( flags & O_CLOEXEC ) fdflags |= FD_CLOEXEC; if ( flags & O_CLOFORK ) fdflags |= FD_CLOFORK; flags &= ~(O_CLOEXEC | O_CLOFORK); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } Ref desc = from->open(&ctx, pathcopy, flags, mode); from.Reset(); delete[] pathcopy; if ( !desc ) return -1; Ref dtable = CurrentProcess()->GetDTable(); int ret = dtable->Allocate(desc, fdflags); if ( ret < 0 ) { // TODO: We should use a fail-safe dtable reservation mechanism that // causes this error earlier before we have side effects. } return ret; } int sys_faccessat(int dirfd, const char* path, int mode, int flags) { if ( flags & ~(AT_SYMLINK_NOFOLLOW | AT_EACCESS) ) return errno = EINVAL, -1; if ( mode & ~(X_OK | W_OK | R_OK) ) return errno = EINVAL, -1; char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int open_flags = O_READ | O_IS_STAT | (flags & AT_SYMLINK_NOFOLLOW ? O_SYMLINK_NOFOLLOW : 0); Ref desc = from->open(&ctx, pathcopy, open_flags); delete[] pathcopy; if ( !desc ) return -1; ioctx_t kctx; SetupKernelIOCtx(&kctx); struct stat st; if ( desc->stat(&kctx, &st) < 0 ) return -1; mode_t mask; if ( kctx.uid == 0 ) mask = 0777; else if ( kctx.uid == st.st_uid ) mask = 0700; else if ( kctx.gid == st.st_gid ) mask = 0070; else mask = 0007; if ( (mode & X_OK) && !(st.st_mode & 0111 & mask) ) return errno = EACCES, -1; if ( (mode & W_OK) && !(st.st_mode & 0222 & mask) ) return errno = EACCES, -1; if ( (mode & R_OK) && !(st.st_mode & 0444 & mask) ) return errno = EACCES, -1; return 0; } int sys_unlinkat(int dirfd, const char* path, int flags) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int ret = from->unlinkat(&ctx, pathcopy, flags); delete[] pathcopy; return ret; } int sys_mkdirat(int dirfd, const char* path, mode_t mode) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int ret = from->mkdir(&ctx, pathcopy, mode); delete[] pathcopy; return ret; } int sys_truncateat(int dirfd, const char* path, off_t length) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } Ref desc = from->open(&ctx, pathcopy, O_WRITE); delete[] pathcopy; if ( !desc ) return -1; return desc->truncate(&ctx, length); } int sys_ftruncate(int fd, off_t length) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->truncate(&ctx, length); } int sys_fstatat(int dirfd, const char* path, struct stat* st, int flags) { if ( flags & ~(AT_SYMLINK_NOFOLLOW) ) return errno = EINVAL; char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int open_flags = O_READ | O_IS_STAT | (flags & AT_SYMLINK_NOFOLLOW ? O_SYMLINK_NOFOLLOW : 0); Ref desc = from->open(&ctx, pathcopy, open_flags); delete[] pathcopy; if ( !desc ) return -1; return desc->stat(&ctx, st); } int sys_fstat(int fd, struct stat* st) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->stat(&ctx, st); } int sys_fstatvfs(int fd, struct statvfs* stvfs) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->statvfs(&ctx, stvfs); } int sys_fstatvfsat(int dirfd, const char* path, struct statvfs* stvfs, int flags) { if ( flags & ~(AT_SYMLINK_NOFOLLOW) ) return errno = EINVAL; char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int open_flags = O_READ | O_IS_STAT | (flags & AT_SYMLINK_NOFOLLOW ? O_SYMLINK_NOFOLLOW : 0); Ref desc = from->open(&ctx, pathcopy, open_flags); delete[] pathcopy; if ( !desc ) return -1; return desc->statvfs(&ctx, stvfs); } int sys_fcntl(int fd, int cmd, uintptr_t arg) { // Operations on the descriptor table. Ref dtable = CurrentProcess()->GetDTable(); if ( cmd == F_PREVFD ) return dtable->Previous(fd); if ( cmd == F_NEXTFD ) return dtable->Next(fd); // Operations on the file descriptior. if ( cmd == F_SETFD ) return dtable->SetFlags(fd, (int) arg) ? 0 : -1; if ( cmd == F_GETFD ) return dtable->GetFlags(fd); // Operations on the file description. if ( F_DECODE_CMD(F_DECODE_CMD_RAW(cmd)) == F_DUPFD_NUM ) { int fd_flags = F_DECODE_FLAGS(F_DECODE_CMD_RAW(cmd)); return dtable->Allocate(fd, fd_flags, (int) arg); } Ref desc = dtable->Get(fd); if ( !desc ) return -1; if ( cmd == F_SETFL ) return desc->SetFlags((int) arg) ? 0 : -1; if ( cmd == F_GETFL ) return desc->GetFlags(); return errno = EINVAL, -1; } int sys_ioctl(int fd, int cmd, uintptr_t arg) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->ioctl(&ctx, cmd, arg); } ssize_t sys_readdirents(int fd, struct dirent* dirent, size_t size) { if ( SSIZE_MAX < size ) size = SSIZE_MAX; Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->readdirents(&ctx, dirent, size); } int sys_fchdir(int fd) { Process* process = CurrentProcess(); Ref desc = process->GetDescriptor(fd); if ( !desc ) return -1; if ( !S_ISDIR(desc->type) ) return errno = ENOTDIR, -1; process->SetCWD(desc); return 0; } int sys_fchdirat(int dirfd, const char* path) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); Ref desc = from->open(&ctx, pathcopy, O_READ | O_DIRECTORY); from.Reset(); delete[] pathcopy; if ( !desc ) return -1; CurrentProcess()->SetCWD(desc); return 0; } int sys_fchroot(int fd) { Process* process = CurrentProcess(); Ref desc = process->GetDescriptor(fd); if ( !desc ) return -1; if ( !S_ISDIR(desc->type) ) return errno = ENOTDIR, -1; process->SetRoot(desc); return 0; } int sys_fchrootat(int dirfd, const char* path) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); Ref desc = from->open(&ctx, pathcopy, O_READ | O_DIRECTORY); from.Reset(); delete[] pathcopy; if ( !desc ) return -1; CurrentProcess()->SetRoot(desc); return 0; } int sys_fchown(int fd, uid_t owner, gid_t group) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->chown(&ctx, owner, group); } int sys_fchownat(int dirfd, const char* path, uid_t owner, gid_t group, int flags) { if ( flags & ~(AT_SYMLINK_NOFOLLOW) ) return errno = EINVAL, -1; char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int open_flags = O_READ | (flags & AT_SYMLINK_NOFOLLOW ? O_SYMLINK_NOFOLLOW : 0); Ref desc = from->open(&ctx, pathcopy, open_flags); from.Reset(); delete[] pathcopy; if ( !desc ) return -1; return desc->chown(&ctx, owner, group); } #if defined(__i386__) struct fchownat_request /* duplicated in libc/unistd/fchownat.c */ { int dirfd; const char* path; uid_t owner; gid_t group; int flags; }; int sys_fchownat_wrapper(const struct fchownat_request* user_request) { struct fchownat_request request; if ( !CopyFromUser(&request, user_request, sizeof(request)) ) return -1; return sys_fchownat(request.dirfd, request.path, request.owner, request.group, request.flags); } #endif int sys_fchmod(int fd, mode_t mode) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->chmod(&ctx, mode); } int sys_fchmodat(int dirfd, const char* path, mode_t mode, int flags) { if ( flags & ~(AT_SYMLINK_NOFOLLOW) ) return errno = EINVAL, -1; char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int open_flags = O_READ | (flags & AT_SYMLINK_NOFOLLOW ? O_SYMLINK_NOFOLLOW : 0); Ref desc = from->open(&ctx, pathcopy, open_flags); from.Reset(); delete[] pathcopy; if ( !desc ) return -1; return desc->chmod(&ctx, mode); } int sys_futimens(int fd, const struct timespec* user_times) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->utimens(&ctx, user_times); } int sys_utimensat(int dirfd, const char* path, const struct timespec* user_times, int flags) { if ( flags & ~(AT_SYMLINK_NOFOLLOW) ) return errno = EINVAL, -1; char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } int open_flags = O_READ | (flags & AT_SYMLINK_NOFOLLOW ? O_SYMLINK_NOFOLLOW : 0); Ref desc = from->open(&ctx, pathcopy, open_flags); from.Reset(); delete[] pathcopy; if ( !desc ) return -1; return desc->utimens(&ctx, user_times); } int sys_linkat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath, int flags) { if ( flags & ~(AT_SYMLINK_FOLLOW) ) return errno = EINVAL, -1; ioctx_t ctx; SetupUserIOCtx(&ctx); char* newpathcopy = GetStringFromUser(newpath); if ( !newpathcopy ) return -1; Ref newfrom(PrepareLookup(newpathcopy, newdirfd)); if ( !newfrom ) { delete[] newpathcopy; return -1; } char* final_elem; Ref dir = OpenDirContainingPath(&ctx, newfrom, newpathcopy, &final_elem); delete[] newpathcopy; if ( !dir ) return -1; char* oldpathcopy = GetStringFromUser(oldpath); if ( !oldpathcopy ) { delete[] final_elem; return -1; } Ref oldfrom = PrepareLookup(oldpathcopy, olddirfd); if ( !oldfrom ) { delete[] oldpathcopy; delete[] final_elem; return -1; } int open_flags = O_READ | (flags & AT_SYMLINK_FOLLOW ? 0 : O_SYMLINK_NOFOLLOW); Ref file = oldfrom->open(&ctx, oldpathcopy, open_flags); delete[] oldpathcopy; if ( !file ) { delete[] final_elem; return -1; } int ret = dir->link(&ctx, final_elem, file); delete[] final_elem; return ret; } int sys_symlinkat(const char* oldpath, int newdirfd, const char* newpath) { ioctx_t ctx; SetupUserIOCtx(&ctx); char* newpath_copy = GetStringFromUser(newpath); if ( !newpath_copy ) return -1; char* oldpath_copy = GetStringFromUser(oldpath); if ( !oldpath_copy ) return delete[] newpath_copy, -1; Ref newfrom(PrepareLookup(newpath_copy, newdirfd)); if ( !newfrom ) return delete[] newpath_copy, -1; int ret = newfrom->symlink(&ctx, oldpath, newpath_copy); delete[] oldpath_copy; delete[] newpath_copy; return ret; } int sys_settermmode(int fd, unsigned mode) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->settermmode(&ctx, mode); } int sys_gettermmode(int fd, unsigned* mode) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->gettermmode(&ctx, mode); } int sys_isatty(int fd) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return 0; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->isatty(&ctx); } int sys_tcgetwincurpos(int fd, struct wincurpos* wcp) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcgetwincurpos(&ctx, wcp); } int sys_tcgetwinsize(int fd, struct winsize* ws) { return sys_ioctl(fd, TIOCGWINSZ, (uintptr_t) ws); } int sys_tcsetpgrp(int fd, pid_t pgid) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcsetpgrp(&ctx, pgid); } int sys_tcgetpgrp(int fd) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcgetpgrp(&ctx); } static int sys_renameat_inner(int olddirfd, const char* oldpath, int newdirfd, const char* newpath) { Ref olddir(PrepareLookup(oldpath, olddirfd)); if ( !olddir ) return -1; Ref newdir(PrepareLookup(newpath, newdirfd)); if ( !newdir ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return newdir->rename_here(&ctx, olddir, oldpath, newpath); } int sys_renameat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath) { char* oldpathcopy = GetStringFromUser(oldpath); if ( !oldpathcopy ) return -1; char* newpathcopy = GetStringFromUser(newpath); if ( !newpathcopy ) { delete[] oldpathcopy; return -1; } int ret = sys_renameat_inner(olddirfd, oldpathcopy, newdirfd, newpathcopy); delete[] newpathcopy; delete[] oldpathcopy; return ret; } // TODO: This should probably be moved into user-space. It'd be nice if // user-space could just open the symlink and read/write it like a regular file. ssize_t sys_readlinkat(int dirfd, const char* path, char* buf, size_t size) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) { delete[] pathcopy; return -1; } Ref desc = from->open(&ctx, pathcopy, O_READ | O_SYMLINK_NOFOLLOW); delete[] pathcopy; if ( !desc ) return -1; return desc->readlink(&ctx, buf, size); } int sys_fsync(int fd) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->sync(&ctx); } int sys_accept4(int fd, void* addr, size_t* addrlen, int flags) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; int fdflags = 0; if ( flags & SOCK_CLOEXEC ) fdflags |= FD_CLOEXEC; if ( flags & SOCK_CLOFORK ) fdflags |= FD_CLOFORK; flags &= ~(SOCK_CLOEXEC | SOCK_CLOFORK); ioctx_t ctx; SetupUserIOCtx(&ctx); Ref conn = desc->accept4(&ctx, (uint8_t*) addr, addrlen, flags); if ( !conn ) return -1; return CurrentProcess()->GetDTable()->Allocate(conn, fdflags); } int sys_bind(int fd, const void* addr, size_t addrlen) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->bind(&ctx, (const uint8_t*) addr, addrlen); } int sys_connect(int fd, const void* addr, size_t addrlen) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->connect(&ctx, (const uint8_t*) addr, addrlen); } int sys_listen(int fd, int backlog) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->listen(&ctx, backlog); } ssize_t sys_recv(int fd, void* buffer, size_t count, int flags) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->recv(&ctx, (uint8_t*) buffer, count, flags); } ssize_t sys_send(int fd, const void* buffer, size_t count, int flags) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->send(&ctx, (const uint8_t*) buffer, count, flags); } ssize_t sys_readv(int fd, const struct iovec* iov, int iovcnt) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->readv(&ctx, iov, iovcnt); } ssize_t sys_preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->preadv(&ctx, iov, iovcnt, offset); } ssize_t sys_writev(int fd, const struct iovec* iov, int iovcnt) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->writev(&ctx, iov, iovcnt); } ssize_t sys_pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->pwritev(&ctx, iov, iovcnt, offset); } int sys_mkpartition(int fd, off_t start, off_t length, int flags) { int fdflags = 0; if ( flags & O_CLOEXEC ) fdflags |= FD_CLOEXEC; if ( flags & O_CLOFORK ) fdflags |= FD_CLOFORK; Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; int dflags = desc->GetFlags(); Ref inner_inode = desc->vnode->inode; desc.Reset(); if ( inner_inode->type & S_IFNEVERWRAP ) return errno = EPERM, -1; if ( !S_ISBLK(inner_inode->type) && !S_ISREG(inner_inode->type) ) return errno = EPERM, -1; if ( start < 0 || length < 0 ) return errno = EINVAL, -1; off_t end; if ( __builtin_add_overflow(start, length, &end) ) return errno = EOVERFLOW, -1; Ref partition(new Partition(inner_inode, start, length)); if ( !partition ) return -1; inner_inode.Reset(); Ref partition_vnode(new Vnode(partition, Ref(NULL), 0, 0)); if ( !partition_vnode ) return -1; partition.Reset(); Ref partition_desc(new Descriptor(partition_vnode, dflags)); if ( !partition_desc ) return -1; partition_vnode.Reset(); return CurrentProcess()->GetDTable()->Allocate(partition_desc, fdflags); } ssize_t sys_sendmsg(int fd, const struct msghdr* msg, int flags) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->sendmsg(&ctx, msg, flags); } ssize_t sys_recvmsg(int fd, struct msghdr* msg, int flags) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->recvmsg(&ctx, msg, flags); } int sys_getsockopt(int fd, int level, int option_name, void* option_value, size_t* option_size_ptr) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->getsockopt(&ctx, level, option_name, option_value, option_size_ptr); } int sys_setsockopt(int fd, int level, int option_name, const void* option_value, size_t option_size) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->setsockopt(&ctx, level, option_name, option_value, option_size); } ssize_t sys_tcgetblob(int fd, const char* name, void* buffer, size_t count) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; char* name_copy = NULL; if ( name && !(name_copy = GetStringFromUser(name)) ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); ssize_t result = desc->tcgetblob(&ctx, name_copy, buffer, count); delete[] name_copy; return result; } ssize_t sys_tcsetblob(int fd, const char* name, const void* buffer, size_t count) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; char* name_copy = NULL; if ( name && !(name_copy = GetStringFromUser(name)) ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); ssize_t result = desc->tcsetblob(&ctx, name_copy, buffer, count); delete[] name_copy; return result; } int sys_getpeername(int fd, void* addr, size_t* addrsize) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->getpeername(&ctx, (uint8_t*) addr, addrsize); } int sys_getsockname(int fd, void* addr, size_t* addrsize) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->getsockname(&ctx, (uint8_t*) addr, addrsize); } int sys_shutdown(int fd, int how) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->shutdown(&ctx, how); } int sys_unmountat(int dirfd, const char* path, int flags) { char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) return delete[] pathcopy, -1; int ret = from->unmount(&ctx, pathcopy, flags); delete[] pathcopy; return ret; } int sys_fsm_fsbind(int rootfd, int mpointfd, int flags) { if ( flags & ~(0) ) return errno = EINVAL, -1; Ref desc = CurrentProcess()->GetDescriptor(rootfd); if ( !desc ) return -1; Ref mpoint = CurrentProcess()->GetDescriptor(mpointfd); if ( !mpoint ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return mpoint->fsm_fsbind(&ctx, desc, flags); } int sys_fsm_mountat(int dirfd, const char* path, const struct stat* rootst, int flags) { if ( flags & ~(FSM_MOUNT_CLOEXEC | FSM_MOUNT_CLOFORK | FSM_MOUNT_NOFOLLOW | FSM_MOUNT_NONBLOCK) ) return -1; int fdflags = 0; if ( flags & FSM_MOUNT_CLOEXEC ) fdflags |= FD_CLOEXEC; if ( flags & FSM_MOUNT_CLOFORK ) fdflags |= FD_CLOFORK; flags &= ~(FSM_MOUNT_CLOEXEC | FSM_MOUNT_CLOFORK); char* pathcopy = GetStringFromUser(path); if ( !pathcopy ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); Ref from = PrepareLookup(pathcopy, dirfd); if ( !from ) return delete[] pathcopy, -1; Ref dtable = CurrentProcess()->GetDTable(); int reservation; if ( !dtable->Reserve(1, &reservation) ) return delete[] pathcopy, -1; Ref desc = from->fsm_mount(&ctx, pathcopy, rootst, flags); delete[] pathcopy; if ( !desc ) return dtable->Unreserve(&reservation), -1; int ret = dtable->Allocate(desc, fdflags, 0, &reservation); assert(0 <= ret); return ret; } int sys_tcdrain(int fd) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcdrain(&ctx); } int sys_tcflow(int fd, int action) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcflow(&ctx, action); } int sys_tcflush(int fd, int queue_selector) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcflush(&ctx, queue_selector); } int sys_tcgetattr(int fd, struct termios* tio) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcgetattr(&ctx, tio); } pid_t sys_tcgetsid(int fd) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcgetsid(&ctx); } int sys_tcsendbreak(int fd, int duration) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcsendbreak(&ctx, duration); } int sys_tcsetattr(int fd, int actions, const struct termios* tio) { Ref desc = CurrentProcess()->GetDescriptor(fd); if ( !desc ) return -1; ioctx_t ctx; SetupUserIOCtx(&ctx); return desc->tcsetattr(&ctx, actions, tio); } } // namespace Sortix