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