chromium/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc

// Copyright 2012 The Chromium Authors
// 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