blob: 15a2e97442d8014b40a842fe7e190debb00e7a85 [file] [log] [blame]
// Copyright 2017 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 <errno.h>
#include <fcntl.h>
#include <fuchsia/boot/c/fidl.h>
#include <fuchsia/fshost/llcpp/fidl.h>
#include <fuchsia/io/llcpp/fidl.h>
#include <fuchsia/ldsvc/c/fidl.h>
#include <getopt.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/watcher.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/fit/defer.h>
#include <lib/zx/channel.h>
#include <stdio.h>
#include <zircon/boot/image.h>
#include <zircon/device/vfs.h>
#include <zircon/dlfcn.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <memory>
#include <thread>
#include <cobalt-client/cpp/collector.h>
#include <fbl/unique_fd.h>
#include <fs/remote_dir.h>
#include <fs/service.h>
#include <ramdevice-client/ramdisk.h>
#include <zbi-bootfs/zbi-bootfs.h>
#include "block-watcher.h"
#include "fs-manager.h"
#include "metrics.h"
#include "src/storage/fshost/deprecated-loader-service.h"
#include "src/sys/lib/stdout-to-debuglog/cpp/stdout-to-debuglog.h"
namespace fio = ::llcpp::fuchsia::io;
namespace devmgr {
namespace {
// local_storage project ID as defined in cobalt-analytics projects.yaml.
constexpr uint32_t kCobaltProjectId = 3676913920;
FsHostMetrics MakeMetrics() {
return FsHostMetrics(std::make_unique<cobalt_client::Collector>(kCobaltProjectId));
}
constexpr char kItemsPath[] = "/svc/" fuchsia_boot_Items_Name;
// Get ramdisk from the boot items service.
zx_status_t get_ramdisk(zx::vmo* ramdisk_vmo) {
zx::channel local, remote;
zx_status_t status = zx::channel::create(0, &local, &remote);
if (status != ZX_OK) {
return status;
}
status = fdio_service_connect(kItemsPath, remote.release());
if (status != ZX_OK) {
return status;
}
uint32_t length;
return fuchsia_boot_ItemsGet(local.get(), ZBI_TYPE_STORAGE_RAMDISK, 0,
ramdisk_vmo->reset_and_get_address(), &length);
}
zx_status_t MiscDeviceAdded(int dirfd, int event, const char* fn, void* cookie) {
if (event != WATCH_EVENT_ADD_FILE || strcmp(fn, "ramctl") != 0) {
return ZX_OK;
}
zx::vmo ramdisk_vmo(static_cast<zx_handle_t>(reinterpret_cast<uintptr_t>(cookie)));
zbi_header_t header;
zx_status_t status = ramdisk_vmo.read(&header, 0, sizeof(header));
if (status != ZX_OK) {
printf("fshost: cannot read ZBI_TYPE_STORAGE_RAMDISK item header: %s\n",
zx_status_get_string(status));
return ZX_ERR_STOP;
}
if (!(header.flags & ZBI_FLAG_VERSION) || header.magic != ZBI_ITEM_MAGIC ||
header.type != ZBI_TYPE_STORAGE_RAMDISK) {
printf("fshost: invalid ZBI_TYPE_STORAGE_RAMDISK item header\n");
return ZX_ERR_STOP;
}
zx::vmo vmo;
if (header.flags & ZBI_FLAG_STORAGE_COMPRESSED) {
status = zx::vmo::create(header.extra, 0, &vmo);
if (status != ZX_OK) {
printf("fshost: cannot create VMO for uncompressed RAMDISK: %s\n",
zx_status_get_string(status));
return ZX_ERR_STOP;
}
status = zbi_bootfs::Decompress(ramdisk_vmo, sizeof(zbi_header_t), header.length, vmo, 0,
header.extra);
if (status != ZX_OK) {
printf("fshost: failed to decompress RAMDISK: %s\n", zx_status_get_string(status));
return ZX_ERR_STOP;
}
} else {
// TODO(fxbug.dev/34597): The old code ignored uncompressed items too, and
// silently. Really the protocol should be cleaned up so the VMO arrives
// without the header in it and then it could just be used here directly
// if uncompressed (or maybe bootsvc deals with decompression in the first
// place so the uncompressed VMO is always what we get).
printf("fshost: ignoring uncompressed RAMDISK item in ZBI\n");
return ZX_ERR_STOP;
}
ramdisk_client* client;
status = ramdisk_create_from_vmo(vmo.release(), &client);
if (status != ZX_OK) {
printf("fshost: failed to create ramdisk from ZBI_TYPE_STORAGE_RAMDISK\n");
} else {
printf("fshost: ZBI_TYPE_STORAGE_RAMDISK attached\n");
}
return ZX_ERR_STOP;
}
int RamctlWatcher(void* arg) {
fbl::unique_fd dirfd(open("/dev/misc", O_DIRECTORY | O_RDONLY));
if (!dirfd) {
printf("fshost: failed to open /dev/misc: %s\n", strerror(errno));
return -1;
}
fdio_watch_directory(dirfd.get(), &MiscDeviceAdded, ZX_TIME_INFINITE, arg);
return 0;
}
int BlockWatcher(std::unique_ptr<devmgr::FsManager> fs_manager) {
// Check relevant boot arguments
devmgr::BlockWatcherOptions options = {};
options.netboot = fs_manager->boot_args()->netboot();
options.check_filesystems = fs_manager->boot_args()->check_filesystems();
options.wait_for_data = fs_manager->boot_args()->wait_for_data();
if (options.netboot) {
printf("fshost: disabling automount\n");
}
BlockDeviceWatcher(std::move(fs_manager), options);
return 0;
}
// Initialize the fshost namespace.
//
// |fs_root_client| is mapped to "/fs", and represents the filesystem of devmgr.
zx_status_t BindNamespace(zx::channel fs_root_client) {
fdio_ns_t* ns;
zx_status_t status;
if ((status = fdio_ns_get_installed(&ns)) != ZX_OK) {
printf("fshost: cannot get namespace: %d\n", status);
return status;
}
// Bind "/fs".
if ((status = fdio_ns_bind(ns, "/fs", fs_root_client.release())) != ZX_OK) {
printf("fshost: cannot bind /fs to namespace: %d\n", status);
return status;
}
// Bind "/system".
{
zx::channel client, server;
if ((status = zx::channel::create(0, &client, &server)) != ZX_OK) {
return status;
}
if ((status = fdio_open("/fs/system",
ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_EXECUTABLE | ZX_FS_RIGHT_ADMIN,
server.release())) != ZX_OK) {
printf("fshost: cannot open connection to /system: %d\n", status);
return status;
}
if ((status = fdio_ns_bind(ns, "/system", client.release())) != ZX_OK) {
printf("fshost: cannot bind /system to namespace: %d\n", status);
return status;
}
}
return ZX_OK;
}
} // namespace
} // namespace devmgr
int main(int argc, char** argv) {
bool disable_block_watcher = false;
bool log_to_debuglog = false;
enum {
kDisableBlockWatcher,
kLogToDebuglog,
};
option options[] = {
{"disable-block-watcher", no_argument, nullptr, kDisableBlockWatcher},
{"log-to-debuglog", no_argument, nullptr, kLogToDebuglog},
};
int opt;
while ((opt = getopt_long(argc, argv, "", options, nullptr)) != -1) {
switch (opt) {
case kDisableBlockWatcher:
printf("fshost: received --disable-block-watcher\n");
disable_block_watcher = true;
break;
case kLogToDebuglog:
log_to_debuglog = true;
break;
}
}
if (log_to_debuglog) {
zx_status_t status = StdoutToDebuglog::Init();
if (status != ZX_OK) {
return status;
}
}
// Setup the fshost loader service, which can load libraries from either /system/lib or /boot/lib.
// TODO(fxbug.dev/34633): This loader is DEPRECATED and should be deleted. Do not add new usages.
async::Loop loader_loop(&kAsyncLoopConfigNoAttachToCurrentThread);
zx_status_t status = loader_loop.StartThread("fshost-loader");
if (status != ZX_OK) {
fprintf(stderr, "fshost: failed to start loader thread: %s\n", zx_status_get_string(status));
return status;
}
fbl::unique_fd root_fd;
status = fdio_open_fd(
"/", fio::OPEN_FLAG_DIRECTORY | fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_EXECUTABLE,
root_fd.reset_and_get_address());
if (status != ZX_OK) {
fprintf(stderr, "fshost: failed to open namespace root: %s\n", zx_status_get_string(status));
return status;
}
auto loader = DeprecatedBootSystemLoaderService::Create(loader_loop.dispatcher(),
std::move(root_fd), "fshost");
// Replace default loader service with a connection to our own.
// TODO(bryanhenry): This is unnecessary and will be removed in a subsequent change. Left in to
// minimize behavior differences per change.
auto conn = loader->Connect();
if (conn.is_error()) {
fprintf(stderr, "fshost: failed to create loader connection: %s\n", conn.status_string());
return conn.status_value();
}
zx_handle_close(dl_set_loader_service(std::move(conn).value().release()));
// Initialize the local filesystem in isolation.
zx::channel dir_request(zx_take_startup_handle(PA_DIRECTORY_REQUEST));
zx::channel lifecycle_request(zx_take_startup_handle(PA_LIFECYCLE));
std::unique_ptr<devmgr::FsManager> fs_manager;
status =
devmgr::FsManager::Create(std::move(loader), std::move(dir_request),
std::move(lifecycle_request), devmgr::MakeMetrics(), &fs_manager);
if (status != ZX_OK) {
printf("fshost: Cannot create FsManager: %s\n", zx_status_get_string(status));
return status;
}
// Serve the root filesystems in our own namespace.
zx::channel fs_root_client, fs_root_server;
status = zx::channel::create(0, &fs_root_client, &fs_root_server);
if (status != ZX_OK) {
return ZX_OK;
}
status = fs_manager->ServeRoot(std::move(fs_root_server));
if (status != ZX_OK) {
printf("fshost: Cannot serve devmgr's root filesystem\n");
return status;
}
// Initialize namespace, and begin monitoring for a termination event.
status = devmgr::BindNamespace(std::move(fs_root_client));
if (status != ZX_OK) {
printf("fshost: cannot bind namespace\n");
return status;
}
// TODO(dgonyeo): call WatchExit from inside FsManager, instead of doing it
// here.
fs_manager->WatchExit();
// If there is a ramdisk, setup the ramctl filesystems.
zx::vmo ramdisk_vmo;
status = devmgr::get_ramdisk(&ramdisk_vmo);
if (status != ZX_OK) {
printf("fshost: failed to get ramdisk: %s\n", zx_status_get_string(status));
} else if (ramdisk_vmo.is_valid()) {
thrd_t t;
int err = thrd_create_with_name(
&t, &devmgr::RamctlWatcher,
reinterpret_cast<void*>(static_cast<uintptr_t>(ramdisk_vmo.release())),
"ramctl-filesystems");
if (err != thrd_success) {
printf("fshost: failed to start ramctl-filesystems: %d\n", err);
}
thrd_detach(t);
}
if (!disable_block_watcher) {
std::thread t(&devmgr::BlockWatcher, std::move(fs_manager));
t.detach();
}
zx::nanosleep(zx::time::infinite());
printf("fshost: terminating (block device filesystems finished?)\n");
return 0;
}