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 <fbl/alloc_checker.h>
11 #include <fbl/auto_lock.h>
12 #include <fbl/intrusive_double_list.h>
13 #include <fbl/ref_ptr.h>
14 #include <fbl/unique_ptr.h>
15 #include <fs/vfs.h>
16 #include <fs/vnode.h>
17 #include <fuchsia/io/c/fidl.h>
18 #include <lib/fdio/debug.h>
19 #include <lib/fdio/vfs.h>
20
21 #include <utility>
22
23 namespace fs {
24
MountNode()25 constexpr Vfs::MountNode::MountNode() : vn_(nullptr) {}
26
~MountNode()27 Vfs::MountNode::~MountNode() {
28 ZX_DEBUG_ASSERT(vn_ == nullptr);
29 }
30
SetNode(fbl::RefPtr<Vnode> vn)31 void Vfs::MountNode::SetNode(fbl::RefPtr<Vnode> vn) {
32 ZX_DEBUG_ASSERT(vn_ == nullptr);
33 vn_ = vn;
34 }
35
ReleaseRemote()36 zx::channel Vfs::MountNode::ReleaseRemote() {
37 ZX_DEBUG_ASSERT(vn_ != nullptr);
38 zx::channel h = vn_->DetachRemote();
39 vn_ = nullptr;
40 return h;
41 }
42
VnodeMatch(fbl::RefPtr<Vnode> vn) const43 bool 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_.
InstallRemote(fbl::RefPtr<Vnode> vn,MountChannel h)49 zx_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(std::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(std::move(vn));
66 fbl::AutoLock lock(&vfs_lock_);
67 remote_list_.push_front(std::move(mount_point));
68 return ZX_OK;
69 }
70
71 // Installs a remote filesystem on vn and adds it to the remote_list_.
InstallRemoteLocked(fbl::RefPtr<Vnode> vn,MountChannel h)72 zx_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(std::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(std::move(vn));
89 remote_list_.push_front(std::move(mount_point));
90 return ZX_OK;
91 }
92
MountMkdir(fbl::RefPtr<Vnode> vn,fbl::StringPiece name,MountChannel h,uint32_t flags)93 zx_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, std::move(h));
115 }
116
UninstallRemote(fbl::RefPtr<Vnode> vn,zx::channel * h)117 zx_status_t Vfs::UninstallRemote(fbl::RefPtr<Vnode> vn, zx::channel* h) {
118 fbl::AutoLock lock(&vfs_lock_);
119 return UninstallRemoteLocked(std::move(vn), h);
120 }
121
ForwardOpenRemote(fbl::RefPtr<Vnode> vn,zx::channel channel,fbl::StringPiece path,uint32_t flags,uint32_t mode)122 zx_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(std::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.
UninstallRemoteLocked(fbl::RefPtr<Vnode> vn,zx::channel * h)141 zx_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.
UninstallAll(zx_time_t deadline)157 zx_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