Home | History | Annotate | Download | only in jsfs
      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