node.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "nacl_io/node.h"
6
7#include <assert.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <poll.h>
11#include <string.h>
12#include <sys/stat.h>
13
14#include <algorithm>
15#include <string>
16
17#include "nacl_io/filesystem.h"
18#include "nacl_io/kernel_handle.h"
19#include "nacl_io/kernel_wrap_real.h"
20#include "nacl_io/osmman.h"
21#include "sdk_util/auto_lock.h"
22
23namespace nacl_io {
24
25static const int USR_ID = 1001;
26static const int GRP_ID = 1002;
27
28Node::Node(Filesystem* filesystem) : filesystem_(filesystem) {
29  memset(&stat_, 0, sizeof(stat_));
30  stat_.st_gid = GRP_ID;
31  stat_.st_uid = USR_ID;
32  stat_.st_mode = S_IRALL | S_IWALL;
33
34  // Filesystem should normally never be NULL, but may be null in tests.
35  // If NULL, at least set the inode to a valid (nonzero) value.
36  if (filesystem_)
37    filesystem_->OnNodeCreated(this);
38  else
39    stat_.st_ino = 1;
40}
41
42Node::~Node() {
43}
44
45Error Node::Init(int open_flags) {
46  return 0;
47}
48
49void Node::Destroy() {
50  if (filesystem_) {
51    filesystem_->OnNodeDestroyed(this);
52  }
53}
54
55EventEmitter* Node::GetEventEmitter() {
56  return NULL;
57}
58
59uint32_t Node::GetEventStatus() {
60  if (GetEventEmitter())
61    return GetEventEmitter()->GetEventStatus();
62
63  return POLLIN | POLLOUT;
64}
65
66bool Node::CanOpen(int open_flags) {
67  switch (open_flags & 3) {
68    case O_RDONLY:
69      return (stat_.st_mode & S_IRALL) != 0;
70    case O_WRONLY:
71      return (stat_.st_mode & S_IWALL) != 0;
72    case O_RDWR:
73      return (stat_.st_mode & S_IRALL) != 0 && (stat_.st_mode & S_IWALL) != 0;
74  }
75
76  return false;
77}
78
79Error Node::FSync() {
80  return 0;
81}
82
83Error Node::FTruncate(off_t length) {
84  return EINVAL;
85}
86
87Error Node::GetDents(size_t offs,
88                     struct dirent* pdir,
89                     size_t count,
90                     int* out_bytes) {
91  *out_bytes = 0;
92  return ENOTDIR;
93}
94
95Error Node::GetStat(struct stat* pstat) {
96  AUTO_LOCK(node_lock_);
97  memcpy(pstat, &stat_, sizeof(stat_));
98  return 0;
99}
100
101Error Node::Ioctl(int request, ...) {
102  va_list ap;
103  va_start(ap, request);
104  Error rtn = VIoctl(request, ap);
105  va_end(ap);
106  return rtn;
107}
108
109Error Node::VIoctl(int request, va_list args) {
110  return EINVAL;
111}
112
113Error Node::Read(const HandleAttr& attr,
114                 void* buf,
115                 size_t count,
116                 int* out_bytes) {
117  *out_bytes = 0;
118  return EINVAL;
119}
120
121Error Node::Write(const HandleAttr& attr,
122                  const void* buf,
123                  size_t count,
124                  int* out_bytes) {
125  *out_bytes = 0;
126  return EINVAL;
127}
128
129Error Node::MMap(void* addr,
130                 size_t length,
131                 int prot,
132                 int flags,
133                 size_t offset,
134                 void** out_addr) {
135  *out_addr = NULL;
136
137  // Never allow mmap'ing PROT_EXEC. The passthrough node supports this, but we
138  // don't. Fortunately, glibc will fallback if this fails, so dlopen will
139  // continue to work.
140  if (prot & PROT_EXEC)
141    return EPERM;
142
143  // This default mmap support is just enough to make dlopen work.  This
144  // implementation just reads from the filesystem into the mmap'd memory area.
145  void* new_addr = addr;
146  int mmap_error = _real_mmap(
147      &new_addr, length, prot | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0);
148  if (new_addr == MAP_FAILED) {
149    _real_munmap(new_addr, length);
150    return mmap_error;
151  }
152
153  HandleAttr data;
154  data.offs = offset;
155  data.flags = 0;
156  int bytes_read;
157  Error read_error = Read(data, new_addr, length, &bytes_read);
158  if (read_error) {
159    _real_munmap(new_addr, length);
160    return read_error;
161  }
162
163  *out_addr = new_addr;
164  return 0;
165}
166
167Error Node::Tcflush(int queue_selector) {
168  return EINVAL;
169}
170
171Error Node::Tcgetattr(struct termios* termios_p) {
172  return EINVAL;
173}
174
175Error Node::Tcsetattr(int optional_actions, const struct termios* termios_p) {
176  return EINVAL;
177}
178
179Error Node::Futimens(const struct timespec times[2]) {
180  return 0;
181}
182
183Error Node::Fchmod(mode_t mode) {
184  return EINVAL;
185}
186
187int Node::GetLinks() {
188  return stat_.st_nlink;
189}
190
191int Node::GetMode() {
192  return stat_.st_mode & ~S_IFMT;
193}
194
195Error Node::GetSize(off_t* out_size) {
196  *out_size = stat_.st_size;
197  return 0;
198}
199
200int Node::GetType() {
201  return stat_.st_mode & S_IFMT;
202}
203
204void Node::SetType(int type) {
205  assert((type & ~S_IFMT) == 0);
206  stat_.st_mode &= ~S_IFMT;
207  stat_.st_mode |= type;
208}
209
210void Node::SetMode(int mode) {
211  assert((mode & S_IFMT) == 0);
212  stat_.st_mode &= S_IFMT;
213  stat_.st_mode |= mode;
214}
215
216bool Node::IsaDir() {
217  return GetType() == S_IFDIR;
218}
219
220bool Node::IsaFile() {
221  return GetType() == S_IFREG;
222}
223
224bool Node::IsaSock() {
225  return GetType() == S_IFSOCK;
226}
227
228Error Node::Isatty() {
229  return ENOTTY;
230}
231
232Error Node::AddChild(const std::string& name, const ScopedNode& node) {
233  return ENOTDIR;
234}
235
236Error Node::RemoveChild(const std::string& name) {
237  return ENOTDIR;
238}
239
240Error Node::FindChild(const std::string& name, ScopedNode* out_node) {
241  out_node->reset(NULL);
242  return ENOTDIR;
243}
244
245int Node::ChildCount() {
246  return 0;
247}
248
249void Node::Link() {
250  stat_.st_nlink++;
251}
252
253void Node::Unlink() {
254  stat_.st_nlink--;
255}
256
257}  // namespace nacl_io
258