OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2012 The Chromium 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 "ppapi/native_client/src/trusted/plugin/json_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 "ppapi/c/private/ppb_nacl_private.h" | |
19 #include "ppapi/cpp/dev/url_util_dev.h" | |
20 #include "ppapi/cpp/var.h" | |
21 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" | |
22 #include "ppapi/native_client/src/trusted/plugin/utility.h" | |
23 #include "third_party/jsoncpp/source/include/json/reader.h" | |
24 | |
25 namespace plugin { | |
26 | |
27 namespace { | |
28 // Top-level section name keys | |
29 const char* const kProgramKey = "program"; | |
30 const char* const kInterpreterKey = "interpreter"; | |
31 const char* const kFilesKey = "files"; | |
32 | |
33 // ISA Dictionary keys | |
34 const char* const kX8632Key = "x86-32"; | |
35 const char* const kX8632NonSFIKey = "x86-32-nonsfi"; | |
36 const char* const kX8664Key = "x86-64"; | |
37 const char* const kX8664NonSFIKey = "x86-64-nonsfi"; | |
38 const char* const kArmKey = "arm"; | |
39 const char* const kArmNonSFIKey = "arm-nonsfi"; | |
40 const char* const kPortableKey = "portable"; | |
41 | |
42 // Url Resolution keys | |
43 const char* const kPnaclDebugKey = "pnacl-debug"; | |
44 const char* const kPnaclTranslateKey = "pnacl-translate"; | |
45 const char* const kUrlKey = "url"; | |
46 | |
47 // PNaCl keys | |
48 const char* const kOptLevelKey = "optlevel"; | |
49 | |
50 // Sample NaCl manifest file: | |
51 // { | |
52 // "program": { | |
53 // "x86-32": {"url": "myprogram_x86-32.nexe"}, | |
54 // "x86-64": {"url": "myprogram_x86-64.nexe"}, | |
55 // "arm": {"url": "myprogram_arm.nexe"} | |
56 // }, | |
57 // "interpreter": { | |
58 // "x86-32": {"url": "interpreter_x86-32.nexe"}, | |
59 // "x86-64": {"url": "interpreter_x86-64.nexe"}, | |
60 // "arm": {"url": "interpreter_arm.nexe"} | |
61 // }, | |
62 // "files": { | |
63 // "foo.txt": { | |
64 // "portable": {"url": "foo.txt"} | |
65 // }, | |
66 // "bar.txt": { | |
67 // "x86-32": {"url": "x86-32/bar.txt"}, | |
68 // "portable": {"url": "bar.txt"} | |
69 // }, | |
70 // "libfoo.so": { | |
71 // "x86-64" : { "url": "..." } | |
72 // } | |
73 // } | |
74 // } | |
75 | |
76 // Sample PNaCl manifest file: | |
77 // { | |
78 // "program": { | |
79 // "portable": { | |
80 // "pnacl-translate": { | |
81 // "url": "myprogram.pexe" | |
82 // }, | |
83 // "pnacl-debug": { | |
84 // "url": "myprogram.debug.pexe", | |
85 // "opt_level": 0 | |
86 // } | |
87 // } | |
88 // }, | |
89 // "files": { | |
90 // "foo.txt": { | |
91 // "portable": {"url": "foo.txt"} | |
92 // }, | |
93 // "bar.txt": { | |
94 // "portable": {"url": "bar.txt"} | |
95 // } | |
96 // } | |
97 // } | |
98 | |
99 // Returns the key for the architecture in non-SFI mode. | |
100 nacl::string GetNonSFIKey(const nacl::string& sandbox_isa) { | |
101 return sandbox_isa + "-nonsfi"; | |
102 } | |
103 | |
104 // Looks up |property_name| in the vector |valid_names| with length | |
105 // |valid_name_count|. Returns true if |property_name| is found. | |
106 bool FindMatchingProperty(const nacl::string& property_name, | |
107 const char** valid_names, | |
108 size_t valid_name_count) { | |
109 for (size_t i = 0; i < valid_name_count; ++i) { | |
110 if (property_name == valid_names[i]) { | |
111 return true; | |
112 } | |
113 } | |
114 return false; | |
115 } | |
116 | |
117 // Return true if this is a valid dictionary. Having only keys present in | |
118 // |valid_keys| and having at least the keys in |required_keys|. | |
119 // Error messages will be placed in |error_string|, given that the dictionary | |
120 // was the property value of |container_key|. | |
121 // E.g., "container_key" : dictionary | |
122 bool IsValidDictionary(const Json::Value& dictionary, | |
123 const nacl::string& container_key, | |
124 const nacl::string& parent_key, | |
125 const char** valid_keys, | |
126 size_t valid_key_count, | |
127 const char** required_keys, | |
128 size_t required_key_count, | |
129 nacl::string* error_string) { | |
130 if (!dictionary.isObject()) { | |
131 nacl::stringstream error_stream; | |
132 error_stream << parent_key << " property '" << container_key | |
133 << "' is non-dictionary value '" | |
134 << dictionary.toStyledString() << "'."; | |
135 *error_string = error_stream.str(); | |
136 return false; | |
137 } | |
138 // Check for unknown dictionary members. | |
139 Json::Value::Members members = dictionary.getMemberNames(); | |
140 for (size_t i = 0; i < members.size(); ++i) { | |
141 nacl::string property_name = members[i]; | |
142 if (!FindMatchingProperty(property_name, | |
143 valid_keys, | |
144 valid_key_count)) { | |
145 // For forward compatibility, we do not prohibit other keys being in | |
146 // the dictionary. | |
147 PLUGIN_PRINTF(("WARNING: '%s' property '%s' has unknown key '%s'.\n", | |
148 parent_key.c_str(), | |
149 container_key.c_str(), property_name.c_str())); | |
150 } | |
151 } | |
152 // Check for required members. | |
153 for (size_t i = 0; i < required_key_count; ++i) { | |
154 if (!dictionary.isMember(required_keys[i])) { | |
155 nacl::stringstream error_stream; | |
156 error_stream << parent_key << " property '" << container_key | |
157 << "' does not have required key: '" | |
158 << required_keys[i] << "'."; | |
159 *error_string = error_stream.str(); | |
160 return false; | |
161 } | |
162 } | |
163 return true; | |
164 } | |
165 | |
166 // Validate a "url" dictionary assuming it was resolved from container_key. | |
167 // E.g., "container_key" : { "url": "foo.txt" } | |
168 bool IsValidUrlSpec(const Json::Value& url_spec, | |
169 const nacl::string& container_key, | |
170 const nacl::string& parent_key, | |
171 const nacl::string& sandbox_isa, | |
172 nacl::string* error_string) { | |
173 static const char* kManifestUrlSpecRequired[] = { | |
174 kUrlKey | |
175 }; | |
176 const char** urlSpecPlusOptional; | |
177 size_t urlSpecPlusOptionalLength; | |
178 if (sandbox_isa == kPortableKey) { | |
179 static const char* kPnaclUrlSpecPlusOptional[] = { | |
180 kUrlKey, | |
181 kOptLevelKey, | |
182 }; | |
183 urlSpecPlusOptional = kPnaclUrlSpecPlusOptional; | |
184 urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kPnaclUrlSpecPlusOptional); | |
185 } else { | |
186 // URL specifications must not contain "pnacl-translate" keys. | |
187 // This prohibits NaCl clients from invoking PNaCl. | |
188 if (url_spec.isMember(kPnaclTranslateKey)) { | |
189 nacl::stringstream error_stream; | |
190 error_stream << "PNaCl-like NMF with application/x-nacl mimetype instead " | |
191 << "of x-pnacl mimetype (has " << kPnaclTranslateKey << ")."; | |
192 *error_string = error_stream.str(); | |
193 return false; | |
194 } | |
195 urlSpecPlusOptional = kManifestUrlSpecRequired; | |
196 urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kManifestUrlSpecRequired); | |
197 } | |
198 if (!IsValidDictionary(url_spec, container_key, parent_key, | |
199 urlSpecPlusOptional, | |
200 urlSpecPlusOptionalLength, | |
201 kManifestUrlSpecRequired, | |
202 NACL_ARRAY_SIZE(kManifestUrlSpecRequired), | |
203 error_string)) { | |
204 return false; | |
205 } | |
206 // Verify the correct types of the fields if they exist. | |
207 Json::Value url = url_spec[kUrlKey]; | |
208 if (!url.isString()) { | |
209 nacl::stringstream error_stream; | |
210 error_stream << parent_key << " property '" << container_key << | |
211 "' has non-string value '" << url.toStyledString() << | |
212 "' for key '" << kUrlKey << "'."; | |
213 *error_string = error_stream.str(); | |
214 return false; | |
215 } | |
216 Json::Value opt_level = url_spec[kOptLevelKey]; | |
217 if (!opt_level.empty() && !opt_level.isNumeric()) { | |
218 nacl::stringstream error_stream; | |
219 error_stream << parent_key << " property '" << container_key << | |
220 "' has non-numeric value '" << opt_level.toStyledString() << | |
221 "' for key '" << kOptLevelKey << "'."; | |
222 *error_string = error_stream.str(); | |
223 return false; | |
224 } | |
225 return true; | |
226 } | |
227 | |
228 // Validate a "pnacl-translate" or "pnacl-debug" dictionary, assuming | |
229 // it was resolved from container_key. | |
230 // E.g., "container_key" : { "pnacl-translate" : URLSpec } | |
231 bool IsValidPnaclTranslateSpec(const Json::Value& pnacl_spec, | |
232 const nacl::string& container_key, | |
233 const nacl::string& parent_key, | |
234 const nacl::string& sandbox_isa, | |
235 nacl::string* error_string) { | |
236 static const char* kManifestPnaclSpecValid[] = { | |
237 kPnaclDebugKey, | |
238 kPnaclTranslateKey | |
239 }; | |
240 static const char* kManifestPnaclSpecRequired[] = { | |
241 kPnaclTranslateKey | |
242 }; | |
243 if (!IsValidDictionary(pnacl_spec, container_key, parent_key, | |
244 kManifestPnaclSpecValid, | |
245 NACL_ARRAY_SIZE(kManifestPnaclSpecValid), | |
246 kManifestPnaclSpecRequired, | |
247 NACL_ARRAY_SIZE(kManifestPnaclSpecRequired), | |
248 error_string)) { | |
249 return false; | |
250 } | |
251 Json::Value url_spec = pnacl_spec[kPnaclTranslateKey]; | |
252 if (!IsValidUrlSpec(url_spec, kPnaclTranslateKey, | |
253 container_key, sandbox_isa, error_string)) { | |
254 return false; | |
255 } | |
256 return true; | |
257 } | |
258 | |
259 // Validates that |dictionary| is a valid ISA dictionary. An ISA dictionary | |
260 // is validated to have keys from within the set of recognized ISAs. Unknown | |
261 // ISAs are allowed, but ignored and warnings are produced. It is also validated | |
262 // that it must have an entry to match the ISA specified in |sandbox_isa| or | |
263 // have a fallback 'portable' entry if there is no match. Returns true if | |
264 // |dictionary| is an ISA to URL map. Sets |error_info| to something | |
265 // descriptive if it fails. | |
266 bool IsValidISADictionary(const Json::Value& dictionary, | |
267 const nacl::string& parent_key, | |
268 const nacl::string& sandbox_isa, | |
269 bool must_find_matching_entry, | |
270 bool nonsfi_enabled, | |
271 ErrorInfo* error_info) { | |
272 if (error_info == NULL) return false; | |
273 | |
274 // An ISA to URL dictionary has to be an object. | |
275 if (!dictionary.isObject()) { | |
276 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
277 nacl::string("manifest: ") + parent_key + | |
278 " property is not an ISA to URL dictionary"); | |
279 return false; | |
280 } | |
281 // Build the set of reserved ISA dictionary keys. | |
282 const char** isaProperties; | |
283 size_t isaPropertiesLength; | |
284 if (sandbox_isa == kPortableKey) { | |
285 // The known values for PNaCl ISA dictionaries in the manifest. | |
286 static const char* kPnaclManifestISAProperties[] = { | |
287 kPortableKey | |
288 }; | |
289 isaProperties = kPnaclManifestISAProperties; | |
290 isaPropertiesLength = NACL_ARRAY_SIZE(kPnaclManifestISAProperties); | |
291 } else { | |
292 // The known values for NaCl ISA dictionaries in the manifest. | |
293 static const char* kNaClManifestISAProperties[] = { | |
294 kX8632Key, | |
295 kX8632NonSFIKey, | |
296 kX8664Key, | |
297 kX8664NonSFIKey, | |
298 kArmKey, | |
299 kArmNonSFIKey, | |
300 // "portable" is here to allow checking that, if present, it can | |
301 // only refer to an URL, such as for a data file, and not to | |
302 // "pnacl-translate", which would cause the creation of a nexe. | |
303 kPortableKey | |
304 }; | |
305 isaProperties = kNaClManifestISAProperties; | |
306 isaPropertiesLength = NACL_ARRAY_SIZE(kNaClManifestISAProperties); | |
307 } | |
308 // Check that entries in the dictionary are structurally correct. | |
309 Json::Value::Members members = dictionary.getMemberNames(); | |
310 for (size_t i = 0; i < members.size(); ++i) { | |
311 nacl::string property_name = members[i]; | |
312 Json::Value property_value = dictionary[property_name]; | |
313 nacl::string error_string; | |
314 if (FindMatchingProperty(property_name, | |
315 isaProperties, | |
316 isaPropertiesLength)) { | |
317 // For NaCl, arch entries can only be | |
318 // "arch/portable" : URLSpec | |
319 // For PNaCl arch in "program" dictionary entries can be | |
320 // "portable" : { "pnacl-translate": URLSpec } | |
321 // or "portable" : { "pnacl-debug": URLSpec } | |
322 // For PNaCl arch elsewhere, dictionary entries can only be | |
323 // "portable" : URLSpec | |
324 if ((sandbox_isa != kPortableKey && | |
325 !IsValidUrlSpec(property_value, property_name, parent_key, | |
326 sandbox_isa, &error_string)) || | |
327 (sandbox_isa == kPortableKey && | |
328 parent_key == kProgramKey && | |
329 !IsValidPnaclTranslateSpec(property_value, property_name, parent_key, | |
330 sandbox_isa, &error_string)) || | |
331 (sandbox_isa == kPortableKey && | |
332 parent_key != kProgramKey && | |
333 !IsValidUrlSpec(property_value, property_name, parent_key, | |
334 sandbox_isa, &error_string))) { | |
335 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
336 nacl::string("manifest: ") + error_string); | |
337 return false; | |
338 } | |
339 } else { | |
340 // For forward compatibility, we do not prohibit other keys being in | |
341 // the dictionary, as they may be architectures supported in later | |
342 // versions. However, the value of these entries must be an URLSpec. | |
343 PLUGIN_PRINTF(("IsValidISADictionary: unrecognized key '%s'.\n", | |
344 property_name.c_str())); | |
345 if (!IsValidUrlSpec(property_value, property_name, parent_key, | |
346 sandbox_isa, &error_string)) { | |
347 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
348 nacl::string("manifest: ") + error_string); | |
349 return false; | |
350 } | |
351 } | |
352 } | |
353 | |
354 if (sandbox_isa == kPortableKey) { | |
355 bool has_portable = dictionary.isMember(kPortableKey); | |
356 | |
357 if (!has_portable) { | |
358 error_info->SetReport( | |
359 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH, | |
360 nacl::string("manifest: no version of ") + parent_key + | |
361 " given for portable."); | |
362 return false; | |
363 } | |
364 } else if (must_find_matching_entry) { | |
365 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include | |
366 // micro-architectures that can resolve to multiple valid sandboxes. | |
367 bool has_isa = dictionary.isMember(sandbox_isa); | |
368 bool has_nonsfi_isa = | |
369 nonsfi_enabled && dictionary.isMember(GetNonSFIKey(sandbox_isa)); | |
370 bool has_portable = dictionary.isMember(kPortableKey); | |
371 | |
372 if (!has_isa && !has_nonsfi_isa && !has_portable) { | |
373 error_info->SetReport( | |
374 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH, | |
375 nacl::string("manifest: no version of ") + parent_key + | |
376 " given for current arch and no portable version found."); | |
377 return false; | |
378 } | |
379 } | |
380 | |
381 return true; | |
382 } | |
383 | |
384 void GrabUrlAndPNaClOptions(const Json::Value& url_spec, | |
385 nacl::string* url, | |
386 PP_PNaClOptions* pnacl_options) { | |
387 *url = url_spec[kUrlKey].asString(); | |
388 pnacl_options->translate = PP_TRUE; | |
389 if (url_spec.isMember(kOptLevelKey)) { | |
390 int32_t opt_raw = url_spec[kOptLevelKey].asInt(); | |
391 int32_t opt_level; | |
392 // Currently only allow 0 or 2, since that is what we test. | |
393 if (opt_raw <= 0) | |
394 opt_level = 0; | |
395 else | |
396 opt_level = 2; | |
397 pnacl_options->opt_level = opt_level; | |
398 } | |
399 } | |
400 | |
401 } // namespace | |
402 | |
403 bool JsonManifest::Init(const nacl::string& manifest_json, | |
404 ErrorInfo* error_info) { | |
405 if (error_info == NULL) { | |
406 return false; | |
407 } | |
408 Json::Reader reader; | |
409 if (!reader.parse(manifest_json, dictionary_)) { | |
410 std::string json_error = reader.getFormatedErrorMessages(); | |
411 error_info->SetReport(PP_NACL_ERROR_MANIFEST_PARSING, | |
412 "manifest JSON parsing failed: " + json_error); | |
413 return false; | |
414 } | |
415 // Parse has ensured the string was valid JSON. Check that it matches the | |
416 // manifest schema. | |
417 return MatchesSchema(error_info); | |
418 } | |
419 | |
420 bool JsonManifest::MatchesSchema(ErrorInfo* error_info) { | |
421 pp::Var exception; | |
422 if (error_info == NULL) { | |
423 return false; | |
424 } | |
425 if (!dictionary_.isObject()) { | |
426 error_info->SetReport( | |
427 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
428 "manifest: is not a json dictionary."); | |
429 return false; | |
430 } | |
431 Json::Value::Members members = dictionary_.getMemberNames(); | |
432 for (size_t i = 0; i < members.size(); ++i) { | |
433 // The top level dictionary entries valid in the manifest file. | |
434 static const char* kManifestTopLevelProperties[] = { kProgramKey, | |
435 kInterpreterKey, | |
436 kFilesKey }; | |
437 nacl::string property_name = members[i]; | |
438 if (!FindMatchingProperty(property_name, | |
439 kManifestTopLevelProperties, | |
440 NACL_ARRAY_SIZE(kManifestTopLevelProperties))) { | |
441 PLUGIN_PRINTF(("JsonManifest::MatchesSchema: WARNING: unknown top-level " | |
442 "section '%s' in manifest.\n", property_name.c_str())); | |
443 } | |
444 } | |
445 | |
446 // A manifest file must have a program section. | |
447 if (!dictionary_.isMember(kProgramKey)) { | |
448 error_info->SetReport( | |
449 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
450 nacl::string("manifest: missing '") + kProgramKey + "' section."); | |
451 return false; | |
452 } | |
453 | |
454 // Validate the program section. | |
455 // There must be a matching (portable or sandbox_isa_) entry for program for | |
456 // NaCl. | |
457 if (!IsValidISADictionary(dictionary_[kProgramKey], | |
458 kProgramKey, | |
459 sandbox_isa_, | |
460 true, | |
461 nonsfi_enabled_, | |
462 error_info)) { | |
463 return false; | |
464 } | |
465 | |
466 // Validate the interpreter section (if given). | |
467 // There must be a matching (portable or sandbox_isa_) entry for interpreter | |
468 // for NaCl. | |
469 if (dictionary_.isMember(kInterpreterKey)) { | |
470 if (!IsValidISADictionary(dictionary_[kInterpreterKey], | |
471 kInterpreterKey, | |
472 sandbox_isa_, | |
473 true, | |
474 nonsfi_enabled_, | |
475 error_info)) { | |
476 return false; | |
477 } | |
478 } | |
479 | |
480 // Validate the file dictionary (if given). | |
481 // The "files" key does not require a matching (portable or sandbox_isa_) | |
482 // entry at schema validation time for NaCl. This allows manifests to specify | |
483 // resources that are only loaded for a particular sandbox_isa. | |
484 if (dictionary_.isMember(kFilesKey)) { | |
485 const Json::Value& files = dictionary_[kFilesKey]; | |
486 if (!files.isObject()) { | |
487 error_info->SetReport( | |
488 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
489 nacl::string("manifest: '") + kFilesKey + "' is not a dictionary."); | |
490 } | |
491 Json::Value::Members members = files.getMemberNames(); | |
492 for (size_t i = 0; i < members.size(); ++i) { | |
493 nacl::string file_name = members[i]; | |
494 if (!IsValidISADictionary(files[file_name], | |
495 file_name, | |
496 sandbox_isa_, | |
497 false, | |
498 nonsfi_enabled_, | |
499 error_info)) { | |
500 return false; | |
501 } | |
502 } | |
503 } | |
504 | |
505 return true; | |
506 } | |
507 | |
508 bool JsonManifest::GetURLFromISADictionary(const Json::Value& dictionary, | |
509 const nacl::string& parent_key, | |
510 nacl::string* url, | |
511 PP_PNaClOptions* pnacl_options, | |
512 bool* uses_nonsfi_mode, | |
513 ErrorInfo* error_info) const { | |
514 DCHECK(url != NULL && pnacl_options != NULL && error_info != NULL); | |
515 | |
516 // When the application actually requests a resolved URL, we must have | |
517 // a matching entry (sandbox_isa_ or portable) for NaCl. | |
518 ErrorInfo ignored_error_info; | |
519 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa_, true, | |
520 nonsfi_enabled_, &ignored_error_info)) { | |
521 error_info->SetReport(PP_NACL_ERROR_MANIFEST_RESOLVE_URL, | |
522 "architecture " + sandbox_isa_ + | |
523 " is not found for file " + parent_key); | |
524 return false; | |
525 } | |
526 | |
527 // The call to IsValidISADictionary() above guarantees that either | |
528 // sandbox_isa_, its nonsfi mode, or kPortableKey is present in the | |
529 // dictionary. | |
530 *uses_nonsfi_mode = false; | |
531 nacl::string chosen_isa; | |
532 if (sandbox_isa_ == kPortableKey) { | |
533 chosen_isa = kPortableKey; | |
534 } else { | |
535 nacl::string nonsfi_isa = GetNonSFIKey(sandbox_isa_); | |
536 if (nonsfi_enabled_ && dictionary.isMember(nonsfi_isa)) { | |
537 chosen_isa = nonsfi_isa; | |
538 *uses_nonsfi_mode = true; | |
539 } else if (dictionary.isMember(sandbox_isa_)) { | |
540 chosen_isa = sandbox_isa_; | |
541 } else if (dictionary.isMember(kPortableKey)) { | |
542 chosen_isa = kPortableKey; | |
543 } else { | |
544 // Should not reach here, because the earlier IsValidISADictionary() | |
545 // call checked that the manifest covers the current architecture. | |
546 DCHECK(false); | |
547 } | |
548 } | |
549 | |
550 const Json::Value& isa_spec = dictionary[chosen_isa]; | |
551 // If the PNaCl debug flag is turned on, look for pnacl-debug entries first. | |
552 // If found, mark that it is a debug URL. Otherwise, fall back to | |
553 // checking for pnacl-translate URLs, etc. and don't mark it as a debug URL. | |
554 if (pnacl_debug_ && isa_spec.isMember(kPnaclDebugKey)) { | |
555 GrabUrlAndPNaClOptions(isa_spec[kPnaclDebugKey], url, pnacl_options); | |
556 pnacl_options->is_debug = PP_TRUE; | |
557 } else if (isa_spec.isMember(kPnaclTranslateKey)) { | |
558 GrabUrlAndPNaClOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options); | |
559 } else { | |
560 // NaCl | |
561 *url = isa_spec[kUrlKey].asString(); | |
562 pnacl_options->translate = PP_FALSE; | |
563 } | |
564 | |
565 return true; | |
566 } | |
567 | |
568 bool JsonManifest::GetKeyUrl(const Json::Value& dictionary, | |
569 const nacl::string& key, | |
570 nacl::string* full_url, | |
571 PP_PNaClOptions* pnacl_options) const { | |
572 DCHECK(full_url != NULL && pnacl_options != NULL); | |
573 if (!dictionary.isMember(key)) { | |
574 PLUGIN_PRINTF(("file key not found in manifest")); | |
575 return false; | |
576 } | |
577 const Json::Value& isa_dict = dictionary[key]; | |
578 nacl::string relative_url; | |
579 bool uses_nonsfi_mode; | |
580 | |
581 // We ignore the error_info we receive here but it's needed for the calls | |
582 // below. | |
583 ErrorInfo error_info; | |
584 | |
585 if (!GetURLFromISADictionary(isa_dict, key, &relative_url, | |
586 pnacl_options, &uses_nonsfi_mode, &error_info)) { | |
587 return false; | |
588 } | |
589 return ResolveURL(relative_url, full_url, &error_info); | |
590 } | |
591 | |
592 bool JsonManifest::GetProgramURL(nacl::string* full_url, | |
593 PP_PNaClOptions* pnacl_options, | |
594 bool* uses_nonsfi_mode, | |
595 ErrorInfo* error_info) const { | |
596 if (full_url == NULL || pnacl_options == NULL || error_info == NULL) | |
597 return false; | |
598 | |
599 const Json::Value& program = dictionary_[kProgramKey]; | |
600 nacl::string nexe_url; | |
601 if (!GetURLFromISADictionary(program, | |
602 kProgramKey, | |
603 &nexe_url, | |
604 pnacl_options, | |
605 uses_nonsfi_mode, | |
606 error_info)) { | |
607 return false; | |
608 } | |
609 return ResolveURL(nexe_url, full_url, error_info); | |
610 } | |
611 | |
612 bool JsonManifest::ResolveKey(const nacl::string& key, | |
613 nacl::string* full_url, | |
614 PP_PNaClOptions* pnacl_options) const { | |
615 NaClLog(3, "JsonManifest::ResolveKey(%s)\n", key.c_str()); | |
616 // key must be one of kProgramKey or kFileKey '/' file-section-key | |
617 | |
618 if (full_url == NULL || pnacl_options == NULL) | |
619 return false; | |
620 | |
621 if (key == kProgramKey) | |
622 return GetKeyUrl(dictionary_, key, full_url, pnacl_options); | |
623 nacl::string::const_iterator p = find(key.begin(), key.end(), '/'); | |
624 if (p == key.end()) { | |
625 std::string err = "ResolveKey: invalid key, no slash: " + key; | |
626 PLUGIN_PRINTF((err.c_str())); | |
627 return false; | |
628 } | |
629 | |
630 // generalize to permit other sections? | |
631 nacl::string prefix(key.begin(), p); | |
632 if (prefix != kFilesKey) { | |
633 std::string err = "ResolveKey: invalid key: no \"files\" prefix: " + key; | |
634 PLUGIN_PRINTF((err.c_str())); | |
635 return false; | |
636 } | |
637 | |
638 nacl::string rest(p + 1, key.end()); | |
639 | |
640 const Json::Value& files = dictionary_[kFilesKey]; | |
641 if (!files.isObject()) { | |
642 std::string err = "ResolveKey: no \"files\" dictionary"; | |
643 PLUGIN_PRINTF((err.c_str())); | |
644 return false; | |
645 } | |
646 if (!files.isMember(rest)) { | |
647 std::string err = "ResolveKey: no such \"files\" entry: " + key; | |
648 PLUGIN_PRINTF((err.c_str())); | |
649 return false; | |
650 } | |
651 return GetKeyUrl(files, rest, full_url, pnacl_options); | |
652 } | |
653 | |
654 bool JsonManifest::ResolveURL(const nacl::string& relative_url, | |
655 nacl::string* full_url, | |
656 ErrorInfo* error_info) const { | |
657 // The contents of the manifest are resolved relative to the manifest URL. | |
658 CHECK(url_util_ != NULL); | |
659 pp::Var resolved_url = | |
660 url_util_->ResolveRelativeToURL(pp::Var(manifest_base_url_), | |
661 relative_url); | |
662 if (!resolved_url.is_string()) { | |
663 error_info->SetReport( | |
664 PP_NACL_ERROR_MANIFEST_RESOLVE_URL, | |
665 "could not resolve url '" + relative_url + | |
666 "' relative to manifest base url '" + manifest_base_url_.c_str() + | |
667 "'."); | |
668 return false; | |
669 } | |
670 *full_url = resolved_url.AsString(); | |
671 return true; | |
672 } | |
673 | |
674 } // namespace plugin | |
OLD | NEW |