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