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