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