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 <unistd.h>
9 
10 #include <fbl/auto_call.h>
11 #include <fs/trace.h>
12 #include <fs/vfs.h>
13 #include <fs/vnode.h>
14 #include <lib/fdio/watcher.h>
15 
16 #ifdef __Fuchsia__
17 #include <threads.h>
18 
19 #include <fbl/auto_lock.h>
20 #include <fbl/ref_ptr.h>
21 #include <fs/connection.h>
22 #include <fs/remote.h>
23 #include <lib/zx/event.h>
24 #include <lib/zx/process.h>
25 #include <zircon/assert.h>
26 
27 #include <utility>
28 #endif
29 
30 namespace fs {
31 namespace {
32 
33 // Trim a name before sending it to internal filesystem functions.
34 // Trailing '/' characters imply that the name must refer to a directory.
vfs_name_trim(fbl::StringPiece name,fbl::StringPiece * name_out,bool * dir_out)35 zx_status_t vfs_name_trim(fbl::StringPiece name, fbl::StringPiece* name_out,
36                           bool* dir_out) {
37     size_t len = name.length();
38     bool is_dir = false;
39     while ((len > 0) && name[len - 1] == '/') {
40         len--;
41         is_dir = true;
42     }
43 
44     // 'name' should not contain paths consisting of exclusively '/' characters.
45     if (len == 0) {
46         return ZX_ERR_INVALID_ARGS;
47     } else if (len > NAME_MAX) {
48         return ZX_ERR_BAD_PATH;
49     }
50 
51     name_out->set(name.data(), len);
52     *dir_out = is_dir;
53     return ZX_OK;
54 }
55 
vfs_lookup(fbl::RefPtr<Vnode> vn,fbl::RefPtr<Vnode> * out,fbl::StringPiece name)56 zx_status_t vfs_lookup(fbl::RefPtr<Vnode> vn, fbl::RefPtr<Vnode>* out,
57                        fbl::StringPiece name) {
58     if (name == "..") {
59         return ZX_ERR_INVALID_ARGS;
60     } else if (name == ".") {
61         *out = std::move(vn);
62         return ZX_OK;
63     }
64     return vn->Lookup(out, name);
65 }
66 
67 // Validate open flags as much as they can be validated
68 // independently of the target node.
vfs_prevalidate_flags(uint32_t flags)69 zx_status_t vfs_prevalidate_flags(uint32_t flags) {
70     if (!(flags & ZX_FS_RIGHT_WRITABLE)) {
71         if (flags & ZX_FS_FLAG_TRUNCATE) {
72             return ZX_ERR_INVALID_ARGS;
73         }
74     } else if (!(flags & ZX_FS_RIGHTS)) {
75         if (!IsPathOnly(flags)) {
76             return ZX_ERR_INVALID_ARGS;
77         }
78     }
79     return ZX_OK;
80 }
81 
82 } // namespace
83 
84 #ifdef __Fuchsia__
85 
IsRemote() const86 bool RemoteContainer::IsRemote() const {
87     return remote_.is_valid();
88 }
89 
DetachRemote()90 zx::channel RemoteContainer::DetachRemote() {
91     return std::move(remote_);
92 }
93 
GetRemote() const94 zx_handle_t RemoteContainer::GetRemote() const {
95     return remote_.get();
96 }
97 
SetRemote(zx::channel remote)98 void RemoteContainer::SetRemote(zx::channel remote) {
99     ZX_DEBUG_ASSERT(!remote_.is_valid());
100     remote_ = std::move(remote);
101 }
102 
103 #endif
104 
105 Vfs::Vfs() = default;
106 Vfs::~Vfs() = default;
107 
108 #ifdef __Fuchsia__
Vfs(async_dispatcher_t * dispatcher)109 Vfs::Vfs(async_dispatcher_t* dispatcher)
110     : dispatcher_(dispatcher) {}
111 #endif
112 
Open(fbl::RefPtr<Vnode> vndir,fbl::RefPtr<Vnode> * out,fbl::StringPiece path,fbl::StringPiece * out_path,uint32_t flags,uint32_t mode)113 zx_status_t Vfs::Open(fbl::RefPtr<Vnode> vndir, fbl::RefPtr<Vnode>* out,
114                       fbl::StringPiece path, fbl::StringPiece* out_path, uint32_t flags,
115                       uint32_t mode) {
116 #ifdef __Fuchsia__
117     fbl::AutoLock lock(&vfs_lock_);
118 #endif
119     return OpenLocked(std::move(vndir), out, path, out_path, flags, mode);
120 }
121 
OpenLocked(fbl::RefPtr<Vnode> vndir,fbl::RefPtr<Vnode> * out,fbl::StringPiece path,fbl::StringPiece * out_path,uint32_t flags,uint32_t mode)122 zx_status_t Vfs::OpenLocked(fbl::RefPtr<Vnode> vndir, fbl::RefPtr<Vnode>* out,
123                             fbl::StringPiece path, fbl::StringPiece* out_path,
124                             uint32_t flags, uint32_t mode) {
125     FS_TRACE_DEBUG("VfsOpen: path='%s' flags=%d\n", path.begin(), flags);
126     zx_status_t r;
127     if ((r = vfs_prevalidate_flags(flags)) != ZX_OK) {
128         return r;
129     }
130     if ((r = Vfs::Walk(vndir, &vndir, path, &path)) < 0) {
131         return r;
132     }
133 #ifdef __Fuchsia__
134     if (vndir->IsRemote()) {
135         // remote filesystem, return handle and path through to caller
136         *out = std::move(vndir);
137         *out_path = path;
138         return ZX_OK;
139     }
140 #endif
141 
142     fbl::RefPtr<Vnode> vn;
143 
144     bool must_be_dir = false;
145     if ((r = vfs_name_trim(path, &path, &must_be_dir)) != ZX_OK) {
146         return r;
147     } else if (path == "..") {
148         return ZX_ERR_INVALID_ARGS;
149     }
150 
151     if (flags & ZX_FS_FLAG_CREATE) {
152         if (must_be_dir && !S_ISDIR(mode)) {
153             return ZX_ERR_INVALID_ARGS;
154         } else if (path == ".") {
155             return ZX_ERR_INVALID_ARGS;
156         } else if (ReadonlyLocked()) {
157             return ZX_ERR_ACCESS_DENIED;
158         }
159         if ((r = vndir->Create(&vn, path, mode)) < 0) {
160             if ((r == ZX_ERR_ALREADY_EXISTS) && (!(flags & ZX_FS_FLAG_EXCLUSIVE))) {
161                 goto try_open;
162             }
163             if (r == ZX_ERR_NOT_SUPPORTED) {
164                 // filesystem may not support create (like devfs)
165                 // in which case we should still try to open() the file
166                 goto try_open;
167             }
168             return r;
169         }
170 #ifdef __Fuchsia__
171         vndir->Notify(path, fuchsia_io_WATCH_EVENT_ADDED);
172 #endif
173     } else {
174     try_open:
175         r = vfs_lookup(std::move(vndir), &vn, path);
176         if (r < 0) {
177             return r;
178         }
179 #ifdef __Fuchsia__
180         if (!(flags & ZX_FS_FLAG_NOREMOTE) && vn->IsRemote()) {
181             // Opening a mount point: Traverse across remote.
182             *out_path = ".";
183             *out = std::move(vn);
184             return ZX_OK;
185         }
186 
187         flags |= (must_be_dir ? ZX_FS_FLAG_DIRECTORY : 0);
188 #endif
189         if (ReadonlyLocked() && IsWritable(flags)) {
190             return ZX_ERR_ACCESS_DENIED;
191         }
192         if ((r = vn->ValidateFlags(flags)) != ZX_OK) {
193             return r;
194         }
195         // VNODE_REF_ONLY requests that we don't actually open the underlying
196         // Vnode.
197         if (!IsPathOnly(flags)) {
198             if ((r = OpenVnode(flags, &vn)) != ZX_OK) {
199                 return r;
200             }
201             if ((flags & ZX_FS_FLAG_TRUNCATE) && ((r = vn->Truncate(0)) < 0)) {
202                 vn->Close();
203                 return r;
204             }
205         }
206     }
207     FS_TRACE_DEBUG("VfsOpen: vn=%p\n", vn.get());
208     *out_path = "";
209     *out = vn;
210     return ZX_OK;
211 }
212 
Unlink(fbl::RefPtr<Vnode> vndir,fbl::StringPiece path)213 zx_status_t Vfs::Unlink(fbl::RefPtr<Vnode> vndir, fbl::StringPiece path) {
214     bool must_be_dir;
215     zx_status_t r;
216     if ((r = vfs_name_trim(path, &path, &must_be_dir)) != ZX_OK) {
217         return r;
218     } else if (path == ".") {
219         return ZX_ERR_UNAVAILABLE;
220     } else if (path == "..") {
221         return ZX_ERR_INVALID_ARGS;
222     }
223 
224     {
225 #ifdef __Fuchsia__
226         fbl::AutoLock lock(&vfs_lock_);
227 #endif
228         if (ReadonlyLocked()) {
229             r = ZX_ERR_ACCESS_DENIED;
230         } else {
231             r = vndir->Unlink(path, must_be_dir);
232         }
233     }
234     if (r != ZX_OK) {
235         return r;
236     }
237 #ifdef __Fuchsia__
238     vndir->Notify(path, fuchsia_io_WATCH_EVENT_REMOVED);
239 #endif
240     return ZX_OK;
241 }
242 
243 #ifdef __Fuchsia__
244 
245 #define TOKEN_RIGHTS (ZX_RIGHTS_BASIC)
246 
TokenDiscard(zx::event ios_token)247 void Vfs::TokenDiscard(zx::event ios_token) {
248     fbl::AutoLock lock(&vfs_lock_);
249     if (ios_token) {
250         // The token is cleared here to prevent the following race condition:
251         // 1) Open
252         // 2) GetToken
253         // 3) Close + Release Vnode
254         // 4) Use token handle to access defunct vnode (or a different vnode,
255         //    if the memory for it is reallocated).
256         //
257         // By cleared the token cookie, any remaining handles to the event will
258         // be ignored by the filesystem server.
259         ios_token.set_cookie(*zx::process::self(), 0);
260     }
261 }
262 
VnodeToToken(fbl::RefPtr<Vnode> vn,zx::event * ios_token,zx::event * out)263 zx_status_t Vfs::VnodeToToken(fbl::RefPtr<Vnode> vn, zx::event* ios_token,
264                               zx::event* out) {
265     uint64_t vnode_cookie = reinterpret_cast<uint64_t>(vn.get());
266     zx_status_t r;
267 
268     fbl::AutoLock lock(&vfs_lock_);
269     if (ios_token->is_valid()) {
270         // Token has already been set for this iostate
271         if ((r = ios_token->duplicate(TOKEN_RIGHTS, out) != ZX_OK)) {
272             return r;
273         }
274         return ZX_OK;
275     }
276 
277     zx::event new_token;
278     zx::event new_ios_token;
279     if ((r = zx::event::create(0, &new_ios_token)) != ZX_OK) {
280         return r;
281     } else if ((r = new_ios_token.duplicate(TOKEN_RIGHTS, &new_token) != ZX_OK)) {
282         return r;
283     } else if ((r = new_ios_token.set_cookie(*zx::process::self(), vnode_cookie)) != ZX_OK) {
284         return r;
285     }
286     *ios_token = std::move(new_ios_token);
287     *out = std::move(new_token);
288     return ZX_OK;
289 }
290 
TokenToVnode(zx::event token,fbl::RefPtr<Vnode> * out)291 zx_status_t Vfs::TokenToVnode(zx::event token, fbl::RefPtr<Vnode>* out) {
292     uint64_t vcookie;
293     zx_status_t r;
294     if ((r = token.get_cookie(*zx::process::self(), &vcookie)) < 0) {
295         // TODO(smklein): Return a more specific error code for "token not from this server"
296         return ZX_ERR_INVALID_ARGS;
297     }
298 
299     if (vcookie == 0) {
300         // Client closed the channel associated with the token
301         return ZX_ERR_INVALID_ARGS;
302     }
303 
304     *out = fbl::RefPtr<fs::Vnode>(reinterpret_cast<fs::Vnode*>(vcookie));
305     return ZX_OK;
306 }
307 
Rename(zx::event token,fbl::RefPtr<Vnode> oldparent,fbl::StringPiece oldStr,fbl::StringPiece newStr)308 zx_status_t Vfs::Rename(zx::event token, fbl::RefPtr<Vnode> oldparent,
309                         fbl::StringPiece oldStr, fbl::StringPiece newStr) {
310     // Local filesystem
311     bool old_must_be_dir;
312     bool new_must_be_dir;
313     zx_status_t r;
314     if ((r = vfs_name_trim(oldStr, &oldStr, &old_must_be_dir)) != ZX_OK) {
315         return r;
316     } else if (oldStr == ".") {
317         return ZX_ERR_UNAVAILABLE;
318     } else if (oldStr == "..") {
319         return ZX_ERR_INVALID_ARGS;
320     }
321 
322     if ((r = vfs_name_trim(newStr, &newStr, &new_must_be_dir)) != ZX_OK) {
323         return r;
324     } else if (newStr == "." || newStr == "..") {
325         return ZX_ERR_INVALID_ARGS;
326     }
327 
328     fbl::RefPtr<fs::Vnode> newparent;
329     {
330         fbl::AutoLock lock(&vfs_lock_);
331         if (ReadonlyLocked()) {
332             return ZX_ERR_ACCESS_DENIED;
333         }
334         if ((r = TokenToVnode(std::move(token), &newparent)) != ZX_OK) {
335             return r;
336         }
337 
338         r = oldparent->Rename(newparent, oldStr, newStr, old_must_be_dir,
339                               new_must_be_dir);
340     }
341     if (r != ZX_OK) {
342         return r;
343     }
344     oldparent->Notify(oldStr, fuchsia_io_WATCH_EVENT_REMOVED);
345     newparent->Notify(newStr, fuchsia_io_WATCH_EVENT_ADDED);
346     return ZX_OK;
347 }
348 
Readdir(Vnode * vn,vdircookie_t * cookie,void * dirents,size_t len,size_t * out_actual)349 zx_status_t Vfs::Readdir(Vnode* vn, vdircookie_t* cookie,
350                          void* dirents, size_t len, size_t* out_actual) {
351     fbl::AutoLock lock(&vfs_lock_);
352     return vn->Readdir(cookie, dirents, len, out_actual);
353 }
354 
Link(zx::event token,fbl::RefPtr<Vnode> oldparent,fbl::StringPiece oldStr,fbl::StringPiece newStr)355 zx_status_t Vfs::Link(zx::event token, fbl::RefPtr<Vnode> oldparent,
356                       fbl::StringPiece oldStr, fbl::StringPiece newStr) {
357     fbl::AutoLock lock(&vfs_lock_);
358     fbl::RefPtr<fs::Vnode> newparent;
359     zx_status_t r;
360     if ((r = TokenToVnode(std::move(token), &newparent)) != ZX_OK) {
361         return r;
362     }
363     // Local filesystem
364     bool old_must_be_dir;
365     bool new_must_be_dir;
366     if (ReadonlyLocked()) {
367         return ZX_ERR_ACCESS_DENIED;
368     } else if ((r = vfs_name_trim(oldStr, &oldStr, &old_must_be_dir)) != ZX_OK) {
369         return r;
370     } else if (old_must_be_dir) {
371         return ZX_ERR_NOT_DIR;
372     } else if (oldStr == ".") {
373         return ZX_ERR_UNAVAILABLE;
374     } else if (oldStr == "..") {
375         return ZX_ERR_INVALID_ARGS;
376     }
377 
378     if ((r = vfs_name_trim(newStr, &newStr, &new_must_be_dir)) != ZX_OK) {
379         return r;
380     } else if (new_must_be_dir) {
381         return ZX_ERR_NOT_DIR;
382     } else if (newStr == "." || newStr == "..") {
383         return ZX_ERR_INVALID_ARGS;
384     }
385 
386     // Look up the target vnode
387     fbl::RefPtr<Vnode> target;
388     if ((r = oldparent->Lookup(&target, oldStr)) < 0) {
389         return r;
390     }
391     r = newparent->Link(newStr, target);
392     if (r != ZX_OK) {
393         return r;
394     }
395     newparent->Notify(newStr, fuchsia_io_WATCH_EVENT_ADDED);
396     return ZX_OK;
397 }
398 
ServeConnection(fbl::unique_ptr<Connection> connection)399 zx_status_t Vfs::ServeConnection(fbl::unique_ptr<Connection> connection) {
400     ZX_DEBUG_ASSERT(connection);
401 
402     zx_status_t status = connection->Serve();
403     if (status == ZX_OK) {
404         RegisterConnection(std::move(connection));
405     }
406     return status;
407 }
408 
OnConnectionClosedRemotely(Connection * connection)409 void Vfs::OnConnectionClosedRemotely(Connection* connection) {
410     ZX_DEBUG_ASSERT(connection);
411 
412     UnregisterConnection(connection);
413 }
414 
ServeDirectory(fbl::RefPtr<fs::Vnode> vn,zx::channel channel)415 zx_status_t Vfs::ServeDirectory(fbl::RefPtr<fs::Vnode> vn, zx::channel channel) {
416     uint32_t flags = ZX_FS_FLAG_DIRECTORY;
417     zx_status_t r;
418     if ((r = vn->ValidateFlags(flags)) != ZX_OK) {
419         return r;
420     } else if ((r = OpenVnode(flags, &vn)) != ZX_OK) {
421         return r;
422     }
423 
424     // Tell the calling process that we've mounted the directory.
425     r = channel.signal_peer(0, ZX_USER_SIGNAL_0);
426     // ZX_ERR_PEER_CLOSED is ok because the channel may still be readable.
427     if (r != ZX_OK && r != ZX_ERR_PEER_CLOSED) {
428         return r;
429     }
430 
431     return vn->Serve(this, std::move(channel), ZX_FS_RIGHT_ADMIN);
432 }
433 
434 #endif // ifdef __Fuchsia__
435 
SetReadonly(bool value)436 void Vfs::SetReadonly(bool value) {
437 #ifdef __Fuchsia__
438     fbl::AutoLock lock(&vfs_lock_);
439 #endif
440     readonly_ = value;
441 }
442 
Walk(fbl::RefPtr<Vnode> vn,fbl::RefPtr<Vnode> * out_vn,fbl::StringPiece path,fbl::StringPiece * out_path)443 zx_status_t Vfs::Walk(fbl::RefPtr<Vnode> vn, fbl::RefPtr<Vnode>* out_vn,
444                       fbl::StringPiece path, fbl::StringPiece* out_path) {
445     zx_status_t r;
446     while (!path.empty() && path[path.length() - 1] == '/') {
447         // Discard extra trailing '/' characters.
448         path.set(path.data(), path.length() - 1);
449     }
450 
451     for (;;) {
452         while (!path.empty() && path[0] == '/') {
453             // Discard extra leading '/' characters.
454             path.set(&path[1], path.length() - 1);
455         }
456         if (path.empty()) {
457             // Convert empty initial path of final path segment to ".".
458             path.set(".", 1);
459         }
460 #ifdef __Fuchsia__
461         if (vn->IsRemote()) {
462             // Remote filesystem mount, caller must resolve.
463             *out_vn = std::move(vn);
464             *out_path = std::move(path);
465             return ZX_OK;
466         }
467 #endif
468 
469         // Look for the next '/' separated path component.
470         const char* next_path = reinterpret_cast<const char*>(
471                 memchr(path.data(), '/', path.length()));
472         if (next_path == nullptr) {
473             // Final path segment.
474             *out_vn = vn;
475             *out_path = std::move(path);
476             return ZX_OK;
477         }
478 
479         // Path has at least one additional segment.
480         fbl::StringPiece component(path.data(), next_path - path.data());
481         if ((r = vfs_lookup(std::move(vn), &vn, component)) != ZX_OK) {
482             return r;
483         }
484         // Traverse to the next segment.
485         path.set(next_path + 1, path.length() - (component.length() + 1));
486     }
487 }
488 
489 } // namespace fs
490