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 <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <threads.h>
11 
12 #include <lib/async-loop/cpp/loop.h>
13 #include <lib/async/cpp/wait.h>
14 #include <fs/vfs.h>
15 #include <zircon/device/device.h>
16 #include <zircon/device/vfs.h>
17 #include <zircon/processargs.h>
18 #include <zircon/syscalls.h>
19 #include <zircon/thread_annotations.h>
20 #include <lib/fdio/debug.h>
21 #include <lib/fdio/io.h>
22 #include <fbl/algorithm.h>
23 #include <fbl/alloc_checker.h>
24 #include <fbl/auto_lock.h>
25 #include <fbl/unique_ptr.h>
26 
27 #include <utility>
28 
29 #include "fshost.h"
30 
31 #define ZXDEBUG 0
32 
33 namespace devmgr {
34 namespace {
35 
AddVmofile(fbl::RefPtr<memfs::VnodeDir> vnb,const char * path,zx_handle_t vmo,zx_off_t off,size_t len)36 zx_status_t AddVmofile(fbl::RefPtr<memfs::VnodeDir> vnb, const char* path, zx_handle_t vmo,
37                        zx_off_t off, size_t len) {
38     zx_status_t r;
39     if ((path[0] == '/') || (path[0] == 0))
40         return ZX_ERR_INVALID_ARGS;
41     for (;;) {
42         const char* nextpath = strchr(path, '/');
43         if (nextpath == nullptr) {
44             if (path[0] == 0) {
45                 return ZX_ERR_INVALID_ARGS;
46             }
47             return vnb->vfs()->CreateFromVmo(vnb.get(), fbl::StringPiece(path, strlen(path)),
48                                              vmo, off, len);
49         } else {
50             if (nextpath == path) {
51                 return ZX_ERR_INVALID_ARGS;
52             }
53 
54             fbl::RefPtr<fs::Vnode> out;
55             r = vnb->Lookup(&out, fbl::StringPiece(path, nextpath - path));
56             if (r == ZX_ERR_NOT_FOUND) {
57                 r = vnb->Create(&out, fbl::StringPiece(path, nextpath - path), S_IFDIR);
58             }
59 
60             if (r < 0) {
61                 return r;
62             }
63             vnb = fbl::RefPtr<memfs::VnodeDir>::Downcast(std::move(out));
64             path = nextpath + 1;
65         }
66     }
67 }
68 
69 } // namespace
70 
71 // TODO: For operations which can fail, we should use a private constructor
72 // pattern and create FsManager with error validation prior to calling
73 // the real constructor.
FsManager()74 FsManager::FsManager() {
75     ZX_ASSERT(global_root_ == nullptr);
76     zx_status_t status = CreateFilesystem("<root>", &root_vfs_, &global_root_);
77     ZX_ASSERT(status == ZX_OK);
78 
79     fbl::RefPtr<memfs::VnodeDir> bootfs_root;
80     status = CreateFilesystem("boot", &root_vfs_, &bootfs_root);
81     ZX_ASSERT(status == ZX_OK);
82     root_vfs_.MountSubtree(global_root_.get(), std::move(bootfs_root));
83 
84     status = CreateFilesystem("tmp", &root_vfs_, &memfs_root_);
85     ZX_ASSERT(status == ZX_OK);
86     root_vfs_.MountSubtree(global_root_.get(), memfs_root_);
87 
88     for (unsigned n = 0; n < fbl::count_of(kMountPoints); n++) {
89         fbl::StringPiece pathout;
90         status = root_vfs_.Open(global_root_, &mount_nodes[n],
91                                 fbl::StringPiece(kMountPoints[n]), &pathout,
92                                 ZX_FS_RIGHT_READABLE | ZX_FS_FLAG_CREATE, S_IFDIR);
93         ZX_ASSERT(status == ZX_OK);
94     }
95 
96     global_loop_.reset(new async::Loop(&kAsyncLoopConfigNoAttachToThread));
97     global_loop_->StartThread("root-dispatcher");
98     root_vfs_.SetDispatcher(global_loop_->dispatcher());
99     system_vfs_.SetDispatcher(global_loop_->dispatcher());
100 }
101 
SystemfsAddFile(const char * path,zx_handle_t vmo,zx_off_t off,size_t len)102 zx_status_t FsManager::SystemfsAddFile(const char* path, zx_handle_t vmo, zx_off_t off,
103                                        size_t len) {
104     return AddVmofile(systemfs_root_, path, vmo, off, len);
105 }
106 
MountSystem()107 zx_status_t FsManager::MountSystem() {
108     ZX_ASSERT(systemfs_root_ == nullptr);
109     zx_status_t status = CreateFilesystem("system", &system_vfs_, &systemfs_root_);
110     ZX_ASSERT(status == ZX_OK);
111     return LocalMount(global_root_.get(), "system", systemfs_root_);
112 }
113 
SystemfsSetReadonly(bool value)114 void FsManager::SystemfsSetReadonly(bool value) {
115     ZX_ASSERT(systemfs_root_ == nullptr);
116     systemfs_root_->vfs()->SetReadonly(value);
117 }
118 
InstallFs(const char * path,zx::channel h)119 zx_status_t FsManager::InstallFs(const char* path, zx::channel h) {
120     for (unsigned n = 0; n < fbl::count_of(kMountPoints); n++) {
121         if (!strcmp(path, kMountPoints[n])) {
122             return root_vfs_.InstallRemote(mount_nodes[n], fs::MountChannel(std::move(h)));
123         }
124     }
125     return ZX_ERR_NOT_FOUND;
126 }
127 
InitializeConnections(zx::channel root,zx::channel devfs_root,zx::channel svc_root,zx::event fshost_event)128 zx_status_t FsManager::InitializeConnections(zx::channel root, zx::channel devfs_root,
129                                              zx::channel svc_root, zx::event fshost_event) {
130     // Serve devmgr's root handle using our own root directory.
131     zx_status_t status = ConnectRoot(std::move(root));
132     if (status != ZX_OK) {
133         printf("fshost: Cannot connect to fshost root: %d\n", status);
134     }
135 
136     zx::channel fs_root;
137     if ((status = ServeRoot(&fs_root)) != ZX_OK) {
138         printf("fshost: cannot create global root\n");
139     }
140 
141     connections_ = fbl::make_unique<FshostConnections>(std::move(devfs_root),
142                                                        std::move(svc_root),
143                                                        std::move(fs_root),
144                                                        std::move(fshost_event));
145     // Now that we've initialized our connection to the outside world,
146     // monitor for external shutdown events.
147     WatchExit();
148     return connections_->CreateNamespace();
149 }
150 
ConnectRoot(zx::channel server)151 zx_status_t FsManager::ConnectRoot(zx::channel server) {
152     return ServeVnode(global_root_, std::move(server));
153 }
154 
ServeRoot(zx::channel * out)155 zx_status_t FsManager::ServeRoot(zx::channel* out) {
156     zx::channel client, server;
157     zx_status_t status = zx::channel::create(0, &client, &server);
158     if (status != ZX_OK) {
159         return ZX_OK;
160     }
161     if ((status = ServeVnode(global_root_, std::move(server))) != ZX_OK) {
162         return status;
163     }
164     *out = std::move(client);
165     return ZX_OK;
166 }
167 
WatchExit()168 void FsManager::WatchExit() {
169     global_shutdown_.set_handler([this](async_dispatcher_t* dispatcher,
170                                         async::Wait* wait,
171                                         zx_status_t status,
172                                         const zx_packet_signal_t* signal) {
173         root_vfs_.UninstallAll(ZX_TIME_INFINITE);
174         system_vfs_.UninstallAll(ZX_TIME_INFINITE);
175         connections_->Event().signal(0, FSHOST_SIGNAL_EXIT_DONE);
176     });
177 
178     global_shutdown_.set_object(connections_->Event().get());
179     global_shutdown_.set_trigger(FSHOST_SIGNAL_EXIT);
180     global_shutdown_.Begin(global_loop_->dispatcher());
181 }
182 
ServeVnode(fbl::RefPtr<memfs::VnodeDir> & vn,zx::channel server)183 zx_status_t FsManager::ServeVnode(fbl::RefPtr<memfs::VnodeDir>& vn, zx::channel server) {
184     return vn->vfs()->ServeDirectory(vn, std::move(server));
185 }
186 
LocalMount(memfs::VnodeDir * parent,const char * name,fbl::RefPtr<memfs::VnodeDir> & subtree)187 zx_status_t FsManager::LocalMount(memfs::VnodeDir* parent, const char* name,
188                                   fbl::RefPtr<memfs::VnodeDir>& subtree) {
189     fbl::RefPtr<fs::Vnode> vn;
190     zx_status_t status = parent->Lookup(&vn, fbl::StringPiece(name));
191     if (status != ZX_OK) {
192         return status;
193     }
194     zx::channel client, server;
195     status = zx::channel::create(0, &client, &server);
196     if (status != ZX_OK) {
197         return ZX_OK;
198     }
199     if ((status = ServeVnode(subtree, std::move(server))) != ZX_OK) {
200         return status;
201     }
202     return parent->vfs()->InstallRemote(std::move(vn),
203                                         fs::MountChannel(std::move(client)));
204 }
205 
206 } // namespace devmgr
207