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 // Continuing DJB2a hash
     33 ino_t Html5Fs::HashPathSegment(ino_t hash, const char *str, size_t len) {
     34   // First add the path seperator
     35   hash = (hash * static_cast<ino_t>(33)) ^ '/';
     36   while (len--) {
     37     hash = (hash * static_cast<ino_t>(33)) ^ *str++;
     38   }
     39   return hash;
     40 }
     41 
     42 ino_t Html5Fs::HashPath(const Path& path) {
     43   // Prime the DJB2a hash
     44   ino_t hash = 5381;
     45 
     46   // Apply a running DJB2a to each part of the path
     47   for (size_t segment = 0; segment < path.Size(); segment++) {
     48     const char *ptr = path.Part(segment).c_str();
     49     size_t len = path.Part(segment).length();
     50     hash = HashPathSegment(hash, ptr, len);
     51   }
     52   return hash;
     53 }
     54 
     55 
     56 // For HTML5, the INO should be the one used by the system, however PPAPI
     57 // does not provide access to the real INO.  Instead, since HTML5 does not
     58 // suport links, we assume that files are unique based on path to the base
     59 // of the mount.
     60 void Html5Fs::OnNodeCreated(Node* node) {
     61   node->stat_.st_dev = dev_;
     62 }
     63 
     64 void Html5Fs::OnNodeDestroyed(Node* node) {}
     65 
     66 
     67 Error Html5Fs::OpenWithMode(const Path& path, int open_flags, mode_t mode,
     68                             ScopedNode* out_node) {
     69   out_node->reset(NULL);
     70   Error error = BlockUntilFilesystemOpen();
     71   if (error)
     72     return error;
     73 
     74   PP_Resource fileref = file_ref_iface_->Create(
     75       filesystem_resource_, GetFullPath(path).Join().c_str());
     76   if (!fileref)
     77     return ENOENT;
     78 
     79   ScopedNode node(new Html5FsNode(this, fileref));
     80   error = node->Init(open_flags);
     81 
     82   // Set the INO based on the path
     83   node->stat_.st_ino = HashPath(path);
     84 
     85   if (error)
     86     return error;
     87 
     88   *out_node = node;
     89   return 0;
     90 }
     91 
     92 Path Html5Fs::GetFullPath(const Path& path) {
     93   Path full_path(path);
     94   full_path.Prepend(prefix_);
     95   return full_path;
     96 }
     97 
     98 Error Html5Fs::Unlink(const Path& path) {
     99   return RemoveInternal(path, REMOVE_FILE);
    100 }
    101 
    102 Error Html5Fs::Mkdir(const Path& path, int permissions) {
    103   Error error = BlockUntilFilesystemOpen();
    104   if (error)
    105     return error;
    106 
    107   // FileRef returns PP_ERROR_NOACCESS which is translated to EACCES if you
    108   // try to create the root directory. EEXIST is a better errno here.
    109   if (path.IsRoot())
    110     return EEXIST;
    111 
    112   ScopedResource fileref_resource(
    113       ppapi(),
    114       file_ref_iface_->Create(filesystem_resource_,
    115                               GetFullPath(path).Join().c_str()));
    116   if (!fileref_resource.pp_resource())
    117     return ENOENT;
    118 
    119   int32_t result = file_ref_iface_->MakeDirectory(
    120       fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete());
    121   if (result != PP_OK)
    122     return PPErrorToErrno(result);
    123 
    124   return 0;
    125 }
    126 
    127 Error Html5Fs::Rmdir(const Path& path) {
    128   return RemoveInternal(path, REMOVE_DIR);
    129 }
    130 
    131 Error Html5Fs::Remove(const Path& path) {
    132   return RemoveInternal(path, REMOVE_ALL);
    133 }
    134 
    135 Error Html5Fs::RemoveInternal(const Path& path, int remove_type) {
    136   Error error = BlockUntilFilesystemOpen();
    137   if (error)
    138     return error;
    139 
    140   ScopedResource fileref_resource(
    141       ppapi(),
    142       file_ref_iface_->Create(filesystem_resource_,
    143                               GetFullPath(path).Join().c_str()));
    144   if (!fileref_resource.pp_resource())
    145     return ENOENT;
    146 
    147   // Check file type
    148   if (remove_type != REMOVE_ALL) {
    149     PP_FileInfo file_info;
    150     int32_t query_result = file_ref_iface_->Query(
    151         fileref_resource.pp_resource(), &file_info, PP_BlockUntilComplete());
    152     if (query_result != PP_OK) {
    153       LOG_ERROR("Error querying file type");
    154       return EINVAL;
    155     }
    156     switch (file_info.type) {
    157       case PP_FILETYPE_DIRECTORY:
    158         if (!(remove_type & REMOVE_DIR))
    159           return EISDIR;
    160         break;
    161       case PP_FILETYPE_REGULAR:
    162         if (!(remove_type & REMOVE_FILE))
    163           return ENOTDIR;
    164         break;
    165       default:
    166         LOG_ERROR("Invalid file type: %d", file_info.type);
    167         return EINVAL;
    168     }
    169   }
    170 
    171   int32_t result = file_ref_iface_->Delete(fileref_resource.pp_resource(),
    172                                            PP_BlockUntilComplete());
    173   if (result != PP_OK)
    174     return PPErrorToErrno(result);
    175 
    176   return 0;
    177 }
    178 
    179 Error Html5Fs::Rename(const Path& path, const Path& newpath) {
    180   Error error = BlockUntilFilesystemOpen();
    181   if (error)
    182     return error;
    183 
    184   std::string oldpath_full = GetFullPath(path).Join();
    185   ScopedResource fileref_resource(
    186       ppapi(),
    187       file_ref_iface_->Create(filesystem_resource_, oldpath_full.c_str()));
    188   if (!fileref_resource.pp_resource())
    189     return ENOENT;
    190 
    191   std::string newpath_full = GetFullPath(newpath).Join();
    192   ScopedResource new_fileref_resource(
    193       ppapi(),
    194       file_ref_iface_->Create(filesystem_resource_, newpath_full.c_str()));
    195   if (!new_fileref_resource.pp_resource())
    196     return ENOENT;
    197 
    198   int32_t result = file_ref_iface_->Rename(fileref_resource.pp_resource(),
    199                                            new_fileref_resource.pp_resource(),
    200                                            PP_BlockUntilComplete());
    201   if (result != PP_OK)
    202     return PPErrorToErrno(result);
    203 
    204   return 0;
    205 }
    206 
    207 Html5Fs::Html5Fs()
    208     : filesystem_iface_(NULL),
    209       file_ref_iface_(NULL),
    210       file_io_iface_(NULL),
    211       filesystem_resource_(0),
    212       filesystem_open_has_result_(false),
    213       filesystem_open_error_(0) {
    214 }
    215 
    216 Error Html5Fs::Init(const FsInitArgs& args) {
    217   pthread_cond_init(&filesystem_open_cond_, NULL);
    218 
    219   Error error = Filesystem::Init(args);
    220   if (error)
    221     return error;
    222 
    223   if (!args.ppapi) {
    224     LOG_ERROR("ppapi is NULL.");
    225     return ENOSYS;
    226   }
    227 
    228   core_iface_ = ppapi()->GetCoreInterface();
    229   filesystem_iface_ = ppapi()->GetFileSystemInterface();
    230   file_io_iface_ = ppapi()->GetFileIoInterface();
    231   file_ref_iface_ = ppapi()->GetFileRefInterface();
    232 
    233   if (!(core_iface_ && filesystem_iface_ && file_io_iface_ &&
    234         file_ref_iface_)) {
    235     LOG_ERROR("Got NULL interface(s): %s%s%s%s",
    236               core_iface_ ? "" : "Core ",
    237               filesystem_iface_ ? "" : "FileSystem ",
    238               file_ref_iface_ ? "" : "FileRef",
    239               file_io_iface_ ? "" : "FileIo ");
    240     return ENOSYS;
    241   }
    242 
    243   // Parse filesystem args.
    244   PP_FileSystemType filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
    245   int64_t expected_size = 0;
    246   for (StringMap_t::const_iterator iter = args.string_map.begin();
    247        iter != args.string_map.end();
    248        ++iter) {
    249     if (iter->first == "type") {
    250       if (iter->second == "PERSISTENT") {
    251         filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
    252       } else if (iter->second == "TEMPORARY") {
    253         filesystem_type = PP_FILESYSTEMTYPE_LOCALTEMPORARY;
    254       } else if (iter->second == "") {
    255         filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
    256       } else {
    257         LOG_ERROR("Unknown filesystem type: '%s'", iter->second.c_str());
    258         return EINVAL;
    259       }
    260     } else if (iter->first == "expected_size") {
    261       expected_size = strtoull(iter->second.c_str(), NULL, 10);
    262     } else if (iter->first == "filesystem_resource") {
    263       PP_Resource resource = strtoull(iter->second.c_str(), NULL, 10);
    264       if (!filesystem_iface_->IsFileSystem(resource))
    265         return EINVAL;
    266 
    267       filesystem_resource_ = resource;
    268       ppapi_->AddRefResource(filesystem_resource_);
    269     } else if (iter->first == "SOURCE") {
    270       prefix_ = iter->second;
    271     } else {
    272       LOG_ERROR("Invalid mount param: %s", iter->first.c_str());
    273       return EINVAL;
    274     }
    275   }
    276 
    277   if (filesystem_resource_ != 0) {
    278     filesystem_open_has_result_ = true;
    279     filesystem_open_error_ = PP_OK;
    280     return 0;
    281   }
    282 
    283   // Initialize filesystem.
    284   filesystem_resource_ =
    285       filesystem_iface_->Create(ppapi_->GetInstance(), filesystem_type);
    286   if (filesystem_resource_ == 0)
    287     return ENOSYS;
    288 
    289   // We can't block the main thread, so make an asynchronous call if on main
    290   // thread. If we are off-main-thread, then don't make an asynchronous call;
    291   // otherwise we require a message loop.
    292   bool main_thread = core_iface_->IsMainThread();
    293   PP_CompletionCallback cc =
    294       main_thread ? PP_MakeCompletionCallback(
    295                         &Html5Fs::FilesystemOpenCallbackThunk, this)
    296                   : PP_BlockUntilComplete();
    297 
    298   int32_t result =
    299       filesystem_iface_->Open(filesystem_resource_, expected_size, cc);
    300 
    301   if (!main_thread) {
    302     filesystem_open_has_result_ = true;
    303     filesystem_open_error_ = PPErrorToErrno(result);
    304 
    305     return filesystem_open_error_;
    306   }
    307 
    308   // We have to assume the call to Open will succeed; there is no better
    309   // result to return here.
    310   return 0;
    311 }
    312 
    313 void Html5Fs::Destroy() {
    314   if (ppapi_ != NULL && filesystem_resource_ != 0)
    315     ppapi_->ReleaseResource(filesystem_resource_);
    316   pthread_cond_destroy(&filesystem_open_cond_);
    317 }
    318 
    319 Error Html5Fs::BlockUntilFilesystemOpen() {
    320   AUTO_LOCK(filesysem_open_lock_);
    321   while (!filesystem_open_has_result_) {
    322     pthread_cond_wait(&filesystem_open_cond_, filesysem_open_lock_.mutex());
    323   }
    324   return filesystem_open_error_;
    325 }
    326 
    327 // static
    328 void Html5Fs::FilesystemOpenCallbackThunk(void* user_data, int32_t result) {
    329   Html5Fs* self = static_cast<Html5Fs*>(user_data);
    330   self->FilesystemOpenCallback(result);
    331 }
    332 
    333 void Html5Fs::FilesystemOpenCallback(int32_t result) {
    334   AUTO_LOCK(filesysem_open_lock_);
    335   filesystem_open_has_result_ = true;
    336   filesystem_open_error_ = PPErrorToErrno(result);
    337   pthread_cond_signal(&filesystem_open_cond_);
    338 }
    339 
    340 }  // namespace nacl_io
    341