| // Copyright 2013 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/node.h" |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include "nacl_io/filesystem.h" |
| #include "nacl_io/kernel_handle.h" |
| #include "nacl_io/kernel_wrap_real.h" |
| #include "nacl_io/osmman.h" |
| #include "sdk_util/auto_lock.h" |
| |
| namespace nacl_io { |
| |
| static const int USR_ID = 1001; |
| static const int GRP_ID = 1002; |
| |
| Node::Node(Filesystem* filesystem) : filesystem_(filesystem) { |
| memset(&stat_, 0, sizeof(stat_)); |
| stat_.st_gid = GRP_ID; |
| stat_.st_uid = USR_ID; |
| stat_.st_mode = S_IRALL | S_IWALL; |
| |
| // Filesystem should normally never be NULL, but may be null in tests. |
| // If NULL, at least set the inode to a valid (nonzero) value. |
| if (filesystem_) |
| filesystem_->OnNodeCreated(this); |
| else |
| stat_.st_ino = 1; |
| } |
| |
| Node::~Node() { |
| } |
| |
| Error Node::Init(int open_flags) { |
| return 0; |
| } |
| |
| void Node::Destroy() { |
| if (filesystem_) { |
| filesystem_->OnNodeDestroyed(this); |
| } |
| } |
| |
| EventEmitter* Node::GetEventEmitter() { |
| return NULL; |
| } |
| |
| uint32_t Node::GetEventStatus() { |
| if (GetEventEmitter()) |
| return GetEventEmitter()->GetEventStatus(); |
| |
| return POLLIN | POLLOUT; |
| } |
| |
| bool Node::CanOpen(int open_flags) { |
| switch (open_flags & 3) { |
| case O_RDONLY: |
| return (stat_.st_mode & S_IRALL) != 0; |
| case O_WRONLY: |
| return (stat_.st_mode & S_IWALL) != 0; |
| case O_RDWR: |
| return (stat_.st_mode & S_IRALL) != 0 && (stat_.st_mode & S_IWALL) != 0; |
| } |
| |
| return false; |
| } |
| |
| Error Node::FSync() { |
| return 0; |
| } |
| |
| Error Node::FTruncate(off_t length) { |
| return EINVAL; |
| } |
| |
| Error Node::GetDents(size_t offs, |
| struct dirent* pdir, |
| size_t count, |
| int* out_bytes) { |
| *out_bytes = 0; |
| return ENOTDIR; |
| } |
| |
| Error Node::GetStat(struct stat* pstat) { |
| AUTO_LOCK(node_lock_); |
| memcpy(pstat, &stat_, sizeof(stat_)); |
| return 0; |
| } |
| |
| Error Node::Ioctl(int request, ...) { |
| va_list ap; |
| va_start(ap, request); |
| Error rtn = VIoctl(request, ap); |
| va_end(ap); |
| return rtn; |
| } |
| |
| Error Node::VIoctl(int request, va_list args) { |
| return EINVAL; |
| } |
| |
| Error Node::Read(const HandleAttr& attr, |
| void* buf, |
| size_t count, |
| int* out_bytes) { |
| *out_bytes = 0; |
| return EINVAL; |
| } |
| |
| Error Node::Write(const HandleAttr& attr, |
| const void* buf, |
| size_t count, |
| int* out_bytes) { |
| *out_bytes = 0; |
| return EINVAL; |
| } |
| |
| Error Node::MMap(void* addr, |
| size_t length, |
| int prot, |
| int flags, |
| size_t offset, |
| void** out_addr) { |
| *out_addr = NULL; |
| |
| // Never allow mmap'ing PROT_EXEC. The passthrough node supports this, but we |
| // don't. Fortunately, glibc will fallback if this fails, so dlopen will |
| // continue to work. |
| if (prot & PROT_EXEC) |
| return EPERM; |
| |
| // This default mmap support is just enough to make dlopen work. This |
| // implementation just reads from the filesystem into the mmap'd memory area. |
| void* new_addr = addr; |
| int mmap_error = _real_mmap( |
| &new_addr, length, prot | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0); |
| if (new_addr == MAP_FAILED) { |
| _real_munmap(new_addr, length); |
| return mmap_error; |
| } |
| |
| HandleAttr data; |
| data.offs = offset; |
| data.flags = 0; |
| int bytes_read; |
| Error read_error = Read(data, new_addr, length, &bytes_read); |
| if (read_error) { |
| _real_munmap(new_addr, length); |
| return read_error; |
| } |
| |
| *out_addr = new_addr; |
| return 0; |
| } |
| |
| Error Node::Tcflush(int queue_selector) { |
| return EINVAL; |
| } |
| |
| Error Node::Tcgetattr(struct termios* termios_p) { |
| return EINVAL; |
| } |
| |
| Error Node::Tcsetattr(int optional_actions, const struct termios* termios_p) { |
| return EINVAL; |
| } |
| |
| int Node::GetLinks() { |
| return stat_.st_nlink; |
| } |
| |
| int Node::GetMode() { |
| return stat_.st_mode & ~S_IFMT; |
| } |
| |
| Error Node::GetSize(off_t* out_size) { |
| *out_size = stat_.st_size; |
| return 0; |
| } |
| |
| int Node::GetType() { |
| return stat_.st_mode & S_IFMT; |
| } |
| |
| void Node::SetType(int type) { |
| assert((type & ~S_IFMT) == 0); |
| stat_.st_mode &= ~S_IFMT; |
| stat_.st_mode |= type; |
| } |
| |
| bool Node::IsaDir() { |
| return GetType() == S_IFDIR; |
| } |
| |
| bool Node::IsaFile() { |
| return GetType() == S_IFREG; |
| } |
| |
| bool Node::IsaSock() { |
| return GetType() == S_IFSOCK; |
| } |
| |
| Error Node::Isatty() { |
| return ENOTTY; |
| } |
| |
| Error Node::AddChild(const std::string& name, const ScopedNode& node) { |
| return ENOTDIR; |
| } |
| |
| Error Node::RemoveChild(const std::string& name) { |
| return ENOTDIR; |
| } |
| |
| Error Node::FindChild(const std::string& name, ScopedNode* out_node) { |
| out_node->reset(NULL); |
| return ENOTDIR; |
| } |
| |
| int Node::ChildCount() { |
| return 0; |
| } |
| |
| void Node::Link() { |
| stat_.st_nlink++; |
| } |
| |
| void Node::Unlink() { |
| stat_.st_nlink--; |
| } |
| |
| } // namespace nacl_io |