Home | History | Annotate | Download | only in html5fs
      1 // Copyright 2013 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/html5fs/html5_fs_node.h"
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <ppapi/c/pp_completion_callback.h>
     10 #include <ppapi/c/pp_directory_entry.h>
     11 #include <ppapi/c/pp_errors.h>
     12 #include <ppapi/c/pp_file_info.h>
     13 #include <ppapi/c/ppb_file_io.h>
     14 #include <string.h>
     15 #include <vector>
     16 
     17 #include "nacl_io/filesystem.h"
     18 #include "nacl_io/getdents_helper.h"
     19 #include "nacl_io/kernel_handle.h"
     20 #include "nacl_io/osdirent.h"
     21 #include "nacl_io/pepper_interface.h"
     22 #include "sdk_util/auto_lock.h"
     23 
     24 namespace nacl_io {
     25 
     26 namespace {
     27 
     28 struct OutputBuffer {
     29   void* data;
     30   int element_count;
     31 };
     32 
     33 void* GetOutputBuffer(void* user_data, uint32_t count, uint32_t size) {
     34   OutputBuffer* output = static_cast<OutputBuffer*>(user_data);
     35   output->element_count = count;
     36   if (count) {
     37     output->data = malloc(count * size);
     38     if (!output->data)
     39       output->element_count = 0;
     40   } else {
     41     output->data = NULL;
     42   }
     43   return output->data;
     44 }
     45 
     46 int32_t OpenFlagsToPPAPIOpenFlags(int open_flags) {
     47   int32_t ppapi_flags = 0;
     48 
     49   switch (open_flags & 3) {
     50     default:
     51     case O_RDONLY:
     52       ppapi_flags = PP_FILEOPENFLAG_READ;
     53       break;
     54     case O_WRONLY:
     55       ppapi_flags = PP_FILEOPENFLAG_WRITE;
     56       break;
     57     case O_RDWR:
     58       ppapi_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE;
     59       break;
     60   }
     61 
     62   if (open_flags & O_CREAT)
     63     ppapi_flags |= PP_FILEOPENFLAG_CREATE;
     64   if (open_flags & O_TRUNC)
     65     ppapi_flags |= PP_FILEOPENFLAG_TRUNCATE;
     66   if (open_flags & O_EXCL)
     67     ppapi_flags |= PP_FILEOPENFLAG_EXCLUSIVE;
     68 
     69   return ppapi_flags;
     70 }
     71 
     72 }  // namespace
     73 
     74 Error Html5FsNode::FSync() {
     75   // Cannot call Flush on a directory; simply do nothing.
     76   if (IsaDir())
     77     return 0;
     78 
     79   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Flush(
     80       fileio_resource_, PP_BlockUntilComplete());
     81   if (result != PP_OK)
     82     return PPErrorToErrno(result);
     83   return 0;
     84 }
     85 
     86 Error Html5FsNode::GetDents(size_t offs,
     87                             struct dirent* pdir,
     88                             size_t size,
     89                             int* out_bytes) {
     90   *out_bytes = 0;
     91 
     92   // If this is not a directory, fail
     93   if (!IsaDir())
     94     return ENOTDIR;
     95 
     96   // TODO(binji): Better handling of ino numbers.
     97   const ino_t kCurDirIno = -1;
     98   const ino_t kParentDirIno = -2;
     99   GetDentsHelper helper(kCurDirIno, kParentDirIno);
    100 
    101   OutputBuffer output_buf = {NULL, 0};
    102   PP_ArrayOutput output = {&GetOutputBuffer, &output_buf};
    103   int32_t result =
    104       filesystem_->ppapi()->GetFileRefInterface()->ReadDirectoryEntries(
    105           fileref_resource_, output, PP_BlockUntilComplete());
    106   if (result != PP_OK)
    107     return PPErrorToErrno(result);
    108 
    109   PP_DirectoryEntry* entries = static_cast<PP_DirectoryEntry*>(output_buf.data);
    110 
    111   for (int i = 0; i < output_buf.element_count; ++i) {
    112     PP_Var file_name_var = filesystem_->ppapi()->GetFileRefInterface()->GetName(
    113         entries[i].file_ref);
    114 
    115     // Release the file reference.
    116     filesystem_->ppapi()->ReleaseResource(entries[i].file_ref);
    117 
    118     if (file_name_var.type != PP_VARTYPE_STRING)
    119       continue;
    120 
    121     uint32_t file_name_length;
    122     const char* file_name = filesystem_->ppapi()->GetVarInterface()->VarToUtf8(
    123         file_name_var, &file_name_length);
    124 
    125     if (file_name) {
    126       file_name_length =
    127           std::min(static_cast<size_t>(file_name_length),
    128                    MEMBER_SIZE(dirent, d_name) - 1);  // -1 for NULL.
    129 
    130       // TODO(binji): Better handling of ino numbers.
    131       helper.AddDirent(1, file_name, file_name_length);
    132     }
    133 
    134     filesystem_->ppapi()->GetVarInterface()->Release(file_name_var);
    135   }
    136 
    137   // Release the output buffer.
    138   free(output_buf.data);
    139 
    140   return helper.GetDents(offs, pdir, size, out_bytes);
    141 }
    142 
    143 Error Html5FsNode::GetStat(struct stat* stat) {
    144   AUTO_LOCK(node_lock_);
    145 
    146   PP_FileInfo info;
    147   int32_t result = filesystem_->ppapi()->GetFileRefInterface()->Query(
    148       fileref_resource_, &info, PP_BlockUntilComplete());
    149   if (result != PP_OK)
    150     return PPErrorToErrno(result);
    151 
    152   // Fill in known info here.
    153   memcpy(stat, &stat_, sizeof(stat_));
    154 
    155   // Fill in the additional info from ppapi.
    156   switch (info.type) {
    157     case PP_FILETYPE_REGULAR:
    158       stat->st_mode |= S_IFREG;
    159       break;
    160     case PP_FILETYPE_DIRECTORY:
    161       stat->st_mode |= S_IFDIR;
    162       break;
    163     case PP_FILETYPE_OTHER:
    164     default:
    165       break;
    166   }
    167   stat->st_size = static_cast<off_t>(info.size);
    168   stat->st_atime = info.last_access_time;
    169   stat->st_mtime = info.last_modified_time;
    170   stat->st_ctime = info.creation_time;
    171 
    172   return 0;
    173 }
    174 
    175 Error Html5FsNode::Read(const HandleAttr& attr,
    176                         void* buf,
    177                         size_t count,
    178                         int* out_bytes) {
    179   *out_bytes = 0;
    180 
    181   if (IsaDir())
    182     return EISDIR;
    183 
    184   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Read(
    185       fileio_resource_,
    186       attr.offs,
    187       static_cast<char*>(buf),
    188       static_cast<int32_t>(count),
    189       PP_BlockUntilComplete());
    190   if (result < 0)
    191     return PPErrorToErrno(result);
    192 
    193   *out_bytes = result;
    194   return 0;
    195 }
    196 
    197 Error Html5FsNode::FTruncate(off_t size) {
    198   if (IsaDir())
    199     return EISDIR;
    200 
    201   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->SetLength(
    202       fileio_resource_, size, PP_BlockUntilComplete());
    203   if (result != PP_OK)
    204     return PPErrorToErrno(result);
    205   return 0;
    206 }
    207 
    208 Error Html5FsNode::Write(const HandleAttr& attr,
    209                          const void* buf,
    210                          size_t count,
    211                          int* out_bytes) {
    212   *out_bytes = 0;
    213 
    214   if (IsaDir())
    215     return EISDIR;
    216 
    217   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Write(
    218       fileio_resource_,
    219       attr.offs,
    220       static_cast<const char*>(buf),
    221       static_cast<int32_t>(count),
    222       PP_BlockUntilComplete());
    223   if (result < 0)
    224     return PPErrorToErrno(result);
    225 
    226   *out_bytes = result;
    227   return 0;
    228 }
    229 
    230 int Html5FsNode::GetType() {
    231   return fileio_resource_ ? S_IFREG : S_IFDIR;
    232 }
    233 
    234 Error Html5FsNode::GetSize(off_t* out_size) {
    235   *out_size = 0;
    236 
    237   if (IsaDir())
    238     return 0;
    239 
    240   AUTO_LOCK(node_lock_);
    241 
    242   PP_FileInfo info;
    243   int32_t result = filesystem_->ppapi()->GetFileIoInterface()->Query(
    244       fileio_resource_, &info, PP_BlockUntilComplete());
    245   if (result != PP_OK)
    246     return PPErrorToErrno(result);
    247 
    248   *out_size = info.size;
    249   return 0;
    250 }
    251 
    252 Html5FsNode::Html5FsNode(Filesystem* filesystem, PP_Resource fileref_resource)
    253     : Node(filesystem),
    254       fileref_resource_(fileref_resource),
    255       fileio_resource_(0) {
    256 }
    257 
    258 Error Html5FsNode::Init(int open_flags) {
    259   Error error = Node::Init(open_flags);
    260   if (error)
    261     return error;
    262 
    263   // First query the FileRef to see if it is a file or directory.
    264   PP_FileInfo file_info;
    265   int32_t query_result = filesystem_->ppapi()->GetFileRefInterface()->Query(
    266       fileref_resource_, &file_info, PP_BlockUntilComplete());
    267   // If this is a directory, do not get a FileIO.
    268   if (query_result == PP_OK && file_info.type == PP_FILETYPE_DIRECTORY)
    269     return 0;
    270 
    271   FileIoInterface* file_io = filesystem_->ppapi()->GetFileIoInterface();
    272   fileio_resource_ = file_io->Create(filesystem_->ppapi()->GetInstance());
    273   if (!fileio_resource_)
    274     return ENOSYS;
    275 
    276   int32_t open_result = file_io->Open(fileio_resource_,
    277                                       fileref_resource_,
    278                                       OpenFlagsToPPAPIOpenFlags(open_flags),
    279                                       PP_BlockUntilComplete());
    280   if (open_result != PP_OK)
    281     return PPErrorToErrno(open_result);
    282   return 0;
    283 }
    284 
    285 void Html5FsNode::Destroy() {
    286   FSync();
    287 
    288   if (fileio_resource_) {
    289     filesystem_->ppapi()->GetFileIoInterface()->Close(fileio_resource_);
    290     filesystem_->ppapi()->ReleaseResource(fileio_resource_);
    291   }
    292 
    293   filesystem_->ppapi()->ReleaseResource(fileref_resource_);
    294   fileio_resource_ = 0;
    295   fileref_resource_ = 0;
    296   Node::Destroy();
    297 }
    298 
    299 }  // namespace nacl_io
    300