scoped_dev_zero_fuchsia.cc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Copyright 2021 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. #include "base/test/scoped_dev_zero_fuchsia.h"
  5. #include <fuchsia/io/cpp/fidl.h>
  6. #include <lib/fdio/namespace.h>
  7. #include <lib/fidl/cpp/interface_request.h>
  8. #include <lib/vfs/cpp/pseudo_dir.h>
  9. #include <lib/vfs/cpp/vmo_file.h>
  10. #include <lib/zx/channel.h>
  11. #include <lib/zx/vmo.h>
  12. #include <stdint.h>
  13. #include <zircon/types.h>
  14. #include <functional>
  15. #include <utility>
  16. #include "base/bind.h"
  17. #include "base/callback.h"
  18. #include "base/check.h"
  19. #include "base/fuchsia/fuchsia_logging.h"
  20. #include "base/message_loop/message_pump_type.h"
  21. #include "base/run_loop.h"
  22. namespace base {
  23. // ScopedDevZero::Server -------------------------------------------------------
  24. // A helper that lives on a dedicated thread, serving up a pesudo-dir containing
  25. // a "zero" file.
  26. class ScopedDevZero::Server {
  27. public:
  28. // Creates the pseudo-dir representing /dev as `directory_request` and serves
  29. // up a "zero" file within it. `on_initialized` is run with the status.
  30. Server(fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
  31. OnceCallback<void(zx_status_t status)> on_initialized);
  32. Server(const Server&) = delete;
  33. Server& operator=(const Server&) = delete;
  34. ~Server() = default;
  35. private:
  36. vfs::PseudoDir dev_dir_;
  37. };
  38. ScopedDevZero::Server::Server(
  39. fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
  40. OnceCallback<void(zx_status_t status)> on_initialized) {
  41. // VMOs are filled with zeros at construction, so create a big one and serve
  42. // it as "zero" within the given `directory_request`. All virtual pages in the
  43. // VMO are backed by the singular physical "zero page", so no memory is
  44. // allocated until a write occurs (which will never happen). On the server
  45. // end, the VMO should not take up address space on account of never being
  46. // mapped. On the read side (libfdio) it may get mapped, but only for the size
  47. // of a given read - it may also just use the zx_vmo_read syscall to avoid
  48. // ever needing to map it.
  49. zx::vmo vmo;
  50. auto status = zx::vmo::create(/*size=*/UINT32_MAX, /*options=*/0, &vmo);
  51. ZX_LOG_IF(ERROR, status != ZX_OK, status);
  52. if (status == ZX_OK) {
  53. status = dev_dir_.AddEntry(
  54. "zero",
  55. std::make_unique<vfs::VmoFile>(std::move(vmo), /*length=*/UINT32_MAX));
  56. ZX_LOG_IF(ERROR, status != ZX_OK, status);
  57. }
  58. if (status == ZX_OK) {
  59. status = dev_dir_.Serve(fuchsia::io::OpenFlags::RIGHT_READABLE,
  60. directory_request.TakeChannel());
  61. ZX_LOG_IF(ERROR, status != ZX_OK, status);
  62. }
  63. std::move(on_initialized).Run(status);
  64. }
  65. // ScopedDevZero ---------------------------------------------------------------
  66. // static
  67. ScopedDevZero* ScopedDevZero::instance_ = nullptr;
  68. // static
  69. scoped_refptr<ScopedDevZero> ScopedDevZero::Get() {
  70. if (instance_)
  71. return WrapRefCounted(instance_);
  72. scoped_refptr<ScopedDevZero> result = AdoptRef(new ScopedDevZero);
  73. return result->Initialize() ? std::move(result) : nullptr;
  74. }
  75. ScopedDevZero::ScopedDevZero() : io_thread_("/dev/zero") {
  76. DCHECK_EQ(instance_, nullptr);
  77. instance_ = this;
  78. }
  79. ScopedDevZero::~ScopedDevZero() {
  80. DCHECK_EQ(instance_, this);
  81. if (global_namespace_)
  82. fdio_ns_unbind(std::exchange(global_namespace_, nullptr), "/dev");
  83. instance_ = nullptr;
  84. }
  85. bool ScopedDevZero::Initialize() {
  86. auto status = fdio_ns_get_installed(&global_namespace_);
  87. if (status != ZX_OK) {
  88. ZX_LOG(ERROR, status);
  89. return false;
  90. }
  91. if (!io_thread_.StartWithOptions(Thread::Options(MessagePumpType::IO, 0)))
  92. return false;
  93. zx::channel client;
  94. zx::channel request;
  95. status = zx::channel::create(0, &client, &request);
  96. ZX_CHECK(status == ZX_OK, status);
  97. RunLoop run_loop;
  98. server_ = SequenceBound<Server>(
  99. io_thread_.task_runner(),
  100. fidl::InterfaceRequest<fuchsia::io::Directory>(std::move(request)),
  101. base::BindOnce(
  102. [](base::OnceClosure quit_loop, zx_status_t& status,
  103. zx_status_t init_status) {
  104. status = init_status;
  105. std::move(quit_loop).Run();
  106. },
  107. run_loop.QuitClosure(), std::ref(status)));
  108. run_loop.Run();
  109. if (status != ZX_OK)
  110. return false;
  111. // Install the directory holding "zero" into the global namespace as /dev.
  112. // This relies on the component not asking for any /dev entries in its
  113. // manifest, as nested namespaces are not allowed.
  114. status = fdio_ns_bind(global_namespace_, "/dev", client.release());
  115. if (status != ZX_OK) {
  116. ZX_LOG(ERROR, status);
  117. global_namespace_ = nullptr;
  118. server_.Reset();
  119. return false;
  120. }
  121. return true;
  122. }
  123. } // namespace base