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