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