| // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <pthread.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| |
| #include <sstream> |
| #include <string> |
| |
| #include "native_client/src/untrusted/irt/irt.h" |
| |
| #include "ppapi/cpp/completion_callback.h" |
| #include "ppapi/cpp/instance.h" |
| #include "ppapi/cpp/module.h" |
| #include "ppapi/cpp/tcp_socket.h" |
| #include "ppapi/cpp/var.h" |
| #include "ppapi/utility/completion_callback_factory.h" |
| |
| #if defined(__clang__) |
| // ipc_message_attachment_set.h depends on C++11 which nacl-g++ does not |
| // fully support. |
| #include "ipc/ipc_message_attachment_set.h" |
| #endif |
| |
| namespace { |
| |
| std::string g_last_error; |
| pp::Instance* g_instance = NULL; |
| |
| // This should be larger than or equal to |
| // MessageAttachmentSet::kMaxDescriptorsPerMessage in |
| // ipc/ipc_message_attachment_set.h. |
| const size_t kMaxDescriptorsPerMessage = 128; |
| |
| #if defined(__clang__) |
| static_assert(kMaxDescriptorsPerMessage >= |
| IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage, |
| "kMaxDescriptorsPerMessage is not up to date"); |
| #endif |
| |
| // Returns true if the resource file whose name is |key| exists and its content |
| // matches |content|. |
| bool LoadManifestInternal(nacl_irt_resource_open* nacl_irt_resource_open, |
| const std::string& key, |
| const std::string& content) { |
| int desc; |
| int error; |
| error = nacl_irt_resource_open->open_resource(key.c_str(), &desc); |
| if (0 != error) { |
| g_last_error = "Can't open file " + key; |
| return false; |
| } |
| |
| std::string str; |
| |
| char buffer[4096]; |
| int len; |
| while ((len = read(desc, buffer, sizeof(buffer) - 1)) > 0) { |
| // Null terminate. |
| buffer[len] = '\0'; |
| str += buffer; |
| } |
| if (close(desc)) { |
| g_last_error = "Close failed: file=" + key; |
| return false; |
| } |
| |
| if (str != content) { |
| g_last_error = "Wrong file content: file=" + key + ", expected=" + content + |
| ", actual=" + str; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Tests if open_resource works in a packaged app. This test is similar to |
| // NaClBrowserTest*.IrtManifestFile, but unlike the NaCl test, this one tests |
| // the "fast path" in DownloadNexe() in ppb_nacl_private_impl.cc which opens |
| // resource files without using URLLoader. |
| void LoadManifest() { |
| if (pthread_detach(pthread_self())) { |
| g_last_error = "pthread_detach failed"; |
| return; |
| } |
| |
| struct nacl_irt_resource_open nacl_irt_resource_open; |
| if (sizeof(nacl_irt_resource_open) != |
| nacl_interface_query(NACL_IRT_RESOURCE_OPEN_v0_1, |
| &nacl_irt_resource_open, |
| sizeof(nacl_irt_resource_open))) { |
| g_last_error = "NACL_IRT_RESOURCE_OPEN_v0_1 not found"; |
| return; |
| } |
| |
| for (size_t i = 0; i <= kMaxDescriptorsPerMessage; ++i) { |
| std::stringstream key; |
| key << "test_file" << i; |
| std::string content = "Example contents for open_resource test" + |
| std::string(i % 2 ? "2" : ""); |
| if (!LoadManifestInternal(&nacl_irt_resource_open, key.str(), content)) |
| break; |
| // Open the same resource file again to make sure each file descriptor |
| // returned from open_resource has its own file offset. |
| if (!LoadManifestInternal(&nacl_irt_resource_open, key.str(), content)) |
| break; |
| } |
| } |
| |
| void PostReply(void* user_data, int32_t status) { |
| if (!g_last_error.empty()) |
| g_instance->PostMessage(g_last_error.c_str()); |
| else |
| g_instance->PostMessage("PASS"); |
| } |
| |
| void* RunTestsOnBackgroundThread(void* thread_id) { |
| LoadManifest(); |
| pp::Module::Get()->core()->CallOnMainThread( |
| 0, pp::CompletionCallback(&PostReply, NULL)); |
| return NULL; |
| } |
| |
| class MyInstance : public pp::Instance { |
| public: |
| explicit MyInstance(PP_Instance instance) |
| : pp::Instance(instance), socket_(this), factory_(this) { |
| g_instance = this; |
| } |
| virtual ~MyInstance() { } |
| |
| void DidBindSocket(int32_t result) { |
| // We didn't ask for socket permission in our manifest, so it should fail. |
| if (result == PP_ERROR_NOACCESS) |
| PostMessage("PASS"); |
| else |
| PostMessage(result); |
| } |
| |
| virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { |
| pthread_t thread; |
| // irt_open_resource() isn't allowed to be called on the main thread once |
| // Pepper starts, so the test must happen on a background thread. |
| if (pthread_create(&thread, NULL, &RunTestsOnBackgroundThread, NULL)) { |
| g_last_error = "pthread_create failed"; |
| PostReply(NULL, 0); |
| } |
| // Attempt to bind a socket. We don't have permissions, so it should fail. |
| PP_NetAddress_IPv4 ipv4_address = {80, {127, 0, 0, 1} }; |
| pp::NetAddress address(this, ipv4_address); |
| socket_.Bind(address, factory_.NewCallback(&MyInstance::DidBindSocket)); |
| return true; |
| } |
| |
| private: |
| pp::TCPSocket socket_; |
| pp::CompletionCallbackFactory<MyInstance> factory_; |
| }; |
| |
| class MyModule : public pp::Module { |
| public: |
| MyModule() : pp::Module() { } |
| virtual ~MyModule() { } |
| |
| virtual pp::Instance* CreateInstance(PP_Instance instance) { |
| return new MyInstance(instance); |
| } |
| }; |
| |
| } // namespace |
| |
| namespace pp { |
| |
| Module* CreateModule() { |
| return new MyModule(); |
| } |
| |
| } // namespace pp |