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