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 #pragma once
6
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/types.h>
11
12 #include <lib/fdio/vfs.h>
13 #include <fs/locking.h>
14 #include <zircon/assert.h>
15 #include <zircon/compiler.h>
16 #include <zircon/device/vfs.h>
17 #include <zircon/types.h>
18
19 #ifdef __Fuchsia__
20 #include <lib/async/dispatcher.h>
21 #include <lib/fdio/io.h>
22 #include <lib/zx/channel.h>
23 #include <lib/zx/event.h>
24 #include <lib/zx/vmo.h>
25 #include <fbl/mutex.h>
26 #include <fs/client.h>
27 #endif // __Fuchsia__
28
29 #include <fbl/function.h>
30 #include <fbl/intrusive_double_list.h>
31 #include <fbl/macros.h>
32 #include <fbl/ref_counted.h>
33 #include <fbl/ref_ptr.h>
34 #include <fbl/string_piece.h>
35 #include <fbl/unique_ptr.h>
36
37 #include <utility>
38
39 namespace fs {
40
41 class Connection;
42 class Vnode;
43
IsWritable(uint32_t flags)44 inline constexpr bool IsWritable(uint32_t flags) {
45 return flags & ZX_FS_RIGHT_WRITABLE;
46 }
47
IsReadable(uint32_t flags)48 inline constexpr bool IsReadable(uint32_t flags) {
49 return flags & ZX_FS_RIGHT_READABLE;
50 }
51
IsPathOnly(uint32_t flags)52 inline constexpr bool IsPathOnly(uint32_t flags) {
53 return flags & ZX_FS_FLAG_VNODE_REF_ONLY;
54 }
55
56 // A storage class for a vdircookie which is passed to Readdir.
57 // Common vnode implementations may use this struct as scratch
58 // space, or cast it to an alternative structure of the same
59 // size (or smaller).
60 //
61 // TODO(smklein): To implement seekdir and telldir, the size
62 // of this vdircookie may need to shrink to a 'long'.
63 typedef struct vdircookie {
Resetvdircookie64 void Reset() {
65 memset(this, 0, sizeof(struct vdircookie));
66 }
67
68 uint64_t n;
69 void* p;
70 } vdircookie_t;
71
72 #ifdef __Fuchsia__
73
74 // MountChannel functions exactly the same as a channel, except that it
75 // intentionally destructs by sending a clean "shutdown" signal to the
76 // underlying filesystem. Up until the point that a remote handle is
77 // attached to a vnode, this wrapper guarantees not only that the
78 // underlying handle gets closed on error, but also that the sub-filesystem
79 // is released (which cleans up the underlying connection to the block
80 // device).
81 class MountChannel {
82 public:
83 constexpr MountChannel() = default;
MountChannel(zx_handle_t handle)84 explicit MountChannel(zx_handle_t handle)
85 : channel_(handle) {}
MountChannel(zx::channel channel)86 explicit MountChannel(zx::channel channel)
87 : channel_(std::move(channel)) {}
MountChannel(MountChannel && other)88 MountChannel(MountChannel&& other)
89 : channel_(std::move(other.channel_)) {}
90
TakeChannel()91 zx::channel TakeChannel() { return std::move(channel_); }
92
~MountChannel()93 ~MountChannel() {
94 if (channel_.is_valid()) {
95 vfs_unmount_handle(channel_.release(), 0);
96 }
97 }
98
99 private:
100 zx::channel channel_;
101 };
102
103 #endif // __Fuchsia__
104
105 // The Vfs object contains global per-filesystem state, which
106 // may be valid across a collection of Vnodes.
107 //
108 // The Vfs object must outlive the Vnodes which it serves.
109 //
110 // This class is thread-safe.
111 class Vfs {
112 public:
113 Vfs();
114 virtual ~Vfs();
115
116 // Traverse the path to the target vnode, and create / open it using
117 // the underlying filesystem functions (lookup, create, open).
118 //
119 // If the node represented by |path| contains a remote node,
120 // set |pathout| to the remaining portion of the path yet to
121 // be traversed (or ".", if the endpoint of |path| is the mount point),
122 // and return the node containing the node in |out|.
123 zx_status_t Open(fbl::RefPtr<Vnode> vn, fbl::RefPtr<Vnode>* out,
124 fbl::StringPiece path, fbl::StringPiece* pathout,
125 uint32_t flags, uint32_t mode) FS_TA_EXCLUDES(vfs_lock_);
126 zx_status_t Unlink(fbl::RefPtr<Vnode> vn, fbl::StringPiece path) FS_TA_EXCLUDES(vfs_lock_);
127
128 // Sets whether this file system is read-only.
129 void SetReadonly(bool value) FS_TA_EXCLUDES(vfs_lock_);
130
131 #ifdef __Fuchsia__
132 // Unmounts the underlying filesystem.
133 //
134 // The closure may be invoked before or after |Shutdown| returns.
135 using ShutdownCallback = fbl::Function<void(zx_status_t status)>;
136 virtual void Shutdown(ShutdownCallback closure) = 0;
137
138 // Identifies if the filesystem is in the process of terminating.
139 // May be checked by active connections, which, upon reading new
140 // port packets, should ignore them and close immediately.
141 virtual bool IsTerminating() const = 0;
142
143 void TokenDiscard(zx::event ios_token) FS_TA_EXCLUDES(vfs_lock_);
144 zx_status_t VnodeToToken(fbl::RefPtr<Vnode> vn, zx::event* ios_token,
145 zx::event* out) FS_TA_EXCLUDES(vfs_lock_);
146 zx_status_t Link(zx::event token, fbl::RefPtr<Vnode> oldparent,
147 fbl::StringPiece oldStr, fbl::StringPiece newStr) FS_TA_EXCLUDES(vfs_lock_);
148 zx_status_t Rename(zx::event token, fbl::RefPtr<Vnode> oldparent,
149 fbl::StringPiece oldStr, fbl::StringPiece newStr) FS_TA_EXCLUDES(vfs_lock_);
150 // Calls readdir on the Vnode while holding the vfs_lock, preventing path
151 // modification operations for the duration of the operation.
152 zx_status_t Readdir(Vnode* vn, vdircookie_t* cookie,
153 void* dirents, size_t len, size_t* out_actual) FS_TA_EXCLUDES(vfs_lock_);
154
155 Vfs(async_dispatcher_t* dispatcher);
156
dispatcher()157 async_dispatcher_t* dispatcher() { return dispatcher_; }
SetDispatcher(async_dispatcher_t * dispatcher)158 void SetDispatcher(async_dispatcher_t* dispatcher) { dispatcher_ = dispatcher; }
159
160 // Begins serving VFS messages over the specified connection.
161 zx_status_t ServeConnection(fbl::unique_ptr<Connection> connection) FS_TA_EXCLUDES(vfs_lock_);
162
163 // Called by a VFS connection when it is closed remotely.
164 // The VFS is now responsible for destroying the connection.
165 void OnConnectionClosedRemotely(Connection* connection) FS_TA_EXCLUDES(vfs_lock_);
166
167 // Serves a Vnode over the specified channel (used for creating new filesystems)
168 zx_status_t ServeDirectory(fbl::RefPtr<Vnode> vn, zx::channel channel);
169
170 // Pins a handle to a remote filesystem onto a vnode, if possible.
171 zx_status_t InstallRemote(fbl::RefPtr<Vnode> vn, MountChannel h) FS_TA_EXCLUDES(vfs_lock_);
172
173 // Create and mount a directory with a provided name
174 zx_status_t MountMkdir(fbl::RefPtr<Vnode> vn, fbl::StringPiece name,
175 MountChannel h, uint32_t flags) FS_TA_EXCLUDES(vfs_lock_);
176
177 // Unpin a handle to a remote filesystem from a vnode, if one exists.
178 zx_status_t UninstallRemote(fbl::RefPtr<Vnode> vn, zx::channel* h) FS_TA_EXCLUDES(vfs_lock_);
179
180 // Forwards an open request to a remote handle.
181 // If the remote handle is closed (handing off returns ZX_ERR_PEER_CLOSED),
182 // it is automatically unmounted.
183 zx_status_t ForwardOpenRemote(fbl::RefPtr<Vnode> vn, zx::channel channel,
184 fbl::StringPiece path, uint32_t flags,
185 uint32_t mode) FS_TA_EXCLUDES(vfs_lock_);
186
187 // Unpins all remote filesystems in the current filesystem, and waits for the
188 // response of each one with the provided deadline.
189 zx_status_t UninstallAll(zx_time_t deadline) FS_TA_EXCLUDES(vfs_lock_);
190 #endif
191
192 protected:
193 // Whether this file system is read-only.
ReadonlyLocked()194 bool ReadonlyLocked() const FS_TA_REQUIRES(vfs_lock_) { return readonly_; }
195
196 private:
197 // Starting at vnode |vn|, walk the tree described by the path string,
198 // until either there is only one path segment remaining in the string
199 // or we encounter a vnode that represents a remote filesystem
200 //
201 // On success,
202 // |out| is the vnode at which we stopped searching.
203 // |pathout| is the remainder of the path to search.
204 zx_status_t Walk(fbl::RefPtr<Vnode> vn, fbl::RefPtr<Vnode>* out,
205 fbl::StringPiece path, fbl::StringPiece* pathout) FS_TA_REQUIRES(vfs_lock_);
206
207 zx_status_t OpenLocked(fbl::RefPtr<Vnode> vn, fbl::RefPtr<Vnode>* out,
208 fbl::StringPiece path, fbl::StringPiece* pathout,
209 uint32_t flags, uint32_t mode) FS_TA_REQUIRES(vfs_lock_);
210
211 bool readonly_{};
212
213 #ifdef __Fuchsia__
214 zx_status_t TokenToVnode(zx::event token, fbl::RefPtr<Vnode>* out) FS_TA_REQUIRES(vfs_lock_);
215 zx_status_t InstallRemoteLocked(fbl::RefPtr<Vnode> vn, MountChannel h) FS_TA_REQUIRES(vfs_lock_);
216 zx_status_t UninstallRemoteLocked(fbl::RefPtr<Vnode> vn,
217 zx::channel* h) FS_TA_REQUIRES(vfs_lock_);
218
219 // Non-intrusive node in linked list of vnodes acting as mount points
220 class MountNode final : public fbl::DoublyLinkedListable<fbl::unique_ptr<MountNode>> {
221 public:
222 using ListType = fbl::DoublyLinkedList<fbl::unique_ptr<MountNode>>;
223 constexpr MountNode();
224 ~MountNode();
225
226 void SetNode(fbl::RefPtr<Vnode> vn);
227 zx::channel ReleaseRemote();
228 bool VnodeMatch(fbl::RefPtr<Vnode> vn) const;
229
230 private:
231 fbl::RefPtr<Vnode> vn_;
232 };
233
234 // The mount list is a global static variable, but it only uses
235 // constexpr constructors during initialization. As a consequence,
236 // the .init_array section of the compiled vfs-mount object file is
237 // empty; "remote_list" is a member of the bss section.
FS_TA_GUARDED(vfs_lock_)238 MountNode::ListType remote_list_ FS_TA_GUARDED(vfs_lock_){};
239
240 async_dispatcher_t* dispatcher_{};
241
242 protected:
243 // A lock which should be used to protect lookup and walk operations
244 mtx_t vfs_lock_{};
245
246 // Starts tracking the lifetime of the connection.
247 virtual void RegisterConnection(fbl::unique_ptr<Connection> connection) = 0;
248
249 // Stops tracking the lifetime of the connection.
250 virtual void UnregisterConnection(Connection* connection) = 0;
251
252 #endif // ifdef __Fuchsia__
253 };
254
255 } // namespace fs
256