Home | History | Annotate | Download | only in plugin
      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 "ppapi/native_client/src/trusted/plugin/pnacl_resources.h"
      6 
      7 #include "native_client/src/include/portability_io.h"
      8 #include "native_client/src/shared/platform/nacl_check.h"
      9 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
     10 #include "ppapi/c/pp_errors.h"
     11 #include "ppapi/native_client/src/trusted/plugin/file_utils.h"
     12 #include "ppapi/native_client/src/trusted/plugin/manifest.h"
     13 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
     14 #include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
     15 #include "ppapi/native_client/src/trusted/plugin/utility.h"
     16 #include "third_party/jsoncpp/source/include/json/reader.h"
     17 #include "third_party/jsoncpp/source/include/json/value.h"
     18 
     19 namespace plugin {
     20 
     21 static const char kPnaclComponentScheme[] = "pnacl-component://";
     22 const char PnaclUrls::kResourceInfoUrl[] = "pnacl.json";
     23 
     24 nacl::string PnaclUrls::GetBaseUrl() {
     25   return nacl::string(kPnaclComponentScheme);
     26 }
     27 
     28 nacl::string PnaclUrls::PrependPlatformPrefix(const nacl::string& url) {
     29   return nacl::string(GetSandboxISA()) + "/" + url;
     30 }
     31 
     32 // Determine if a URL is for a pnacl-component file, or if it is some other
     33 // type of URL (e.g., http://, https://, chrome-extension://).
     34 // The URL could be one of the other variants for shared libraries
     35 // served from the web.
     36 bool PnaclUrls::IsPnaclComponent(const nacl::string& full_url) {
     37   return full_url.find(kPnaclComponentScheme, 0) == 0;
     38 }
     39 
     40 // Convert a URL to a filename accepted by GetReadonlyPnaclFd.
     41 // Must be kept in sync with chrome/browser/nacl_host/nacl_file_host.
     42 nacl::string PnaclUrls::PnaclComponentURLToFilename(
     43     const nacl::string& full_url) {
     44   // strip component scheme.
     45   nacl::string r = full_url.substr(
     46       nacl::string(kPnaclComponentScheme).length());
     47 
     48   // Use white-listed-chars.
     49   size_t replace_pos;
     50   static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
     51   replace_pos = r.find_first_not_of(white_list);
     52   while(replace_pos != nacl::string::npos) {
     53     r = r.replace(replace_pos, 1, "_");
     54     replace_pos = r.find_first_not_of(white_list);
     55   }
     56   return r;
     57 }
     58 
     59 //////////////////////////////////////////////////////////////////////
     60 
     61 PnaclResources::~PnaclResources() {
     62   for (std::map<nacl::string, nacl::DescWrapper*>::iterator
     63            i = resource_wrappers_.begin(), e = resource_wrappers_.end();
     64        i != e;
     65        ++i) {
     66     delete i->second;
     67   }
     68   resource_wrappers_.clear();
     69 }
     70 
     71 // static
     72 int32_t PnaclResources::GetPnaclFD(Plugin* plugin, const char* filename) {
     73   PP_FileHandle file_handle =
     74       plugin->nacl_interface()->GetReadonlyPnaclFd(filename);
     75   if (file_handle == PP_kInvalidFileHandle)
     76     return -1;
     77 
     78 #if NACL_WINDOWS
     79   //////// Now try the posix view.
     80   int32_t posix_desc = _open_osfhandle(reinterpret_cast<intptr_t>(file_handle),
     81                                        _O_RDONLY | _O_BINARY);
     82   if (posix_desc == -1) {
     83     PLUGIN_PRINTF((
     84         "PnaclResources::GetPnaclFD failed to convert HANDLE to posix\n"));
     85     // Close the Windows HANDLE if it can't be converted.
     86     CloseHandle(file_handle);
     87   }
     88   return posix_desc;
     89 #else
     90   return file_handle;
     91 #endif
     92 }
     93 
     94 nacl::DescWrapper* PnaclResources::WrapperForUrl(const nacl::string& url) {
     95   CHECK(resource_wrappers_.find(url) != resource_wrappers_.end());
     96   return resource_wrappers_[url];
     97 }
     98 
     99 void PnaclResources::ReadResourceInfo(
    100     const nacl::string& resource_info_url,
    101     const pp::CompletionCallback& resource_info_read_cb) {
    102   PLUGIN_PRINTF(("PnaclResources::ReadResourceInfo\n"));
    103 
    104   nacl::string full_url;
    105   ErrorInfo error_info;
    106   if (!manifest_->ResolveURL(resource_info_url, &full_url, &error_info)) {
    107     ReadResourceInfoError(nacl::string("failed to resolve ") +
    108                           resource_info_url + ": " +
    109                           error_info.message() + ".");
    110     return;
    111   }
    112   PLUGIN_PRINTF(("Resolved resources info url: %s\n", full_url.c_str()));
    113   nacl::string resource_info_filename =
    114     PnaclUrls::PnaclComponentURLToFilename(full_url);
    115 
    116   PLUGIN_PRINTF(("Pnacl-converted resources info url: %s\n",
    117                  resource_info_filename.c_str()));
    118 
    119   int32_t fd = GetPnaclFD(plugin_, resource_info_filename.c_str());
    120   if (fd < 0) {
    121     // File-open failed. Assume this means that the file is
    122     // not actually installed.
    123     ReadResourceInfoError(
    124         nacl::string("The Portable Native Client component is not installed"
    125                      " or has been disabled."));
    126     return;
    127   }
    128 
    129   nacl::string json_buffer;
    130   file_utils::StatusCode status = file_utils::SlurpFile(fd, json_buffer);
    131   if (status != file_utils::PLUGIN_FILE_SUCCESS) {
    132     ReadResourceInfoError(
    133         nacl::string("PnaclResources::ReadResourceInfo reading "
    134                      "failed for: ") + resource_info_filename);
    135     return;
    136   }
    137 
    138   // Finally, we have the resource info JSON data in json_buffer.
    139   PLUGIN_PRINTF(("Resource info JSON data:\n%s\n", json_buffer.c_str()));
    140   nacl::string error_message;
    141   if (!ParseResourceInfo(json_buffer, error_message)) {
    142     ReadResourceInfoError(nacl::string("Parsing resource info failed: ") +
    143                           error_message + "\n");
    144     return;
    145   }
    146 
    147   // Done. Queue the completion callback.
    148   pp::Core* core = pp::Module::Get()->core();
    149   core->CallOnMainThread(0, resource_info_read_cb, PP_OK);
    150 }
    151 
    152 void PnaclResources::ReadResourceInfoError(const nacl::string& msg) {
    153   coordinator_->ReportNonPpapiError(ERROR_PNACL_RESOURCE_FETCH, msg);
    154 }
    155 
    156 bool PnaclResources::ParseResourceInfo(const nacl::string& buf,
    157                                        nacl::string& errmsg) {
    158   // Expect the JSON file to contain a top-level object (dictionary).
    159   Json::Reader json_reader;
    160   Json::Value json_data;
    161   if (!json_reader.parse(buf, json_data)) {
    162     errmsg = nacl::string("JSON parse error: ") +
    163              json_reader.getFormatedErrorMessages();
    164     return false;
    165   }
    166 
    167   if (!json_data.isObject()) {
    168     errmsg = nacl::string("Malformed JSON dictionary");
    169     return false;
    170   }
    171 
    172   if (json_data.isMember("pnacl-llc-name")) {
    173     Json::Value json_name = json_data["pnacl-llc-name"];
    174     if (json_name.isString()) {
    175       llc_tool_name = json_name.asString();
    176       PLUGIN_PRINTF(("Set llc_tool_name=%s\n", llc_tool_name.c_str()));
    177     }
    178   }
    179 
    180   if (json_data.isMember("pnacl-ld-name")) {
    181     Json::Value json_name = json_data["pnacl-ld-name"];
    182     if (json_name.isString()) {
    183       ld_tool_name = json_name.asString();
    184       PLUGIN_PRINTF(("Set ld_tool_name=%s\n", ld_tool_name.c_str()));
    185     }
    186   }
    187 
    188   return true;
    189 }
    190 
    191 void PnaclResources::StartLoad(
    192     const pp::CompletionCallback& all_loaded_callback) {
    193   PLUGIN_PRINTF(("PnaclResources::StartLoad\n"));
    194 
    195   std::vector<nacl::string> resource_urls;
    196   resource_urls.push_back(GetLlcUrl());
    197   resource_urls.push_back(GetLdUrl());
    198 
    199   PLUGIN_PRINTF(("PnaclResources::StartLoad -- local install of PNaCl.\n"));
    200   // Do a blocking load of each of the resources.
    201   int32_t result = PP_OK;
    202   for (size_t i = 0; i < resource_urls.size(); ++i) {
    203     const nacl::string& url_with_platform_prefix =
    204       PnaclUrls::PrependPlatformPrefix(resource_urls[i]);
    205     nacl::string full_url;
    206     ErrorInfo error_info;
    207     if (!manifest_->ResolveURL(url_with_platform_prefix, &full_url,
    208                                &error_info)) {
    209       coordinator_->ReportNonPpapiError(
    210           ERROR_PNACL_RESOURCE_FETCH,
    211           nacl::string("failed to resolve ") +
    212           url_with_platform_prefix + ": " +
    213           error_info.message() + ".");
    214       break;
    215     }
    216     nacl::string filename = PnaclUrls::PnaclComponentURLToFilename(full_url);
    217 
    218     int32_t fd = PnaclResources::GetPnaclFD(plugin_, filename.c_str());
    219     if (fd < 0) {
    220       // File-open failed. Assume this means that the file is
    221       // not actually installed. This shouldn't actually occur since
    222       // ReadResourceInfo() should happen first, and error out.
    223       coordinator_->ReportNonPpapiError(
    224           ERROR_PNACL_RESOURCE_FETCH,
    225           nacl::string("The Portable Native Client component is not installed "
    226                        "or has been disabled. Cannot open file: ") + filename);
    227       result = PP_ERROR_FILENOTFOUND;
    228       break;
    229     } else {
    230       resource_wrappers_[resource_urls[i]] =
    231           plugin_->wrapper_factory()->MakeFileDesc(fd, O_RDONLY);
    232     }
    233   }
    234   // We're done!  Queue the callback.
    235   pp::Core* core = pp::Module::Get()->core();
    236   core->CallOnMainThread(0, all_loaded_callback, result);
    237 }
    238 
    239 }  // namespace plugin
    240