1 // Copyright 2013 The Flutter 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 "system.h"
6
7 #include <array>
8
9 #include <fcntl.h>
10 #include <lib/fdio/directory.h>
11 #include <lib/fdio/io.h>
12 #include <lib/fdio/limits.h>
13 #include <lib/fdio/namespace.h>
14 #include <lib/zx/channel.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <zircon/process.h>
18 #include <zircon/processargs.h>
19
20 #include "flutter/fml/unique_fd.h"
21 #include "third_party/tonic/dart_binding_macros.h"
22 #include "third_party/tonic/dart_class_library.h"
23
24 #if !defined(FUCHSIA_SDK)
25 #include <fuchsia/device/manager/cpp/fidl.h>
26 #include "lib/fsl/io/fd.h"
27 #endif // !defined(FUCHSIA_SDK)
28
29 using tonic::ToDart;
30
31 namespace zircon {
32 namespace dart {
33
34 namespace {
35
36 constexpr char kGetSizeResult[] = "GetSizeResult";
37 constexpr char kHandlePairResult[] = "HandlePairResult";
38 constexpr char kHandleResult[] = "HandleResult";
39 constexpr char kReadResult[] = "ReadResult";
40 constexpr char kWriteResult[] = "WriteResult";
41 constexpr char kFromFileResult[] = "FromFileResult";
42 constexpr char kMapResult[] = "MapResult";
43
44 class ByteDataScope {
45 public:
ByteDataScope(Dart_Handle dart_handle)46 explicit ByteDataScope(Dart_Handle dart_handle) : dart_handle_(dart_handle) {
47 Acquire();
48 }
49
ByteDataScope(size_t size)50 explicit ByteDataScope(size_t size) {
51 dart_handle_ = Dart_NewTypedData(Dart_TypedData_kByteData, size);
52 FML_DCHECK(!tonic::LogIfError(dart_handle_));
53 Acquire();
54 FML_DCHECK(size == size_);
55 }
56
~ByteDataScope()57 ~ByteDataScope() {
58 if (is_valid_) {
59 Release();
60 }
61 }
62
data() const63 void* data() const { return data_; }
size() const64 size_t size() const { return size_; }
dart_handle() const65 Dart_Handle dart_handle() const { return dart_handle_; }
is_valid() const66 bool is_valid() const { return is_valid_; }
67
Release()68 void Release() {
69 FML_DCHECK(is_valid_);
70 Dart_Handle result = Dart_TypedDataReleaseData(dart_handle_);
71 tonic::LogIfError(result);
72 is_valid_ = false;
73 data_ = nullptr;
74 size_ = 0;
75 }
76
77 private:
Acquire()78 void Acquire() {
79 FML_DCHECK(size_ == 0);
80 FML_DCHECK(data_ == nullptr);
81 FML_DCHECK(!is_valid_);
82
83 Dart_TypedData_Type type;
84 intptr_t size;
85 Dart_Handle result =
86 Dart_TypedDataAcquireData(dart_handle_, &type, &data_, &size);
87 is_valid_ =
88 !tonic::LogIfError(result) && type == Dart_TypedData_kByteData && data_;
89 if (is_valid_) {
90 size_ = size;
91 } else {
92 size_ = 0;
93 }
94 }
95
96 Dart_Handle dart_handle_;
97 bool is_valid_ = false;
98 size_t size_ = 0;
99 void* data_ = nullptr;
100 };
101
MakeHandleList(const std::vector<zx_handle_t> & in_handles)102 Dart_Handle MakeHandleList(const std::vector<zx_handle_t>& in_handles) {
103 tonic::DartClassLibrary& class_library =
104 tonic::DartState::Current()->class_library();
105 Dart_Handle handle_type = class_library.GetClass("zircon", "Handle");
106 Dart_Handle list = Dart_NewListOfType(handle_type, in_handles.size());
107 if (Dart_IsError(list))
108 return list;
109 for (size_t i = 0; i < in_handles.size(); i++) {
110 Dart_Handle result =
111 Dart_ListSetAt(list, i, ToDart(Handle::Create(in_handles[i])));
112 if (Dart_IsError(result))
113 return result;
114 }
115 return list;
116 }
117
118 template <class... Args>
ConstructDartObject(const char * class_name,Args &&...args)119 Dart_Handle ConstructDartObject(const char* class_name, Args&&... args) {
120 tonic::DartClassLibrary& class_library =
121 tonic::DartState::Current()->class_library();
122 Dart_Handle type =
123 Dart_HandleFromPersistent(class_library.GetClass("zircon", class_name));
124 FML_DCHECK(!tonic::LogIfError(type));
125
126 const char* cstr;
127 Dart_StringToCString(Dart_ToString(type), &cstr);
128
129 std::array<Dart_Handle, sizeof...(Args)> args_array{
130 {std::forward<Args>(args)...}};
131 Dart_Handle object =
132 Dart_New(type, Dart_EmptyString(), sizeof...(Args), args_array.data());
133 FML_DCHECK(!tonic::LogIfError(object));
134 return object;
135 }
136
GetNamespace()137 fdio_ns_t* GetNamespace() {
138 // Grab the fdio_ns_t* out of the isolate.
139 Dart_Handle zircon_lib = Dart_LookupLibrary(ToDart("dart:zircon"));
140 FML_DCHECK(!tonic::LogIfError(zircon_lib));
141 Dart_Handle namespace_type =
142 Dart_GetType(zircon_lib, ToDart("_Namespace"), 0, nullptr);
143 FML_DCHECK(!tonic::LogIfError(namespace_type));
144 Dart_Handle namespace_field =
145 Dart_GetField(namespace_type, ToDart("_namespace"));
146 FML_DCHECK(!tonic::LogIfError(namespace_field));
147 uint64_t fdio_ns_ptr;
148 Dart_Handle result = Dart_IntegerToUint64(namespace_field, &fdio_ns_ptr);
149 FML_DCHECK(!tonic::LogIfError(result));
150
151 return reinterpret_cast<fdio_ns_t*>(fdio_ns_ptr);
152 }
153
FdFromPath(std::string path)154 fml::UniqueFD FdFromPath(std::string path) {
155 // Get a VMO for the file.
156 fdio_ns_t* ns = reinterpret_cast<fdio_ns_t*>(GetNamespace());
157 fml::UniqueFD dirfd(fdio_ns_opendir(ns));
158 if (!dirfd.is_valid())
159 return fml::UniqueFD();
160
161 const char* c_path = path.c_str();
162 if (path.length() > 0 && c_path[0] == '/')
163 c_path = &c_path[1];
164 return fml::UniqueFD(openat(dirfd.get(), c_path, O_RDONLY));
165 }
166
167 } // namespace
168
169 IMPLEMENT_WRAPPERTYPEINFO(zircon, System);
170
ChannelCreate(uint32_t options)171 Dart_Handle System::ChannelCreate(uint32_t options) {
172 zx_handle_t out0 = 0, out1 = 0;
173 zx_status_t status = zx_channel_create(options, &out0, &out1);
174 if (status != ZX_OK) {
175 return ConstructDartObject(kHandlePairResult, ToDart(status));
176 } else {
177 return ConstructDartObject(kHandlePairResult, ToDart(status),
178 ToDart(Handle::Create(out0)),
179 ToDart(Handle::Create(out1)));
180 }
181 }
182
ConnectToService(std::string path,fml::RefPtr<Handle> channel)183 zx_status_t System::ConnectToService(std::string path,
184 fml::RefPtr<Handle> channel) {
185 return fdio_ns_connect(GetNamespace(), path.c_str(),
186 ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE,
187 channel->ReleaseHandle());
188 }
189
CloneChannelFromFileDescriptor(int fd)190 zx::channel System::CloneChannelFromFileDescriptor(int fd) {
191 zx::handle handle;
192 zx_status_t status = fdio_fd_clone(fd, handle.reset_and_get_address());
193 if (status != ZX_OK)
194 return zx::channel();
195
196 zx_info_handle_basic_t info = {};
197 status =
198 handle.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), NULL, NULL);
199
200 if (status != ZX_OK || info.type != ZX_OBJ_TYPE_CHANNEL)
201 return zx::channel();
202
203 return zx::channel(handle.release());
204 }
205
ChannelFromFile(std::string path)206 Dart_Handle System::ChannelFromFile(std::string path) {
207 fml::UniqueFD fd = FdFromPath(path);
208 if (!fd.is_valid()) {
209 return ConstructDartObject(kHandleResult, ToDart(ZX_ERR_IO));
210 }
211
212 // Get channel from fd.
213 zx::channel channel = CloneChannelFromFileDescriptor(fd.get());
214 if (!channel) {
215 return ConstructDartObject(kHandleResult, ToDart(ZX_ERR_IO));
216 }
217
218 return ConstructDartObject(kHandleResult, ToDart(ZX_OK),
219 ToDart(Handle::Create(channel.release())));
220 }
221
ChannelWrite(fml::RefPtr<Handle> channel,const tonic::DartByteData & data,std::vector<Handle * > handles)222 zx_status_t System::ChannelWrite(fml::RefPtr<Handle> channel,
223 const tonic::DartByteData& data,
224 std::vector<Handle*> handles) {
225 if (!channel || !channel->is_valid()) {
226 data.Release();
227 return ZX_ERR_BAD_HANDLE;
228 }
229
230 std::vector<zx_handle_t> zx_handles;
231 for (Handle* handle : handles) {
232 zx_handles.push_back(handle->handle());
233 }
234
235 zx_status_t status = zx_channel_write(channel->handle(), 0, data.data(),
236 data.length_in_bytes(),
237 zx_handles.data(), zx_handles.size());
238 // Handles are always consumed.
239 for (Handle* handle : handles) {
240 handle->ReleaseHandle();
241 }
242
243 data.Release();
244 return status;
245 }
246
ChannelQueryAndRead(fml::RefPtr<Handle> channel)247 Dart_Handle System::ChannelQueryAndRead(fml::RefPtr<Handle> channel) {
248 if (!channel || !channel->is_valid()) {
249 return ConstructDartObject(kReadResult, ToDart(ZX_ERR_BAD_HANDLE));
250 }
251
252 uint32_t actual_bytes = 0;
253 uint32_t actual_handles = 0;
254
255 // Query the size of the next message.
256 zx_status_t status = zx_channel_read(channel->handle(), 0, nullptr, nullptr,
257 0, 0, &actual_bytes, &actual_handles);
258 if (status != ZX_ERR_BUFFER_TOO_SMALL) {
259 // An empty message or an error.
260 return ConstructDartObject(kReadResult, ToDart(status));
261 }
262
263 // Allocate space for the bytes and handles.
264 ByteDataScope bytes(actual_bytes);
265 FML_DCHECK(bytes.is_valid());
266 std::vector<zx_handle_t> handles(actual_handles);
267
268 // Make the call to actually get the message.
269 status = zx_channel_read(channel->handle(), 0, bytes.data(), handles.data(),
270 bytes.size(), handles.size(), &actual_bytes,
271 &actual_handles);
272 FML_DCHECK(status != ZX_OK || bytes.size() == actual_bytes);
273
274 bytes.Release();
275
276 if (status == ZX_OK) {
277 FML_DCHECK(handles.size() == actual_handles);
278
279 // return a ReadResult object.
280 return ConstructDartObject(kReadResult, ToDart(status), bytes.dart_handle(),
281 ToDart(actual_bytes), MakeHandleList(handles));
282 } else {
283 return ConstructDartObject(kReadResult, ToDart(status));
284 }
285 }
286
EventpairCreate(uint32_t options)287 Dart_Handle System::EventpairCreate(uint32_t options) {
288 zx_handle_t out0 = 0, out1 = 0;
289 zx_status_t status = zx_eventpair_create(0, &out0, &out1);
290 if (status != ZX_OK) {
291 return ConstructDartObject(kHandlePairResult, ToDart(status));
292 } else {
293 return ConstructDartObject(kHandlePairResult, ToDart(status),
294 ToDart(Handle::Create(out0)),
295 ToDart(Handle::Create(out1)));
296 }
297 }
298
SocketCreate(uint32_t options)299 Dart_Handle System::SocketCreate(uint32_t options) {
300 zx_handle_t out0 = 0, out1 = 0;
301 zx_status_t status = zx_socket_create(options, &out0, &out1);
302 if (status != ZX_OK) {
303 return ConstructDartObject(kHandlePairResult, ToDart(status));
304 } else {
305 return ConstructDartObject(kHandlePairResult, ToDart(status),
306 ToDart(Handle::Create(out0)),
307 ToDart(Handle::Create(out1)));
308 }
309 }
310
SocketWrite(fml::RefPtr<Handle> socket,const tonic::DartByteData & data,int options)311 Dart_Handle System::SocketWrite(fml::RefPtr<Handle> socket,
312 const tonic::DartByteData& data,
313 int options) {
314 if (!socket || !socket->is_valid()) {
315 data.Release();
316 return ConstructDartObject(kWriteResult, ToDart(ZX_ERR_BAD_HANDLE));
317 }
318
319 size_t actual;
320 zx_status_t status = zx_socket_write(socket->handle(), options, data.data(),
321 data.length_in_bytes(), &actual);
322 data.Release();
323 return ConstructDartObject(kWriteResult, ToDart(status), ToDart(actual));
324 }
325
SocketRead(fml::RefPtr<Handle> socket,size_t size)326 Dart_Handle System::SocketRead(fml::RefPtr<Handle> socket, size_t size) {
327 if (!socket || !socket->is_valid()) {
328 return ConstructDartObject(kReadResult, ToDart(ZX_ERR_BAD_HANDLE));
329 }
330
331 ByteDataScope bytes(size);
332 size_t actual;
333 zx_status_t status =
334 zx_socket_read(socket->handle(), 0, bytes.data(), size, &actual);
335 bytes.Release();
336 if (status == ZX_OK) {
337 FML_DCHECK(actual <= size);
338 return ConstructDartObject(kReadResult, ToDart(status), bytes.dart_handle(),
339 ToDart(actual));
340 }
341
342 return ConstructDartObject(kReadResult, ToDart(status));
343 }
344
VmoCreate(uint64_t size,uint32_t options)345 Dart_Handle System::VmoCreate(uint64_t size, uint32_t options) {
346 zx_handle_t vmo = ZX_HANDLE_INVALID;
347 zx_status_t status = zx_vmo_create(size, options, &vmo);
348 if (status != ZX_OK) {
349 return ConstructDartObject(kHandleResult, ToDart(status));
350 } else {
351 return ConstructDartObject(kHandleResult, ToDart(status),
352 ToDart(Handle::Create(vmo)));
353 }
354 }
355
VmoFromFile(std::string path)356 Dart_Handle System::VmoFromFile(std::string path) {
357 fml::UniqueFD fd = FdFromPath(path);
358 if (!fd.is_valid())
359 return ConstructDartObject(kFromFileResult, ToDart(ZX_ERR_IO));
360
361 struct stat stat_struct;
362 if (fstat(fd.get(), &stat_struct) == -1)
363 return ConstructDartObject(kFromFileResult, ToDart(ZX_ERR_IO));
364 zx_handle_t vmo = ZX_HANDLE_INVALID;
365 zx_status_t status = fdio_get_vmo_clone(fd.get(), &vmo);
366 if (status != ZX_OK)
367 return ConstructDartObject(kFromFileResult, ToDart(status));
368
369 return ConstructDartObject(kFromFileResult, ToDart(status),
370 ToDart(Handle::Create(vmo)),
371 ToDart(stat_struct.st_size));
372 }
373
VmoGetSize(fml::RefPtr<Handle> vmo)374 Dart_Handle System::VmoGetSize(fml::RefPtr<Handle> vmo) {
375 if (!vmo || !vmo->is_valid()) {
376 return ConstructDartObject(kGetSizeResult, ToDart(ZX_ERR_BAD_HANDLE));
377 }
378
379 uint64_t size;
380 zx_status_t status = zx_vmo_get_size(vmo->handle(), &size);
381
382 return ConstructDartObject(kGetSizeResult, ToDart(status), ToDart(size));
383 }
384
VmoSetSize(fml::RefPtr<Handle> vmo,uint64_t size)385 zx_status_t System::VmoSetSize(fml::RefPtr<Handle> vmo, uint64_t size) {
386 if (!vmo || !vmo->is_valid()) {
387 return ZX_ERR_BAD_HANDLE;
388 }
389 return zx_vmo_set_size(vmo->handle(), size);
390 }
391
VmoWrite(fml::RefPtr<Handle> vmo,uint64_t offset,const tonic::DartByteData & data)392 zx_status_t System::VmoWrite(fml::RefPtr<Handle> vmo,
393 uint64_t offset,
394 const tonic::DartByteData& data) {
395 if (!vmo || !vmo->is_valid()) {
396 data.Release();
397 return ZX_ERR_BAD_HANDLE;
398 }
399
400 zx_status_t status =
401 zx_vmo_write(vmo->handle(), data.data(), offset, data.length_in_bytes());
402
403 data.Release();
404 return status;
405 }
406
VmoRead(fml::RefPtr<Handle> vmo,uint64_t offset,size_t size)407 Dart_Handle System::VmoRead(fml::RefPtr<Handle> vmo,
408 uint64_t offset,
409 size_t size) {
410 if (!vmo || !vmo->is_valid()) {
411 return ConstructDartObject(kReadResult, ToDart(ZX_ERR_BAD_HANDLE));
412 }
413
414 // TODO: constrain size?
415 ByteDataScope bytes(size);
416 zx_status_t status = zx_vmo_read(vmo->handle(), bytes.data(), offset, size);
417 bytes.Release();
418 if (status == ZX_OK) {
419 return ConstructDartObject(kReadResult, ToDart(status), bytes.dart_handle(),
420 ToDart(size));
421 }
422 return ConstructDartObject(kReadResult, ToDart(status));
423 }
424
425 struct SizedRegion {
SizedRegionzircon::dart::SizedRegion426 SizedRegion(void* r, size_t s) : region(r), size(s) {}
427 void* region;
428 size_t size;
429 };
430
VmoMapFinalizer(void * isolate_callback_data,Dart_WeakPersistentHandle handle,void * peer)431 void System::VmoMapFinalizer(void* isolate_callback_data,
432 Dart_WeakPersistentHandle handle,
433 void* peer) {
434 SizedRegion* r = reinterpret_cast<SizedRegion*>(peer);
435 zx_vmar_unmap(zx_vmar_root_self(), reinterpret_cast<uintptr_t>(r->region),
436 r->size);
437 delete r;
438 }
439
VmoMap(fml::RefPtr<Handle> vmo)440 Dart_Handle System::VmoMap(fml::RefPtr<Handle> vmo) {
441 if (!vmo || !vmo->is_valid())
442 return ConstructDartObject(kMapResult, ToDart(ZX_ERR_BAD_HANDLE));
443
444 uint64_t size;
445 zx_status_t status = zx_vmo_get_size(vmo->handle(), &size);
446 if (status != ZX_OK)
447 return ConstructDartObject(kMapResult, ToDart(status));
448
449 uintptr_t mapped_addr;
450 status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo->handle(),
451 0, size, &mapped_addr);
452 if (status != ZX_OK)
453 return ConstructDartObject(kMapResult, ToDart(status));
454
455 void* data = reinterpret_cast<void*>(mapped_addr);
456 Dart_Handle object = Dart_NewExternalTypedData(Dart_TypedData_kUint8, data,
457 static_cast<intptr_t>(size));
458 FML_DCHECK(!tonic::LogIfError(object));
459
460 SizedRegion* r = new SizedRegion(data, size);
461 Dart_NewWeakPersistentHandle(object, reinterpret_cast<void*>(r),
462 static_cast<intptr_t>(size) + sizeof(*r),
463 System::VmoMapFinalizer);
464
465 return ConstructDartObject(kMapResult, ToDart(ZX_OK), object);
466 }
467
ClockGet(uint32_t clock_id)468 uint64_t System::ClockGet(uint32_t clock_id) {
469 zx_time_t result = 0;
470 zx_clock_get(clock_id, &result);
471 return result;
472 }
473
474 // clang-format: off
475
476 #define FOR_EACH_STATIC_BINDING(V) \
477 V(System, ChannelCreate) \
478 V(System, ChannelFromFile) \
479 V(System, ChannelWrite) \
480 V(System, ChannelQueryAndRead) \
481 V(System, EventpairCreate) \
482 V(System, ConnectToService) \
483 V(System, SocketCreate) \
484 V(System, SocketWrite) \
485 V(System, SocketRead) \
486 V(System, VmoCreate) \
487 V(System, VmoFromFile) \
488 V(System, VmoGetSize) \
489 V(System, VmoSetSize) \
490 V(System, VmoRead) \
491 V(System, VmoWrite) \
492 V(System, VmoMap) \
493 V(System, ClockGet)
494
495 // clang-format: on
496
497 // Tonic is missing a comma.
498 #define DART_REGISTER_NATIVE_STATIC_(CLASS, METHOD) \
499 DART_REGISTER_NATIVE_STATIC(CLASS, METHOD),
500
FOR_EACH_STATIC_BINDING(DART_NATIVE_CALLBACK_STATIC)501 FOR_EACH_STATIC_BINDING(DART_NATIVE_CALLBACK_STATIC)
502
503 void System::RegisterNatives(tonic::DartLibraryNatives* natives) {
504 natives->Register({FOR_EACH_STATIC_BINDING(DART_REGISTER_NATIVE_STATIC_)});
505 }
506
507 } // namespace dart
508 } // namespace zircon
509