1 // Copyright 2017 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 <fs/connection.h>
6
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13
14 #include <fs/handler.h>
15 #include <fs/trace.h>
16 #include <fs/vnode.h>
17 #include <fuchsia/io/c/fidl.h>
18 #include <lib/fdio/debug.h>
19 #include <lib/fdio/io.h>
20 #include <lib/fdio/vfs.h>
21 #include <lib/zx/handle.h>
22 #include <zircon/assert.h>
23
24 #include <utility>
25
26 #define ZXDEBUG 0
27
28 namespace fs {
29 namespace {
30
WriteDescribeError(zx::channel channel,zx_status_t status)31 void WriteDescribeError(zx::channel channel, zx_status_t status) {
32 fuchsia_io_NodeOnOpenEvent msg;
33 memset(&msg, 0, sizeof(msg));
34 msg.hdr.ordinal = fuchsia_io_NodeOnOpenOrdinal;
35 msg.s = status;
36 channel.write(0, &msg, sizeof(msg), nullptr, 0);
37 }
38
GetNodeInfo(const fbl::RefPtr<Vnode> & vn,uint32_t flags,fuchsia_io_NodeInfo * info)39 zx_status_t GetNodeInfo(const fbl::RefPtr<Vnode>& vn, uint32_t flags,
40 fuchsia_io_NodeInfo* info) {
41 if (IsPathOnly(flags)) {
42 return vn->Vnode::GetHandles(flags, info);
43 } else {
44 return vn->GetHandles(flags, info);
45 }
46 }
47
Describe(const fbl::RefPtr<Vnode> & vn,uint32_t flags,OnOpenMsg * response,zx_handle_t * handle)48 void Describe(const fbl::RefPtr<Vnode>& vn, uint32_t flags,
49 OnOpenMsg* response, zx_handle_t* handle) {
50 response->primary.hdr.ordinal = fuchsia_io_NodeOnOpenOrdinal;
51 response->extra.file.event = ZX_HANDLE_INVALID;
52 zx_status_t r = GetNodeInfo(vn, flags, &response->extra);
53
54 // We unfortunately encode this message by hand because FIDL events
55 // are not yet supported by the C bindings.
56 auto encode_handle = [](zx_handle_t* encode_location, zx_handle_t* out) {
57 // If a handle was returned, transfer it to the output location, and
58 // encode it in-place.
59 *out = *encode_location;
60 if (*encode_location != ZX_HANDLE_INVALID) {
61 *encode_location = FIDL_HANDLE_PRESENT;
62 } else {
63 *encode_location = FIDL_HANDLE_ABSENT;
64 }
65 };
66 switch (response->extra.tag) {
67 case fuchsia_io_NodeInfoTag_service:
68 case fuchsia_io_NodeInfoTag_directory:
69 break;
70 case fuchsia_io_NodeInfoTag_file:
71 encode_handle(&response->extra.file.event, handle);
72 break;
73 case fuchsia_io_NodeInfoTag_pipe:
74 encode_handle(&response->extra.pipe.socket, handle);
75 break;
76 case fuchsia_io_NodeInfoTag_vmofile:
77 encode_handle(&response->extra.vmofile.vmo, handle);
78 break;
79 case fuchsia_io_NodeInfoTag_device:
80 encode_handle(&response->extra.device.event, handle);
81 break;
82 default:
83 ZX_DEBUG_ASSERT_MSG(false, "Unsupported NodeInfoTag: %d\n", response->extra.tag);
84 }
85
86 // If a valid response was returned, encode it.
87 response->primary.s = r;
88 response->primary.info = reinterpret_cast<fuchsia_io_NodeInfo*>(r == ZX_OK ?
89 FIDL_ALLOC_PRESENT :
90 FIDL_ALLOC_ABSENT);
91 }
92
FilterFlags(uint32_t flags,uint32_t * out_flags,bool * out_describe)93 void FilterFlags(uint32_t flags, uint32_t* out_flags, bool* out_describe) {
94 // Filter out flags that are invalid when combined with REF_ONLY.
95 if (IsPathOnly(flags)) {
96 flags &= ZX_FS_FLAG_VNODE_REF_ONLY | ZX_FS_FLAG_DIRECTORY | ZX_FS_FLAG_DESCRIBE;
97 }
98
99 *out_describe = flags & ZX_FS_FLAG_DESCRIBE;
100 *out_flags = flags & (~ZX_FS_FLAG_DESCRIBE);
101 }
102
VnodeServe(Vfs * vfs,fbl::RefPtr<Vnode> vnode,zx::channel channel,uint32_t open_flags)103 void VnodeServe(Vfs* vfs, fbl::RefPtr<Vnode> vnode, zx::channel channel, uint32_t open_flags) {
104 if (IsPathOnly(open_flags)) {
105 vnode->Vnode::Serve(vfs, std::move(channel), open_flags);
106 } else {
107 vnode->Serve(vfs, std::move(channel), open_flags);
108 }
109 }
110
111 // Performs a path walk and opens a connection to another node.
OpenAt(Vfs * vfs,fbl::RefPtr<Vnode> parent,zx::channel channel,fbl::StringPiece path,uint32_t flags,uint32_t mode)112 void OpenAt(Vfs* vfs, fbl::RefPtr<Vnode> parent, zx::channel channel,
113 fbl::StringPiece path, uint32_t flags, uint32_t mode) {
114 bool describe;
115 uint32_t open_flags;
116 FilterFlags(flags, &open_flags, &describe);
117
118 fbl::RefPtr<Vnode> vnode;
119 zx_status_t r = vfs->Open(std::move(parent), &vnode, path, &path, open_flags, mode);
120
121 if (r != ZX_OK) {
122 FS_TRACE_DEBUG("vfs: open failure: %d\n", r);
123 } else if (!(open_flags & ZX_FS_FLAG_NOREMOTE) && vnode->IsRemote()) {
124 // Remote handoff to a remote filesystem node.
125 vfs->ForwardOpenRemote(std::move(vnode), std::move(channel), std::move(path),
126 flags, mode);
127 return;
128 }
129
130 if (describe) {
131 // Regardless of the error code, in the 'describe' case, we
132 // should respond to the client.
133 if (r != ZX_OK) {
134 WriteDescribeError(std::move(channel), r);
135 return;
136 }
137
138 OnOpenMsg response;
139 memset(&response, 0, sizeof(response));
140 zx_handle_t extra = ZX_HANDLE_INVALID;
141 Describe(vnode, flags, &response, &extra);
142 uint32_t hcount = (extra != ZX_HANDLE_INVALID) ? 1 : 0;
143 channel.write(0, &response, sizeof(OnOpenMsg), &extra, hcount);
144 } else if (r != ZX_OK) {
145 return;
146 }
147
148 VnodeServe(vfs, std::move(vnode), std::move(channel), open_flags);
149 }
150
151 // This template defines a mechanism to transform a member of Connection
152 // into a FIDL-dispatch operation compatible format, independent of
153 // FIDL arguments.
154 //
155 // For example:
156 //
157 // ZXFIDL_OPERATION(Foo)
158 //
159 // Defines the following method:
160 //
161 // zx_status_t FooOp(void* ctx, Args... args);
162 //
163 // That invokes:
164 //
165 // zx_status_t Connection::Foo(Args... args);
166 //
167 // Such that FooOp may be used in the fuchsia_io_* ops table.
168 #define ZXFIDL_OPERATION(Method) \
169 template <typename... Args> \
170 zx_status_t Method ## Op(void* ctx, Args... args) { \
171 TRACE_DURATION("vfs", #Method); \
172 auto connection = reinterpret_cast<Connection*>(ctx); \
173 return (connection->Connection::Method)(std::forward<Args>(args)...); \
174 }
175
176 ZXFIDL_OPERATION(NodeClone)
177 ZXFIDL_OPERATION(NodeClose)
178 ZXFIDL_OPERATION(NodeDescribe)
179 ZXFIDL_OPERATION(NodeSync)
180 ZXFIDL_OPERATION(NodeGetAttr)
181 ZXFIDL_OPERATION(NodeSetAttr)
182 ZXFIDL_OPERATION(NodeIoctl)
183
184 const fuchsia_io_Node_ops kNodeOps = {
185 .Clone = NodeCloneOp,
186 .Close = NodeCloseOp,
187 .Describe = NodeDescribeOp,
188 .Sync = NodeSyncOp,
189 .GetAttr = NodeGetAttrOp,
190 .SetAttr = NodeSetAttrOp,
191 .Ioctl = NodeIoctlOp,
192 };
193
194 ZXFIDL_OPERATION(FileRead)
195 ZXFIDL_OPERATION(FileReadAt)
196 ZXFIDL_OPERATION(FileWrite)
197 ZXFIDL_OPERATION(FileWriteAt)
198 ZXFIDL_OPERATION(FileSeek)
199 ZXFIDL_OPERATION(FileTruncate)
200 ZXFIDL_OPERATION(FileGetFlags)
201 ZXFIDL_OPERATION(FileSetFlags)
202 ZXFIDL_OPERATION(FileGetVmo)
203
204 const fuchsia_io_File_ops kFileOps = {
205 .Clone = NodeCloneOp,
206 .Close = NodeCloseOp,
207 .Describe = NodeDescribeOp,
208 .Sync = NodeSyncOp,
209 .GetAttr = NodeGetAttrOp,
210 .SetAttr = NodeSetAttrOp,
211 .Ioctl = NodeIoctlOp,
212 .Read = FileReadOp,
213 .ReadAt = FileReadAtOp,
214 .Write = FileWriteOp,
215 .WriteAt = FileWriteAtOp,
216 .Seek = FileSeekOp,
217 .Truncate = FileTruncateOp,
218 .GetFlags = FileGetFlagsOp,
219 .SetFlags = FileSetFlagsOp,
220 .GetVmo = FileGetVmoOp,
221 };
222
223 ZXFIDL_OPERATION(DirectoryOpen)
ZXFIDL_OPERATION(DirectoryUnlink)224 ZXFIDL_OPERATION(DirectoryUnlink)
225 ZXFIDL_OPERATION(DirectoryReadDirents)
226 ZXFIDL_OPERATION(DirectoryRewind)
227 ZXFIDL_OPERATION(DirectoryGetToken)
228 ZXFIDL_OPERATION(DirectoryRename)
229 ZXFIDL_OPERATION(DirectoryLink)
230 ZXFIDL_OPERATION(DirectoryWatch)
231
232 const fuchsia_io_Directory_ops kDirectoryOps {
233 .Clone = NodeCloneOp,
234 .Close = NodeCloseOp,
235 .Describe = NodeDescribeOp,
236 .Sync = NodeSyncOp,
237 .GetAttr = NodeGetAttrOp,
238 .SetAttr = NodeSetAttrOp,
239 .Ioctl = NodeIoctlOp,
240 .Open = DirectoryOpenOp,
241 .Unlink = DirectoryUnlinkOp,
242 .ReadDirents = DirectoryReadDirentsOp,
243 .Rewind = DirectoryRewindOp,
244 .GetToken = DirectoryGetTokenOp,
245 .Rename = DirectoryRenameOp,
246 .Link = DirectoryLinkOp,
247 .Watch = DirectoryWatchOp,
248 };
249
250 ZXFIDL_OPERATION(DirectoryAdminMount)
ZXFIDL_OPERATION(DirectoryAdminMountAndCreate)251 ZXFIDL_OPERATION(DirectoryAdminMountAndCreate)
252 ZXFIDL_OPERATION(DirectoryAdminUnmount)
253 ZXFIDL_OPERATION(DirectoryAdminUnmountNode)
254 ZXFIDL_OPERATION(DirectoryAdminQueryFilesystem)
255 ZXFIDL_OPERATION(DirectoryAdminGetDevicePath)
256
257 const fuchsia_io_DirectoryAdmin_ops kDirectoryAdminOps {
258 .Clone = NodeCloneOp,
259 .Close = NodeCloseOp,
260 .Describe = NodeDescribeOp,
261 .Sync = NodeSyncOp,
262 .GetAttr = NodeGetAttrOp,
263 .SetAttr = NodeSetAttrOp,
264 .Ioctl = NodeIoctlOp,
265 .Open = DirectoryOpenOp,
266 .Unlink = DirectoryUnlinkOp,
267 .ReadDirents = DirectoryReadDirentsOp,
268 .Rewind = DirectoryRewindOp,
269 .GetToken = DirectoryGetTokenOp,
270 .Rename = DirectoryRenameOp,
271 .Link = DirectoryLinkOp,
272 .Watch = DirectoryWatchOp,
273 .Mount = DirectoryAdminMountOp,
274 .MountAndCreate = DirectoryAdminMountAndCreateOp,
275 .Unmount = DirectoryAdminUnmountOp,
276 .UnmountNode = DirectoryAdminUnmountNodeOp,
277 .QueryFilesystem = DirectoryAdminQueryFilesystemOp,
278 .GetDevicePath = DirectoryAdminGetDevicePathOp,
279 };
280
281 } // namespace
282
283 constexpr zx_signals_t kWakeSignals = ZX_CHANNEL_READABLE |
284 ZX_CHANNEL_PEER_CLOSED | kLocalTeardownSignal;
285
Connection(Vfs * vfs,fbl::RefPtr<Vnode> vnode,zx::channel channel,uint32_t flags)286 Connection::Connection(Vfs* vfs, fbl::RefPtr<Vnode> vnode,
287 zx::channel channel, uint32_t flags)
288 : vfs_(vfs), vnode_(std::move(vnode)), channel_(std::move(channel)),
289 wait_(this, ZX_HANDLE_INVALID, kWakeSignals), flags_(flags) {
290 ZX_DEBUG_ASSERT(vfs);
291 ZX_DEBUG_ASSERT(vnode_);
292 ZX_DEBUG_ASSERT(channel_);
293 }
294
~Connection()295 Connection::~Connection() {
296 // Stop waiting and clean up if still connected.
297 if (wait_.is_pending()) {
298 zx_status_t status = wait_.Cancel();
299 ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "Could not cancel wait: status=%d", status);
300 }
301
302 // Invoke a "close" call to the underlying object if we haven't already.
303 if (is_open()) {
304 CallClose();
305 }
306
307 // Release the token associated with this connection's vnode since the connection
308 // will be releasing the vnode's reference once this function returns.
309 if (token_) {
310 vfs_->TokenDiscard(std::move(token_));
311 }
312 }
313
AsyncTeardown()314 void Connection::AsyncTeardown() {
315 if (channel_) {
316 ZX_ASSERT(channel_.signal(0, kLocalTeardownSignal) == ZX_OK);
317 }
318 }
319
SyncTeardown()320 void Connection::SyncTeardown() {
321 if (wait_.Cancel() == ZX_OK) {
322 Terminate(/* call_close= */ true);
323 }
324 }
325
Serve()326 zx_status_t Connection::Serve() {
327 wait_.set_object(channel_.get());
328 return wait_.Begin(vfs_->dispatcher());
329 }
330
HandleSignals(async_dispatcher_t * dispatcher,async::WaitBase * wait,zx_status_t status,const zx_packet_signal_t * signal)331 void Connection::HandleSignals(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
332 const zx_packet_signal_t* signal) {
333 ZX_DEBUG_ASSERT(is_open());
334
335 if (status == ZX_OK) {
336 if (vfs_->IsTerminating()) {
337 // Short-circuit locally destroyed connections, rather than servicing
338 // requests on their behalf. This prevents new requests from being
339 // opened while filesystems are torn down.
340 status = ZX_ERR_PEER_CLOSED;
341 } else if (signal->observed & ZX_CHANNEL_READABLE) {
342 // Handle the message.
343 status = CallHandler();
344 switch (status) {
345 case ERR_DISPATCHER_ASYNC:
346 return;
347 case ZX_OK:
348 status = wait_.Begin(dispatcher);
349 if (status == ZX_OK) {
350 return;
351 }
352 break;
353 }
354 }
355 }
356
357 bool call_close = (status != ERR_DISPATCHER_DONE);
358 Terminate(call_close);
359 }
360
Terminate(bool call_close)361 void Connection::Terminate(bool call_close) {
362 if (call_close) {
363 // Give the dispatcher a chance to clean up.
364 CallClose();
365 } else {
366 // It's assumed that someone called the close handler
367 // prior to calling this function.
368 set_closed();
369 }
370
371 // Tell the VFS that the connection closed remotely.
372 // This might have the side-effect of destroying this object.
373 vfs_->OnConnectionClosedRemotely(this);
374 }
375
CallHandler()376 zx_status_t Connection::CallHandler() {
377 return ReadMessage(channel_.get(), [this] (fidl_msg_t* msg, FidlConnection* txn) {
378 return HandleMessage(msg, txn->Txn());
379 });
380 }
381
CallClose()382 void Connection::CallClose() {
383 CloseMessage([this] (fidl_msg_t* msg, FidlConnection* txn) {
384 return HandleMessage(msg, txn->Txn());
385 });
386 set_closed();
387 }
388
389 // Flags which can be modified by SetFlags.
390 constexpr uint32_t kSettableStatusFlags = ZX_FS_FLAG_APPEND;
391
392 // All flags which indicate state of the
393 // connection (excluding rights).
394 constexpr uint32_t kStatusFlags = kSettableStatusFlags | ZX_FS_FLAG_VNODE_REF_ONLY;
395
NodeClone(uint32_t flags,zx_handle_t object)396 zx_status_t Connection::NodeClone(uint32_t flags, zx_handle_t object) {
397 zx::channel channel(object);
398
399 bool describe;
400 uint32_t open_flags;
401 FilterFlags(flags, &open_flags, &describe);
402 // TODO(smklein): Avoid automatically inheriting rights
403 // from the cloned file descriptor; allow de-scoping.
404 // Currently, this is difficult, since the remote IO interface
405 // to clone does not specify a reduced set of rights.
406 open_flags |= (flags_ & (ZX_FS_RIGHTS | kStatusFlags));
407
408 fbl::RefPtr<Vnode> vn(vnode_);
409 zx_status_t status = ZX_OK;
410 if (!IsPathOnly(open_flags)) {
411 status = OpenVnode(open_flags, &vn);
412 }
413 if (describe) {
414 OnOpenMsg response;
415 memset(&response, 0, sizeof(response));
416 response.primary.s = status;
417 zx_handle_t extra = ZX_HANDLE_INVALID;
418 if (status == ZX_OK) {
419 Describe(vnode_, open_flags, &response, &extra);
420 }
421 uint32_t hcount = (extra != ZX_HANDLE_INVALID) ? 1 : 0;
422 channel.write(0, &response, sizeof(OnOpenMsg), &extra, hcount);
423 }
424
425 if (status == ZX_OK) {
426 VnodeServe(vfs_, std::move(vn), std::move(channel), open_flags);
427 }
428 return ZX_OK;
429 }
430
NodeClose(fidl_txn_t * txn)431 zx_status_t Connection::NodeClose(fidl_txn_t* txn) {
432 zx_status_t status;
433 if (IsPathOnly(flags_)) {
434 status = ZX_OK;
435 } else {
436 status = vnode_->Close();
437 }
438 fuchsia_io_NodeClose_reply(txn, status);
439
440 return ERR_DISPATCHER_DONE;
441 }
442
NodeDescribe(fidl_txn_t * txn)443 zx_status_t Connection::NodeDescribe(fidl_txn_t* txn) {
444 fuchsia_io_NodeInfo info;
445 memset(&info, 0, sizeof(info));
446 zx_status_t status = GetNodeInfo(vnode_, flags_, &info);
447 if (status != ZX_OK) {
448 return status;
449 }
450 return fuchsia_io_NodeDescribe_reply(txn, &info);
451 }
452
NodeSync(fidl_txn_t * txn)453 zx_status_t Connection::NodeSync(fidl_txn_t* txn) {
454 if (IsPathOnly(flags_)) {
455 return fuchsia_io_NodeSync_reply(txn, ZX_ERR_BAD_HANDLE);
456 }
457 Vnode::SyncCallback closure([this, ctxn = FidlConnection::CopyTxn(txn)]
458 (zx_status_t status) mutable {
459 fuchsia_io_NodeSync_reply(ctxn.Txn(), status);
460
461 // Try to reset the wait object
462 ZX_ASSERT_MSG(wait_.Begin(vfs_->dispatcher()) == ZX_OK,
463 "Dispatch loop unexpectedly ended");
464 });
465
466 vnode_->Sync(std::move(closure));
467 return ERR_DISPATCHER_ASYNC;
468 }
469
NodeGetAttr(fidl_txn_t * txn)470 zx_status_t Connection::NodeGetAttr(fidl_txn_t* txn) {
471 fuchsia_io_NodeAttributes attributes;
472 memset(&attributes, 0, sizeof(attributes));
473
474 // TODO(smklein): Consider using "NodeAttributes" within
475 // ulib/fs, rather than vnattr_t.
476 // Alternatively modify vnattr_t to match "NodeAttributes"
477 vnattr_t attr;
478 zx_status_t r;
479 if ((r = vnode_->Getattr(&attr)) != ZX_OK) {
480 return fuchsia_io_NodeGetAttr_reply(txn, r, &attributes);
481 }
482
483 attributes.mode = attr.mode;
484 attributes.id = attr.inode;
485 attributes.content_size = attr.size;
486 attributes.storage_size = VNATTR_BLKSIZE * attr.blkcount;
487 attributes.link_count = attr.nlink;
488 attributes.creation_time = attr.create_time;
489 attributes.modification_time = attr.modify_time;
490
491 return fuchsia_io_NodeGetAttr_reply(txn, ZX_OK, &attributes);
492 }
493
NodeSetAttr(uint32_t flags,const fuchsia_io_NodeAttributes * attributes,fidl_txn_t * txn)494 zx_status_t Connection::NodeSetAttr(uint32_t flags,
495 const fuchsia_io_NodeAttributes* attributes,
496 fidl_txn_t* txn) {
497 // TODO(smklein): Prevent read-only files from setting attributes,
498 // but allow attribute-setting on mutable directories.
499 // For context: ZX-1262, ZX-1065
500 if (IsPathOnly(flags_)) {
501 return fuchsia_io_NodeSetAttr_reply(txn, ZX_ERR_BAD_HANDLE);
502 }
503
504 vnattr_t attr;
505 attr.valid = flags;
506 attr.create_time = attributes->creation_time;
507 attr.modify_time = attributes->modification_time;
508 zx_status_t status = vnode_->Setattr(&attr);
509 return fuchsia_io_NodeSetAttr_reply(txn, status);
510 }
511
NodeIoctl(uint32_t opcode,uint64_t max_out,const zx_handle_t * handles,size_t handles_count,const uint8_t * in_data,size_t in_count,fidl_txn_t * txn)512 zx_status_t Connection::NodeIoctl(uint32_t opcode, uint64_t max_out,
513 const zx_handle_t* handles, size_t handles_count,
514 const uint8_t* in_data, size_t in_count, fidl_txn_t* txn) {
515 zx_handle_close_many(handles, handles_count);
516 return fuchsia_io_NodeIoctl_reply(txn, ZX_ERR_NOT_SUPPORTED, nullptr, 0, nullptr, 0);
517 }
518
FileRead(uint64_t count,fidl_txn_t * txn)519 zx_status_t Connection::FileRead(uint64_t count, fidl_txn_t* txn) {
520 if (!IsReadable(flags_)) {
521 return fuchsia_io_FileRead_reply(txn, ZX_ERR_BAD_HANDLE, nullptr, 0);
522 } else if (count > ZXFIDL_MAX_MSG_BYTES) {
523 return fuchsia_io_FileRead_reply(txn, ZX_ERR_INVALID_ARGS, nullptr, 0);
524 }
525 uint8_t data[count];
526 size_t actual = 0;
527 zx_status_t status = vnode_->Read(data, count, offset_, &actual);
528 if (status == ZX_OK) {
529 ZX_DEBUG_ASSERT(actual <= count);
530 offset_ += actual;
531 }
532 return fuchsia_io_FileRead_reply(txn, status, data, actual);
533 }
534
FileReadAt(uint64_t count,uint64_t offset,fidl_txn_t * txn)535 zx_status_t Connection::FileReadAt(uint64_t count, uint64_t offset, fidl_txn_t* txn) {
536 if (!IsReadable(flags_)) {
537 return fuchsia_io_FileReadAt_reply(txn, ZX_ERR_BAD_HANDLE, nullptr, 0);
538 } else if (count > ZXFIDL_MAX_MSG_BYTES) {
539 return fuchsia_io_FileReadAt_reply(txn, ZX_ERR_INVALID_ARGS, nullptr, 0);
540 }
541 uint8_t data[count];
542 size_t actual = 0;
543 zx_status_t status = vnode_->Read(data, count, offset, &actual);
544 if (status == ZX_OK) {
545 ZX_DEBUG_ASSERT(actual <= count);
546 }
547 return fuchsia_io_FileReadAt_reply(txn, status, data, actual);
548 }
549
FileWrite(const uint8_t * data_data,size_t data_count,fidl_txn_t * txn)550 zx_status_t Connection::FileWrite(const uint8_t* data_data, size_t data_count, fidl_txn_t* txn) {
551 if (!IsWritable(flags_)) {
552 return fuchsia_io_FileWrite_reply(txn, ZX_ERR_BAD_HANDLE, 0);
553 }
554
555 size_t actual = 0;
556 zx_status_t status;
557 if (flags_ & ZX_FS_FLAG_APPEND) {
558 size_t end;
559 status = vnode_->Append(data_data, data_count, &end, &actual);
560 if (status == ZX_OK) {
561 offset_ = end;
562 }
563 } else {
564 status = vnode_->Write(data_data, data_count, offset_, &actual);
565 if (status == ZX_OK) {
566 offset_ += actual;
567 }
568 }
569 ZX_DEBUG_ASSERT(actual <= data_count);
570 return fuchsia_io_FileWrite_reply(txn, status, actual);
571 }
572
FileWriteAt(const uint8_t * data_data,size_t data_count,uint64_t offset,fidl_txn_t * txn)573 zx_status_t Connection::FileWriteAt(const uint8_t* data_data, size_t data_count,
574 uint64_t offset, fidl_txn_t* txn) {
575 if (!IsWritable(flags_)) {
576 return fuchsia_io_FileWriteAt_reply(txn, ZX_ERR_BAD_HANDLE, 0);
577 }
578 size_t actual = 0;
579 zx_status_t status = vnode_->Write(data_data, data_count, offset, &actual);
580 ZX_DEBUG_ASSERT(actual <= data_count);
581 return fuchsia_io_FileWriteAt_reply(txn, status, actual);
582 }
583
FileSeek(int64_t offset,fuchsia_io_SeekOrigin start,fidl_txn_t * txn)584 zx_status_t Connection::FileSeek(int64_t offset, fuchsia_io_SeekOrigin start, fidl_txn_t* txn) {
585 static_assert(SEEK_SET == fuchsia_io_SeekOrigin_START, "");
586 static_assert(SEEK_CUR == fuchsia_io_SeekOrigin_CURRENT, "");
587 static_assert(SEEK_END == fuchsia_io_SeekOrigin_END, "");
588
589 if (IsPathOnly(flags_)) {
590 return fuchsia_io_FileSeek_reply(txn, ZX_ERR_BAD_HANDLE, offset_);
591 }
592 vnattr_t attr;
593 zx_status_t r;
594 if ((r = vnode_->Getattr(&attr)) < 0) {
595 return r;
596 }
597 size_t n;
598 switch (start) {
599 case SEEK_SET:
600 if (offset < 0) {
601 return fuchsia_io_FileSeek_reply(txn, ZX_ERR_INVALID_ARGS, offset_);
602 }
603 n = offset;
604 break;
605 case SEEK_CUR:
606 n = offset_ + offset;
607 if (offset < 0) {
608 // if negative seek
609 if (n > offset_) {
610 // wrapped around. attempt to seek before start
611 return fuchsia_io_FileSeek_reply(txn, ZX_ERR_INVALID_ARGS, offset_);
612 }
613 } else {
614 // positive seek
615 if (n < offset_) {
616 // wrapped around. overflow
617 return fuchsia_io_FileSeek_reply(txn, ZX_ERR_INVALID_ARGS, offset_);
618 }
619 }
620 break;
621 case SEEK_END:
622 n = attr.size + offset;
623 if (offset < 0) {
624 // if negative seek
625 if (n > attr.size) {
626 // wrapped around. attempt to seek before start
627 return fuchsia_io_FileSeek_reply(txn, ZX_ERR_INVALID_ARGS, offset_);
628 }
629 } else {
630 // positive seek
631 if (n < attr.size) {
632 // wrapped around
633 return fuchsia_io_FileSeek_reply(txn, ZX_ERR_INVALID_ARGS, offset_);
634 }
635 }
636 break;
637 default:
638 return fuchsia_io_FileSeek_reply(txn, ZX_ERR_INVALID_ARGS, offset_);
639 }
640 offset_ = n;
641 return fuchsia_io_FileSeek_reply(txn, ZX_OK, offset_);
642 }
643
FileTruncate(uint64_t length,fidl_txn_t * txn)644 zx_status_t Connection::FileTruncate(uint64_t length, fidl_txn_t* txn) {
645 if (!IsWritable(flags_)) {
646 return fuchsia_io_FileTruncate_reply(txn, ZX_ERR_BAD_HANDLE);
647 }
648
649 zx_status_t status = vnode_->Truncate(length);
650 return fuchsia_io_FileTruncate_reply(txn, status);
651 }
652
FileGetFlags(fidl_txn_t * txn)653 zx_status_t Connection::FileGetFlags(fidl_txn_t* txn) {
654 uint32_t flags = flags_ & (kStatusFlags | ZX_FS_RIGHTS);
655 return fuchsia_io_FileGetFlags_reply(txn, ZX_OK, flags);
656 }
657
FileSetFlags(uint32_t flags,fidl_txn_t * txn)658 zx_status_t Connection::FileSetFlags(uint32_t flags, fidl_txn_t* txn) {
659 flags_ = (flags_ & ~kSettableStatusFlags) | (flags & kSettableStatusFlags);
660 return fuchsia_io_FileSetFlags_reply(txn, ZX_OK);
661 }
662
FileGetVmo(uint32_t flags,fidl_txn_t * txn)663 zx_status_t Connection::FileGetVmo(uint32_t flags, fidl_txn_t* txn) {
664 if (IsPathOnly(flags_)) {
665 return fuchsia_io_FileGetVmo_reply(txn, ZX_ERR_BAD_HANDLE, ZX_HANDLE_INVALID);
666 }
667
668 if ((flags & fuchsia_io_VMO_FLAG_PRIVATE) && (flags & fuchsia_io_VMO_FLAG_EXACT)) {
669 return fuchsia_io_FileGetVmo_reply(txn, ZX_ERR_INVALID_ARGS, ZX_HANDLE_INVALID);
670 } else if ((flags_ & ZX_FS_FLAG_APPEND) && flags & fuchsia_io_VMO_FLAG_WRITE) {
671 return fuchsia_io_FileGetVmo_reply(txn, ZX_ERR_ACCESS_DENIED, ZX_HANDLE_INVALID);
672 } else if (!IsWritable(flags_) && (flags & fuchsia_io_VMO_FLAG_WRITE)) {
673 return fuchsia_io_FileGetVmo_reply(txn, ZX_ERR_ACCESS_DENIED, ZX_HANDLE_INVALID);
674 } else if (!IsReadable(flags_)) {
675 return fuchsia_io_FileGetVmo_reply(txn, ZX_ERR_ACCESS_DENIED, ZX_HANDLE_INVALID);
676 }
677
678 zx_handle_t handle = ZX_HANDLE_INVALID;
679 zx_status_t status = vnode_->GetVmo(flags, &handle);
680 return fuchsia_io_FileGetVmo_reply(txn, status, handle);
681 }
682
DirectoryOpen(uint32_t flags,uint32_t mode,const char * path_data,size_t path_size,zx_handle_t object)683 zx_status_t Connection::DirectoryOpen(uint32_t flags, uint32_t mode, const char* path_data,
684 size_t path_size, zx_handle_t object) {
685 zx::channel channel(object);
686 bool describe = flags & ZX_FS_FLAG_DESCRIBE;
687 if ((path_size < 1) || (path_size > PATH_MAX)) {
688 if (describe) {
689 WriteDescribeError(std::move(channel), ZX_ERR_INVALID_ARGS);
690 }
691 } else if ((flags & ZX_FS_RIGHT_ADMIN) && !(flags_ & ZX_FS_RIGHT_ADMIN)) {
692 if (describe) {
693 WriteDescribeError(std::move(channel), ZX_ERR_ACCESS_DENIED);
694 }
695 } else {
696 OpenAt(vfs_, vnode_, std::move(channel),
697 fbl::StringPiece(path_data, path_size), flags, mode);
698 }
699 return ZX_OK;
700 }
701
DirectoryUnlink(const char * path_data,size_t path_size,fidl_txn_t * txn)702 zx_status_t Connection::DirectoryUnlink(const char* path_data, size_t path_size, fidl_txn_t* txn) {
703 zx_status_t status = vfs_->Unlink(vnode_, fbl::StringPiece(path_data, path_size));
704 return fuchsia_io_DirectoryUnlink_reply(txn, status);
705 }
706
DirectoryReadDirents(uint64_t max_out,fidl_txn_t * txn)707 zx_status_t Connection::DirectoryReadDirents(uint64_t max_out, fidl_txn_t* txn) {
708 if (IsPathOnly(flags_)) {
709 return fuchsia_io_DirectoryReadDirents_reply(txn, ZX_ERR_BAD_HANDLE, nullptr, 0);
710 }
711 if (max_out > ZXFIDL_MAX_MSG_BYTES) {
712 return fuchsia_io_DirectoryReadDirents_reply(txn, ZX_ERR_INVALID_ARGS, nullptr, 0);
713 }
714 uint8_t data[max_out];
715 size_t actual = 0;
716 zx_status_t status = vfs_->Readdir(vnode_.get(), &dircookie_, data, max_out, &actual);
717 return fuchsia_io_DirectoryReadDirents_reply(txn, status, data, actual);
718 }
719
DirectoryRewind(fidl_txn_t * txn)720 zx_status_t Connection::DirectoryRewind(fidl_txn_t* txn) {
721 if (IsPathOnly(flags_)) {
722 return fuchsia_io_DirectoryRewind_reply(txn, ZX_ERR_BAD_HANDLE);
723 }
724 dircookie_.Reset();
725 return fuchsia_io_DirectoryRewind_reply(txn, ZX_OK);
726 }
727
DirectoryGetToken(fidl_txn_t * txn)728 zx_status_t Connection::DirectoryGetToken(fidl_txn_t* txn) {
729 zx::event returned_token;
730 zx_status_t status = vfs_->VnodeToToken(vnode_, &token_, &returned_token);
731 return fuchsia_io_DirectoryGetToken_reply(txn, status, returned_token.release());
732 }
733
DirectoryRename(const char * src_data,size_t src_size,zx_handle_t dst_parent_token,const char * dst_data,size_t dst_size,fidl_txn_t * txn)734 zx_status_t Connection::DirectoryRename(const char* src_data, size_t src_size,
735 zx_handle_t dst_parent_token, const char* dst_data,
736 size_t dst_size, fidl_txn_t* txn) {
737 zx::event token(dst_parent_token);
738 fbl::StringPiece oldStr(src_data, src_size);
739 fbl::StringPiece newStr(dst_data, dst_size);
740
741 if (src_size < 1 || dst_size < 1) {
742 return fuchsia_io_DirectoryRename_reply(txn, ZX_ERR_INVALID_ARGS);
743 }
744 zx_status_t status = vfs_->Rename(std::move(token), vnode_,
745 std::move(oldStr), std::move(newStr));
746 return fuchsia_io_DirectoryRename_reply(txn, status);
747 }
748
DirectoryLink(const char * src_data,size_t src_size,zx_handle_t dst_parent_token,const char * dst_data,size_t dst_size,fidl_txn_t * txn)749 zx_status_t Connection::DirectoryLink(const char* src_data, size_t src_size,
750 zx_handle_t dst_parent_token, const char* dst_data,
751 size_t dst_size, fidl_txn_t* txn) {
752 zx::event token(dst_parent_token);
753 fbl::StringPiece oldStr(src_data, src_size);
754 fbl::StringPiece newStr(dst_data, dst_size);
755
756 if (src_size < 1 || dst_size < 1) {
757 return fuchsia_io_DirectoryLink_reply(txn, ZX_ERR_INVALID_ARGS);
758 }
759 zx_status_t status = vfs_->Link(std::move(token), vnode_, std::move(oldStr),
760 std::move(newStr));
761 return fuchsia_io_DirectoryLink_reply(txn, status);
762 }
763
DirectoryWatch(uint32_t mask,uint32_t options,zx_handle_t handle,fidl_txn_t * txn)764 zx_status_t Connection::DirectoryWatch(uint32_t mask, uint32_t options, zx_handle_t handle,
765 fidl_txn_t* txn) {
766 zx::channel watcher(handle);
767 zx_status_t status = vnode_->WatchDir(vfs_, mask, options, std::move(watcher));
768 return fuchsia_io_DirectoryWatch_reply(txn, status);
769 }
770
DirectoryAdminMount(zx_handle_t remote,fidl_txn_t * txn)771 zx_status_t Connection::DirectoryAdminMount(zx_handle_t remote, fidl_txn_t* txn) {
772 if (!(flags_ & ZX_FS_RIGHT_ADMIN)) {
773 vfs_unmount_handle(remote, 0);
774 return fuchsia_io_DirectoryAdminMount_reply(txn, ZX_ERR_ACCESS_DENIED);
775 }
776 MountChannel c = MountChannel(remote);
777 zx_status_t status = vfs_->InstallRemote(vnode_, std::move(c));
778 return fuchsia_io_DirectoryAdminMount_reply(txn, status);;
779 }
780
DirectoryAdminMountAndCreate(zx_handle_t remote,const char * name,size_t name_size,uint32_t flags,fidl_txn_t * txn)781 zx_status_t Connection::DirectoryAdminMountAndCreate(zx_handle_t remote, const char* name,
782 size_t name_size, uint32_t flags,
783 fidl_txn_t* txn) {
784 if (!(flags_ & ZX_FS_RIGHT_ADMIN)) {
785 vfs_unmount_handle(remote, 0);
786 return fuchsia_io_DirectoryAdminMount_reply(txn, ZX_ERR_ACCESS_DENIED);
787 }
788 fbl::StringPiece str(name, name_size);
789 zx_status_t status = vfs_->MountMkdir(vnode_, std::move(str), MountChannel(remote), flags);
790 return fuchsia_io_DirectoryAdminMount_reply(txn, status);
791 }
792
DirectoryAdminUnmount(fidl_txn_t * txn)793 zx_status_t Connection::DirectoryAdminUnmount(fidl_txn_t* txn) {
794 if (!(flags_ & ZX_FS_RIGHT_ADMIN)) {
795 return fuchsia_io_DirectoryAdminUnmount_reply(txn, ZX_ERR_ACCESS_DENIED);
796 }
797 vfs_->UninstallAll(ZX_TIME_INFINITE);
798
799 // Unmount is fatal to the requesting connections.
800 Vfs::ShutdownCallback closure([ch = std::move(channel_),
801 ctxn = FidlConnection::CopyTxn(txn)]
802 (zx_status_t status) mutable {
803 fuchsia_io_DirectoryAdminUnmount_reply(ctxn.Txn(), status);
804 });
805 Vfs* vfs = vfs_;
806 Terminate(/* call_close= */ true);
807 vfs->Shutdown(std::move(closure));
808 return ERR_DISPATCHER_ASYNC;
809 }
810
DirectoryAdminUnmountNode(fidl_txn_t * txn)811 zx_status_t Connection::DirectoryAdminUnmountNode(fidl_txn_t* txn) {
812 if (!(flags_ & ZX_FS_RIGHT_ADMIN)) {
813 return fuchsia_io_DirectoryAdminUnmountNode_reply(txn, ZX_ERR_ACCESS_DENIED, ZX_HANDLE_INVALID);
814 }
815 zx::channel c;
816 zx_status_t status = vfs_->UninstallRemote(vnode_, &c);
817 return fuchsia_io_DirectoryAdminUnmountNode_reply(txn, status, c.release());
818 }
819
DirectoryAdminQueryFilesystem(fidl_txn_t * txn)820 zx_status_t Connection::DirectoryAdminQueryFilesystem(fidl_txn_t* txn) {
821 fuchsia_io_FilesystemInfo info;
822 zx_status_t status = vnode_->QueryFilesystem(&info);
823 return fuchsia_io_DirectoryAdminQueryFilesystem_reply(txn, status,
824 status == ZX_OK ? &info : nullptr);
825 }
826
DirectoryAdminGetDevicePath(fidl_txn_t * txn)827 zx_status_t Connection::DirectoryAdminGetDevicePath(fidl_txn_t* txn) {
828 if (!(flags_ & ZX_FS_RIGHT_ADMIN)) {
829 return fuchsia_io_DirectoryAdminGetDevicePath_reply(txn, ZX_ERR_ACCESS_DENIED, nullptr, 0);
830 }
831
832 char name[fuchsia_io_MAX_PATH];
833 size_t actual = 0;
834 zx_status_t status = vnode_->GetDevicePath(sizeof(name), name, &actual);
835 return fuchsia_io_DirectoryAdminGetDevicePath_reply(txn, status, name, actual);
836 }
837
HandleFsSpecificMessage(fidl_msg_t * msg,fidl_txn_t * txn)838 zx_status_t Connection::HandleFsSpecificMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
839 zx_handle_close_many(msg->handles, msg->num_handles);
840 return ZX_ERR_NOT_SUPPORTED;
841 }
842
HandleMessage(fidl_msg_t * msg,fidl_txn_t * txn)843 zx_status_t Connection::HandleMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
844 zx_status_t status = fuchsia_io_Node_try_dispatch(this, txn, msg, &kNodeOps);
845 if (status != ZX_ERR_NOT_SUPPORTED) {
846 return status;
847 }
848 status = fuchsia_io_File_try_dispatch(this, txn, msg, &kFileOps);
849 if (status != ZX_ERR_NOT_SUPPORTED) {
850 return status;
851 }
852 status = fuchsia_io_Directory_try_dispatch(this, txn, msg, &kDirectoryOps);
853 if (status != ZX_ERR_NOT_SUPPORTED) {
854 return status;
855 }
856 status = fuchsia_io_DirectoryAdmin_try_dispatch(this, txn, msg, &kDirectoryAdminOps);
857 if (status != ZX_ERR_NOT_SUPPORTED) {
858 return status;
859 }
860 return HandleFsSpecificMessage(msg, txn);
861 }
862
863 } // namespace fs
864