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