dev_fs.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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#if defined(WIN32)
6#define _CRT_RAND_S
7#endif
8
9#include "nacl_io/devfs/dev_fs.h"
10
11#include <errno.h>
12#include <fcntl.h>
13#include <pthread.h>
14#include <stdio.h>
15#include <string.h>
16
17#include "nacl_io/devfs/jspipe_node.h"
18#include "nacl_io/devfs/tty_node.h"
19#include "nacl_io/dir_node.h"
20#include "nacl_io/kernel_wrap_real.h"
21#include "nacl_io/node.h"
22#include "nacl_io/osunistd.h"
23#include "nacl_io/pepper_interface.h"
24#include "sdk_util/auto_lock.h"
25
26#if defined(__native_client__)
27#include <irt.h>
28#elif defined(WIN32)
29#include <stdlib.h>
30#endif
31
32namespace nacl_io {
33
34namespace {
35
36class RealNode : public Node {
37 public:
38  RealNode(Filesystem* filesystem, int fd);
39
40  virtual Error Read(const HandleAttr& attr,
41                     void* buf,
42                     size_t count,
43                     int* out_bytes);
44  virtual Error Write(const HandleAttr& attr,
45                      const void* buf,
46                      size_t count,
47                      int* out_bytes);
48  virtual Error GetStat(struct stat* stat);
49
50 protected:
51  int fd_;
52};
53
54class NullNode : public CharNode {
55 public:
56  explicit NullNode(Filesystem* filesystem) : CharNode(filesystem) {}
57
58  virtual Error Read(const HandleAttr& attr,
59                     void* buf,
60                     size_t count,
61                     int* out_bytes);
62  virtual Error Write(const HandleAttr& attr,
63                      const void* buf,
64                      size_t count,
65                      int* out_bytes);
66};
67
68class ConsoleNode : public CharNode {
69 public:
70  ConsoleNode(Filesystem* filesystem, PP_LogLevel level);
71
72  virtual Error Write(const HandleAttr& attr,
73                      const void* buf,
74                      size_t count,
75                      int* out_bytes);
76
77 private:
78  PP_LogLevel level_;
79};
80
81class ZeroNode : public Node {
82 public:
83  explicit ZeroNode(Filesystem* filesystem);
84
85  virtual Error Read(const HandleAttr& attr,
86                     void* buf,
87                     size_t count,
88                     int* out_bytes);
89  virtual Error Write(const HandleAttr& attr,
90                      const void* buf,
91                      size_t count,
92                      int* out_bytes);
93};
94
95class UrandomNode : public Node {
96 public:
97  explicit UrandomNode(Filesystem* filesystem);
98
99  virtual Error Read(const HandleAttr& attr,
100                     void* buf,
101                     size_t count,
102                     int* out_bytes);
103  virtual Error Write(const HandleAttr& attr,
104                      const void* buf,
105                      size_t count,
106                      int* out_bytes);
107
108 private:
109#if defined(__native_client__)
110  nacl_irt_random random_interface_;
111  bool interface_ok_;
112#endif
113};
114
115class FsNode : public Node {
116 public:
117  FsNode(Filesystem* filesystem, Filesystem* other_fs);
118
119  virtual Error VIoctl(int request, va_list args);
120
121 private:
122  // Don't addref the filesystem. We are relying on the fact that the
123  // KernelObject will keep the filsystem around as long as we need it, and
124  // this node will be destroyed when the filesystem is destroyed.
125  Filesystem* other_fs_;
126};
127
128RealNode::RealNode(Filesystem* filesystem, int fd) : Node(filesystem), fd_(fd) {
129  SetType(S_IFCHR);
130}
131
132Error RealNode::Read(const HandleAttr& attr,
133                     void* buf,
134                     size_t count,
135                     int* out_bytes) {
136  *out_bytes = 0;
137
138  size_t readcnt;
139  int err = _real_read(fd_, buf, count, &readcnt);
140  if (err)
141    return err;
142
143  *out_bytes = static_cast<int>(readcnt);
144  return 0;
145}
146
147Error RealNode::Write(const HandleAttr& attr,
148                      const void* buf,
149                      size_t count,
150                      int* out_bytes) {
151  *out_bytes = 0;
152
153  size_t writecnt;
154  int err = _real_write(fd_, buf, count, &writecnt);
155  if (err)
156    return err;
157
158  *out_bytes = static_cast<int>(writecnt);
159  return 0;
160}
161
162Error RealNode::GetStat(struct stat* stat) {
163  return _real_fstat(fd_, stat);
164}
165
166Error NullNode::Read(const HandleAttr& attr,
167                     void* buf,
168                     size_t count,
169                     int* out_bytes) {
170  *out_bytes = 0;
171  return 0;
172}
173
174Error NullNode::Write(const HandleAttr& attr,
175                      const void* buf,
176                      size_t count,
177                      int* out_bytes) {
178  *out_bytes = count;
179  return 0;
180}
181
182ConsoleNode::ConsoleNode(Filesystem* filesystem, PP_LogLevel level)
183    : CharNode(filesystem), level_(level) {
184}
185
186Error ConsoleNode::Write(const HandleAttr& attr,
187                         const void* buf,
188                         size_t count,
189                         int* out_bytes) {
190  *out_bytes = 0;
191
192  ConsoleInterface* con_iface = filesystem_->ppapi()->GetConsoleInterface();
193  VarInterface* var_iface = filesystem_->ppapi()->GetVarInterface();
194
195  if (!(var_iface && con_iface)) {
196    LOG_ERROR("Got NULL interface(s): %s%s",
197              con_iface ? "" : "Console ",
198              var_iface ? "" : "Var");
199    return ENOSYS;
200  }
201
202  const char* var_data = static_cast<const char*>(buf);
203  uint32_t len = static_cast<uint32_t>(count);
204  struct PP_Var val = var_iface->VarFromUtf8(var_data, len);
205  con_iface->Log(filesystem_->ppapi()->GetInstance(), level_, val);
206  var_iface->Release(val);
207
208  *out_bytes = count;
209  return 0;
210}
211
212ZeroNode::ZeroNode(Filesystem* filesystem) : Node(filesystem) {
213  SetType(S_IFCHR);
214}
215
216Error ZeroNode::Read(const HandleAttr& attr,
217                     void* buf,
218                     size_t count,
219                     int* out_bytes) {
220  memset(buf, 0, count);
221  *out_bytes = count;
222  return 0;
223}
224
225Error ZeroNode::Write(const HandleAttr& attr,
226                      const void* buf,
227                      size_t count,
228                      int* out_bytes) {
229  *out_bytes = count;
230  return 0;
231}
232
233UrandomNode::UrandomNode(Filesystem* filesystem) : Node(filesystem) {
234  SetType(S_IFCHR);
235#if defined(__native_client__)
236  size_t result = nacl_interface_query(
237      NACL_IRT_RANDOM_v0_1, &random_interface_, sizeof(random_interface_));
238  interface_ok_ = result != 0;
239#endif
240}
241
242Error UrandomNode::Read(const HandleAttr& attr,
243                        void* buf,
244                        size_t count,
245                        int* out_bytes) {
246  *out_bytes = 0;
247
248#if defined(__native_client__)
249  if (!interface_ok_) {
250    LOG_ERROR("NACL_IRT_RANDOM_v0_1 interface not avaiable.");
251    return EBADF;
252  }
253
254  size_t nread;
255  int error = (*random_interface_.get_random_bytes)(buf, count, &nread);
256  if (error)
257    return error;
258#elif defined(WIN32)
259  char* out = static_cast<char*>(buf);
260  size_t bytes_left = count;
261  while (bytes_left) {
262    unsigned int random_int;
263    errno_t err = rand_s(&random_int);
264    if (err) {
265      *out_bytes = count - bytes_left;
266      return err;
267    }
268
269    int bytes_to_copy = std::min(bytes_left, sizeof(random_int));
270    memcpy(out, &random_int, bytes_to_copy);
271    out += bytes_to_copy;
272    bytes_left -= bytes_to_copy;
273  }
274#endif
275
276  *out_bytes = count;
277  return 0;
278}
279
280Error UrandomNode::Write(const HandleAttr& attr,
281                         const void* buf,
282                         size_t count,
283                         int* out_bytes) {
284  *out_bytes = count;
285  return 0;
286}
287
288FsNode::FsNode(Filesystem* filesystem, Filesystem* other_fs)
289    : Node(filesystem), other_fs_(other_fs) {
290}
291
292Error FsNode::VIoctl(int request, va_list args) {
293  return other_fs_->Filesystem_VIoctl(request, args);
294}
295
296}  // namespace
297
298Error DevFs::Access(const Path& path, int a_mode) {
299  ScopedNode node;
300  int error = root_->FindChild(path.Join(), &node);
301  if (error)
302    return error;
303
304  // Don't allow execute access.
305  if (a_mode & X_OK) {
306    LOG_TRACE("Executing devfs nodes is not allowed.");
307    return EACCES;
308  }
309
310  return 0;
311}
312
313Error DevFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
314  out_node->reset(NULL);
315  int error;
316  if (path.Part(1) == "fs") {
317    if (path.Size() == 3) {
318      error = fs_dir_->FindChild(path.Part(2), out_node);
319    } else {
320      LOG_TRACE("Bad devfs path: %s", path.Join().c_str());
321      error = ENOENT;
322    }
323  } else {
324    error = root_->FindChild(path.Join(), out_node);
325  }
326
327  // Only return EACCES when trying to create a node that does not exist.
328  if ((error == ENOENT) && (open_flags & O_CREAT)) {
329    LOG_TRACE("Cannot create devfs node: %s", path.Join().c_str());
330    return EACCES;
331  }
332
333  return error;
334}
335
336Error DevFs::Unlink(const Path& path) {
337  LOG_ERROR("unlink not supported.");
338  return EPERM;
339}
340
341Error DevFs::Mkdir(const Path& path, int permissions) {
342  LOG_ERROR("mkdir not supported.");
343  return EPERM;
344}
345
346Error DevFs::Rmdir(const Path& path) {
347  LOG_ERROR("rmdir not supported.");
348  return EPERM;
349}
350
351Error DevFs::Remove(const Path& path) {
352  LOG_ERROR("remove not supported.");
353  return EPERM;
354}
355
356Error DevFs::Rename(const Path& path, const Path& newpath) {
357  LOG_ERROR("rename not supported.");
358  return EPERM;
359}
360
361Error DevFs::CreateFsNode(Filesystem* other_fs) {
362  int dev = other_fs->dev();
363  char path[32];
364  snprintf(path, 32, "%d", dev);
365  ScopedNode new_node(new FsNode(this, other_fs));
366  return fs_dir_->AddChild(path, new_node);
367}
368
369Error DevFs::DestroyFsNode(Filesystem* other_fs) {
370  int dev = other_fs->dev();
371  char path[32];
372  snprintf(path, 32, "%d", dev);
373  return fs_dir_->RemoveChild(path);
374}
375
376DevFs::DevFs() {
377}
378
379#define INITIALIZE_DEV_NODE(path, klass)   \
380  new_node = ScopedNode(new klass(this));  \
381  error = root_->AddChild(path, new_node); \
382  if (error)                               \
383    return error;
384
385#define INITIALIZE_DEV_NODE_1(path, klass, arg) \
386  new_node = ScopedNode(new klass(this, arg));  \
387  error = root_->AddChild(path, new_node);      \
388  if (error)                                    \
389    return error;
390
391Error DevFs::Init(const FsInitArgs& args) {
392  Error error = Filesystem::Init(args);
393  if (error)
394    return error;
395
396  root_.reset(new DirNode(this));
397
398  ScopedNode new_node;
399  INITIALIZE_DEV_NODE("/null", NullNode);
400  INITIALIZE_DEV_NODE("/zero", ZeroNode);
401  INITIALIZE_DEV_NODE("/urandom", UrandomNode);
402  INITIALIZE_DEV_NODE_1("/console0", ConsoleNode, PP_LOGLEVEL_TIP);
403  INITIALIZE_DEV_NODE_1("/console1", ConsoleNode, PP_LOGLEVEL_LOG);
404  INITIALIZE_DEV_NODE_1("/console2", ConsoleNode, PP_LOGLEVEL_WARNING);
405  INITIALIZE_DEV_NODE_1("/console3", ConsoleNode, PP_LOGLEVEL_ERROR);
406  INITIALIZE_DEV_NODE("/tty", TtyNode);
407  INITIALIZE_DEV_NODE_1("/stdin", RealNode, 0);
408  INITIALIZE_DEV_NODE_1("/stdout", RealNode, 1);
409  INITIALIZE_DEV_NODE_1("/stderr", RealNode, 2);
410  INITIALIZE_DEV_NODE("/jspipe1", JSPipeNode);
411  new_node->Ioctl(NACL_IOC_PIPE_SETNAME, "jspipe1");
412  INITIALIZE_DEV_NODE("/jspipe2", JSPipeNode);
413  new_node->Ioctl(NACL_IOC_PIPE_SETNAME, "jspipe2");
414  INITIALIZE_DEV_NODE("/jspipe3", JSPipeNode);
415  new_node->Ioctl(NACL_IOC_PIPE_SETNAME, "jspipe3");
416
417  // Add a directory for "fs" nodes; they represent all currently-mounted
418  // filesystems. We can ioctl these nodes to make changes or provide input to
419  // a mounted filesystem.
420  INITIALIZE_DEV_NODE("/fs", DirNode);
421  fs_dir_ = new_node;
422
423  return 0;
424}
425
426}  // namespace nacl_io
427