1// Copyright 2016 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 <stdlib.h> 6#include <string.h> 7#include <sys/stat.h> 8#include <threads.h> 9 10#include <fs/vfs.h> 11#include <fs/vnode.h> 12#include <fuchsia/io/c/fidl.h> 13#include <lib/fdio/debug.h> 14#include <lib/fdio/remoteio.h> 15#include <lib/fdio/vfs.h> 16#include <fbl/alloc_checker.h> 17#include <fbl/auto_lock.h> 18#include <fbl/intrusive_double_list.h> 19#include <fbl/ref_ptr.h> 20#include <fbl/type_support.h> 21#include <fbl/unique_ptr.h> 22 23namespace fs { 24 25constexpr Vfs::MountNode::MountNode() : vn_(nullptr) {} 26 27Vfs::MountNode::~MountNode() { 28 ZX_DEBUG_ASSERT(vn_ == nullptr); 29} 30 31void Vfs::MountNode::SetNode(fbl::RefPtr<Vnode> vn) { 32 ZX_DEBUG_ASSERT(vn_ == nullptr); 33 vn_ = vn; 34} 35 36zx::channel Vfs::MountNode::ReleaseRemote() { 37 ZX_DEBUG_ASSERT(vn_ != nullptr); 38 zx::channel h = vn_->DetachRemote(); 39 vn_ = nullptr; 40 return h; 41} 42 43bool Vfs::MountNode::VnodeMatch(fbl::RefPtr<Vnode> vn) const { 44 ZX_DEBUG_ASSERT(vn_ != nullptr); 45 return vn == vn_; 46} 47 48// Installs a remote filesystem on vn and adds it to the remote_list_. 49zx_status_t Vfs::InstallRemote(fbl::RefPtr<Vnode> vn, MountChannel h) { 50 if (vn == nullptr) { 51 return ZX_ERR_ACCESS_DENIED; 52 } 53 54 // Allocate a node to track the remote handle 55 fbl::AllocChecker ac; 56 fbl::unique_ptr<MountNode> mount_point(new (&ac) MountNode()); 57 if (!ac.check()) { 58 return ZX_ERR_NO_MEMORY; 59 } 60 zx_status_t status = vn->AttachRemote(fbl::move(h)); 61 if (status != ZX_OK) { 62 return status; 63 } 64 // Save this node in the list of mounted vnodes 65 mount_point->SetNode(fbl::move(vn)); 66 fbl::AutoLock lock(&vfs_lock_); 67 remote_list_.push_front(fbl::move(mount_point)); 68 return ZX_OK; 69} 70 71// Installs a remote filesystem on vn and adds it to the remote_list_. 72zx_status_t Vfs::InstallRemoteLocked(fbl::RefPtr<Vnode> vn, MountChannel h) { 73 if (vn == nullptr) { 74 return ZX_ERR_ACCESS_DENIED; 75 } 76 77 // Allocate a node to track the remote handle 78 fbl::AllocChecker ac; 79 fbl::unique_ptr<MountNode> mount_point(new (&ac) MountNode()); 80 if (!ac.check()) { 81 return ZX_ERR_NO_MEMORY; 82 } 83 zx_status_t status = vn->AttachRemote(fbl::move(h)); 84 if (status != ZX_OK) { 85 return status; 86 } 87 // Save this node in the list of mounted vnodes 88 mount_point->SetNode(fbl::move(vn)); 89 remote_list_.push_front(fbl::move(mount_point)); 90 return ZX_OK; 91} 92 93zx_status_t Vfs::MountMkdir(fbl::RefPtr<Vnode> vn, fbl::StringPiece name, MountChannel h, 94 uint32_t flags) { 95 fbl::AutoLock lock(&vfs_lock_); 96 zx_status_t r = OpenLocked(vn, &vn, name, &name, ZX_FS_FLAG_CREATE | 97 ZX_FS_RIGHT_READABLE | ZX_FS_FLAG_DIRECTORY | 98 ZX_FS_FLAG_NOREMOTE, S_IFDIR); 99 ZX_DEBUG_ASSERT(r <= ZX_OK); // Should not be accessing remote nodes 100 if (r < 0) { 101 return r; 102 } 103 if (vn->IsRemote()) { 104 if (flags & fuchsia_io_MOUNT_CREATE_FLAG_REPLACE) { 105 // There is an old remote handle on this vnode; shut it down and 106 // replace it with our own. 107 zx::channel old_remote; 108 Vfs::UninstallRemoteLocked(vn, &old_remote); 109 vfs_unmount_handle(old_remote.release(), 0); 110 } else { 111 return ZX_ERR_BAD_STATE; 112 } 113 } 114 return Vfs::InstallRemoteLocked(vn, fbl::move(h)); 115} 116 117zx_status_t Vfs::UninstallRemote(fbl::RefPtr<Vnode> vn, zx::channel* h) { 118 fbl::AutoLock lock(&vfs_lock_); 119 return UninstallRemoteLocked(fbl::move(vn), h); 120} 121 122zx_status_t Vfs::ForwardOpenRemote(fbl::RefPtr<Vnode> vn, zx::channel channel, 123 fbl::StringPiece path, uint32_t flags, uint32_t mode) { 124 fbl::AutoLock lock(&vfs_lock_); 125 zx_handle_t h = vn->GetRemote(); 126 if (h == ZX_HANDLE_INVALID) { 127 return ZX_ERR_NOT_FOUND; 128 } 129 130 zx_status_t r = fuchsia_io_DirectoryOpen(h, flags, mode, path.data(), 131 path.length(), channel.release()); 132 if (r == ZX_ERR_PEER_CLOSED) { 133 zx::channel c; 134 UninstallRemoteLocked(fbl::move(vn), &c); 135 } 136 return r; 137} 138 139// Uninstall the remote filesystem mounted on vn. Removes vn from the 140// remote_list_, and sends its corresponding filesystem an 'unmount' signal. 141zx_status_t Vfs::UninstallRemoteLocked(fbl::RefPtr<Vnode> vn, zx::channel* h) { 142 fbl::unique_ptr<MountNode> mount_point; 143 { 144 mount_point = remote_list_.erase_if([&vn](const MountNode& node) { 145 return node.VnodeMatch(vn); 146 }); 147 if (!mount_point) { 148 return ZX_ERR_NOT_FOUND; 149 } 150 } 151 *h = mount_point->ReleaseRemote(); 152 return ZX_OK; 153} 154 155// Uninstall all remote filesystems. Acts like 'UninstallRemote' for all 156// known remotes. 157zx_status_t Vfs::UninstallAll(zx_time_t deadline) { 158 fbl::unique_ptr<MountNode> mount_point; 159 for (;;) { 160 { 161 fbl::AutoLock lock(&vfs_lock_); 162 mount_point = remote_list_.pop_front(); 163 } 164 if (mount_point) { 165 vfs_unmount_handle(mount_point->ReleaseRemote().release(), deadline); 166 } else { 167 return ZX_OK; 168 } 169 } 170} 171 172} // namespace fs 173