Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/trusted/plugin/manifest.cc

Issue 7799028: Remove src/trusted/plugin (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: fix gyp file for necessary -I Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/trusted/plugin/manifest.h ('k') | src/trusted/plugin/method_map.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <algorithm>
8
9 #include "native_client/src/trusted/plugin/manifest.h"
10
11 #include <stdlib.h>
12
13 #include "native_client/src/include/nacl_base.h"
14 #include "native_client/src/include/nacl_macros.h"
15 #include "native_client/src/include/nacl_string.h"
16 #include "native_client/src/include/portability.h"
17 #include "native_client/src/shared/platform/nacl_check.h"
18 #include "native_client/src/trusted/plugin/plugin_error.h"
19 #include "native_client/src/trusted/plugin/utility.h"
20 #include "native_client/src/third_party_mod/jsoncpp/include/json/reader.h"
21 #include "ppapi/cpp/dev/url_util_dev.h"
22 #include "ppapi/cpp/var.h"
23
24 namespace plugin {
25
26 namespace {
27 // Top-level section name keys
28 const char* const kProgramKey = "program";
29 const char* const kInterpreterKey = "interpreter";
30 const char* const kFilesKey = "files";
31
32 // ISA Dictionary keys
33 const char* const kX8632Key = "x86-32";
34 const char* const kX8664Key = "x86-64";
35 const char* const kArmKey = "arm";
36 const char* const kPortableKey = "portable";
37 const char* const kUrlKey = "url";
38
39 // Sample manifest file:
40 // {
41 // "program": {
42 // "x86-32": {"url": "myprogram_x86-32.nexe"},
43 // "x86-64": {"url": "myprogram_x86-64.nexe"},
44 // "arm": {"url": "myprogram_arm.nexe"},
45 // "portable": {"url": "myprogram.pexe"}
46 // },
47 // "interpreter": {
48 // "x86-32": {"url": "interpreter_x86-32.nexe"},
49 // "x86-64": {"url": "interpreter_x86-64.nexe"},
50 // "arm": {"url": "interpreter_arm.nexe"}
51 // },
52 // "files": {
53 // "foo.txt": {
54 // "portable": {"url": "foo.txt"}
55 // },
56 // "bar.txt": {
57 // "x86-32": {"url": "x86-32/bar.txt"},
58 // "portable": {"url": "bar.txt"}
59 // }
60 // }
61 // }
62
63 // TODO(jvoung): Remove these when we find a better way to store/install them.
64 const char* const kPnaclLlcKey = "pnacl-llc";
65 const char* const kPnaclLdKey = "pnacl-ld";
66
67 // Looks up |property_name| in the vector |valid_names| with length
68 // |valid_name_count|. Returns true if |property_name| is found.
69 bool FindMatchingProperty(nacl::string property_name,
70 const char** valid_names,
71 size_t valid_name_count) {
72 for (size_t i = 0; i < valid_name_count; ++i) {
73 if (property_name == valid_names[i]) {
74 return true;
75 }
76 }
77 return false;
78 }
79
80 // Validates that |dictionary| is a valid ISA dictionary. An ISA dictionary
81 // is validated to have keys from within the set of recognized ISAs. Unknown
82 // ISAs are allowed, but ignored and warnings are produced. It is also validated
83 // that it must have an entry to match the ISA specified in |sandbox_isa| or
84 // have a fallback 'portable' entry if there is no match. Returns true if
85 // |dictionary| is an ISA to URL map. Sets |error_string| to something
86 // descriptive if it fails.
87 bool IsValidISADictionary(const Json::Value& dictionary,
88 const nacl::string& sandbox_isa,
89 nacl::string* error_string) {
90 if (error_string == NULL)
91 return false;
92
93 // An ISA to URL dictionary has to be an object.
94 if (!dictionary.isObject()) {
95 *error_string = " property is not an ISA to URL dictionary";
96 return false;
97 }
98 // The keys to the dictionary have to be valid ISA names.
99 Json::Value::Members members = dictionary.getMemberNames();
100 for (size_t i = 0; i < members.size(); ++i) {
101 // The known ISA values for ISA dictionaries in the manifest.
102 static const char* kManifestISAProperties[] = {
103 kX8632Key,
104 kX8664Key,
105 kArmKey,
106 kPortableKey
107 };
108 nacl::string property_name = members[i];
109 if (!FindMatchingProperty(property_name,
110 kManifestISAProperties,
111 NACL_ARRAY_SIZE(kManifestISAProperties))) {
112 PLUGIN_PRINTF(("IsValidISADictionary: unrecognized ISA '%s'.\n",
113 property_name.c_str()));
114 }
115 Json::Value url_spec = dictionary[property_name];
116 if (!url_spec.isObject()) {
117 *error_string = " ISA property '" + property_name +
118 "' has non-dictionary value'" + url_spec.toStyledString() + "'.";
119 return false;
120 }
121 // Check properties of the arch-specific 'URL spec' dictionary
122 static const char* kManifestUrlSpecProperties[] = {
123 kUrlKey
124 };
125 Json::Value::Members ISA_members = url_spec.getMemberNames();
126 for (size_t j = 0; j < ISA_members.size(); ++j) {
127 nacl::string ISA_property_name = ISA_members[j];
128 if (!FindMatchingProperty(ISA_property_name,
129 kManifestUrlSpecProperties,
130 NACL_ARRAY_SIZE(kManifestUrlSpecProperties))) {
131 PLUGIN_PRINTF(("IsValidISADictionary: unrecognized property for '%s'"
132 ": '%s'.",
133 property_name.c_str(), ISA_property_name.c_str()));
134 }
135 }
136 // A "url" key is required for each URL spec.
137 if (!url_spec.isMember(kUrlKey)) {
138 *error_string = " ISA property '" + property_name +
139 "' has no key '" + kUrlKey + "'.";
140 return false;
141 }
142 Json::Value url = url_spec[kUrlKey];
143 if (!url.isString()) {
144 *error_string = " ISA property '" + property_name +
145 "' has non-string value '" + url.toStyledString() +
146 "' for key '" + kUrlKey + "'.";
147 return false;
148 }
149 }
150
151 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include
152 // micro-architectures that can resolve to multiple valid sandboxes.
153 bool has_isa = dictionary.isMember(sandbox_isa);
154 bool has_portable = dictionary.isMember(kPortableKey);
155
156 if (!has_isa && !has_portable) {
157 *error_string =
158 " no version given for current arch and no portable version found.";
159 return false;
160 }
161
162 return true;
163 }
164
165 bool GetURLFromISADictionary(const Json::Value& dictionary,
166 const nacl::string& sandbox_isa,
167 nacl::string* url,
168 nacl::string* error_string,
169 bool* is_portable) {
170 if (url == NULL || error_string == NULL || is_portable == NULL)
171 return false;
172
173 if (!IsValidISADictionary(dictionary, sandbox_isa, error_string))
174 return false;
175
176 const char* isa_string;
177 // The call to IsValidISADictionary() above guarantees that either
178 // sandbox_isa or kPortableKey is present in the dictionary.
179 bool has_portable = dictionary.isMember(kPortableKey);
180 bool has_isa = dictionary.isMember(sandbox_isa);
181 if ((has_portable && Manifest::PreferPortable()) || !has_isa) {
182 *is_portable = true;
183 isa_string = kPortableKey;
184 } else {
185 *is_portable = false;
186 isa_string = sandbox_isa.c_str();
187 }
188
189 const Json::Value& json_url = dictionary[isa_string][kUrlKey];
190 *url = json_url.asString();
191
192 return true;
193 }
194
195 // this will probably be replaced by jvoung's version that exposes
196 // is_portable checks
197 bool GetKeyUrl(const Json::Value& dictionary,
198 const nacl::string& key,
199 const nacl::string& sandbox_isa,
200 nacl::string* full_url,
201 nacl::string* error_string,
202 bool* is_portable) {
203 CHECK(full_url != NULL && error_string != NULL);
204 if (!dictionary.isMember(key)) {
205 *error_string = "file key not found in manifest";
206 return false;
207 }
208 const Json::Value& isa_dict = dictionary[key];
209 if (isa_dict.isMember(sandbox_isa)) {
210 *full_url = isa_dict[sandbox_isa][kUrlKey].asString();
211 *is_portable = false;
212 return true;
213 }
214 if (isa_dict.isMember(kPortableKey)) {
215 *full_url = isa_dict[kPortableKey][kUrlKey].asString();
216 *is_portable = true;
217 return true;
218 }
219 *error_string = "neither ISA-specific nor portable representations exist";
220 return false;
221 }
222
223 } // namespace
224
225 bool Manifest::PreferPortable() {
226 return getenv("NACL_PREFER_PORTABLE_IN_MANIFEST") != NULL;
227 }
228
229 bool Manifest::Init(const nacl::string& manifest_json, ErrorInfo* error_info) {
230 if (error_info == NULL) {
231 return false;
232 }
233 Json::Reader reader;
234 if (!reader.parse(manifest_json, dictionary_)) {
235 std::string json_error = reader.getFormatedErrorMessages();
236 error_info->SetReport(ERROR_MANIFEST_PARSING,
237 "manifest JSON parsing failed: " + json_error);
238 return false;
239 }
240 // Parse has ensured the string was valid JSON. Check that it matches the
241 // manifest schema.
242 return MatchesSchema(error_info);
243 }
244
245 bool Manifest::MatchesSchema(ErrorInfo* error_info) {
246 pp::Var exception;
247 if (error_info == NULL) {
248 return false;
249 }
250 if (!dictionary_.isObject()) {
251 error_info->SetReport(
252 ERROR_MANIFEST_SCHEMA_VALIDATE,
253 "manifest: is not a json dictionary.");
254 return false;
255 }
256 Json::Value::Members members = dictionary_.getMemberNames();
257 for (size_t i = 0; i < members.size(); ++i) {
258 // The top level dictionary entries valid in the manifest file.
259 static const char* kManifestTopLevelProperties[] = { kProgramKey,
260 kInterpreterKey,
261 kFilesKey };
262 nacl::string property_name = members[i];
263 if (!FindMatchingProperty(property_name,
264 kManifestTopLevelProperties,
265 NACL_ARRAY_SIZE(kManifestTopLevelProperties))) {
266 PLUGIN_PRINTF(("Manifest::MatchesSchema: WARNING: unknown top-level "
267 "section '%s' in manifest.\n", property_name.c_str()));
268 }
269 }
270
271 nacl::string error_string;
272
273 // A manifest file must have a program section.
274 if (!dictionary_.isMember(kProgramKey)) {
275 error_info->SetReport(
276 ERROR_MANIFEST_SCHEMA_VALIDATE,
277 nacl::string("manifest: missing '") + kProgramKey + "' section.");
278 return false;
279 }
280
281 // Validate the program section.
282 if (!IsValidISADictionary(dictionary_[kProgramKey],
283 sandbox_isa_,
284 &error_string)) {
285 error_info->SetReport(
286 ERROR_MANIFEST_SCHEMA_VALIDATE,
287 nacl::string("manifest: ") + kProgramKey + error_string);
288 return false;
289 }
290
291 // Validate the interpreter section (if given).
292 if (dictionary_.isMember(kInterpreterKey)) {
293 if (!IsValidISADictionary(dictionary_[kProgramKey],
294 sandbox_isa_,
295 &error_string)) {
296 error_info->SetReport(
297 ERROR_MANIFEST_SCHEMA_VALIDATE,
298 nacl::string("manifest: ") + kInterpreterKey + error_string);
299 return false;
300 }
301 }
302
303 // Validate the file dictionary (if given).
304 if (dictionary_.isMember(kFilesKey)) {
305 const Json::Value& files = dictionary_[kFilesKey];
306 if (!files.isObject()) {
307 error_info->SetReport(
308 ERROR_MANIFEST_SCHEMA_VALIDATE,
309 nacl::string("manifest: '") + kFilesKey + "' is not a dictionary.");
310 }
311 Json::Value::Members members = files.getMemberNames();
312 for (size_t i = 0; i < members.size(); ++i) {
313 nacl::string property_name = members[i];
314 if (!IsValidISADictionary(files[property_name],
315 sandbox_isa_,
316 &error_string)) {
317 error_info->SetReport(
318 ERROR_MANIFEST_SCHEMA_VALIDATE,
319 nacl::string("manifest: file '") + property_name + "'" +
320 error_string);
321 return false;
322 }
323 }
324 }
325
326 return true;
327 }
328
329 bool Manifest::ResolveURL(const nacl::string& relative_url,
330 nacl::string* full_url,
331 ErrorInfo* error_info) const {
332 // The contents of the manifest are resolved relative to the manifest URL.
333 CHECK(url_util_ != NULL);
334 pp::Var resolved_url =
335 url_util_->ResolveRelativeToURL(pp::Var(manifest_base_url_),
336 relative_url);
337 if (!resolved_url.is_string()) {
338 error_info->SetReport(
339 ERROR_MANIFEST_RESOLVE_URL,
340 "could not resolve url '" + relative_url +
341 "' relative to manifest base url '" + manifest_base_url_.c_str() +
342 "'.");
343 return false;
344 }
345 *full_url = resolved_url.AsString();
346 return true;
347 }
348
349 bool Manifest::GetProgramURL(nacl::string* full_url,
350 ErrorInfo* error_info,
351 bool* is_portable) {
352 if (full_url == NULL || error_info == NULL || is_portable == NULL)
353 return false;
354
355 Json::Value program = dictionary_[kProgramKey];
356
357 nacl::string nexe_url;
358 nacl::string error_string;
359
360 if (!GetURLFromISADictionary(program,
361 sandbox_isa_,
362 &nexe_url,
363 &error_string,
364 is_portable)) {
365 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL,
366 nacl::string("program:") + sandbox_isa_ +
367 error_string);
368 return false;
369 }
370
371 return ResolveURL(nexe_url, full_url, error_info);
372 }
373
374 bool Manifest::GetFileKeys(std::set<nacl::string>* keys) const {
375 if (!dictionary_.isMember(kFilesKey)) {
376 // trivial success: no keys when there is no "files" section.
377 return true;
378 }
379 const Json::Value& files = dictionary_[kFilesKey];
380 CHECK(files.isObject());
381 Json::Value::Members members = files.getMemberNames();
382 for (size_t i = 0; i < members.size(); ++i) {
383 keys->insert(members[i]);
384 }
385 return true;
386 }
387
388 bool Manifest::ResolveKey(const nacl::string& key,
389 nacl::string* full_url,
390 ErrorInfo* error_info,
391 bool* is_portable) const {
392 NaClLog(3, "Manifest::ResolveKey(%s)\n", key.c_str());
393 // key must be one of kProgramKey or kFileKey '/' file-section-key
394
395 *full_url = "";
396 if (key == kProgramKey) {
397 nacl::string error_string;
398 if (!GetKeyUrl(dictionary_, key, sandbox_isa_,
399 full_url, &error_string, is_portable)) {
400 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, error_string);
401 return false;
402 }
403 return true;
404 }
405 nacl::string::const_iterator p = find(key.begin(), key.end(), '/');
406 if (p == key.end()) {
407 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL,
408 nacl::string("ResolveKey: invalid key, no slash: ")
409 + key);
410 return false;
411 }
412
413 // generalize to permit other sections?
414 nacl::string prefix(key.begin(), p);
415 if (prefix != kFilesKey) {
416 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL,
417 nacl::string("ResolveKey: invalid key: not \"files\""
418 " prefix: ") + key);
419 return false;
420 }
421
422 nacl::string rest(p + 1, key.end());
423
424 const Json::Value& files = dictionary_[kFilesKey];
425 CHECK(files.isObject());
426 if (!files.isMember(rest)) {
427 error_info->SetReport(
428 ERROR_MANIFEST_RESOLVE_URL,
429 nacl::string("ResolveKey: no such \"files\" entry: ") + key);
430 *is_portable = false;
431 return false;
432 }
433 nacl::string error_string;
434 if (!GetKeyUrl(files, rest, sandbox_isa_,
435 full_url, &error_string, is_portable)) {
436 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, error_string);
437 *full_url = "";
438 return false;
439 }
440 return true;
441 }
442
443 // TODO(jvoung): We won't need these if we figure out how to install llc and ld.
444 bool Manifest::GetLLCURL(nacl::string* full_url, ErrorInfo* error_info) {
445 if (full_url == NULL || error_info == NULL)
446 return false;
447
448 Json::Value pnacl_llc = dictionary_[kPnaclLlcKey];
449
450 nacl::string nexe_url;
451 nacl::string error_string;
452 bool is_portable;
453 if (!GetURLFromISADictionary(pnacl_llc,
454 sandbox_isa_,
455 &nexe_url,
456 &error_string,
457 &is_portable)) {
458 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL,
459 nacl::string(kPnaclLlcKey) + ":" + sandbox_isa_ +
460 error_string);
461 return false;
462 }
463
464 if (is_portable) {
465 // Bootstrap problem -- we need this to translate portable programs!
466 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL,
467 nacl::string(kPnaclLlcKey) +
468 " must be pre-translated for " + sandbox_isa_ + "!");
469 return false;
470 }
471
472 return ResolveURL(nexe_url, full_url, error_info);
473 }
474
475 bool Manifest::GetLDURL(nacl::string* full_url, ErrorInfo* error_info) {
476 if (full_url == NULL || error_info == NULL)
477 return false;
478
479 Json::Value pnacl_ld = dictionary_[kPnaclLdKey];
480
481 nacl::string nexe_url;
482 nacl::string error_string;
483 bool is_portable;
484 if (!GetURLFromISADictionary(pnacl_ld,
485 sandbox_isa_,
486 &nexe_url,
487 &error_string,
488 &is_portable)) {
489 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL,
490 nacl::string(kPnaclLdKey) + ":" + sandbox_isa_ +
491 error_string);
492 return false;
493 }
494
495 if (is_portable) {
496 // Bootstrap problem -- we need this to translate portable programs!
497 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL,
498 nacl::string(kPnaclLdKey) +
499 " must be pre-translated for " + sandbox_isa_ + "!");
500 return false;
501 }
502
503 return ResolveURL(nexe_url, full_url, error_info);
504 }
505
506 } // namespace plugin
OLDNEW
« no previous file with comments | « src/trusted/plugin/manifest.h ('k') | src/trusted/plugin/method_map.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698