| // Copyright 2015 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 <fcntl.h> |
| |
| #include "base/files/file_util.h" |
| #include "base/sha1.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "mojo/application/application_runner_chromium.h" |
| #include "mojo/application/content_handler_factory.h" |
| #include "mojo/data_pipe_utils/data_pipe_utils.h" |
| #include "mojo/file_utils/file_util.h" |
| #include "mojo/message_pump/message_pump_mojo.h" |
| #include "mojo/nacl/nonsfi/file_util.h" |
| #include "mojo/nacl/nonsfi/nexe_launcher_nonsfi.h" |
| #include "mojo/public/c/system/main.h" |
| #include "mojo/public/cpp/application/application_impl.h" |
| #include "mojo/public/cpp/application/connect.h" |
| #include "mojo/public/cpp/bindings/array.h" |
| #include "mojo/public/cpp/bindings/synchronous_interface_ptr.h" |
| #include "mojo/services/files/interfaces/directory.mojom-sync.h" |
| #include "mojo/services/files/interfaces/files.mojom.h" |
| #include "services/nacl/nonsfi/pnacl_compile.mojom-sync.h" |
| #include "services/nacl/nonsfi/pnacl_link.mojom-sync.h" |
| |
| namespace nacl { |
| namespace content_handler { |
| |
| class PexeContentHandler : public mojo::ApplicationDelegate, |
| public mojo::ContentHandlerFactory::Delegate { |
| public: |
| PexeContentHandler() : content_handler_factory_(this) {} |
| |
| private: |
| // Overridden from ApplicationDelegate: |
| void Initialize(mojo::ApplicationImpl* app) override { |
| mojo::ConnectToService(app->shell(), "mojo:pnacl_compile", |
| GetSynchronousProxy(&compiler_init_)); |
| mojo::ConnectToService(app->shell(), "mojo:pnacl_link", |
| GetSynchronousProxy(&linker_init_)); |
| mojo::ConnectToService(app->shell(), "mojo:files", GetProxy(&files_)); |
| mojo::files::Error error = mojo::files::Error::INTERNAL; |
| files_->OpenFileSystem("app_persistent_cache", |
| GetSynchronousProxy(&nexe_cache_directory_), |
| [&error](mojo::files::Error e) { error = e; }); |
| CHECK(files_.WaitForIncomingResponse()); |
| CHECK_EQ(mojo::files::Error::OK, error); |
| } |
| |
| // Overridden from ApplicationDelegate: |
| bool ConfigureIncomingConnection( |
| mojo::ApplicationConnection* connection) override { |
| connection->GetServiceProviderImpl().AddService<mojo::ContentHandler>( |
| content_handler_factory_.GetInterfaceRequestHandler()); |
| return true; |
| } |
| |
| int AccessFileFromCache(std::string& digest) { |
| mojo::files::Error error = mojo::files::Error::INTERNAL; |
| mojo::files::FilePtr nexe_cache_file; |
| CHECK(nexe_cache_directory_->OpenFile(digest, GetProxy(&nexe_cache_file), |
| mojo::files::kOpenFlagRead, &error)); |
| if (mojo::files::Error::OK == error) |
| // Copy the mojo cached file into an open temporary file. |
| return ::nacl::MojoFileToTempFileDescriptor(nexe_cache_file.Pass()); |
| else |
| // If error != OK, The failure may have been for a variety of reasons -- |
| // assume that the file does not exist. |
| return -1; |
| } |
| |
| void StoreFileInCache(int nexe_fd, std::string& digest) { |
| // First, open a "temporary" file. |
| mojo::files::Error error = mojo::files::Error::INTERNAL; |
| std::string temp_file_name; |
| auto nexe_cache_file = |
| mojo::files::FilePtr::Create(file_utils::CreateTemporaryFileInDir( |
| &nexe_cache_directory_, &temp_file_name)); |
| CHECK(nexe_cache_file); |
| |
| // Copy the contents of nexe_fd into the temporary Mojo file. |
| FileDescriptorToMojoFile(nexe_fd, nexe_cache_file.Pass()); |
| |
| // The file is named after the hash of the requesting pexe. |
| // This makes it usable by future requests for the same pexe under different |
| // names. It also atomically moves the entire temp file. |
| CHECK(nexe_cache_directory_->Rename(temp_file_name, digest, &error)); |
| CHECK_EQ(mojo::files::Error::OK, error); |
| } |
| |
| int DoPexeTranslation(base::FilePath& pexe_file_path) { |
| // Compile the pexe into an object file |
| mojo::SynchronousInterfacePtr<mojo::nacl::PexeCompiler> compiler; |
| CHECK(compiler_init_->PexeCompilerStart(GetSynchronousProxy(&compiler))); |
| |
| // Communicate with the compiler using a mojom interface. |
| mojo::Array<mojo::String> object_files; |
| CHECK(compiler->PexeCompile(pexe_file_path.value(), &object_files)); |
| |
| // Link the object file into a nexe |
| mojo::SynchronousInterfacePtr<mojo::nacl::PexeLinker> linker; |
| CHECK(linker_init_->PexeLinkerStart(GetSynchronousProxy(&linker))); |
| |
| // Communicate with the linker using a mojom interface. |
| mojo::String nexe_file; |
| CHECK(linker->PexeLink(std::move(object_files), &nexe_file)); |
| |
| // Open the nexe file and launch it (with our mojo handle) |
| int nexe_fd = open(nexe_file.get().c_str(), O_RDONLY); |
| CHECK(!unlink(nexe_file.get().c_str())) |
| << "Could not unlink temporary nexe file"; |
| CHECK_GE(nexe_fd, 0) << "Could not open nexe object file"; |
| return nexe_fd; |
| } |
| |
| // Overridden from ContentHandlerFactory::Delegate: |
| void RunApplication( |
| mojo::InterfaceRequest<mojo::Application> application_request, |
| mojo::URLResponsePtr response) override { |
| // Needed to use Mojo interfaces on this thread. |
| base::MessageLoop loop(mojo::common::MessagePumpMojo::Create()); |
| // Create temporary file for pexe |
| base::FilePath pexe_file_path; |
| FILE* pexe_fp = CreateAndOpenTemporaryFile(&pexe_file_path); |
| CHECK(pexe_fp) << "Could not create temporary file for pexe"; |
| // Acquire the pexe. |
| CHECK(mojo::common::BlockingCopyToFile(response->body.Pass(), pexe_fp)) |
| << "Could not copy pexe to file"; |
| CHECK_EQ(fclose(pexe_fp), 0) << "Could not close pexe file"; |
| |
| // Try to access the translated pexe from the cache based on the |
| // SHA1 hash of the pexe itself. |
| unsigned char raw_hash[base::kSHA1Length]; |
| CHECK(base::SHA1HashFile(pexe_file_path.value().c_str(), raw_hash)); |
| |
| std::string digest = base::HexEncode(raw_hash, sizeof(raw_hash)); |
| int nexe_fd = AccessFileFromCache(digest); |
| if (nexe_fd == -1) { |
| nexe_fd = DoPexeTranslation(pexe_file_path); |
| // Store the nexe in the cache for the next translation |
| StoreFileInCache(nexe_fd, digest); |
| } |
| |
| // Pass the handle connecting us with mojo_shell to the nexe. |
| MojoHandle handle = application_request.PassMessagePipe().release().value(); |
| ::nacl::MojoLaunchNexeNonsfi(nexe_fd, handle, |
| false /* enable_translation_irt */); |
| } |
| |
| private: |
| mojo::SynchronousInterfacePtr<mojo::files::Directory> nexe_cache_directory_; |
| mojo::files::FilesPtr files_; |
| mojo::ContentHandlerFactory content_handler_factory_; |
| mojo::SynchronousInterfacePtr<mojo::nacl::PexeCompilerInit> compiler_init_; |
| mojo::SynchronousInterfacePtr<mojo::nacl::PexeLinkerInit> linker_init_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PexeContentHandler); |
| }; |
| |
| } // namespace content_handler |
| } // namespace nacl |
| |
| MojoResult MojoMain(MojoHandle application_request) { |
| mojo::ApplicationRunnerChromium runner( |
| new nacl::content_handler::PexeContentHandler()); |
| return runner.Run(application_request); |
| } |