1// Copyright 2014 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/jsfs/js_fs.h" 6 7#include <assert.h> 8#include <errno.h> 9#include <fcntl.h> 10#include <limits.h> 11#include <string.h> 12 13#include "nacl_io/ioctl.h" 14#include "nacl_io/jsfs/js_fs_node.h" 15#include "nacl_io/kernel_handle.h" 16#include "nacl_io/log.h" 17#include "nacl_io/osdirent.h" 18#include "nacl_io/pepper_interface.h" 19#include "sdk_util/macros.h" 20 21namespace nacl_io { 22 23JsFs::JsFs() 24 : messaging_iface_(NULL), 25 array_iface_(NULL), 26 buffer_iface_(NULL), 27 dict_iface_(NULL), 28 var_iface_(NULL), 29 request_id_(0) { 30} 31 32Error JsFs::Init(const FsInitArgs& args) { 33 Error error = Filesystem::Init(args); 34 if (error) 35 return error; 36 37 pthread_cond_init(&response_cond_, NULL); 38 39 messaging_iface_ = ppapi_->GetMessagingInterface(); 40 array_iface_ = ppapi_->GetVarArrayInterface(); 41 buffer_iface_ = ppapi_->GetVarArrayBufferInterface(); 42 dict_iface_ = ppapi_->GetVarDictionaryInterface(); 43 var_iface_ = ppapi_->GetVarInterface(); 44 45 if (!messaging_iface_ || !array_iface_ || !buffer_iface_ || !dict_iface_ || 46 !var_iface_) { 47 LOG_ERROR("Got 1+ NULL interface(s): %s%s%s%s%s", 48 messaging_iface_ ? "" : "Messaging ", 49 array_iface_ ? "" : "VarArray ", 50 buffer_iface_ ? "" : "VarArrayBuffer ", 51 dict_iface_ ? "" : "VarDictionary ", 52 var_iface_ ? "" : "Var "); 53 return ENOSYS; 54 } 55 56 return 0; 57} 58 59void JsFs::Destroy() { 60 pthread_cond_destroy(&response_cond_); 61} 62 63bool JsFs::SetDictVar(PP_Var dict, const char* key, PP_Var value) { 64 PP_Var key_var = var_iface_->VarFromUtf8(key, strlen(key)); 65 ScopedVar scoped_key(ppapi_, key_var); 66 if (key_var.type != PP_VARTYPE_STRING) { 67 LOG_ERROR("Unable to create string key \"%s\".", key); 68 return false; 69 } 70 71 PP_Bool success = dict_iface_->Set(dict, key_var, value); 72 if (!success) { 73 LOG_ERROR("Unable to set \"%s\" key of dictionary.", key); 74 return false; 75 } 76 77 return true; 78} 79 80PP_Var JsFs::GetDictVar(PP_Var dict, const char* key) { 81 PP_Var key_var = var_iface_->VarFromUtf8(key, strlen(key)); 82 ScopedVar scoped_key(ppapi_, key_var); 83 if (key_var.type != PP_VARTYPE_STRING) { 84 LOG_ERROR("Unable to create string key \"%s\".", key); 85 return PP_MakeUndefined(); 86 } 87 88 return dict_iface_->Get(dict, key_var); 89} 90 91bool JsFs::GetVarInt32(PP_Var var, int32_t* out_value) { 92 switch (var.type) { 93 case PP_VARTYPE_INT32: 94 *out_value = var.value.as_int; 95 return true; 96 97 case PP_VARTYPE_DOUBLE: 98 *out_value = static_cast<int32_t>(var.value.as_double); 99 return true; 100 101 default: 102 return false; 103 } 104} 105 106bool JsFs::GetVarUint32(PP_Var var, uint32_t* out_value) { 107 switch (var.type) { 108 case PP_VARTYPE_INT32: 109 *out_value = static_cast<uint32_t>(var.value.as_int); 110 return true; 111 112 case PP_VARTYPE_DOUBLE: 113 *out_value = static_cast<uint32_t>(var.value.as_double); 114 return true; 115 116 default: 117 return false; 118 } 119} 120 121bool JsFs::GetVarInt64(PP_Var var, int64_t* out_value) { 122 switch (var.type) { 123 case PP_VARTYPE_INT32: 124 *out_value = var.value.as_int; 125 return true; 126 127 case PP_VARTYPE_DOUBLE: 128 *out_value = static_cast<int64_t>(var.value.as_double); 129 return true; 130 131 case PP_VARTYPE_ARRAY: { 132 uint32_t len = array_iface_->GetLength(var); 133 if (len != 2) { 134 LOG_ERROR("Expected int64 array type to have 2 elements, not %d", len); 135 return false; 136 } 137 138 PP_Var high_int_var = array_iface_->Get(var, 0); 139 ScopedVar scoped_high_int_var(ppapi_, high_int_var); 140 uint32_t high_int; 141 if (!GetVarUint32(high_int_var, &high_int)) 142 return false; 143 144 PP_Var low_int_var = array_iface_->Get(var, 1); 145 ScopedVar scoped_low_int_var(ppapi_, low_int_var); 146 uint32_t low_int; 147 if (!GetVarUint32(low_int_var, &low_int)) 148 return false; 149 150 *out_value = static_cast<int64_t>( 151 (static_cast<uint64_t>(high_int) << 32) | low_int); 152 return true; 153 } 154 155 default: 156 return false; 157 } 158} 159 160PP_Var JsFs::VMakeRequest(RequestId request_id, 161 const char* format, 162 va_list args) { 163 PP_Var dict = dict_iface_->Create(); 164 ScopedVar scoped_dict(ppapi_, dict); 165 166 if (!SetDictVar(dict, "id", PP_MakeInt32(request_id))) 167 return PP_MakeNull(); 168 169 const char* p = format; 170 while (*p) { 171 assert(*p == '%'); 172 ++p; 173 174 const char* key = va_arg(args, const char*); 175 PP_Var value_var = PP_MakeUndefined(); 176 177 switch(*p) { 178 case 'd': 179 value_var = PP_MakeInt32(va_arg(args, int32_t)); 180 break; 181 case 'u': 182 value_var = PP_MakeInt32(va_arg(args, uint32_t)); 183 break; 184 case 's': { 185 const char* value = va_arg(args, const char*); 186 value_var = var_iface_->VarFromUtf8(value, strlen(value)); 187 if (value_var.type != PP_VARTYPE_STRING) { 188 LOG_ERROR("Unable to create \"%s\" string var.", value); 189 return PP_MakeNull(); 190 } 191 break; 192 } 193 case 'p': 194 value_var = *va_arg(args, const PP_Var*); 195 var_iface_->AddRef(value_var); 196 break; 197 case 'l': { 198 // Only '%lld' is supported. 199 ++p; 200 assert(*p == 'l'); 201 ++p; 202 assert(*p == 'd'); 203 204 int64_t value = va_arg(args, int64_t); 205 if (value >= INT_MIN && value <= INT_MAX) { 206 // Send as an int. 207 value_var = PP_MakeInt32(static_cast<int32_t>(value)); 208 } else { 209 // Send as an array of two ints: [high int32, low int32]. 210 value_var = array_iface_->Create(); 211 if (!array_iface_->SetLength(value_var, 2)) { 212 LOG_ERROR("Unable to set length of s64 array."); 213 return PP_MakeNull(); 214 } 215 216 if (!array_iface_->Set(value_var, 0, PP_MakeInt32(value >> 32))) { 217 LOG_ERROR("Unable to set of high int32 of s64 array."); 218 return PP_MakeNull(); 219 } 220 221 if (!array_iface_->Set( 222 value_var, 1, PP_MakeInt32(value & 0xffffffff))) { 223 LOG_ERROR("Unable to set of low int32 of s64 array."); 224 return PP_MakeNull(); 225 } 226 } 227 228 break; 229 } 230 default: 231 LOG_ERROR("Unknown format specifier %%\"%s\"", p); 232 assert(0); 233 return PP_MakeNull(); 234 } 235 236 ++p; 237 238 if (!SetDictVar(dict, key, value_var)) 239 return PP_MakeNull(); 240 241 // Unconditionally release the value var. It is legal to do this even for 242 // non-refcounted types. 243 var_iface_->Release(value_var); 244 } 245 246 return scoped_dict.Release(); 247} 248 249JsFs::RequestId JsFs::VSendRequest(const char* format, va_list args) { 250 AUTO_LOCK(lock_); 251 RequestId id = ++request_id_; 252 // Skip 0 (the invalid request id) in the very unlikely case that the request 253 // id wraps. 254 if (id == 0) 255 id = ++request_id_; 256 257 PP_Var dict_var = VMakeRequest(id, format, args); 258 ScopedVar scoped_dict_var(ppapi_, dict_var); 259 if (dict_var.type != PP_VARTYPE_DICTIONARY) 260 return 0; 261 262 messaging_iface_->PostMessage(ppapi_->GetInstance(), dict_var); 263 return id; 264} 265 266bool JsFs::VSendRequestAndWait(ScopedVar* out_response, 267 const char* format, 268 va_list args) { 269 RequestId id = VSendRequest(format, args); 270 if (id == 0) 271 return false; 272 273 out_response->Reset(WaitForResponse(id)); 274 return true; 275} 276 277bool JsFs::SendRequestAndWait(ScopedVar* out_response, 278 const char* format, 279 ...) { 280 va_list args; 281 va_start(args, format); 282 bool result = VSendRequestAndWait(out_response, format, args); 283 va_end(args); 284 return result; 285} 286 287Error JsFs::ErrorFromResponse(const ScopedVar& response) { 288 int32_t error; 289 if (ScanVar(response.pp_var(), "%d", "error", &error) != 1) { 290 LOG_ERROR("Expected \"error\" field in response."); 291 return EINVAL; 292 } 293 294 return error; 295} 296 297int JsFs::ScanVar(PP_Var var, const char* format, ...) { 298 va_list args; 299 va_start(args, format); 300 int result = VScanVar(var, format, args); 301 va_end(args); 302 return result; 303} 304 305int JsFs::VScanVar(PP_Var dict_var, const char* format, va_list args) { 306 if (dict_var.type != PP_VARTYPE_DICTIONARY) { 307 LOG_ERROR("Expected var of type dictionary, not %d.", dict_var.type); 308 return 0; 309 } 310 311 int num_values = 0; 312 313 const char* p = format; 314 while (*p) { 315 assert(*p == '%'); 316 ++p; 317 318 const char* key = va_arg(args, const char*); 319 PP_Var value_var = GetDictVar(dict_var, key); 320 ScopedVar scoped_value_var(ppapi_, value_var); 321 322 if (value_var.type == PP_VARTYPE_UNDEFINED) 323 break; 324 325 bool ok = true; 326 327 switch (*p) { 328 case 'd': { 329 int32_t* value = va_arg(args, int32_t*); 330 if (!GetVarInt32(value_var, value)) { 331 LOG_ERROR("Expected int32_t value for key \"%s\"", key); 332 ok = false; 333 } 334 break; 335 } 336 case 'u': { 337 uint32_t* value = va_arg(args, uint32_t*); 338 if (!GetVarUint32(value_var, value)) { 339 LOG_ERROR("Expected uint32_t value for key \"%s\"", key); 340 ok = false; 341 } 342 break; 343 } 344 case 'l': { 345 // Only '%lld' is supported. 346 ++p; 347 assert(*p == 'l'); 348 ++p; 349 assert(*p == 'd'); 350 351 int64_t* value = va_arg(args, int64_t*); 352 if (!GetVarInt64(value_var, value)) { 353 LOG_ERROR("Expected int64_t value for key \"%s\"", key); 354 ok = false; 355 } 356 break; 357 } 358 case 'p': { 359 PP_Var* value = va_arg(args, PP_Var*); 360 *value = scoped_value_var.Release(); 361 break; 362 } 363 default: 364 LOG_ERROR("Unknown format specifier %%\"%s\"", p); 365 assert(0); 366 ok = false; 367 break; 368 } 369 370 if (!ok) 371 break; 372 373 p++; 374 num_values++; 375 } 376 377 return num_values; 378} 379 380PP_Var JsFs::WaitForResponse(RequestId request_id) { 381 AUTO_LOCK(lock_); 382 while (1) { 383 ResponseMap_t::iterator iter = responses_.find(request_id); 384 if (iter != responses_.end()) { 385 PP_Var response = iter->second; 386 responses_.erase(iter); 387 return response; 388 } 389 390 pthread_cond_wait(&response_cond_, lock_.mutex()); 391 } 392} 393 394Error JsFs::OpenWithMode(const Path& path, int open_flags, mode_t t, 395 ScopedNode* out_node) { 396 out_node->reset(NULL); 397 ScopedVar response(ppapi_); 398 if (!SendRequestAndWait(&response, "%s%s%d", 399 "cmd", "open", 400 "path", path.Join().c_str(), 401 "oflag", open_flags)) { 402 LOG_ERROR("Failed to send request."); 403 return EINVAL; 404 } 405 406 int32_t error; 407 int32_t fd; 408 int result = ScanVar(response.pp_var(), "%d%d", "error", &error, "fd", &fd); 409 if (result >= 1 && error) 410 return error; 411 412 if (result != 2) { 413 LOG_ERROR("Expected \"error\" and \"fd\" fields in response."); 414 return EINVAL; 415 } 416 417 out_node->reset(new JsFsNode(this, fd)); 418 return 0; 419} 420 421Error JsFs::Unlink(const Path& path) { 422 ScopedVar response(ppapi_); 423 if (!SendRequestAndWait( 424 &response, "%s%s", "cmd", "unlink", "path", path.Join().c_str())) { 425 LOG_ERROR("Failed to send request."); 426 return EINVAL; 427 } 428 429 return ErrorFromResponse(response); 430} 431 432Error JsFs::Mkdir(const Path& path, int perm) { 433 ScopedVar response(ppapi_); 434 if (!SendRequestAndWait(&response, "%s%s%d", 435 "cmd", "mkdir", 436 "path", path.Join().c_str(), 437 "mode", perm)) { 438 LOG_ERROR("Failed to send request."); 439 return EINVAL; 440 } 441 442 return ErrorFromResponse(response); 443} 444 445Error JsFs::Rmdir(const Path& path) { 446 ScopedVar response(ppapi_); 447 if (!SendRequestAndWait( 448 &response, "%s%s", "cmd", "rmdir", "path", path.Join().c_str())) { 449 LOG_ERROR("Failed to send request."); 450 return EINVAL; 451 } 452 453 return ErrorFromResponse(response); 454} 455 456Error JsFs::Remove(const Path& path) { 457 ScopedVar response(ppapi_); 458 if (!SendRequestAndWait( 459 &response, "%s%s", "cmd", "remove", "path", path.Join().c_str())) { 460 LOG_ERROR("Failed to send request."); 461 return EINVAL; 462 } 463 464 return ErrorFromResponse(response); 465} 466 467Error JsFs::Rename(const Path& path, const Path& newpath) { 468 ScopedVar response(ppapi_); 469 if (!SendRequestAndWait(&response, "%s%s%s", 470 "cmd", "rename", 471 "old", path.Join().c_str(), 472 "new", newpath.Join().c_str())) { 473 LOG_ERROR("Failed to send request."); 474 return EINVAL; 475 } 476 477 return ErrorFromResponse(response); 478} 479 480Error JsFs::Filesystem_VIoctl(int request, va_list args) { 481 if (request != NACL_IOC_HANDLEMESSAGE) { 482 LOG_ERROR("Unknown ioctl: %#x", request); 483 return EINVAL; 484 } 485 486 PP_Var response = *va_arg(args, PP_Var*); 487 488 AUTO_LOCK(lock_); 489 490 RequestId response_id; 491 if (ScanVar(response, "%d", "id", &response_id) != 1) { 492 LOG_TRACE("ioctl with no \"id\", ignoring.\n"); 493 return EINVAL; 494 } 495 496 responses_.insert(ResponseMap_t::value_type(response_id, response)); 497 pthread_cond_broadcast(&response_cond_); 498 return 0; 499} 500 501} // namespace nacl_io 502