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