blob: 38715ad9783805ad3b3f54edaa650d8cd0ec2cd0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "nacl_io/kernel_proxy.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <iterator>
#include <string>
#include "nacl_io/host_resolver.h"
#include "nacl_io/kernel_handle.h"
#include "nacl_io/kernel_wrap_real.h"
#include "nacl_io/mount.h"
#include "nacl_io/mount_dev.h"
#include "nacl_io/mount_html5fs.h"
#include "nacl_io/mount_http.h"
#include "nacl_io/mount_mem.h"
#include "nacl_io/mount_node.h"
#include "nacl_io/mount_node_pipe.h"
#include "nacl_io/mount_node_tcp.h"
#include "nacl_io/mount_node_udp.h"
#include "nacl_io/mount_passthrough.h"
#include "nacl_io/mount_stream.h"
#include "nacl_io/osmman.h"
#include "nacl_io/ossocket.h"
#include "nacl_io/osstat.h"
#include "nacl_io/path.h"
#include "nacl_io/pepper_interface.h"
#include "nacl_io/typed_mount_factory.h"
#include "sdk_util/auto_lock.h"
#include "sdk_util/ref_object.h"
#include "sdk_util/string_util.h"
#ifndef MAXPATHLEN
#define MAXPATHLEN 256
#endif
namespace nacl_io {
KernelProxy::KernelProxy() : dev_(0), ppapi_(NULL),
sigwinch_handler_(SIG_IGN),
signal_emitter_(new EventEmitter) {
}
KernelProxy::~KernelProxy() {
// Clean up the MountFactories.
for (MountFactoryMap_t::iterator i = factories_.begin();
i != factories_.end();
++i) {
delete i->second;
}
delete ppapi_;
}
Error KernelProxy::Init(PepperInterface* ppapi) {
Error rtn = 0;
ppapi_ = ppapi;
dev_ = 1;
factories_["memfs"] = new TypedMountFactory<MountMem>;
factories_["dev"] = new TypedMountFactory<MountDev>;
factories_["html5fs"] = new TypedMountFactory<MountHtml5Fs>;
factories_["httpfs"] = new TypedMountFactory<MountHttp>;
factories_["passthroughfs"] = new TypedMountFactory<MountPassthrough>;
int result;
result = mount("", "/", "passthroughfs", 0, NULL);
if (result != 0) {
assert(false);
rtn = errno;
}
result = mount("", "/dev", "dev", 0, NULL);
if (result != 0) {
assert(false);
rtn = errno;
}
// Open the first three in order to get STDIN, STDOUT, STDERR
int fd;
fd = open("/dev/stdin", O_RDONLY);
assert(fd == 0);
if (fd < 0)
rtn = errno;
fd = open("/dev/stdout", O_WRONLY);
assert(fd == 1);
if (fd < 0)
rtn = errno;
fd = open("/dev/stderr", O_WRONLY);
assert(fd == 2);
if (fd < 0)
rtn = errno;
#ifdef PROVIDES_SOCKET_API
host_resolver_.Init(ppapi_);
#endif
StringMap_t args;
stream_mount_.reset(new MountStream());
result = stream_mount_->Init(0, args, ppapi);
if (result != 0) {
assert(false);
rtn = result;
}
return rtn;
}
int KernelProxy::open_resource(const char* path) {
ScopedMount mnt;
Path rel;
Error error = AcquireMountAndRelPath(path, &mnt, &rel);
if (error) {
errno = error;
return -1;
}
ScopedMountNode node;
error = mnt->OpenResource(rel, &node);
if (error) {
// OpenResource failed, try Open().
error = mnt->Open(rel, O_RDONLY, &node);
if (error) {
errno = error;
return -1;
}
}
ScopedKernelHandle handle(new KernelHandle(mnt, node));
error = handle->Init(O_RDONLY);
if (error) {
errno = error;
return -1;
}
return AllocateFD(handle);
}
int KernelProxy::open(const char* path, int open_flags) {
ScopedMount mnt;
ScopedMountNode node;
Error error = AcquireMountAndNode(path, open_flags, &mnt, &node);
if (error) {
errno = error;
return -1;
}
ScopedKernelHandle handle(new KernelHandle(mnt, node));
error = handle->Init(open_flags);
if (error) {
errno = error;
return -1;
}
return AllocateFD(handle);
}
int KernelProxy::pipe(int pipefds[2]) {
MountNodePipe* pipe = new MountNodePipe(stream_mount_.get());
ScopedMountNode node(pipe);
if (pipe->Init(S_IREAD | S_IWRITE) == 0) {
ScopedKernelHandle handle0(new KernelHandle(stream_mount_, node));
ScopedKernelHandle handle1(new KernelHandle(stream_mount_, node));
// Should never fail, but...
if (handle0->Init(S_IREAD) || handle1->Init(S_IWRITE)) {
errno = EACCES;
return -1;
}
pipefds[0] = AllocateFD(handle0);
pipefds[1] = AllocateFD(handle1);
return 0;
}
errno = ENOSYS;
return -1;
}
int KernelProxy::close(int fd) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
// Remove the FD from the process open file descriptor map
FreeFD(fd);
return 0;
}
int KernelProxy::dup(int oldfd) {
ScopedKernelHandle handle;
Error error = AcquireHandle(oldfd, &handle);
if (error) {
errno = error;
return -1;
}
return AllocateFD(handle);
}
int KernelProxy::dup2(int oldfd, int newfd) {
// If it's the same file handle, just return
if (oldfd == newfd)
return newfd;
ScopedKernelHandle old_handle;
Error error = AcquireHandle(oldfd, &old_handle);
if (error) {
errno = error;
return -1;
}
FreeAndReassignFD(newfd, old_handle);
return newfd;
}
int KernelProxy::chdir(const char* path) {
Error error = SetCWD(path);
if (error) {
errno = error;
return -1;
}
return 0;
}
char* KernelProxy::getcwd(char* buf, size_t size) {
std::string cwd = GetCWD();
if (size <= 0) {
errno = EINVAL;
return NULL;
}
// If size is 0, allocate as much as we need.
if (size == 0) {
size = cwd.size() + 1;
}
// Verify the buffer is large enough
if (size <= cwd.size()) {
errno = ERANGE;
return NULL;
}
// Allocate the buffer if needed
if (buf == NULL) {
buf = static_cast<char*>(malloc(size));
}
strcpy(buf, cwd.c_str());
return buf;
}
char* KernelProxy::getwd(char* buf) {
if (NULL == buf) {
errno = EFAULT;
return NULL;
}
return getcwd(buf, MAXPATHLEN);
}
int KernelProxy::chmod(const char* path, mode_t mode) {
int fd = KernelProxy::open(path, O_RDONLY);
if (-1 == fd)
return -1;
int result = fchmod(fd, mode);
close(fd);
return result;
}
int KernelProxy::chown(const char* path, uid_t owner, gid_t group) {
return 0;
}
int KernelProxy::fchown(int fd, uid_t owner, gid_t group) {
return 0;
}
int KernelProxy::lchown(const char* path, uid_t owner, gid_t group) {
return 0;
}
int KernelProxy::utime(const char* filename, const struct utimbuf* times) {
return 0;
}
int KernelProxy::mkdir(const char* path, mode_t mode) {
ScopedMount mnt;
Path rel;
Error error = AcquireMountAndRelPath(path, &mnt, &rel);
if (error) {
errno = error;
return -1;
}
error = mnt->Mkdir(rel, mode);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::rmdir(const char* path) {
ScopedMount mnt;
Path rel;
Error error = AcquireMountAndRelPath(path, &mnt, &rel);
if (error) {
errno = error;
return -1;
}
error = mnt->Rmdir(rel);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::stat(const char* path, struct stat* buf) {
int fd = open(path, O_RDONLY);
if (-1 == fd)
return -1;
int result = fstat(fd, buf);
close(fd);
return result;
}
int KernelProxy::mount(const char* source,
const char* target,
const char* filesystemtype,
unsigned long mountflags,
const void* data) {
std::string abs_path = GetAbsParts(target).Join();
// Find a factory of that type
MountFactoryMap_t::iterator factory = factories_.find(filesystemtype);
if (factory == factories_.end()) {
errno = ENODEV;
return -1;
}
// Create a map of settings
StringMap_t smap;
smap["SOURCE"] = source;
smap["TARGET"] = abs_path;
if (data) {
std::vector<std::string> elements;
sdk_util::SplitString(static_cast<const char*>(data), ',', &elements);
for (std::vector<std::string>::const_iterator it = elements.begin();
it != elements.end(); ++it) {
size_t location = it->find('=');
if (location != std::string::npos) {
std::string key = it->substr(0, location);
std::string val = it->substr(location + 1);
smap[key] = val;
} else {
smap[*it] = "TRUE";
}
}
}
ScopedMount mnt;
Error error = factory->second->CreateMount(dev_++, smap, ppapi_, &mnt);
if (error) {
errno = error;
return -1;
}
error = AttachMountAtPath(mnt, abs_path);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::umount(const char* path) {
Error error = DetachMountAtPath(path);
if (error) {
errno = error;
return -1;
}
return 0;
}
ssize_t KernelProxy::read(int fd, void* buf, size_t nbytes) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int cnt = 0;
error = handle->Read(buf, nbytes, &cnt);
if (error) {
errno = error;
return -1;
}
return cnt;
}
ssize_t KernelProxy::write(int fd, const void* buf, size_t nbytes) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int cnt = 0;
error = handle->Write(buf, nbytes, &cnt);
if (error) {
errno = error;
return -1;
}
return cnt;
}
int KernelProxy::fstat(int fd, struct stat* buf) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->GetStat(buf);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::getdents(int fd, void* buf, unsigned int count) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int cnt = 0;
error = handle->GetDents(static_cast<dirent*>(buf), count, &cnt);
if (error)
errno = error;
return cnt;
}
int KernelProxy::fchdir(int fd) {
errno = ENOSYS;
return -1;
}
int KernelProxy::ftruncate(int fd, off_t length) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->FTruncate(length);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::fsync(int fd) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->FSync();
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::fdatasync(int fd) {
errno = ENOSYS;
return -1;
}
int KernelProxy::isatty(int fd) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->IsaTTY();
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::ioctl(int fd, int request, va_list args) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->VIoctl(request, args);
if (error) {
errno = error;
return -1;
}
return 0;
}
off_t KernelProxy::lseek(int fd, off_t offset, int whence) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
off_t new_offset;
error = handle->Seek(offset, whence, &new_offset);
if (error) {
errno = error;
return -1;
}
return new_offset;
}
int KernelProxy::unlink(const char* path) {
ScopedMount mnt;
Path rel;
Error error = AcquireMountAndRelPath(path, &mnt, &rel);
if (error) {
errno = error;
return -1;
}
error = mnt->Unlink(rel);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::truncate(const char* path, off_t len) {
errno = ENOSYS;
return -1;
}
int KernelProxy::lstat(const char* path, struct stat* buf) {
errno = ENOSYS;
return -1;
}
int KernelProxy::rename(const char* path, const char* newpath) {
errno = ENOSYS;
return -1;
}
int KernelProxy::remove(const char* path) {
ScopedMount mnt;
Path rel;
Error error = AcquireMountAndRelPath(path, &mnt, &rel);
if (error) {
errno = error;
return -1;
}
error = mnt->Remove(rel);
if (error) {
errno = error;
return -1;
}
return 0;
}
// TODO(noelallen): Needs implementation.
int KernelProxy::fchmod(int fd, int mode) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::fcntl(int fd, int request, va_list args) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int rtn = 0;
error = handle->VFcntl(request, &rtn, args);
if (error) {
errno = error;
return -1;
}
return rtn;
}
int KernelProxy::access(const char* path, int amode) {
ScopedMount mnt;
Path rel;
Error error = AcquireMountAndRelPath(path, &mnt, &rel);
if (error) {
errno = error;
return -1;
}
error = mnt->Access(rel, amode);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::readlink(const char *path, char *buf, size_t count) {
errno = EINVAL;
return -1;
}
int KernelProxy::utimes(const char *filename, const struct timeval times[2]) {
errno = EINVAL;
return -1;
}
// TODO(noelallen): Needs implementation.
int KernelProxy::link(const char* oldpath, const char* newpath) {
errno = EINVAL;
return -1;
}
int KernelProxy::symlink(const char* oldpath, const char* newpath) {
errno = EINVAL;
return -1;
}
void* KernelProxy::mmap(void* addr,
size_t length,
int prot,
int flags,
int fd,
size_t offset) {
// We shouldn't be getting anonymous mmaps here.
assert((flags & MAP_ANONYMOUS) == 0);
assert(fd != -1);
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return MAP_FAILED;
}
void* new_addr;
error = handle->node()->MMap(addr, length, prot, flags, offset, &new_addr);
if (error) {
errno = error;
return MAP_FAILED;
}
return new_addr;
}
int KernelProxy::munmap(void* addr, size_t length) {
// NOTE: The comment below is from a previous discarded implementation that
// tracks mmap'd regions. For simplicity, we no longer do this; because we
// "snapshot" the contents of the file in mmap(), and don't support
// write-back or updating the mapped region when the file is written, holding
// on to the KernelHandle is pointless.
//
// If we ever do, these threading issues should be considered.
//
// WARNING: this function may be called by free().
//
// There is a potential deadlock scenario:
// Thread 1: open() -> takes lock1 -> free() -> takes lock2
// Thread 2: free() -> takes lock2 -> munmap() -> takes lock1
//
// Note that open() above could be any function that takes a lock that is
// shared with munmap (this includes munmap!)
//
// To prevent this, we avoid taking locks in munmap() that are used by other
// nacl_io functions that may call free. Specifically, we only take the
// mmap_lock, which is only shared with mmap() above. There is still a
// possibility of deadlock if mmap() or munmap() calls free(), so this is not
// allowed.
//
// Unfortunately, munmap still needs to acquire other locks; see the call to
// ReleaseHandle below which takes the process lock. This is safe as long as
// this is never executed from free() -- we can be reasonably sure this is
// true, because malloc only makes anonymous mmap() requests, and should only
// be munmapping those allocations. We never add to mmap_info_list_ for
// anonymous maps, so the unmap_list should always be empty when called from
// free().
return 0;
}
int KernelProxy::tcflush(int fd, int queue_selector) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->Tcflush(queue_selector);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::tcgetattr(int fd, struct termios* termios_p) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->Tcgetattr(termios_p);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::tcsetattr(int fd, int optional_actions,
const struct termios *termios_p) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->Tcsetattr(optional_actions, termios_p);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::kill(pid_t pid, int sig) {
// Currently we don't even pretend that other processes exist
// so we can only send a signal to outselves. For kill(2)
// pid 0 means the current process group and -1 means all the
// processes we have permission to send signals to.
if (pid != getpid() && pid != -1 && pid != 0) {
errno = ESRCH;
return -1;
}
// Raise an event so that select/poll get interrupted.
AUTO_LOCK(signal_emitter_->GetLock())
signal_emitter_->RaiseEvents_Locked(POLLERR);
switch (sig) {
case SIGWINCH:
if (sigwinch_handler_ != SIG_IGN)
sigwinch_handler_(SIGWINCH);
break;
case SIGUSR1:
case SIGUSR2:
break;
default:
errno = EINVAL;
return -1;
}
return 0;
}
sighandler_t KernelProxy::sigset(int signum, sighandler_t handler) {
switch (signum) {
// Handled signals.
case SIGWINCH: {
sighandler_t old_value = sigwinch_handler_;
if (handler == SIG_DFL)
handler = SIG_IGN;
sigwinch_handler_ = handler;
return old_value;
}
// Known signals
case SIGHUP:
case SIGINT:
case SIGKILL:
case SIGPIPE:
case SIGPOLL:
case SIGPROF:
case SIGTERM:
case SIGCHLD:
case SIGURG:
case SIGFPE:
case SIGILL:
case SIGQUIT:
case SIGSEGV:
case SIGTRAP:
if (handler == SIG_DFL)
return SIG_DFL;
break;
}
errno = EINVAL;
return SIG_ERR;
}
#ifdef PROVIDES_SOCKET_API
int KernelProxy::select(int nfds, fd_set* readfds, fd_set* writefds,
fd_set* exceptfds, struct timeval* timeout) {
fd_set ignore;
std::vector<pollfd> pollfds;
// Simplify logic, by using an IGNORE set for any undefined set
FD_ZERO(&ignore);
if (NULL == readfds)
readfds = &ignore;
if (NULL == writefds)
writefds = &ignore;
if (NULL == exceptfds)
exceptfds = &ignore;
for (int fd = 0; fd < nfds; fd++) {
int events = 0;
if (FD_ISSET(fd, readfds))
events |= POLLIN;
if (FD_ISSET(fd, writefds))
events |= POLLOUT;
if (FD_ISSET(fd, exceptfds))
events |= POLLERR | POLLHUP;
if (events) {
pollfd info;
info.fd = fd;
info.events = events;
pollfds.push_back(info);
}
}
FD_ZERO(readfds);
FD_ZERO(writefds);
FD_ZERO(exceptfds);
// NULL timeout signals wait forever.
int ms_timeout = -1;
if (timeout != NULL) {
int64_t ms = timeout->tv_sec * 1000 + ((timeout->tv_usec + 500) / 1000);
// If the timeout is invalid or too long (larger than signed 32 bit).
if ((timeout->tv_sec < 0) || (timeout->tv_sec >= (INT_MAX / 1000)) ||
(timeout->tv_usec < 0) || (timeout->tv_usec >= 1000000) ||
(ms < 0) || (ms >= INT_MAX)) {
errno = EINVAL;
return -1;
}
ms_timeout = static_cast<int>(ms);
}
int result = poll(&pollfds[0], pollfds.size(), ms_timeout);
if (result == -1)
return -1;
int event_cnt = 0;
for (size_t index = 0; index < pollfds.size(); index++) {
pollfd* info = &pollfds[index];
if (info->revents & POLLIN) {
FD_SET(info->fd, readfds);
event_cnt++;
}
if (info->revents & POLLOUT) {
FD_SET(info->fd, writefds);
event_cnt++;
}
if (info->revents & (POLLHUP | POLLERR)) {
FD_SET(info->fd, exceptfds);
event_cnt++;
}
}
return event_cnt;
}
struct PollInfo {
PollInfo() : index(-1) {};
std::vector<struct pollfd*> fds;
int index;
};
typedef std::map<EventEmitter*, PollInfo> EventPollMap_t;
int KernelProxy::poll(struct pollfd *fds, nfds_t nfds, int timeout) {
EventPollMap_t event_map;
std::vector<EventRequest> requests;
size_t event_cnt = 0;
for (int index = 0; static_cast<nfds_t>(index) < nfds; index++) {
ScopedKernelHandle handle;
struct pollfd* fd_info = &fds[index];
Error err = AcquireHandle(fd_info->fd, &handle);
fd_info->revents = 0;
// If the node isn't open, or somehow invalid, mark it so.
if (err != 0) {
fd_info->revents = POLLNVAL;
event_cnt++;
continue;
}
// If it's already signaled, then just capture the event
ScopedEventEmitter emitter(handle->node()->GetEventEmitter());
int events = POLLIN | POLLOUT;
if (emitter)
events = emitter->GetEventStatus();
if (events & fd_info->events) {
fd_info->revents = events & fd_info->events;
event_cnt++;
continue;
}
if (NULL == emitter) {
fd_info->revents = POLLNVAL;
event_cnt++;
continue;
}
// Otherwise try to track it.
PollInfo* info = &event_map[emitter.get()];
if (info->index == -1) {
EventRequest request;
request.emitter = emitter;
request.filter = fd_info->events;
request.events = 0;
info->index = requests.size();
requests.push_back(request);
}
info->fds.push_back(fd_info);
requests[info->index].filter |= fd_info->events;
}
// If nothing is signaled, then we must wait on the event map
if (0 == event_cnt) {
EventListenerPoll wait;
Error err = wait.WaitOnAny(&requests[0], requests.size(), timeout);
if ((err != 0) && (err != ETIMEDOUT)) {
errno = err;
return -1;
}
for (size_t rindex = 0; rindex < requests.size(); rindex++) {
EventRequest* request = &requests[rindex];
if (request->events) {
PollInfo* poll_info = &event_map[request->emitter.get()];
for (size_t findex = 0; findex < poll_info->fds.size(); findex++) {
struct pollfd* fd_info = poll_info->fds[findex];
uint32_t events = fd_info->events & request->events;
if (events) {
fd_info->revents = events;
event_cnt++;
}
}
}
}
}
return event_cnt;
}
// Socket Functions
int KernelProxy::accept(int fd, struct sockaddr* addr, socklen_t* len) {
if (NULL == addr || NULL == len) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
PP_Resource new_sock = 0;
error = handle->Accept(&new_sock, addr, len);
if (error != 0) {
errno = error;
return -1;
}
MountNodeSocket* sock = new MountNodeTCP(stream_mount_.get(), new_sock);
// The MountNodeSocket now holds a reference to the new socket
// so we release ours.
ppapi_->ReleaseResource(new_sock);
error = sock->Init(S_IREAD | S_IWRITE);
if (error != 0) {
errno = error;
return -1;
}
ScopedMountNode node(sock);
ScopedKernelHandle new_handle(new KernelHandle(stream_mount_, node));
return AllocateFD(new_handle);
}
int KernelProxy::bind(int fd, const struct sockaddr* addr, socklen_t len) {
if (NULL == addr) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
Error err = handle->socket_node()->Bind(addr, len);
if (err != 0) {
errno = err;
return -1;
}
return 0;
}
int KernelProxy::connect(int fd, const struct sockaddr* addr, socklen_t len) {
if (NULL == addr) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->Connect(addr, len);
if (error != 0) {
errno = error;
return -1;
}
return 0;
}
struct hostent* KernelProxy::gethostbyname(const char* name) {
return host_resolver_.gethostbyname(name);
}
int KernelProxy::getpeername(int fd, struct sockaddr* addr, socklen_t* len) {
if (NULL == addr || NULL == len) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
Error err = handle->socket_node()->GetPeerName(addr, len);
if (err != 0) {
errno = err;
return -1;
}
return 0;
}
int KernelProxy::getsockname(int fd, struct sockaddr* addr, socklen_t* len) {
if (NULL == addr || NULL == len) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
Error err = handle->socket_node()->GetSockName(addr, len);
if (err != 0) {
errno = err;
return -1;
}
return 0;
}
int KernelProxy::getsockopt(int fd,
int lvl,
int optname,
void* optval,
socklen_t* len) {
if (NULL == optval || NULL == len) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
Error err = handle->socket_node()->GetSockOpt(lvl, optname, optval, len);
if (err != 0) {
errno = err;
return -1;
}
return 0;
}
int KernelProxy::listen(int fd, int backlog) {
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
Error err = handle->socket_node()->Listen(backlog);
if (err != 0) {
errno = err;
return -1;
}
return 0;
}
ssize_t KernelProxy::recv(int fd,
void* buf,
size_t len,
int flags) {
if (NULL == buf) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int out_len = 0;
error = handle->Recv(buf, len, flags, &out_len);
if (error != 0) {
errno = error;
return -1;
}
return static_cast<ssize_t>(out_len);
}
ssize_t KernelProxy::recvfrom(int fd,
void* buf,
size_t len,
int flags,
struct sockaddr* addr,
socklen_t* addrlen) {
// According to the manpage, recvfrom with a null addr is identical to recv.
if (NULL == addr) {
return recv(fd, buf, len, flags);
}
if (NULL == buf || NULL == addrlen) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int out_len = 0;
error = handle->RecvFrom(buf, len, flags, addr, addrlen, &out_len);
if (error != 0) {
errno = error;
return -1;
}
return static_cast<ssize_t>(out_len);
}
ssize_t KernelProxy::recvmsg(int fd, struct msghdr* msg, int flags) {
if (NULL == msg ) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
errno = EOPNOTSUPP;
return -1;
}
ssize_t KernelProxy::send(int fd, const void* buf, size_t len, int flags) {
if (NULL == buf) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int out_len = 0;
error = handle->Send(buf, len, flags, &out_len);
if (error != 0) {
errno = error;
return -1;
}
return static_cast<ssize_t>(out_len);
}
ssize_t KernelProxy::sendto(int fd,
const void* buf,
size_t len,
int flags,
const struct sockaddr* addr,
socklen_t addrlen) {
// According to the manpage, sendto with a null addr is identical to send.
if (NULL == addr) {
return send(fd, buf, len, flags);
}
if (NULL == buf) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int out_len = 0;
error = handle->SendTo(buf, len, flags, addr, addrlen, &out_len);
if (error != 0) {
errno = error;
return -1;
}
return static_cast<ssize_t>(out_len);
}
ssize_t KernelProxy::sendmsg(int fd, const struct msghdr* msg, int flags) {
if (NULL == msg) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
errno = EOPNOTSUPP;
return -1;
}
int KernelProxy::setsockopt(int fd,
int lvl,
int optname,
const void* optval,
socklen_t len) {
if (NULL == optval) {
errno = EFAULT;
return -1;
}
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
Error err = handle->socket_node()->SetSockOpt(lvl, optname, optval, len);
if (err != 0) {
errno = err;
return -1;
}
return 0;
}
int KernelProxy::shutdown(int fd, int how) {
ScopedKernelHandle handle;
if (AcquireSocketHandle(fd, &handle) == -1)
return -1;
Error err = handle->socket_node()->Shutdown(how);
if (err != 0) {
errno = err;
return -1;
}
return 0;
}
int KernelProxy::socket(int domain, int type, int protocol) {
if (AF_INET != domain && AF_INET6 != domain) {
errno = EAFNOSUPPORT;
return -1;
}
MountNodeSocket* sock = NULL;
switch (type) {
case SOCK_DGRAM:
sock = new MountNodeUDP(stream_mount_.get());
break;
case SOCK_STREAM:
sock = new MountNodeTCP(stream_mount_.get());
break;
default:
errno = EPROTONOSUPPORT;
return -1;
}
ScopedMountNode node(sock);
Error rtn = sock->Init(S_IREAD | S_IWRITE);
if (rtn != 0) {
errno = rtn;
return -1;
}
ScopedKernelHandle handle(new KernelHandle(stream_mount_, node));
return AllocateFD(handle);
}
int KernelProxy::socketpair(int domain, int type, int protocol, int* sv) {
if (NULL == sv) {
errno = EFAULT;
return -1;
}
// Catch-22: We don't support AF_UNIX, but any other AF doesn't support
// socket pairs. Thus, this function always fails.
if (AF_UNIX != domain) {
errno = EPROTONOSUPPORT;
return -1;
}
if (AF_INET != domain && AF_INET6 != domain) {
errno = EAFNOSUPPORT;
return -1;
}
// We cannot reach this point.
errno = ENOSYS;
return -1;
}
int KernelProxy::AcquireSocketHandle(int fd, ScopedKernelHandle* handle) {
Error error = AcquireHandle(fd, handle);
if (error) {
errno = error;
return -1;
}
if ((handle->get()->node_->GetType() & S_IFSOCK) == 0) {
errno = ENOTSOCK;
return -1;
}
return 0;
}
#endif // PROVIDES_SOCKET_API
} // namespace_nacl_io