1 // Copyright 2018 The Fuchsia 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
5 #include <fbl/algorithm.h>
6 #include <fs/connection.h>
7 #include <fuchsia/io/c/fidl.h>
8 #include <lib/fdio/namespace.h>
9 #include <lib/fdio/util.h>
10 #include <lib/zx/channel.h>
11 #include <unittest/unittest.h>
12 #include <zircon/device/vfs.h>
13 #include <zircon/syscalls.h>
14
15 #include <utility>
16
17 namespace {
18
OpenHelper(const zx::channel & directory,const char * path,zx::channel * response_channel)19 bool OpenHelper(const zx::channel& directory, const char* path, zx::channel* response_channel) {
20 BEGIN_HELPER;
21
22 // Open the requested path from the provded directory, and wait for the open
23 // response on the accompanying channel.
24 zx::channel client, server;
25 ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
26 ASSERT_EQ(fuchsia_io_DirectoryOpen(directory.get(), ZX_FS_RIGHT_READABLE | ZX_FS_FLAG_DESCRIBE,
27 0, path, strlen(path), server.release()), ZX_OK);
28 zx_signals_t pending;
29 ASSERT_EQ(client.wait_one(ZX_CHANNEL_PEER_CLOSED | ZX_CHANNEL_READABLE,
30 zx::deadline_after(zx::sec(1)), &pending), ZX_OK);
31 ASSERT_EQ(pending & ZX_CHANNEL_READABLE, ZX_CHANNEL_READABLE);
32 *response_channel = std::move(client);
33
34 END_HELPER;
35 }
36
37 // Validate some size information and expected fields without fully decoding the
38 // FIDL message, for opening a path from a directory where we expect to open successfully.
FidlOpenValidator(const zx::channel & directory,const char * path,fidl_union_tag_t expected_tag,size_t expected_handles)39 bool FidlOpenValidator(const zx::channel& directory, const char* path,
40 fidl_union_tag_t expected_tag, size_t expected_handles) {
41 BEGIN_HELPER;
42
43 zx::channel client;
44 ASSERT_TRUE(OpenHelper(directory, path, &client));
45
46 char buf[8192];
47 zx_handle_t handles[4];
48 uint32_t actual_bytes;
49 uint32_t actual_handles;
50 ASSERT_EQ(client.read(0, buf, sizeof(buf), &actual_bytes, handles, fbl::count_of(handles),
51 &actual_handles), ZX_OK);
52 ASSERT_EQ(actual_bytes, sizeof(fs::OnOpenMsg));
53 ASSERT_EQ(actual_handles, expected_handles);
54 auto response = reinterpret_cast<fs::OnOpenMsg*>(buf);
55 ASSERT_EQ(response->primary.hdr.ordinal, fuchsia_io_NodeOnOpenOrdinal);
56 ASSERT_EQ(response->primary.s, ZX_OK);
57 ASSERT_EQ(response->extra.tag, expected_tag);
58 zx_handle_close_many(handles, actual_handles);
59
60 END_HELPER;
61 }
62
63 // Validate some size information and expected fields without fully decoding the
64 // FIDL message, for opening a path from a directory where we expect to fail.
FidlOpenErrorValidator(const zx::channel & directory)65 bool FidlOpenErrorValidator(const zx::channel& directory) {
66 BEGIN_HELPER;
67
68 const char* path = "this-path-better-not-actually-exist";
69 zx::channel client;
70 ASSERT_TRUE(OpenHelper(directory, path, &client));
71
72 char buf[8192];
73 zx_handle_t handles[4];
74 uint32_t actual_bytes;
75 uint32_t actual_handles;
76 ASSERT_EQ(client.read(0, buf, sizeof(buf), &actual_bytes, handles, fbl::count_of(handles),
77 &actual_handles), ZX_OK);
78 ASSERT_EQ(actual_bytes, sizeof(fuchsia_io_NodeOnOpenEvent));
79 ASSERT_EQ(actual_handles, 0);
80 auto response = reinterpret_cast<fuchsia_io_NodeOnOpenEvent*>(buf);
81 ASSERT_EQ(response->hdr.ordinal, fuchsia_io_NodeOnOpenOrdinal);
82 ASSERT_EQ(response->s, ZX_ERR_NOT_FOUND);
83
84 END_HELPER;
85 }
86
87 // Ensure that our hand-rolled FIDL messages within devfs and memfs are acting correctly
88 // for open event messages (on both success and error).
TestFidlOpen()89 bool TestFidlOpen() {
90 BEGIN_TEST;
91
92 {
93 zx::channel dev_client, dev_server;
94 ASSERT_EQ(zx::channel::create(0, &dev_client, &dev_server), ZX_OK);
95 fdio_ns_t* ns;
96 ASSERT_EQ(fdio_ns_get_installed(&ns), ZX_OK);
97 ASSERT_EQ(fdio_ns_connect(ns, "/dev", ZX_FS_RIGHT_READABLE, dev_server.release()), ZX_OK);
98 ASSERT_TRUE(FidlOpenValidator(dev_client, "zero", fuchsia_io_NodeInfoTag_device, 1));
99 ASSERT_TRUE(FidlOpenErrorValidator(dev_client));
100 }
101
102 {
103 zx::channel dev_client, dev_server;
104 ASSERT_EQ(zx::channel::create(0, &dev_client, &dev_server), ZX_OK);
105 fdio_ns_t* ns;
106 ASSERT_EQ(fdio_ns_get_installed(&ns), ZX_OK);
107 ASSERT_EQ(fdio_ns_connect(ns, "/boot", ZX_FS_RIGHT_READABLE, dev_server.release()), ZX_OK);
108 ASSERT_TRUE(FidlOpenValidator(dev_client, "lib", fuchsia_io_NodeInfoTag_directory, 0));
109 ASSERT_TRUE(FidlOpenErrorValidator(dev_client));
110 }
111
112 END_TEST;
113 }
114
TestFidlBasic()115 bool TestFidlBasic() {
116 BEGIN_TEST;
117
118 fuchsia_io_NodeInfo info = {};
119 {
120 zx::channel client, server;
121 ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
122 ASSERT_EQ(fdio_service_connect("/dev/class", server.release()), ZX_OK);
123 memset(&info, 0, sizeof(info));
124 ASSERT_EQ(fuchsia_io_FileDescribe(client.get(), &info), ZX_OK);
125 ASSERT_EQ(info.tag, fuchsia_io_NodeInfoTag_directory);
126 }
127
128 {
129 zx::channel client, server;
130 ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
131 ASSERT_EQ(fdio_service_connect("/dev/zero", server.release()), ZX_OK);
132 memset(&info, 0, sizeof(info));
133 ASSERT_EQ(fuchsia_io_FileDescribe(client.get(), &info), ZX_OK);
134 ASSERT_EQ(info.tag, fuchsia_io_NodeInfoTag_device);
135 ASSERT_NE(info.device.event, ZX_HANDLE_INVALID);
136 zx_handle_close(info.device.event);
137 }
138
139 END_TEST;
140 }
141
142 typedef struct {
143 // Buffer containing cached messages
144 uint8_t buf[fuchsia_io_MAX_BUF];
145 uint8_t name_buf[fuchsia_io_MAX_FILENAME + 1];
146 // Offset into 'buf' of next message
147 uint8_t* ptr;
148 // Maximum size of buffer
149 size_t size;
150 } watch_buffer_t;
151
CheckLocalEvent(watch_buffer_t * wb,const char ** name,uint8_t * event)152 bool CheckLocalEvent(watch_buffer_t* wb, const char** name, uint8_t* event) {
153 if (wb->ptr != nullptr) {
154 // Used a cached event
155 *event = wb->ptr[0];
156 ASSERT_LT(static_cast<size_t>(wb->ptr[1]), sizeof(wb->name_buf));
157 memcpy(wb->name_buf, wb->ptr + 2, wb->ptr[1]);
158 wb->name_buf[wb->ptr[1]] = 0;
159 *name = reinterpret_cast<const char*>(wb->name_buf);
160 wb->ptr += wb->ptr[1] + 2;
161 ASSERT_LE((uintptr_t)wb->ptr, (uintptr_t) wb->buf + wb->size);
162 if ((uintptr_t) wb->ptr == (uintptr_t) wb->buf + wb->size) {
163 wb->ptr = nullptr;
164 }
165 return true;
166 }
167 return false;
168 }
169
170 // Read the next event off the channel. Storage for |*name| will be reused
171 // between calls.
ReadEvent(watch_buffer_t * wb,const zx::channel & c,const char ** name,uint8_t * event)172 bool ReadEvent(watch_buffer_t* wb, const zx::channel& c, const char** name,
173 uint8_t* event) {
174 if (wb->ptr == nullptr) {
175 zx_signals_t observed;
176 ASSERT_EQ(c.wait_one(ZX_CHANNEL_READABLE, zx::deadline_after(zx::sec(5)), &observed),
177 ZX_OK);
178 ASSERT_EQ(observed & ZX_CHANNEL_READABLE, ZX_CHANNEL_READABLE);
179 uint32_t actual;
180 ASSERT_EQ(c.read(0, wb->buf, sizeof(wb->buf), &actual, nullptr, 0, nullptr), ZX_OK);
181 wb->size = actual;
182 wb->ptr = wb->buf;
183 }
184 return CheckLocalEvent(wb, name, event);
185 }
186
TestDirectoryWatcherExisting()187 bool TestDirectoryWatcherExisting() {
188 BEGIN_TEST;
189
190 // Channel pair for fuchsia.io.Directory interface
191 zx::channel h, request;
192 // Channel pair for directory watch events
193 zx::channel watcher, remote_watcher;
194
195 ASSERT_EQ(zx::channel::create(0, &h, &request), ZX_OK);
196 ASSERT_EQ(zx::channel::create(0, &watcher, &remote_watcher), ZX_OK);
197 ASSERT_EQ(fdio_service_connect("/dev/class", request.release()), ZX_OK);
198
199 zx_status_t status;
200 ASSERT_EQ(fuchsia_io_DirectoryWatch(h.get(), fuchsia_io_WATCH_MASK_ALL, 0,
201 remote_watcher.release(), &status), ZX_OK);
202 ASSERT_EQ(status, ZX_OK);
203
204 watch_buffer_t wb = {};
205 // We should see nothing but EXISTING events until we see an IDLE event
206 while (1) {
207 const char* name = nullptr;
208 uint8_t event = 0;
209 ASSERT_TRUE(ReadEvent(&wb, watcher, &name, &event));
210 if (event == fuchsia_io_WATCH_EVENT_IDLE) {
211 ASSERT_STR_EQ(name, "");
212 break;
213 }
214 ASSERT_EQ(event, fuchsia_io_WATCH_EVENT_EXISTING);
215 ASSERT_STR_NE(name, "");
216 }
217
218 END_TEST;
219 }
220
TestDirectoryWatcherWithClosedHalf()221 bool TestDirectoryWatcherWithClosedHalf() {
222 BEGIN_TEST;
223
224 // Channel pair for fuchsia.io.Directory interface
225 zx::channel h, request;
226 // Channel pair for directory watch events
227 zx::channel watcher, remote_watcher;
228
229 ASSERT_EQ(zx::channel::create(0, &h, &request), ZX_OK);
230 ASSERT_EQ(zx::channel::create(0, &watcher, &remote_watcher), ZX_OK);
231 ASSERT_EQ(fdio_service_connect("/dev/class", request.release()), ZX_OK);
232
233 // Close our half of the watcher before devmgr gets its half.
234 watcher.reset();
235
236 zx_status_t status;
237 ASSERT_EQ(fuchsia_io_DirectoryWatch(h.get(), fuchsia_io_WATCH_MASK_ALL, 0,
238 remote_watcher.release(), &status), ZX_OK);
239 ASSERT_EQ(status, ZX_OK);
240 // If we're here and usermode didn't crash, we didn't hit the bug.
241
242 // Create a new watcher, and see if it's functional at all
243 ASSERT_EQ(zx::channel::create(0, &watcher, &remote_watcher), ZX_OK);
244 ASSERT_EQ(fuchsia_io_DirectoryWatch(h.get(), fuchsia_io_WATCH_MASK_ALL, 0,
245 remote_watcher.release(), &status), ZX_OK);
246 ASSERT_EQ(status, ZX_OK);
247
248 watch_buffer_t wb = {};
249 const char* name = nullptr;
250 uint8_t event = 0;
251 ASSERT_TRUE(ReadEvent(&wb, watcher, &name, &event));
252 ASSERT_EQ(event, fuchsia_io_WATCH_EVENT_EXISTING);
253
254 END_TEST;
255 }
256
257 } // namespace
258
259 BEGIN_TEST_CASE(fidl_tests)
260 RUN_TEST(TestFidlOpen)
261 RUN_TEST(TestFidlBasic)
262 RUN_TEST(TestDirectoryWatcherWithClosedHalf)
263 RUN_TEST(TestDirectoryWatcherExisting)
264 END_TEST_CASE(fidl_tests)
265