pnacl_resources.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/manifest.h"
11#include "native_client/src/trusted/plugin/plugin.h"
12#include "native_client/src/trusted/plugin/pnacl_coordinator.h"
13#include "native_client/src/trusted/plugin/utility.h"
14
15#include "ppapi/c/pp_errors.h"
16
17namespace plugin {
18
19static const char kExtensionOrigin[] =
20    "chrome-extension://gcodniebolpnpaiggndmcmmfpldlknih/";
21static const char kPnaclComponentID[] =
22    "pnacl-component://";
23const char PnaclUrls::kLlcUrl[] = "llc.nexe";
24const char PnaclUrls::kLdUrl[] = "ld.nexe";
25
26bool PnaclUrls::UsePnaclExtension(const Plugin* plugin) {
27  // TODO(jvoung): remove extension stuff if we aren't using it.
28  return false;
29}
30
31nacl::string PnaclUrls::GetBaseUrl(bool use_extension) {
32  if (use_extension) {
33    return nacl::string(kExtensionOrigin) + GetSandboxISA() + "/";
34  } else {
35    return nacl::string(kPnaclComponentID) + GetSandboxISA() + "/";
36  }
37}
38
39bool PnaclUrls::IsPnaclComponent(const nacl::string& full_url) {
40  return full_url.find(kPnaclComponentID, 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 componentID.
48  nacl::string r = full_url.substr(nacl::string(kPnaclComponentID).length());
49
50  // Use white-listed-chars.
51  size_t replace_pos;
52  static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
53  replace_pos = r.find_first_not_of(white_list);
54  while(replace_pos != nacl::string::npos) {
55    r = r.replace(replace_pos, 1, "_");
56    replace_pos = r.find_first_not_of(white_list);
57  }
58  return r;
59}
60
61//////////////////////////////////////////////////////////////////////
62
63PnaclResources::~PnaclResources() {
64  for (std::map<nacl::string, nacl::DescWrapper*>::iterator
65           i = resource_wrappers_.begin(), e = resource_wrappers_.end();
66       i != e;
67       ++i) {
68    delete i->second;
69  }
70  resource_wrappers_.clear();
71}
72
73// static
74int32_t PnaclResources::GetPnaclFD(Plugin* plugin, const char* filename) {
75  PP_FileHandle file_handle =
76      plugin->nacl_interface()->GetReadonlyPnaclFd(filename);
77  if (file_handle == PP_kInvalidFileHandle)
78    return -1;
79
80#if NACL_WINDOWS
81  //////// Now try the posix view.
82  int32_t posix_desc = _open_osfhandle(reinterpret_cast<intptr_t>(file_handle),
83                                       _O_RDONLY | _O_BINARY);
84  if (posix_desc == -1) {
85    PLUGIN_PRINTF((
86        "PnaclResources::GetPnaclFD failed to convert HANDLE to posix\n"));
87    // Close the Windows HANDLE if it can't be converted.
88    CloseHandle(file_handle);
89  }
90  return posix_desc;
91#else
92  return file_handle;
93#endif
94}
95
96nacl::DescWrapper* PnaclResources::WrapperForUrl(const nacl::string& url) {
97  CHECK(resource_wrappers_.find(url) != resource_wrappers_.end());
98  return resource_wrappers_[url];
99}
100
101void PnaclResources::StartLoad() {
102  PLUGIN_PRINTF(("PnaclResources::StartLoad\n"));
103
104  CHECK(resource_urls_.size() > 0);
105  if (PnaclUrls::UsePnaclExtension(plugin_)) {
106    PLUGIN_PRINTF(("PnaclResources::StartLoad -- PNaCl chrome extension.\n"));
107    // Do a URL fetch.
108    // Create a counter (barrier) callback to track when all of the resources
109    // are loaded.
110    uint32_t resource_count = static_cast<uint32_t>(resource_urls_.size());
111    delayed_callback_.reset(
112        new DelayedCallback(all_loaded_callback_, resource_count));
113
114    // Schedule the downloads.
115    for (size_t i = 0; i < resource_urls_.size(); ++i) {
116      nacl::string full_url;
117      ErrorInfo error_info;
118      if (!manifest_->ResolveURL(resource_urls_[i], &full_url, &error_info)) {
119        coordinator_->ReportNonPpapiError(
120            ERROR_PNACL_RESOURCE_FETCH,
121            nacl::string("failed to resolve ") +
122            resource_urls_[i] + ": " +
123            error_info.message() + ".");
124        break;
125      }
126      pp::CompletionCallback ready_callback =
127          callback_factory_.NewCallback(
128              &PnaclResources::ResourceReady,
129              resource_urls_[i],
130              full_url);
131      if (!plugin_->StreamAsFile(full_url,
132                                 ready_callback.pp_completion_callback())) {
133        coordinator_->ReportNonPpapiError(
134            ERROR_PNACL_RESOURCE_FETCH,
135            nacl::string("failed to download ") +
136            resource_urls_[i] + ".");
137        break;
138      }
139    }
140  } else {
141    PLUGIN_PRINTF(("PnaclResources::StartLoad -- local install of PNaCl.\n"));
142    // Do a blocking load of each of the resources.
143    int32_t result = PP_OK;
144    for (size_t i = 0; i < resource_urls_.size(); ++i) {
145      const nacl::string& url = resource_urls_[i];
146      nacl::string full_url;
147      ErrorInfo error_info;
148      if (!manifest_->ResolveURL(resource_urls_[i], &full_url, &error_info)) {
149        coordinator_->ReportNonPpapiError(
150            ERROR_PNACL_RESOURCE_FETCH,
151            nacl::string("failed to resolve ") +
152            url + ": " +
153            error_info.message() + ".");
154        break;
155      }
156      nacl::string filename = PnaclUrls::PnaclComponentURLToFilename(full_url);
157
158      int32_t fd = PnaclResources::GetPnaclFD(plugin_, filename.c_str());
159      if (fd < 0) {
160        coordinator_->ReportNonPpapiError(
161            ERROR_PNACL_RESOURCE_FETCH,
162            nacl::string("PnaclLocalResources::StartLoad failed for: ") +
163            filename);
164        result = PP_ERROR_FILENOTFOUND;
165        break;
166      } else {
167        resource_wrappers_[url] =
168            plugin_->wrapper_factory()->MakeFileDesc(fd, O_RDONLY);
169      }
170    }
171    // We're done!  Queue the callback.
172    pp::Core* core = pp::Module::Get()->core();
173    core->CallOnMainThread(0, all_loaded_callback_, result);
174  }
175}
176
177void PnaclResources::ResourceReady(int32_t pp_error,
178                                   const nacl::string& url,
179                                   const nacl::string& full_url) {
180  PLUGIN_PRINTF(("PnaclResources::ResourceReady (pp_error=%"
181                 NACL_PRId32", url=%s)\n", pp_error, url.c_str()));
182  // pp_error is checked by GetLoadedFileDesc.
183  int32_t fd = coordinator_->GetLoadedFileDesc(pp_error,
184                                               full_url,
185                                               "resource " + url);
186  if (fd < 0) {
187    coordinator_->ReportPpapiError(ERROR_PNACL_RESOURCE_FETCH,
188                                   pp_error,
189                                   "PnaclResources::ResourceReady failed.");
190  } else {
191    resource_wrappers_[url] =
192        plugin_->wrapper_factory()->MakeFileDesc(fd, O_RDONLY);
193    delayed_callback_->RunIfTime();
194  }
195}
196
197}  // namespace plugin
198