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