blob: 9ffd555ebdbf855f7afdc6fab017038885827176 [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/devfs/dev_fs.h"
#include "nacl_io/filesystem.h"
#include "nacl_io/fusefs/fuse_fs_factory.h"
#include "nacl_io/googledrivefs/googledrivefs.h"
#include "nacl_io/host_resolver.h"
#include "nacl_io/html5fs/html5_fs.h"
#include "nacl_io/httpfs/http_fs.h"
#include "nacl_io/kernel_handle.h"
#include "nacl_io/kernel_wrap_real.h"
#include "nacl_io/log.h"
#include "nacl_io/memfs/mem_fs.h"
#include "nacl_io/node.h"
#include "nacl_io/osinttypes.h"
#include "nacl_io/osmman.h"
#include "nacl_io/ossocket.h"
#include "nacl_io/osstat.h"
#include "nacl_io/passthroughfs/passthrough_fs.h"
#include "nacl_io/path.h"
#include "nacl_io/pepper_interface.h"
#include "nacl_io/pipe/pipe_node.h"
#include "nacl_io/socket/tcp_node.h"
#include "nacl_io/socket/udp_node.h"
#include "nacl_io/socket/unix_node.h"
#include "nacl_io/stream/stream_fs.h"
#include "nacl_io/typed_fs_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),
exit_callback_(NULL),
exit_callback_user_data_(NULL),
mount_callback_(NULL),
mount_callback_user_data_(NULL),
signal_emitter_(new EventEmitter) {
memset(&sigwinch_handler_, 0, sizeof(sigwinch_handler_));
sigwinch_handler_.sa_handler = SIG_DFL;
}
KernelProxy::~KernelProxy() {
// Clean up the FsFactories.
for (FsFactoryMap_t::iterator i = factories_.begin(); i != factories_.end();
++i) {
delete i->second;
}
}
Error KernelProxy::Init(PepperInterface* ppapi) {
Error rtn = 0;
ppapi_ = ppapi;
dev_ = 1;
factories_["memfs"] = new TypedFsFactory<MemFs>;
factories_["dev"] = new TypedFsFactory<DevFs>;
factories_["googledrivefs"] = new TypedFsFactory<GoogleDriveFs>;
factories_["html5fs"] = new TypedFsFactory<Html5Fs>;
factories_["httpfs"] = new TypedFsFactory<HttpFs>;
factories_["passthroughfs"] = new TypedFsFactory<PassthroughFs>;
ScopedFilesystem root_fs;
rtn = MountInternal("", "/", "passthroughfs", 0, NULL, false, &root_fs);
if (rtn != 0)
return rtn;
ScopedFilesystem fs;
rtn = MountInternal("", "/dev", "dev", 0, NULL, false, &fs);
if (rtn != 0)
return rtn;
dev_fs_ = sdk_util::static_scoped_ref_cast<DevFs>(fs);
// Create the filesystem nodes for / and /dev afterward. They can't be
// created the normal way because the dev filesystem didn't exist yet.
rtn = CreateFsNode(root_fs);
if (rtn != 0)
return rtn;
rtn = CreateFsNode(dev_fs_);
if (rtn != 0)
return rtn;
// Open the first three in order to get STDIN, STDOUT, STDERR
int fd;
fd = open("/dev/stdin", O_RDONLY, 0);
if (fd < 0) {
LOG_ERROR("failed to open /dev/stdin: %s", strerror(errno));
return errno;
}
assert(fd == 0);
fd = open("/dev/stdout", O_WRONLY, 0);
if (fd < 0) {
LOG_ERROR("failed to open /dev/stdout: %s", strerror(errno));
return errno;
}
assert(fd == 1);
fd = open("/dev/stderr", O_WRONLY, 0);
if (fd < 0) {
LOG_ERROR("failed to open /dev/sterr: %s", strerror(errno));
return errno;
}
assert(fd == 2);
#ifdef PROVIDES_SOCKET_API
host_resolver_.Init(ppapi_);
#endif
FsInitArgs args;
args.dev = dev_++;
args.ppapi = ppapi_;
stream_fs_.reset(new StreamFs());
int result = stream_fs_->Init(args);
if (result != 0) {
LOG_ERROR("initializing streamfs failed: %s", strerror(result));
return result;
}
return 0;
}
bool KernelProxy::RegisterFsType(const char* fs_type,
fuse_operations* fuse_ops) {
FsFactoryMap_t::iterator iter = factories_.find(fs_type);
if (iter != factories_.end())
return false;
factories_[fs_type] = new FuseFsFactory(fuse_ops);
return true;
}
bool KernelProxy::UnregisterFsType(const char* fs_type) {
FsFactoryMap_t::iterator iter = factories_.find(fs_type);
if (iter == factories_.end())
return false;
delete iter->second;
factories_.erase(iter);
return true;
}
void KernelProxy::SetExitCallback(nacl_io_exit_callback_t exit_callback,
void* user_data) {
exit_callback_ = exit_callback;
exit_callback_user_data_ = user_data;
}
void KernelProxy::SetMountCallback(nacl_io_mount_callback_t mount_callback,
void* user_data) {
mount_callback_ = mount_callback;
mount_callback_user_data_ = user_data;
}
int KernelProxy::open_resource(const char* path) {
ScopedFilesystem fs;
Path rel;
Error error = AcquireFsAndRelPath(path, &fs, &rel);
if (error) {
errno = error;
return -1;
}
ScopedNode node;
error = fs->OpenResource(rel, &node);
if (error) {
// OpenResource failed, try Open().
error = fs->Open(rel, O_RDONLY, &node);
if (error) {
errno = error;
return -1;
}
}
ScopedKernelHandle handle(new KernelHandle(fs, node));
error = handle->Init(O_RDONLY);
if (error) {
errno = error;
return -1;
}
return AllocateFD(handle, path);
}
int KernelProxy::open(const char* path, int open_flags, mode_t mode) {
ScopedFilesystem fs;
ScopedNode node;
mode_t mask = ~GetUmask() & S_MODEBITS;
Error error = AcquireFsAndNode(path, open_flags, mode & mask, &fs, &node);
if (error) {
errno = error;
return -1;
}
ScopedKernelHandle handle(new KernelHandle(fs, node));
error = handle->Init(open_flags);
if (error) {
errno = error;
return -1;
}
return AllocateFD(handle, path);
}
int KernelProxy::pipe(int pipefds[2]) {
PipeNode* pipe = new PipeNode(stream_fs_.get());
ScopedNode node(pipe);
if (pipe->Init(O_RDWR) == 0) {
ScopedKernelHandle handle0(new KernelHandle(stream_fs_, node));
ScopedKernelHandle handle1(new KernelHandle(stream_fs_, node));
// Should never fail, but...
if (handle0->Init(O_RDONLY) || handle1->Init(O_WRONLY)) {
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;
std::string path;
Error error = AcquireHandleAndPath(oldfd, &handle, &path);
if (error) {
errno = error;
return -1;
}
return AllocateFD(handle, path);
}
int KernelProxy::dup2(int oldfd, int newfd) {
// If it's the same file handle, just return
if (oldfd == newfd)
return newfd;
if (newfd < 0) {
errno = EBADF;
return -1;
}
ScopedKernelHandle old_handle;
std::string old_path;
Error error = AcquireHandleAndPath(oldfd, &old_handle, &old_path);
if (error) {
errno = error;
return -1;
}
FreeAndReassignFD(newfd, old_handle, old_path);
return newfd;
}
int KernelProxy::chdir(const char* path) {
Error error = SetCWD(path);
if (error) {
errno = error;
return -1;
}
return 0;
}
void KernelProxy::exit(int status) {
if (exit_callback_)
exit_callback_(status, exit_callback_user_data_);
}
char* KernelProxy::getcwd(char* buf, size_t size) {
if (NULL == buf) {
errno = EFAULT;
return NULL;
}
std::string cwd = GetCWD();
// Verify the buffer is large enough
if (size <= cwd.size()) {
errno = ERANGE;
return NULL;
}
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) {
ScopedFilesystem fs;
ScopedNode node;
Error error = AcquireFsAndNode(path, O_RDONLY, 0, &fs, &node);
if (error) {
errno = error;
return -1;
}
error = node->Fchmod(mode & S_MODEBITS);
if (error) {
errno = error;
return -1;
}
return 0;
}
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::mkdir(const char* path, mode_t mode) {
ScopedFilesystem fs;
Path rel;
Error error = AcquireFsAndRelPath(path, &fs, &rel);
if (error) {
errno = error;
return -1;
}
mode_t mask = ~GetUmask() & S_MODEBITS;
error = fs->Mkdir(rel, mode & mask);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::rmdir(const char* path) {
ScopedFilesystem fs;
Path rel;
Error error = AcquireFsAndRelPath(path, &fs, &rel);
if (error) {
errno = error;
return -1;
}
error = fs->Rmdir(rel);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::stat(const char* path, struct stat* buf) {
ScopedFilesystem fs;
ScopedNode node;
Error error = AcquireFsAndNode(path, O_RDONLY, 0, &fs, &node);
if (error) {
errno = error;
return -1;
}
error = node->GetStat(buf);
if (error) {
errno = error;
return -1;
}
/*
* newlib's scandir() assumes that directories are empty if st_size == 0.
* This is probably a bad assumption, but until we fix newlib always return
* a non-zero directory size.
*/
if (node->IsaDir() && buf->st_size == 0)
buf->st_size = 4096;
return 0;
}
int KernelProxy::mount(const char* source,
const char* target,
const char* filesystemtype,
unsigned long mountflags,
const void* data) {
ScopedFilesystem fs;
Error error = MountInternal(
source, target, filesystemtype, mountflags, data, true, &fs);
if (error) {
errno = error;
return -1;
}
return 0;
}
Error KernelProxy::MountInternal(const char* source,
const char* target,
const char* filesystemtype,
unsigned long mountflags,
const void* data,
bool create_fs_node,
ScopedFilesystem* out_filesystem) {
std::string abs_path = GetAbsParts(target).Join();
// Find a factory of that type
FsFactoryMap_t::iterator factory = factories_.find(filesystemtype);
if (factory == factories_.end()) {
LOG_ERROR("Unknown filesystem type: %s", filesystemtype);
return ENODEV;
}
// Create a map of settings
StringMap_t smap;
smap["SOURCE"] = source;
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";
}
}
}
FsInitArgs args;
args.dev = dev_++;
args.string_map = smap;
args.ppapi = ppapi_;
ScopedFilesystem fs;
Error error = factory->second->CreateFilesystem(args, &fs);
if (error)
return error;
error = AttachFsAtPath(fs, abs_path);
if (error)
return error;
if (create_fs_node) {
error = CreateFsNode(fs);
if (error) {
DetachFsAtPath(abs_path, &fs);
return error;
}
}
*out_filesystem = fs;
if (mount_callback_) {
mount_callback_(source,
target,
filesystemtype,
mountflags,
data,
fs->dev(),
mount_callback_user_data_);
}
return 0;
}
Error KernelProxy::CreateFsNode(const ScopedFilesystem& fs) {
assert(dev_fs_);
return dev_fs_->CreateFsNode(fs.get());
}
int KernelProxy::umount(const char* path) {
ScopedFilesystem fs;
Error error = DetachFsAtPath(path, &fs);
if (error) {
errno = error;
return -1;
}
error = dev_fs_->DestroyFsNode(fs.get());
if (error) {
// Ignore any errors here, just log.
LOG_ERROR("Unable to destroy FsNode: %s", strerror(error));
}
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;
}
/*
* newlib's scandir() assumes that directories are empty if st_size == 0.
* This is probably a bad assumption, but until we fix newlib always return
* a non-zero directory size.
*/
if (handle->node()->IsaDir() && buf->st_size == 0)
buf->st_size = 4096;
return 0;
}
int KernelProxy::getdents(int fd, struct dirent* buf, unsigned int count) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
int cnt = 0;
error = handle->GetDents(buf, count, &cnt);
if (error)
errno = error;
return cnt;
}
int KernelProxy::fchdir(int fd) {
ScopedKernelHandle handle;
std::string path;
Error error = AcquireHandleAndPath(fd, &handle, &path);
if (error) {
errno = error;
return -1;
}
if (!handle->node()->IsaDir()) {
errno = ENOTDIR;
return -1;
}
if (path.empty()) {
errno = EBADF;
return -1;
}
error = SetCWD(path);
if (error) {
// errno is return value from SetCWD
errno = error;
return -1;
}
return 0;
}
int KernelProxy::ftruncate(int fd, off_t length) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
if (handle->OpenMode() == O_RDONLY) {
errno = EACCES;
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 0;
}
error = handle->node()->Isatty();
if (error) {
errno = error;
return 0;
}
return 1;
}
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;
}
int KernelProxy::futimens(int fd, const struct timespec times[2]) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
return FutimensInternal(handle->node(), times);
}
Error KernelProxy::FutimensInternal(const ScopedNode& node,
const struct timespec times[2]) {
Error error(0);
if (times == NULL) {
struct timespec now[2];
struct timeval tm;
error = gettimeofday(&tm, NULL);
if (error) {
errno = error;
return -1;
}
now[0].tv_sec = now[1].tv_sec = tm.tv_sec;
now[0].tv_nsec = now[1].tv_nsec = tm.tv_usec * 1000;
error = node->Futimens(now);
} else {
error = node->Futimens(times);
}
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) {
ScopedFilesystem fs;
Path rel;
Error error = AcquireFsAndRelPath(path, &fs, &rel);
if (error) {
errno = error;
return -1;
}
error = fs->Unlink(rel);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::truncate(const char* path, off_t len) {
ScopedFilesystem fs;
ScopedNode node;
Error error = AcquireFsAndNode(path, O_WRONLY, 0, &fs, &node);
if (error) {
errno = error;
return -1;
}
// Directories cannot be truncated.
if (node->IsaDir()) {
return EISDIR;
}
if (!node->CanOpen(O_WRONLY)) {
errno = EACCES;
return -1;
}
error = node->FTruncate(len);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::lstat(const char* path, struct stat* buf) {
return stat(path, buf);
}
int KernelProxy::rename(const char* path, const char* newpath) {
ScopedFilesystem fs;
Path rel;
Error error = AcquireFsAndRelPath(path, &fs, &rel);
if (error) {
errno = error;
return -1;
}
ScopedFilesystem newfs;
Path newrel;
error = AcquireFsAndRelPath(newpath, &newfs, &newrel);
if (error) {
errno = error;
return -1;
}
if (newfs.get() != fs.get()) {
// Renaming across mountpoints is not allowed
errno = EXDEV;
return -1;
}
// They already point to the same path
if (rel == newrel)
return 0;
error = fs->Rename(rel, newrel);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::remove(const char* path) {
ScopedFilesystem fs;
Path rel;
Error error = AcquireFsAndRelPath(path, &fs, &rel);
if (error) {
errno = error;
return -1;
}
error = fs->Remove(rel);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::fchmod(int fd, mode_t mode) {
ScopedKernelHandle handle;
Error error = AcquireHandle(fd, &handle);
if (error) {
errno = error;
return -1;
}
error = handle->node()->Fchmod(mode & S_MODEBITS);
if (error) {
errno = error;
return -1;
}
return 0;
}
int KernelProxy::fcntl(int fd, int request, va_list args) {
Error error = 0;
// F_GETFD and F_SETFD are descriptor specific flags that
// are stored in the KernelObject's decriptor map unlike
// F_GETFL and F_SETFL which are handle specific.
switch (request) {
case F_GETFD: {
int rtn = -1;
error = GetFDFlags(fd, &rtn);
if (error) {
errno = error;
return -1;
}
return rtn;
}
case F_SETFD: {
int flags = va_arg(args, int);
error = SetFDFlags(fd, flags);
if (error) {
errno = error;
return -1;
}
return 0;
}
}
ScopedKernelHandle handle;
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) {
struct stat buf;
int rtn = stat(path, &buf);
if (rtn != 0)
return rtn;
if (((amode & R_OK) && !(buf.st_mode & S_IREAD)) ||
((amode & W_OK) && !(buf.st_mode & S_IWRITE)) ||
((amode & X_OK) && !(buf.st_mode & S_IEXEC))) {
errno = EACCES;
return -1;
}
return 0;
}
int KernelProxy::readlink(const char* path, char* buf, size_t count) {
LOG_TRACE("readlink is not implemented.");
errno = EINVAL;
return -1;
}
int KernelProxy::utimens(const char* path, const struct timespec times[2]) {
ScopedFilesystem fs;
ScopedNode node;
Error error = AcquireFsAndNode(path, O_WRONLY, 0, &fs, &node);
if (error) {
errno = error;
return -1;
}
return FutimensInternal(node, times);
}
// TODO(bradnelson): Needs implementation.
int KernelProxy::link(const char* oldpath, const char* newpath) {
LOG_TRACE("link is not implemented.");
errno = EINVAL;
return -1;
}
int KernelProxy::symlink(const char* oldpath, const char* newpath) {
LOG_TRACE("symlink is not implemented.");
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_.sa_handler != SIG_IGN &&
sigwinch_handler_.sa_handler != SIG_DFL) {
sigwinch_handler_.sa_handler(SIGWINCH);
}
break;
case SIGUSR1:
case SIGUSR2:
break;
default:
LOG_TRACE("Unsupported signal: %d", sig);
errno = EINVAL;
return -1;
}
return 0;
}
int KernelProxy::sigaction(int signum,
const struct sigaction* action,
struct sigaction* oaction) {
#if defined(SA_SIGINFO)
if (action && action->sa_flags & SA_SIGINFO) {
// We don't support SA_SIGINFO (sa_sigaction field) yet
errno = EINVAL;
return -1;
}
#endif
switch (signum) {
// Handled signals.
case SIGWINCH: {
if (oaction)
*oaction = sigwinch_handler_;
if (action) {
sigwinch_handler_ = *action;
}
return 0;
}
// Known signals
case SIGHUP:
case SIGINT:
case SIGPIPE:
#if defined(SIGPOLL)
case SIGPOLL:
#endif
case SIGPROF:
case SIGTERM:
case SIGCHLD:
case SIGURG:
case SIGFPE:
case SIGILL:
case SIGQUIT:
case SIGSEGV:
case SIGTRAP:
if (action && action->sa_handler != SIG_DFL) {
// Trying to set this action to anything other than SIG_DFL
// is not yet supported.
LOG_TRACE("sigaction on signal %d != SIG_DFL not supported.", signum);
errno = EINVAL;
return -1;
}
if (oaction) {
memset(oaction, 0, sizeof(*oaction));
oaction->sa_handler = SIG_DFL;
}
return 0;
// KILL and STOP cannot be handled
case SIGKILL:
case SIGSTOP:
LOG_TRACE("sigaction on SIGKILL/SIGSTOP not supported.");
errno = EINVAL;
return -1;
}
// Unknown signum
errno = EINVAL;
return -1;
}
mode_t KernelProxy::umask(mode_t mask) {
return SetUmask(mask & S_MODEBITS);
}
#ifdef PROVIDES_SOCKET_API
int KernelProxy::select(int nfds,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
struct timeval* timeout) {
std::vector<pollfd> pollfds;
for (int fd = 0; fd < nfds; fd++) {
int events = 0;
if (readfds && FD_ISSET(fd, readfds)) {
events |= POLLIN;
FD_CLR(fd, readfds);
}
if (writefds && FD_ISSET(fd, writefds)) {
events |= POLLOUT;
FD_CLR(fd, writefds);
}
if (exceptfds && FD_ISSET(fd, exceptfds)) {
events |= POLLERR | POLLHUP;
FD_CLR(fd, exceptfds);
}
if (events) {
pollfd info;
info.fd = fd;
info.events = events;
pollfds.push_back(info);
}
}
// 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)) {
LOG_TRACE("Invalid timeout: tv_sec=%" PRIi64 " tv_usec=%ld.",
timeout->tv_sec,
timeout->tv_usec);
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) {
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;
}
SocketNode* sock = new TcpNode(stream_fs_.get(), new_sock);
// The SocketNode now holds a reference to the new socket
// so we release ours.
ppapi_->ReleaseResource(new_sock);
error = sock->Init(O_RDWR);
if (error != 0) {
errno = error;
return -1;
}
ScopedNode node(sock);
ScopedKernelHandle new_handle(new KernelHandle(stream_fs_, node));
error = new_handle->Init(O_RDWR);
if (error != 0) {
errno = error;
return -1;
}
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;
}
void KernelProxy::freeaddrinfo(struct addrinfo* res) {
return host_resolver_.freeaddrinfo(res);
}
int KernelProxy::getaddrinfo(const char* node,
const char* service,
const struct addrinfo* hints,
struct addrinfo** res) {
return host_resolver_.getaddrinfo(node, service, hints, res);
}
int KernelProxy::getnameinfo(const struct sockaddr *sa,
socklen_t salen,
char *host,
size_t hostlen,
char *serv,
size_t servlen,
int flags) {
return host_resolver_.getnameinfo(sa, salen, host, hostlen, serv, servlen,
flags);
}
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;
int total_len = 0;
int out_len = 0;
for (size_t i = 0; i < static_cast<size_t>(msg->msg_iovlen); i++) {
if (NULL == msg->msg_iov[i].iov_base) {
errno = EFAULT;
return -1;
}
// Note that msg_control is not implemented.
Error error = handle->RecvFrom(msg->msg_iov[i].iov_base,
msg->msg_iov[i].iov_len, flags,
static_cast<struct sockaddr*>(msg->msg_name),
&msg->msg_namelen, &out_len);
if (error != 0) {
errno = error;
return -1;
}
total_len += out_len;
}
return static_cast<ssize_t>(total_len);
}
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;
int total_len = 0;
int out_len = 0;
for (size_t i = 0; i < static_cast<size_t>(msg->msg_iovlen); i++) {
if (NULL == msg->msg_iov[i].iov_base) {
errno = EFAULT;
return -1;
}
// Note that msg_control is not implemented.
Error error = handle->SendTo(msg->msg_iov[i].iov_base,
msg->msg_iov[i].iov_len, flags,
static_cast<struct sockaddr*>(msg->msg_name),
msg->msg_namelen, &out_len);
if (error != 0) {
errno = error;
return -1;
}
total_len += out_len;
}
return static_cast<ssize_t>(total_len);
}
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;
}
int open_flags = O_RDWR;
#if defined(SOCK_CLOEXEC)
if (type & SOCK_CLOEXEC) {
#if defined(O_CLOEXEC)
// The NaCl newlib version of fcntl.h doesn't currently define
// O_CLOEXEC.
// TODO(sbc): remove this guard once it gets added.
open_flags |= O_CLOEXEC;
#endif
type &= ~SOCK_CLOEXEC;
}
#endif
#if defined(SOCK_NONBLOCK)
if (type & SOCK_NONBLOCK) {
open_flags |= O_NONBLOCK;
type &= ~SOCK_NONBLOCK;
}
#endif
SocketNode* sock = NULL;
switch (type) {
case SOCK_DGRAM:
sock = new UdpNode(stream_fs_.get());
break;
case SOCK_STREAM:
sock = new TcpNode(stream_fs_.get());
break;
case SOCK_SEQPACKET:
case SOCK_RDM:
case SOCK_RAW:
errno = EPROTONOSUPPORT;
return -1;
default:
errno = EINVAL;
return -1;
}
ScopedNode node(sock);
Error rtn = sock->Init(O_RDWR);
if (rtn != 0) {
errno = rtn;
return -1;
}
ScopedKernelHandle handle(new KernelHandle(stream_fs_, node));
rtn = handle->Init(open_flags);
if (rtn != 0) {
errno = rtn;
return -1;
}
return AllocateFD(handle);
}
int KernelProxy::socketpair(int domain, int type, int protocol, int* sv) {
if (NULL == sv) {
errno = EFAULT;
return -1;
}
if (AF_INET == domain || AF_INET6 == domain) {
errno = EOPNOTSUPP;
return -1;
}
if (AF_UNIX != domain) {
errno = EAFNOSUPPORT;
return -1;
}
// TODO(cernekee): mask this off with SOCK_TYPE_MASK first.
if (SOCK_STREAM != type && SOCK_DGRAM != type) {
errno = EPROTOTYPE;
return -1;
}
int open_flags = O_RDWR;
#if defined(SOCK_CLOEXEC)
if (type & SOCK_CLOEXEC) {
#if defined(O_CLOEXEC)
// The NaCl newlib version of fcntl.h doesn't currently define
// O_CLOEXEC.
// TODO(sbc): remove this guard once it gets added.
open_flags |= O_CLOEXEC;
#endif
type &= ~SOCK_CLOEXEC;
}
#endif
#if defined(SOCK_NONBLOCK)
if (type & SOCK_NONBLOCK) {
open_flags |= O_NONBLOCK;
type &= ~SOCK_NONBLOCK;
}
#endif
UnixNode* socket = new UnixNode(stream_fs_.get(), type);
Error rtn = socket->Init(O_RDWR);
if (rtn != 0) {
errno = rtn;
return -1;
}
ScopedNode node0(socket);
socket = new UnixNode(stream_fs_.get(), *socket);
rtn = socket->Init(O_RDWR);
if (rtn != 0) {
errno = rtn;
return -1;
}
ScopedNode node1(socket);
ScopedKernelHandle handle0(new KernelHandle(stream_fs_, node0));
rtn = handle0->Init(open_flags);
if (rtn != 0) {
errno = rtn;
return -1;
}
ScopedKernelHandle handle1(new KernelHandle(stream_fs_, node1));
rtn = handle1->Init(open_flags);
if (rtn != 0) {
errno = rtn;
return -1;
}
sv[0] = AllocateFD(handle0);
sv[1] = AllocateFD(handle1);
return 0;
}
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