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_node.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/jsfs/js_fs.h"
     14 #include "nacl_io/kernel_handle.h"
     15 #include "nacl_io/log.h"
     16 #include "nacl_io/osdirent.h"
     17 #include "nacl_io/pepper_interface.h"
     18 #include "sdk_util/macros.h"
     19 
     20 namespace nacl_io {
     21 
     22 JsFsNode::JsFsNode(Filesystem* filesystem, int32_t fd)
     23     : Node(filesystem),
     24       ppapi_(filesystem->ppapi()),
     25       array_iface_(ppapi_->GetVarArrayInterface()),
     26       buffer_iface_(ppapi_->GetVarArrayBufferInterface()),
     27       var_iface_(ppapi_->GetVarInterface()),
     28       fd_(fd) {
     29 }
     30 
     31 void JsFsNode::Destroy() {
     32   // TODO(binji): implement
     33 }
     34 
     35 bool JsFsNode::SendRequestAndWait(ScopedVar* out_response,
     36                                   const char* format,
     37                                   ...) {
     38   va_list args;
     39   va_start(args, format);
     40   bool result = filesystem()->VSendRequestAndWait(out_response, format, args);
     41   va_end(args);
     42   return result;
     43 }
     44 
     45 int JsFsNode::ScanVar(PP_Var var, const char* format, ...) {
     46   va_list args;
     47   va_start(args, format);
     48   int result = filesystem()->VScanVar(var, format, args);
     49   va_end(args);
     50   return result;
     51 }
     52 
     53 bool JsFsNode::CanOpen(int open_flags) {
     54   struct stat statbuf;
     55   Error error = GetStat(&statbuf);
     56   if (error)
     57     return false;
     58 
     59   // GetStat cached the mode in stat_.st_mode. Forward to Node::CanOpen,
     60   // which will check this mode against open_flags.
     61   return Node::CanOpen(open_flags);
     62 }
     63 
     64 Error JsFsNode::GetStat(struct stat* stat) {
     65   AUTO_LOCK(node_lock_);
     66 
     67   ScopedVar response(ppapi_);
     68   if (!SendRequestAndWait(&response, "%s%d", "cmd", "fstat", "fildes", fd_)) {
     69     LOG_ERROR("Failed to send request.");
     70     return EINVAL;
     71   }
     72 
     73   // TODO(binji): find out the size of bionic stat fields.
     74 #if defined(__native_client__) && !defined(__BIONIC__)
     75 #if defined(__GLIBC__)
     76 const char* format = "%d%lld%d%d%d%d%lld%lld%lld%lld%lld%lld%lld";
     77 #else
     78 const char* format = "%d%lld%d%d%d%d%lld%lld%d%d%lld%lld%lld";
     79 #endif
     80 #else
     81 #define FIELD(x)                              \
     82   assert(sizeof(stat->x) >= sizeof(int32_t)); \
     83   strcat(format, sizeof(stat->x) == sizeof(int64_t) ? "%lld" : "%d");
     84 
     85   // For host builds, we'll build up the format string at runtime.
     86   char format[100] = "%d";  // First field is "error".
     87   FIELD(st_ino);
     88   FIELD(st_mode);
     89   FIELD(st_nlink);
     90   FIELD(st_uid);
     91   FIELD(st_gid);
     92   FIELD(st_rdev);
     93   FIELD(st_size);
     94   FIELD(st_blksize);
     95   FIELD(st_blocks);
     96   FIELD(st_atime);
     97   FIELD(st_mtime);
     98   FIELD(st_ctime);
     99 
    100 #undef FIELD
    101 #endif
    102 
    103   int32_t error;
    104   int result = ScanVar(response.pp_var(),
    105                        format,
    106                        "error", &error,
    107                        "st_ino", &stat->st_ino,
    108                        "st_mode", &stat->st_mode,
    109                        "st_nlink", &stat->st_nlink,
    110                        "st_uid", &stat->st_uid,
    111                        "st_gid", &stat->st_gid,
    112                        "st_rdev", &stat->st_rdev,
    113                        "st_size", &stat->st_size,
    114                        "st_blksize", &stat->st_blksize,
    115                        "st_blocks", &stat->st_blocks,
    116                        "st_atime", &stat->st_atime,
    117                        "st_mtime", &stat->st_mtime,
    118                        "st_ctime", &stat->st_ctime);
    119 
    120   if (result >= 1 && error)
    121     return error;
    122 
    123   if (result != 13) {
    124     LOG_ERROR(
    125         "Expected \"st_*\" and \"error\" fields in response (should be 13 "
    126         "total).");
    127     return EINVAL;
    128   }
    129 
    130   stat->st_dev = filesystem()->dev();
    131 
    132   return 0;
    133 }
    134 
    135 Error JsFsNode::GetSize(off_t* out_size) {
    136   *out_size = 0;
    137 
    138   struct stat statbuf;
    139   Error error = GetStat(&statbuf);
    140   if (error)
    141     return error;
    142 
    143   *out_size = stat_.st_size;
    144   return 0;
    145 }
    146 
    147 Error JsFsNode::FSync() {
    148   AUTO_LOCK(node_lock_);
    149 
    150   ScopedVar response(ppapi_);
    151   if (!SendRequestAndWait(&response, "%s%d", "cmd", "fsync", "fildes", fd_)) {
    152     LOG_ERROR("Failed to send request.");
    153     return EINVAL;
    154   }
    155 
    156   return filesystem()->ErrorFromResponse(response);
    157 }
    158 
    159 Error JsFsNode::FTruncate(off_t length) {
    160   AUTO_LOCK(node_lock_);
    161 
    162   ScopedVar response(ppapi_);
    163   if (!SendRequestAndWait(&response,
    164       "%s%d%lld", "cmd", "ftruncate", "fildes", fd_, "length", length)) {
    165     LOG_ERROR("Failed to send request.");
    166     return EINVAL;
    167   }
    168 
    169   return filesystem()->ErrorFromResponse(response);
    170 }
    171 
    172 Error JsFsNode::Read(const HandleAttr& attr,
    173                      void* buf,
    174                      size_t count,
    175                      int* out_bytes) {
    176   AUTO_LOCK(node_lock_);
    177 
    178   *out_bytes = 0;
    179 
    180   ScopedVar response(ppapi_);
    181   if (!SendRequestAndWait(&response, "%s%d%u%lld",
    182                           "cmd", "pread",
    183                           "fildes", fd_,
    184                           "nbyte", count,
    185                           "offset", attr.offs)) {
    186     LOG_ERROR("Failed to send request.");
    187     return EINVAL;
    188   }
    189 
    190   int32_t error;
    191 
    192   PP_Var buf_var;
    193   int result =
    194       ScanVar(response.pp_var(), "%d%p", "error", &error, "buf", &buf_var);
    195   ScopedVar scoped_buf_var(ppapi_, buf_var);
    196 
    197   if (result >= 1 && error)
    198     return error;
    199 
    200   if (result != 2) {
    201     LOG_ERROR("Expected \"error\" and \"buf\" fields in response.");
    202     return EINVAL;
    203   }
    204 
    205   if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) {
    206     LOG_ERROR("Expected \"buf\" to be an ArrayBuffer.");
    207     return EINVAL;
    208   }
    209 
    210   uint32_t src_buf_len;
    211   if (!buffer_iface_->ByteLength(buf_var, &src_buf_len)) {
    212     LOG_ERROR("Unable to get byteLength of \"buf\".");
    213     return EINVAL;
    214   }
    215 
    216   if (src_buf_len > count)
    217     src_buf_len = count;
    218 
    219   void* src_buf = buffer_iface_->Map(buf_var);
    220   if (src_buf == NULL) {
    221     LOG_ERROR("Unable to map \"buf\".");
    222     return EINVAL;
    223   }
    224 
    225   memcpy(buf, src_buf, src_buf_len);
    226   *out_bytes = src_buf_len;
    227 
    228   buffer_iface_->Unmap(buf_var);
    229 
    230   return 0;
    231 }
    232 
    233 Error JsFsNode::Write(const HandleAttr& attr,
    234                       const void* buf,
    235                       size_t count,
    236                       int* out_bytes) {
    237   AUTO_LOCK(node_lock_);
    238 
    239   *out_bytes = 0;
    240 
    241   PP_Var buf_var = buffer_iface_->Create(count);
    242   ScopedVar scoped_buf_var(ppapi_, buf_var);
    243 
    244   if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) {
    245     LOG_ERROR("Unable to create \"buf\" var.");
    246     return EINVAL;
    247   }
    248 
    249   void* dst_buf = buffer_iface_->Map(buf_var);
    250   if (dst_buf == NULL) {
    251     LOG_ERROR("Unable to map \"buf\".");
    252     return EINVAL;
    253   }
    254 
    255   memcpy(dst_buf, buf, count);
    256 
    257   buffer_iface_->Unmap(buf_var);
    258 
    259   ScopedVar response(ppapi_);
    260   if (!SendRequestAndWait(&response, "%s%d%p%u%lld",
    261                           "cmd", "pwrite",
    262                           "fildes", fd_,
    263                           "buf", &buf_var,
    264                           "nbyte", count,
    265                           "offset", attr.offs)) {
    266     LOG_ERROR("Failed to send request.");
    267     return EINVAL;
    268   }
    269 
    270   int error;
    271   uint32_t nwrote;
    272   int result =
    273       ScanVar(response.pp_var(), "%d%u", "error", &error, "nwrote", &nwrote);
    274 
    275   if (result >= 1 && error)
    276     return error;
    277 
    278   if (result != 2) {
    279     LOG_ERROR("Expected \"error\" and \"nwrote\" fields in response.");
    280     return EINVAL;
    281   }
    282 
    283   *out_bytes = nwrote;
    284   return 0;
    285 }
    286 
    287 Error JsFsNode::GetDents(size_t offs,
    288                          struct dirent* pdir,
    289                          size_t count,
    290                          int* out_bytes) {
    291   AUTO_LOCK(node_lock_);
    292 
    293   *out_bytes = 0;
    294 
    295   // Round to the nearest sizeof(dirent) and ask for that.
    296   size_t first = offs / sizeof(dirent);
    297   size_t last = (offs + count + sizeof(dirent) - 1) / sizeof(dirent);
    298 
    299   ScopedVar response(ppapi_);
    300   if (!SendRequestAndWait(&response, "%s%d%u%u",
    301                           "cmd", "getdents",
    302                           "fildes", fd_,
    303                           "offs", first,
    304                           "count", last - first)) {
    305     LOG_ERROR("Failed to send request.");
    306     return EINVAL;
    307   }
    308 
    309   int error;
    310   PP_Var dirents_var;
    311   int result = ScanVar(
    312       response.pp_var(), "%d%p", "error", &error, "dirents", &dirents_var);
    313 
    314   ScopedVar scoped_dirents_var(ppapi_, dirents_var);
    315 
    316   if (result >= 1 && error)
    317     return error;
    318 
    319   if (result != 2) {
    320     LOG_ERROR("Expected \"error\" and \"dirents\" fields in response.");
    321     return EINVAL;
    322   }
    323 
    324   if (dirents_var.type != PP_VARTYPE_ARRAY) {
    325     LOG_ERROR("Expected \"dirents\" to be an Array.");
    326     return EINVAL;
    327   }
    328 
    329   uint32_t dirents_len = array_iface_->GetLength(dirents_var);
    330   uint32_t dirents_byte_len = dirents_len * sizeof(dirent);
    331 
    332   // Allocate enough full dirents to copy from. This makes it easier if, for
    333   // some reason, we are reading unaligned dirents.
    334   dirent* dirents = static_cast<dirent*>(malloc(dirents_byte_len));
    335 
    336   for (uint32_t i = 0; i < dirents_len; ++i) {
    337     PP_Var dirent_var = array_iface_->Get(dirents_var, i);
    338     PP_Var d_name_var;
    339     result = ScanVar(dirent_var,
    340                      "%lld%p",
    341                      "d_ino", &dirents[i].d_ino,
    342                      "d_name", &d_name_var);
    343     ScopedVar scoped_dirent_var(ppapi_, dirent_var);
    344     ScopedVar scoped_d_name_var(ppapi_, d_name_var);
    345 
    346     if (result != 2) {
    347       LOG_ERROR("Expected dirent[%d] to have \"d_ino\" and \"d_name\".", i);
    348       free(dirents);
    349       return EINVAL;
    350     }
    351 
    352     uint32_t d_name_len;
    353     const char* d_name = var_iface_->VarToUtf8(d_name_var, &d_name_len);
    354 
    355     dirents[i].d_off = sizeof(dirent);
    356     dirents[i].d_reclen = sizeof(dirent);
    357     strncpy(dirents[i].d_name, d_name, sizeof(dirents[i].d_name));
    358   }
    359 
    360   size_t dirents_offs = offs - first * sizeof(dirent);
    361   if (dirents_offs + count > dirents_byte_len)
    362     count = dirents_byte_len - dirents_offs;
    363 
    364   memcpy(pdir, reinterpret_cast<const char*>(dirents) + dirents_offs, count);
    365   *out_bytes = count;
    366 
    367   free(dirents);
    368   return 0;
    369 }
    370 
    371 }  // namespace nacl_io
    372