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