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