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_http.h"
      6 
      7 #include <assert.h>
      8 #include <ctype.h>
      9 #include <errno.h>
     10 #include <fcntl.h>
     11 #include <stdio.h>
     12 #include <string.h>
     13 #include <sys/stat.h>
     14 #include <sys/types.h>
     15 
     16 #include <vector>
     17 
     18 #include <ppapi/c/pp_errors.h>
     19 
     20 #include "nacl_io/mount_node_dir.h"
     21 #include "nacl_io/mount_node_http.h"
     22 #include "nacl_io/osinttypes.h"
     23 #include "nacl_io/osunistd.h"
     24 
     25 namespace nacl_io {
     26 
     27 namespace {
     28 
     29 typedef std::vector<char*> StringList_t;
     30 size_t SplitString(char* str, const char* delim, StringList_t* list) {
     31   char* item = strtok(str, delim);
     32 
     33   list->clear();
     34   while (item) {
     35     list->push_back(item);
     36     item = strtok(NULL, delim);
     37   }
     38 
     39   return list->size();
     40 }
     41 
     42 }  // namespace
     43 
     44 std::string NormalizeHeaderKey(const std::string& s) {
     45   // Capitalize the first letter and any letter following a hyphen:
     46   // e.g. ACCEPT-ENCODING -> Accept-Encoding
     47   std::string result;
     48   bool upper = true;
     49   for (size_t i = 0; i < s.length(); ++i) {
     50     char c = s[i];
     51     result += upper ? toupper(c) : tolower(c);
     52     upper = c == '-';
     53   }
     54 
     55   return result;
     56 }
     57 
     58 Error MountHttp::Access(const Path& path, int a_mode) {
     59   assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
     60 
     61   NodeMap_t::iterator iter = node_cache_.find(path.Join());
     62   if (iter == node_cache_.end()) {
     63     // If we can't find the node in the cache, fetch it
     64     std::string url = MakeUrl(path);
     65     ScopedMountNode node(new MountNodeHttp(this, url, cache_content_));
     66     Error error = node->Init(O_RDONLY);
     67     if (error)
     68       return error;
     69 
     70     error = node->GetStat(NULL);
     71     if (error)
     72       return error;
     73   }
     74 
     75   // Don't allow write or execute access.
     76   if (a_mode & (W_OK | X_OK))
     77     return EACCES;
     78 
     79   return 0;
     80 }
     81 
     82 Error MountHttp::Open(const Path& path, int mode, ScopedMountNode* out_node) {
     83   out_node->reset(NULL);
     84   assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/');
     85 
     86   NodeMap_t::iterator iter = node_cache_.find(path.Join());
     87   if (iter != node_cache_.end()) {
     88     *out_node = iter->second;
     89     return 0;
     90   }
     91 
     92   // If we can't find the node in the cache, create it
     93   std::string url = MakeUrl(path);
     94   ScopedMountNode node(new MountNodeHttp(this, url, cache_content_));
     95   Error error = node->Init(mode);
     96   if (error)
     97     return error;
     98 
     99   error = node->GetStat(NULL);
    100   if (error)
    101     return error;
    102 
    103   ScopedMountNode parent;
    104   error = FindOrCreateDir(path.Parent(), &parent);
    105   if (error)
    106     return error;
    107 
    108   error = parent->AddChild(path.Basename(), node);
    109   if (error)
    110     return error;
    111 
    112   node_cache_[path.Join()] = node;
    113   *out_node = node;
    114   return 0;
    115 }
    116 
    117 Error MountHttp::Unlink(const Path& path) {
    118   NodeMap_t::iterator iter = node_cache_.find(path.Join());
    119   if (iter == node_cache_.end())
    120     return ENOENT;
    121 
    122   if (iter->second->IsaDir())
    123     return EISDIR;
    124 
    125   return EACCES;
    126 }
    127 
    128 Error MountHttp::Mkdir(const Path& path, int permissions) {
    129   NodeMap_t::iterator iter = node_cache_.find(path.Join());
    130   if (iter != node_cache_.end()) {
    131     if (iter->second->IsaDir())
    132       return EEXIST;
    133   }
    134   return EACCES;
    135 }
    136 
    137 Error MountHttp::Rmdir(const Path& path) {
    138   NodeMap_t::iterator iter = node_cache_.find(path.Join());
    139   if (iter == node_cache_.end())
    140     return ENOENT;
    141 
    142   if (!iter->second->IsaDir())
    143     return ENOTDIR;
    144 
    145   return EACCES;
    146 }
    147 
    148 Error MountHttp::Remove(const Path& path) {
    149   NodeMap_t::iterator iter = node_cache_.find(path.Join());
    150   if (iter == node_cache_.end())
    151     return ENOENT;
    152 
    153   return EACCES;
    154 }
    155 
    156 PP_Resource MountHttp::MakeUrlRequestInfo(const std::string& url,
    157                                           const char* method,
    158                                           StringMap_t* additional_headers) {
    159   URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface();
    160   VarInterface* var_interface = ppapi_->GetVarInterface();
    161 
    162   PP_Resource request_info = interface->Create(ppapi_->GetInstance());
    163   if (!request_info)
    164     return 0;
    165 
    166   interface->SetProperty(request_info,
    167                          PP_URLREQUESTPROPERTY_URL,
    168                          var_interface->VarFromUtf8(url.c_str(), url.length()));
    169   interface->SetProperty(request_info,
    170                          PP_URLREQUESTPROPERTY_METHOD,
    171                          var_interface->VarFromUtf8(method, strlen(method)));
    172   interface->SetProperty(request_info,
    173                          PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
    174                          PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE));
    175   interface->SetProperty(request_info,
    176                          PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
    177                          PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE));
    178 
    179   // Merge the mount headers with the request headers. If the field is already
    180   // set it |additional_headers|, don't use the one from headers_.
    181   for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end();
    182        ++iter) {
    183     const std::string& key = NormalizeHeaderKey(iter->first);
    184     if (additional_headers->find(key) == additional_headers->end()) {
    185       additional_headers->insert(std::make_pair(key, iter->second));
    186     }
    187   }
    188 
    189   // Join the headers into one string.
    190   std::string headers;
    191   for (StringMap_t::iterator iter = additional_headers->begin();
    192        iter != additional_headers->end();
    193        ++iter) {
    194     headers += iter->first + ": " + iter->second + '\n';
    195   }
    196 
    197   interface->SetProperty(
    198       request_info,
    199       PP_URLREQUESTPROPERTY_HEADERS,
    200       var_interface->VarFromUtf8(headers.c_str(), headers.length()));
    201 
    202   return request_info;
    203 }
    204 
    205 MountHttp::MountHttp()
    206     : allow_cors_(false),
    207       allow_credentials_(false),
    208       cache_stat_(true),
    209       cache_content_(true) {}
    210 
    211 Error MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
    212   Error error = Mount::Init(dev, args, ppapi);
    213   if (error)
    214     return error;
    215 
    216   // Parse mount args.
    217   for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) {
    218     if (iter->first == "SOURCE") {
    219       url_root_ = iter->second;
    220 
    221       // Make sure url_root_ ends with a slash.
    222       if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') {
    223         url_root_ += '/';
    224       }
    225     } else if (iter->first == "manifest") {
    226       char* text;
    227       error = LoadManifest(iter->second, &text);
    228       if (error)
    229         return error;
    230 
    231       error = ParseManifest(text);
    232       if (error) {
    233         delete[] text;
    234         return error;
    235       }
    236 
    237       delete[] text;
    238     } else if (iter->first == "allow_cross_origin_requests") {
    239       allow_cors_ = iter->second == "true";
    240     } else if (iter->first == "allow_credentials") {
    241       allow_credentials_ = iter->second == "true";
    242     } else if (iter->first == "cache_stat") {
    243       cache_stat_ = iter->second == "true";
    244     } else if (iter->first == "cache_content") {
    245       cache_content_ = iter->second == "true";
    246     } else {
    247       // Assume it is a header to pass to an HTTP request.
    248       headers_[NormalizeHeaderKey(iter->first)] = iter->second;
    249     }
    250   }
    251 
    252   return 0;
    253 }
    254 
    255 void MountHttp::Destroy() {}
    256 
    257 Error MountHttp::FindOrCreateDir(const Path& path,
    258                                  ScopedMountNode* out_node) {
    259   out_node->reset(NULL);
    260   std::string strpath = path.Join();
    261   NodeMap_t::iterator iter = node_cache_.find(strpath);
    262   if (iter != node_cache_.end()) {
    263     *out_node = iter->second;
    264     return 0;
    265   }
    266 
    267   // If the node does not exist, create it.
    268   ScopedMountNode node(new MountNodeDir(this));
    269   Error error = node->Init(S_IREAD);
    270   if (error)
    271     return error;
    272 
    273   // If not the root node, find the parent node and add it to the parent
    274   if (!path.Top()) {
    275     ScopedMountNode parent;
    276     error = FindOrCreateDir(path.Parent(), &parent);
    277     if (error)
    278       return error;
    279 
    280     error = parent->AddChild(path.Basename(), node);
    281     if (error)
    282       return error;
    283   }
    284 
    285   // Add it to the node cache.
    286   node_cache_[strpath] = node;
    287   *out_node = node;
    288   return 0;
    289 }
    290 
    291 Error MountHttp::ParseManifest(char* text) {
    292   StringList_t lines;
    293   SplitString(text, "\n", &lines);
    294 
    295   for (size_t i = 0; i < lines.size(); i++) {
    296     StringList_t words;
    297     SplitString(lines[i], " ", &words);
    298 
    299     if (words.size() == 3) {
    300       char* modestr = words[0];
    301       char* lenstr = words[1];
    302       char* name = words[2];
    303 
    304       assert(modestr && strlen(modestr) == 4);
    305       assert(name && name[0] == '/');
    306       assert(lenstr);
    307 
    308       // Only support regular and streams for now
    309       // Ignore EXEC bit
    310       int mode = S_IFREG;
    311       switch (modestr[0]) {
    312         case '-':
    313           mode = S_IFREG;
    314           break;
    315         case 'c':
    316           mode = S_IFCHR;
    317           break;
    318         default:
    319           fprintf(stderr, "Unable to parse type %s for %s.\n", modestr, name);
    320           return EINVAL;
    321       }
    322 
    323       switch (modestr[1]) {
    324         case '-':
    325           break;
    326         case 'r':
    327           mode |= S_IREAD;
    328           break;
    329         default:
    330           fprintf(stderr, "Unable to parse read %s for %s.\n", modestr, name);
    331           return EINVAL;
    332       }
    333 
    334       switch (modestr[2]) {
    335         case '-':
    336           break;
    337         case 'w':
    338           mode |= S_IWRITE;
    339           break;
    340         default:
    341           fprintf(stderr, "Unable to parse write %s for %s.\n", modestr, name);
    342           return EINVAL;
    343       }
    344 
    345       Path path(name);
    346       std::string url = MakeUrl(path);
    347 
    348       MountNodeHttp* http_node = new MountNodeHttp(this, url, cache_content_);
    349       ScopedMountNode node(http_node);
    350 
    351       Error error = node->Init(mode);
    352       if (error)
    353         return error;
    354       http_node->SetCachedSize(atoi(lenstr));
    355 
    356       ScopedMountNode dir_node;
    357       error = FindOrCreateDir(path.Parent(), &dir_node);
    358       if (error)
    359         return error;
    360 
    361       error = dir_node->AddChild(path.Basename(), node);
    362       if (error)
    363         return error;
    364 
    365       std::string pname = path.Join();
    366       node_cache_[pname] = node;
    367     }
    368   }
    369 
    370   return 0;
    371 }
    372 
    373 Error MountHttp::LoadManifest(const std::string& manifest_name,
    374                               char** out_manifest) {
    375   Path manifest_path(manifest_name);
    376   ScopedMountNode manifest_node;
    377   *out_manifest = NULL;
    378 
    379   int error = Open(manifest_path, O_RDONLY, &manifest_node);
    380   if (error)
    381     return error;
    382 
    383   size_t size;
    384   error = manifest_node->GetSize(&size);
    385   if (error)
    386     return error;
    387 
    388   char* text = new char[size + 1];
    389   int len;
    390   error = manifest_node->Read(0, text, size, &len);
    391   if (error)
    392     return error;
    393 
    394   text[len] = 0;
    395   *out_manifest = text;
    396   return 0;
    397 }
    398 
    399 std::string MountHttp::MakeUrl(const Path& path) {
    400   return url_root_ +
    401          (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join());
    402 }
    403 
    404 }  // namespace nacl_io
    405