| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <threads.h> |
| |
| #include <fbl/alloc_checker.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/unique_ptr.h> |
| #include <fs/vfs.h> |
| #include <fs/vnode.h> |
| #include <fuchsia/io/c/fidl.h> |
| #include <lib/fdio/debug.h> |
| #include <lib/fdio/vfs.h> |
| |
| #include <utility> |
| |
| namespace fs { |
| |
| constexpr Vfs::MountNode::MountNode() : vn_(nullptr) {} |
| |
| Vfs::MountNode::~MountNode() { |
| ZX_DEBUG_ASSERT(vn_ == nullptr); |
| } |
| |
| void Vfs::MountNode::SetNode(fbl::RefPtr<Vnode> vn) { |
| ZX_DEBUG_ASSERT(vn_ == nullptr); |
| vn_ = vn; |
| } |
| |
| zx::channel Vfs::MountNode::ReleaseRemote() { |
| ZX_DEBUG_ASSERT(vn_ != nullptr); |
| zx::channel h = vn_->DetachRemote(); |
| vn_ = nullptr; |
| return h; |
| } |
| |
| bool Vfs::MountNode::VnodeMatch(fbl::RefPtr<Vnode> vn) const { |
| ZX_DEBUG_ASSERT(vn_ != nullptr); |
| return vn == vn_; |
| } |
| |
| // Installs a remote filesystem on vn and adds it to the remote_list_. |
| zx_status_t Vfs::InstallRemote(fbl::RefPtr<Vnode> vn, MountChannel h) { |
| if (vn == nullptr) { |
| return ZX_ERR_ACCESS_DENIED; |
| } |
| |
| // Allocate a node to track the remote handle |
| fbl::AllocChecker ac; |
| fbl::unique_ptr<MountNode> mount_point(new (&ac) MountNode()); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| zx_status_t status = vn->AttachRemote(std::move(h)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| // Save this node in the list of mounted vnodes |
| mount_point->SetNode(std::move(vn)); |
| fbl::AutoLock lock(&vfs_lock_); |
| remote_list_.push_front(std::move(mount_point)); |
| return ZX_OK; |
| } |
| |
| // Installs a remote filesystem on vn and adds it to the remote_list_. |
| zx_status_t Vfs::InstallRemoteLocked(fbl::RefPtr<Vnode> vn, MountChannel h) { |
| if (vn == nullptr) { |
| return ZX_ERR_ACCESS_DENIED; |
| } |
| |
| // Allocate a node to track the remote handle |
| fbl::AllocChecker ac; |
| fbl::unique_ptr<MountNode> mount_point(new (&ac) MountNode()); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| zx_status_t status = vn->AttachRemote(std::move(h)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| // Save this node in the list of mounted vnodes |
| mount_point->SetNode(std::move(vn)); |
| remote_list_.push_front(std::move(mount_point)); |
| return ZX_OK; |
| } |
| |
| zx_status_t Vfs::MountMkdir(fbl::RefPtr<Vnode> vn, fbl::StringPiece name, MountChannel h, |
| uint32_t flags) { |
| fbl::AutoLock lock(&vfs_lock_); |
| zx_status_t r = OpenLocked(vn, &vn, name, &name, ZX_FS_FLAG_CREATE | |
| ZX_FS_RIGHT_READABLE | ZX_FS_FLAG_DIRECTORY | |
| ZX_FS_FLAG_NOREMOTE, S_IFDIR); |
| ZX_DEBUG_ASSERT(r <= ZX_OK); // Should not be accessing remote nodes |
| if (r < 0) { |
| return r; |
| } |
| if (vn->IsRemote()) { |
| if (flags & fuchsia_io_MOUNT_CREATE_FLAG_REPLACE) { |
| // There is an old remote handle on this vnode; shut it down and |
| // replace it with our own. |
| zx::channel old_remote; |
| Vfs::UninstallRemoteLocked(vn, &old_remote); |
| vfs_unmount_handle(old_remote.release(), 0); |
| } else { |
| return ZX_ERR_BAD_STATE; |
| } |
| } |
| return Vfs::InstallRemoteLocked(vn, std::move(h)); |
| } |
| |
| zx_status_t Vfs::UninstallRemote(fbl::RefPtr<Vnode> vn, zx::channel* h) { |
| fbl::AutoLock lock(&vfs_lock_); |
| return UninstallRemoteLocked(std::move(vn), h); |
| } |
| |
| zx_status_t Vfs::ForwardOpenRemote(fbl::RefPtr<Vnode> vn, zx::channel channel, |
| fbl::StringPiece path, uint32_t flags, uint32_t mode) { |
| fbl::AutoLock lock(&vfs_lock_); |
| zx_handle_t h = vn->GetRemote(); |
| if (h == ZX_HANDLE_INVALID) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| zx_status_t r = fuchsia_io_DirectoryOpen(h, flags, mode, path.data(), |
| path.length(), channel.release()); |
| if (r == ZX_ERR_PEER_CLOSED) { |
| zx::channel c; |
| UninstallRemoteLocked(std::move(vn), &c); |
| } |
| return r; |
| } |
| |
| // Uninstall the remote filesystem mounted on vn. Removes vn from the |
| // remote_list_, and sends its corresponding filesystem an 'unmount' signal. |
| zx_status_t Vfs::UninstallRemoteLocked(fbl::RefPtr<Vnode> vn, zx::channel* h) { |
| fbl::unique_ptr<MountNode> mount_point; |
| { |
| mount_point = remote_list_.erase_if([&vn](const MountNode& node) { |
| return node.VnodeMatch(vn); |
| }); |
| if (!mount_point) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| } |
| *h = mount_point->ReleaseRemote(); |
| return ZX_OK; |
| } |
| |
| // Uninstall all remote filesystems. Acts like 'UninstallRemote' for all |
| // known remotes. |
| zx_status_t Vfs::UninstallAll(zx_time_t deadline) { |
| fbl::unique_ptr<MountNode> mount_point; |
| for (;;) { |
| { |
| fbl::AutoLock lock(&vfs_lock_); |
| mount_point = remote_list_.pop_front(); |
| } |
| if (mount_point) { |
| vfs_unmount_handle(mount_point->ReleaseRemote().release(), deadline); |
| } else { |
| return ZX_OK; |
| } |
| } |
| } |
| |
| } // namespace fs |