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.h"
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 
     12 #include <algorithm>
     13 
     14 #include <ppapi/c/pp_completion_callback.h>
     15 #include <ppapi/c/pp_errors.h>
     16 
     17 #include "nacl_io/html5fs/html5_fs_node.h"
     18 #include "sdk_util/auto_lock.h"
     19 
     20 namespace nacl_io {
     21 
     22 namespace {
     23 
     24 #if defined(WIN32)
     25 int64_t strtoull(const char* nptr, char** endptr, int base) {
     26   return _strtoui64(nptr, endptr, base);
     27 }
     28 #endif
     29 
     30 }  // namespace
     31 
     32 Error Html5Fs::Access(const Path& path, int a_mode) {
     33   // a_mode is unused, since all files are readable, writable and executable.
     34   ScopedNode node;
     35   return Open(path, O_RDONLY, &node);
     36 }
     37 
     38 Error Html5Fs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
     39   out_node->reset(NULL);
     40   Error error = BlockUntilFilesystemOpen();
     41   if (error)
     42     return error;
     43 
     44   PP_Resource fileref = ppapi()->GetFileRefInterface()->Create(
     45       filesystem_resource_, GetFullPath(path).Join().c_str());
     46   if (!fileref)
     47     return ENOENT;
     48 
     49   ScopedNode node(new Html5FsNode(this, fileref));
     50   error = node->Init(open_flags);
     51   if (error)
     52     return error;
     53 
     54   *out_node = node;
     55   return 0;
     56 }
     57 
     58 Path Html5Fs::GetFullPath(const Path& path) {
     59   Path full_path(path);
     60   full_path.Prepend(prefix_);
     61   return full_path;
     62 }
     63 
     64 Error Html5Fs::Unlink(const Path& path) {
     65   return RemoveInternal(path, REMOVE_FILE);
     66 }
     67 
     68 Error Html5Fs::Mkdir(const Path& path, int permissions) {
     69   Error error = BlockUntilFilesystemOpen();
     70   if (error)
     71     return error;
     72 
     73   // FileRef returns PP_ERROR_NOACCESS which is translated to EACCES if you
     74   // try to create the root directory. EEXIST is a better errno here.
     75   if (path.IsRoot())
     76     return EEXIST;
     77 
     78   ScopedResource fileref_resource(
     79       ppapi(),
     80       ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
     81                                              GetFullPath(path).Join().c_str()));
     82   if (!fileref_resource.pp_resource())
     83     return ENOENT;
     84 
     85   int32_t result = ppapi()->GetFileRefInterface()->MakeDirectory(
     86       fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete());
     87   if (result != PP_OK)
     88     return PPErrorToErrno(result);
     89 
     90   return 0;
     91 }
     92 
     93 Error Html5Fs::Rmdir(const Path& path) {
     94   return RemoveInternal(path, REMOVE_DIR);
     95 }
     96 
     97 Error Html5Fs::Remove(const Path& path) {
     98   return RemoveInternal(path, REMOVE_ALL);
     99 }
    100 
    101 Error Html5Fs::RemoveInternal(const Path& path, int remove_type) {
    102   Error error = BlockUntilFilesystemOpen();
    103   if (error)
    104     return error;
    105 
    106   ScopedResource fileref_resource(
    107       ppapi(),
    108       ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
    109                                              GetFullPath(path).Join().c_str()));
    110   if (!fileref_resource.pp_resource())
    111     return ENOENT;
    112 
    113   // Check file type
    114   if (remove_type != REMOVE_ALL) {
    115     PP_FileInfo file_info;
    116     int32_t query_result = ppapi()->GetFileRefInterface()->Query(
    117         fileref_resource.pp_resource(), &file_info, PP_BlockUntilComplete());
    118     if (query_result != PP_OK) {
    119       LOG_ERROR("Error querying file type");
    120       return EINVAL;
    121     }
    122     switch (file_info.type) {
    123       case PP_FILETYPE_DIRECTORY:
    124         if (!(remove_type & REMOVE_DIR))
    125           return EISDIR;
    126         break;
    127       case PP_FILETYPE_REGULAR:
    128         if (!(remove_type & REMOVE_FILE))
    129           return ENOTDIR;
    130         break;
    131       default:
    132         LOG_ERROR("Invalid file type: %d", file_info.type);
    133         return EINVAL;
    134     }
    135   }
    136 
    137   int32_t result = ppapi()->GetFileRefInterface()->Delete(
    138       fileref_resource.pp_resource(), PP_BlockUntilComplete());
    139   if (result != PP_OK)
    140     return PPErrorToErrno(result);
    141 
    142   return 0;
    143 }
    144 
    145 Error Html5Fs::Rename(const Path& path, const Path& newpath) {
    146   Error error = BlockUntilFilesystemOpen();
    147   if (error)
    148     return error;
    149 
    150   const char* oldpath_full = GetFullPath(path).Join().c_str();
    151   ScopedResource fileref_resource(
    152       ppapi(),
    153       ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
    154                                              oldpath_full));
    155   if (!fileref_resource.pp_resource())
    156     return ENOENT;
    157 
    158   const char* newpath_full = GetFullPath(newpath).Join().c_str();
    159   ScopedResource new_fileref_resource(
    160       ppapi(),
    161       ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
    162                                              newpath_full));
    163   if (!new_fileref_resource.pp_resource())
    164     return ENOENT;
    165 
    166   int32_t result =
    167       ppapi()->GetFileRefInterface()->Rename(fileref_resource.pp_resource(),
    168                                              new_fileref_resource.pp_resource(),
    169                                              PP_BlockUntilComplete());
    170   if (result != PP_OK)
    171     return PPErrorToErrno(result);
    172 
    173   return 0;
    174 }
    175 
    176 Html5Fs::Html5Fs()
    177     : filesystem_resource_(0),
    178       filesystem_open_has_result_(false),
    179       filesystem_open_error_(0) {
    180 }
    181 
    182 Error Html5Fs::Init(const FsInitArgs& args) {
    183   Error error = Filesystem::Init(args);
    184   if (error)
    185     return error;
    186 
    187   if (!args.ppapi)
    188     return ENOSYS;
    189 
    190   pthread_cond_init(&filesystem_open_cond_, NULL);
    191 
    192   // Parse filesystem args.
    193   PP_FileSystemType filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
    194   int64_t expected_size = 0;
    195   for (StringMap_t::const_iterator iter = args.string_map.begin();
    196        iter != args.string_map.end();
    197        ++iter) {
    198     if (iter->first == "type") {
    199       if (iter->second == "PERSISTENT") {
    200         filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
    201       } else if (iter->second == "TEMPORARY") {
    202         filesystem_type = PP_FILESYSTEMTYPE_LOCALTEMPORARY;
    203       } else if (iter->second == "") {
    204         filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
    205       } else {
    206         LOG_ERROR("html5fs: unknown type: '%s'", iter->second.c_str());
    207         return EINVAL;
    208       }
    209     } else if (iter->first == "expected_size") {
    210       expected_size = strtoull(iter->second.c_str(), NULL, 10);
    211     } else if (iter->first == "filesystem_resource") {
    212       PP_Resource resource = strtoull(iter->second.c_str(), NULL, 10);
    213       if (!ppapi_->GetFileSystemInterface()->IsFileSystem(resource))
    214         return EINVAL;
    215 
    216       filesystem_resource_ = resource;
    217       ppapi_->AddRefResource(filesystem_resource_);
    218     } else if (iter->first == "SOURCE") {
    219       prefix_ = iter->second;
    220     } else {
    221       LOG_ERROR("html5fs: bad param: %s", iter->first.c_str());
    222       return EINVAL;
    223     }
    224   }
    225 
    226   if (filesystem_resource_ != 0) {
    227     filesystem_open_has_result_ = true;
    228     filesystem_open_error_ = PP_OK;
    229     return 0;
    230   }
    231 
    232   // Initialize filesystem.
    233   filesystem_resource_ = ppapi_->GetFileSystemInterface()->Create(
    234       ppapi_->GetInstance(), filesystem_type);
    235   if (filesystem_resource_ == 0)
    236     return ENOSYS;
    237 
    238   // We can't block the main thread, so make an asynchronous call if on main
    239   // thread. If we are off-main-thread, then don't make an asynchronous call;
    240   // otherwise we require a message loop.
    241   bool main_thread = ppapi_->GetCoreInterface()->IsMainThread();
    242   PP_CompletionCallback cc =
    243       main_thread ? PP_MakeCompletionCallback(
    244                         &Html5Fs::FilesystemOpenCallbackThunk, this)
    245                   : PP_BlockUntilComplete();
    246 
    247   int32_t result = ppapi_->GetFileSystemInterface()->Open(
    248       filesystem_resource_, expected_size, cc);
    249 
    250   if (!main_thread) {
    251     filesystem_open_has_result_ = true;
    252     filesystem_open_error_ = PPErrorToErrno(result);
    253 
    254     return filesystem_open_error_;
    255   }
    256 
    257   // We have to assume the call to Open will succeed; there is no better
    258   // result to return here.
    259   return 0;
    260 }
    261 
    262 void Html5Fs::Destroy() {
    263   ppapi_->ReleaseResource(filesystem_resource_);
    264   pthread_cond_destroy(&filesystem_open_cond_);
    265 }
    266 
    267 Error Html5Fs::BlockUntilFilesystemOpen() {
    268   AUTO_LOCK(filesysem_open_lock_);
    269   while (!filesystem_open_has_result_) {
    270     pthread_cond_wait(&filesystem_open_cond_, filesysem_open_lock_.mutex());
    271   }
    272   return filesystem_open_error_;
    273 }
    274 
    275 // static
    276 void Html5Fs::FilesystemOpenCallbackThunk(void* user_data, int32_t result) {
    277   Html5Fs* self = static_cast<Html5Fs*>(user_data);
    278   self->FilesystemOpenCallback(result);
    279 }
    280 
    281 void Html5Fs::FilesystemOpenCallback(int32_t result) {
    282   AUTO_LOCK(filesysem_open_lock_);
    283   filesystem_open_has_result_ = true;
    284   filesystem_open_error_ = PPErrorToErrno(result);
    285   pthread_cond_signal(&filesystem_open_cond_);
    286 }
    287 
    288 }  // namespace nacl_io
    289