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