blob: 86c06570327bd55d7e27aae425588e0e9cd5548f [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <threads.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/wait.h>
#include <fs/vfs.h>
#include <zircon/device/device.h>
#include <zircon/device/vfs.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <zircon/thread_annotations.h>
#include <fdio/debug.h>
#include <fdio/io.h>
#include <fdio/remoteio.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/unique_ptr.h>
#include "devmgr.h"
#include "memfs-private.h"
#define ZXDEBUG 0
namespace memfs {
namespace {
Vfs root_vfs;
Vfs system_vfs;
fbl::unique_ptr<async::Loop> global_loop;
async::Wait global_shutdown;
} // namespace
static fbl::RefPtr<VnodeDir> global_root = nullptr;
static fbl::RefPtr<VnodeDir> memfs_root = nullptr;
static fbl::RefPtr<VnodeDir> devfs_root = nullptr;
static fbl::RefPtr<VnodeDir> bootfs_root = nullptr;
static fbl::RefPtr<VnodeDir> systemfs_root = nullptr;
static VnodeMemfs* global_vfs_root = nullptr;
zx_status_t add_vmofile(fbl::RefPtr<VnodeDir> vnb, const char* path, zx_handle_t vmo,
zx_off_t off, size_t len) {
zx_status_t r;
if ((path[0] == '/') || (path[0] == 0))
return ZX_ERR_INVALID_ARGS;
for (;;) {
const char* nextpath = strchr(path, '/');
if (nextpath == nullptr) {
if (path[0] == 0) {
return ZX_ERR_INVALID_ARGS;
}
bool vmofile = true;
return vnb->vfs()->CreateFromVmo(vnb.get(), vmofile,
fbl::StringPiece(path, strlen(path)), vmo, off, len);
} else {
if (nextpath == path) {
return ZX_ERR_INVALID_ARGS;
}
fbl::RefPtr<fs::Vnode> out;
r = vnb->Lookup(&out, fbl::StringPiece(path, nextpath - path));
if (r == ZX_ERR_NOT_FOUND) {
r = vnb->Create(&out, fbl::StringPiece(path, nextpath - path), S_IFDIR);
}
if (r < 0) {
return r;
}
vnb = fbl::RefPtr<VnodeDir>::Downcast(fbl::move(out));
path = nextpath + 1;
}
}
}
} // namespace memfs
// The following functions exist outside the memfs namespace so they
// can be exposed to C:
fbl::RefPtr<memfs::VnodeDir> SystemfsRoot() {
if (memfs::systemfs_root == nullptr) {
zx_status_t r = memfs::createFilesystem("system", &memfs::system_vfs, &memfs::systemfs_root);
if (r < 0) {
printf("fatal error %d allocating 'system' file system\n", r);
__builtin_trap();
}
}
return memfs::systemfs_root;
}
fbl::RefPtr<memfs::VnodeDir> MemfsRoot() {
if (memfs::memfs_root == nullptr) {
zx_status_t r = memfs::createFilesystem("tmp", &memfs::root_vfs, &memfs::memfs_root);
if (r < 0) {
printf("fatal error %d allocating 'tmp' file system\n", r);
__builtin_trap();
}
}
return memfs::memfs_root;
}
fbl::RefPtr<memfs::VnodeDir> DevfsRoot() {
if (memfs::devfs_root == nullptr) {
zx_status_t r = memfs::createFilesystem("dev", &memfs::root_vfs, &memfs::devfs_root);
if (r < 0) {
printf("fatal error %d allocating 'device' file system\n", r);
__builtin_trap();
}
}
return memfs::devfs_root;
}
fbl::RefPtr<memfs::VnodeDir> BootfsRoot() {
if (memfs::bootfs_root == nullptr) {
zx_status_t r = memfs::createFilesystem("boot", &memfs::root_vfs, &memfs::bootfs_root);
if (r < 0) {
printf("fatal error %d allocating 'boot' file system\n", r);
__builtin_trap();
}
}
return memfs::bootfs_root;
}
zx_status_t devfs_mount(zx_handle_t h) {
return DevfsRoot()->AttachRemote(fs::MountChannel(h));
}
VnodeDir* systemfs_get_root() {
return SystemfsRoot().get();
}
void systemfs_set_readonly(bool value) {
SystemfsRoot()->vfs()->SetReadonly(value);
}
zx_status_t bootfs_add_file(const char* path, zx_handle_t vmo, zx_off_t off, size_t len) {
return add_vmofile(BootfsRoot(), path, vmo, off, len);
}
zx_status_t systemfs_add_file(const char* path, zx_handle_t vmo, zx_off_t off, size_t len) {
return add_vmofile(SystemfsRoot(), path, vmo, off, len);
}
static const char* mount_points[] = {
"/data", "/volume", "/system", "/install", "/blob", "/pkgfs"
};
static fbl::RefPtr<fs::Vnode> mount_nodes[countof(mount_points)];
zx_status_t vfs_install_fs(const char* path, zx_handle_t h) {
for (unsigned n = 0; n < countof(mount_points); n++) {
if (!strcmp(path, mount_points[n])) {
return memfs::root_vfs.InstallRemote(mount_nodes[n], fs::MountChannel(h));
}
}
zx_handle_close(h);
return ZX_ERR_NOT_FOUND;
}
// Hardcoded initialization function to create/access global root directory
VnodeDir* vfs_create_global_root() {
if (memfs::global_root == nullptr) {
zx_status_t r = memfs::createFilesystem("<root>", &memfs::root_vfs, &memfs::global_root);
if (r < 0) {
printf("fatal error %d allocating root file system\n", r);
__builtin_trap();
}
memfs::root_vfs.MountSubtree(memfs::global_root.get(), DevfsRoot());
memfs::root_vfs.MountSubtree(memfs::global_root.get(), BootfsRoot());
memfs::root_vfs.MountSubtree(memfs::global_root.get(), MemfsRoot());
for (unsigned n = 0; n < countof(mount_points); n++) {
fbl::StringPiece pathout;
r = memfs::root_vfs.Open(memfs::global_root, &mount_nodes[n],
fbl::StringPiece(mount_points[n]), &pathout,
ZX_FS_RIGHT_READABLE | ZX_FS_FLAG_CREATE, S_IFDIR);
ZX_ASSERT(r == ZX_OK);
}
memfs::global_loop.reset(new async::Loop());
memfs::global_loop->StartThread("root-dispatcher");
memfs::root_vfs.SetAsync(memfs::global_loop->async());
memfs::system_vfs.SetAsync(memfs::global_loop->async());
}
return memfs::global_root.get();
}
zx_status_t memfs_mount(VnodeDir* parent, const char* name, VnodeDir* subtree) {
fbl::RefPtr<fs::Vnode> vn;
zx_status_t status = parent->Lookup(&vn, fbl::StringPiece(name));
if (status != ZX_OK)
return status;
zx_handle_t h;
status = vfs_create_root_handle(subtree, &h);
if (status != ZX_OK)
return status;
return parent->vfs()->InstallRemote(fbl::move(vn), fs::MountChannel(h));
}
// Acquire the root vnode and return a handle to it through the VFS dispatcher
zx_status_t vfs_create_root_handle(VnodeMemfs* vn, zx_handle_t* out) {
zx::channel h1, h2;
zx_status_t r = zx::channel::create(0, &h1, &h2);
if (r == ZX_OK) {
r = vn->vfs()->ServeDirectory(fbl::RefPtr<fs::Vnode>(vn),
fbl::move(h1));
}
if (r == ZX_OK) {
*out = h2.release();
}
return r;
}
zx_status_t vfs_connect_root_handle(VnodeMemfs* vn, zx_handle_t h) {
zx::channel ch(h);
return vn->vfs()->ServeDirectory(fbl::RefPtr<fs::Vnode>(vn), fbl::move(ch));
}
// Initialize the global root VFS node
void vfs_global_init(VnodeDir* root) {
memfs::global_vfs_root = root;
}
void vfs_watch_exit(zx_handle_t event) {
memfs::global_shutdown.set_handler([event](async_t* async,
async::Wait* wait,
zx_status_t status,
const zx_packet_signal_t* signal) {
memfs::root_vfs.UninstallAll(ZX_TIME_INFINITE);
memfs::system_vfs.UninstallAll(ZX_TIME_INFINITE);
zx_object_signal(event, 0, FSHOST_SIGNAL_EXIT_DONE);
});
memfs::global_shutdown.set_object(event);
memfs::global_shutdown.set_trigger(FSHOST_SIGNAL_EXIT);
memfs::global_shutdown.Begin(memfs::global_loop->async());
}
// Return a RIO handle to the global root
zx_status_t vfs_create_global_root_handle(zx_handle_t* out) {
return vfs_create_root_handle(memfs::global_vfs_root, out);
}
zx_status_t vfs_connect_global_root_handle(zx_handle_t h) {
return vfs_connect_root_handle(memfs::global_vfs_root, h);
}