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